Transfer and receive data on the i2c bus

Thread Starter

Sparsh45

Joined Dec 6, 2021
143
I implemented multi-byte read and write in 170 lines of ARM assembler.
I've written an algorithm to send multiple bytes to DS1307. This code is pseudocode It is not written for any specific micro and compiler It show my plan to send data multiple byte to DS1307

I want someone to review code and give me a feedback on my logic what should I improve

C:
#define ACK                           0
#define NACK                          1

#define    Slave_Devive_Address       0xD0
#define    Slave_Word_Address         0x00


#define Seconds                       00
#define Minutes                       00
#define Hours                         1
#define Day                           2
#define Date                          21
#define Month                         12
#define Year                          20

// SDA and SCL should be High in ideal condition
void Ideal ()
{
    SCL = High ;
    SDA = High ;     
}

// Function to start I2C communication
void I2C_START ()
{
   Ideal ();
   SDA = High ;
   SCL = High;
   SDA = Low ;
   nop();
   SCL = Low ;
}

// Function to stop I2C communication
void I2C_STOP ()
{
   SDA = Low ;
   SCL == High;
   SDA = High ;
   nop();
   SCL = Low ;
}

// Function to send one byte to slave device
void I2C_Send( uint8_t Byte)
{
    uint8_t i;

   for( i = 0; i < 8; i++)
   {
        if ( (Byte & 0x80 ) == 1)
        {
            SDA = High ; // Set SDA to High
        {
            nop();
        }
    
        else
            SDA = Low; // Set SDA to Low
        }
  
        Byte = (Byte << 1);  // shift the next bit

        SCL = High ;
        nop();
        SCL = Low;
    }
}

//Function to get ACK/ NACK from slave
bit Get_ACK_NACK ()
{
    SCL = High;
    if ( SDA == Low)
         return ACK;
    else
        return NACK;
}

// Write data bytes to DS1307
// send Slave address
// check if slave acknowledged address
// Yes send next byte (address of seconds)
// No, call the I2C_STOP function and Stop communication
// Loop for sending multple bytes value 
bit I2C_Write_Device ( uint8_t Slave_Address, uint8_t Register_Addres, uint8_t * Register_Data, Byte_Size )
{
     uint8_t i = 0;
  
     I2C_START ();
  
     I2C_Send(Slave_Address);
  
     if ( Get_ACK_NACK() == NACK_Received )
         I2C_STOP ();
         return 1;
      
     I2C_Send( Register_Addres );
     if ( Get_ACK_NACK() == NACK_Received )
         I2C_STOP ();
         return 2;
      
     for ( i = 0; i < size; i++)
     {
        I2C_Send(*Register_Data );
        if ( Get_ACK_NACK() == NACK_Received )
           I2C_STOP ();
           return 3;
        *Register_Data++;
     }
  
     I2C_STOP ();
  
     return 0;
}

void main ()
{
    uint8_t Trnasfer_Bytes_Result;
  
    uint8_t Register_Bytes [ ] = {Year, Month, Date, Day, Hours, Minutes, Seconds};
    uint8_t Byte_Size;
  
    // Pass slave address, Pass frist address of Ds1307 register
    // Pass register value that will store at seconds register of DS1307, pass the size How many bytes you want to write   
    Trnasfer_Bytes_Result =  ( Slave_Devive_Address, Slave_Word_Address, Register_Bytes, Byte_Size );
  
}
@Ian0
 

dcbingaman

Joined Jun 30, 2021
1,065
I've written an algorithm to send multiple bytes to DS1307. This code is pseudocode It is not written for any specific micro and compiler It show my plan to send data multiple byte to DS1307

I want someone to review code and give me a feedback on my logic what should I improve

C:
#define ACK                           0
#define NACK                          1

#define    Slave_Devive_Address       0xD0
#define    Slave_Word_Address         0x00


#define Seconds                       00
#define Minutes                       00
#define Hours                         1
#define Day                           2
#define Date                          21
#define Month                         12
#define Year                          20

// SDA and SCL should be High in ideal condition
void Ideal ()
{
    SCL = High ;
    SDA = High ;   
}

// Function to start I2C communication
void I2C_START ()
{
   Ideal ();
   SDA = High ;
   SCL = High;
   SDA = Low ;
   nop();
   SCL = Low ;
}

