How to use the Timer Output Compare mode

This is the 3rd tutorial in the AVR series and today we will see how to use the output compare mode in the timer. The output compare mode us useful as we need to generate the precise timing in order to use the peripherals like UART, I2C, SPI etc.
In the future tutorials we will cover these peripherals and we will mostly use the bit manipulation method for them. That means we need to generate the timing ourself and this is why the output compare mode is useful to learn in the beginning itself.

From the datasheet of the ATtiny85, you can get the information about the Timer properties. The image is shown below

  • Timer Timer supports 2 separate Output compare units, which work independly.
  • It also supports CTC mode (Clear Timer on Compare Match) which resets the counter to 0 when there is a match with the compare register.
  • We have the separate interrupt sources for both the compare units, OCF0A and OCF0B

Today we are just focussing on the output compare mode of the timer. We will dicuss the rest of the features in future tutorials.

How it works ?

The timer runs at a specific clock which can be modified using the prescalar. It consists of a counter which counts up/down at each clock cycle.

Assume that the TIM clock is at 1MHz, then each count of the counter will take 1us (1/1MHz). This means the counter will take 1us to go from 0 to 1 and from 1 to 2. Since the timer is 8 bit in size, the maximum value of the counter can be 255 (0xFF).

Now comes the compare register. The value set in the compare register sets the limit for the counter. In CTC mode, when the counter reaches this value, it will automatically resets back to 0 and triggers an interrupt.

In the previous example of 1MHz TIM clock, assume that we set the Compare value to 99. The counter will take 100us to reach this value and to trigger the interrupt. The counter will reset to 0 and will trigger another interrupt in another 100us. This way we will achieve continuous interrupts every 100us.

We can modify this interrupt rate by modifying the TIM clock and the compare value. So we can achieve any desired rate for the interrupt.

In this tutorial I will toggle a pin inside the interrupt handler and measure its frequency on an oscilloscope. This will confirm whether the interrupt is generating at the defined rate or not.

The Code

First of all we need to set a pin (for toggling) as output. I am using the pin PB0 for this purpose.

int main(void)
	DDRB = 0x01;  // 0b0000 0001 -> PB0 as output
	PORTB = 0;   // PB0 LOW

Writing a 1 at the 0th position in the DDRB register sets the pin PB0 as the output pin.
Also I am resetting the pin to LOW by writing 0 to the PORTB register.

Now we will start configuring the timer in the CTC (Clear timer on compare) mode. We will configure the registers in order of how they are provided in the datasheet.

Let’s start with the Control Register A TCCR0A. This register configures the modes of operation for the timer.

We are only intersted in the lower bits of this register. These bits are used to set the different modes of operation.

To set the Timer in the CTC mode, we need to write a 1 to the WGM1 bit and a 0 to the WGM0 bit.

TCCR0A = 1<<WGM01;  // enable CTC mode for TIM0

Next is the Control Register B TCCR0B. This register is again used in combination with the TCCR0A to configure the modes of operation. It is also used to select the prescalar for the Timer.

Again we are interested in the Lower bits of this register. The timer is clocked by the system clock and we can use the prescaler to further reduce this clock.

The final TIM CLK = CLKIO/PSC. For the example purpose, I am selecting the prescaler of 8 by wrtiting a 1 to the CS01 bit and 0 to the CS00 and CS02 bits.

TCCR0B = 1<<CS01;  // PSC = 8, TIMCLK = 1MHz/8 = 125KHz

Next in line is the Output Compare Register A OCR0A. This register holds the value which will be compared with the counter. If the counter matches this value, an interrupt will be triggered.

We can set value based on the desrired interrupt rate. For the example purpose, I am setting the value 99.

OCR0A = 99;   // Output compare value

Next is the Interrupt Mask Register TIMSK. This register enables the interrupt for different modes of the timer.

Since we are using the Output Compare Mode A, we need to enable the interrupt only for it. This can be done by writing a 1 to the OCIE0A bit.

TIMSK = 1<<OCIE0A;   // Enable interrupt for  Compare Match A

Along with enabling the interrupts for the individual peripherals, we also need to enable the global interrupt. This can be done by calling the function sei().

sei();  // Enable global Interrupt Mask

Once the counter value matches the compare value, an interrupt will trigger. We will write an Interrupt Service Routine (ISR) to handle this interrupt.

	PORTB ^= 1<<PB0;  // Toggle PB0
//	TCNT0 = 0;   // Rest the counter to 0

The argument of the ISR is the interrupt we are handling, in this case it is TIM0_COMPA_vect, Compare A interrupt for Timer 0.

Inside the ISR, we will just toggle the pin PB0 and this will generate a square wave of a fixed frequency.


There is not much to the connection. As I mentioned, the Pin PB0 will toggle whenever the interrupt is generated. I have connected the PB0 to the Logic Analyzer so that we can measure the square wave frequency.

The output frequency of the wave can be calculated using the formula shown below.

With the Prescaler of 8 and the Compare value of 99, we can expect the frequency to be around 625 Hertz.


The Output of the logic analyzer is shown below.

As you can see the Frequency is around the same that we were expecting as per the calculation. The interrupt is actually triggering at twice this frequency and we will use this rate to generate the timing for different peripherals in future tutorials.

Check out the Video Below


You can help with the development by DONATING OR Just click DOWNLOAD to download the code

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.


Adblocker detected! Please consider reading this notice.

We've detected that you are using AdBlock Plus or some other adblocking software which is preventing the page from fully loading.

We don't have any banner, Flash, animation, obnoxious sound, or popup ad. We do not implement these annoying types of ads!

We need money to operate the site, and almost all of it comes from our online advertising.

Please add to your ad blocking whitelist or disable your adblocking software.