# Maximum pin toggle speed

**URL:** <https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378>\
**Category:** Frequently-Asked Questions\
**Created:** [December 26, 2008, 10:06am UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378 "2008-12-26T10:06:56Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![westfw](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/westfw/32/213443_2.png) [@westfw](https://forum.arduino.cc/u/westfw)\
**Post date:** [December 26, 2008, 10:06am UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/1 "2008-12-26T10:06:56Z")

</div>

Hmm. This was asked over on AVRFreaks, and it's FREQUENTLY a Frequently asked question about CPUs/etc, though I don't recall ever seeing it asked here. Since I actually did the experiment, I'll post the answer anyway!

```cpp
  while (1) {
    digitalWrite(3, 1);
    digitalWrite(3, 0);
  }

```

produces a 106.8kHz square wave on digital pin 3 in Arduino 0010, 0011, and 0012. Though it would probably be foolish to count on exactly that speed; library functions are subject to change.

```cpp
  cli();
  while (1) {
    PORTD |= 0x8;
    PORTD &= ~0x8;
  }

```

on the same board runs at 2.667MHz. (This does produce the minimal sbi/cbi/rjmp loop that you'd expect, BTW.)  
(so that's about a 20x penalty for the arduino library code; sounds about right: the overhead of abstracting IO to "pin number" is pretty substantial: a subroutine call, lookup table to get the port, another lookup table to get the bit, a third to check whether analogWrite is in use, and then less efficient instructions to access the port "indirectly")

---

<div class="post-metadata">

**Author:** ![system](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/system/32/1140315_2.png) [@system](https://forum.arduino.cc/u/system)\
**Post date:** [December 26, 2008, 2:46pm UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/2 "2008-12-26T14:46:49Z")

</div>

Speaking of the hefty digitalWrite() overhead, I noticed two things when I went poking into that code.

One, some pins are slower than others, because they have PWM timers that have to be disengaged.

Two, the "function" that turns off those timers has a really ugly chain of if/if/if/if statements that should really be a switch or at least if/else if/else if/else statements. I did a timing analysis like you, westfw, and found that the gcc compiler really does optimize the code the same way since it's all forced inline, and comparing against static const data. But it bugs me to see code that relies on the optimizer to rephrase so drastically to do the right thing-- one accidental tweak or compiler update and boom, digitalWrite() could be twice as slow, because the compiler decides to implement what is written, not what might possibly be implied.

---

<div class="post-metadata">

**Author:** ![system](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/system/32/1140315_2.png) [@system](https://forum.arduino.cc/u/system)\
**Post date:** [December 26, 2008, 6:58pm UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/3 "2008-12-26T18:58:23Z")

</div>

That's interesting.

re: 2.667MHz example. Was that at 8hz? I have been operating under the assumption that a 16mhz atmega did 16,000,000 instructions per second. Which would be 5.333 million loops per second with only 3 instructions.

Also the output isn't exactly "square", you would need another STI (or NOP) to hold the pin high for the same time as low, but it would be a bit slower if half/on half/off was a requirement.

---

<div class="post-metadata">

**Author:** ![mem](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/mem/32/213477_2.png) [@mem](https://forum.arduino.cc/u/mem)\
**Post date:** [December 26, 2008, 7:49pm UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/4 "2008-12-26T19:49:30Z")

</div>

I would have thought it would do 4 million loops per second at 16mhz. The while statement will be optimized to a relative jump, which is a two clock instruction – so the whole loop should be 4 clock cycles long

---

<div class="post-metadata">

**Author:** ![westfw](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/westfw/32/213443_2.png) [@westfw](https://forum.arduino.cc/u/westfw)\
**Post date:** [December 26, 2008, 9:55pm UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/5 "2008-12-26T21:55:20Z")

</div>

> some pins are slower than others, because they have PWM timers that have to be disengaged.

Yes. The code reads:

```arduino
  timer = digitalPinToTimer(pin);
    :
  if (timer != NOT_ON_TIMER) turnOffPWM(timer);

```

I had assumed digitalPinToTimer() would be false if analogWrite wasn't active, but it's actually a ROM-based function that says which timer MIGHT be associated with the pin. Pin3 used in my example IS a PWM output...

The examples are with a 16MHz MDC Bare Bones Board, as shown by the little frequency readout on my Tek TDS210 scope; you're right that that's not what I'd expect based on instruction timings. This may require more investigation!

---

<div class="post-metadata">

**Author:** ![westfw](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/westfw/32/213443_2.png) [@westfw](https://forum.arduino.cc/u/westfw)\
**Post date:** [December 26, 2008, 10:00pm UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/6 "2008-12-26T22:00:25Z")

</div>

Ah. SBI and CBI are 2-cycle instructions (I guess that makes sense, since they're read-modify-write), as is the jmp, so 2.66666 MHz is exactly as expected after all!

This implies that I can make a faster loop using OUT and pre-loaded registers...

---

<div class="post-metadata">

**Author:** ![westfw](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/westfw/32/213443_2.png) [@westfw](https://forum.arduino.cc/u/westfw)\
**Post date:** [December 27, 2008, 3:10am UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/7 "2008-12-27T03:10:02Z")

</div>

```cpp
  while (1) {
    PORTD = ones;
    PORTD = zeros;
  }

```

Gives me 4Mhz (and noticeably not "square wave." Adding a nop makes it more square but changes the max freq to about 3.2MHz. Adding two nops makes for very square, but back to 2.667MHz.)

Using digitalWrite() on a non-PWM pin (4 instead of 3) runs about 148.4kHz instead of 106.8kHz:

```cpp
  while (1) {
    digitalWrite(4, 1);
    digitalWrite(4, 0);
  }

```

---

<div class="post-metadata">

**Author:** ![system](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/system/32/1140315_2.png) [@system](https://forum.arduino.cc/u/system)\
**Post date:** [December 27, 2008, 6:04am UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/8 "2008-12-27T06:04:59Z")

</div>

Ok, it makes sense now 🙂

Here's a square wave version (resulting assembler confirmed), should be 8 cycles per loop, and that would be a 2mz output on a 16mhz CPU (1 mhz on an 8mhz cpu).

```cpp
  cli();
  while (1) {
    PORTD |= 0x8;
    PORTD |= 0x8;
    PORTD &= ~0x8;
  }

```

Are changing fuses allowed :D? it seems you can also program the CKOUT fuse and get the system clock echoed on CLK0 (digital 8), which could be useful I recon. So that would be a 16mhz toggle speed, and it is conceivable that you could control it with an external gate with high precision.

---

<div class="post-metadata">

**Author:** ![westfw](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/westfw/32/213443_2.png) [@westfw](https://forum.arduino.cc/u/westfw)\
**Post date:** [December 27, 2008, 9:00am UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/9 "2008-12-27T09:00:37Z")

</div>

You can usually suck the system clock off of one of the oscillator pins (XTAL2 is an output of an inverter-based circuit.), especially if you're willing to add a gate to square things off. I guess it depends on whether the fuse bits are set for the "low power" oscillator or the "full swing" oscillator. See Sections 7.2 through 7.4 of the Atmega168 data sheet.

---

<div class="post-metadata">

**Author:** ![selfonlypath](https://avatars.discourse-cdn.com/v4/letter/s/9de0a6/32.png) [@selfonlypath](https://forum.arduino.cc/u/selfonlypath)\
**Post date:** [June 14, 2009, 3:04am UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/10 "2009-06-14T03:04:27Z")

</div>

You can save one cycle from

```cpp
cli();
  while (1) {
    PORTD |= 0x8;
    PORTD |= 0x8;
    PORTD &= ~0x8;
  }

```

and get the higher square wave frequency with

```cpp
cli();
  while (1) {
    PORTD |= B1000;
    PORTD &= B11110111;
  }

```

---

<div class="post-metadata">

**Author:** ![kg4wsv](https://avatars.discourse-cdn.com/v4/letter/k/3ab097/32.png) [@kg4wsv](https://forum.arduino.cc/u/kg4wsv)\
**Post date:** [June 14, 2009, 5:32pm UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/11 "2009-06-14T17:32:57Z")

</div>

The newer devices (168/328, IIRC) have an instruction to toggle a pin - too bad I can't recall the name of it right now... That would reduce it by one more instruction.

-j

---

<div class="post-metadata">

**Author:** ![system](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/system/32/1140315_2.png) [@system](https://forum.arduino.cc/u/system)\
**Post date:** [June 14, 2009, 6:14pm UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/12 "2009-06-14T18:14:29Z")

</div>

I think the new 'toggle' functionailty for pins works by writing a 1 to it's PIN register.  
e.g. to toggle bit 3 of port B, you can do:

PINB = B1000 ;

[yup - M88 datasheet, section 13.1 : "writing a logic one to a bit in the PINx Register, will result in a toggle in the corresponding bit in the Data Register"]

This does NOT work on Mega8's.

I think this means a 3 cycle loop is possible (1 cycle for the OUT instruction, 2 cycles for the RJMP).  
That results in a 2.666Mhz square signal.

In theory you can beat this by filling the memory space with that OUT instruction, and letting the program-counter roll-over at the end of flash, but there are problems with this solution, despite a theoretical 8Mhz output...

---

<div class="post-metadata">

**Author:** ![selfonlypath](https://avatars.discourse-cdn.com/v4/letter/s/9de0a6/32.png) [@selfonlypath](https://forum.arduino.cc/u/selfonlypath)\
**Post date:** [June 14, 2009, 6:35pm UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/13 "2009-06-14T18:35:43Z")

</div>

Could someone tell exactly how many cycles uses the while(true), I mean the overhead or surrounding

```cpp
cli();
  while (true) {
    PORTD |= B1000;
    PORTD &= B11110111;
  }

```

For example, each while(true) loop will take 2 cycles to execute both PORTD writing / toggling but how many cycles the while(true) itself will take each run whatever instructions inside the loop ?

---

<div class="post-metadata">

**Author:** ![zooto68](https://avatars.discourse-cdn.com/v4/letter/z/5f9b8f/32.png) [@zooto68](https://forum.arduino.cc/u/zooto68)\
**Post date:** [June 14, 2009, 6:37pm UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/14 "2009-06-14T18:37:19Z")

</div>

That's an inexcusable loss of speed. The compiler should be conveting the digitalWrite code into the same ASM that you get by doing it in raw C. This should be fixed in the next IDE if possible.

---

<div class="post-metadata">

**Author:** ![system](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/system/32/1140315_2.png) [@system](https://forum.arduino.cc/u/system)\
**Post date:** [June 14, 2009, 7:31pm UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/15 "2009-06-14T19:31:58Z")

</div>

@selfonlypath:  
Your code (or at least a variant using PORTB on my M8) compiles to:

```cpp
sbi      0x18, 3
cbi      0x18, 3
rjmp      .-6

```

Each of these instructions are 2 cycles, hence this is a 6 cycle loop.  
Note that it's not uniform though. The output will be on for 2 cycles, and off for 4 cycles.

---

<div class="post-metadata">

**Author:** ![selfonlypath](https://avatars.discourse-cdn.com/v4/letter/s/9de0a6/32.png) [@selfonlypath](https://forum.arduino.cc/u/selfonlypath)\
**Post date:** [June 14, 2009, 8:58pm UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/16 "2009-06-14T20:58:08Z")

</div>

@condemmed

i'm a bit surprised you come with 2 cycles for`PORTD |= B1000;` and 2 cycles for `PORTD &= B11110111;` because I've benchmarked & runned real time with big N this

```cpp
for(i=0;i<N;i++){
    PORTD |= B1000;
    PORTD &= B11110111;
  }

```

in the arduino standard sketch and I come up with one cycle each instruction. Of course, in that case the iteration overhead consumes 6 extra-cycles whatever inside the loop hence a total of 8 cycles.

Please note my benchmarking method cannot trace execution time when using

```cpp
while (true) {
    .....
  }

```

which was my initial question.

---

<div class="post-metadata">

**Author:** ![westfw](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/westfw/32/213443_2.png) [@westfw](https://forum.arduino.cc/u/westfw)\
**Post date:** [June 14, 2009, 10:21pm UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/17 "2009-06-14T22:21:41Z")

</div>

> That's an inexcusable loss of speed.

It's a lot more complicated than that. The digitalWrite code does a lot of things that the raw C code we're looking at here does not do:

- Pin number is a variable rather than a constant
- New pin value is a variable rather than a constant
- Convert "arduino pin number" to AVR port address and bit
- If the port is capable of hardware PWM, make sure PWM is off

I mean, it may SOUND awful that a digitalWrite loop is 20x slower than the "fastest possible" loop, but that "fastest" possible pin change operation is a single instruction, which means that digitalWrite is only using on the order of 20 instructions (including function call overhead), and there is not a lot of room for improvement while maintaining the same functionality (there is room for some, but then you hit the fact that people wanting ultimate speed still won't be happy, and blinding speed is not particularly one of the goals of the arduino environment anyway.) (I think digitalWrite got slower in Arduino15, too...)

---

<div class="post-metadata">

**Author:** ![westfw](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/westfw/32/213443_2.png) [@westfw](https://forum.arduino.cc/u/westfw)\
**Post date:** [June 14, 2009, 11:40pm UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/18 "2009-06-14T23:40:57Z")

</div>

@selfonlypath: read the whole thread. The cbi/sbi instructions are two cycles according to the datasheet. The extra instruction you removed to make things faster was added to make the output a square wave rather than asymetric.

Your for loop produces:

```cpp
void loop()                     // run over and over again
{
  int i;
#define N 10000
  for(i=0;i<N;i++){
 11c:   80 e0           ldi     r24, 0x00       ; 0  (initialization)
 11e:   90 e0           ldi     r25, 0x00       ; 0
    PORTD |= B1000;
 120:   5b 9a           sbi     0x0b, 3 ; 2 cycles
    PORTD &= B11110111;
 122:   5b 98           cbi     0x0b, 3 ; 2 cycles
 for(i=0;i<N;i++){
 124:   01 96           adiw    r24, 0x01       ; 2 cycles
 126:   27 e2           ldi     r18, 0x27       ;  1 cycle
 128:   80 31           cpi     r24, 0x10       ; 1 cycle
 12a:   92 07           cpc     r25, r18         ;1 cycle
 12c:   c9 f7           brne    .-14            ; 0x120 <loop+0x8> (2 cycles if taken)
  }

```

or 11 cycles for the whole loop by "analysis." How are you measuring 8 cycles?  
Note that your code has the pin set to 1 for 1 or two cycles and set to zero for 9 or 10 cycles; I hope you didn't expect a square wave...

---

<div class="post-metadata">

**Author:** ![system](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/system/32/1140315_2.png) [@system](https://forum.arduino.cc/u/system)\
**Post date:** [June 14, 2009, 11:54pm UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/19 "2009-06-14T23:54:41Z")

</div>

@selfonlypath:  
The following code

```cpp
    for(uint8_t i=0;i<250; i++){
        PORTD |= 0b00001000;
        PORTD &= 0b11110111;
    }

```

compiles (WinAVR-20090313, -Os) to the following instructions:

```cpp

```

**`**  **sbi      0x0b, 3**  **`**

```cpp
      ; 11

```

**`**  **cbi      0x0b, 3**  **`**

```cpp
      ; 11
    subi      r24, 0xFF      ; 255
    cpi      r24, 0xFA      ; 250
    brne      .-10

```

sbi and cbi are 2 cycle instructions, according to my Mega88 datasheet.  
This should therefore give a loop of 2 ticks ON, 6 ticks OFF, so 2Mhz output at 25% duty cycle.  
I'd test this output, but the only test equipment I own is an ohmmeter, an LED and a damp finger (none of which help here).

Your original code (using while(1)) is slightly tighter, and should produce a 2.6666Mhz output at 33.3333% duty cycle (2 ticks ON, 4 ticks OFF).

I think that the best that is possible (without resorting to filling the chip with instructions) is the following code:

```cpp
    while(1){
        PIND = 0b00001000 ;
    }

```

which results in a 2.6666Mhz output at 50% duty cycle (3 ticks ON, 3 ticks OFF)  
The main caveat with this approach is it will only work on M88s and above.

If you know the state of the other pins on the port, then you could do something like:

```cpp
    while(1){
        PORTD = 0b00001000;
        PORTD = 0b00000000;
    }

```

for a 4Mhz 25% duty cycle signal (1 tick ON, 3 ticks OFF)  
or

```cpp
    while(1){
        PORTD = 0b00001000;
        asm("nop");
        asm("nop");
        PORTD = 0b00000000;
    }

```

for a 2.6666Mhz 50% duty cycle (3 ticks ON, 3 ticks OFF) (e.g. similar to the earlier PIND example, except that in this case the state of the other pins must be known at compile time)

It would probably be useful to see the resulting assembly listing of sketches - does anyone know a simple way of getting a .lss file or similar from the arduino IDE?

Was there a real-world need for this square wave, or was it just a 'how fast can it go'?

---

<div class="post-metadata">

**Author:** ![westfw](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/westfw/32/213443_2.png) [@westfw](https://forum.arduino.cc/u/westfw)\
**Post date:** [June 15, 2009, 1:12am UTC](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378/20 "2009-06-15T01:12:21Z")

</div>

(westfw is finding the idea of using a write to PINx to implement toggle to be rather ... distasteful.)  
☹

[Next page](https://forum.arduino.cc/t/maximum-pin-toggle-speed/4378.md?page=2)
