Tuesday, 28 August 2012

Timers ( AVR ) : The theory behind it

*As the Analog to Digital tutorial is going take longer than i estimated at first, i'll leave you another introductory on Timers...   

While the use of delay() can be useful for simple tasks,  will also keep the uC busy until it it finishes waiting ! 
So, we are going to have a look at the timers of an AVR chip !

    A timer is a simple counter, controlled by registers. Its advantage is that the input clock and operation of the timer is independent of the program execution. This makes it possible to measure time by counting the elapsed cycles and take the input frequency of the timer into account. So instead of providing instructions in the program that are executed regularly and increment a register, the microcontroller does this all by itself. It is possible to connect the timer to the system clock and thus take advantage of the accuracy of the crystal.
    The AVR has an interesting counting circuit. This circuit has 2 inputs and one output. If you tie it into the Tn pin it will count the pulses on the pin (this is referred to as a counter on the datasheet). If you tie it into the AVRs internal(or external) clock you now have a timer. If you tie the counter to the output you will get an Pulse Width Modulator signal on the OCnx pin. 

    When the Control Logic gets a pulse from the pre-scaler (more on that later) it increments/decrements the TCNTn register. The TCNTn register is compared to the OCRn register. When the TCNTn register reaches the same value as the OCRn value or the TOP/BOTTOM value (0xFF for 8bit Registers/Counters and 0xFFFF for 16bit  Registers/Counters ) the Control Logic clears the TCNT register and activates the TOVn bit (Timer Overflow) which stays set until it is reset by the user. Figure below shows this

- 1stt ( red) shows the pulses generated by the system clock(clk).
- 2nd ( blue) shows the pulses coming out of the pre-scaler (clk_tn or clk/8). In our example the pre-scaler is set to 8 so, it divides the clk(red) by 8.
- 3rd shows the TCNTn (basically the count). The TCNTn updates on the falling edge of the pulse coming out of the pre-scaler. On our first pre-scaler pulse ( blue) our TCNTn is TOP-1 (or 254). On our 2nd pre-scaler pulse( blue) our TCNTn goes to TOP (or 255). Now, on our third pre-scaler ( blue) pulse the TCNTn register overflows back to the BOTTOM (or 0) because it was at its TOP value on the previous pulse. On our 4th pre-scaler pulse ( blue) the TCNTn register gets incremented again this time to BOTTOM + 1 (or 1).
- The 4th shows the TCNTn register again. The only difference is that it shows TCNTn register in a Phase Corrected PWM pattern, which means that it counts up until it hits the TOP and then start to count down until it hits the BOTTOM . * More about this in the PWM tutorial.
- The 5th line ( green) shows us the TOVn (Timer Overflow bit). This bit is set the first time that a timer overflows. Or in other words the first time that the timer goes from its TOP state to its next state (BOTTOM in the 3rd line or, TOP -1 in the 4th line).
- The 6th line shows the OCRnx register. This register deals with the PWM. The only thing that it is showing, is that the OCRnx register can only be updated when an TOVn (timer overflow) condition happens. * More on the OCRnx register in the PWM section.1


In other words, the pre-scaler counts the number of input pulses (from the internal clock or from external sources) and when the number reaches the preset number (0, 8, 32, 64, 128, 256 or 1024) it generates a pulse of its own. This can be seen in Figure 2 (above) which divides the input by 8. This is needed because the AVR only has an 8bit and a 16bit register

Now the maths :

The math is fairly simple:

Pre-scaler = Pre-scaler / Pre-scaler


Pre-scaler = Pre-scaler * Pre-scaler

And don't forget the old

Frequency = Period / Time

Then, to calculate how long would take to the AVR to generate a pulse ?!?
Frequency = Period / Time

Period / Frequency = Time

Time = Period / Frequency

Time = 16bit_register_size / System_clock_speed

Time = 0xFFFF(aka 65535 )/ 16Mhz


What if we use a pre-scaler of (lets say ) 1024 ?!

 = Pre-scaler * 

Pre-scaler =   Time   * 1024


In the datasheet says that it is recommended that all external sources are limited to system clock / 2.5 . The Arduino runs at 16Mhz therefore  you are only able to reliably measure up to an  6.4Mhz  signal or less.
   Also, starting the pre-scaler will start the timer, so I recommend setting the pre-scaler bits last when setting up your timer in your program.

Now, i will leave you with a handy reference table, that i am sure will be helpful many a time !!

As a taster, ill leave you a simple code here for a blinky, to use on the Arduino Board ( as Port B  5/ pin 13 is used for led ); I included a commented pre-scaler at 1 just for reference

#include <avr/io.h> 

int main (void) 
  unsigned char ElapsedTime = 0; // Make a new counter variable and initialise to zero 

  DDRB |= (1 << 5); // Set LED as output 
  TCCR1B |= (1 << CS10); // Set up timer at Fcpu/1 

  for (;;) 
    // Check timer value in if statement, true when count matches 1/2 second 
    if (TCNT1 >= 16000) 
      TCNT1 = 0; // Reset timer value 

      if (ElapsedTime == 244) // Check 
        ElapsedTime = 0; // Reset counter variable 

        PORTB ^= (1 << 5); // Toggle the LED 

Reference :
- AVR130: Setup and Use the AVR Timers

- Reference table
- Timers and counter

1 comment:

  1. I just want you to know:
    This is the most fantastic Analog tutorial I found anywhere. You helped me finish my AVR assignment in time, thank you so much!


Feel free to contact me with any suggestions, doubts or requests.