With a 16 MHz clock, if you prescale it by 8, you are clocking a timer at a rate of 2 MHz (0.5 microsecond count intervals). For Timer2 in CTC mode, you can generate frequencies from a couple of kHz to something in the neighborhood of 100 kHz
I won't make any claim for "absolute minimum" code but, if you leave out the print statements that I put in for verification, it's just about as low as I care to go:
//
// Use of timer2 to generate a signal for a particular frequency on any output pin
//
// davekw7x
//
#include <avr/io.h>
#include <avr/interrupt.h>
const int ledPin = 13;
// Constants are computed at compile time
// If you change the prescale value, it affects CS22, CS21, and CS20
// For a given prescale value, the eight-bit number that you
// load into OCR2A determines the frequency according to the
// following formulas:
// Note that, due to the time spent in the ISR, it's not practical to generate
// a waveform with period much less than 20 microseconds. You can speed it up
// a little by using bit manipulation on the output port instead of digitalWrite.
//
// Better yet: Enable "Toggle Output on Compare Match" and don't do any port
// reading or writing in the ISR. For this scheme, uou will have to use an
// Output Compare pin on the ATmega instead of an arbitrary output pin.
//
const int prescale = 8;
const int ocr2aval = 127;
// The following are scaled for convenient printing
//
// Interrupt interval in microseconds
const float iinterval = prescale * (ocr2aval+1) / (F_CPU / 1.0e6);
// Period in microseconds
const float period = 2.0 * iinterval;
// Frequency in Hz
const float freq = 1.0e6 / period;
void setup()
{
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
//
// I like to disable global interrupts while initializing counter registers.
// That may not be necessary, but...
cli();
// Set Timer 2 CTC mode and prescale by 8
//
// WGM22:0 = 010: CTC Mode
// WGM2 bits 1 and 0 are in TCCR2A,
// WGM2 bit 2 is in TCCR2B
//
TCCR2A = (1 << WGM21);
// Set Timer 2 No prescaling
//
// CS22:0 = 010: prescale = 8
// CS2 bits 2:0 are all in TCCR2B
TCCR2B = (1 << CS21);
// Enable Compare-match register A interrupt for timer2
TIMSK2 = (1 << OCIE2A);
// This value determines the interrupt interval
OCR2A = ocr2aval;
// Enable global interrupts: Ready to run!
sei();
Serial.print("Interrupt interval = ");
Serial.print(iinterval);
Serial.println(" microseconds");
Serial.print("Period = ");
Serial.print(period);
Serial.println(" microseconds");
Serial.print("Frequency = ");
Serial.print(freq);
Serial.println(" Hz");
}
void loop()
{
// main code
}
// ISR For Timer 2 Compare-match overflow
volatile unsigned char value = 0;
ISR(TIMER2_COMPA_vect)
{
digitalWrite(ledPin, (++value)&1);
}
Output on serial port from my 16 MHz ATmega328p board:
[color=#0000ff]Interrupt interval = 64.00 microseconds
Period = 128.00 microseconds
Frequency = 7812.50 Hz
[/color]
Period/Frequency verified with 'scope and counter.
Now, it "just turns out" that for this setup (16 MHz CPU clock prescaled by 8), the output period in microseconds is OCR2A + 1.
Is this some kind of amazing cosmic coincidence? No, it isn't. Think about it! (See Footnote.)
Anyhow...
By my reckoning, an OCR2A value of 103 should give an output period of 104 microseconds, which is going to be about as close to 9600 Hz that you are going to get with this scheme. I'll leave that verification up to you.
Regards,
Dave
Footnote:
Are you a Terry Pratchett fan, or is your choice of "wossname" as a screen ID just some cosmic coincidence?
Terry has tried to teach us through the years that the universe doesn't supply us with answers...
Just more questions.