I2C communication question

jpanhalt

Joined Jan 18, 2008
11,087
At least in my mind, there can be overlap between what goes in a Table and what goes in a Flowchart. In general, a Table is an organized list of data, definitions and/or relationships. A flowchart describes a process, which may (usually does) include decision points and branches.

In the Table I suggested (post #20), it was intended for definitions . That is, defining the various "conditions" that are used in I2C. One could then refer to each condition in designing a flowchart.

In the draft table you post above, you have mixed definitions (i.e., the I2C start condition) with processes, e.g, "Send slave address with R/W mode."

Looking only at that post, I would put the definition of "R/W mode" in a table and the "Send slave address" in a flowchart. You could also put the definition of "slave address" in a table, but that might be unnecessary.

Think of the process in your mind. Let's say you have a flowchart and the first step is "Send start condition." Alternatively, you could break that up into several steps, such as, "is SDA high?" "yes/no" "if high, next step" 'if low, set high" "next step" (or revisit "is SDA high") "set SCL high" and finally , "set SDA low" and so forth. If you did the latter throughout a flowchart, it might get really complicated and difficult to follow.

So, you put the definition of "Start condition" in a table and avoid that complexity in the flow chart. The decision is up to you whether to have an extremely detailed flow chart or a flow chart that shows the overall process and has much of the detail in a table. The one thing I would avoid would be mixing definitions and processes in a table, if the table was supposed to be only definitions.
 

BobaMosfet

Joined Jul 1, 2009
2,113
I tried to make table as you suggested I have read the datasheet but I can't figure out what would be SCL and SDA at specific condition
View attachment 194226
@BobaMosfet
When you think about 'conditon' what you need to be thinking about is the state of the SDA and SCL lines, and the associated registers and protcol flagbits for the MCU you're using.

For me, a table isn't complete answer, or a roadmap. it's a way to condense paragraphical information (as you find in a datasheet) into a table so the relationships between condition and lines and registers etc are defined, shown, and provided as an easy reference for you to then determine the flow.

I _highly_ recommend you take advantage of youtube and look at everything you can find about i2c/twi.

If you want to do i2c/twi you need to first understand what terms mean exactly.

  1. What is a START? Or a REPEATED START? What signifies an ACK, or a STOP? There are 13 such state conditions.
  2. You need to then figure out how to put those things together (START, STOP, ACK, etc) in the right way- this becomes your flow-chart of commands. There are about 15 commands.
  3. Then you need to code it and test it. Put a means of debugging in, while you code it, so you can test it.

Understanding your registers, your flags, and how to use those together for status and commands is the key to the protocol. After that it's a simple process of following the methodology provided (see your MCU's datasheet, or the Philips/NXP datasheet on i2c/twi).
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
  1. What is a START? Or a REPEATED START? What signifies an ACK, or a STOP? There are 13 such state conditions.
  2. You need to then figure out how to put those things together (START, STOP, ACK, etc) in the right way- this becomes your flow-chart of commands. There are about 15 commands.
  3. Then you need to code it and test it. Put a means of debugging in, while you code it, so you can test it.
I have read it datasheet But I do not understand how to convert my flow chart to code

1576079767296.png
There are two signals SDA an SCL
Given in datasheet, A high to low transition on SDA line while SCL is high

SCL = High //
SDA = 0 or 1 ?

What would be code for I2C start block ?

@JohnInTX @BobaMosfet @jpanhalt
 

JohnInTX

Joined Jun 26, 2012
4,787
I have read it datasheet But I do not understand how to convert my flow chart to code
??
If you are bit-banging I2C it's very similar to your bit-banging SPI. You need to change the output levels of SCL and SDA to generate the proper bus signals. @BobaMosfet correctly points out that I2C communications can be reduced to a number of states. So, identify those states and code them as separate functions. Then use those functions to create messages.

Here are a couple of examples to get you started:

C:
#define OK (unsigned char) 0
#define ERROR_BUS_BUSY (unsigned char) 1

// Send I2C Start.  Bus must be idle i.e. SCL and SDA both high.  Return error if bus not idle.  Return OK if Start successfully sent
unsigned char (I2C_Start)
{
   if ((SCL == 0) || (SDA == 0))  // make sure bus is idle (SDA and SCL are 1), if not, return an error
    return(ERROR_BUS_BUSY);

  SDA = 0;  // bus is idle, drop SDA while SCL is high
  delay_us();
  SCL = 0;  // then drop SCL
  return(OK);
}