// Function to stop I2C communication
void I2C_STOP ()
{
   SDA = Low ;
   SCL == High;
   SDA = High ;
   nop();
   SCL = Low ;
}

// Function to send one byte to slave device
void I2C_Send( uint8_t Byte)
{
    uint8_t i;

   for( i = 0; i < 8; i++)
   {
        if ( (Byte & 0x80 ) == 1)
        {
            SDA = High ; // Set SDA to High
        {
            nop();
        }
  
        else
            SDA = Low; // Set SDA to Low
        }

        Byte = (Byte << 1);  // shift the next bit

        SCL = High ;
        nop();
        SCL = Low;
    }
}

//Function to get ACK/ NACK from slave
bit Get_ACK_NACK ()
{
    SCL = High;
    if ( SDA == Low)
         return ACK;
    else
        return NACK;
}

// Write data bytes to DS1307
// send Slave address
// check if slave acknowledged address
// Yes send next byte (address of seconds)
// No, call the I2C_STOP function and Stop communication
// Loop for sending multple bytes value
bit I2C_Write_Device ( uint8_t Slave_Address, uint8_t Register_Addres, uint8_t * Register_Data, Byte_Size )
{
     uint8_t i = 0;

     I2C_START ();

     I2C_Send(Slave_Address);

     if ( Get_ACK_NACK() == NACK_Received )
         I2C_STOP ();
         return 1;
    
     I2C_Send( Register_Addres );
     if ( Get_ACK_NACK() == NACK_Received )
         I2C_STOP ();
         return 2;
    
     for ( i = 0; i < size; i++)
     {
        I2C_Send(*Register_Data );
        if ( Get_ACK_NACK() == NACK_Received )
           I2C_STOP ();
           return 3;
        *Register_Data++;
     }

     I2C_STOP ();

     return 0;
}

void main ()
{
    uint8_t Trnasfer_Bytes_Result;

    uint8_t Register_Bytes [ ] = {Year, Month, Date, Day, Hours, Minutes, Seconds};
    uint8_t Byte_Size;

    // Pass slave address, Pass frist address of Ds1307 register
    // Pass register value that will store at seconds register of DS1307, pass the size How many bytes you want to write 
    Trnasfer_Bytes_Result =  ( Slave_Devive_Address, Slave_Word_Address, Register_Bytes, Byte_Size );

}
@Ian0
I don't see anything technically wrong with what you are doing.
I would recommend placing all the I2C code in its own .c file with a .h file exposing three methods:
I2C_Init();
I2C_Write(byte slaveAddress,const byte* ptrData,byte lenOfData);
I2C_Read(byte slaveAddress,byte* ptrData,byte lenOfData);
I2C_WriteRead(byte slaveAddress,const byte* ptrSendData,byte lenSendData,byte* ptrRcvData,byte lenRcvData);

The rest of the functions should be static local to the .c file.

The last function does a write operation followed by a 'restart' and then a 'read' operation. Many I2C devices use this format so it should be included in a general I2C library.

Each function should return 1 for success and 0 for failure (or something similar)
 

dcbingaman

Joined Jun 30, 2021
1,065
Is it better to use enum instead of defining the value of multiple registers in code ?
I would use:

static const byte ds1307_year=20;

Stay away from the preprocessor if possible. The preprocessor symbol 'Year' and other similar to it can show up all over the place in embedded code causing painstaking name-clashes to occur. Notice here I prefixed the year for what is using it to reduce the name-clash issue.

But when you use local constants they can have the same name as another local constant in another file. But that is simply not true of the preprocessor.

Rule of thumb:
Keep variables and constants that are needed by more than one function local to the .c file they are used in via the static keyword. This will save you many headaches and increase code reusability.
 

Thread Starter

Sparsh45

Joined Dec 6, 2021
143
I have attached the data sheet of LM75 temperature sensor support I2C protocol.

please correct me if i misread the datasheet

There are the four registers of LM35
  • Temperature register
  • Configuration register
  • THYST register.
  • TOS register

Page 8 from diagram to write sensor

1) Master send address of slave register.

2) Master sand pointer register which is the address of the Configuration register.

