Go Down

Topic: RTC DS1307/IN1307 - Very innacurate time (Read 6200 times) previous topic - next topic

greatidea

Sep 18, 2014, 06:28 am Last Edit: Sep 18, 2014, 06:44 am by greatidea Reason: 1
Hello guys,

Starting with my journey in experimentation and projects in Arduino UNO R3, here comes the chance to display time. I've started using the RTC DS1307 coz it has a dedicated timing chip and a dedicated 32Khz oscillator. I have tried various standard sketches and it successfully shows time, but the problem I'm facing is that whenever I disconnect the RTC from Arduino or power off the Arduino, and then reconnect it, the time drifts backwards ranging from 2 minutes to 10 minutes and sometimes hours. I have checked the 3V button cell and it is new and delivers 2.95volts at present, so the battery wouldn't be a problem. I have checked whether the battery connects properly to the IC and I can read the voltage on the IC's pins when it is not connected to the Arduino.

I have also tried replacing the ds1307 with another IC IN1307 (same function, same pinout), but the problem still remains and time just drifts backwards. Forget about accuracy, there are delays of minutes to hours in one single day if the RTC is left idle with the battery plugged into it. I've even tried replacing the crystal of RTC with another 32Khz (cylindrical micro crystal), but the result remains the same. Please advice. :smiley-roll:

Peter_n

You will find many problems with DS1307 and DS1302 on this forum. The modules from Sparkfun and Adafruit do work, but many modules from Ebay don't. I read once that someone used a trick by switching the power, but I can't find it in the forum right now.
The new DS3231 has a crystal inside the chip and will run, even the cheap modules from Ebay.


greatidea

Thanks for the suggestion. I was already looking for a DS3231 enabled RTC, but it not so easily available in India(not even on ebay.in). Considering the number of people trying the DS1307 RTC, my results are way beyond the norm and hence wanted to see if anybody faced such a problem.

Also I was wondering can we do something like use the Arduino UNO's default timing mechanism and attach the 3v battery onto the board itself. Can this be feasible?

billhowl

Read this,
Quote
with some coding that improves accuracy from (typically) 5 - 10 seconds lost per day to maybe a couple of seconds a week or better.

http://forum.arduino.cc/index.php?topic=87846.msg686532#msg686532

Peter_n

The microcontroller (ATmega328p) of the Arduino Uno is not designed for a battery operated clock. Some ATmega chips are, they have extra pins for a 32.768 kHz crystal.

Can you try to find a better module with the DS1307 ? Perhaps replace the 32.768 kHz crystal ?
Sometimes the crystal (the metal housing) is soldered to the board to make it work.

billhowl

The datasheets for the crystals I bought said specifically *not* to do that.  The heat of soldering can crack the crystal.

jremington

Quote
The microcontroller (ATmega328p) of the Arduino Uno is not designed for a battery operated clock. Some ATmega chips are, they have extra pins for a 32.768 kHz crystal.
The Arduino Uno is not designed to be used as a battery operated clock, but the ATmega328p is and it works very well. I use it in that mode for remote sensors and with proper choice of crystal, it keeps better time than the DS1307s I've tried.

The oscillator pins of the ATmega48/88/168/328 are designed to function with a 32768 Hz crystal, allowing Timer2 to form the heart of an interrupt-based real time clock while the processor sleeps. See the ATmega data sheet for details. The rest of the chip can be clocked with the internal 8 MHz RC oscillator when the CPU is needed for data processing.

Peter_n

jremington, thanks for the correction.

tylernt

Thanks jremington, today I learned something new.  :)

jremington

I personally think the designers of the chip did a fantastic job and their efforts should not be overlooked!

An additional cool feature of the Atmega chips is that you can use the 32768 Hz crystal to calibrate the 8 MHz internal oscillator, for much more accurate timing on RS232 transmissions, etc. 

If anyone is interested, and Just for completeness and future reference, here is the code for a wind gauge remote sensor, which counts pulses, keeps time with an RTC and outputs RPM data on the uart.  This was done using Atmel Studio IV, so is not directly Arduino compatible.

Code: [Select]

// wind gauge controller, ATmega168
// This code incorporates an RTC formed by Timer2 and a 32768 xtal on OSC pins
// The RC oscillator is calibrated by comparison with the 32768 standard,
// so that RS232 baud rates are more accurate.

// Dorkboard platform!
// choose fuses for 8 MHz internal clock, LP xtal (32768 Hz) on OSC pins
// sjames dot remington @ gmail.com
// Using Atmel Studio IV on this project


#include <avr/io.h>

//8 MHz internal RC , internal divider set to 8 programmatically

#define F_CPU 1000000UL
#include <util/delay.h>
#include <stdio.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <avr/io.h>

// Global variables for RTC

