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 DS1307I implemented multi-byte read and write in 170 lines of ARM assembler.
#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 );
}
I don't see anything technically wrong with what you are doing.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
@Ian0C:#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 ); }
I would use:Is it better to use enum instead of defining the value of multiple registers in code ?
hi S,I don't understand why Most Significant and least Significant data byte are sent
I'm having some trouble understanding this timing diagram Page 8, figure 5,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
Does the following process take place in Figure 5 (b) ?
- 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.
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.there are many C libraries / tutorials on line for I2C bit banging
// 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
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.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?
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.2) Is function I2C_Read returning value store at register address correctly ?
if ( Get_ACK_NACK() == NACK_Received )
I2C_STOP ();
return 1;
if ( (Byte & 0x80 ) == 1)
{
SDA = High ; // Set SDA to High
nop();
}
else
SDA = Low; // Set SDA to Low
}
I don't understand what you mean, I have used brackets nowThis is a "goto fail" bug... (Remember that you can't create a body using tabs in C)
@JohnInTXThe 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!
// 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