Using Timer Overflow Interrupt Requests (IRQs)

Last modified by Microchip on 2023/11/10 11:09

Overview

  • Overview of the timer-counter and block diagram.
  • Calculate the frequency of a timer-counter overflow.
  • Determine the slowest an 8-bit timer can overflow, running the CPU at 16 MHz.
  • Register the configuration to set up a 16-bit timer to overflow twice a second, including setting the timer to prescalar and enabling the timer overflow Interrupt Request (IRQ).
  • Add the AVR® Libc header file for interrupts.
  • Toggle the LED in the timer overflow Interrupt Service Routine (ISR).

Now that we have learned about interrupts, we are going to circle back to our flashing LED application.  Recall that in that exercise, we used a delay function to determine how long to wait before turning on the LED.  This works great if you are doing very little else.  If we want to do other things while we wait to turn on the LED or possibly go into a low power mode, we can utilize the timer-counter overflow feature of the device.

Back to top

Procedure

LED Toggle Code

 Let's start with the code below.  It has all the elements that we have discussed previously and will toggle the LED every 500ms.

#define F_CPU 16000000UL
#include
<avr/io.h>
#include
<util/delay.h>

#define LED_ON  PORTB |= (1<<PORTB5)
#define LED_OFF PORTB &= ~(1<<PORTB5)
#define LED_TOGGLE  PINB |= (1<<PINB5)


int main(void) {
    DDRB |= (1 << PB5); // set PB5 as output pin
   DDRB &= ~(1<<DDB7); //set PB7 as an input pin
   
   while (1) {
       
       LED_TOGGLE;
             
       _delay_ms(500);
    }
}

Review Datasheet

Refer to the TC0 - 8-bit Timer/Counter0 with PWM section of the datasheet.

We'll be using the timer in Normal Mode meaning that the the counting direction is up (incrementing) with no clearing.  It simply runs up to its maximum 8-bit value (0xFF) and restarts at the bottom (0x00).  When it goes to 0xFF and rolls over to 0x00, it sets the Timer/Counter Overflow Flag (TOV1) .  At this point an interrupt is generated which will clear TOV1 and the process repeats.  In order to calculate the frequency of the overflow timer, we will use the following formula from the datasheet.

Frequency Formula

Where...

N =  256 (the prescaler value)
fclk_I/0 = 16000000 (CPU frequency)
OCRFnx = 255 (the highest value we can count to with an 8=bit timer)

If you perform this calculation, you will see that the 8-bit timer is good for really short durations.  In this case the timer will overflow roughly 30 times per second which is too fast for us to see the LED toggle.

Let's take a look at the 16-bit timer TC1, 3, 4 - 16-bit Timer/Counter1, 3, 4 with PWM.  You can see in the the datasheet that it has the exact same formula as above with the only change in values being that with that with a 16-bit timer, it has a much higher value for OCRnx which is now 65536 (0xFFFF).

Run this calculation for the 16-bit timer and you will get approximately 0.476 which means the 16-bit timer will overflow about twice a second which is slow enough for us to be able to observe the LED toggle.


TCCR1B Register

 Now let's take a look at the register for the 16-bit timer and start writing some code.

TCCR1B Register

The N value has already been determined for our application (16000000) as well as OCRFnx (65536).  We just need to set the clock source at the appropriate prescaler value.

Note that the timer is not running until a clock source is selected.

From the table below, we see that in order to set a clock source at the prescaler value of 256, we need to write to Bit CS12

Clock Select Bit

We add the following line to our main function to write to CS12 in register TCCR1B

TCCR1B |= (1 << CS12);

With the following code, we are turning on the timer with the prescaler set to 256 and setting the mode of operation to Normal Mode (counting).

#define F_CPU 16000000UL
#include
<avr/io.h>
#include
<util/delay.h>

#define LED_ON  PORTB |= (1<<PORTB5)
#define LED_OFF PORTB &= ~(1<<PORTB5)
#define LED_TOGGLE  PINB |= (1<<PINB5)


int main(void) {
    DDRB |= (1 << PB5); // set PB5 as output pin
   DDRB &= ~(1<<DDB7); //set PB7 as an input pin
   
    TCCR1B |= (1 << CS12);  //Set up the clock source
   
   while (1) {
       
       LED_TOGGLE;
             
       _delay_ms(500);
    }
}


Enable the interrupt

Within the Register Description section of the datasheet is the Timer/Counter 1 Interrupt Mask Register.

TIMSK1 Register

With the following bit description, we see that we need to set bit 0 in order to enable the overflow interrupt.

TMSK1 Bit 0

We add the following line to our main function to write to TIMSK1 bit 0

TIMSK1 |= (1 << TOIE1);

This part of interrupt setup is just like in our previous lesson on interrupts.  We will go ahead and...

  • Add the Interrupt Include file: avr/interrupt.h
  • Enable Global Interrupts: sei();

Set up the ISR

 Set up our interrupt service routine.

From the interrupts lesson, we learned that we need to add the following line with the appropriate interrupt vector from the Interrupt Vector table below.

ISR(Interrupt Vector from Table_vect){
  
  
}

Reset and Interrupt Vectors in ATmega328PB table

Moving the LED_TOGGLE routine into our Interrupt Service Routine and removing the delay function gives us the following finished code...

#define F_CPU 16000000UL
#include
<avr/io.h>
#include
<util/delay.h>
#include
<avr/interrupt.h>

#define LED_ON  PORTB |= (1<<PORTB5)
#define LED_OFF PORTB &= ~(1<<PORTB5)
#define LED_TOGGLE  PINB |= (1<<PINB5)

ISR(TIMER1_OVF_vect)
{
   LED_TOGGLE;
}


int main(void) {
    DDRB |= (1 << PB5); // set PB5 as output pin
   DDRB &= ~(1<<DDB7); //set PB7 as an input pin
   
    TCCR1B |= (1 << CS12);  //Set up the clock source
   TIMSK1 |= (1 << TOIE1); //Enable the overflow interrupt
   sei();                  //Enable global interrupts
   
   
   while (1) {
       
      //LED_TOGGLE;
             
      //_delay_ms(500);
   }
}

Program the Device

Finally, program the device with the Make and Program Device button at the top of the MPLAB X IDE GUI.

Make and Program Device button

You should see the LED flashing but at a slightly slower frequency than we observed in the flashing an LED at a specific frequency lesson.

Back to top

Learn More

Back to top