The I2C Physical Protocol
I2C Device Addressing
Notes:
/* Name : main.c
* Purpose : Source code for RTC-DS1307 Interfacing with AT89C52.
* Author : Gemicates
* Date : 2014-03-05
* Website : www.gemicates.org
* Revision : None
*/
#include "Includes.h"
// Main function
void main()
{
InitLCD(); // Initialize LCD
InitI2C(); // Initialize i2c pins
// Set initial time
Set_DS1307_RTC_Time(PM_Time, 8, 8, 8); // Set time 08:32:59 AM
// Set initial date
Set_DS1307_RTC_Date(4, 03, 91, Monday); // Set 04-03-1991 @ Monday
while(1)
{
// Display RTC time on first line of LCD
DisplayTimeToLCD(Get_DS1307_RTC_Time());
// Display RTC date on second line of LCD
DisplayDateOnLCD(Get_DS1307_RTC_Date());
delay(65000); // Roughly about 1 second delay
}
}
#include "Includes.h"
void ToggleEpinOfLCD(void)
{
E = 1; // Give a pulse on E pin
__delay_us(E_Delay); // so that LCD can latch the
E = 0; // data from data bus
__delay_us(E_Delay);
}
void WriteDataToLCD(char t)
{
RS = 1; // This is data
P2 &= 0x0F; // Make P2.4 to P2.7 zero
P2 |= (t&0xF0); // Write Upper nibble of data
ToggleEpinOfLCD(); // Toggle E pin to send data
P2 &= 0x0F; // Make P2.4 to P2.7 zero
P2 |= ((t<<4)&0xF0); // Write Lower nibble of data
ToggleEpinOfLCD(); // Toggle E pin to send data
}
void WriteCommandToLCD(int z)
{
RS = 0; // This is command
P2 &= 0x0F; // Make P2.4 to P2.7 zero
P2 |= (z&0xF0); // Write Upper nibble of data
ToggleEpinOfLCD(); // Toggle E pin to send data
P2 &= 0x0F; // Make P2.4 to P2.7 zero
P2 |= ((z<<4)&0xF0); // Write Lower nibble of data
ToggleEpinOfLCD(); // Toggle E pin to send data
}
void InitLCD(void)
{
RS = 0; // Make pin zero
E = 0; // Make Pin zero
///////////// Reset process from datasheet /////////
__delay_us(15000);
P2 &= 0x0F; // Make P2.4 to P2.7 zero
P2 |= 0x30; // Write 0x3
ToggleEpinOfLCD(); // Toggle E pin to send data
__delay_us(4500);
P2 &= 0x0F; // Make P2.4 to P2.7 zero
P2 |= 0x30; // Write 0x3
ToggleEpinOfLCD(); // Toggle E pin to send data
__delay_us(300);
P2 &= 0x0F; // Make P2.4 to P2.7 zero
P2 |= 0x30; // Write 0x3
ToggleEpinOfLCD(); // Toggle E pin to send data
__delay_us(650);
P2 &= 0x0F; // Make P2.4 to P2.7 zero
P2 |= 0x20; // Write 0x2
ToggleEpinOfLCD(); // Toggle E pin to send data
__delay_us(650);
/////////////////////////////////////////////////////
WriteCommandToLCD(0x28); //function set
WriteCommandToLCD(0x0c); //display on,cursor off,blink off
WriteCommandToLCD(0x01); //clear display
WriteCommandToLCD(0x06); //entry mode, set increment
}
void ClearLCDScreen(void) // Clear the Screen and return cursor to zero position
{
WriteCommandToLCD(0x01); // Clear the screen
__delay_us(2000); // Delay for cursor to return at zero position
}
void WriteStringToLCD(const char *s)
{
while(*s)
WriteDataToLCD(*s++);
}
void DisplayTimeToLCD( unsigned char* pTimeArray ) // Displays time in HH:MM:SS AM/PM format
{
ClearLCDScreen(); // Move cursor to zero location and clear screen
// Display Hour
WriteDataToLCD( (pTimeArray[2]/10)+0x30 );
WriteDataToLCD( (pTimeArray[2]%10)+0x30 );
//Display ':'
WriteDataToLCD(':');
//Display Minutes
WriteDataToLCD( (pTimeArray[1]/10)+0x30 );
WriteDataToLCD( (pTimeArray[1]%10)+0x30 );
//Display ':'
WriteDataToLCD(':');
//Display Seconds
WriteDataToLCD( (pTimeArray[0]/10)+0x30 );
WriteDataToLCD( (pTimeArray[0]%10)+0x30 );
//Display Space
WriteDataToLCD(' ');
// Display mode
switch(pTimeArray[3])
{
case AM_Time: WriteStringToLCD("AM"); break;
case PM_Time: WriteStringToLCD("PM"); break;
default: WriteDataToLCD('H'); break;
}
}
void DisplayDateOnLCD( unsigned char* pDateArray ) // Displays Date in DD:MM:YY @ Day format
{
WriteCommandToLCD(0xc0); // Move cursor to second line
// Display Date
WriteDataToLCD( (pDateArray[1]/10)+0x30 );
WriteDataToLCD( (pDateArray[1]%10)+0x30 );
//Display '/'
WriteDataToLCD('/');
//Display Month
WriteDataToLCD( (pDateArray[2]/10)+0x30 );
WriteDataToLCD( (pDateArray[2]%10)+0x30 );
//Display '/'
WriteDataToLCD('/');
//Display Year
WriteDataToLCD( (pDateArray[3]/10)+0x30 );
WriteDataToLCD( (pDateArray[3]%10)+0x30 );
//Display Space
WriteDataToLCD(' ');
// Display Day
switch(pDateArray[0])
{
case Monday: WriteStringToLCD("MON"); break;
case Tuesday: WriteStringToLCD("TUE"); break;
case Wednesday: WriteStringToLCD("WED"); break;
case Thursday: WriteStringToLCD("THU"); break;
case Friday: WriteStringToLCD("FRI"); break;
case Saturday: WriteStringToLCD("SAT"); break;
case Sunday: WriteStringToLCD("SUN"); break;
default: WriteStringToLCD("???"); break;
}
}
#include "Includes.h"
// Function Purpose: Produce approximate delay in given uSecs.
void __delay_us(unsigned int d)
{
unsigned int i, limit;
limit = d/15;
for(i=0;i<limit;i++);
}
// Function Purpose: Set initial values of SCK and SDA pins
void InitI2C(void)
{
// Make SDA and SCK pins input initially
SDA = 1;
SCK = 1;
}
// Function Purpose: I2C_Start sends start bit sequence
void I2C_Start(void)
{
Set_SCK_High; // Make SCK pin high
Set_SDA_High; // Make SDA pin High
__delay_us(HalfBitDelay); // Half bit delay
Set_SDA_Low; // Make SDA Low
__delay_us(HalfBitDelay); // Half bit delay
}
// Function Purpose: I2C_ReStart sends start bit sequence
void I2C_ReStart(void)
{
Set_SCK_Low; // Make SCK pin low
__delay_us(HalfBitDelay/2); // Data pin should change it's value,
// when it is confirm that SCK is low
Set_SDA_High; // Make SDA pin High
__delay_us(HalfBitDelay/2); // 1/4 bit delay
Set_SCK_High; // Make SCK pin high
__delay_us(HalfBitDelay/2); // 1/4 bit delay
Set_SDA_Low; // Make SDA Low
__delay_us(HalfBitDelay/2); // 1/4 bit delay
}
//Function : I2C_Stop sends stop bit sequence
void I2C_Stop(void)
{
Set_SCK_Low; // Make SCK pin low
__delay_us(HalfBitDelay/2); // Data pin should change it's value,
// when it is confirm that SCK is low
Set_SDA_Low; // Make SDA pin low
__delay_us(HalfBitDelay/2); // 1/4 bit delay
Set_SCK_High; // Make SCK pin high
__delay_us(HalfBitDelay/2); // 1/4 bit delay
Set_SDA_High; // Make SDA high
__delay_us(HalfBitDelay/2); // 1/4 bit delay
}
//Function : I2C_Send_ACK sends ACK bit sequence
void I2C_Send_ACK(void)
{
Set_SCK_Low; // Make SCK pin low
__delay_us(HalfBitDelay/2); // Data pin should change it's value,
// when it is confirm that SCK is low
Set_SDA_Low; // Make SDA Low
__delay_us(HalfBitDelay/2); // 1/4 bit delay
Set_SCK_High; // Make SCK pin high
__delay_us(HalfBitDelay); // Half bit delay
}
//Function : I2C_Send_NACK sends NACK bit sequence
void I2C_Send_NACK(void)
{
Set_SCK_Low; // Make SCK pin low
__delay_us(HalfBitDelay/2); // Data pin should change it's value,
// when it is confirm that SCK is low
Set_SDA_High; // Make SDA high
__delay_us(HalfBitDelay/2); // 1/4 bit delay
Set_SCK_High; // Make SCK pin high
__delay_us(HalfBitDelay); // Half bit delay
}
// Function Purpose: I2C_Write_Byte transfers one byte
bit I2C_Write_Byte(unsigned char Byte)
{
unsigned char i; v // Variable to be used in for loop
for(i=0;i<8;i++) // Repeat for every bit
{
Set_SCK_Low; // Make SCK pin low
__delay_us(HalfBitDelay/2); // Data pin should change it's value,
// when it is confirm that SCK is low
if((Byte<<i)&0x80) // Place data bit value on SDA pin
Set_SDA_High; // If bit is high, make SDA high
else // Data is transferred MSB first
Set_SDA_Low; // If bit is low, make SDA low
__delay_us(HalfBitDelay/2); // Toggle SCK pin
Set_SCK_High; // So that slave can
__delay_us(HalfBitDelay); // latch data bit
}
// Get ACK from slave
Set_SCK_Low;
Set_SDA_High;
__delay_us(HalfBitDelay);
Set_SCK_High;
__delay_us(HalfBitDelay);
return SDA;
}
// Function Purpose: I2C_Read_Byte reads one byte
unsigned char I2C_Read_Byte(void)
{
unsigned char i, d, RxData = 0;
for(i=0;i<8;i++)
{
Set_SCK_Low; // Make SCK pin low
Set_SDA_High; // Don't drive SDA
__delay_us(HalfBitDelay); // Half bit delay
Set_SCK_High; // Make SCK pin high
__delay_us(HalfBitDelay/2); // 1/4 bit delay
d = SDA; // Capture Received Bit
RxData = RxData|(d<<(7-i)); // Copy it in RxData
__delay_us(HalfBitDelay/2); // 1/4 bit delay
}
return RxData; // Return received byte
}
#include "Includes.h"
// Global RTC Array and temp variable
unsigned char pRTCArray[4];
unsigned char Temp;
// Function Purpose: delay generate some delay according to d value
void delay(unsigned int d)
{
unsigned int i;
for(i=0;i<d;i++);
}
// Function Purpose: Write_Byte_To_DS1307_RTC writes a single byte on given address
// Address can have any value fromm 0 to 0xFF, and DataByte can have a value of 0 to 0xFF.
void Write_Byte_To_DS1307_RTC(unsigned char Address, unsigned char DataByte)
{
I2C_Start(); // Start i2c communication
// Send i2c address of DS1307 with write command
while(I2C_Write_Byte(Device_Address_DS1307_EEPROM + 0) == 1) // Wait until device is free
{ I2C_Start(); }
I2C_Write_Byte(Address); // Write Address byte
I2C_Write_Byte(DataByte); // Write data byte
I2C_Stop(); // Stop i2c communication
}
// Function Purpose: Read_Byte_From_DS1307_RTC reads a single byte from given address
// Address can have any value fromm 0 to 0xFF.
unsigned char Read_Byte_From_DS1307_RTC(unsigned char Address)
{
unsigned char Byte = 0; // Variable to store Received byte
I2C_Start(); // Start i2c communication
// Send i2c address of DS1307 with write command
while(I2C_Write_Byte(Device_Address_DS1307_EEPROM + 0) == 1) // Wait until device is free
{ I2C_Start(); }
I2C_Write_Byte(Address); // Write Address byte
I2C_ReStart(); // Restart i2c
// Send i2c address of DS1307 RTC with read command
I2C_Write_Byte(Device_Address_DS1307_EEPROM + 1);
Byte = I2C_Read_Byte(); // Read byte from EEPROM
// Make SCK low, so that slave can stop driving SDA pin
// Send a NACK to indiacate single byte read is complete
I2C_Send_NACK();
// Send start bit and then stop bit to stop transmission
Set_SDA_Low; // Make SDA Low
__delay_us(HalfBitDelay); // Half bit delay
Set_SDA_High; // Make SDA high
__delay_us(HalfBitDelay); // Half bit delay
return Byte; // Return the byte received from 24LC64 EEPROM
}
// Function Purpose: Write_Bytes_To_DS1307_RTC writes mulitple bytes from given starting address.
// Address can have any value fromm 0 to 0xFF and pData is pointer to the array
// containing NoOfBytes bytes in it. NoOfBytes is the number of bytes to write.
void Write_Bytes_To_DS1307_RTC(unsigned char Address,unsigned char* pData,unsigned char NoOfBytes)
{
unsigned int i;
I2C_Start(); // Start i2c communication
// Send i2c address of DS1307 with write command
while(I2C_Write_Byte(Device_Address_DS1307_EEPROM + 0) == 1) // Wait until device is free
{ I2C_Start(); }
I2C_Write_Byte(Address); // Write Address byte
for(i=0;i<NoOfBytes;i++) // Write NoOfBytes
I2C_Write_Byte(pData[i]); // Write data byte
I2C_Stop(); // Stop i2c communication
}
// Function Purpose: Read_Bytes_From_DS1307_RTC reads a NoOfBytes bytes from given starting address.
// Address can have any value fromm 0 to 0xFF. NoOfBytes is the number of bytes to write.
// Read bytes are returned in pData array.
void Read_Bytes_From_DS1307_RTC(unsigned char Address, unsigned char* pData, unsigned int NoOfBytes)
{
unsigned int i;
I2C_Start(); // Start i2c communication
// Send i2c address of DS1307 with write command
while(I2C_Write_Byte(Device_Address_DS1307_EEPROM + 0) == 1) // Wait until device is free
{ I2C_Start(); }
I2C_Write_Byte(Address); // Write Address byte
I2C_ReStart(); // Restart i2c
// Send i2c address of DS1307 RTC with read command
I2C_Write_Byte(Device_Address_DS1307_EEPROM + 1);
pData[0] = I2C_Read_Byte(); // Read First byte from EEPROM
for(i=1;i<NoOfBytes;i++) // Read NoOfBytes
{
I2C_Send_ACK(); // Give Ack to slave to start receiving next byte
pData[i] = I2C_Read_Byte(); // Read next byte from EEPROM
}
// Make SCK low, so that slave can stop driving SDA pin
// Send a NACK to indiacate read operation is complete
I2C_Send_NACK();
// Send start bit and then stop bit to stop transmission
Set_SDA_Low; // Make SDA Low
__delay_us(HalfBitDelay); // Half bit delay
Set_SDA_High; // Make SDA high
__delay_us(HalfBitDelay); // Half bit delay
}
// Function Purpose: Set_DS1307_RTC_Time sets given time in RTC registers.
// Mode can have a value AM_Time or PM_Time or TwentyFourHoursMode only.
// Hours can have value from 0 to 23 only.
// Mins can have value from 0 to 59 only.
// Secs can have value from 0 to 59 only.
void Set_DS1307_RTC_Time(unsigned char Mode, unsigned char Hours, unsigned char Mins, unsigned char Secs)
{
// Convert Hours, Mins, Secs into BCD
pRTCArray[0] = (((unsigned char)(Secs/10))<<4)|((unsigned char)(Secs%10));
pRTCArray[1] = (((unsigned char)(Mins/10))<<4)|((unsigned char)(Mins%10));
pRTCArray[2] = (((unsigned char)(Hours/10))<<4)|((unsigned char)(Hours%10));
switch(Mode) // Set mode bits
{
case AM_Time: pRTCArray[2] |= 0x40; break;
case PM_Time: pRTCArray[2] |= 0x60; break;
default: break; // do nothing for 24HoursMode
}
// WritepRTCArray to DS1307
Write_Bytes_To_DS1307_RTC(0x00, pRTCArray, 3);
}
// Function Purpose: Get_DS1307_RTC_Time returns current time from RTC registers.
// Pointer to pRTCArray is returned, in this array
// pRTCArray[3] can have a value AM_Time or PM_Time or TwentyFourHoursMode only.
// pRTCArray[2] (Hours byte) can have value from 0 to 23 only.
// pRTCArray[1] (Mins byte) can have value from 0 to 59 only.
// pRTCArray[0] (Secs byte) can have value from 0 to 59 only.
unsigned char* Get_DS1307_RTC_Time(void)
{
// Read Hours, Mins, Secs register from RTC
Read_Bytes_From_DS1307_RTC(0x00, pRTCArray, 3);
// Convert Secs back from BCD into number
Temp = pRTCArray[0];
pRTCArray[0] = ((Temp&0x7F)>>4)*10 + (Temp&0x0F);
// Convert Mins back from BCD into number
Temp = pRTCArray[1];
pRTCArray[1] = (Temp>>4)*10 + (Temp&0x0F);
// Convert Hours back from BCD into number
if(pRTCArray[2]&0x40) // if 12 hours mode
{
if(pRTCArray[2]&0x20) // if PM Time
pRTCArray[3] = PM_Time;
else // if AM time
pRTCArray[3] = AM_Time;
Temp = pRTCArray[2];
pRTCArray[2] = ((Temp&0x1F)>>4)*10 + (Temp&0x0F);
}
else // if 24 hours mode
{
Temp = pRTCArray[2];
pRTCArray[2] = (Temp>>4)*10 + (Temp&0x0F);
pRTCArray[3] = TwentyFourHoursMode;
}
return pRTCArray;
}
// Function Purpose: Set_DS1307_RTC_Date sets given date in RTC registers.
// Year can have a value from 0 to 99 only.
// Month can have value from 1 to 12 only.
// Date can have value from 1 to 31 only.
// Day can have value from 1 to 7 only. Where 1 means Monday, 2 means Tuesday etc.
void Set_DS1307_RTC_Date(unsigned char Date, unsigned char Month, unsigned char Year, unsigned char Day)
{
// Convert Year, Month, Date, Day into BCD
pRTCArray[0] = (((unsigned char)(Day/10))<<4)|((unsigned char)(Day%10));
pRTCArray[1] = (((unsigned char)(Date/10))<<4)|((unsigned char)(Date%10));
pRTCArray[2] = (((unsigned char)(Month/10))<<4)|((unsigned char)(Month%10));
pRTCArray[3] = (((unsigned char)(Year/10))<<4)|((unsigned char)(Year%10));
// WritepRTCArray to DS1307
Write_Bytes_To_DS1307_RTC(0x03, pRTCArray, 4);
}
// Function Purpose: Get_DS1307_RTC_Date returns current date from RTC registers.
// Pointer to pRTCArray is returned, in this array
// pRTCArray[3] (Year byte) can have value from 0 to 99 only.
// pRTCArray[2] (Month byte) can have value from 1 to 12 only.
// pRTCArray[1] (Date byte) can have value from 1 to 31 only.
// pRTCArray[0] (Day byte) can have value from 1 to 7 only.
unsigned char* Get_DS1307_RTC_Date(void)
{
// Read Hours, Mins, Secs register from RTC
Read_Bytes_From_DS1307_RTC(0x03, pRTCArray, 4);
// Convert Date back from BCD into number
Temp = pRTCArray[1];
pRTCArray[1] = (Temp>>4)*10 + (Temp&0x0F);
// Convert Month back from BCD into number
Temp = pRTCArray[2];
pRTCArray[2] = (Temp>>4)*10 + (Temp&0x0F);
// Convert Year back from BCD into number
Temp = pRTCArray[3];
pRTCArray[3] = (Temp>>4)*10 + (Temp&0x0F);
return pRTCArray;
}

