# Error interfacing DS1307 RTC with PIC32

I am interfacing DS1307 RTC with PIC32MX795F512L. I am using I2C1 for DS1307 RTC and then using UART2 to send the RTC value to the terminal. I have written the code but don't know why I am not getting data.

In my code I am using

OpenI2C2 to open the i2c channel.
StartI2C2() to start the communication
StopI2C2() to stop the communication
MasterWriteI2C() to write the data


These are included in plib.h file.

Updated CODE:

OpenI2C2(I2C_EN, 163); // I2C channel Configuration

StartI2C2();
IdleI2C2();
IdleI2C2();
MasterWriteI2C2(0x07);
IdleI2C2();
MasterWriteI2C2(0x00);
IdleI2C2();
StopI2C2();

StartI2C2();
IdleI2C2();
MasterWriteI2C2(0xD0);
IdleI2C2();
MasterWriteI2C2(0x01);
IdleI2C2();
MasterWriteI2C2(0b00010011);
IdleI2C2();
StopI2C2();

StartI2C2();
IdleI2C2();
MasterWriteI2C2(0xD0);
IdleI2C2();
MasterWriteI2C2(0x01);
IdleI2C2();
StopI2C2();

StartI2C2();
IdleI2C2();
MasterWriteI2C2(0xD1);
IdleI2C2();
IdleI2C2();
NotAckI2C2();
IdleI2C2();
StopI2C2();


I am using 163 ((33000000/2/100000)-2)as BRG value for I2C communication. I am debugging the code and can see all the values in I2C registers are correct but at res = MasterReadI2C2(), nothing shows up in I2C2RCV register which holds the received value and even nothing showing up in the res variable. I also used a variable n to check if the values are actually transmitting or not. So I received 0x00 as the value of n and according to document, 0 means transmission successful.

I don't know where I am missing the point.

• You mean nothing as in really nothing, or nothing as in all three bytes are 0? – Arsenal Aug 21 '15 at 11:57
• sometime i get garbage values like ? or * or something else. Sometime terminal is completly blank or sometime i get NULL. I am using Docklight – Aircraft Aug 21 '15 at 12:00
• Maybe initialize the chars to a human readable, but nonsensical value like 'Z', currently it sounds more like you have a problem with your UART. (and you did realize, that you only get the time once and print it out forever?) And for this kind of test make sure to set your terminal to display hex or decimal values, as ASCII values might be very misleading. – Arsenal Aug 21 '15 at 12:10
• what do you mean by "initialize the chars to a human readable" which chars you are talking about?. my uart is working because if i send something like helloworld, i am getting this on terminal – Aircraft Aug 21 '15 at 12:16
• You have unitialized chars i j k and i1 (which is not used), which you put into the GetTime function, to check whether the values are changed and thus a time is read, you should initialize them with some value, which prints out nicely on the terminal. – Arsenal Aug 21 '15 at 12:20

1. Based on the datasheet:

Bit 7 of Register 0 is the clock halt (CH) bit. When this bit is set to 1, the oscillator is disabled. When cleared to 0, the oscillator is enabled

So when you set second, make sure bit 7 (CH) is 0. AND set bit 7 (CH) to zero every time the IC is powering up.

1. Try to add small delay (10ms) before I2C start and after I2C stop.
2. Try to set one register at one time.
3. Adjust the pull-up resistor, try to change to 1.5k/1.8k Ohm

I worked this RTC with ATMega128 and CodeVisionAVR This code works:

rtc_init(0,1,0); // output pin for debugging

delay_ms(10);
i2c_start();
i2c_write(0xD0);
i2c_write(0x00);
i2c_write( (data_rtc[10]-48)*16 + (data_rtc[11]-48));      //second
i2c_stop();

delay_ms(10);
i2c_start();
i2c_write(0xD0);
i2c_write(0x01);
i2c_write( (data_rtc[8]-48)*16 + (data_rtc[9]-48));        // minutes
i2c_stop();