3) Then the master sends the Most Significant and

4) Master send least Significant data byte

I don't understand why Most Significant and least Significant data byte are sent
 

Attachments

Thread Starter

Sparsh45

Joined Dec 6, 2021
143
hi S,
The Data is in a 16 Bit Word, so two 8 Bit Bytes need to be sent.
ie: 16 Clock pulses in total in order to get both the High and Low Bytes of data.
E
I'm having some trouble understanding this timing diagram Page 8, figure 5,

(0x90) // Address of LM75 address
(0x00) // Address of Temperature Register
(0x01) // address of Configuration Register

The pointer byte selects the sensor register's

We can only read temperature register, can't write any value to it, still couldn't understand what data value was being sent to it.

I do not understand what data is being sent in timing diagram A,B, C. I just understood that first byte is slave address and second byte is pointer register it could be one of the four
 

JohnInTX

Joined Jun 26, 2012
4,787
Figure 5 (c) is writing to the temperature setpoint registers, not the ambient temperature registers. The ambient temperature registers are read only.

Note that to read or write ANY of the registers you must first set the pointer register by WRITING to the LM75. Once you have set the pointer register any further reads or writes will act on the register being pointed to until it is changed by writing to it again. Figure 4 (b) shows a method that works for any of the 4 registers (and any other I2C device, too). The steps are:
  • Address the slave for WRITE then the next byte will be set the pointer register to the internal register you want. Then:
    • If you are WRITING to one of the internal registers, continue outputting to the slave. The data sent will be stored in the register selected by the pointer register.
    • If you are READING from one of the internal registers you must change the LM75 from WRITE to READ. You have to send
      • Another start followed by the slave address again with READ selected.
      • Then begin reading data from the slave.
  • Send a stop at the end.
 

Thread Starter

Sparsh45

Joined Dec 6, 2021
143
  • Address the slave for WRITE then the next byte will be set the pointer register to the internal register you want. Then:
    • If you are WRITING to one of the internal registers, continue outputting to the slave. The data sent will be stored in the register selected by the pointer register.
  • Send a stop at the end.
Does the following process take place in Figure 5 (b) ?

  • START
  • Master send slave Address (0x90) for WRITE
  • Master send internal register address (0x01) to set Configuration Register
  • Master send value 0x00 that will store at Configuration Register
  • STOP
 

Thread Starter

Sparsh45

Joined Dec 6, 2021
143
there are many C libraries / tutorials on line for I2C bit banging
I really liked your @dcbingaman idea for multiple source file. I have little problem with static storage class when I fully understand concept of extern and static. I will definitely try to make multiple source files.

Here is my complete pseudocode to READ/ WRITE DS1307 and it is designed by me reading document posted by @JohnInTX in post #2
pseudocode show idea to write data to Ds1307 and read data from Ds1307
C:
// I2C bit banging pseudocode for Ds1307

#define ACK                                   0
#define NACK                                  1

#define successful                            0
#define failed                                1

#define Slave_Address_Error_Write             1

#define  I2C_START_Error                      3
#define  I2C_Slave_Address_Error_W            4
#define  Slave_Not_ Acknowledgrd_Error        5
#define  I2C_Slave_Byte_Error_W               6
#define  I2C_START_Error_R                    7
#define  I2C_Slave_Address_Error_r            8


#define    Slave_Devive_Write_Address       0xD0
#define    Slave_Devive_Read_Address        0xD1
#define    Slave_Word_Address               0x00


#define Seconds                             00
#define Minutes                             00
#define Hours                               1
#define Day                                 2
#define Date                                21
#define Month                               12
#define Year                                20


// Function to start I2C communication
// Return successful if all ok,
uint8_t I2C_START ()
{
   // SDA and SCL should be High in ideal condition
   SDA = High ;
   SCL = High;
   SDA = Low ;  // The master pulls SDA low
   nop();       // Wait
   SCL = Low ;
   return successful;
}

// Function to stop I2C communication
void I2C_STOP ()
{
   SDA = Low ;
   SCL == High;
   SDA = High ;
   nop();
   SCL = Low ;
}

