Using Timer Compare Match IRQs

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

In This Video

  • Change timer mode to Clear Timer on Compare Match (CTC) mode.
  • Enable Compare Match IRQ.
  • Update the interrupt vector efficiently (from timer overflow to CTC match on channel A).
  • Write a generic function to overflow the timer at a given frequency.

Procedure

Change the Mode

We want to change the Mode to Clear Timer on Compare Mode instead of Normal Mode used in the previous lesson.

We see in the Waveform Generation Mode Bit Description table below that in order to set the mode to CTC, we need to set the bit in WGM12.

Wave Generation Mode Table

The WGM12 bit exists in the TCCR1B register as indicated in the accompanying image.

TCCR1B WGM Bit

Let's pull up our code from the previous lesson and make the necessary modification to change to CTC mode.

#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) {
              
    }
}

From above, you can see that we're already writing to TCCR1B to set up the clock source.  We need to change the mode so we modify the line to read as:

TCCR1B |= (1 << CS12) | (1 << WGM12);

Change the Interrupt Type

Previously, we used the overflow interrupt but we need to make a change in our code to use a CTC interrupt instead. Going back to the TMSK1 register we see in the accompanying image that we need to set bit 1.

TIMSK1 CTC Mode

OCIE1A Bit for CTC Interrupt

 We modify this line:

TIMSK1 |= (1 << TOIE1);

To read like this line:

TIMSK1 |= (1 << OCIE1A);

Set up the Interrupt Vector

Find the Interrupt Table in the datasheet and look for Timer/Counter1 Compare Match A to find the interrupt vector source.

TIMER1_COMPA Source

Modify this line:

ISR(TIMER1_OVF_vect)

To read like this line:

ISR(TIMER1_COMPA_vect)

Here is everything we have so far:

#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_COMPA_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) | (1 << WGM12);  //Set clock source & set mode to CTC
   TIMSK1 |= (1 << OCIE1A); //Enable the CTC interrupt
   sei();                  //Enable global interrupts
   
   
   while (1) {
              
    }
}

Rearrange the Frequency Equation

Recall the frequency equation from the Timer Overflow lesson.

Frequency Equation

We want to rearrange this equation so that we are solving for OCRnx and using f0cnx.  This equation would now look like this after we substitute F_CPU for fclk_I/0, OCR1A for OCRnx, and include freq as a variable in place of f0Cnx.

OCR1A = (F_CPU/(freq*2*256)-1)


Create a function called Timer_Frequency and call it from our main.

Using the rearranged formula from above and moving our clock source and CTC interrupt into it we get the following function:

void Timer_Frequency(uint8_t freq)
{
    TCCR1B |= (1 << CS12) | (1 << WGM12);  //Set clock source & set mode to CTC
   TIMSK1 |= (1 << OCIE1A); //Enable the CTC interrupt
   OCR1A = (F_CPU/(freq*2*256)-1);
}

Here is the completed code a call to the Timer_Frequency function.  We will set jit at a frequency of 4Hz.

#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_COMPA_vect)
{
   LED_TOGGLE;
}

void Timer_Frequency(uint8_t freq)
{
    TCCR1B |= (1 << CS12) | (1 << WGM12);  //Set clock source & set mode to CTC
   TIMSK1 |= (1 << OCIE1A); //Enable the CTC interrupt
   OCR1A = (F_CPU/(freq*2*256)-1);
}

int main(void) {
    DDRB |= (1 << PB5); // set PB5 as output pin
   DDRB &= ~(1<<DDB7); //set PB7 as an input pin
   
    Timer_Frequency(4);
   
    sei();                  //Enable global interrupts
   
   
   while (1) {
              
    }
}

Program the Device

Program the device by selecting the Make and Program Device Main Project button at the top of the MPLAB X GUI .  You should see the LED blinking at 4Hz. 

Program the Device

Try changing the frequency value.  You should see the LED blinking at different rates each time you change it.

Learn More

Back to Top