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 that 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 instead of my usual hardware, theory and software format.

ATmega328 - COUNTERS

Figure 1: ATmega168/328 - Counter Pins


COUNTER0 (8 Bit):

Theory of Operation:

Counter0 - ATmega328

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 until 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
TCCR0A COM0A1 COM0A0 COM0B1 COM0B0 - - WGM01 WGM00

Timer/Counter Control Register 0 A

7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit
TCCR0B FOC0A FOC0B - - WGM02 CS02 CS01 CS00

Timer/Counter Control Register 0 B

CS02 CS01 CS00 DESCRIPTION
0 0 0 Timer/Counter0 Disabled
x x x Prescaler settings (omited in this tutorial)
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

CS bits Settings

WGM03 WGM02 WGM01 WGM00 MODE TOP TOV0
0 0 0 0 Normal 0xFF MAX
0 1 0 0 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 OCIE0A TOIE0

Timer/Counter Interrupt Mask Register

7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit
TIFR0 - - - - - OCF0B OCF0A TOV0

Timer/Counter Interrupt Flag Register

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

Timer/Counter 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:

Counter1 - ATmega328

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
TCCR1A COM1A1 COM1A0 COM1B1 COM1B0 - - WGM11 WGM10

Timer/Counter Control Register 1 A

7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit
TCCR1B ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10

Timer/Counter Control Register 1 B

CS12 CS11 CS10 DESCRIPTION
0 0 0 Timer/Counter1 Disabled
x x x Prescaler settings (omited in this tutorial)
1 1 0 External clock source on T1 pin, Clock on Falling edge
1 1 1 External clock source on T1 pin, Clock on rising edge

CS bits Settings

WGM13 WGM12 WGM11 WGM10 MODE TOP TOV1
0 0 0 0 Normal 0xFFFF MAX
0 1 0 0 CTC OCR1A MAX

Counter Modes (Abbreviated Version)

7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit
TIMSK1 - - ICIE1 - - OCIE1B OCIE1A TOIE1

Timer/Counter1 Interrupt Mask Register

7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit
TIFR1 - - ICF1 - - OCF1B OCF1A TOV1

Timer/Counter1 Interrupt Flag Register

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

Timer/Counter Register (stores the counter value, 16 bit) 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 don't 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:

Counter2 - ATmega328

Figure 4: 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