timerInterrupt_Match.pde

Submitted by Ed_B on Mon, 08/30/2010 - 16:09

Printer-friendly versionPrinter-friendly version

click READ MORE to see the whole post

timerInterrupt_Match.pde  Copyright (c) 2010 Ed Bennett    Demo of setup for a compare-match timer2 interrupt for an Arduino AVR.  Timer2 counts up to a pre-defined value, then clears the counter and sets an interrupt which toggles an i/o pin.   The program generates a square wave with a period of about 30uS -- at the expense of the chip doing anything else. Slowing the interrupt rate will free up the processor, but the speed control values chosen for this example are to make the output go fast.



/*
timerInterrupt_Match.pde  Copyright (c) 2010 Ed Bennett 

demo of setup for a compare-match timer2 interrupt for an Arduino AVR. Generates a square
wave with a period of about 30uS -- at the expense of everything else.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// for an example of a timer overflow interrupt, see timerInterrupt_Overflow.pde


//
// syntax note: WGM22:0 means a group of 3 bits (2, 1, and 0) in the byte called WGM2. 
//

// This demo takes control of Timer2. The Arduino loses the PWM functionality provided 
// by Timer2

int HF0 = 13;       
int outval = 0;

void setup()
{  
  pinMode(HF0, OUTPUT); 
  IRQ_init();
}

/*
loop() is empty in this demo, but it can be used normally in the usual fashion without 
modifing any of the interrupt code.
*/
void loop()
{   

}

/*
This function does setup on the timer 2 hardware to make it generate interrupts. The code uses 
output-compare channel A (it does not touch channel B) to fire the interrupt.

In this setup/initialization function, I'm poking the control bits into the appropriate 
hardware regisers in the MCU. Against normal practice, I'm poking hard-coded values (numbers)
into the registers. This is to make it easy to see what bit is doing what in the different 
registers. An example:

For the Mega48, 88, 168, and 328, bit "OCIE2A" (Output Compare Interrupt Enable timer 2 channel A)
is in bit position 1 of the TMSK2 register. To set this bit, the hard-coded version looks like
TIMSK2 |= 2, binary b00000010. Normally, the bit positions in registers would be expressed
using_BV() and mnemonics from the include file (iom168p.h) for the chip being used. In the include
file, OCIE2A is defined as 2. It's used like this: TIMSK2 |= _BV(OCIE2A); This makes code portable
across chips where OCIE2A might be in a different position in the register. For instance, on some
other chip OCIE2A might be in position 4 (binary b00010000, 0x10hex, decimal 16). The code 
TIMSK2 |= _BV(OCIE2A) would compile for the other chip, without modification, because the header
file for the other chip would have the correct value for OCIE2A (the value 4). By contrast, to run
on the other chip, the hard-coded value (0x02)used in this piece of code TMSK2 |= 2 would have to
be changed by hand to TIMSK2 |= 16. And you would have to read the register description for the
other chip to know this. This function is written using hard-coded values because it has a
didactic purpose in addition to a functional one. It's an opportunity to see what's happening
under the hood when you're poking registers.

*/ 


void IRQ_init(void)
{
  
// This is the master speed adjuster. (in addition to the prescaler, below) 
// It's a numeric value to match w/ TCNT2 to gen. an IRQ.
    OCR2A = 1;     

/* 
Setup  timer 2 for CTC (Clear Timer on Compare match) mode:
Set WGM22:0 = 0x07 (binary 111). (WGM = Waveform Generation Mode)

From the manual: "TOP is defined as OCR2A when WGM2:0 = 7". WGM21 and WGM20 are located at bit
positions 1 and 0 in TCCR2A. Bit WGM22 is located at bit position 3 in TCCR2B. ICK!

WGM value = 0x07 (00000111 binary) sets CTC mode to have the settings of: Fast PWM, OCR2A 
updated on BOTTOM, and TOV set on TOP. Note that CTC mode requires only WGM21 to be set, but the timer
generates interrupts faster with WGM22:0 set.

Output pins are in "normal" mode i.e. disconnected from the timer, when COM2A1:0 = 0x00 and
COM2B1:0 = 0x00. This is the default value at startup.

*/
    TCCR2A |= 3; // binary 00000011
    TCCR2B |= 8; // binary 00001000

/*
Set the clock prescaler to "NO PRESCALING". With a 16MHz crystal,
TIMER2 is clocked at 16MHz. This setting is made with the CS22:0 
(Clock Select) control bits. The setting is CS22:0 = 0x01. Set
CS20 to 1 in TCCR2B. CS20 is located at bit poition 0 in TCCR2B. 
*/
    TCCR2B |= 1; // binary 00000001

/*
Enable OCF2A flag to generate the ISR(TIMER2_COMPA_vect){} interrupt on a match between TCNT2 and
OCR2A (the counter "TOP" value). To enable the OCF2A flag interrupt, enable the OCIE2A (Timer2
Output Compare Match A Enable) bit. The OCIE2A bit is located at bit position 1 in TMSK2.
*/
    TIMSK2 |= 2; // binary 00000010

    sei(); // set global interrupts enable

}

/*
The ISR name is particular to a particlar interrupt. You can't make up your own a name for
the function. The required names are found at nongnu.org in the AVR  superproject libc docs.
At http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html there's a big list
of them along with a list of which chips do a particular type of interrupt. 
*/

ISR(TIMER2_COMPA_vect){ 
  
  outval ^= 1;          // toggle the pin
  digitalWrite(HF0, outval);

}