/digitalIOPerformance

An Arduino library for high performance digital I/O with no code changes necessary. Inspired by digitalWriteFast.

Primary LanguageC++

IMPORTANT

With the release of Arduino 1.6, this library is obsolete and mostly a historical curiosity for now. It doesn't support the latest boards or the new core/library format, etc.

If you're interested in taking over maintainence and updating it, let me know.

digitalIOPerformance.h

digitalIOPerformance.h is a single-file Arduino library (header file) that adds higher-performance digital I/O functions to Arduino programs.

Quick Start

  • Copy the "digitalIOPerformance" directory to your Arduino libraries folder.

  • Add #include "digitalIOPerformance.h" near the top of your sketch.

  • Done! When you recompile, performance of digitalRead/digitalWrite & pinMode should be substantially faster in most cases. However, functionality should be otherwise identical to the original Arduino functions.

  • Your sketch's compiled size may also go down (depending on how much digital I/O you do.)

What Becomes Faster?

Any digital I/O when the pin number is known at compile time:

const int led_pin = 13;
digitalWrite(led_pin, HIGH);  // <-- gets ~30x faster

#define RELAY_PIN 11
digitalWrite(RELAY_PIN, x>2); // <-- also ~30x faster

Not the case where the pin number isn't known at compile time:

int my_pin = 4; // <-- note this is a variable, no 'const' marker!

void loop() {
   digitalWrite(my_pin, LOW); // <-- same speed as normal Arduino
}

Option: More performance, smaller compiled size, on PWM Enabled pins

The Arduino's digital I/O functions deal with the possibility that a pin is used for PWM output with analogWrite(), and then the same pin gets used again later for normal digital output with digitalWrite(). Something like this:

analogWrite(MY_PIN, 120);
// ... do some stuff
digitalWrite(MY_PIN, LOW);

If you never mix analogWrite and digitalRead/Write on the same pin, you can boost performance on PWM-enabled pins (making them equal with non-PWM-enabled pins) by adding a second line above the first:

#define DIGITALIO_NO_MIX_ANALOGWRITE
#include "digitalIOPerformance.h

Digital read & write performance will be higher, and the compiled sketch size will be smaller.

... if you still want to mix analogWrite() and digitalRead/digitalWrite() on the same pin, but also use DIGITALIO_NO_MIX_ANALOGWRITE, then you can use the noAnalogWrite() function in between to turn off the PWM output:

analogWrite(MY_PIN, 120);
// ... do some stuff
noAnalogWrite(MY_PIN);
digitalWrite(MY_PIN, LOW);

Option: Last shreds of performance

Under some circumstances the Arduino digital functions have to protect against interrupts occuring while an I/O read or write is in progress. This can cause things to get out of sync if an I/O operation takes more than a single instruction to process ("interrupt unsafe".)

When using digitalIOPerformance, most writes only take one instruction anyway, so they are naturally "interrupt safe". However some cases have to be made "interrupt safe":

  • Writing to certain pins on Arduino Mega models.

  • Setting pinMode() to INPUT_PULLUP.

By default, "digitalIOPerformance" makes these cases interrupt safe, in order to keep them as safe as the built-in Arduino versions. However if your Arduino sketch doesn't use interrupts, or you don't care about interrupt safety for digital I/O, then you can add another line to get faster digital I/O on them:

#define DIGITALIO_NO_INTERRUPT_SAFETY
#include "digitalIOPerformance.h

Only do this one if you're very sure you don't need interrupt safe digital reads & writes. If you're already using DIGITALIO_NO_MIX_ANALOGWRITE then the only real improvement is on an Arduino Mega, and even then only on some of the pins.

Option: Disable automatic performance boost

If you don't want the library to automatically replace your digitalRead/digitalWrite/pinMode function calls, you can do that as well:

#define DIGITALIO_MANUAL
#include "digitalIOPerformance.h

You can still use the functions in the library if you call them by their original names (given below.)

Libraries

"digitalIOPerformance.h" has to be included into each separate compilation unit (ie each .ino and .cpp file) which is compiled by the gcc compiler invoked by Arduino.

This means that if you want it to apply to libraries you #include from your Sketch, then you'll have to add it to any of the .cpp files that are part of that library. Because those files are compiled independently from the main sketch.

(This isn't true for .h files in the library, that might get included directly into the sketch.)

Bear in mind the same limitations about "pin number must be known at compile time" apply.

Functions Defined

These functions are defined by the library:

digitalWriteSafe / digitalReadSafe / pinModeSafe

These versions of digitalWrite/digitalRead & pinMode run faster than the built-in Arduino versions, if the pin number is known at compile time.

They are also just as safe as the built-in Arduino versions - they're interrupt safe, and they disable any previous analogWrite() calls.

When you include the library, these functions automatically replace the built-in digitalWrite & pinMode functions. If you don't want this to happen, define DIGITALIO_MANUAL before including (as shown above.)

digitalWriteFast / digitalReadFast / pinModeFast

These versions of digitalWrite/digitalRead & pinMode will usually compile down to a single port register instruction (as fast as is possible to be) if the pin number is known at compile time. If the pin number is a variable then they fall through to the slower Arduino version if the pin number is a variable.

You can have these functions automatically replace all Arduino digitalWrite/digitalRead & pinMode functions if you include the library thus:

#define DIGITALIO_NO_INTERRUPT_SAFETY
#define DIGITALIO_NO_MIX_ANALOGWRITE
#include "digitalIOPerformance.h

noAnalogWrite

Using digitalWriteFast() will not automatically turn off a previous analogWrite() to that port, unlike Arduino's digitalWrite().

If you are mixing analogWrite() and digitalWriteFast() on a port, call this function after immediately before calling digitalWriteFast(), if you had previously called analogWrite().

The "safe" methods already call noAnalogWrite() any time you access a PWM-capable pin, unless you've defined DIGITALIO_NO_MIX_ANALOGWRITE.

Status

New, untested, hacky, work in progress. :)

Please raise an issue if this doesn't work with your Arduino install, or doesn't seem to inline properly (ie massively bloated code size instead of shrinking code size!)

Minimal testing done with Windows Arduino 1.0.3 (gcc 4.3) and my Ubuntu Arduino 1.0.3 (gcc 4.7.) Should work with any Arduino version above 1.0 (I think.)

Known Shortcomings

  • No ARM support, does nothing at all on the Arduino Due.

Internal Workings

digitalIOPerformance.h is code generated automatically from an existing Arduino installation by the script generateDigitalIOHeader.py.

You shouldn't need to run the code generation script unless you have a newer/different Arduino version than the one it was last run against.

However, having code generation means it should be simple to update against future new boards like the Leonardo (assuming the file formats don't change much.)

Thanks

Big thanks to the authors of digitalWriteFast - Paul Stoffregen, Bill Westfield, an John Raines. I wrote this instead of updating digitalWriteFast.h to support the Leonardo (code generation was more appealing than handwritten bit fiddles!)

Also thanks to Alastair D'Silva who told me a while ago about the trick of preprocessing pins_arduino.h to extract Arduino pin information, he uses this in the performance-oriented AVR library MHVLib.