ATmega328p is a popular microcontroller which can be used in electronics projects for peripheral control application that is useful in automation in manufacturing industry, home automation etc. This microcontroller is used in Arduino which is a popular ready made easy to use microcontroller based system. The microcontroller contain Timers and Counters which are useful for creating time based interrupt, create counting mechanism for counting external events, creating signal waveform, for generating PWM signal for motor controls etc.
Understanding the Timer/Counter of ATMega328 microcontroller is essential for making microcontroller based projects. Here we will illustrate with examples how the ATmega328p microcontroller can be programmed to work in various mode of operation, how the timer/counters interrupts works.
Below are links to ATmega328P modes of operation tutorials with examples.
- Normal Mode(see ATmega328p Timer Programming Examples)
- CTC mode(see Programming ATmega328p in CTC mode)
- Fast PWM mode(see ATmega328P Fast PWM mode Programming Examples)
- Phase Correct PWM mode(Phase Correct PWM with ATmega328P)
ATmega328p has three Timer/Counter which are called Timer/Counter 0, Timer/Counter 1 and Timer/Counter 2. The Timer/Counter 0 and Timer/Counter2 are 8-bit timer/counter while the Timer/Counter1 is 16-bit timer/counter.
Here we will use Timer/Counter 0 to illustrate examples.
The registers which are important in configuring and reading flags for Timer/Counter 0 are as follows.
a. TCCR0A – Timer/Counter 0 Control Register A
b. TCCR0B – Timer/Counter 0 Control Register B
c. TIMSK0 – Timer/Counter 0 Interrupt Mask Register
d. TIFR0 – Timer/Counter 0 Interrupt Flag Register
e. TCNT0 – Timer/Counter 0 Register
f. OCR0A – Output Compare Register0 A
g. OCR0B – Output Compare Register0 B
Basic Mode of Timer Operations
There are basically three types of timer mode of operation which are:
1. Normal Mode
2. CTC Mode
3. PWM
a. Fast PWM
b. Phase Correct PWM
1. Normal Mode
In this mode of operation, the Timer is usually used in creating certain time delay. The required time delay determines count value that is loaded into the Timer TCNT0 register. Once TCNT0 is loaded the timer is started. The count starts from the loaded count value to the top value which is 256 decimal or 0xFF hex for 8-bit timer0. Once the top value is reached, flags are set in the Timer/Counter flag register and the timer is again loaded with the count value and the process is repeated. We can also setup to generate timer/counter interrupt by configuring certain registers related to the timer/counter.
Here we show examples of how to setup Timer/Counter 0 to create certain time delay.
i) Determination of Count Value to be loaded into Timer/Counter 0
Let's suppose that we want to create a time delay of 50us. Then the formula to calculate the count value to be loaded into TCNT0 register is as follows.
\[C=256-\frac{F_{osc} T_{d}}{N}\]
where, Td = 50us, Fosc = 8MHz, and N is pre-scalar which can be 1, 8, 64, 256 or 1024
We cannot use pre-scalar of 1 because it will give negative value for C. So we use next pre-scalar 8. This then will give value C as,
\[C=256-\frac{8*10^{6} *50*10^{-6}}{8}\]
\[C= 206\]
ii) Configuring Timer/Counter 0 in Normal Mode
To configure Timer/Counter 0 in Normal Mode we need to set the wave generation mode bits(WGM02, WGM01 and WGM00) that are located in TCCR0A and TCCR0B. The WGM02 is located in TCCR0B while the WGM01 and WGM00 bits are located in TCCR0A register.
The following table shows what bit combination of WGM02, WGM01 and WGM00 configures the micrcontroller timer/counter in which mode.
From the above table, for normal mode (mode 0), all WGM bits should be zeros.
iii) Configuring the Pre-Scalar value(N)
The pre-scalar value can be set using the clock select bits CS02, CS01 and CS00 located in the TCCR0B register. The following tables is used to determine the value of these bits.
So for N= 8 pre-scalar, the CS bits values are: CS02=0, CS01=1 and CS00=0
We can create delay using timer0 by polling the TOV0 flag or using Overflow Interrupt. These are illustrated next with C program codes.
(a) Normal Mode with Polling of TOV0 flag
For normal mode of operation, we do not need to set the Compare Output Mode bits(COM0A0, COM0A1,COM0B0,COM0B1) located in TCCR0A. The Force Output Compare bits(FOC0A, FOC0B) area also not required. The various interrupt enable bits(OCIE0A, OCIE0B, TOIE0) in the TIMSK0 register are also not required since we are not using any interrupt. The output compare registers(OCR0A, OCR0B) are also not required because we are using normal mode.
For normal mode of operation, we need the Timer/Counter 0 Register(TCNT0) where we load the count value. And we also need the Timer/Counter 0 Interrupt Flag Register(TIFR0) because we need to poll(monitor) the Timer/Counter Overflow bit(TOV0).
# C Program Code for Timer0 delay with Polling method
#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/io.h>
void Timer0(void); //function prototype
int main()
{
DDRB |= (1<<PB0); //for LED output
while (1){
PORTB ^= (1<<PB0); //toggle PORT B pin 0
Timer0();
}
return 0;
}
void Timer0(void){
TCNT0 = 206; //load the TCNT0 with 206 for 50us delay
//for normal mode operation and pre-scalar of 8
TCCR0A = 0x00;
TCCR0B = 0b00000010; // FOC0A | FOC0B | - | - | WGM02 | CS02 | CS01 | CS00
while((TIFR0 & (1<<TOV0)) == 0);
TCCR0B = 0x00;
TIFR0 = 0x01;
}
In the above program, we have used a LED connected to the Port B pin 0. Thus we have that port as an output. In the while loop we toggle the Port B pin 0 so that we can generate square wave on the pin where the LED is connected. Between the toggle, the Timer0() delay function is called. This Timer0() function creates a delay of 50us.
When the Timer0() function is called, we load value of 206 into the TCNT0 register. This value was calculated above. You can also use the online ATmega32 Calculator which is same as ATmega328P timer Calculator to calculate the count value. We have then configured the TCCR0A and TCCR0B for normal mode of operation with pre-scalar value of 8. Using the while function we monitor whether the TOV0 flag is raised or not. Once the TOV0 flag is raised which means that the counting has completed(meaning the timer waited for 50us), we then clear the TCCR0B register to stop the timer.
Using Timer0 with polling method for creating delay is not a good solution since the CPU is occupied with other program code processing. We can observe that the delay is not accurately 50us.
The following picture shows circuit schematic simulation in proteus as well as the waveform signal generated and the Fourier transform.
The following graph shows signal generated with time period of 100us(that is delay of 50us).
The following picture shows 10KHz signal(100us period signal) in frequency domain.
(b) Timer 0 delay in Normal Mode with Overflow Interrupt
In this example we show how to create delay with Timer0 in interrupt mode. As with the first example(polling method) above we will create a 50us delay by loading 206 count value into the TCNT0 register. The pre-scalar is 8 and the CPU frequency is 8MHz.
# C Program Code for Timer 0 with overflow interrupt
#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
int main()
{
DDRB |= (1<<PB0); //make PORT B pin 0 an output pin
TCNT0 = 206; //load TCNT0 with 206 for 50us delay
//configure timer0 for normal mode with pre-scalar of 8
TCCR0A =0x00;
TCCR0B |= (1<<CS01);
TIMSK0 |= (1<<TOIE0); // enable timer overflow interrupt
sei(); //enable global interrupt
while (1);
return 0;
}
ISR(TIMER0_OVF_vect){
TCNT0 = 206;
PORTB ^= (1<<PB0);
}
In this Atmega328p Timer interrupt example program code, we have configured the Port B Pin 0 an output. Then we have load 206 count value into the TCNT0 register. After that we configure the Timer0 in normal mode with pre-scalar of 8 by setting appropriate bits in the TCCR0A and TCCR0B registers. Then we have to enable the global interrupt using the sei() function. This sei() function sets the I bit in the SREG register. Then in the while loop we do nothing. The ISR(TIMER0_OVF_vect) is the routine which gets called whenever the overflow flag TOV0 in the TIFR0 is set. Note that in order to use the interrupt feature we have to include the interrupt.h header file.
In the next tutorials on ATmega328p Timer Counter Programming with Examples we will show examples of CTC mode and PWM modes.