// Function to send one byte to slave device
// Return successful if all ok,
uint8_t I2C_Write( uint8_t Byte)
{
    uint8_t i;

   for( i = 0; i < 8; i++)
   {
        if ( (Byte & 0x80 ) == 1)
        {
            SDA = High ; // Set SDA to High
            nop();
        }

        else
            SDA = Low; // Set SDA to Low
        }

        Byte = (Byte << 1);  // shift the next bit

        SCL = High ;
        nop();
        SCL = Low;
    }
    return successful;                  
}


//Function to get ACK/ NACK from slave
bit Get_ACK_NACK ()
{
    SCL = High;
    if ( SDA == Low)
         return ACK;
    else
        return NACK;
}

// Function to read one byte
// Return byte stored in register
uint8_t I2C_Read( uint8_t * Byte)
{
    uint8_t i;

   for( i = 0; i < 8; i++)
   {
        if ( (Byte & 0x80 ) == 1)
        {
            SDA = High ;                       // Set SDA to High
            nop();
     
        }

        else
            SDA = Low;                          // Set SDA to Low
        }

        Byte = (Byte << 1);                     // shift the next bit

        SCL = High ;
        nop();
        SCL = Low;
    }

    Byte;

    return *Byte;
}

// Send NACK to slave at 9th clock
void I2C_Send_Nak()
{
    SCL = High;
    SDA = High ;
    nop();
    SCL = Low ;
}

// Function to write DS1307
// send Slave address
// check if slave acknowledged address
// Yes send next byte (address of seconds)
// check if slave acknowledged address
// Yes send next byte
// No, call the I2C_STOP function and Stop communication
bit I2C_Write_Device ( uint8_t Slave_Address, uint8_t Register_Addres, uint8_t * Register_Data, uint8_t Byte_length )
{
     uint8_t i = 0;

     if ( I2C_START() == failed );                   //Generate a start, and check if it failed, show error
         return I2C_START_Error;

       if ( I2C_Write(Slave_Address) == failed )      //Send Slave Address, and check if it failed
         I2C_STOP ();                                // yes, terminates a transfer
         return I2C_Slave_Address_Error_W;

     if ( Get_ACK_NACK() == NACK_Received )          // Get ACK/ NACK from slave and check if it is NACK
         I2C_STOP ();                                // Yes, terminates a transfer
         return Slave_Not_ Acknowledgrd_Error;

     if ( I2C_Write( Register_Addres ) == failed ) //Send Register Address, and check if it failed
         I2C_STOP ();                                // Yes, terminates a transfer
         return Slave_Not_ Acknowledgrd_Error;
 
     if ( Get_ACK_NACK() == NACK_Received )          // Get ACK/ NACK from slave and check if it is NACK
         I2C_STOP ();                                // Yes, terminates a transfer
         return Slave_Not_ Acknowledgrd_Error;

    // Loop for sending multple bytes value
     for ( i = 0; i < Byte_length; i++ )
     {
        if (I2C_Write(*Register_Data) == failed )
            return I2C_Slave_Byte_Error_W;
 
        if ( Get_ACK_NACK() == NACK_Received )
           I2C_STOP ();
           return Slave_Not_ Acknowledgrd_Error;
   
        *Register_Data++;
     }

     I2C_STOP ();                                   // Yes, terminates a transfer

     return successful;                             // Device has been written successfully
}

// Function to read one register of DS1307
// send Slave address
// check if slave acknowledged address
// send address of register
uint8_t I2C_Read_Device ( uint8_t Slave_Address, uint8_t * Register_Addres )
{
     uint8_t i = 0;
     uint8_t Receive_Bytes_Value;

    if (I2C_START() == failed);                             // Send a start, and check if it failed
        return I2C_START_Error_R;

      if ( I2C_Write(Slave_Address) == failed )                // Send Slave Address, and check if it failed
         I2C_STOP ();                                       // Yes, terminates a transfer
         return I2C_Slave_Address_Error_R;

    if ( Get_ACK_NACK() == NACK_Received )                  // Get ACK/ NACK from slave and check if it is NACK
         I2C_STOP ();                                       // Yes, terminates a transfer
         return Slave_Not_ Acknowledgrd_Error;
 
     Receive_Bytes_Value = I2C_Read( Register_Addres );     // Read one DS1307 register
     I2C_Send_Nak();                                        // Don't want to read next register
     I2C_STOP ();                                           // Terminates a transfer

     return successful;                                     // one register of DS1307 has been read successfully
}

