Ready, Set, Oscillate! The Fastest Way to Change Arduino Pins

Posted in Tutorials by Bill
18 Aug 2010

There are many ways to change an output pin. The way we know and love is the famous digitalWrite() function. (Spoiler: Want a faster digitalWrite? Download Here!)

But even the Arduino Reference claims that it is not the most efficient. The Arduino functions do a lot of error checking to make sure the pin is configured right and has to map Arduino numbering to actual IO ports.  All this cost processor cycles, and time.  But how much? This article is not to teach you how to useIO registers, you can read about it on the Arduino Port Manipulation page. This is to cover exactly how inefficient the Arduino functions are.

I ran some tests to find out. The test platform was a 16Mhz Arduino and a very nice oscilloscope watching one of its output pins. I ran two tests, one setting the pins to know values(on or off), and one that ‘flips’ the pin from its previous state.  My code was inside a never ending for loop, so the result would always be a square wave form that I could measure in frequency.

The estimated CPU cycles is calculated from ½ the waveform period measured divided by the period of 16Mhz, since it takes two write operations to complete a full period in a waveform.  There could be some differences in the machine instructions it takes to set a bit compared to clearing a bit, so this is somewhat rough.

The 3 methods I tested were

  • digitalWrite(pin, LOW);         digitalWrite(pin, HIGH);
  • CLR(PORTB, 0) ;     SET(PORTB, 0);
  • PORTB |= _BV(0);                   PORTB &= ~(_BV(0));

The macros used:

#define CLR(x,y) (x&=(~(1<<y)))

#define SET(x,y) (x|=(1<<y))

#define _BV(bit) (1 << (bit))

The results

As you can see, digitalWrite takes around 56 cycles to complete, while direct Port addressing takes 2 cycles. That’s a big difference in time for programs that have lot’s of IO operations!

Next I tested just flipping a pin. By this I mean I just changed the pin state without knowing what the initial state was without testing. The methods of testing

  • digitalWrite(pin, !digitalRead(pin))
  • PORTB ^= (_BV(0))
  • sbi(PINB,0)

#define sbi(port,bit) (port)|=(1<<(bit))

The results

Wow, the Arduino method takes a whopping 121 cycles to flip a pin! The sbi() using the PIN register is a neat trick for what usually is a read only register, and is the fastest at only 2 cycles.

So you see, the Arduino functions take much MUCH longer to complete pin operations then using direct port IO. But there is a reason why. Arduino does a lot of error checking, and has to look up pin number mappings to actual Atmega pins. Direct port access is not for the faint of heart, but it can be much faster for when you are ready to take off some of the Arduino training wheels.

Credit to Webbot for showing us the PIN register trick.

UPDATE: Well, it seems the attention of my article has made me aware of a neat-o library for Arduino that keeps the code simple, but runs just as fast as direct port manipulation. Arduino Forum post here. I just tested the digitalWriteFast2() function and it also seems to only require 2 cycles to complete.


Trackbacks / Pingbacks

  1. Arduino : Plus dan Minus | Christianto Tjahyadi
  2. Digital I/O touch detection code improvements | Keyglove
  3. WS2812 NeoPixels are not so finicky once you get to know them |
  4. Using AVRweasel With Atmel Studio© 6 | JoeKimbler.Com
  1. 41 Comments.

    • […] Fungsi digitalWrite() memerlukan 56 siklus clock untuk meng-eksekusinya. Beberapa rekan melaporkan 400 siklus clock! Ada cara mudah untuk […]

      • Brian ParkNo Gravatar says:

        It is refreshing to see someone getting to the root of modern processors!
        Is there a setup for the Arduino that uses only assembly coding and no “operatiing system” overhead? I have stayed away from Arduino because of the overhead, and the insistence of C coding only, and endless stacks of libraries I have no idea what’s in them. The coding I do typically runs at “hardware speeds”, so I can’t afford the complexity. The other thing I want to do is to debug code while it runs, which most operating systems and the JTAG protocols forbid. (Interrupts and hardware timers must be allowed to run while in “debug mode”.) I have done this on older processors (6808 and 65816) using “NoICE debugger” but am looking for faster machines that are “hardware simple” (Englishspeak for 5V and no QFN packages).

    • miasNo Gravatar says:

      Hello…i want to ask u how to write a coding for CLR() and PORT changing…

    • […] digitalWrite() and digitalRead() functions are ridiculously slow compared to direct port manipulation (~20 times […]

    • It’s appropriate time to make some plans for the
      future and it is time to be happy. I’ve read this post and if I could I want to suggest you few interesting things or tips.
      Perhaps you can write next articles referring to this article.
      I want to read even more things about it!

    • Hey! This is kind of off topic but I need some guidance from an established blog.
      Is it difficult to set up your own blog? I’m not very
      techincal but I can figure things out pretty quick.

      I’m thinking about setting up my own but I’m not sure where
      to begin. Do you have any points or suggestions? Cheers

    • […] sending the pulse. Heck, the AVR in an Arduino can toggle a bit from low to high and back again in 4 cycles if you are careful, so no […]

    • […] are libraries available for Arduino that optimize the “digitalRead” and “digitalWrite” […]

Leave a Reply

Your email address will not be published. Required fields are marked *