Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

DS1307 RTC Interfacing with MCU

Status
Not open for further replies.

gauravkothari23

Advanced Member level 2
Joined
Mar 21, 2015
Messages
640
Helped
5
Reputation
10
Reaction score
4
Trophy points
1,298
Activity points
6,922
Hello All.
I am trying to Interface RTC DS1307 with NUC029LAN Controller and display the date and time on LCD.
but the code is not working with NUC029LAN controller.
The same code i tried using 89S52 controller, but it works perfectly. (Code attached)
i have also tried increasing the Delay but still it does not work.
also tried to change the Pull Up resistor to 1K, 4K7, 10K.

Can anybody please let me know, where the issue is,


C:
#include <stdio.h>
#include "NUC029xAN.h"

#define SCL P36    // DS1307 SCL
#define SDA P37    // DS1307 SDA

#define BITT6 0x40         //12 hour format set bit
#define BITT5 0x20

#define DS1307_ID 0xD0    // DS1307 ID  
#define SEC_ADDRESS 0x00  // Address to access Ds1307 SEC register
#define DATE_ADDRESS 0x04 // Address to access Ds1307 DATE register
#define CONTROL 0x07      // Address to access Ds1307 CONTROL register  

unsigned char  sec,min,hour,day,month,year,AM,PM;
                                                          
int32_t main(void)
{
    unsigned int i;
    while(1)
    {
        msdelay(20);
        DS1307_Init();     
        msdelay(20);
        LCD_init();
      
        lcdclear();
        lcdrow2();
        LCD_puts(" TESTING RTC "); 
      
        DS1307_SetDate(0x24,0x04,0x21);
        DS1307_SetTime(0x10,0x10,0x10);
        lcdclear();
        for(i=0;i<10;i++)
        {
            i=0;
            DS1307_GetDate(&day,&month,&year);
            DS1307_GetTime(&sec,&min,&hour);
            lcdrow2();
            send2lcd(day);
            LCD_puts("-");
            send2lcd(month);
            LCD_puts("-");
            send2lcd(year);
            LCD_puts("    ");
            if(hour&BITT5)
             {
                AM=0;
             }
            else
             {
                AM=1;
             }
            send2lcd(0x1F&hour);
            LCD_puts("-");
            send2lcd(min);
            LCD_puts("-");
            send2lcd(sec);
        }

    }
}          // MAIN ENDS HERE

void msdelay(unsigned int value)
{
unsigned int i,j;
for(i=0;i<value;i++)
for(j=0;j<2000;j++);
}

        // DS-1307 CODE START HERE
void I2C_Clock(void)
{
delay_us(1);
SCL = 1;
delay_us(1);
SCL = 0;
}

void I2C_Start()
{
SCL = 0; // Pull SCL low 
SDA = 1; // Pull SDA High
delay_us(1); 
SCL = 1; //Pull SCL high
delay_us(1); 
SDA = 0; //Now Pull SDA LOW, to generate the Start Condition
delay_us(1); 
SCL = 0; //Finally Clear the SCL to complete the cycle
}

void I2C_Stop(void)
{
SCL = 0; // Pull SCL low
delay_us(1);
SDA = 0; // Pull SDA low
delay_us(1); 
SCL = 1; // Pull SCL High
delay_us(1); 
SDA = 1; // Now Pull SDA High, to generate the Stop Condition
}

void I2C_Write(unsigned char dat)
{
unsigned char i; 
for(i=0;i<8;i++) // loop 8 times to send 1-byte of data
  {
  SDA = dat & 0x80; // Send Bit by Bit on SDA line
  I2C_Clock(); // Generate Clock at SCL
  dat = dat<<1;
  }
  SDA = 1; // Set SDA at last
}

unsigned char I2C_Read(void)
{
unsigned char i,
dat=0x00; 
SDA=1; //Make SDA as I/P
for(i=0;i<8;i++) // loop 8times to read 1-byte of data
  {
  delay_us(1);
  SCL = 1; // Pull SCL High
  delay_us(1);
  dat = dat<<1; //dat is Shifted each time and
  dat = dat | SDA; //ORed with the received bit to pack into byte 
  SCL = 0; // Clear SCL to complete the Clock
  }
  return dat; // Finally return the received Byte*
}

void I2C_Ack()
{
SDA = 0; //Pull SDA low to indicate Positive ACK
I2C_Clock(); //Generate the Clock
SDA = 1; // Pull SDA back to High(IDLE state)
}

void I2C_NoAck()
{
SDA = 1; //Pull SDA high to indicate Negative/NO ACK
I2C_Clock(); // Generate the Clock
SCL = 1; // Set SCL */
}

