Arduino Uno's Timer maximum frequency using Timer Compare Interrupt, not Timer Output Pins (e.g. OC0A)

I want to achieve the generation of a custom binary signal over a digital pin at the highest possible frequency on Arduino Uno.

Using Timer's output pins (e.g. OC0A), one can get a pin to toggle at a maximum frequency of 16MHz (which corresponds to a sqaure wave of frequency 8MHz), as described here: Maximum frequency of digital signal in Arduino Uno?

However as far as I understand, Timer's output pins are only useful if one needs to generate a repetitive waveform such as a PWM or square wave, so it cannot serve my purpose which requires the generation of a custom binary signal.

Using the following code, I was expecting digital pin 9 to toggle at a frequency close to 16MHz too, since I use no prescaler and set the Compare Match Register value to 0:

``````void setup()
{
DDRB = 1 << 1;  // Set D9 as OUTPUT

cli();
TCNT0 = 0; TCCR0A = 0; TCCR0B = 0;
TCCR0A |= (1 << WGM01);  // CTC mode
TCCR0B |= (1 << CS00);  // no prescaling
OCR0A = 0;  // Highest timer overflow frequency possible
TIMSK0 |= (1 << OCIE0A);
sei();
}

ISR(TIMER0_COMPA_vect)
{
PORTB ^= 1 << 1;  // Toggle D9; this allows to measure the toggle frequency, however this could be replaced by some code to generate a custom binary signal
}

void loop(){}
``````

However in fact it appears to toggle at a frequency of only approximately 410kHz (which corresponds to a square wave of frequency 205kHz), as the below image shows:

I used Timer0 but I get the same frequency with Timer1 and Timer2.

• How can one explain that using this code I get this toggle frequency on the digital pin ?
• How can I generate a custom binary signal over a digital pin at the highest possible frequency ?
• I realize this is peanuts compared to the greater issues which are outlined in Majenko's answer, but if you do want to toggle PB1, you can `PINB = 1 << 1;` rather than `PORTB ^= 1 << 1` to save a couple of cycles. The construction may look weird, but they've designed the AVR GPIO hardware so that you can toggle pin be writing 1 bits to their bit positions in the PINx registers; writing 0-bits have no effect. If you take the writing in a tight loop outside of an interrupt suggestion, this may have some impact. Apr 7, 2021 at 21:34
• Thanks, I didn't know you could do it more efficiently that way; this would be impactful in some situations indeed. Apr 8, 2021 at 16:46

How can one explain that using this code I get this toggle frequency on the digital pin ?

Code takes time to execute. Your ISR, when compiled, looks like:

``````ISR(TIMER0_COMPA_vect)
{
124:   1f 92           push    r1
126:   0f 92           push    r0
128:   0f b6           in  r0, 0x3f    ; 63
12a:   0f 92           push    r0
12c:   11 24           eor r1, r1
12e:   8f 93           push    r24
130:   9f 93           push    r25
PORTB ^= 1 << 1;  // Toggle D9; this allows to measure the toggle frequency, however this could be replaced by some code to generate a custom binary signal
132:   85 b1           in  r24, 0x05   ; 5
134:   92 e0           ldi r25, 0x02   ; 2
136:   89 27           eor r24, r25
138:   85 b9           out 0x05, r24   ; 5
}
13a:   9f 91           pop r25
13c:   8f 91           pop r24
13e:   0f 90           pop r0
140:   0f be           out 0x3f, r0    ; 63
142:   0f 90           pop r0
144:   1f 90           pop r1
146:   18 95           reti
``````

That's 18 assembly instructions, each needing between 1 and 4 clock cycles to execute. Referencing the datasheet you can count the exact cycles needed and sum them up, yielding 31 clock cycles.

Toggling at maximum speed that would be 16MHz / 31 = 516kHz.

And then you have the rest of the code running between each call to the interrupt, so it's slower than that.

How can I generate a custom binary signal over a digital pin at the highest possible frequency ?

By not using interrupts, and writing a tight loop in assembly language.

• How did you get the exact assembly out? Is it possible to do that with the Arduino IDE or did you have to use the Atmel IDE?
– Tri
Apr 8, 2021 at 7:40
• That actually addresses the question quite well. My understanding of what is happening is the following: the first Timer Compare Interrupt occurs. Before the ISR has finished running, a second Timer Compare Interrupt occurs, but is queued because the previous ISR has not finished running. As soon as the first ISR has finished running, the second ISR is allowed to run. And that goes on and on is such a way that the ISR is running at a frequency equal to (or at least close to) the inverse of its duration. What do you say about this interpretation? Apr 8, 2021 at 17:38
• How can I use assembly language to achieve a higher toggle frequency than with the Timer Compare Interrupt ? In fact, how can I use assembly language to achieve my goal without using a timer interrupt (which as you showed is slow)? Apr 8, 2021 at 17:40
• @Tri I used `avr-objdump -S <filename.elf>` on the compiled elf file from the Arduino IDE (actually `arduino-cli` as I hate the IDE). Apr 8, 2021 at 18:05
• @Ramanewbie That's a good interpretation, yes. Writing assembly language can be a bit of a black art. You would need to code delay loops to get the timing you want, along with toggling of the IO pin and looking up of the data sequence. Not a fun task. Another option could be to stream the data out using the SPI port, where the speed will be relative to the resolution you desire - maximum 8 MBits/s (4MHz square wave) Apr 8, 2021 at 18:07