volatile unsigned int ticks = 0;  //global tick counter, increments @ 128.00 Hz
volatile unsigned char RTC_buffer[]={'0','0','0','0','0','0',0}; //daytime, with zero terminator
volatile unsigned int dayno=0; //day number since startup

/*
** long, approximate delays
*/

void delay1ms(unsigned int ms)
{
while (ms--) _delay_ms(1);
}

void OSCCAL_calibration(void);

#include "uart.c"


/*
// initialize Timer2 as asynchronous 32768 Hz timing source
*/

void timer_init(void) {

   TCCR0B = 0;  //stop Timer 0
   TCCR2B = 0;  //stop Timer 2

    TIMSK2 = 0; // disable Timer 2 interrupts
    ASSR = (1<<AS2); // select asynchronous operation of Timer2
    TCNT2 = 0;              // clear Timer 2 counter
    TCCR2A = 0;  //normal count up mode, no port output
TCCR2B = (1<<CS20); // select precaler = 1 : 32768 Hz / 256 => 7.8125 msec/tick
// TCCR2B = (1<<CS22) | (1<<CS20); // select precaler: 128 => 1 sec between each overflow

    while (ASSR & ((1<<TCN2UB)|(1<<TCR2BUB))); // wait for TCN2UB and TCR2BUB to be cleared

    TIFR2 = (1<<TOV2); // clear interrupt-flag
    TIMSK2 = (1<<TOIE2); // enable Timer2 overflow interrupt

/* initialize timer 0 for 4kHz square wave PWM on PD6

   TCCR0A = _BV(COM0A1) | _BV(WGM01) | _BV(WGM00);      //OC0A (PD6/D6), set at BOTTOM, clear on Compare Match, Mode =3 FAST PWM
   TCCR0B = (1<<CS01);  // Start Timer 0, no prescaling, 1MHz/256 = ~ 4 kHz
   OCR0A = 127;  //square wave, 50% duty cycle
*/

//  alternative to 4kHz output above: 15 Hz output
//  TCCR0A = _BV(COM0A1) | _BV(COM0B1) | _BV(WGM01) | _BV(WGM00);      // OC0A (PD6) and OC0B (PD5)
//   TCCR0B = (1<<CS02);  // /256 = 15 Hz = 20 mph calibration output! 

}

// set up I/O for printf function

FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);


/*
** MAIN
*/
int main(void)
{

unsigned int period,rpm,maxrpm,nsamples;
unsigned long average;

DDRB = (1<<PB5); //output LED Arduino Pin 13
DDRD = (1<<PD1)|(1<<PD6); //output on Arduino Pin 6, UART TX
DDRC = (1<<PC1); //all PORTC input except PC1

PORTC = (1<<PC1); //power for the wind gauge
PORTB |= (1<<PB5); //OFF on Dorkboard, ON on Arduino

OSCCAL_calibration();

// reprogram timers:
timer_init();

stdout = &uart_str;
uart_init(4800);  //0.2% accuracy at 1 MHz clock speed
delay1ms(3000);

printf("Anemom_1.0 ,%u,%s\r",dayno,RTC_buffer);

sei();  //enable interrupts (Timer 2)

nsamples = 0;
average = 0;

char ten_seconds=RTC_buffer[4]; //starting value of ten seconds counter

//first pass: synchronizes edges of wind gauge output with ~3 second timeout

ticks=0;
while( ((PINC&(1<<PC0)) ==0) && (ticks<384)); //seeing white, wait till black
while( ((PINC&(1<<PC0)) !=0) && (ticks<384)); //seeing black, wait till white

while(1){

//count timer ticks for 1 revolution (2 hi/low cycles on input)

ticks=0;
PORTB |= (1<<PB5); //off //now time one full revolution
while( ((PINC&(1<<PC0)) ==0) && (ticks<384)); //seeing white, wait till black
PORTB &= ~(1<<PB5); //on
while( ((PINC&(1<<PC0)) !=0) && (ticks<384)); //seeing black, wait till white
PORTB |= (1<<PB5); //off
while( ((PINC&(1<<PC0)) ==0) && (ticks<384)); //seeing white, wait till black
PORTB &= ~(1<<PB5); //on
while( ((PINC&(1<<PC0)) !=0) && (ticks<384)); //seeing black, wait till white
PORTB |= (1<<PB5); //off

period = ticks;
if (period >= 384) period = 0; //stopped

// this version for the 12/2013 modified Weathertronics anemometer, 2 low/high per revolution
// 1 rpm = 60*128 ticks = 7680

if(period) rpm = 7860UL/period; //convert to rpm (60 mph ~ 1400 rpm, so this should be safe)
else rpm = 0;
if (rpm > maxrpm) maxrpm = rpm;
average = average + (unsigned long) rpm;

nsamples++;
if(RTC_buffer[4] != ten_seconds) {
ten_seconds = RTC_buffer[4];
if (nsamples > 0) {
average=average/nsamples;
}

// output data on uart line

printf("%u,%s,%u,%u\r",dayno,RTC_buffer,(unsigned int) average,maxrpm);

//after manual calibration, conversion factor is mph = rpm*0.044275 or rpm/22.586

nsamples = 0;
average = 0;
maxrpm = 0;
}
}
}

