Maker Pro
Maker Pro

DS1307 initializing

adel2014

Dec 1, 2013
22
Joined
Dec 1, 2013
Messages
22
hello , every one in the following code , the seven segments clock is working correctly but i don not need to loose the seconds every time i switched on , so i add " void ds1307_init() " but it always show - in seconds - 03 ; so please what is wrong ?

[I've put your code into a [CODE] block so the indentation can be shown -- KrisBlueNZ, moderator]

Code:
unsigned short mask(unsigned short num) {
    switch (num) {
    case 0 : return 0xC0;
    case 1 : return 0xF9;
    case 2 : return 0xA4;
    case 3 : return 0xB0;
    case 4 : return 0x99;
    case 5 : return 0x92;
    case 6 : return 0x82;
    case 7 : return 0xF8;
    case 8 : return 0x80;
    case 9 : return 0x90;
    } //case end
}

char seconds, minutes, hours, day, date, month, year;
char s1, s10, m1, m10, h1, h10, date1, date10, month1, month10, year1, year10;
char x = 0, a = 0, b = 0;
sbit swup   at portc.B6;
sbit swdown at portc.B7;
sbit swset  at portc.B5;
sbit buzzer at portc.B2;

void read_DS1307(void) {
    I2C1_Start();
    I2C1_Wr(0xD0);                      // DS1307 address + 0write
    I2C1_Wr(0x00);                      // second address
    I2C1_Repeated_Start();
    I2C1_Wr(0xD1);                      // DS1307 address + 1read
    seconds = I2C1_Rd(1);
    minutes = I2C1_Rd(1);
    hours   = I2C1_Rd(1);
    day     = I2C1_Rd(1);
    date    = I2C1_Rd(1);
    month   = I2C1_Rd(1);
    year    = I2C1_Rd(0);
    I2C1_Stop();
}

void transform(void) {
    seconds = Bcd2Dec(seconds);
    minutes = Bcd2Dec(minutes);
    hours   = Bcd2Dec(hours);
    date    = Bcd2Dec(date);
    month   = Bcd2Dec(month);
    year    = Bcd2Dec(year);

    s1      = seconds % 10;
    m1      = minutes % 10;
    h1      = hours   % 10;
    date1   = date    % 10;
    month1  = month   % 10;
    year1   = year    % 10;
    s10     = seconds / 10;
    m10     = minutes / 10;
    h10     = hours   / 10;
    date10  = date    / 10;
    month10 = month   / 10;
    year10  = year    / 10;
}

void display(void) {
    portb = mask(s1);      porta = 0; delay_ms(10);
    portb = mask(s10);     porta = 1; delay_ms(10);
    portb = mask(m1);      porta = 2; delay_ms(10);
    portb = mask(m10);     porta = 3; delay_ms(10);
    portb = mask(date1);   porta = 4; delay_ms(1);
    portb = mask(date10);  porta = 5; delay_ms(1);
    portb = mask(month1);  porta = 6; delay_ms(1);
    portb = mask(month10); porta = 7; delay_ms(1);
    portb = mask(year1);   porta = 8; delay_ms(1);
    portb = mask(year10);  porta = 9; delay_ms(1);
}

void ds1307_init(void) {
    int seconds = 0;
    I2C1_Start();
    I2C1_Wr(0xD0);                      // WR to RTC
    I2C1_Wr(0x00);                      // REG 0
    I2C1_Start();
    I2C1_Wr(0xD1);                      // RD from RTC
    seconds = Bcd2Dec(I2C1_Rd(0));      // Read current "seconds" in DS1307
    I2C1_Stop();
    seconds &= 0x7F;

    Delay_ms(300);

    I2C1_Start();
    I2C1_Wr(0xD0);                      // WR to RTC
    I2C1_Wr(0x00);                      // REG 0
    I2C1_Wr(Dec2Bcd(seconds));          // Start oscillator with current "seconds value
    I2C1_repeated_Start();
    I2C1_Wr(0xD0);                      // WR to RTC
    I2C1_Wr(0x07);                      // Control Register
    I2C1_Wr(0x80);                      // Disable squarewave output pin
    I2C1_Stop();
}

void main(void) {
    trisb = 0;
    trisa = 0;
    trisc = 0b11100000;
    portc = 0;
    I2C1_Init(100000);
    ds1307_init();

    while (1) {
        read_DS1307();
        transform();
        display();
    }
}
 
Last edited by a moderator:

KrisBlueNZ

Sadly passed away in 2015
Nov 28, 2011
8,393
Joined
Nov 28, 2011
Messages
8,393
I think the problem may be that you're not masking off the CH bit, bit 7 of the seconds register. The data sheet is not clear on what this bit does, but it seems it's set on startup. To ignore this bit, you need to change the line in read_DS1307() that reads
Code:
    seconds = I2C1_Rd(1);
to
Code:
   seconds = I2C1_Rd(1) & 0x7F;
 

adel2014

Dec 1, 2013
22
Joined
Dec 1, 2013
Messages
22
sorry , it does not working , the CH bit is set to enable the DS1307 oscillation , so it must be configured in the initializing ( from the data sheet) , but my code is always show (03) in the beginning
 

KrisBlueNZ

Sadly passed away in 2015
Nov 28, 2011
8,393
Joined
Nov 28, 2011
Messages
8,393
OK, here's ds1307_init():
Code:
01  void ds1307_init(void) {
02      int seconds = 0;
03      I2C1_Start();
04      I2C1_Wr(0xD0);  // WR to RTC
05      I2C1_Wr(0x00);  // REG 0
06      I2C1_Start();
07      I2C1_Wr(0xD1);  // RD from RTC
08      seconds = Bcd2Dec(I2C1_Rd(0));  // Read current "seconds" in DS1307
09      I2C1_Stop();
10      seconds &= 0x7F;

OK, you generate a start condition, send an address of 0xD0 which has the R/-W bit clear for a write, then you send an address of 0 (register 0 is the seconds byte), to set the DS1307's data pointer to 0. Then you send a repeated start condition, and you read one byte, convert it from BCD to a machine integer, then mask the machine integer value with 0x7F.

1. What is the difference between I2C1_Start() and I2C1_repeated_Start()? You have used I2C1_repeated_Start() in the next section, but not in the first part, above.

2. You need to apply the 0x7F mask to the raw BCD value that you get from the register. Masking the machine integer (after the conversion from BCD) with 0x7F will not have any effect because that value will never be greater than 99 (0x63). At least, it couldn't be greater than 0x63 if you masked off the top bit of the BCD value (before conversion from BCD to machine integer) as you should.

So you need to change line 08 from
Code:
seconds = Bcd2Dec(I2C1_Rd(0));
to
Code:
seconds = Bcd2Dec(I2C1_Rd(0) & 0x7F);

and you need to delete line 10.

Continuing with ds1307_init()...

Code:
11
12      Delay_ms(300);
13
14      I2C1_Start();
15      I2C1_Wr(0xD0);  // WR to RTC
16      I2C1_Wr(0x00);  // REG 0
17      I2C1_Wr(Dec2Bcd(seconds));  // Start oscillator with current "seconds value
18      I2C1_repeated_Start();
19      I2C1_Wr(0xD0);  // WR to RTC
20      I2C1_Wr(0x07);  // Control Register
21      I2C1_Wr(0x80);  // Disable squarewave output pin
22      I2C1_Stop();

OK, I get what you're doing. You read the seconds register, mask off the CH (clock halt) bit, then write it back, and you disable the square wave output. This ensures that the DS1307 is running.

Once you've fixed the bug with masking off the CH bit (item 2 above), have a think about another bug in this approach.

What happens if ds1307_init() is called when the seconds register is 59 and the minute is about to roll over? For example, say the time is 01:23:59. You will read a seconds value of 59, the DS1307's time will roll over to 01:24:00, then you will write the seconds register back with a value of 59. So the time will go forwards by 60 seconds.

Since you have a 0.3 second delay between the read and the write, the chance of this occuring is about one 0.3 in 60, or 1 in 200. So for every 200 times you call ds1307_init(), on average the clock will gain 60 seconds.
 

adel2014

Dec 1, 2013
22
Joined
Dec 1, 2013
Messages
22
thanks for supporting me Kris , but i changed the code as you say , the only change is the seconds always starts from 00 ; All i need is to keep seconds register at initializing , only start the DS1307 oscillator ( CH=1) without changing the seconds , so you can neglect my code and if you have a code that uses for my purpose , it will be fine
 

KrisBlueNZ

Sadly passed away in 2015
Nov 28, 2011
8,393
Joined
Nov 28, 2011
Messages
8,393
I don't have code to do that, but I've modified your ds1307_init() function. Try this.

Code:
#define DS1307_I2C_ADDR      0xD0 // I2C address of DS1307
#define DS1307_ADDR_WRITE    0x00 // Address offset for write command
#define DS1307_ADDR_READ     0x01 // Address offset for read command
#define DS1307_SECONDS_REG   0x00 // Register number for seconds and CH (clock halt) bit
#define DS1307_CTRL_REG      0x07 // Register number for control register
#define DS1307_CTRLREG_VALUE 0x80 // Value to write to DS1307 control register (0x80 = OUT high; no square wave)

void ds1307_init(void) {
    int seconds;

    I2C1_Start();
    I2C1_Wr(DS1307_I2C_ADDR + DS1307_ADDR_WRITE); // Request a write operation
    I2C1_Wr(DS1307_CTRL_REG);                     // Specify Control register for writing
    I2C1_Wr(DS1307_CTRLREG_VALUE);                // Write desired control register value
    I2C1_repeated_Start();                        // New I2C transaction
    I2C1_Wr(DS1307_I2C_ADDR + DS1307_ADDR_WRITE); // Request a write operation
    I2C1_Wr(DS1307_SECONDS_REG);                  // Specify Seconds register to set data pointer there
    I2C1_repeated_Start();                        // New I2C transaction
    I2C1_Wr(DS1307_I2C_ADDR + DS1307_ADDR_READ);  // Request a read operation
    seconds = I2C1_Rd(0);                         // Read current seconds register
    seconds &= 0x7F;                              // Mask off CH (clock halt) bit - enable the DS1307's clock
    I2C1_repeated_Start();                        // New I2C transaction
    I2C1_Wr(DS1307_I2C_ADDR + DS1307_ADDR_WRITE); // Request a write operation
    I2C1_Wr(DS1307_SECONDS_REG);                  // Specify Seconds register for writing
    I2C1_Wr(seconds);                             // Write same seconds value back, but with CH bit cleared
    I2C1_Stop();
}

Try that. If it doesn't work, post the code for your I2C access functions (I2C1_Wr(), I2C1_Rd(), I2C1_Start() and I2C1_repeated_Start()).
 
Last edited:

adel2014

Dec 1, 2013
22
Joined
Dec 1, 2013
Messages
22
I replaced the new " void ds1307_init " but it doesn't worked , the only change that it is always starts from - seconds - 25 , i don't understand why , but your code is seems to be good ??
 

KrisBlueNZ

Sadly passed away in 2015
Nov 28, 2011
8,393
Joined
Nov 28, 2011
Messages
8,393
I don't know what the problem is. But I don't think you should be writing to the seconds register every time your code starts up. The DS1307 is supposed to be used with a backup battery and it will remember the state of the CH bit. So if the CH bit is already 0 (clock is already running), there's no need to write the seconds register at all. So try this:
Code:
#define DS1307_I2C_ADDR      0xD0 // I2C address of DS1307
#define DS1307_ADDR_WRITE    0x00 // Address offset for write command
#define DS1307_ADDR_READ     0x01 // Address offset for read command
#define DS1307_SECONDS_REG   0x00 // Register number for seconds and CH (clock halt) bit
#define DS1307_CTRL_REG      0x07 // Register number for control register
#define DS1307_CTRLREG_VALUE 0x80 // Value to write to DS1307 control register (0x80 = OUT high; no square wave)

void ds1307_init(void) {
    int seconds;

    I2C1_Start();
    I2C1_Wr(DS1307_I2C_ADDR + DS1307_ADDR_WRITE); // Request a write operation
    I2C1_Wr(DS1307_CTRL_REG);                     // Specify Control register for writing
    I2C1_Wr(DS1307_CTRLREG_VALUE);                // Write desired control register value
    I2C1_repeated_Start();                        // New I2C transaction
    I2C1_Wr(DS1307_I2C_ADDR + DS1307_ADDR_WRITE); // Request a write operation
    I2C1_Wr(DS1307_SECONDS_REG);                  // Specify Seconds register to set data pointer there
    I2C1_repeated_Start();                        // New I2C transaction
    I2C1_Wr(DS1307_I2C_ADDR + DS1307_ADDR_READ);  // Request a read operation
    seconds = I2C1_Rd(0);                         // Read current seconds register
    if ((seconds & 0x80) == 0x80) {               // Clock is currently halted
        seconds &= 0x7F;                          // Mask off CH (clock halt) bit - enable the DS1307's clock
        I2C1_repeated_Start();                    // New I2C transaction
        I2C1_Wr(DS1307_I2C_ADDR + DS1307_ADDR_WRITE); // Request a write operation
        I2C1_Wr(DS1307_SECONDS_REG);              // Specify Seconds register for writing
        I2C1_Wr(seconds);                         // Write same seconds value back, but with CH bit cleared
    }
    I2C1_Stop();
}
 

ligo.george

Feb 2, 2013
12
Joined
Feb 2, 2013
Messages
12
MikroC Codes :
Code:
unsigned short read_ds1307(unsigned short address)
{
unsigned short temp
I2C1_Start();
I2C1_Wr(0xD0);
I2C1_Wr(address);
I2C1_Repeated_Start();
I2C1_Wr(0xD1);
temp = I2C1_Rd(0);
I2C1_Stop();
return(temp);
}
Code:
void write_ds1307(unsigned short address, unsigned short w_data)
{
I2C1_Start();
I2C1_Wr(0xD0);
I2C1_Wr(address);
I2C1_Wr(w_data);
I2C1_Stop();
}
Try the following links, it will help you.
Interfacing DS1307 RTC with PIC Microcontroller - MikroC
Digital Clock using PIC Microcontroller and DS1307 RTC - MikroC
 
Top