COUNTERS ON THE ATmega8
INTRODUCTION:
This chapter describes the operation of the hardware counters of the ATmega8. 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 ATmega8 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: ATmega8 - Counter Pins
COUNTER0 (8 Bit):
Theory of Operation:
Figure 2: Counter0
Counter0 is the most basic Counter on the AVR. It gets its input from 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 receives the signal and increments the TCNT0 register. When the TCNT0 register passes the TOP value (0xFF or 255) it simply overflows (or overruns) back to 0, at the same time the TOV0 flag is set. Note that the prescaler is part not of the Counter.
An interrupt can be set to trigger when Counter0 overflows, however since the value at which the overflow occurs is set in stone to (0xFF or 255) we need to use a bit of programming if we want to count to a given number that is bigger then 255 and not an increment of 256.
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 | |
---|---|---|---|---|---|---|---|---|
TCCR0 | - | - | - | - | - | CS02 | CS01 | CS00 |
Timer/Counter Control Register 0
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
7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
---|---|---|---|---|---|---|---|---|
TIMSK | OCIE2 | TOIE2 | TICIE1 | OCIE1A | OCIE1B | TOIE1 | - | TOIE0 |
Timer/Counter Interrupt Mask Register
7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
---|---|---|---|---|---|---|---|---|
TIFR | OCF2 | TOV2 | ICF1 | OCF1A | OCF1B | TOV1 | - | 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, then decide if you want to use the overflow interrupt or not using the TOIEn bit within the TIMSK register. Brilliantly simple.
ATmega8 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 TCCR0 |= (1 << CS02) | (1 << CS01) | (1 << CS00); // Turn on the counter, Clock on Rise while (1) { // we can read the value of TCNT0 hurray !! } }
ATmega8 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 // PD4 is now an input with pull-up enabled TIMSK |= (1 << TOIE0); // enable timer interrupt TCCR0 |= (1 << CS02) | (1 << CS01) | (1 << CS00); // Turn on the counter, Clock on Rise sei(); 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 a bit more fancy then Counter0 but, all the theory is still the same. The T1 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 TCNT1 register which is a 16 bit register. The biggest between Counter0 and Counter1 (aside from being 16bit) is that 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).
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. 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 | FOC1A | FOC1B | 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 | |
---|---|---|---|---|---|---|---|---|
TIMSK | OCIE2 | TOIE2 | TICIE1 | OCIE1A | OCIE1B | TOIE1 | - | TOIE0 |
Timer/Counter Interrupt Mask Register
7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
---|---|---|---|---|---|---|---|---|
TIFR | OCF2 | TOV2 | ICF1 | OCF1A | OCF1B | TOV1 | - | TOV0 |
Timer/Counter 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
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). So first we set our mode using the WGM1n bits within the TCCR1A and TCCR1B register. Then we choose if we want to clock on the falling edge or the rising edge using CS1n within the TCCR1B register. And finally decide if we want to enable the interrupt using the TOIE1 within the TIMSK register.
ATmega8 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 !! } }
ATmega8 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 TIMSK |= (1 << TOIE1); // enable timer interrupt TCCR1B |= (1 << CS12) | (1 << CS11) | (1 << CS10); // Turn on the counter, sei(); 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, another one bites the dust!
Cheers
Q