I'm trying to get a LED blink at 56 kHz with using an interrupt. At some point the board wouldn't get higher then around 29.5 kHz (measured with a USBee) when changing the compare register. I've used several codes from the net but following code (thanks to http://www.letmakerobots.com) illustrates this the most simplest way:
I suspect digitalRead()+digitalWrite() are too slow.
Have you thought of using the timer to generate the square wave output directly? There is a pin associated with each Output Compare Register. You can set the timer to toggle the pin each time it hits TOP. No interrupt needed! You can get output frequencies up to 4 MHz for 56 kHz should not be a problem.
The interrupts can't keep up at that rate. You need an interrupt every 8.9375 uS, and it takes around 3 uS to set and and leave an interrupt. Then there is the time taken to read the pin, toggle it, and write it again.
This code generates 56 kHz on pin 9 using the timer but the hardware toggling (no interrupts):
const byte LED = 9;
void setup() {
pinMode (LED, OUTPUT);
// set up Timer 1
TCCR1A = _BV (COM1A0); // toggle OC1A on Compare Match
TCCR1B = _BV(WGM12) | _BV(CS10); // CTC, no prescaler
OCR1A = 142; // compare A register value (143 * clock speed)
} // end of setup
void loop() { }
If you do direct port manipulation you can just squeeze it in, in time, using an interrupt:
digitalRead and digitalWrite are functions (which have an overhead to call) plus they do a table lookup to convert the pin number to the bit number. By doing it yourself you are saving some time.
I'm no AVR expert by any stretch, but in Arduino Internals by Dale Wheat,
he mentions using the following functions to speedup ISRs:
bitSet(PORTB, 5);
bitClear(PORTB, 5);
Doing direct calls to the compiler underlying the Arduino shell, I think. You have
to use the AVR nomenclature as arguments, and not the Arduino nomenclature,
for the pin callouts.
Based on one of Nick's proposals I've altered and optimized my code, see below, and it works 100%. This now can go up to 193 kHz. Many thanks to all that have responded!!!
#define ledPin 12
void setup()
{
pinMode(ledPin, OUTPUT);
// initialize timer1
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
TCCR1B |= (1 << WGM12); // CTC mode
TCCR1B |= (1 << CS10); // no prescaler
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
OCR1A = 141; // compare match register =16MHz/1/56 kHz/2 -1 (troggle) = 142
// in practice use 141 then signal 56.2-56.3 kHz, if 1 used then max is 193.5 kHz
}
ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine
{
PORTB ^=(1<<6); //pin 12 on Mega 2560
}
void loop()
{
// your program here...
}
One little-known feature of the ATmega processor is that each PORT has a PIN register that will do the XOR for you. I expect that will be faster because it doesn't have to do a read/modify/write cycle. Try this:
ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine
{
// The PIN register, when written, will toggle only those pins whose bits are set to 1.
PINB = (1<<6); // Toggle pin 12 on Mega 2560. Equivalent to bitSet(PINB, 6);
}