First, some macros to (hopefully) make the code more readable...
#define MASK1(b1) ( (1<<b1) )
#define MASK2(b1,b2) ( (1<<b1) | (1<<b2) )
#define MASK3(b1,b2,b3) ( (1<<b1) | (1<<b2) | (1<<b3) )
#define MASK4(b1,b2,b3,b4) ( (1<<b1) | (1<<b2) | (1<<b3) | (1<<b4) )
#define MASK5(b1,b2,b3,b4,b5) ( (1<<b1) | (1<<b2) | (1<<b3) | (1<<b4) | (1<<b5) )
#define MASK6(b1,b2,b3,b4,b5,b6) ( (1<<b1) | (1<<b2) | (1<<b3) | (1<<b4) | (1<<b5) | (1<<b6) )
#define MASK7(b1,b2,b3,b4,b5,b6,b7) ( (1<<b1) | (1<<b2) | (1<<b3) | (1<<b4) | (1<<b5) | (1<<b6) | (1<<b7) )
#define MASK8(b1,b2,b3,b4,b5,b6,b7,b8) ( (1<<b1) | (1<<b2) | (1<<b3) | (1<<b4) | (1<<b5) | (1<<b6) | (1<<b7) | (1<<b8) )
These are the steps to setting up a timer...
- Disable the timer while we muck with it. This amounts to setting the "Clock Select" to zero (set CS bits to zero)...
TCCR2B = TCCR2B & ~ MASK3( CS22, CS21, CS20 );
- Configure the waveform we want. To get a precise frequency, we need the timer to automatically clear at a particular value. CTC is the Waveform Generation Mode we want (set WGM bits to 010)...
TCCR2A = (TCCR2A & ~ MASK2( WGM21, WGM20 )) | MASK1( WGM21 );
TCCR2B = (TCCR2B & ~ MASK1( WGM22 ));
- Configure the action to perform whan a compare match occurs. We need the timer to be disconnected from the output pins (COM2A and COM2B set to zero)...
TCCR2A = (TCCR2A & ~ MASK4( COM2A1, COM2A0, COM2B1, COM2B0 ));
- We don't want an interrupt to occur until the timer has gone around at least once so we'll clear the interrupt flag...
TIFR2 = (TIFR2 & ~ MASK1( OCF2A ));
- At this point, we'll go ahead and enable the interrupt. The timer is stopped so an interrupt won't be generated until we've finished configuring the timer...
TIMSK2 |= MASK1( OCIE2A );
[edit]6. Ensure the counter starts from zero the first time...
TCNT2 = 0;[/edit]
- Now for the Clock Select (prescaler) and Output Compare values. The processor's clock (16MHz) is divided by these two values combined. The formula is...
16000000 / (prescaler * (output_compare + 1)) = frequency
We want a prescaler that gets us as close to the goal (16,000 Hz) as possible without going above the goal. Our choices for this timer are 1, 8, 32, 64, 128, 256, and 1024. A prescaler of 8 is the best we can do (7812.5 Hz). Next, we choose an output compare that gets us as close to the goal as possible. 124 is the value. So, by using a prescaler of 8 and an output compare of 124, an interrupt will be generated every...
16000000 / (8 * (124 + 1)) = 16000 times per second
OCR2A = 124;
TCCR2B = (TCCR2B & ~ MASK3( CS22, CS21, CS20 )) | MASK1( CS21 );
- As soon as we set the Clock Select, the timer starts running. There's just one more change: the interrupt generated is a Timer/Counter2 Compare Match A...
ISR(TIMER2_COMPA_vect)
{
// Put your code here.
}
I believe the code can be reduced to this...
TCCR2B = 0;
TCCR2A = _BV( WGM21 );
TIFR2 = 0;
TIMSK2 = _BV( OCIE2A );
OCR2A = 124;
TCCR2B = _BV( CS21 );
If it works, please report back for the benefit of others who might find this thread.