void delay_us(unsigned int us_count)
{
while(us_count!=0)
{
  us_count--;
}
}

void DS1307_Init()
{
I2C_Start(); // Start I2C communication 
DS1307_Write(DS1307_ID); // Connect to DS1307 by sending its ID on I2c Bus
DS1307_Write(CONTROL); // Select the Ds1307 ControlRegister to configure Ds1307 
DS1307_Write(0x00); // Write 0x00 to Control register to disable SQW-Out 
I2C_Stop(); // Stop I2C communication after initilizing DS1307 
}

void DS1307_Write(unsigned char dat)
{
I2C_Write(dat); // Connect to DS1307 by sending its ID on I2c Bus
I2C_Clock();
}

unsigned char DS1307_Read()
{
unsigned char dat;
dat = I2C_Read(); // Connect to DS1307 by sending its ID on I2c Bus
return(dat);
}

void DS1307_SetTime(unsigned char ss, unsigned char mm, unsigned char hh)
{
I2C_Start(); // Start I2C communication 
DS1307_Write(DS1307_ID); // connect to DS1307 by sending its ID on I2c Bus
DS1307_Write(SEC_ADDRESS); // Select the SEC RAM address
DS1307_Write(ss); // Write sec on RAM address 00H
DS1307_Write(mm); // Write min on RAM address 01H
if(AM==1)
DS1307_Write(BIT6|hh); // Write hour on RAM address 02H 
else
DS1307_Write(BIT5|BIT6|hh);
I2C_Stop(); // Stop I2C communication after Setting the Time
}

void DS1307_SetDate(unsigned char dd, unsigned char mm, unsigned char yy)
{
I2C_Start(); // Start I2C communication 
DS1307_Write(DS1307_ID); // connect to DS1307 by sending its ID on I2c Bus
DS1307_Write(DATE_ADDRESS); // Request DAY RAM address at 04H 
DS1307_Write(dd); // Write date on RAM address 04H
DS1307_Write(mm); // Write month on RAM address 05H
DS1307_Write(yy); // Write year on RAM address 06h 
I2C_Stop(); // Stop I2C communication after Setting the Date
}

void DS1307_GetTime(unsigned char *s_ptr,unsigned char *m_ptr,unsigned char *h_ptr)
{
I2C_Start(); // Start I2C communication 
DS1307_Write(DS1307_ID); // connect to DS1307 by sending its ID on I2c Bus
DS1307_Write(SEC_ADDRESS); // Request Sec RAM address at 00H 
I2C_Stop(); // Stop I2C communication after selecting Sec Register 
I2C_Start(); // Start I2C communication
DS1307_Write(0xD1); // connect to DS1307( under Read mode) //by sending its ID on I2c Bus 
*s_ptr = DS1307_Read();
I2C_Ack(); // read second and return Positive ACK
*m_ptr = DS1307_Read();
I2C_Ack(); // read minute and return Positive ACK
*h_ptr = DS1307_Read();
I2C_NoAck(); // read hour and return Negative/No ACK 
I2C_Stop(); // Stop I2C communication after reading the Time
}

void DS1307_GetDate(unsigned char *d_ptr,unsigned char *m_ptr,unsigned char *y_ptr)
{
I2C_Start(); // Start I2C communication 
DS1307_Write(DS1307_ID); // connect to DS1307 by sending its ID on I2c Bus
DS1307_Write(DATE_ADDRESS); // Request DAY RAM address at 04H 
I2C_Stop(); // Stop I2C communication after selecting DAY Register   
I2C_Start(); // Start I2C communication
DS1307_Write(0xD1); // connect to DS1307( under Read mode) // by sending its ID on I2c Bus 
*d_ptr = DS1307_Read();
I2C_Ack(); // read Day and return Positive ACK
*m_ptr = DS1307_Read();
I2C_Ack(); // read Month and return Positive ACK
*y_ptr = DS1307_Read();
I2C_NoAck(); // read Year and return Negative/No ACK 
I2C_Stop(); // Stop I2C communication after reading the Time
}

                                                                                //  LCD CODE START HERE

void LCD_delay(unsigned char ms)
{
  unsigned char n;
  unsigned int i;
  for (n=0; n<ms; n++)
  {
    for (i=0; i<LCD_DELAY; i++); /* For 1 ms */
  }
}

void LCD_enable()
{
LCD_en = 0; /* Clear bit*/
LCD_delay(5);
LCD_en = 1; /* Set bit */
}

