I2C communication question

BobaMosfet

Joined Jul 1, 2009
2,211
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
My words aren't bitter, they are incredulous. Because even with all the help others are giving you- and they've coded it and explained it several times- it still doesn't seem to be enough. I think i2c is beyond your skill level, and you need to work at something else first, and then come back to it. That's not mean, it's just being fair.
 

JohnInTX

Joined Jun 26, 2012
4,787
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.
I think your misunderstanding is due to terminology. After the master SENDS an 8 bit byte, it generates the 9th clock then RECEIVES the ACK/NAK from the slave. So the routine should be named I2C_GetACK_NAK(), not I2C_SendACK/NAK.

So when the master is sending data to the slave, you have something like:

for(number of bytes)
I2C_SendByte(data);
if((I2C_GetACK_NAK) == ACK)
continue with next byte
else
quit and send stoP.
}
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
I think your misunderstanding is due to terminology.
I have written the routines that required for the flow chart

Does it make sense ? If all this is correct then okay I have a doubt that I want to ask you

C:
#define SDA P0_0
#define SCL P0_1

// Send I2C start.
void I2C_Start (void)
{
   SDA = 1 // SDA and SCL are 1
   SCL = 1 

  SDA = 0;  // drop SDA while SCL is high
  delay_us();
  SCL = 0;  // then drop SCL

}

// Send I2C stoP.
void I2C_Stop (void)
{
  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.
}

void I2C_SendByte(unsigned char Data);
{

  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
  }

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

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

//send ack
void SendAck()
{
    SDA = 0;
    SCL = 1;
    SCL = 0;
    SDA = 1;
}

//send nak
void SendNak()
{
    SDA = 1;
    SCL = 1;
    SCL = 0;
    SDA = 1;
}
If the data line changes state while the clock line is high, it is either a START condition or a STOP condition. A START condition occurs when the clock line is high, and the data line goes from high to low. A STOP condition occurs when the clock line is high, and the data line goes from low to high.

After the master issues a START condition, the master sends a byte This byte is called the address byte. Each device on an I 2C bus has a unique 7-bit address to which it responds. The master sends an address in the address byte, together with a bit that indicates whether the master wishes to read from or write to the slave device. Every byte (address and data) transmitted on the I2C bus is acknowledged with an acknowledge bit
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
I have written the routines that required for the flow chart
Does it make sense ? If all this is correct then okay I have a doubt that I want to ask you
Makes sense but there are problems. In several of your new routines, the clock pulse is going to be very short. Why and what can you do to fix that?

We are doing this backwards.. flow charting is usually done before coding. But the pseudo-code you‘ve done can be an effective tool as well. The important thing is to use a method that works for you and helps you through the design process. Flowcharts, tables, and pseudo-code all can be useful IF you take the time to understand them, use them to validate your thinking, use them to solve your problem and then use them to guide your coding. Like any other tool, those who take the time to master its use will have the most success.
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
I agree, I should have done all this in the flow chart I think I should write code to main function Because i think i have the functions for all blocks and And I can call in main function
We are doing this backwards.. flow charting is usually done before coding. But the pseudo-code you‘ve done can be an effective tool as well.
I agree, I should have done all this in the flow chart
I think I should write pseudo-code Because i think i have the functions for all blocks

C:
//This function provide the delay which is used in clock generation.

void delay-us(unsigned int d)
{
    unsigned int i;

    for( i = 0; i < d; i++);
}

// This function performs the start operation
void I2C_Start (void)
{
   SDA = 1 // SDA and SCL are 1
   SCL = 1

  SDA = 0;  // drop SDA while SCL is high
  delay_us();
  SCL = 0;  // then drop SCL

}

// This function performs the stop operation.
void I2C_Stop (void)
{
  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.
}


