Examples of using Arduino/Atmega 16 bit hardware timer for digital clock

Arduino Mega with Atmega 1280 has four 16 bit timers, that could be used for various purposes, like time/frequency measurement, control with precise timing, PWM generation. Today I hope to explain how to use timer for clocks, timers (countdown) and other things, where You need µCPU to perform some tasks after precise period of time. I’ll give You two examples:

  • Pseudo 1 second timer
  • Real 1 second timer

How counter works? It is simple independent  16 bit accumulator, which value increases by 1 at clock cycle. 16 bit means that maximum counter’s value is 65536. When this value is reached counter starts counting from 0 again and gives hardware interrupt. Counter value could be changed any time. This is normal counter mode, Atmega 1280 offers total 14 operating modes.

Pseudo 1 second timer (1.048576 s)

This is the simplest example of timer, LED is turned on for “1 second”, and when turned off.

// timer example from electronicsblog.net
#define LED 13

boolean x=false;

ISR(TIMER1_OVF_vect) {
x=!x;
}

void setup() {

pinMode(LED, OUTPUT);

TIMSK1=0x01; // enabled global and timer overflow interrupt;
TCCR1A = 0x00; // normal operation page 148 (mode0);
TCNT1=0x0000; // 16bit counter register
TCCR1B = 0x04; // start timer/ set clock
};

void loop () {

digitalWrite(LED, x);

};

ISR(TIMER1_OVF_vect) {} is interrupt function, which is called when timer1 overflow interrupt occurs.  Here You should put task to perform after timer overflow. It is recommended to put as shorter code as You can. It’s precaution, because if another hardware interrupt occurs while performing the first’s tasks, CPU jumps to perform second’s interrupt’s routine, although first’s is unfinished. In this example I just invert variable “x”.

TIMSK1 is Interrupt Mask Register for timer 1 , we need to enable overflow interrupt, so we set LSB to 1;

TCCR1A - reset all bits to enable normal timer mode.

TCNT1 is 16 bit register, that saves counter value. It is set to 0×0000 to make sure that counter starts from 0.

TCCR1B register’s 3 first bits saves value of counter clock prescaler:

By setting TCCR1B register to 0×04 we using /256 prescaler. Atmega in Arduino Mega board is clocked at 16 MHz, accordingly timer’s  clock frequency is 16000000/256=62500 Hz. Because timers max value is 65536 it overflows every 65536/62500 =1.048576 seconds. At first look difference from real 1 second is small, but if clock uses this 1.048576 s interval as 1 second it will be 3 minutes behind after 1 hour.

In loop we set or clear LED accordingly to variable x value, which is inverted at every timers interrupt, or 1.048576 seconds.

Result:

Real 1 second timer

To have more precise 1 second timer we need to change only one thing – timer’s start value saved by  TCNT1 register. Overflow’s period is calculated by dividing counter’s interval by counters clock frequency. Using max interval (65536) and /256 prescaler we have 65536/62500 = 1.048576 s. It is easy to see if we need 1 second timer counter interval must be 62500. So it should be 65536-62500 =  3036 shorter than maximum, to do that we need to set counter start value to 0x0BDC (hex of 3036) – TCNT1=0x0BDC;

// timer example from electronicsblog.net
#define LED 13

boolean x=false;

ISR(TIMER1_OVF_vect) {
TCNT1=0x0BDC; // set initial value to remove time error (16bit counter register)
  x=!x;  
}

void setup() {

pinMode(LED, OUTPUT);

TIMSK1=0x01; // enabled global and timer overflow interrupt;
TCCR1A = 0x00; // normal operation page 148 (mode0);
TCNT1=0x0BDC; // set initial value to remove time error (16bit counter register)
TCCR1B = 0x04; // start timer/ set clock

};

void loop () {

digitalWrite(LED, x);

};

Result:

After almost one hour our Arduino clock are showing the same time as referenced clock.

  • Pingback: Arduino 4 digits 7 segments LED countdown timer with buzzer | electronicsblog.net

  • Pingback: Light Project(3) | Projects & hobbies

  • Wnc3

    What was the long term stability like?

  • SebCZ

    Can you post the code of the Result please?

  • Pingback: Arduino and ultra sonic range measurement module or how to measure the pulse time with a hardware timer and an interrupt | electronicsblog.net

  • Pingback: Very simple Arduino Lithium-ion battery capacity tester/discharge monitor | electronicsblog.net

  • DaFat

    This example works well for the 1Hz timer implemented here, and
    I have used it with great success myself. However when I attempted to use this method
    in the implementation of a simple waveform generator, I found that its accuracy
    will begin to diminish at higher frequencies. The reason for this is related to
    the number of clock cycles required to set the initial value of timer 1 in the
    overflow interrupt while the clock is still ticking. I found that this
    introduced an error of about 30 clock cycles.

    This error could be fudged out by setting the initial value 30 clock cycles
    below what it should be, however if higher or very precise frequencies are
    required it is best to use one of the phase and frequency correct timer modes. i.e.
    mode 8 or 9 (page 148 of the data sheet).

  • http://twitter.com/evilnick Nick Veitch

    I don’t really understand why you reset the clock. wouldn’t it be simpler to set the compare timer to 62500 and use it to initiate the interrupt? Then there would be no need to set the clock yourself and it would probably be a tiny bit more accurate

    • http://www.electronicsblog.net/ Darius

      At the time I was writing code this method i had used look more simple. Maybe some time i will try to compare accuracy of these methods.

  • http://twitter.com/evilnick Nick Veitch

    I meant somewhat like this:

    cli(); // stop interrupts while we meddle with the clocks
    TCNT1=0×0000; // reset counter to 0 at start
    TCCR1A= B00000000; // turn off PWM
    TCCR1B= B00001100; // set CTC on and prescalar to /256
    TIMSK1 |= _BV(OCIE1A) ; // enable timer compare A interrupt
    OCR1A = 62499; //set the comparator A to trigger at 1 second
    sei(); //start interrupts again
    ….

    ISR(TIMER1_COMPA_vect) {

    }

  • Anonymous

    Hi Guys I am newbee and I am not a software goro. I am trying to generate a PPM pulse to make a transmitter using Atmega 328  I can find this on the web
    TCCR1A = B00110001;     // Compare register B used in mode ’3′TCCR1B = B00010010;     // WGM13 and CS11 set to 1TCCR1C = B00000000;     // All set to 0TIMSK1 = B00000010;     // Interrupt on compare BTIFR1  = B00000010;    OCR1A = 22000;  OCR1B = 1000;
    }

    ISR(TIMER1_COMPA_vect) {    }

    my questions are
     
    1- I cannot  see the corresponding letters in the ATMEL manual eg TCCR1A, TIFR1, TIMSK1 etc ( I may be looking at the wrong manual !!???)
    1- How do you select and setup your timers in general
    3- The value you put in TCCR1A ie 00110001 = 31H that your comment says Mode 3 but I can not find this set up in the manual
    4- When the count up starts the counter TCCR1A starts from the BOTTOM ie 31H and goes up to FF? how does it compare to OCR1A ?
    5- where did (Timer1_compA_Vec) come from?.

    I appreciate if you can explain to me as i want to understand how the timers work and how to select them and how to control them. 

    • http://www.electronicsblog.net/ Darius

      Atmega 328 have different registers http://www.electronicsblog.net/atmega328-development-kit/ Later i will look to datasheet and try to answer your questions.

      • Avionics2

        Thanks Darius appreciate it.

        • http://www.electronicsblog.net/ Darius

          1. TCCR1A is found at the page 134 http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf  TIFR1 , TIMSK1  -139,
          2. It depends on the task, that counter performs. It can be used for count impulse, time, or to generate PWM.
          3. Modes of operation is at page 125. At 136 you find table where is described how registers should be configured.
          4. Look at the datasheet from 149 page, where is some graphic explanation how does counter works.
          5. Interrupt vector can be found on page 57.

          • Avionics

            Thanks Darius for your help.
            let me put my understanding and please correct me where I am wrong.
            in my case we are using Timer/Counter 1 that monitors the compare registers Comp OCR1A and B constantly and when a match occures then it calls an ISR in our case ISR(TIMER1_COMPA_vect). OCR1A is holding the TOP value for our counter i.e 22000 Micro seconds.
            What I dont understand is the role of OCR1B which is holding 1000 ? also when i checked the mode with the manual it is Mode 9 not Mode 3 as it says in the comment !? am i correct?

            Appreciate if you can clarify if you have time to answer  :-)

          • http://www.electronicsblog.net/ Darius

            To set “mode 3″ TCCR1A = xxxxxx11 ; TCCR1B = xxx00xxx;
             In your code TCCR1A = B001100 01;  TCCR1B = B000 10 010; So yes, it’s  not mode 3, but mode 9.

            On compare match, not only the interrupt is triggered, but also changes status of  OC1A/OC1B

            pins.
             

  • Alex

    Hi, I’ new to AVR but I start to programing litle by lilte me and google, I have a problem how make an 16 bit counter. My code beloy, is an 8 bit counter, wit 4 push buttons up/down -> 1 unit, and up/down 10 units in binary. I want to make something similar with on demands unit set/counter x10, x100, x1000 

    unsigned char count;int x1 = B0;   // pas x1 unitateint x10 = B0;   // pas x10 unitatiint val_buton1 = 0;int val_buton2 = 0;int val_buton3 = 0;int val_buton4 = 0;void setup (void) {PORTB=0×00;DDRB=0xFF;  pinMode (6, INPUT);   //setat pin ca input  pinMode (7, INPUT);   //setat pin ca input  pinMode (8, INPUT);   //setat pin ca input  pinMode (9, INPUT);   //setat pin ca input}void loop (void) {      val_buton1 = digitalRead(6);   //citeste valoarea pinului      val_buton2 = digitalRead(7);   //citeste valoarea pinului      val_buton3 = digitalRead(8);   //citeste valoarea pinului      val_buton4 = digitalRead(9);   //citeste valoarea pinului    //count 1 unitate  if (val_buton1 == LOW && val_buton2 == HIGH)  {    x1 = x1 + B1;  PORTB = x1;  delay (10);  }    if (val_buton2 == LOW && val_buton1 == HIGH)  {    x1 = x1 – B1;  PORTB = x1;  delay (10);  }    //count 10 unitati  if (val_buton3 == LOW && val_buton4 == HIGH)  {  x10 = x10 + B1010;  PORTB = x10;  delay (10);  }    if (val_buton4 == LOW && val_buton3 == HIGH)  {    x10 = x10 – B1010;  PORTB = x10;  delay (10);  }}

    • http://www.electronicsblog.net/ Darius

       And what exactly is problem?

  • JoseM

    great job!!! I’ve found this page very useful. Thanks!!