void LCD_command(unsigned char command)
{
unsigned long temp;
  temp = P1->PIN;
  LCD_rs = 0; /* Clear bit */
  P1->DOUT = (temp & 0xFFFFFFF0)|((command>>4) & 0xFFFFFF0F);
  LCD_enable();
  temp = P1->PIN;
  P1->DOUT = (temp & 0xFFFFFFF0)|(command & 0xFFFFFF0F);
  LCD_enable();
  LCD_delay(5);
}


void LCD_putc(unsigned char ascii)
{
unsigned long temp;
  temp = P1->PIN;
  LCD_rs = 1;
  P1->DOUT = (temp & 0xF0)|((ascii>>4) & 0x0F);
  LCD_enable();
  temp = P1->PIN;
  P1->DOUT = (temp & 0xF0)|(ascii & 0x0F);
  LCD_enable();
  LCD_delay(5);
}

void LCD_puts(unsigned char *lcd_string)
{
  while (*lcd_string)
  {
    LCD_putc(*lcd_string++);
  }
}

void LCD_init()                // INTERFACE THE LCD IN 4 BIT MODE
{
    LCD_en=1;       /* Set bit */
    LCD_rs=0;         /* Clear bit  */
    LCD_command(0x33);
    LCD_command(0x32);
    LCD_command(0x28);
    LCD_command(0x0C);
    LCD_command(0x06);
    LCD_command(0x01); /* Clear */
    msdelay(400);
}

void send2lcd(unsigned char value)
{
unsigned char buf = 0;
buf = value & 0xF0; /* Filter for high byte */
buf = (buf>>4)|(0x30); /* Convert  to ascii code */
LCD_putc(buf); /* Show on LCD */
buf = value & 0x0F; /* Filter for low byte */
buf = buf | 0x30;        /* Convert to ascii code */
LCD_putc(buf); /* Show on LCD */
}
 
Last edited:

If you want to do bit-bang I2C, you need to learn about GPIO mode control. The x51 code is written for open drain GPIO pins and doesn't work with push-pull mode, which can be expected as default IO mode of ARM processors.

It's o.k. for many simple I2C devices (e.g. DS1307) to drive SCL in a push-pull manner, but not SDA. The SDA driver needs to switch between '0' and 'Z' rather than '0' and '1'. High level is only driven by the external pull-up resistor.

As any advanced microcontroller, NUC029LAN has a dedicated I2C interface that could be used to communicate with DS1307.
 

If you want to do bit-bang I2C, you need to learn about GPIO mode control. The x51 code is written for open drain GPIO pins and doesn't work with push-pull mode, which can be expected as default IO mode of ARM processors.

It's o.k. for many simple I2C devices (e.g. DS1307) to drive SCL in a push-pull manner, but not SDA. The SDA driver needs to switch between '0' and 'Z' rather than '0' and '1'. High level is only driven by the external pull-up resistor.

As any advanced microcontroller, NUC029LAN has a dedicated I2C interface that could be used to communicate with DS1307.
Yes, I Agree with you.
But i have already configured both the lines as OPEN Drain
C:
P3->PMD = (P3->PMD & (~GPIO_PMD_PMD6_Msk)) | (GPIO_PMD_OPEN_DRAIN << GPIO_PMD_PMD6_Pos);
P3->PMD = (P3->PMD & (~GPIO_PMD_PMD7_Msk)) | (GPIO_PMD_OPEN_DRAIN << GPIO_PMD_PMD7_Pos);

and also added external pull up resistor of 1K, 4K7, 10K, non of them are working.
--- Updated ---

As any advanced microcontroller, NUC029LAN has a dedicated I2C interface that could be used to communicate with DS1307.
Agreed that NUC029 Controller has a inbuilt I2C, but cannot use it, because the PCB is being designed and fabricated long back which is being used right now for some purpose. and now the existing system is being updated by adding RTC to available port pins where PIN P3.6 and P3.7 is only available.
 
Last edited:

No open drain programming shown in the code.

There are possibly other violations of I2C protocol in the code. I suggest to run it stepwise in debugger and check against protocol specification. Or watch SDA and SCL with oscilloscope.
 

Take all of the initialisation code out of the main loop.
Your user coded delay functions may not be working, depending on the compiler and optimisation settings. I recommend using delay functions that are provided by your compiler (if any) or use a timer.
Susan
 

Take all of the initialisation code out of the main loop.
Your user coded delay functions may not be working, depending on the compiler and optimisation settings. I recommend using delay functions that are provided by your compiler (if any) or use a timer.
Susan
I have tested the delay function, but switching the LED ON and OFF using different delay value, it works properly.
--- Updated ---