// Send I2C stoP.  Assumes SCL is 0 after last ACK/NAK.  No error checking shown.
unsigned char(I2C_Stop)
{
  if(SDA == 1){
   SDA = 0;  // be sure SDA is low
   delay_us();
  }
  SCL = 1;  // raise SCL
  delay_us();
  SDA = 1; // then SDA to generate the stoP.
  return(OK);
}
I've shown some error handling in I2C_Start() to give you an idea of how to make sure your bus is OK. You can ignore error handling for playing around but in practice, you'll need to anticipate and handle errors on the bus. In the case of I2C_Start, the bus must be idle i.e. SCL and SDA both high. If one or both are low, you can't send a Start condition and the code should know about that. A full I2C implementation will have a lot of error checking/bus monitoring. For example, a slave can hold SCL low if it needs more time to process something. If you set SCL = 1, you should check to see if it actually is a 1 on the bus to ensure that the slave is not stretching the clock. You don't have to worry about all of that now, just be aware that those things need to be considered in the real world.

From here you should be able to add functions to output a data byte and return ACK or NAK from the slave, read a byte from slave and generate ACK/NAK etc. Those functions would include:
C:
unsigned char I2C_SendByte(unsigned char data);  // send 8 bits to the slave - also used to send slave address
unsigned char I2C_GetACK_NAK();  // clock once and read ACK or NAK after sending a byte. Return result
unsigned char I2C_ReadByte(char *c);  // clock SCL 8 times and read 8 bits from slave to c
unsigned char I2C_SendACK();  // clock once with SDA=0 to ACK slave
unsigned char I2C_SendNAK();  // clock once with SDA=1 to NAK slave
unsigned char I2C_SendRepeatStart();  // get the bus to idle then send repeat start condition
Note that they all return unsigned char as an error code. That's optional at this point.

Have fun!
 
Last edited:

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
??
If you are bit-banging I2C it's very similar to your bit-banging SPI. You need to change the output levels of SCL and SDA to generate the proper bus signals Here are a couple of examples to get you started:
@JohnInTX Where did you found following information in datasheet. Which page and section show. I read it many times i didn't find all this in the datasheet
Maybe I don't know how to get required information in datasheet well this is the time to understand how to learn to read a datasheet
C:
// Send I2C Start.  Bus must be idle i.e. SCL and SDA both high.  Return error if bus not idle.  Return OK if Start successfully sent
unsigned char (I2C_Start)
{
  SDA = 0;  // bus is idle, drop SDA while SCL is high
  delay_us();
  SCL = 0;  // then drop SCL
  return(OK);
}
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
Where did you found following information in datasheet. Which page and section show. I read it many times i didn't find all this in the datasheet
Maybe I don't know how to get required information in datasheet well this is the time to understand how to learn to read a datasheet
The I2C Start condition is described in the figure 3.1.4 in your post #24. In fact, they drew a box around the Start condition i.e. SDA goes 1->0 when SCL is high. Perhaps the confusion is that I also drop SCL after SDA in my I2C_Start routine. This is because after any Start, you send data (the slave address) and to do that, you start with SCL=0. Since in I2C, you can only change the data (SDA) when the clock is low it makes sense to set SCL=0 after the Start condition so that when the function returns SCL=0 and you can get on with the next thing which is clocking out the slave address.

Another way to look at it is to realize that unless you are generating Start or Stop, SCL always starts low then clocks low-high-low to do something. It makes sense to make SCL low as the normal value unless you are generating Start or Stop.

That's why my I2C_Start drops both lines.

If you are asking how I knew to write I2C_Start(), I just looked at what SCL and SDA had to do, as described in the figure, then wrote to the IO lines to do it. I specified a delay_us between them because as you can see from the diagram, SCL drops after SDA so.. delay. There are timing requirements that are described in the I2C spec as well as specific device datasheets. I didn't look them up in this case but just showed some delay so that you knew about that.

To learn to read datasheets, read datasheets - lots of them... thoroughly. After awhile, you'll realize that most are organized in a similar way. They start with an overview that tells you what the device does usually with helpful block diagrams. Then they proceed to pin descriptions then maybe application info with sample circuits. Details like how to talk to it with I2C, SPI etc. with detailed diagrams of bus signals. Most important. Towards the end, you'll find the boring but also important stuff, electrical and timing specifications. Finally, packages are described. For things like I2C and SPI, pull the actual I2C or SPI spec and read it completely. Then pick a device that uses I2C and/or SPI and read its datasheet to see how they use I2C/SPI to talk to the chip's functions, whatever they may be. Read read read. More than one engineer of my generation will tell you that while the grunts read Playboy in the bathroom, we took databooks and read those. That's how you learn.

Does that answer your question?
 
Last edited:

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
If you are asking how I knew to write I2C_Start(), I just looked at what SCL and SDA had to do, as described in the figure, then wrote to the IO lines to do it. I specified a delay_us between them because as you can see from the diagram, SCL drops after SDA so.. delay. There are timing requirements that are described in the I2C spec as well as specific device datasheets. I didn't look them up in this case but just showed some delay so that you knew about that.
@JohnInTX Thank you for your detail explanations. I appreciate your time and effort

