Actions

Fast, arduino compatible digital pin functions

From Just in Time

Revision as of 01:25, 2 December 2014 by Danny (talk | contribs) (saving WIP)
Work in Progress
This page describes a work in progress. All specifications may change and the final product may significantly deviate from what we describe here. It is very well possible that there will be no final product at all.
Warning.png

[[Revision timestamp::20141202012548|]]

Lotsapins.jpg

Suppose you'd write the following simple–and utterly useless–arduino-sketch: <source lang="cpp"> int myPin = 12;

void setup() {

 pinMode( myPin, OUTPUT);

}

void loop() {

 digitalWrite( myPin, HIGH);
 digitalWrite( myPin, LOW);

} </source> Those digitalWrite-calls will set you back more than 50 clock cycles apiece![1] In itself it's not so bad: the convenience that the Arduino environment offers is worth some performance penalty. The problem is: library writers now have to make a choice between using slow digitalWrite as above, or using less readable code like this:

<source lang="cpp">

  1. define MYPINPORT PORTB
  2. define MYPINMASK _BV(4)

void loop() {

   MYPINPORT |= MYPINMASK;
   MYPINPORT &= ~MYPINMASK;

} </source>

The code above is somewhat less readable. It becomes worse when there are several configurable pins that need their own #defines and unmanageable if some of those pins are on the same port and you'd want to make use of that fact.

Wouldn't it be nice if digitalWrite could be implemented in such a way that it would be as fast as handwritten bit manipulations? Toggling an output pin should take 2 clock cycles at most and 1 cycle on tiny AVRs that support such fast toggles.

The Arduino environment employs a modern C++ compiler that enables modern C++ techniques like template metaprogramming (TMP). If the pin we're writing to is known at compile time, TMP can be used to do most of the work at compile time, leaving a single assembler instruction to be performed at run time. All of this can be done with minimal intrusion: the only thing we need to do is to change the pin variables into pin types. The result looks like this:

<source lang="cpp">

  1. include "FastPinDefinitions.h"

using namespace FastPinDefinitions; DigitalPin<12>::type myPin;

void setup() {

 pinMode( myPin, OUTPUT);

}

void loop() {

 digitalWrite( myPin, HIGH);
 digitalWrite( myPin, LOW);

} </source>

You see? Almost the same code. The difference is that code above takes only 2 clock cycles for each digitalWrite. And there are more benefits to this:

  • In my Arduino environment, the binary size for the code above shrinks from 882 bytes to 472 bytes. If I throw in a shiftOut, code shrinks from 1042 bytes to 508 bytes.
  • If I use an invalid pin number in the original Arduino environment, my code will compile and silently fail. The FastPins version will throw a compiler error.


References