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.

Share

Trackbacks / Pingbacks

  1. Dr. Rainer Hessmer » Blog Archive » Quadrature Encoder too Fast for Arduino
  2. Driving Stepper Motors with Arduino. « Giant Pong – Making of.
  3. New Project Added: FastDigitalWrite for Arduino 1.0 | Mecharobotics's Blog
  4. YM2149 sound generator, Arduino and fast pin switching « electronic fields
  5. Playing chiptunes with a YM2149 and optimizing an Arduino - Hack a Day
  6. Génération de son avec le YM2149 (AY-3-8910)

Warning: count(): Parameter must be an array or an object that implements Countable in /homepages/46/d285670699/htdocs/bill/wp-includes/comment.php on line 879
  1. 42 Comments.

    • BillNo Gravatar says:

      @Alan, I’m a Electrical Engineer by degree and programmer by hobby, so I used what I knew best!

      I was under the impression the 2 cycles used to set a pin; one cycle was to read and apply the bitwise operation to the register, the second was to write back to the register. Is this true? How can it go any faster then that?

    • ericwertzNo Gravatar says:

      “Technically” one shouldn’t be doing the following:

      digitalWrite(pin, !digitalRead(pin))

      as this only works when either HIGH or LOW is defined to be 0, and the other defined to be 1, neither of which are specified in the interface for the digital I/O functions. This code, as written, is implementation-dependent.

      This really *ought* to be:
      digitalWrite(pin, (digitalRead(pin) == HIGH) ? LOW : HIGH);

      Is the Arduino team ever going to change the way that they’re defined in the future? Highly unlikely. Would this code be portable to other Arduino-like implementations? Probably? Am I a nit? Possibly. Am I annoyingly asking questions and then answering them like one of our previous Ministers of War, Bill Ashcroft? Yes, I am.

      Just felt this needed to be pointed out because it’s not strictly a kosher programming practice. FWIW.

      Thanks for pointing out the pin toggling trick with writing ones to PINx. This was a surprise to me and wondered why I hadn’t seen this before — until I realized that this isn’t specified on the older ATmega chips I’ve been working with most recently. Pure bit-twiddling awesomeness.

    • BillNo Gravatar says:

      @eric, but isn’t LOW and HIGH defined?
      From the Arduino source:
      #define HIGH 0x1
      #define LOW 0x0

      Or do you mean we shouldn’t assume that this will always be?

      I came up with that method with a few minutes of googling. I’m only a hacker level programmer, so i don’t adhere to many programming practices mostly because i don’t know what many of them are; but thanks for the more accepted way.

    • Joel says:

      Forgive me for being anal, however I would just like to point out that CLR(), SET() and PORTB |=, PORTB ^= techniques are essentially exactly the same by way of the pre-processor. When compiled they will both produce the same set of instructions given a particular compiler.

      Also, I can’t help but notice there may be a flaw with the sbi() macro for flipping bits. Its definition in the article uses the bitwise-or operator on a particular bit – which will actually *always* set a particular bit. In fact, the sbi() macro is exactly equivalent to the SET() macro from the previous section. This is also reflected in the data you have provided. You should be using the bitwise exclusive-or operation for bit-flipping within the sbi() macro, which should once more give you the exact same result as the PORTB ^= _BV method.

      I believe PINB is also a read-only register, and performing any operations on it would be futile.

    • Joel says:

      Forgive me, first paragraph should mention PORTB ~= and not PORTB ^=.

    • BillNo Gravatar says:

      Joel, the purpose of testing some similar methods was to demonstrate they all compile to the same number of instructions. The different macros come from different sources on the web, and i kept them the way i found them.

      There is NO flaw in sbi(PINB,0); it is in fact a neat trick to flip a pin in one less instruction then the usual method of reading the register first, and writing it back. Writing a bit to the PIN register causes the pin to change state, no matter which state it was in previously. That’s why the macro *always* sets a bit.

      As i said in my report: “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.”

      If you don’t believe it, give it a try. That’s what I did and it worked.

    • BillNo Gravatar says:

      From the Atmega datasheet, section 13.2.2:

      “13.2.2 Toggling the Pin
      Writing a logic one to PINxn toggles the value of PORTxn, independent on the value of DDRxn.
      Note that the SBI instruction can be used to toggle one single bit in a port.”

      Flawed & futile huh? :-p

    • MikeNo Gravatar says:

      It would have been interesting to see the disassembly of your test programs to check that the cycle counts you measured are about right.

    • […] and is correspondingly slow. Both Jean-Claude Wippler (Pin I/O performance) and Bill Porter (Ready, Set, Oscillate! The Fastest Way to Change Arduino Pins) discuss the overhead and demonstrate how it can be avoided by directly reading from Atmel ports […]

    • John WNo Gravatar says:

      How did you get the duty cycle to be equal, as shown in your oscilloscope trace? On my MEGA2560 board, using

      for(;;){
      digitalWriteFast2(14,LOW);
      digitalWriteFast2(14,HIGH);
      }
      the low state persists for about 300nS, and the high state for about 440 nS (and reverse if I flip the order of instructions). The extra time I’m assuming is the time to execute the FOR loop. Any suggestions on how you got around this? Thanks.

      • BillNo Gravatar says:

        You are right, the extra time is for the branch instruction that tells the core to skip back a couple of instructions. For this testing, I avoided it by putting ALOT of writes in the code, like:

        for(;;){
        digitalWriteFast2(14,LOW);
        digitalWriteFast2(14,HIGH);
        digitalWriteFast2(14,LOW);
        digitalWriteFast2(14,HIGH);
        digitalWriteFast2(14,LOW);
        digitalWriteFast2(14,HIGH);
        digitalWriteFast2(14,LOW);
        digitalWriteFast2(14,HIGH);
        digitalWriteFast2(14,LOW);
        digitalWriteFast2(14,HIGH);
        etc…
        }

        and looped that.

        I’m guessing that won’t be helpful for you though. What are you trying to do?

        • John WNo Gravatar says:

          Thanks, that answered my question. I was just misled by your statement in the introduction that your “code was inside a never ending FOR loop” versus what was shown in the oscilloscope trace. There’s a lot of good discussion on the arduino forums which has been helpful. I’m trying to get a good feel on characterizing the timing for the code execution.

          • BillNo Gravatar says:

            I still used a for loop, I just put more then one iteration of the pulse train in it. The digitalwritefast library is one of the more useful Arduino Libraries, I tend to use it a lot opposed to the regular Arduino functions. Saves me from remembering ports and registers.

    • […] thought I might need up to 4000 pulses for second, each one is 4 digitalWrites, which according to this are about 56 cycles each. Oh, time that by 4, as I have 4 motors.. that means that about 4 mhz out […]

    • […] was reading Bill Porter‘s article about how many cycles it takes to do a digitalWrite as fast as you can in […]

    • […] I remembered a post dealing with this issue on Bill Porter’s blog. Let’s look at these lovely low-level tricks! Bill provides two […]

    • […] remembered seeing a great blog post going over the speed at which the digitalWrite() function changes pin states. We’ve seen […]

    • […] code arduino qui reste assez simple contient quand même un trick assez intéressant, inspiré du blog de bill porter, pour la synchronisation de certaines […]

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.