delay_ms(10);
i2c_start();
i2c_write(0xD0);
i2c_write(0x02);
i2c_write( (data_rtc[6]-48)*16 + (data_rtc[7]-48));        // hour
i2c_stop();

delay_ms(10);
i2c_start();
i2c_write(0xD0);
i2c_write(0x04);
i2c_write( (data_rtc[4]-48)*16 + (data_rtc[5]-48));        // day
i2c_stop();

delay_ms(10);
i2c_start();
i2c_write(0xD0);
i2c_write(0x05);
i2c_write( (data_rtc[2]-48)*16 + (data_rtc[3]-48));        // month
i2c_stop();

delay_ms(10);
i2c_start();
i2c_write(0xD0);
i2c_write(0x06);
i2c_write( (data_rtc[0]-48)*16 + (data_rtc[1]-48));        // year
i2c_stop();

delay_ms(10);
rtc_init(0,1,0);

• okay so to get proper timing, we should always enable the oscillator that means bit 7 will always be 0 and in my code, RTC_SetTime(0x16,0x00,0x10) 0x10 means 0001 0000 that means I have already done that. so whats the problem. – Aircraft Aug 21 '15 at 12:32
• @CZabhinav, sorry for my mistake, I edited my answer – Oka Aug 21 '15 at 12:48
• what are you doing in this line i2c_write( (data_rtc[0]-48)*16 + (data_rtc[1]-48)); ?? – Aircraft Aug 21 '15 at 13:02
• Setting the time, but data_rtc[x] in ASCII format, so I subtract with 48. Then I multiply the left digit with 16. For example, we want to set second = 21, then i2c_write( ('2'-48)*16 + ('1'-48)) – Oka Aug 21 '15 at 13:04
• I havent done this type of conversion in my code. May be this is the error in my code. Am I right? – Aircraft Aug 21 '15 at 13:06

Using the Microchip PLIB legacy libraries for I2C, you need to manually poll for the bus to be idle after each operation. An example is shown below:

StartI2C2(); // Send the Start Bit
IdleI2C2(); // Wait to complete
MasterWriteI2C2(0xE0); // Send slave address with write bit
IdleI2C2(); // Wait to complete
IdleI2C2(); // Wait to complete
StopI2C2(); // Send Stop bit
IdleI2C2();
StartI2C2(); // Send the Start Bit
IdleI2C2(); // Wait to complete
IdleI2C2(); // Wait to complete
IdleI2C2();
NotAckI2C2(); // Master  Aknodledge End of transfer
StopI2C2(); // Send Stop bit
IdleI2C2();


The IdleI2C2() function is simply the following:

while(I2C2CONbits.SEN || I2C2CONbits.PEN || I2C2CONbits.RSEN || I2C2CONbits.RCEN || I2C2CONbits.ACKEN || I2C2STATbits.TRSTAT);


