Timer/Counter 2 is an 8 bit timer/counter, which means it can count from 0-255. It's not used by the arduino API (as long as you avoid using the analogWrite function on pins 3 or 11), so you can set it up however you want. You can use it for various things, for example:
These are some general notes on how the timer/counter works.
There are several modes of operation:
Most of these modes give you a choice whether the counter top value is 0xff (255 decimal) or the value in OCR2A. You set the mode using the WGM2[n] bits in registers TCCR2A and TCCR2B.
All the modes can be set to interrupt when the counter value is equal to either of the two compare registers, OCR2A and OCR2B. The PWM modes also allow you to change pins 3 and 11 on a compare match. OCR2A controls pin 11; OCR2B controls pin 3. This is set using the COM2[x][n] bits in TCCR2A.
In any of the modes, there are 3 bits in TCCR2B that change the prescaler that the system clock is divided by to generate the counter clock. You use these to divide the clock by 1, 8, 32, 64, 128, 256, and 1024. For example, a prescaler of 8 gives a counter clock of 2MHz with a 16MHz system clock.
The code below will generate a square wave on pin 11 at 1 kHz using phase-correct PWM.
// Set up phase correct PWM on pin OC2A (pin 11) pinMode(11,OUTPUT); TCCR2A = _BV(COM2A1) | _BV(WGM22) | _BV(WGM20); TCCR2B = _BV(CS21) | _BV(CS20); // prescaler = 32 OCR2A = 125; // timer top. adjust this value to tweak the frequency.
The way the code works is it writes to the chip's registers to put the timer into phase correct PWM mode, where it counts up to a top value (which can either be 255 or the value in OCR2A), then counts back down to zero and so on. This example uses OCR2A as the top value, which gives you more control over the exact frequency. Pin 11 toggles every time the count reaches the value in OCR2A. It also sets a prescaler of 32 times on the clock source, which means that the timer counts at 500,000 counts per second (16 Mhz divided by 32).
The advantage of doing it this way, rather than using a loop with delays, is that the signal produced is software independent, which makes it absolutely stable and also frees the body of the program to do other stuff.
You can change the frequency a little bit by increasing or decreasing the OCR2A value, which is the top value the timer counts up to before counting back down. To make bigger changes in frequency, you need to change the prescaler by setting different CS2x bits in TCCR2B.
The code below generates a 1 kHz signal with a duty cycle of 75% (high 3/4 of the time) on pin 11, with one interrupt when the pin goes high, and also an extra interrupt towards the end of the duty cycle.
#include <avr/interrupt.h>
// N.B. these variables need to be declared volatile so they can be used in the
// main loop as well as the interrupt service routine.
volatile char startRead=false; // flag which is set when the low half-cycle starts.
volatile char nextRead=false; // flag which tells main loop to do second reading.
void setup() {
// set pin 11 as an output (for doing PWM)
pinMode(11,OUTPUT);
// Set up the timer.
// Enable interrupt on OCR2A compare match and OCR2B compare match.
TIMSK2 |= _BV(OCIE2A) | _BV(OCIE2B);
// Set up fast PWM on pin OC2A (pin 11)
TCNT2 = 0; // reset counter.
OCR2A = 64; // PWM width. 128 = square wave.
OCR2B = 192; // interrupt on this to do the second reading.
TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20); // set pin 11
// on OCR2A match. Use fast PWM mode with TOP=0xff
TCCR2B = _BV(CS22); // prescaler = 64 from 16 MHz clock
}
ISR(TIMER2_COMPA_vect) {
// Interrupt service routine for timer 2 compare match on OCR2A.
startRead=true; // tell the main loop to start first reading
}
ISR(TIMER2_COMPB_vect) {
// interrupt service routine for timer 2 compare match on OCR2B
nextRead=true; // tell the main loop to start second reading.
}
As far as I can work out, there is a mistake in the datasheet in the section about how the PWM compare matches are done. The code above works as described, but my reading of the datasheet is that it should generate a 25% duty cycle signal (i.e. an inverted 75% signal).
The code below will call an interrupt service routine at regular intervals, with the frequency depending on the clock pre-scaler value you have set.
#include <avr/interrupt.h>
void setup() {
// Set up the timer.
TCCR2A = 0; // normal mode, no PWM, overflow at 0xff.
TCCR2B = _BV(CS21) | _BV(CS20); // pre-scaler = 32.
// Enable interrupt on overflow
TIMSK2 |= _BV(TOIE2);
TCNT2 = 0; // reset counter.
}
ISR(TIMER2_OVF_vect) {
// Interrupt service routine for timer 2 overflow.
// ... do something ...
}
void loop() {
// No need to do anything in the main loop, but if the interrupt code is long, it's
// better to do the important bits first, then set a flag in the interrupt routine, and
// wait for it in the main loop and carry on with the rest when it's set.
// If you want to do this, declare the flag variable as 'volatile'. (which
// tells the compiler to always re-load it from memory, rather than keeping it in
// a register.)
}