In this tutorial, it is shown how to create time delay with Timer 0 of the ATMega328 microcontroller. Timer 0 is controlled using 5 to 6 timer/counter registers. To create time delay we need to write integer counting value that has to be put into one of the Timer 0 registers. A formula(available in the MCU datasheet) is used to calculate the count value to be loaded into the Timer 0 register for given tine delay such as 1m, 10ms etc. Basically then when the timer starts counting we have to stop and start the timer/counter after one period count. Microcontroller then has basically two ways to let the user know that counting is completed- via overflow flag and through interrupt. Here it is shown how to write time delay by monitoring the overflow flag.
Creating Time Delay Function
Here first we will create one millisecond time delay function called T0delayms(). Any milliseconds delay can be created by passing in that millisecond. For example if you want 100 millisecond delay then we write T0delayms(100). There are two methods of generating time delay-(1)using polling method (2) using interrupt method. Here the polling method is used. To illustrate the usage of the timer 0 delay function we will turn on and off a LED connected to the PortB pin 5(PB5) with arbitrary time delay in milliseconds.
Time Delay Example with LED blink
The following shows the LED connected to the PB5 pin which blinks at the rate of 100 milliseconds.
The following is the timer 0 delay atmega328p C program code which blinks the LED at 100ms interval.
//ATmega328p Program for Time Delay using Timer 0
//by https://ee-diary.com
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#include <avr/io.h>
#define LED PB5
void T0delayOnems(void);
void T0delayms(int n);
int main()
{
DDRB |= (1<<LED);
while (1){
PORTB ^= (1<<LED);
T0delayms(100);
}
return 0;
}
void T0delayOnems(){
//setup Timer 0
TCNT0 = 248; //load count value for 1ms time delay
TCCR0A = 0x00;
TCCR0B = 0b00000101; //normal mode with 1024 pre-scalar
while(!(TIFR0 & (1<<TOV0))); //wait until TOV0 flag is set
TCCR0B = 0; //turn off timer 0
TIFR0 |= (1<<TOV0); //clear TOV0 flag
}
void T0delayms(int n){
for(int i = 0; i <= n; i++){
T0delayOnems();
}
}
The working of this code is as follows.
First we have declared a variable name for the PB5 pin called LED using the define preprocessor directive. Then we have two prototype functions called T0delayOnems() and T0delayms(). In the main loop function, the LED pin is made an output pin. In the while() loop, we toggle the LED pin once every 100ms by passing 100 into the T0delayms() function.
In the T0delayms() function we use the passed in integer value n and loop over n times calling the function the function T0delayOnems().
TheT0delayOnems() function is the actual timer 0 delay function which creates 1ms delay and when called n times generates n milliseconds delay. So can we generate 1ms delay?
The ATmega328p can be operated in four different modes- normal mode, CTC mode, Phase Correct PWM and Fast PWM mode. First we have to set it up in normal mode and because the Timer 0 is 8 bit timer so it can count from 0 to \(2^8-1=255\). When in normal mode, the certain count value is loaded into the TCNT0 register and when the timer is started, it counts from 0 until the count value is reaches and the overflow flag is set. We monitor the state of the overflow flag and reset the timer overflow flag and put again the same count value into the TCNT0 register. So how to calculate timer overflow?
The Timer 0 formula for calculating the count value is given by the following equation.
\(C = 256 -\frac{ F_{CPU}}{NF_d}\) -------------->(1)
where, \(F_d\) is the frequency for the desired time delay
In our case, delay is 1ms so the frequency \(F_d\) is,
\(F_d=\frac{1}{T_d}=\frac{1}{1ms}=1KHz\)
And therefore,
\(C = 256 -\frac{ F_{CPU}}{NF_d}= 256 - \frac{8MHz}{1024 \times 1KHz} = 256 - 8 =248\)
This means that when the TCNT0 register is loaded with 248 then the Timer 0 will overflow every 1ms and overflow flag will be set. So the above formula above is also for the formula to calculate timer overflow. You can also use the ATmega Timer/Counter online calculator to calculate the count value.
In the above equation N is the pre-scalar which can have values 1,8,64,256 and 1024. As can be seen in the above formula, it divides the microcontroller CPU frequency so higher the value of prescalar N the lower will be the CPU frequency and the count value. So the question of how to calculate timer pre-scalar depends upon what timer delay and hence timer frequency and also timer resolution you want.
Once you first set your timer frequency and timer delay goals, you need to configure the ATmega328P Timer registers. As you can see in the timer 0 delay atmega code example above, the resisters which are required are TCCR0A, TCCR0B and TIFR0.
The TCCR0A and TCCR0B ATmega328p registers and their content is shown below.
The TCCR0A and TCCR0B contains the bits to configure the microcontroller mode of operation(WGM bits), the compare output mode bits(COM bits) and the pre-scalar or Clock Source bits(CS bits).
The wave generation mode bits(WGM) dictates the mode of operation according to the following table.
In our case we are using normal mode so all the WGM bits are 0.
The compare output mode(COM) bits dictates nature of the output signal on compare match which is different for the four waveform generation modes. The definition of these COM bits for various modes is below.
In our case we are operating in normal mode(non-PWM mode) and we are not using the CTC mode so the proper COM bits are COM0A1=0 and COM0A0=0.
The pre-scalar or Clock Select bits(CS) dictates the pre-scalar value and hence the timing of the timer 0 according to the following table.
So in our example we do not use any prescalar, that is N=1 and hence the CS bits are CS02=0, CS01=0 and CS00=1. When all these bits are 0 then the timer is stopped.
Hence in the above ATmega328p timer 0 code above, we have used the following C statements to configure the microcontroller in Normal mode, with pre-scalar of N=1
TCCR0A = 0x00;
TCCR0B = 0b00000101; //normal mode with 1024 pre-scalar
The next step is then to monitor the timer overflow flag TOV0 which is located in the TIFR0 as shown below.
We can poll or monitor this TOV0 flag using the while statement in C.
while(!(TIFR0 & (1<<TOV0))); //wait until TOV0 flag is set
Once the overflow flag is set we exit the waiting loop and then execute the next lines of statements. This means that after the overflow flag is set, we stop the timer 0 and clear the overflow flag.
TCCR0B = 0; //turn off timer 0
TIFR0 |= (1<<TOV0); //clear TOV0 flag
Then the same process is repeated for n milliseconds. The LED is therefore turned on and off alternatively with 100ms interval.
Compile and Upload
You need to compile and upload the code into the AVR microcontroller. If you don't know which ATmega328p programming software to use then see the atmega328p isp programming tutorial. If you have difficulty with checking this Timer 0 delay program is working or not, you can try the atmega328p led blink program which uses inbuilt delay function.
Tutorial Furthermore
This same program code for the time delay can also be used with Arduino Uno/Nano boards since they are based on the ATmega328p microcontroller. This is illustrated in the tutorial Time delay using timer 0 without inbuild functions in Arduino. Custom time delay function was created and used to blink a LED but one also use inbuilt function _delay_ms() and _delay_us() which is illustrated in the tutorial ATmega328p LED Blink Programming Tutorial. Here we have used polling method to create timer 0 time delay. An alternative and better method is to use interrupt which is illustrated in the next tutorial .