// Main program strat
void main (void)
{
    uint8_t Trnasfer_Bytes_Result;
    uint8_t Byte_Stored_Seconds;

    uint8_t Register_Bytes [ ] = {Year, Month, Date, Day, Hours, Minutes, Seconds};
    uint8_t Byte_Size;

    // Pass slave address with write direction in function
    // Pass address of the first register DS1307 (seconds register)
    // pass the length of registers that we want to write
    Trnasfer_Bytes_Result = I2C_Write_Device ( Slave_Device_Write_Address , Slave_Word_Address, Register_Byte, Byte's_length );
    if ( Trnasfer_Bytes_Result == successful)
    {
        Byte_Stored_Seconds = I2C_Read_Device ( Slave_Device_Read_Address, Seconds )  //  value stoared in seconds register
    }

}// Main program end
1) I have doubt in START and STOP condition because the value I am returning I have never done this before. I always first check condition and then return any value, so correct me if I am wrong in START and STOP function?

2) Is function I2C_Read returning value store at register address correctly ?
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
1) I have doubt in START and STOP condition because the value I am returning I have never done this before. I always first check condition and then return any value, so correct me if I am wrong in START and STOP function?
The reference code used the hardware I2C peripheral and that unit reports flags to indicate success or failure. In bit bang, you have the option but I always provide a success/failure flag, even in Start and stoP. For example, if you call I2C_Start() and you find the bus is not idle i.e. one or both SCL or SDA are not '1', then that is an error. You have to remedy the error before continuing with a new I2C transaction. In I2C_Stop(), a common error is SCL or SDA do not raise to '1' after you set them. That is an error too. A lot of programmers ignore those issues. A lot of programmers have problems in their I2C systems, too.

I would provide for error reporting as shown in the reference code. When an error is returned to the top level I2C_Write and I2C_Read routines, they should attempt to recover from the error and re-try the operation. If they can't, they report an error but this time it is fatal since there is nothing more to try except a full system reset. We can talk more about that later but for now, provide the error reporting is my advice.

2) Is function I2C_Read returning value store at register address correctly ?
No. You are storing the error code only in a local variable. You need a loop like in the write routine to read a byte, store it at *Register_Address then increment that pointer. Like the reference code, you need a byte count for the number of bytes to read and store.

A few other things:
Line 174: you are incrementing the byte that the pointer Register_Data is pointing at. You want to increment the pointer itself.
It is not obvious but you should leave SCL low everywhere except after start and stop. SCL low is the starting point for every bit operation so it makes sense to always leave it low after any bit operation. That way you always know it is ready for whatever is next.
Take a look at the I2C_Write and I2C_Read reference code. Both use a similar prototype including
  • The slave address + Read/Write
  • The register address to Read to/Write from
  • A pointer to the target buffer for reads or the source data for writes
  • The number of data bytes to transfer (excluding the slave address and register address)
  • A return value indicating success or failure of the overall operation.
Those routines are usable for many different devices.

I like that you are using pseudo-code first and actually planning and understanding before coding. You would be surprised how many students (and professionals too) don't do that enough. Good for you.

Give those things a thought and good luck!
 

Ian0

Joined Aug 7, 2020
13,158
The need for error reporting depends a lot on the peripheral devices you are using. Because the DS1307/1338 is a RAM device, it doesn't do anything odd like hold up the clock whilst it writes its internal memory. If you are writing to EEPROMs they have to write the page of data to memory when you have finished sending it, and either hold up the clock until they have finished, or simply become unresponsive whilst they are doing it. If you are using multiple devices then the need for error reporting becomes more important.
If you read back the time as 0xFFFFFFFF, when it's meant to be in BCD you know you have an error!
Turning off the open-drain device doesn't force the bus to logic 1, only the pullup-resistor can do that and so you must check that it really has. Any device could be jamming the bus.
 

click_here