I read it from the datasheet

1576157503086.png

Diagram shows if the 9th clock is high that means ACK signals and if 9th clock is low that means NACK signal

from #25
C:
unsigned char I2C_GetACK_NAK();  // clock once and read ACK or NAK after sending a byte. Return result
This function doesn't pass any parameter but it return only ACK or NAK.

This is only pesudo code I do not understand how to convert it into code

C:
#define ACK   1
#define NACK   0
unsigned char I2C_GetACK_NAK()
{
    if (9th clock == High)
         return ACK;
     else
         return NACK;
}
 

JohnInTX

Joined Jun 26, 2012
4,787
You misunderstand howACK/NAK is generated. After sending the 8 bit data, the master releases SDA and makes it an input. The slave now has control of SDA.

If the slave wants to ACK the byte just sent it will pull down SDA to make it 0.
If the slave wants to NAK the byte it will not drive SDA and SDA will be pulled up to 1 by the bus pull-up resistor.

In either case the master raises SCL and reads SDA to see if the slave is
ACK or NAK. After reading the master drops SCL to complete the operation. At this point the slave releases SDA and the master has control again.

The result is that each byte transferred takes 9 clocks, 8 for data and 1 for handshaking.

Note that in Fig 9 above the first two ACKs are shown correctly. The last one is technically not an ACK since SDA is indeterminate and should not be labeled as ACK.
 
Last edited:

BobaMosfet

Joined Jul 1, 2009
2,113
@JohnInTX Thank you for your detail explanations. I appreciate your time and effort

I read it from the datasheet

View attachment 194378

Diagram shows if the 9th clock is high that means ACK signals and if 9th clock is low that means NACK signal

from #25
C:
unsigned char I2C_GetACK_NAK();  // clock once and read ACK or NAK after sending a byte. Return result
This function doesn't pass any parameter but it return only ACK or NAK.

This is only pesudo code I do not understand how to convert it into code

C:
#define ACK   1
#define NACK   0
unsigned char I2C_GetACK_NAK()
{
    if (9th clock == High)
         return ACK;
     else
         return NACK;
}
:: blinks :: What you're saying is that can't take a concept and turn it into an algorithm in a programming language. We're not here to write your programs for you. If you want to write code for embedded, you need to learn how to program in C. And don't tell me you know how to program-- if you did, you wouldn't be asking us how to convert an idea into syntax.

If you need to learn how to program, then go spend a year or two learning how to do it. Like the rest of us did. Or, you might be able to hire someone who is willing to do it for you.
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
:: blinks :: What you're saying is that can't take a concept and turn it into an algorithm in a programming language. We're not here to write your programs for you. If you want to write code for embedded, you need to learn how to program in C. And don't tell me you know how to program-- if you did, you wouldn't be asking us how to convert an idea into syntax.

If you need to learn how to program, then go spend a year or two learning how to do it. Like the rest of us did. Or, you might be able to hire someone who is willing to do it for you.
Very bitter your words. I never said to write complete code for me. Did i say that? Please tell me when i said

You are right if i know i don't ask you. But i don't know so i asked question. Yes i know i need to learn a lot. I am trying my best I am reading books, datasheet and making flow chart
 
Last edited:

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
You misunderstand howACK/NAK is generated. After sending the 8 bit data, the master releases SDA and makes it an input. The slave now has control of SDA.

If the slave wants to ACK the byte just sent it will pull down SDA to make it 0.
If the slave wants to NAK the byte it will not drive SDA and SDA will be pulled up to 1 by the bus pull-up resistor.
Take a look at this function to generate ACK/NAK

C:
unsigned char I2C_GetACK_NAK()
{
    SDA = High; // sets SDA to an input
    SCL = high ; // sets SCL to high

   if (SDA == 0) //) reads SDA  If SDA is 0, the slave ACKnowledged and the master can send another byte.
      SCL = Low;
      return ACK;
   else                     .
      SCL = Low;
      return NACK;
}
 
Last edited:

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
That should work OK.
What does I2C_SendByte(unsigned char data); function return ? you created function in #25

Does it return something according to flow chart ? I don't think so

In a flow chart function only send slave address

I can make function that send bit one bye one ie we can send address of slave

