I2C communication question

BobaMosfet

Joined Jul 1, 2009
2,211
@BobaMosfet I appreciated your advice but what's wrong with current approach. I am new boy in embedded world. Things are not easy for me but I am trying my best. I have learned so much in this thread. And I am still trying to understand some things that can be learned myself by spending a some time. I wanted to come back by figuring out some facts by myself. And i am doing thats why I am taking time


you know why I am fan of @JohnInTX because look at his passion He helped me a lot. And if I made a mistake on somewhere , he never gives negative thoughts. he is motivating me to keep working
I'm not giving you negative thoughts. I'm trying to get you to align your grasp with your reach. So you succeed. By picking an MCU to work with, you start moving things in your favor because then you give everybody trying to help you, the same reference to work from- testable working examples, etc. Concrete things that can be described and shown.

God Bless JohnInTX for his gracious and benevolent help, the man is a Saint- but when he steps away and the training wheels are gone, you're still not going to be able to write it because every MCU is different and you're going to wonder how you actually do an ACK or NAK on the i2c bus. Some MCUs don't even have i2c, so you'd have to do it all yourself? But until you pick an MCU, we don't know which one to provide examples for.

This is sounding like your very first project in embedded, and if it is, this is way over your head.

Help us help you. Please.

Seriously. On an ATMEGA MCU, you can do non-multi-master i2c/twi in just 3 functions. Most people never need multi-master (but it's easy to add).
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
I'm not giving you negative thoughts. I'm trying to get you to align your grasp with your reach. So you succeed. By picking an MCU to work with, you start moving things in your favor because then you give everybody trying to help you, the same reference to work from- testable working examples, etc. Concrete things that can be described and shown.

God Bless JohnInTX for his gracious and benevolent help, the man is a Saint- but when he steps away and the training wheels are gone, you're still not going to be able to write it because every MCU is different and you're going to wonder how you actually do an ACK or NAK on the i2c bus. Some MCUs don't even have i2c, so you'd have to do it all yourself? But until you pick an MCU, we don't know which one to provide examples for.

This is sounding like your very first project in embedded, and if it is, this is way over your head.
I am not saying anything wrong about you. I have not rejected your advice. I am following top to bottom design approach in which you create your flow chart, then convert it into code and then test the code on micro-controller. I would test code on micro-controller at the end.

I have done this before, I have also tested some sample code on PIC16F877A. I created flow chart for timer interrupt then converted flow chart into code and finally tested on PiC16F877A.
 

JohnInTX

Joined Jun 26, 2012
4,787
I have matched code with flow chart and I have written code as shown in flow chart as per my understanding
Looks like it should work. You covered all of the points to write to I2C. Good work!

There are some differences between your code and the flow chart, though. You exit the C routine in 3 places and the flow chart shows only one place. I've modified your routine to show how I would do it to have only one exit point. Note that while yours works, mine is easier to maintain with the single exit point. If you had to modify how the routine terminated, or added something to the routine that also might need to terminate, you would have to modify 3 or more places. It's easy to miss something and have strange behavior. It's always better to arrange things so that each action happens in only one place.

Here's my version of WriteI2C. Note the many comments. Always important so that you know what you are trying to do.
C:
// WriteI2C
// Sends ByteCount bytes to I2C slave at SlaveAddr from character data at DataBufferP
// If called with ByteCount = 0, it just sees if there is a slave at that address
// Returns:
// ACK if all bytes sent successfully i.e. valid slave and no NAKs
// NAK if no slave at SlaveAddr or other error - slave returned NAK.

unsigned char WriteI2C (unsigned char SlaveAddr, int ByteCount,  unsigned char *DataBufferP)
{
    unsigned char ReturnValue;      // temporary storage for status of SendByte

    I2C_Start();                    // send Start
    I2C_SendByte(SlaveAddr & 0xFE)  // send slave address with R/W- forced to 0 for write
    ReturnValue = I2C_GetACK_NAK(); // get ACK or NAK.  ACKs only if slave present.
    if(ReturnValue == ACK){         // if ACK, slave is present so.. 
        while (ByteCount > 0){      // if there are still bytes to send..
            I2C_SendByte(*DataBufferP) // send one byte at passed pointer
            ReturnValue = I2C_GetACK_NAK(); // get ACK or NAK and store in ReturnValue
            if(ReturnValue == NAK)          // if NAK, we are done, even if we haven't sent all bytes
                break;                      // break exits the while loop, ReturnValue will be NAK
                                            // else, return from SendByte was ACK so..
            ++DataBufferP;                  // point to next character in buffer
            --ByteCount;                    // decrement bytes remaining count
        }// while send byte loop         
    }// if slave present and ACKed the slave address
  
    I2C_Stop();                             // exit from any point.  Always send stoP then..
    return ReturnValue;                     // ReturnValue will be ACK for success, NAK on any problem
}
So after much work, you've taken a flow-chart (mine) and done a reasonable job of showing how it could be coded. If you are happy, I am happy. I think you could use some more work in generating a flow chart to solve a problem then convert to code but it's up to you. A logical place to start would be to implement ReadI2C or some of that LCD stuff in your other thread. But as @BobaMosfet says, eventually you have to get this coded onto a real chip. If you want to explore some of that go ahead and start a new thread for that. At the very least things like SCL=1 and SDA = 0 will require chip-specific code to actually change the I2C levels e.g. on a PIC, you have to flip bits in the TRIS registers. It's not as simple as we've shown here.

In any case,
Have fun!
 
Last edited:

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
So after much work, you've taken a flow-chart (mine) and done a reasonable job of showing how it could be coded. If you are happy, I am happy. I think you could use some more work in generating a flow chart to solve a problem then convert to code but it's up to you. A logical place to start would be to implement ReadI2C or some of that LCD stuff in your other thread.
Have fun!
Hi JohnInTX

I have worked on the read function For this I have also made a flow chart

Code:
unsigned char ReadI2C (unsigned char SlaveAddrR, int ByteCount,  unsigned char *c)
Please look at following flow chart
 

Attachments

JohnInTX

Joined Jun 26, 2012
4,787
Good first effort. Some problems, though.
1: in the read loop. does I2C_sendACK() return anything? If so, why? If not, how can you assign a value from it?
2: does ++*c do what you expect?
3: Call it with ByteCount==0. Does it do what it should? What could you do to fix that?
 
Last edited:

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
Good first effort. Some problems, though.
1: in the read loop. does I2C_sendACK() return anything? If so, why? If not, how can you assign a value from it?
2: does ++*c do what you expect?
3: Call it with ByteCount==0. Does it do what it should? What could you do to fix that?
It should be look like something
1577252278235.png
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
Good first effort. Some problems, though.
1: in the read loop. does I2C_sendACK() return anything? If so, why? If not, how can you assign a value from it?
2: does ++*c do what you expect?
3: Call it with ByteCount==0. Does it do what it should? What could you do to fix that?
OK.
#1 is fixed.
#2 not fixed - you never increment the pointer to the next spot in the buffer
#3 not fixed - if ByteCount is 0 when you call ReadI2C(), it sends a NAK right after getting ACK from sending the slave address. That will break the I2C protocol.
Think about this:
When receiving, the master clocks in a data byte with 8 clocks, the slave controls SDA. On the 9th clock, the slave expects the master to take control of SDA, set it low or high (for ACK or NAK) and issue the 9th clock. When you enter your loop with ByteCount == 0, you haven't sent the 8 clocks to receive the first byte so the slave is not expecting to see ACK or NAK. In fact, the slave will take what you think is the ACK/NAK and treat it as the first bit of a data byte. That will break the I2C protocol and cause problems.
Think some more: some I2C devices expect a NAK to terminate a read. Can you ever send a NAK if you don't read any bytes? So, can the ByteCount in I2C_ReadByte EVER be 0? What can you do to ensure that you never start reading with a byte count of 0?

#4 (new) - If I2C_ReadByte() has this prototype:
unsigned char I2C_ReadByte(char *c); // clock SCL 8 times and read 8 bits from slave to c
then you are calling it with an incorrect parameter.

Flow chart: your flow chart should show the prototype for I2C_ReadByte so that we are all on the same page.

Progress!
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
OK.
#1 is fixed.
#2 not fixed - you never increment the pointer to the next spot in the buffer
#3 not fixed - if ByteCount is 0 when you call ReadI2C(), it sends a NAK right after getting ACK from sending the slave address. That will break the I2C protocol.
one step back on basics

Code:
#include<stdio.h>
int main ()
{
  int i, buffer[10], Count;
 
  printf("count Size :  " );
  scanf ("%d", &Count ); 
 
  for (i = 0; i < Count; i++){
        printf("Read byte : " );
        scanf("%d", &buffer[i]);
  }
  return 0;
}
count Size : 5
Read byte : 1
Read byte : 2
Read byte : 3
Read byte : 4
Read byte : 5

This is basic way I used to read the array element in plain c language

Should i use above logic in my ReadI2C function to read each byte?
 

JohnInTX

Joined Jun 26, 2012
4,787
Keep the original flow chart and fix the problems with it. Don't make a new one. You have introduced NEW problems in #90 - for example, now you are back to a fixed byte count instead of using the passed ByteCount. Again, slow down and fix the problems one by one. Do not just guess, identify each problem and fix it.
I've annotated your original flow chart. Fix EACH problem one by one.

EDIT: I've added a few things. Be sure to see the latest PDF.
It would help you if you wrote down what the function should do - not just 'It reads I2C' but how it does it. For example:

unsigned char I2C_ReadBytes(unsigned char SlaveAddress, unsigned int ByteCount, unsigned char *C)

Reads the number of bytes indicated by ByteCount from a slave at SlaveAddress into a character array pointed to by C.
Returns ACK and buffer loaded if successful.
Returns NAK if no slave at address or called with ByteCount==0.


Then review your flow chart to see if it actually does that.
 

Attachments

Last edited:

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
Not even close.
See #91
done some modification and should be work
1577357382059.png

I read array something like this

Code:
#include <stdio.h>
void function( int value[9]){
    int i;
    for( i = 0; i < 9; i++){
        value[i];
        printf("\n value %d ", value[i]);
    }
}

int main(){
    unsigned int RegData[]={0x00, 0, 1, 2, 3, 4, 5, 6, 7};
    function (RegData);
    return 0;
}
result with proof
value 0
value 0
value 1
value 2
value 3
value 4
value 5
value 6
value 7
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
Again, not even close.
See #91
I can read the address of register and the value of register at address. I did not create a flow chart because I don't understand where I am wrong

I have tested c code to read array data by its address
Code:
#include <stdio.h>

void WriteI2C ( int ByteCount,  unsigned int *DataBufferP)
{
    int i;
    for( i = 0; i < 9; i++){  
       printf("\n read address %p \n value store at this address %d", (DataBufferP + i), *(DataBufferP + i));
     
    }
}

int main(){
    unsigned int ByteCount = 9;
   
    unsigned int RegData[]={0x00, 0, 1, 2, 3, 4, 5, 6, 7};
    WriteI2C(ByteCount, RegData);
    return 0;
}
 

JohnInTX

Joined Jun 26, 2012
4,787
I have tested c code to read array data by its address
Yes, I can see that. You've posted your array code several times, each time a bit different and none of them applicable to what we were doing in I2C.
The topic of this thread is General I2C Communications Flowchart and then moving on to C code. Posting unrelated routines is not going to help you.

This is the last time I want to go through this so in detail:
In #84, you presented your first try at an I2C Read function flow chart.
In #85, I described some problems with that first flow chart. I provided a short list of 3 things to look at:
Good first effort. Some problems, though.
1: in the read loop. does I2C_sendACK() return anything? If so, why? If not, how can you assign a value from it?
2: does ++*c do what you expect?
3: Call it with ByteCount==0. Does it do what it should? What could you do to fix that?
Since then, you have provided several NEW flow charts that pretty much ignore the list of errors I provided and introduce new ones. I keep referring you to #91 which has ALL of the information you need to fix the errors in your ORIGINAL I2C Read flow chart INCLUDING a .PDF of your original flow chart with annotations and arrows to the problem areas:
Keep the original flow chart and fix the problems with it. Don't make a new one. You have introduced NEW problems in #90 - for example, now you are back to a fixed byte count instead of using the passed ByteCount. Again, slow down and fix the problems one by one. Do not just guess, identify each problem and fix it.
I've annotated your original flow chart. Fix EACH problem one by one.

EDIT: I've added a few things. Be sure to see the latest PDF
https://forum.allaboutcircuits.com/attachments/read-function-problems-noted-pdf.195296/

It would help you if you wrote down what the function should do - not just 'It reads I2C' but how it does it. For example:

unsigned char I2C_ReadBytes(unsigned char SlaveAddress, unsigned int ByteCount, unsigned char *C)
Reads the number of bytes indicated by ByteCount from a slave at SlaveAddress into a character array pointed to by C.
Returns ACK and buffer loaded if successful.
Returns NAK if no slave at address or called with ByteCount==0.

Then review your flow chart to see if it actually does that.
I would recommend that you:
  • Review the I2C spec to see what you have to do to Read I2C.
  • Review the smaller I2C functions to see how to use them. It would be a good exercise to take each one and write a comment that said what the function does, what parameters it takes and what it returns.
  • Review the work we did on WriteI2C - the structure is similar.
  • Review the annotated flow chart I posted. Start at the first problem noted and fix it. Then go to the next one and fix that. Do NOT write a new flow chart. Do NOT post any more array code. When you have fixed ALL of the problems I noted, trace the flow with a pencil on a printed page and see where it goes. Does it conform to the I2C spec and protocol?
  • If you have questions on any part of the annotated flow char I posted, ask them here.
  • If you wish not to follow this process, let me know and I'll step aside.

Focus and have fun.
That's all the help I can offer.
 
Last edited:

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
Since then, you have provided several NEW flow charts that pretty much ignore the list of errors I provided and introduce new ones. I keep referring you to #91 which has ALL of the information you need to fix the errors in your ORIGINAL I2C Read flow chart INCLUDING a .PDF of your original flow chart with annotations and arrows to the problem areas:
I have fixed my old flow chart. And I think that's right. Because i don't see anything wrong with it

1577898547004.png
sorry for poor quality of chart because I did not save the design. And I lost all my work. I only had one image left of the chart so I only posted it
 

JohnInTX

Joined Jun 26, 2012
4,787
I have fixed my old flow chart. And I think that's right. Because i don't see anything wrong with it
No, it is not right.
I have a question: Do you know how to trace through a flow chart to see what the code flow is? I ask because there are many problems here, some very obvious that you should have picked up when drawing it. Other problems would become obvious if you went through the flow chart step by step and followed the flow.

I was typing a large reply after analyzing your flow chart but this question needs to be answered first.

EDIT: Do this.
Labeled Flow Chart.PDF is your flow chart with each step numbered. There are several paths through the flow chart and I want you to describe them by number. For example,
Call the routine with ByteCount == 0
The answer is:
1 10 11 12
meaning the flow goes as shown in the example PDF.

Now, you post the flow for these conditions:
ByteCount > 0 but you get NAK in #5
and:
ByteCount == 3 and you get ACK in #5

Do the results for ANY of these 3 flows do proper I2C? Refer back to the spec or examples in this thread if you need to.

Good luck!
 

Attachments

Last edited:

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
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_ReadByte(char *c);  // clock SCL 8 times and read 8 bits from slave to c
you are passing pointer c. What it is ? Is it salve address or Is it register address?

If I am not wrong it is register address so we can read different register at every function call
 
Top