//******************************************************************
//  Timer2 Interrupt Service
//  32 kKz / 256 = 128 Hz (7.8125 ms/tick)
//  provides global tick timer and RTC

//ISR (TIMER2_COMPA_vect) {
ISR (TIMER2_OVF_vect) {
static uint8_t counter=128;
uint8_t i;
char t[6];

ticks++;
counter--;
if(!counter) { //1 second has passed

counter=128;

// RTC function

for (i=0;i<6;i++) t[i]=RTC_buffer[i]; //make a copy, so don't change digit more than once on rollover

    t[5]++; // increment second

   if (t[5] > '9')
{
t[5]='0'; // increment ten seconds
t[4]++;
if ( t[4] > '5')
{
t[4]='0';
t[3]++; // increment minutes
    if (t[3] > '9')
    {
t[3]='0';
t[2]++; // increment ten minutes

if (t[2] > '5')
{
t[2]='0';
t[1]++; // increment hours
char b = t[0]; // tens of hours, handle rollover at 19 or 23
      if ( ((b < '2') && (t[1] > '9')) || ((b=='2') && (t[1] > '3')) )
    {
t[1]='0';
t[0]++; // increment ten hours and day number, if midnight rollover
if (t[0] > '2') {t[0]='0'; dayno++;}
    }
}
}
}
}
for (i=0;i<6;i++) RTC_buffer[i]=t[i];  //copy updated time
  }
}


//
// Calibrate the internal OSCCAL byte, using the external
// 32768 Hz crystal as reference. Sets system clock to 1 MHz
//


void OSCCAL_calibration(void)  //This version specific to ATmegaXX8 (tested with ATmega168)
{
    unsigned char calibrate = 0; //FALSE;
    int temp;
    unsigned char tempL;

    CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable
    // set prescaler = 8, Internal RC 8Mhz / 8 = 1Mhz
    CLKPR = (1<<CLKPS1) | (1<<CLKPS0);

    TIMSK2 = 0;             //disable OCIE2A and TOIE2

    ASSR = (1<<AS2);        //select asynchronous operation of timer2 (32,768kHz)

    OCR2A = 200;            // set timer2 compare value

    TIMSK0 = 0;             // disable all interrupt sources

    TCCR1B = (1<<CS10);     // start timer1 with no prescaling
    TCCR2B = (1<<CS20);     // start timer2 with no prescaling (ATmega169 is TCCR2A!)

    while ((ASSR & 0x01) | (ASSR & 0x04));       //wait for TCN2UB and TCR2BUB to be cleared

    // wait for external crystal to stabilise
delay1ms(2000);

     while(!calibrate)
    {
        cli(); // mt __disable_interrupt();  // disable global interrupt

        TIFR1 = 0xFF;   // delete TIFR1 flags
        TIFR2 = 0xFF;   // delete TIFR2 flags

        TCNT1H = 0;     // clear timer1 counter
        TCNT1L = 0;
        TCNT2 = 0;      // clear timer2 counter

        while ( !(TIFR2 && (1<<OCF2A)) );   // wait for timer2 compareflag

        TCCR1B = 0; // stop timer1

        sei(); // __enable_interrupt();  // enable global interrupt

        if ( (TIFR1 && (1<<TOV1)) )
        {
            temp = 0xFFFF;      // if timer1 overflows, set the temp to 0xFFFF
        }
        else
        {   // read out the timer1 counter value
            tempL = TCNT1L;
            temp = TCNT1H;
            temp = (temp << 8);
            temp += tempL;
        }

        if (temp > 6250)
        {
            OSCCAL--;   // the internRC oscillator runs too fast, decrease the OSCCAL
        }
        else if (temp < 6120)
        {
            OSCCAL++;   // the internRC oscillator runs too slow, increase the OSCCAL
        }
        else
            calibrate = 1; //TRUE;   // the internal RC is correct

        TCCR1B = (1<<CS10); // start timer1
    }
}

tylernt


you can use the 32768 Hz crystal to calibrate the 8 MHz internal oscillator, for much more accurate timing
Clever.  8)

Peter_n

Someone just posted this: http://forum.arduino.cc/index.php?topic=267625.0
The DS3231 is very good.

pranav2016

my ds 1307 is having problem like...when i upload the program..it starts heating...and in the serial monitor i got the message "RTC is not running"

INTP

Make sure the battery you are using is an LIR rechargeable.

You have to modify some modules to use standard non-rechargeable 2032 batteries.

If you have a non-rechargeable battery and the module is trying to charge it, bad things happen.

Go Up