My Books‎ > ‎AVR GUIDE‎ > ‎

COUNTERS ON THE ATmega168/328


INTRODUCTION:

    This chapter is basiclly the same as 4.1 however, it is adapted to the ATmega168/328.  The reason I chose this is because the registers have changed dramatically from the ATmega8.

    This chapter describes the operation of the hardware counters of the ATmega168/328. While software counters are fairly simple they can easily miss fast signals in long complex programs.  Hardware counters are always looking for inputs and if configured correctly will not miss even fast signals.

    The ATmega168/328 has 3 counters, 2 8bit counters and 1 16bit counter.  Since all 3 counters have unique registers and special functions I will break this chapter subsections by counter insted of my usual hardware, theory and software format.


Figure 1: ATmega168/328 - Counter Pins




COUNTER0 (8 Bit):


Theory of Operation:


Figure 2: Counter0


    Counter0 is the most basic Counter on the AVR.  The T0 pin(as always T0 has the same restrictions as any  INPUT or OUTPUT), passes the signals into the Edge Detection, which when triggered sends a pulse to the Control Logic.  The Control Logic updates the TCNT0 register which is a 8 bit register.  The Control Logic unit in counter0 has the ability to increment, decrement or clear(reset) the TCNT0 register.  There are 8 different modes of operation that govern the Control Logic, however, most of them are only useful for PWM operations.  

    For counting external inputs only 2 modes really apply.  the Normal Mode, and the CTC (Clear Timer On Comparer). 

    In Normal Mode the counter will count untill it reaches the TOP value.  When the TCNT0 register passes the TOP value (0xFF or 255) it simply overflows (or overruns) back to 0, at the same time the TOV1 flag is set.  In CTC (Clear Timer on Compare) mode the counter will count until it hits the value specified in the OCR0 register.  When the TCNT0 passes the TOP value (Specified by the OCR0) it resets to 0 and at the same time sets the TOV0 flag.

    The only other thing that you I could say about Counter0 is that if it is enabled, it will count events on the T0 pin regardless of if it is set as an input or an output.

  7 bit 6 bit  5 bit  4 bit  3 bit  2 bit  1 bit  0 bit 
TCCR0ACOM0A1COM0A0 COM0B1 COM0B0  - WGM01WGM00 
Timer/Counter Control Register 0 



  7 bit 6 bit  5 bit  4 bit  3 bit  2 bit  1 bit  0 bit 
TCCR0B FOC0AFOC0B WGM02  CS02 CS01CS00 
Timer/Counter Control Register 0


 CS02 CS01  CS00  DESCRIPTION
000 Timer/Counter0 Disabled 
XXX Prescaler settings (omited in this tutorial)
110 External Clock Source on T0 pin, Clock on falling Edge
111 External Clock Source on T0 pin, Clock on Rising Edge 
CS bits

 WGM03 WGM02 WGM01 WGM00 MODE  TOP  TOV1 
0000 Normal 0xFF MAX 
0100 CTC OCR1A MAX
Counter0 Modes (Abbreviated Version)


  7 bit 6 bit  5 bit  4 bit  3 bit  2 bit  1 bit  0 bit 
 TIMSK0 - OCIE0B OCIE0ATOIE0 
Timer/Counter0 Interrupt Mask Register


  7 bit 6 bit  5 bit  4 bit  3 bit  2 bit  1 bit  0 bit 
 TIFR0 - OCF0B OCF0ATOV0 
Timer/Counter0 Interrupt Flag Register


  7 bit 6 bit  5 bit  4 bit  3 bit  2 bit  1 bit  0 bit 
 TCNT0 





 
Timer/Counter0 Register (stores the counter value)


Software:

    You want simple, you got it.  When it comes to a Counter all you have to do is activate it for clock on falling edge or clock on rising edge using the CS0n bits within the TCCR0 register and decide if you want to use the overflow interrupt or not.  Brilliantly simple.

ATmega168/328 Code:
// this code sets up counter0 and with no interrupts


#include <avr/io.h>


int main(void)
{
    DDRD &= ~(1 << DDD4);     // Clear the PD4 pin
    // PD4 is now an input

    PORTD |= (1 << PORTD4);   // turn On the Pull-up
    // PD4 is now an input with pull-up enabled

    TCCR0B |= (1 << CS02) | (1 << CS01) | (1 << CS00);
    // Turn on the counter, Clock on Rise


    while (1)
    {
        // we can read the value of TCNT0 hurray !!
    }
}


ATmega168/328 Code:
// this code sets up counter0 and with interrupts enabled


#include <avr/io.h>
#include <avr/interrupt.h>



int main(void)
{
    DDRD &= ~(1 << DDD4);     // Clear the PD4 pin
    // PD0 is now an input

    PORTD |= (1 << PORTD4);   // turn On the Pull-up
    // PDO is now an input with pull-up enabled

    TIMSK0 |= (1 << TOIE0);    // enable timer interrupt

    TCCR0B |= (1 << CS02) | (1 << CS01) | (1 << CS00);
    // Turn on the counter, Clock on Risf

    sei();                    // enable Interrupts


    while (1)
    {
        // we can read the value of TCNT0 hurray !!
    }
}


ISR (TIMER0_OVF_vect)
{
    // interrupt just fired
}





COUNTER1 (16 Bit):


Theory of Operation:

Figure 3: Counter1


    As you could see counter1 is just a 16bit version of Counter0.  The T1 pin(as always T1 has the same restrictions as any  INPUT or OUTPUT), passes the signals into the Edge Detection, which when triggered sends a pulse to the Control Logic.  The Control Logic updates the TCNT1 register which is a 16 bit register.  The Control Logic unit in counter1 has the ability to increment, decrement or clear(reset) the TCNT1 register.  There are 16 different modes of operation that govern the Control Logic, however, most of them are only useful for PWM operations.  

    For counting external inputs only 2 modes really apply.  the Normal Mode, and the CTC (Clear Timer On Comparer). 


Normal Mode:

    In Normal Mode the counter will count untill it reaches the TOP value.  When the TCNT1 register passes the TOP value (0xFFFF or 65535) it simply overflows (or overruns) back to 0, at the same time the TOV1 flag is set. 


CTC Mode:

    In CTC (Clear Timer on Compare) mode the counter will count until it hits the value specified in the OCR1 register.  When the TCNT1 passes the TOP value (Specified by the OCR1) it resets to 0 and at the same time sets the TOV1 flag.

    An interrupt can be configured to trigger when the TOV1 flag is set.



  7 bit 6 bit  5 bit  4 bit  3 bit  2 bit  1 bit  0 bit 
TCCR1ACOM1A1COM1A0COM1B1COM1B0  - WGM11WGM10 
Timer/Counter1 Control Register A


  7 bit 6 bit  5 bit  4 bit  3 bit  2 bit  1 bit  0 bit 
 TCCR1B ICNC1ICES1 WGM13 WGM12  CS12 CS11CS10 
Timer/Counter1 Control Register B


 CS12 CS11  CS10  DESCRIPTION
000 Timer/Counter0 Disabled 
XXX Prescaler settings (omited in this tutorial)
110 External Clock Source on T1 pin, Clock on falling Edge
111 External Clock Source on T1 pin, Clock on Rising Edge 
CS bits


 WGM13 WGM12 WGM11 WGM10 MODE  TOP  TOV1 
0000 Normal 0xFFFF MAX 
0100 CTC OCR1A MAX
Counter1 Modes (Abbreviated Version)


  7 bit 6 bit  5 bit  4 bit  3 bit  2 bit  1 bit  0 bit 
 TIMSK1 -ICIE1  OCIE1B OCIE1ATOIE1 
Timer/Counter1 Interrupt Mask Register


  7 bit 6 bit  5 bit  4 bit  3 bit  2 bit  1 bit  0 bit 
 TIFR1 -ICF1  OCF1B OCF1ATOV1 
Timer/Counter Interrupt Flag Register


  7 bit 6 bit  5 bit  4 bit  3 bit  2 bit  1 bit  0 bit 
TCNT1L 





 
TCNT1H        
Timer/Counter Register (stores the counter value) Can be accessed as TCNT1


<<< ADD OCRIA register here >>>


Selecting Counter Modes:

    This is a bit funny, the 4 bits that control Counter2's mode are spread across 2 different registers:  the WGM13 bit and the WGM12 bit are stored in the TCR1B register while the WGM11 and WGM12 bits are stored within the TCR1A register.  Set the bits according to the Counter1 modes table and your ready and your almost ready to rock.  If you selected the CTC mode, your next step is to put the value that you want to count to into the OCR1A register. 




Software:

    Counter1 isn't any different then Counter0, if we dont change the values of the WGMnx registers it will function the same way as Counter0 (well aside from being 16 bit instead of 8 bit that is).


ATmega168/328 Code:
// this code sets up counter1 and with no interrupts in normal mode


#include <avr/io.h>


int main(void)
{
    DDRD &= ~(1 << DDD5);     // Clear the PD5 pin
    // PD5 is now an input

    PORTD |= (1 << PORTD5);   // turn On the Pull-up
    // PD5 is now an input with pull-up enabled

    TCCR1B |= (1 << CS12) | (1 << CS11) | (1 << CS10);
    // Turn on the counter, Clock on Rise


    while (1)
    {
        // we can read the value of TCNT1 hurray !!
    }
}


ATmega168/328 Code:
// this code sets up counter1 and with interrupts enabled


#include <avr/io.h>
#include <avr/interrupt.h>



int main(void)
{
    DDRD &= ~(1 << DDD5);     // Clear the PD4 pin
    // PD5 is now an input

    PORTD |= (1 << PORTD5);   // turn On the Pull-up
    // PD5 is now an input with pull-up enabled

    TIMSK1 |= (1 << TOIE1);    // enable timer interrupt

    TCCR1B |= (1 << CS12) | (1 << CS11) | (1 << CS10);
    // Turn on the counter

    sei();                     // Turn on Interupts


    while (1)
    {
        // we can read the value of TCNT1 hurray !!
    }
}


ISR (TIMER1_OVF_vect)
{
    // interrupt just fired
}






COUNTER2 (8 Bit):


Theory of Operation:

Figure 3: Counter 2

    Counter2 is designed to be used with an external Crystal Oscillatory.  In fact, it is optimized for a 32.768 kHz XTAL, this will provide you with a very accurate Real Time Clock.  Because this is a lot closer to being a Timer then a counter I will cover this topic in the Timer tutorial.

    Lastly, the datasheet states that hooking up a input to TOSC1 is not recommended.  I have never done it so I can say what it will do.


    Woot, for copy and paste.  I got this done at the airport in 30 min !!!

Cheers
Q



Comments