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.
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 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:
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:
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