//This function use to generate  ACK/NACK
  unsigned char I2C_GetACK_NAK()
{
    SDA = 1; // sets SDA to an input
    SCL = 1 ; // sets SCL to high

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

//This function use to send the acknoledgement(ACK)

void SendAck()
{
    SDA = 0;
    SCL = 1;
    delay_us();
    SCL = 0;
    SDA = 1;
}

//This function use to send the Non-acknoledgement(NACK) bit
void SendNak()
{
    SDA = 1;
    SCL = 1;
    delay_us();
    SCL = 0;
    SDA = 1;
}

//This function use to send signle byte
void I2C_SendByte(unsigned char Data);
{

  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
  }

  //This function use to read the single data byte
unsigned char ReadByte(void)  // This function returns one byte
{
   unsigned char Receive = 0;  // the receive register

  for(int i = 0; i < 8; i++)  // 8 bits in a byte
  {

    Receive = Receive << 1;

    SCK = 1;

    if(SDA == 1)
     Receive |= 0x01;  // SDA == 1, set the LS bit in Receive
  }
  return Receive;        // Return the received data
}
int main (void)
{

   I2C_Start ();

   I2C_SendByte(SlaveAddress); // suppose slave address is 10000000

   GetACK_NAK = unsigned char I2C_GetACK_NAK();

    return 0;
}
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
Now I have stopped here. Please take a look at post #48 and give me some direction
I have made the necessary blocks of the flow chart But I'm struggling to convert it into to code
???
I'm confused. In #48, you show the basic code for all of the I2C functions that you need to make complete messages. You don't have a flow chart for those but you don't need one at this point.

You can construct complete I2C messages from the functions that you have. A flow chart showing you calling the functions in the proper sequence to read and write and I2C device might be helpful. Use the figure in @BobaMosfet 's post here as a reference.

But before you can proceed, you have to state exactly what you want to do - to us and most importantly to you. What do you want the next step to be?
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
You can construct complete I2C messages from the functions that you have.

you have to state exactly what you want to do - to us and most importantly to you. What do you want the next step to be?
I want to construct complete I2C messages from the functions that I have.

C:
int main (void)
{

   I2C_Start ();

   I2C_SendByte(SlaveAddress); // suppose slave address is 10000000

   GetACK_NAK = I2C_GetACK_NAK();  //call function
  
   if ( GetACK_NAK == 0)
       I2C_SendByte(unsigned char Data);//suppose slave Data is 1010 0101
       GetACK_NAK =  I2C_GetACK_NAK(); //call function   What to do next after this line ?
  
   else
       I2C_Stop();

    return 0;
}
I send Data= 1010 0101 to slave device Now i need to check its ACK/NAK
 

JohnInTX

Joined Jun 26, 2012
4,787
I want to construct complete I2C messages from the functions that I have.
So do that. But first define the message. How many bytes? Where are they stored? You will need some kind of loop that counts the bytes sent and ACKs received. When the loop is done OR you get a NAK at any time end the loop and send stop.

You have to have all of that figured out before you can proceed with coding. A flow chart would help. You should review the SPI flow chart that you made. The basic operations are similar.
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
So do that. But first define the message. How many bytes? Where are they stored? You will need some kind of loop that counts the bytes sent and ACKs received. When the loop is done OR you get a NAK at any time end the loop and send stop.
I have downloaded a datasheet for ds1307 so it works on the I2C. I have all i2c function so first I want to write data to 1307.

@JohnInTX Is my main function correct according to the flow chart to write data to ds1307?

C:
#include<stdio.h>

#define Slave_Address   0xD0         // datasheet Page 12 , 0xD0 = 11010000
#define Rgister_Seconds 0x00     //datasheet page 8
#define ACK   0
#define NACK  1

//This function use to send signle byte
void I2C_SendByte(unsigned int Data);
{
  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
  }

//This function use to generate  ACK/NACK

  unsigned char I2C_GetACK_NAK()
{
    SDA = 1; // sets SDA to an input
    SCL = 1 ; // sets SCL to high
   if (SDA == 0) //) reads SDA  If SDA is 0, the slave ACKnowledged and the master can send another byte.
      SCL = 0;
      return ACK;
   else                     .
      SCL = 0;
      return NACK;
}

// This function performs the stop operation.
void I2C_Stop (void)
{
  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.
}

int main ()
{
   /* start condition */
    I2CStart();     

   /* Send DS1307 slave address with write operation */
   I2C_SendByte(Slave_Address);

   GetACK_NAK = I2C_GetACK_NAK();

   if(GetACK_NAK == ACK)
   {     
        /* Send DS1307 slave address with write operation */
        I2C_SendByte(Rgister_Seconds);
        GetACK_NAK = I2C_GetACK_NAK();
      
        if(GetACK_NAK == ACK)
        {
          
        }
   }
      
    I2C_Stop();

   return 0;

}
 

Attachments

JohnInTX

Joined Jun 26, 2012
4,787
I want to write data to 1307.
Is my main function correct according to the flow chart to write data to ds1307?
No, it isn't.

First, there is no flow chart.
Second, it is incomplete. It only sends the slave address, no data.

But first define the message. How many bytes? Where are they stored? You will need some kind of loop that counts the bytes sent and ACKs received. When the loop is done OR you get a NAK at any time end the loop and send stop.
You have to have all of that figured out before you can proceed with coding.
You must decide the answers to these questions so that you know what is required. You didn't answer them so you don't know how to proceed.
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
No, it isn't.
First, there is no flow chart.
Second, it is incomplete. It only sends the slave address, no data.
I wanted to know from you am I sending the address of Slave in the right way Thank you so much for the confirmation. I know code is not complete
I made a flow chart in post 16. I am taking that as reference.

I am chasing a big goal, so I have divided my work to small things That's why I sent the address first. I am trying my best attempt

But first define the message. How many bytes? Where are they stored?
You must decide the answers to these questions so that you know what is required. You didn't answer them so you don't know how to proceed.
I want to send the address 00h to07h so total eight bytes

1576679112241.png

You noticed I have sent address of ds1307 then I tried to send the address of seconds register after that I lost

You will need some kind of loop that counts the bytes sent and ACKs received. When the loop is done OR you get a NAK at any time end the loop and send stop.
Basic idea to store bytes
C:
#include<stdio.h>
int main (void)
{
    int i;
    int array[8];
    for(i=0; i < 8; i++)
    {
        printf("store bytes : ", array[i]);
        scanf("%d",&array[i]);
    }
    return 0;
}
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
No. The task at this point is to complete writing bytes to an I2C slave, not to get them from a terminal.

An array IS a good way to store the bytes to send but for now, assume the array is loaded and you need to send the bytes. It does not matter if you are sending to a DS1307 or an EEPROM, the goal here is:
Implement a function called WriteI2C() that writes an arbitrary number of bytes to an I2C slave and returns OK or ERROR. Do this by using the smaller I2C functions in sequence to conform to the I2C protocol.

Don't worry about anything else.

Note that we are building a bigger, general purpose function using the smaller I2C functions. That's how it's done. Properly implemented, WriteI2C will work with any slave address, with data stored anywhere. That means that you only have to write it ONCE and it will talk to anything.


What information do you need to write such a function?
You need to know:
  • The slave's I2C address
  • The number of bytes to write (not counting the slave address)
  • Where the bytes are stored
  • How to return OK or ERROR. An example of an ERROR would be a NAK after the slave address, indicating no slave at that address.
So, WriteI2C could have a prototype like this:
unsigned char WriteI2C(unsigned char SlaveAddress, unsigned int ByteCount, unsigned char *BytesToSend);
The return value indicates whether or not the write was successful.

Does that cover all of the information we need to write to an I2C slave?

How would you implement such a function?
Start with a flow-chart or pseudo-code. Refer to the I2C specification and previous posts. Think about each step as you proceed.
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
unsigned char WriteI2C(unsigned char SlaveAddress, unsigned int ByteCount, unsigned char *BytesToSend);
The return value indicates whether or not the write was successful.
okay I am showing you only function. remember it is not a complete code

Is that what you advised ? I am passing address of ds1307s register, Data to write slave and ByteCount

C:
unsigned char WriteI2C(unsigned char SlaveAddress, unsigned int ByteCount, unsigned char *BytesToSend)
{
    I2CStart();

    I2C_SendByte(Slave_Address);

    GetACK_NAK = I2C_GetACK_NAK();

    if(GetACK_NAK == ACK)
    {
       for ( ByteCount= 0; ByteCount < 8; ByteCount++)
       {

            *(BytesToSend + ByteCount));
       
            /* check for last byte */
            if(ByteCount == 7)
               SendNAk()    /* NAK if last byte */
               return Failure
            else          
              SendACK();    /* ACK for all bytes */
              return Success
       
       }
    }
    I2C_Stop();
       
}
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
That's what I advised but there are problems with the C here:
for ( ByteCount= 0; ByteCount < 8; ByteCount++)
*(BytesToSend + ByteCount));
You are not actually sending any data bytes.