This line simply is checking any bits that signify that the peripheral is busy, (such as an active start or stop condition, currently clocking out data, etc.

• I have used your code to interface it with DS1307. I have updated my code. Can you help me with it.! – Aircraft Nov 20 '15 at 10:13

Looking at you code, I don't see anything glaringly wrong, but if we look at the DS1307 datasheet, page 13, it specifies that to do a data read from a specific location you write the address, the location in the DS1307 registers, then initiate a "Repeated Start" routine, send the address again, then read the data. You can read in a multibyte burst, sequentially, or a single byte and then send a STOP and start the process over again at a different register location.

I was recently finding an attempt to communicate between a master and slave PIC unsuccessful, and I had a DS1307 lying around, so I whipped up some code. Thankfully, for i2c.h, no more bit-banging. The code is written for the PIC18F8722, but you should find that something similar will work for you:

#include <pic18f8722.h>
#include <i2c.h>
#include <spi.h>
#include <delays.h>
#include "LCD.h"
#include "SerComm.h"

#pragma config OSC = HSPLL
#pragma config WDT = OFF
#pragma config FCMEN = OFF

#define newline 0x0A
#define carriageReturn 0x0D

void initialize(void);
unsigned char x;
unsigned char seconds = 0x00;
unsigned char minutes = 0x36;
unsigned char hours = 0x48;
unsigned char days = 0x03;
unsigned char date = 0x17;
unsigned char month = 0x08;
unsigned char year = 0x15;
unsigned char hyear, lyear, hmonth, lmonth, hdate, ldate, hdays, ldays, hhours, lhours, hminutes, lminutes, hseconds, lseconds;
unsigned char time[7];

void main(void)
{
lcdInit();
initialize();
CloseI2C2();

OpenI2C2(MASTER, SLEW_OFF);

IdleI2C2();
StartI2C2();
WriteI2C2(0xD0);
WriteI2C2(0x00);
WriteI2C2(seconds);
WriteI2C2(minutes);
WriteI2C2(hours);
WriteI2C2(days);
WriteI2C2(date);
WriteI2C2(month);
WriteI2C2(year);
StopI2C2();

while(1)
{
IdleI2C2();
StartI2C2();
WriteI2C2(0xD0);
WriteI2C2(0x00);
RestartI2C2();
WriteI2C2(0xD1);
for(x = 0; x < 6; x++)
{
AckI2C2();
}
NotAckI2C2();
StopI2C2();

hyear = ((time[6] & 0xF0) >> 4) + 0x30;
lyear = (time[6] & 0x0F) + 0x30;
hmonth = ((time[5] & 0xF0) >> 4) + 0x30;
lmonth = (time[5] & 0x0F) + 0x30;
hdate = ((time[4] & 0xF0) >> 4) + 0x30;
ldate = (time[4] & 0x0F) + 0x30;
hdays = ((time[3] & 0xF0) >> 4) + 0x30;
ldays = (time[3] & 0x0F) + 0x30;
hhours = ((time[2] & 0x10) >> 4) + 0x30;
lhours = (time[2] & 0x0F) + 0x30;
hminutes = ((time[1] & 0xF0) >> 4) + 0x30;
lminutes = (time[1] & 0x0F) + 0x30;
hseconds = ((time[0] & 0xF0) >> 4) + 0x30;
lseconds = (time[0] & 0x0F) + 0x30;

lcdGoTo(0x00);
lcdWriteString("Time: ");
lcdChar(hhours);
lcdChar(lhours);
lcdChar(':');
lcdChar(hminutes);
lcdChar(lminutes);
lcdChar(':');
lcdChar(hseconds);
lcdChar(lseconds);
SerTx(hhours);
SerTx(lhours);
SerTx(':');
SerTx(hminutes);
SerTx(lminutes);
SerTx(':');
SerTx(hseconds);
SerTx(lseconds);
SerTx(newline);
SerTx(carriageReturn);
}

}

void initialize(void)
{
TRISAbits.RA2=0; // Chip select pin for the SPI port expander;

TRISDbits.RD5 = 1;  //Set the ports of the I2C bus;
TRISDbits.RD6 = 0;

SSP2STAT = 0x80;    //Disable the slew rate control;
SSP2CON1 = 0x28;    //Enable the Serial Port, Set the mode to: Master Mode, clock = Fosc/(4*(SSP1ADD + 1);
SSP2CON2 = 0x00;    //No bits to set here;

SSP2ADD = 0x59;     //Set to 100 kHz at 40 MHz clock;

TXSTA1 = 0x24;      //Set serial communication to High Speed, TX enabled, 8 bit mode;
RCSTA1 = 0x90;      //Enable the serial port, Enable the Receiver;
BAUDCON1 = 0x00;    //;

SPBRG1 = 42;        //57600 kBaud;
}


Please, don't be too harsh on the code. I hope this helps.

• In your while(1), you have written WriteI2C2(0x00);, what is this for.?? and with this line WriteI2C2(0xD1); you are trying to read back data but you haven't mentioned which register to read. Any way thanks for sharing your code, i'll definitely try this.! – Aircraft Aug 26 '15 at 4:13