Introduction: AVR Microcontroller. LEDs Flasher Using Timer. Timers Interrupts. Timer CTC Mode
Hello everyone!
Timers is an important concept in the field of electronics. Every electronic component works on a time base. This time base helps to keep all the work synchronized. All the microcontrollers work at some predefined clock frequency, they all have a provision to set up timers. AVR boasts of having a timer which is very accurate, precise and reliable. It offers loads of features in it, thus making it a vast topic. The best part is that the timer is totally independent of the CPU. Thus, it runs parallel to the CPU and there is no CPU’s intervention, which makes the timer quite accurate. In this section I explain the basic concepts of AVR Timers. I am writing simple program in C code to control LED flasher, using timers.
Step 1: Description
In ATMega328 there are three types of timers:
- Timer/Counter0 (TC0) - is a general purpose 8-bit Timer/Counter module, with two independent OutputCompare Units, and PWM support;
- Timer/Counter1 (TC1) - The 16-bit Timer/Counter unit allows accurate program execution timing (event management), wave generation, and signal timing measurement;
- Timer/Counter2 (TC2) -is a general purpose, channel, 8-bit Timer/Counter module with PWM and Asynchronous Operation;
Step 2: Problem Statement 1: Let’s Flash First LED(green) Every 50 Ms
Methodology:
- using a Timer0 prescaler to reduce a high frequency electrical signal to a lower frequency by integer division;
- using an interrupt every time the Timer0 overflows;
Timer0 (8 bit) it counts from 0 to 255 after that, they overflow, this value changes at every clock pulse.
F_CPU=16MHz: Clock time period = 1000ms / 16000000Hz = 0.0000625ms
Timer count = (Required Delay / Clock Time Period)-1 = (50ms / 0.0000625ms) = 799999
The clock has already ticked 799999 times to give a delay of only 50 ms!
We can use technique of frequency division is called prescaling to decrease timer count. The AVR offers us the following prescaler values to choose from: 8, 64, 256 and 1024. See the table summarizes the results of using different prescalers.
The counter value should always be an integer. Let’s choose a prescaler 256!
In most microcontrollers, there is something called Interrupt. This interrupt can be fired whenever certain conditions are met. Now whenever an interrupt is fired, the AVR stops and saves its execution of the main routine, attends to the interrupt call (by executing a special routine, called the Interrupt Service Routine, ISR) and once it is done with it, returns to the main routine and continues executing it.
Since the required delay (50ms) is greater than the maximum possible delay: 4,096ms = 1000ms / 62500Hz * 256, obviously the timer will overflow. And whenever the timer overflows, an interrupt is fired.
How many times should the interrupt be fired?
50ms / 4.096ms = 3125 / 256 = 12.207 If the timer has overflown 12 times, 12 * 4.096ms = 49.152ms would have passed. In the 13th iteration, we need a delay of 50ms – 49.152ms = 0.848ms.
At a frequency of 62500Hz (prescaler = 256), each tick takes 0.016ms. Thus to achieve a delay of 0.848ms, it would require 0.848ms / 0.016ms = 53 ticks. Thus, in the 13th iteration, we only allow the timer to count up to 53, and then reset it.
Initialize Timer0/Counter (see pic):
TCCR0B |= (1 << CS02) // set up timer with prescaler = 256 TCNT0 = 0 // initialize counter TIMSK0 |= (1 << TOIE0) // enable overflow interrupt sei() // enable global interrupts tot_overflow = 0 // initialize overflow counter variable
Step 3: Problem Statement 2: Let’s Flash Second LED(blue) Every 1s
Methodology:
- using a Timer1 prescaler to reduce a high frequency electrical signal to a lower frequency by integer division;
- using Clear Timer on Compare (CTC) Mode;
- using Interrupts with CTC Mode;
Timer1 (16 bit) it counts from 0 to 65534 after that, they overflow. This value changes at every clock pulse.
F_CPU=16MHz: Clock time period = 1000ms / 16000000Hz = 0.0000625ms
Timer count = (Required Delay / Clock Time Period)-1 = (1000ms / 0.0000625ms) = 15999999
The clock has already ticked 15999999 times to give a delay of 1s!
We can use technique of frequency division is called prescaling to decrease timer count. The AVR offers us the following prescaler values to choose from: 8, 64, 256 and 1024. See the table summarizes the results of using different prescalers. The counter value should always be an integer. Let’s choose a prescaler 256!
In Clear timer on Compare(CTC) mode, the OCR1A or ICR1 register are used to manipulate the counter resolution. In CTC mode the counter is cleared to zero when the counter value (TCNT1) matches either the OCR1A or the ICR1. The OCR1A or ICR1 define the top value for the counter,hence also its resolution. This mode allows greater control of the compare match output frequency It also simplifies the operation of counting external events. We must tell the AVR to reset the Timer1/Counter as soon as its value reaches value 62500, thus to achieve a delay of 1s.
Initialize Timer1/Counter (see pic):
TCCR1B |= (1 << WGM12)|(1 << CS12) // set up timer with prescaler = 256 and CTC mode TCNT1 = 0 // initialize counter TIMSK1 |= (1 << OCIE1A) // enable compare interrupt OCR1A = 62500 // initialize compare value
Step 4: Problem Statement 3: Let’s Flash Third LED(red) Every 16ms
Methodology:
- using a Timer2 prescaler to reduce a high frequency electrical signal to a lower frequency by integer division;
- using Clear Timer on Compare (CTC) Mode;
- using Hardware CTC Mode without interrupts;
Timer2 (8 bit) it counts from 0 to 255 after that, they overflow. This value changes at every clock pulse.
F_CPU=16MHz: Clock time period = 1000ms / 16000000Hz = 0.0000625ms
Timer count = (Required Delay / Clock Time Period)-1 = (16ms / 0.0000625ms) = 255999
The clock has already ticked 255999 times to give a delay of 16ms!
See the table summarizes the results of using different prescalers. The counter value should always be an integer. Let’s choose a prescaler 1024!
In CTC mode the counter is cleared to zero when the counter value (TCNT2) matches either the OCR2A or the ICR2. Pin PB3 is also the Output Compare pin of TIMER2 - OC2A (see diagram).
Timer/Counter2 Control Register A – TCCR2A Bit 7:6 – COM2A1:0 – Compare Output Mode for Compare Unit A. Since we need to toggle the LED, we choose the option: Toggle OC2A on Compare Match Whenever a compare match occurs, the OC2A pin is automatically toggled. No need to check any flag bit, no need to attend to any interrupts.
Initialize Timer2/Counter
TCCR2A |= (1 << COM2A0)|(1 << WGM21) // set up timer OC2A pin in toggle mode and CTC mode TCCR2B |= (1 << CS22)|(1 << CS21)|(1 << CS20) // set up timer with prescaler = 1024 TCNT2 = 0 // initialize counter OCR2A = 250 // initialize compare value
Step 5: Writing Code for a Program in C. Uploading HEX File Into the Microcontroller Flash Memory
Writing and building the AVR microcontroller application in C Code using the Integrated Development Platform - Atmel Studio.
F_CPU defines the clock frequency in Hertz and is common in programs using the avr-libc library. In this case it is used by the delay routines to determine how to calculate time delays.
#ifndef F_CPU #define F_CPU 16000000UL // telling controller crystal frequency (16 MHz AVR ATMega328P) #endif
#include <avr/io.h> // header to enable data flow control over pins. Defines pins, ports, etc.
The first include file is part of avr-libc and will be used in pretty much any AVR project you work on. io.h will determine the CPU you're using (which is why you specify the part when compiling) and in turn include the appropriate IO definition header for the chip we're using. It simply defines the constants for all your pins, ports, special registers, etc.
#include <avr/interrupt.h> // header to enable interrupt volatile uint8_t tot_overflow; // global variable to count the number of overflows
Methodology of Problem Statement:Flash First (Green) LED every 50 ms
- using a Timer0 prescaler to reduce a high frequency electrical signal to a lower frequency by integer division;
- using an interrupt every time the Timer0 overflows;
void timer0_init() // initialize timer0, interrupt and variable { TCCR0B |= (1 << CS02); // set up timer with prescaler = 256 TCNT0 = 0; // initialize counter TIMSK0 |= (1 << TOIE0); // enable overflow nterrupt sei(); // enable global interrupts tot_overflow = 0; // initialize overflow counter variable }
Methodology of Problem Statement: Flash Second LED(blue) every 1s
- using a Timer1 prescaler to reduce a high frequency electrical signal to a lower frequency by integer division;
- using Clear Timer on Compare (CTC) Mode;
- using Interrupts with CTC Mode;
void timer1_init() // initialize timer1, interrupt and variable<br>{ TCCR1B |= (1 << WGM12)|(1 << CS12); // set up timer with prescaler = 256 and CTC mode<br> TCNT1 = 0; // initialize counter OCR1A = 62500; // initialize compare value TIMSK1 |= (1 << OCIE1A); // enable compare interrupt<br>}
Methodology of Problem Statement: Flash third LED(red) every 16ms
- using a Timer2 prescaler to reduce a high frequency electrical signal to a lower frequency by integer division;
- using Clear Timer on Compare (CTC) Mode;
- using Hardware CTC Mode without interrupts;
void timer2_init() // initialize timer2<br>{ TCCR2A |= (1 << COM2A0)|(1 << WGM21); // set up timer OC2A pin in toggle mode and CTC mode<br> TCCR2B |= (1 << CS22)|(1 << CS21)|(1 << CS20); // set up timer with prescaler = 1024 TCNT2 = 0; // initialize counter<br> OCR2A = 250; // initialize compare value<br> }
TIMER0 overflow interrupt service routine called whenever TCNT0 overflows:
ISR(TIMER0_OVF_vect) { tot_overflow++; // keep a track of number of overflows }<br>
This ISR is fired whenever a match occurs hence, toggle led here itself:
ISR (TIMER1_COMPA_vect)<br>{ PORTC ^= (1 << 1); // toggle led here<br>}
int main(void) { DDRB |= (1 << 0); // connect 1 (green) led to pin PB0<br> DDRC |= (1 << 1); // connect 2 (blue) led to pin PC1 DDRB |= (1 << 3); // connect 3 (red) led to pin PB3 (OC2A) timer0_init(); // initialize timer0 timer1_init(); // initialize timer1 timer2_init(); // initialize timer2 while(1) // loop forever {
If the Timer0 has overflown 12 times, 12 * 4.096ms = 49.152ms would have passed. In the 13th iteration, we need a delay of 50ms – 49.152ms = 0.848ms. Thus, in the 13th iteration, we only allow the timer to count up to 53, and then reset it.
if (tot_overflow >= 12) // check if no. of overflows = 12 NOTE: '>=' is used { if (TCNT0 >= 53) // check if the timer count reaches 53 { PORTB ^= (1 << 0); // toggles the led TCNT0 = 0; // reset counter tot_overflow = 0; // reset overflow counter } } } }
Uploading HEX file into the microcontroller flash memory:
type in DOS prompt window the command:
avrdude –c [name of programmer] –p m328p –u –U flash:w:[name of your hex file]
In my case it is: avrdude –c ISPProgv1 –p m328p –u –U flash:w:Timers.hex
This command writes hex file to the microcontroller’s memory. Watch the video with a detailed description of the microcontroller flash memory burning:
Microcontroller flash memory burning...
Ok! Now, the microcontroller works in accordance with the instructions of our program. Let's check it out!
Step 6: Making the Electrical Circuit
Connect components in accordance with schematic diagram.
Plug power and it is working!