Introduction: Beginning Microcontrollers Part 11: Timers, Counters, and the Microcontroller Clock
Timers and counters are so integral that you will see numerous examples involving them throughout this series. As the name says, timers are used for time and counting. Counting and timing allows you to do some very neat things such as controlling LED brights, angle degrees of servo shafts, receiving sensor data that transmit in PWM, creating a timer, or simply adding a time variable to your microcontroller project.
First it is important to understand there is a clock inside (or outside) the AVR microcontrollers. All microcontrollers have clocks in them or use an external clock. Microcontrollers require clocks so the programs can be executed in rhythm with the clock. Like the programs we are writing, as each clock tick passes, instructions are processed in time with the ticks of the clock.
The timer and counter functions in the microcontroller count in sync with the microcontroller clock. However, the counter only counts up to to either 256 (8 bit counter) or 65535 (16 bit counter). That is a far cry from the 1,000,000 ticks per second that the standard AVR microcontroller provides. The microcontroller offers a quite useful feature called prescaling. Prescaling is a simplistic way for the counter to skip a certain number of clock ticks. The AVR microcontrollers allow prescaling numbers of: 8, 64, 256, and 1024. For example, if set to 64 on the prescaler, the counter will only count every time the clock ticks 64 times. This means in one second (where the microcontroller clicks 1,000,000 times) the counter would only count up to 15,625. If the counter counts up to that number, then you would be able to blink an LED every one 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 in previous tutorials (LED turning on and off and arrays).
Step 1: The Code
#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;}}