sample RTC code

An advanced Arduino-compatible board with ATmega1284P and ATmega32u2 MCUs for experienced users. More RAM than a Mega, but in a convenient Uno-style form factor. [Product page]
Post Reply
niels
Posts: 7
Joined: Thu Aug 22, 2013 12:43 am

sample RTC code

Post by niels » Thu Aug 22, 2013 2:35 pm

One of the nice features of the goldilocks is the built-in RTC timer. I hadn't tried coding with interrupts in Arduino before, and as some of the examples I found didn't work I decided to code my own.

A few tips for those in the same situation I was in earlier today:
- don't use routines that require interrupts (such as delay()) in your interrupt handlers.
- keep your interrupt handler as quick as possible
- check the datasheet, setting some registers (ASSR) can corrupt others, so order is important.
- the largest prescalar is 1024x, so the longest delay between interrupts is 8 seconds. You need a counter if you want increase this (as in the example).

Code: Select all

int led = 13;         // led pin
int led_state = 0;    // initial led state
int toggle_led = 0;   // flag to indicate led needs to be toggled
int flash_led = 0;    // flag to indicate led needs to be flashed
int count_int = 0;    // count 

void setup() {
  // disable interrupts
  noInterrupts();

  // set out LED to output and set its initial state
  pinMode(led, OUTPUT);
  digitalWrite(led, led_state);
  
  // enable crystal clock source
  ASSR = (1 << AS2);
  
  // use CTC mode
  TCCR2A = (1 << WGM21);

  // set prescalar to 1024x, each count is 0.03125 seconds.
  TCCR2B = ( (1<<CS22) | (1<<CS21) | (1<<CS20) );

  // set OCR2A to 32 (1 second)
  OCR2A = 32;
  
  // enable timer compare interrupt
  TIMSK2 = (1 << OCIE2A);

  // enable interrupts
  interrupts();
}

void loop() {
  // If the toggle_led flag is set, toggle the led
  if (toggle_led == 1) {
    toggle_led = 0; // clear the flag
    led_state = ~led_state;
    digitalWrite(led, led_state);
  }
  
  // if the flash_led is set, flash the led a few times
  if (flash_led == 1) {
    flash_led = 0; // clear the flag
    for (int i = 0; i<4; i++) {
      digitalWrite(led, HIGH);
      delay(80);
      digitalWrite(led, LOW);
      delay(80);
    }
    // set the led back to what it was
    digitalWrite(led, led_state);
  }
  
}

ISR(TIMER2_COMPA_vect){ 
  // increment our interrupt counter
  count_int++;
  
  // set the toggle_led flag to true
  toggle_led = 1;
  
  // set the flash_led count to true every 10th interrupt
  if ((count_int % 10) == 0) {
    flash_led = 1;
  }
  
  // reset the interrupt counter to zero after 60 interrupts
  if (count_int > 59) {
    count_int = 0;
  }
}

feilipu
Posts: 52
Joined: Fri Jul 19, 2013 6:17 am

Re: sample RTC code

Post by feilipu » Sat Aug 24, 2013 5:49 am

Hi Neils,

Some time ago I was looking for an implementation of the libc time functions, and found that avr-libc team have already put a version together. Here's a clone of their upstream repository.
https://github.com/vancegroup-mirrors/a ... /libc/time
I expect that sometime (soon?) they'll push this out into the avr-libc released version.

In the interim, I've been using it in my work, and found it works great. They have implemented almost all of the standard, and esoteric, time based functions that can be found on a standard linux libc time.h, except there is no timezone file. Timezone has to be set manually.

They've built an optimised assembler system_tick() function, that can be inserted into a naked interrupt. That means that the lowest overhead possible is needed to keep accurate time.

This is the code to set up Timer 2 as a 1 second interrupt.

Code: Select all

TIMSK2 &= ~( _BV(OCIE2B)|_BV(OCIE2A)|_BV(TOIE2) ); // Disable all Timer2 interrupts
ASSR = _BV(AS2); // set Timer/Counter2 to be asynchronous from the CPU clock with a second external clock (32,768kHz) driving it.
TCNT2  = 0x00; // zero out the counter
TCCR2A = 0x00; // no compares, no waveforms
TCCR2B = _BV(CS22)|_BV(CS20); // prescale the timer to be clock source/128 to make it exactly 1 second for every overflow to occur
while( ASSR & (_BV(TCN2UB)|_BV(OCR2AUB)|_BV(TCR2AUB))); // Wait until Timer2 update complete
TIMSK2 |= _BV(TOIE2); // set 8-bit Timer/Counter2 Overflow Interrupt Enable
This is for the interrupt.

Code: Select all

#include <time.h>
ISR(TIMER2_OVF_vect, ISR_NAKED)
{
    system_tick(); // the time.h tick
    asm volatile ( "reti" ); // return from naked interrupt 
}
I've also built the system_tick() into my port of freeRTOS, so that I never need to see it again.
https://sourceforge.net/projects/avrfreertos/
Now having the real time is fully automagic!

It would be great if someone who knows how to build Arduino libraries could assemble this avr-libc time.h code into a library, then anyone with a Goldilocks could easily use it. I'm afraid I don't know how to do this.

Regards, Phillip

feilipu
Posts: 52
Joined: Fri Jul 19, 2013 6:17 am

Re: sample RTC code

Post by feilipu » Thu Aug 29, 2013 2:18 pm

I've attached the library code (extracted from the clone of the avr-libc upstream repository mentioned above), with some sample initialisation code put into init_system_tick(), and some timer code borrowed from Contiki OS.

The time.h file includes all of the usage descriptions.
lib_time.zip
lib_time which includes avr-libc time.h functions
(70.83 KiB) Downloaded 294 times

Post Reply