Difference between revisions of "Fast, arduino compatible digital pin functions"
From Just in Time
m (clean up download url) |
|||
Line 65: | Line 65: | ||
You see? Almost the same code. One difference that you can’t see is that now the digitalWrite function compiles into 1 assembler instruction, taking 2 clock cycles. | You see? Almost the same code. One difference that you can’t see is that now the digitalWrite function compiles into 1 assembler instruction, taking 2 clock cycles. | ||
− | The big difference | + | The big difference is that the digital pin functions are not the original Arduino ones. These are overloads. The other big difference is that ''myPin'' in the code above is no longer an integer which is read and interpreted at run-time. Instead, it is now a variable of some special type, where the pin number is part of the type. This means that no time is spent at run time to determine which hardware address to use and which bit to set in that hardware. |
The generated code is also smaller; 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. | The generated code is also smaller; 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. | ||
− | + | Also: if you specify a nonsensical pin number (like, say, 42) you will be punished with a compiler error instead of being silently ignored at run time, which is the Arduino treatment. | |
− | There are disadvantages, but let me just show you one more advantage: | + | There are disadvantages—or rather: consequences, but let me just show you one more advantage: the functions in the FastPins library allow setting or clearing several bits at the same time. The library will generate optimal code, which means that if a call simultaneously sets or resets bits in the same AVR port, the fastest code will be emitted to do that. For example: |
<source lang="cpp"> | <source lang="cpp"> |
Revision as of 07:44, 5 December 2014
[[Revision timestamp::20141205074417|]]
Fast or readable?
When reading, or setting pin values on AVRS, there are typically only two options: the readable way or the fast way. The readable way is offered by the Arduino platform and consists of digital pin functions like digitalRead, shiftOut, etc. The fast way is available both on Arduino and on ‘raw’ AVR and consists of bit-wise AND- and OR-operations. In code this looks like this:
<source lang="cpp"> // ************************************************** // The 'readable' way. Use digitalWrite() and friends
int myPin = 12;
void setup() {
pinMode( myPin, OUTPUT);
}
void loop() {
digitalWrite( myPin, HIGH); digitalWrite( myPin, LOW);
}
// ************************************************** // The 'fast' way. Use bitwise operators and defines.
- define MYPINPORT PORTB
- define MYPINMASK _BV(4)
void loop() {
MYPINPORT |= MYPINMASK; MYPINPORT &= ~MYPINMASK;
}
</source> Apart from the obvious differences, there is another, more subtle difference between the two approaches: when giving a name to a pin function, this is typically done by using a variable on Arduino, which is resolved at run-time, while the pins and ports for bitwise operations are normally declared using preprocessor macros, which get resolved at compile time.
It is partly the run-time resolving of pin numbers by Arduino’s digital pin functions that makes them so slow; simply setting or clearing a single output pin will set you back more than 50 clock cycles![1] As a developer, you have to choose between fast, but less readable bitwise operators in combination with macros, or readable but slow Arduino digital pin functions, or so it seems…
At the same time, both ‘raw’ AVR developers and Arduino developers use AVR-GCC, which is a full-flexed, modern C++ compiler. Modern C++ compilers allow techniques like template meta programming (TMP) which in turn allows the compiler to perform almost arbitrarily complex processing before generating the assembler instructions that end up in your AVR’s firmware. Shouldn’t it be possible to use the compiler to solve the readable/fast dilemma?
It should, and it is.
Fast and readable
Look at the following code and spot the differences with the digital pin functions as shown earlier
<source lang="cpp">
- include "FastPins.h"
using namespace FastPins; DigitalPin<12>::type myPin;
void setup() {
pinMode( myPin, OUTPUT);
}
void loop() {
digitalWrite( myPin, HIGH); digitalWrite( myPin, LOW);
} </source>
You see? Almost the same code. One difference that you can’t see is that now the digitalWrite function compiles into 1 assembler instruction, taking 2 clock cycles.
The big difference is that the digital pin functions are not the original Arduino ones. These are overloads. The other big difference is that myPin in the code above is no longer an integer which is read and interpreted at run-time. Instead, it is now a variable of some special type, where the pin number is part of the type. This means that no time is spent at run time to determine which hardware address to use and which bit to set in that hardware.
The generated code is also smaller; 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.
Also: if you specify a nonsensical pin number (like, say, 42) you will be punished with a compiler error instead of being silently ignored at run time, which is the Arduino treatment.
There are disadvantages—or rather: consequences, but let me just show you one more advantage: the functions in the FastPins library allow setting or clearing several bits at the same time. The library will generate optimal code, which means that if a call simultaneously sets or resets bits in the same AVR port, the fastest code will be emitted to do that. For example:
<source lang="cpp">
- include "FastPins.h"
using namespace FastPins; DigitalPin<12>::type led1; // this is in port B DigitalPin< 7>::type led2; // port D DigitalPin<11>::type led3; // port B again
void setup()
{
pinMode( led1 | led2 | led3, OUTPUT);
}
void loop() {
// this will combine the output to led1 and led3 // for optimum performance. digitalWrite( led1 | led2 | led3, HIGH);
}
</source>
Download
The Arduino-ified library is available here: FastPins.zip.