NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Basic Electronics » accurate time for a real time clock

March 12, 2013
by cwmilost
cwmilost's Avatar

i been seeing lots of people having problems not having accurate clocks using nerdkits. tip that i believe works well is using timer1 instead of timer0. this has advantages for getting a better resolution. this is because timer1 is 16 bits instead of 8 bits like timer0 . you can use a 256 prescaler instead of 1024 to get less round off error when it divides the clock cycle since it not a floating point register. 14745600/256, or 57600 increments per second you can use this to interrupt every 1 sec. you can fine tune 57600 by adding to it to slow clock down or subtract from it to speed clock up. this is an easy way to get more accurate timing for anything. you will probalbly have to use a different value than 57638 depending on your circuit.try it out i want to see how accurate people can get there real time clocks by using this approach. try note: this code the interrupt fires every 1 sec not every 1/100 th of a second

   void realtimeclock_setup() {
    TCCR1B = 4;//TCCR1B |= (1<<CS02) ;setting reg with 00000100
    OCR1A = 57638;// clocked from CLK/256
  // which is 14745600/256, or 57600 increments per second
  //used 57638
    TCCR1B |= _BV(WGM12); // CTC mode
    TIMSK1 = _BV(OCIE1A); // _BV is bit value same as TIMSK1 |= (1<<OCIE1A);interrupt on OCR0A match
}

SIGNAL(TIMER1_COMPA_vect) {
// when Timer1 gets to its Output Compare value,
// one second has elapsed (1 second).
//updates time and date
  the_sectime++;}
April 07, 2013
by Blackwatch42nd
Blackwatch42nd's Avatar

CW, I've been looking at the same problem and came up with almost the same way of fine tuning the clock. I stuck with the prescale of 1024 and went to timer1 to use 14400 as the the trigger varying this number to accurize the clock. I found mine runs a little fast using this number. Here's the code I'm using:

// realtimeclock1.c (variation)
// for NerdKits with ATmega168 
// mrobbins@mit.edu

#define F_CPU 14745600

//  ??? #include #include #include #include #include #include 
#include <stdio.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h" 
#include "../libnerdkits/lcd.h" 
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:

void realtimeclock_setup() 
{ 
    // setup 16 bit Timer1: TCCR1A and TCCR1B
    // Insure all of bits in 1A are reset to default

    TCCR1A = 0x00;

    // CTC (Clear Timer on Compare Match mode): Set WGM12 to "1"
    // Set prescale to 1024: Set CS12, CS11 and CS10 to "101"
    // Insure all other Time/Count1 Control Register B bits are reset: thus "00001101" -> 0x0D

    TCCR1B = 0x0D;

    // set TOP to 14399 
    // because it counts 0, 1, 2, ... 142, 14399, 0, 1, 2 ... 
    // so 0 through 14399 equals 14400 events (approx interrrupt 1/sec)

    OCR1A = 14399;

    // enable interrupt on compare event

    TIMSK1 |= (1<<OCIE1A); 
}

// These variables are marked "volatile" because they modified 
// by an interrupt handler. Without the "volatile" marking, 
// the compiler might just assume that it doesn't change in 
// the flow of any given function (if the compiler doesn't 
// see any code in that function modifying it -- sounds 
// reasonable, normally!). 
// 
// But with "volatile", it will always read it from memory 
// instead of making that assumption.

volatile int8_t time_seconds; 
volatile int8_t time_minutes;
volatile int8_t time_hours;

// Interrupt Handler

ISR(TIMER1_COMPA_vect) 
{ 
// when Timer1 gets to its Output Compare value, 
// approx one second has elapsed (1 second). 
    if (time_seconds++ == 59)
    {
        time_seconds = 0;
        if (time_minutes++ == 59)
        {
            time_minutes = 0;
            time_hours++;
        }
    }
}

int main() 
{ 
    realtimeclock_setup();

    // init lcd

    lcd_init(); 
    FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE); 
    lcd_home();

    // init serial port

    uart_init(); FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW); 
    stdin = stdout = &uart_stream;

    // turn on interrupt handler

    sei();

    while(1) 
    { 
        lcd_home(); 
        fprintf_P(&lcd_stream, PSTR("%02i:%02i:%02i"), (int) time_hours, (int) time_minutes, (int) time_seconds); 
    } 
return 0; 
}

But, I've a couple of observations. As you might notice, I'm also tracking Hours and Minutes but do so within the interrupt routine. Could not decide if this would effect the timing but don't think so. Opinions?

Also notice line 7. In the downloaded code there were no line feeds and this makes it hard to read and pares. There were several "#include" statements, one after another, leading me to believe that the file may be corrupted. Can someone check that? Thanx

Let me know what you think.

John

April 08, 2013
by pcbolt
pcbolt's Avatar

Hi John -

The code inside the interrupt should be fine. It should execute very quickly. If you are ever really curious about how long it will take to execute you could look at the assembly listing (files that end with *.ass) which can be generated by tweaking the makefile or by typing "make filename.ass". You then just need to count the number of code lines inside the ISR and multiply by 68 nSec. Some assembly instructions take twice as long but you get a general idea.

In the "realtimeclock.c" listing the only header file you don't have included is <stdlib.h>. Not sure you really need it in your code though.

April 08, 2013
by pcbolt
pcbolt's Avatar

Hi John -

The code inside the interrupt should be fine. It should execute very quickly. If you are ever really curious about how long it will take to execute you could look at the assembly listing (files that end with *.ass) which can be generated by tweaking the makefile or by typing "make filename.ass". You then just need to count the number of code lines inside the ISR and multiply by 68 nSec. Some assembly instructions take twice as long but you get a general idea.

In the "realtimeclock.c" listing the only header file you don't have included is <stdlib.h>. Not sure you really need it in your code though.

April 08, 2013
by pcbolt
pcbolt's Avatar

Sorry for the double post. One other thing...the clock is only as accurate as the oscillator which is subject to changes due to temperature, shock, etc.

Post a Reply

Please log in to post a reply.

Did you know that reading a double floating point variable with scanf requires "%lf" for "long float"? Learn more...