You are also mixing up I2C write and I2C read when it comes to ACK/NAK.
You are not always sending stoP.

This is all basic C and code flow. Take your time and review each step.
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
That's what I advised but there are problems with the C here:
for ( ByteCount= 0; ByteCount < 8; ByteCount++)
*(BytesToSend + ByteCount));
You are not actually sending any data bytes.
Good Morning, I said i am showing you only a one function
I had a following idea in my mind
C:
#include <stdio.h> 
void fun(int *pointer)
{
   int i;
   for ( i = 0; i < 8; i++)
       printf("\n %d store at %p ",*(pointer+i),(pointer+i));
}  
int main()
{
    int i;
   int array[] = {1, 2, 3, 4, 5, 6, 7, 8};
    fun(array);  
    return 0;
}
1 store at 0061FF10
2 store at 0061FF14
3 store at 0061FF18
4 store at 0061FF1C
5 store at 0061FF20
6 store at 0061FF24
7 store at 0061FF28
8 store at 0061FF2C
 

Thread Starter

Gajyamadake

Joined Oct 9, 2019
310
You are also mixing up I2C write and I2C read when it comes to ACK/NAK.
You are not always sending stoP.
I don't understand what that means
C:
 /* check for last byte */
            if(ByteCount == 7)
               SendNAk()    /* NAK if last byte */  Is this line wrong ?
               I2C_Stop();
               return Failure
            else        
              SendACK();    /* ACK for all bytes */    Is this line wrong ?
              return Success
 
Last edited:
Top