Introduction
Arduino comes with built-in GPIO functions which serve the purpose well. But it becomes clear that these functions are bulky and slow, after we compare their performance with other more efficient implementations. The argument that is often presented in favor of these inefficient functions is that they are primarily meant to be used by the hobbyists, which often value ease-of-use more that efficiency.
Here, I am not presenting the fastest implementation, but rather an implementation that is efficient while maintaining the same ease-of-use of the original Arduino functions.
Background
A couple of years ago while I was in university, I did a research to make Arduino libraries more efficient. Although I did succeed, but it was never used outside the laboratory. But now I have cleaned up the code, to make its use easier.
Using the Code
Using this code is fairly easy. All you have to do is copy the FastIO.h and FastIO.c to the Arduino core directory ([Arduino Folder]\hardware\arduino\avr\cores\arduino) and include the header file in your sketch.
#include <FastIO.h>
How It Works
Every pin on Arduino is attached to a pin of AVR microcontroller. These pins are divided among different ports. These ports are memory mapped at different addresses inside AVR's memory. In order to manipulate these pins, we have to write to these specific memory regions.
So, how does Arduino access its pins just by using a pin number? Actually, Arduino has saved all the required information to its memory. So, when you call digitalWrite
on a specific pin, Arduino software searches its memory for the required information (the address of the port to which the pin belongs, and its place on the port). Arduino software also requires some additional information about whether the pin is also attached to a timer or not, if it is then, which timer and at which place.
All this overhead makes the Arduino's built-in function very slow. My implementation tries to ease off this overhead, by making all the required information readily available to the function. So that the function could start manipulating the pins as soon as it is called instead of searching for the information.
It works by storing the information in an array of 4 bytes.
- The first byte contains the location of the pin on a specific port.
- The second byte contains the address of the port to which the pin belongs.
- If the pin also has a timer attached to it, then the third byte contains the timer pins which controls the pin function.
- The fourth byte contains the address of the timer which is attached to the pin.
In case the pin doesn't have a timer attached to it, both the third and fourth byte will be equal to zero.
At function calls, a reference to this array is passed to the function, instead of simply passing the pin number.
Benchmarks
The built-in digitalWrite
function takes about 5us to change the pin state which is very slow for a microcontroller running at 16MHz (62.5ns period). My implementation makes this function 20 times faster by reducing the time to about 250ns. All this performance boost is achieved without making any changes to the sketch code, which is the main aim of this implementation.
As a bonus, the size of compiled sketch is also smaller when using my code (two kills in one shot).
As you can see here, near native speeds could be achieved without making any changes to the sketch.
Limitations
Currently, this code is incompatible with the Arduino mega based boards. This is because I have reserved only one byte for the port address in the pin definition array, but in Atmega2560, some ports are located at addresses greater than 0xFF. This limitation could easily be avoided by reserving 2 bytes for port address and timer address. These changes will be made in a subsequent version once my implementation has been thoroughly tested on Atmega8/168/328P based boards.