Joined Sep 22, 2020
548
This is a "goto fail" bug... (Remember that you can't create a body using tabs in C)
Code:
     if ( Get_ACK_NACK() == NACK_Received )
         I2C_STOP ();
         return 1;
You also have a missing brace for the "else" here
Code:
        if ( (Byte & 0x80 ) == 1)
        {
            SDA = High ; // Set SDA to High
            nop();
        }

        else
            SDA = Low; // Set SDA to Low
        }
 

Thread Starter

Sparsh45

Joined Dec 6, 2021
143
This is a "goto fail" bug... (Remember that you can't create a body using tabs in C)
I don't understand what you mean, I have used brackets now
The reference code used the hardware I2C peripheral and that unit reports flags to indicate success or failure. In bit bang, you have the option but I always provide a success/failure flag, even in Start and stoP. For example, if you call I2C_Start() and you find the bus is not idle i.e. one or both SCL or SDA are not '1', then that is an error. You have to remedy the error before continuing with a new I2C transaction. In I2C_Stop(), a common error is SCL or SDA do not raise to '1' after you set them. That is an error too. A lot of programmers ignore those issues. A lot of programmers have problems in their I2C systems, too.

I would provide for error reporting as shown in the reference code. When an error is returned to the top level I2C_Write and I2C_Read routines, they should attempt to recover from the error and re-try the operation. If they can't, they report an error but this time it is fatal since there is nothing more to try except a full system reset. We can talk more about that later but for now, provide the error reporting is my advice.


No. You are storing the error code only in a local variable. You need a loop like in the write routine to read a byte, store it at *Register_Address then increment that pointer. Like the reference code, you need a byte count for the number of bytes to read and store.

A few other things:
Line 174: you are incrementing the byte that the pointer Register_Data is pointing at. You want to increment the pointer itself.
It is not obvious but you should leave SCL low everywhere except after start and stop. SCL low is the starting point for every bit operation so it makes sense to always leave it low after any bit operation. That way you always know it is ready for whatever is next.
I like that you are using pseudo-code first and actually planning and understanding before coding. You would be surprised how many students (and professionals too) don't do that enough. Good for you.

Give those things thought and good luck!
@JohnInTX

I have improved the code after seeing your feedback. I think I have covered all the points you mentioned

C:
// I2C bit banging pseudocode for Ds1307

#define    Slave_Devive_Write_Address       0xD0
#define    Slave_Devive_Read_Address        0xD1
#define    Slave_Word_Address               0x00

#define ACK                                 0
#define NACK                                1

#define successful                          0
#define failed                              1

#define Seconds                             00
#define Minutes                             00
#define Hours                               1
#define Day                                 2
#define Date                                21
#define Month                               12
#define Year                                20


// Function to start I2C communication
// Return successful if all ok,
uint8_t I2C_START ()
{
   // SDA and SCL should be High in ideal condition
   SDA = High ;
   SCL = High;
   SDA = Low ;  // The master pulls SDA low
   nop();       // Wait
   SCL = Low ;
   return successful;
}

// Function to stop I2C communication
void I2C_STOP ()
{
   SDA = Low ;
   SCL == High;
   SDA = High ;
   nop();
   SCL = Low ;
}

// Function to send one byte to slave device
// Return successful if all ok,
uint8_t I2C_Write( uint8_t Byte)
{
    uint8_t i;

   for( i = 0; i < 8; i++)
   {
        if ( (Byte & 0x80 ) == 1)
        {
            SDA = High ; // Set SDA to High
            nop();
        }

        else
        {
            SDA = Low; // Set SDA to Low
        }

        Byte = (Byte << 1);  // shift the next bit

        SCL = High ;
        nop();
        SCL = Low;
    }
    return successful;                
}


//Function to get ACK/ NACK from slave
bit Get_ACK_NACK ()
{
    SCL = High;
   
    if ( SDA == Low)
    {      
         return ACK;
    }
   
    else
    {
         return NACK;
    }
     
}

// Function to read one byte
// Return byte stored in register
uint8_t I2C_Read( uint8_t * Byte)
{
    uint8_t i;

   for( i = 0; i < 8; i++)
   {
        if ( (Byte & 0x80 ) == 1)
        {
            SDA = High ;                       // Set SDA to High
            nop();
   
        }

        else
        {
            SDA = Low;                          // Set SDA to Low
        }

        Byte = (Byte << 1);                     // shift the next bit

        SCL = High ;
        nop();
        SCL = Low;
    }

    Byte;

    return *Byte;
}

// Send NACK to slave at 9th clock
void I2C_Send_Nak()
{
    SCL = High;
    SDA = High ;
    nop();
    SCL = Low ;
}

// Send ACK to slave at 9th clock
void I2C_Send_ACK()
{
    SCL = High;
    SDA = Low ;
    nop();
    SCL = Low ;
}

// Function to write DS1307
// send Slave address
// check if slave acknowledged address
// Yes send next byte (address of seconds)
// check if slave acknowledged address
// Yes send next byte
// No, call the I2C_STOP function and Stop communication
bit I2C_Write_Device ( uint8_t Slave_Address, uint8_t Register_Addres, uint8_t * Register_Data, uint8_t Byte_length )
{
     uint8_t i = 0;

     if ( I2C_START() == failed )                    //Generate a start, and check if it failed, show error
     {
        return failed;
     }
   

    if ( I2C_Write(Slave_Address) == failed )        //Send Slave Address, and check if it failed
    {
         I2C_STOP ();                                // yes, terminates a transfer
         return failed;
    }

    if ( Get_ACK_NACK() == NACK_Received )           // Get ACK/ NACK from slave and check if it is NACK
    {    
         I2C_STOP ();                                // Yes, terminates a transfer
         return failed;
    }
   
    if ( I2C_Write( Register_Addres ) == failed )   //Send Register Address, and check if it failed
    {
        I2C_STOP ();                                // Yes, terminates a transfer
        return failed;
    }

    if ( Get_ACK_NACK() == NACK_Received )          // Get ACK/ NACK from slave and check if it is NACK  
    {
         I2C_STOP ();                                // Yes, terminates a transfer
         return failed;
    }
   
    // Loop for sending multple bytes value
     for ( i = 0; i < Byte_length; i++ )
     {
        if (I2C_Write(*Register_Data) == failed )
        {
           return failed;
        }
       
        if ( Get_ACK_NACK() == NACK_Received )
        {
            I2C_STOP ();
           return failed;
        }
 
        *Register_Data++;
         Register_Data++;
     }

     I2C_STOP ();                                   // Yes, terminates a transfer

     return successful;                             // Device has been written successfully
}

// Function to read DS1307
// send Slave address
// check if slave acknowledged address
// send address of register
uint8_t I2C_Read_Device ( uint8_t Slave_Address, uint8_t * Register_Addres, uint8_t Byte's_length )
{
     uint8_t i = 0;


    if (I2C_START() == failed);                             // Send a start, and check if it failed  
    {
        return failed;
    }

    if ( I2C_Write(Slave_Address) == failed )                // Send Slave Address, and check if it failed
    {      I2C_STOP ();                                       // Yes, terminates a transfer
           return failed;
    }

    if ( Get_ACK_NACK() == NACK_Received )                  // Get ACK/ NACK from slave and check if it is NACK
    {      
          I2C_STOP ();                                       // Yes, terminates a transfer
         return failed;
    }


     for ( i = 0; i < Byte_length; i++ )
     {
           Receive_Bytes_Value[i] = I2C_Read(Register_Addres );            
           Register_Addres++;
           I2C_Send_ACK();          
     }
   
     I2C_Send_Nak();
     I2C_STOP ();    

     return successful;                                  
}

uint8_t Receive_Bytes_Value[] ;
   
// Main program strat
void main (void)
{
    uint8_t Trnasfer_Bytes_Result;
    uint8_t Byte_Stored_Register_Result;
   

    uint8_t Register_Bytes [ ] = {Year, Month, Date, Day, Hours, Minutes, Seconds};

    // Pass slave address with write direction in function
    // Pass address of the first register DS1307 (seconds register)
    // pass the length of registers that we want to write
    Trnasfer_Bytes_Result = I2C_Write_Device ( Slave_Device_Write_Address , Slave_Word_Address, Register_Byte, 6 );
   
    if ( Trnasfer_Bytes_Result == successful)
    {
        Byte_Stored_Register_Result = I2C_Read_Device ( Slave_Device_Read_Address, Slave_Word_Address, 6 );
    }

}// Main program end
 
Top