C:
unsigned char I2C_SendByte(unsigned char Data);
{
  unsigned char ReceiveData = 0;

  for(int i = 0; i < 8; i++)  // 8 bits in a byte
  {
    if ((Data & 0x80) == 1) // sample the bit to output - the test is TRUE if the MSbit of DataOut is a 1
         SDA = 1;  // test TRUE, output 1
    else
         SDA = 0;  // test FALSE, output 0

    Data = Data << 1;          // shift the next bit to transmit up to MSbit of Send

    SCL = 1;

    delay();

    SCL = 0;    // drop clock to complete one bit
  }
 

JohnInTX

Joined Jun 26, 2012
4,787
What does I2C_SendByte(unsigned char data); function return ? you created function in #25
Does it return something according to flow chart ? I don't think so
In a flow chart function only send slave address
You are correct, it does not have to return anything. In #25, I noted that a complete I2C implementation would detect errors and report them. I showed an example in I2C_Start() where if the bus was not idle, it was an error. I used the function return value as an error report to show one way of error reporting. BUT, that's not necessary at this point so you are correct, I2C_SendByte does not have to return anything and can be declared as 'void'.

What does 'ReceiveData' do in your #34?
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
What does 'ReceiveData' do in your #34?
I just declare for error monitoring but I will remove it from that function

I wrote other function

unsigned char I2C_SendACK(); // clock once with SDA=0 to ACK slave
unsigned char I2C_SendNAK(); // clock once with SDA=1 to NAK slave
unsigned char I2C_SendRepeatStart(); // get the bus to idle then send repeat start condition

C:
unsigned char I2C_SendACK()  // clock once with SDA=0 to ACK slave
{
    SDA = High; // sets SDA to an input
    SCL = high ; // sets SCL to high

   if (SDA == 0) //) reads SDA  If SDA is 0, the slave ACKnowledged and the master can send another byte.
      SCL = Low;
      return ACK;
}


unsigned char I2C_SendACK()  // clock once with SDA=0 to ACK slave

{
    SDA = High; // sets SDA to an input
    SCL = high ; // sets SCL to high

   if (SDA == 1) //) reads SDA  If SDA is 1, the slave is not ACKnowledged and the master stop to send another byte.
      SCL = Low;
      return NACK;
}
unsigned char I2C_SendRepeatStart()
{
    SDA = 1;
    SCL = 1;
  
   if ((SCL == 0) || (SDA == 0))  // make sure bus is idle (SDA and SCL are 1), if not, return an error
    return(ERROR_BUS_BUSY);

  SDA = 0;  // bus is idle, drop SDA while SCL is high
  delay_us();
  SCL = 0;  // then drop SCL
  return(OK);
}
I hope it would also work according to flow chart
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
Well, it looks like block-copying without really understanding. I can tell these things..

If you are SENDING an ACK or NAK, why would you check SDA??

In RepeatStart, since you are setting SDA and SCL == 1, what is the purpose of this?
if ((SCL == 0) || (SDA == 0)) // make sure bus is idle (SDA and SCL are 1), if not, return an error
return(ERROR_BUS_BUSY);
 

JohnInTX

Joined Jun 26, 2012
4,787
C:
unsigned char I2C_SendRepeatStart()
{
    SDA = 1;
    SCL = 1;
 
   if ((SCL == 0) || (SDA == 0))  // make sure bus is idle (SDA and SCL are 1), if not, return an error
    return(ERROR_BUS_BUSY);

  SDA = 0;  // bus is idle, drop SDA while SCL is high
  delay_us();
  SCL = 0;  // then drop SCL
  return(OK);
}
You told me that function in post #25. There is no delay and restart block in flow chart but we need in program so I followed your advice
But that's my point about copying code from me or anyone but not understanding the reasons for doing something. You can't do this stuff without a good understanding of what is necessary to do and why. You can't use what someone tells you without:

1) knowing exactly why they said what they said
I said it was an example of error detection and reporting. I also said not to worry about that right now. It shouldn't be there.

2) knowing that it's correct
Take a look at the code. Is it correct?

3) knowing that it applies to what you are doing
If you are not implementing error checking, you don't need it. Even then, there is a difference between Start and RepeatStart that affects whether you need to check SCL and SDA at the beginning. What is the difference?


If you are SENDING an ACK or NAK, why would you check SDA??
Good question. What do you think?
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
But that's my point about copying code from me or anyone but not understanding the reasons for doing something. You can't do this stuff without a good understanding of what is necessary to do and why. You can't use what someone tells you without:
This function will work to send restart sequence on i2c bus
C:
void I2C_SendRepeatStart()
{
    SDA = 1;
    SCL = 1;
 
    SDA = 0;  // bus is idle, drop SDA while SCL is high
    delay_us();
    SCL = 0;  // then drop SCL
}
If you are SENDING an ACK or NAK, why would you check SDA??
When writing to the slave, the master send 8 bits of data. slave generates the ACK/NAK master sends one more clock to get an ACK or NAK from the slave.

If SDA was 0, at 9th clock the slave Acknowledged and the master can send another byte.
If SDA was 1, at 9th clock the slave negative Acknowledged. The master must stop sending data
 
Top