Atmega328p timer
Hello there. It been a while for me not updating my blog. This is because last week I was quite busy with the school exam. Anyways, let’s learn how to control atmega328p timer by controlling register directly. The aim of this blog is using the internal timer to achieve timestamp since start and delay function.
How a timer works
To explain this, I have drawn a simplified diagram which show its mechanism.
DISCLAIMER: This diagram is not the complete block diagram of the timer but it is enough for learning how timer works.
Basically, only two register need to handle. They are TCNTn (Timer/Counter) and TCCRX (Timer/Counter control register).
Register Description
There are three timer in atmega328p. Although their specification are slightly different, their registers are still similar. So, I will only show the first timer register in this blog. For the others, you can refer to the official datasheet from page 74.
Timer 0 (8 bits)
TCCR0A (Timer/Counter 0 control register
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Name | COM0A1 | COM0A0 | COM0B1 | COM0B0 | - | - | WGM01 | WGM00 |
IO | R/W | R/W | R/W | R/W | R | R | R/W | R/W |
Init | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
TCCR0B (Timer/Counter 0 control register B)
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Name | FOC0A | FOC0B | - | - | WGM02 | CS02 | CS01 | CS00 |
IO | W | W | R | R | R/W | R/W | R/W | R/W |
Init | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Compare mode bit (COM0A1, COM0A0, COM0B1, COM0B0) are for PWM and not important in this blog. Meanwhile, wavefront generation mode (WGM01, WGM 00) are important for configuring TOV0 interrupt (Timer 0 Overflow) trigger value. This can be done by refering to the table below.
Mode | WGM02 | WGM01 | WGM00 | TOP | TOV Flag Set on |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0xFF | MAX |
1 | 0 | 0 | 1 | 0xFF | BOTTOM |
2 | 0 | 1 | 0 | OCRA | MAX |
3 | 0 | 1 | 1 | BOTTOM | MAX |
4 | 1 | 0 | 0 | - | - |
5 | 1 | 0 | 1 | OCRA | BOTTOM |
6 | 1 | 1 | 0 | - | - |
7 | 1 | 1 | 1 | OCRA | TOP |
Force Output Compare (FOC0A, FOC0B) is not in today’s topic so we are not going to discuss about it. However, clock selection bit (CS00, CS01, CS02) is necessary for setting clock prescaler. Here’s the configuration that can be done.
CS02 | CS01 | CS00 | Description |
---|---|---|---|
0 | 0 | 0 | No clock source (Timer/Counter stopped) |
0 | 0 | 1 | clk I/O /(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. |
Now, we already know the registers and their usages. But, there’s still a thing we need to learn. It is the TOV, timer overflow interrupt. However, it is initially disabled by the control unit. To enable it, we need to write a logical one to TOIE (Timer/Counter0 Overflow Interrupt Enable) bit to TIMSK (Timer/Counter Interrupt Mask Register).
How to achieve millis() and micros() function
millis()
At first, TOV interrupt ISR is needed.
When interrupt is triggered:
- Increase
timer0_overflow_count
by 1 - Add the
timer0_millis
with millisecond per overflow - Add the fraction value of millisecond per overflow to
timer0_fract
- If
timer0_fract
overflow,timer0_fract
clear andtimer0_millis
increase by 1
When millis() function is called:
- store state register and disable interrupt by
cli()
- store
timer0_millis
tom
- restore state register
- return
m
micros()
When micros() function is called:
- store state register and disable interrupt by
cli()
- store
timer0_overflow_count
tom
- fetch
TCNT0
value tot
- if TOV0 bit in TIFR0 is one, increase
m
by 1 - shift
m
to left by 8 bit and add witht
- multiple
m
by 4 - restore state register
- return
m
For further understanding, you can refer to my code