Focused on Your Needs
3. Timer and Counter Introduction
This tutorial is to introduce timer and counter in AVR microcontrollers.
The timer and counter in the microcontroller count based on the clock frequency. The microcontroller has an embedded clock, and it is also able to connect to an external clock for a higher-frequency operation.
The counter can only count up to either 256 (8 bit) or 65535 (16 bit). The Prescaler can be used to skip a certain number of clock ticks and allow the counter to count in a longer interval. The AVR microcontroller has the prescaling number of 8, 64, 256, and 1024. For instance, if 256 is set in the Prescaler, the counter will count once when the clock ticks 256 times.
In AVR microcontrollers, such as ATmega328/P, TC0 is a general purpose 8-bit timer/counter, and TC1 is a 16-bit time/counter. This tutorial is only focused on the 8-bit timer/counter.
The behaviors of the timer and counter are determined by the Timer/Counter Control Registers.
Example 1: Overview of the TCCR0A and TCCR0B registers
BIT 7 6 5 4 3 2 1 0
TCCR0A COM0A1 COM0A0 COM0B1 COM0B0 - - WGM01 WGM00
BIT 7 6 5 4 3 2 1 0
TCCR0B FOC0A FOC0B - - WGM02 CS02 CS01 CS00
COM0A and COM0B controls the OC0A pin’s and OC0B pin’s behavior respectively. WGM0 is used to set the Waveform Generation Mode.
Example 2: The description of the Waveform Generation Mode in WGM0
Mode WGM02 WGM01 WGM00 Timer/Counter Mode TOP Update of OCROx at TOV Flag Set on
0 0 0 0 Normal 0xFF Immediate MAX
1 0 0 1 PWM, Phase Correct 0xFF TOP BOTTOM
2 0 1 0 CTC OCR0A Immediate MAX
3 0 1 1 Fast PWM 0xFF BOTTOM MAX
4 1 0 0 Reserved - - -
5 1 0 1 PWM. Phase Correct OCR0A TOP BOTTOM
6 1 1 0 Reserved - - -
7 1 1 1 Fast PWM OCR0A BOTTOM MAX
MAX: 0xFF (8 bit) or 0xFFFF (16 bit)
BOTTOM: 0x00 or 0x0000
TOP: The counter reaches to the highest value in the count sequence. TOP value can be MAX or the value stored in the Output Compare Register A (OCR0A) Register.
FOC0x (x = A or B) is only active when the WGM0 is specified as a non-PWM mode. When a logic 1 is written to FOC0x, an immediate compare match is forced on the waveform generation unit. The output from OC0x pin is changed based on the COM0x setting.
The FOC0x bit is implemented as a strobe that neither generates any interrupt, nor clears the timer in Clear timer on Compare match (CTC) mode using OCR0x as TOP. The FOC0x bit is always read as zero.
CS0 is used to select the clock source and set the Prescaler
Example 3: The description of the Clock Select Bit
CS02 CS01 CS00 Description
0 0 0 No Clock source (timer/counter stopped)
0 0 1 CLK I/O/1 (no prescaling)
0 1 0 CLK I/O/8 (from prescaler)
0 1 1 CLK I/O/64 (from prescaler)
1 0 0 CLK I/O/256 (from prescaler)
1 0 1 CLK I/O/1024 (from prescaler)
1 1 0 External clock source on T0 pin. Clock on falling edge
1 1 1 External clock source on T0 pin. Clock on rising edge
TCNT Register holds the 8-bit counter value in the microcontroller. TCNT0 is 8-bit and TCNT1 is 16-bit.
OCR0x (x = A or B) is an 8-bit register that contains a value that is continuously compared with the counter value in TCNT0. When the value in OCR0x matches the value in TCNT0, an output compare interrupt or a waveform output on the OC0x pin can be generated.
Example 4: Set timer 0 in CTC mode
TCCR0A = 0b00000010; //Put in CTC mode
Alternatively, the code in Example 4 can be written as:
TCCR0A |= 1 << WGM01; //Put in CTC mode
Example 5: Set the Prescaler to 1024
TCCR0B = 0b00000101; //Set the Prescaler to 1024
Alternatively, the code in Example 5 can be written as:
TCCR0B |= 1<<CS00 | 1 << CS02; //Set the Prescaler to 1024
Example 6: Set the TOP value to 200
OCR0A = 200; //Set the TOP value
Timer/Counter Interrupt Mask Register (TIMSK) is used to enable different interrupt mode. Timer/Counter Interrupt Flag (TIFR) is used to determine which interrupt is currently pending.
Example 7: Set the interrupt
TIMSK0 |= 1 << OCIE0A; //Interrupt will be triggered when counter reaches the number set in OCR0A
Because there is an interrupt event in the program, the library <avr/interrupt.h> has to be included and the global interrupt needs to be enabled.
Example 8: Enable the global interrupt
sei(); //Enable global interrupt
When the condition meets, the Interrupt Service Routing is called.
Example 9: The function of the Interrupt Service Routing
ISR (TIMER0_COMPA_vect)
{
}
In a 1MHZ microcontroller, if each count takes 1024 clock ticks, and a total count is 200 before the counter restarts, a full cycle would take (1,024x200)/1,000,000 = 0.2048 second.
Inside the Interrupt Service Routing, an XOR operation is written to toggle the LED in PIND3
Example 10: write a program to toggle the LED in PIND3
PORTD ^= 1<<PIND3; //Toggle the LED
Example 11: The full code to toggle the LED in PIND3
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void)
{
TCCR0A |= 1 << WGM01; //Put in CTC mode
TCCR0B |= 1 << CS00 | 1<<CS02; //Set the Prescaler to 1024
OCR0A = 200; //Set the TOP value
TIMSK0 |= 1 << OCIE0A; //Interrupt will be triggered when counter reaches the number set in OCR0A
DDRD |= 1<<3; //Set PIN3 in PORTD as an output
sei(); //enable global interrupt
while (1) {}
}
ISR (TIMER0_COMPA_vect)
{
PORTD ^= 1<<PIND3; //Toggle the LED
}
The code above is to toggle the LED in every 0.2048s; thus, the frequency of the blinking LED is somewhere around 1/(2*0.2048) = 2.4414 Hz.