Take all of the initialisation code out of the main loop.
Your user coded delay functions may not be working, depending on the compiler and optimisation settings. I recommend using delay functions that are provided by your compiler (if any) or use a timer.
Susan
I even tried interfacing 24C16 Eeprom with 4K7 pull up resistor, also it works,
But dont know what's the issue with RTC.
I even tried changing DS1307 IC
 
Last edited:

Hi,

But dont know what's the issue with RTC.
I guess it's not a problem of the RTC, it rather is because of not keeping to I2C specifications.

Show scope pictures. Both SCL and SDA of "IDLE plus the first two or three bytes".

Klaus
--- Updated ---

Hi,
SDA = dat & 0x80; // Send Bit by Bit on SDA line
Don't know if this may cause a problem:
SDA is a bit. Usually the value is 0 or 1 (0x00 or 0x01).
But in your case it is 0x00 or 0x80

delay_us(1); SCL = 1; delay_us(1); SCL = 0;
This causes worst case about 500kHz of SCK frequency, but max standard I2C frequency is 400kHz...if it works correctly.
But why should it work correctly.
A "delay by fixed code" isn't a good idea. It is not portable between different microcontrollers (this is what you try to do) nor does it care about the really used clock freqyency.
With this code it seems one count loop takes 1us ...... but for the ns_delay you run 2000 counts for 1ms, which seems to be 0.5us per loop.
It doesn't match.

There may be more issues.

All in al there are two major issues:
* bed programming style
* not keeping on I2C specification

Klaus
 
Last edited:

Hi,


I guess it's not a problem of the RTC, it rather is because of not keeping to I2C specifications.

Show scope pictures. Both SCL and SDA of "IDLE plus the first two or three bytes".

Klaus
--- Updated ---

Hi,

Don't know if this may cause a problem:
SDA is a bit. Usually the value is 0 or 1 (0x00 or 0x01).
But in your case it is 0x00 or 0x80


This causes worst case about 500kHz of SCK frequency, but max standard I2C frequency is 400kHz...if it works correctly.
But why should it work correctly.
A "delay by fixed code" isn't a good idea. It is not portable between different microcontrollers (this is what you try to do) nor does it care about the really used clock freqyency.
With this code it seems one count loop takes 1us ...... but for the ns_delay you run 2000 counts for 1ms, which seems to be 0.5us per loop.
It doesn't match.

There may be more issues.

All in al there are two major issues:
* bed programming style
* not keeping on I2C specification

Klaus
Have attached the scope data. where yellow lines are for SCL
 

Attachments

  • WhatsApp Image 2021-04-26 at 1.07.26 PM.jpeg
    WhatsApp Image 2021-04-26 at 1.07.26 PM.jpeg
    112.6 KB · Views: 105

Hi,

I guess you see yourself that this is far from I2C signals - like they should be.
See the timing diagrams given in the datatsheets and see the timing diagrams in the I2C specification.

A bit of anlyzing your signals and the issues:
* IDLE = both lines HIGH (here your signal seem to be correct)
* your signal: toggle of SCL. Not according specification - but not necessarily causing a problem --> Don't do this
* your signal: SDA stays LOW while SCL makes crazy things. How it should be: 9 clear SCK cycles with SDA sending out 8 bits of address with correct timing related to SCL.
--> neither 9 SCL cycles, nor 8 bits of address sent out, nor timing is correct...it can´t work this way...

.. and so on.

Either
* write your own clean code form the scratch. step by step, each step verified with I2C specification before you write code for the next step. -
* or use proven code

Klaus
 

Hi,

I guess you see yourself that this is far from I2C signals - like they should be.
See the timing diagrams given in the datatsheets and see the timing diagrams in the I2C specification.

A bit of anlyzing your signals and the issues:
* IDLE = both lines HIGH (here your signal seem to be correct)
* your signal: toggle of SCL. Not according specification - but not necessarily causing a problem --> Don't do this
* your signal: SDA stays LOW while SCL makes crazy things. How it should be: 9 clear SCK cycles with SDA sending out 8 bits of address with correct timing related to SCL.
--> neither 9 SCL cycles, nor 8 bits of address sent out, nor timing is correct...it can´t work this way...

.. and so on.

Either
* write your own clean code form the scratch. step by step, each step verified with I2C specification before you write code for the next step. -
* or use proven code

Klaus
Yes, KlaustST,
You are right, the I2C signals what i was getting was purely a garbage, Even it was quite difficult for me to understand the data signals after verifying with the scope data's.

But Yes, Have Finally done it,
Have to do a lot of changes in the software part.
After Monitoring each and every clock and data part, Now things are working perfect.
But still i am surprised, how this code works great with 89S52.

Thanks to all
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top