Microcontroller - A Beginners Guide - Basic and Default Usage of a Timer and Counter and The Microcontroller Clock
Timers and counters are so important that you will find many examples throughout
this tutorial series. As the name implies, timers can tell the time and count. Counting
and timing allows for some really cool things, like controlling the brightness of
LEDs, controlling the angle of servo shafts, receiving sensor data that transmit
in PWM (Pulse Width Modulation - more on that in another tuorial), making a timer
(like on the stove), or just simply adding a time variable to your microcontroller
project.
But first, it's important to know that there is a clock inside (or outside) the AVR microcontrollers. In fact, all microcontrollers have clocks in them (or use one that resides outside of a microcontroller). Microcontrollers need clocks so the our programs can be executed in rythm with the clock. This is the basic function of microcontrollers. A basic instruction is processed when a tick from the clock passes. Just like these programs we are writing, as clock ticks pass, instructions are processed in time with the ticks of the clock.
The timer and counter functions in the microcontroller simply count in sync with the microcontroller clock. However, the counter can only count up to either 256 (8-bit counter), or 65535 (16-bit counter). That's far from the 1,000,000 ticks per second that the standard AVR microcontroller provides. The microcontroller provides a very useful feature called prescaling. Prescaling is simply a way for the counter to skip a certain number of microcontroller clock ticks. The AVR microcontrollers allow prescaling (skipping) numbers of: 8, 64, 256 and 1024. That is, if 64 is set as the prescaler, then the counter will only count every time the clock ticks 64 times. That means, in one second (where the microcontroller ticks one million times) the counter would only count up to 15,625. you could see that if the counter counts up to that number, then you would be able to blink an LED every second.
Mainly, timers have a register for control, and a register that holds the count
number. The control register contains some switches to turn on and off features.
And you guessed it... one of the features is which prescaling to select. The control
register is called TCCR0 or TCCR1 (Timer/Counter Control Register). The TCCR0 is
the 8-bit control register and only has an 8-bit control register, so there is only
8 switches to turn on and off. TCCR1 is 16-bit, so it has 16 switches to turn on
and off, but it comes in two 8-bit registers labeled A and B (TCCR1A and TCCR1B).
The switches are as follows: FOC (force Output Compare), WGM (Waveform Generation
Mode), COM (Compare Match Output Mode) and CS (Clock Select).
The register that holds the count is called the TCNT register. And there is an 8-bit
version (TCNT0) and a 16-bit version (TCNT1). The TCNT1 register actually gets its
number from two other 8-bit registers to create a complete 16-bit number, but that
is all done behind the scenes (abstracted) so you don't need to worry about how
the TCNT1 gets this ability to have 16-bit, just think it's magic.
In the video, two programs were shown: one that just shows a single LED blinking
at approximately 1 second, and another program that has one row of 7 LEDs chasing
every second, and another row of 7 LEDs chasing each at 1 second. The latter program
is shown here since it has the most features used with the 16-bit timer.
Without being repetitive from previous posts, the program initializes the ports
for the LEDs and sets the timer/counter #1 (the 16-bit timer). The TCCR1B control
register is used to set the prescaling factor of 64 with the CS10 and CS11 switches.
Since we want one of the 7 LEDs to chase 1/7th of a second each, we take the number
15,625 (1000000/64 - remember the 1000000 is the 1 mhz clock of the micrcontroller)
and divide it by 7 to get ~2,232.143. Now, you're saying, but you use only 2232
in the pogram!! that's because TCNT1 will only accept integer (no decimals). Now
you're saying, the timing will be off by the amount of the decimal!! True, but the
AVR internal clock is +/- 10% inaccurate anyway. If an external crystal is used,
you sould use a perfect number that represents the appropriate count.
You will notice that the TCNT1 is also reset to zero manually. This is needed otherwise
the TCNT1 will keep counting past the 2232 condition that was set. There are other
control features that has an automatic zeroing of this number, but we will get to
that in another tutorial. The remaining parts of the program is using stuff we learned
i previous tutorials (LED turning on and off and arrays).
#include <avr/io.h>
int main(void)
{
DDRB = 0b01111111;
PORTB = 0b00000000;
DDRD = 0b01111111;
PORTD = 0b00000000;
TCCR1B |= 1<<CS10 | 1<<CS11;
int LEDNumber[2];
while(1)
{
if (TCNT1 > 2232)
{
TCNT1 = 0;
PORTB = 1<<LEDNumber[0];
LEDNumber[0] ++;
if (LEDNumber[0] > 6)
{
LEDNumber[0] = 0;
PORTD = 1<<LEDNumber[1];
LEDNumber[1] ++;
if (LEDNumber[1] > 6)
LEDNumber[1] = 0;
}
}
}
}