Following on from the last experiment with the digital I/O pins, which achieved an output frequency of 95 KHz from the Arduino (and a similar rate from the BeagleBone Black), I found this Youtube video about inline assembler, which is a feature provided by the Arduino software. Using the inline assembler enables the slow I/O libraries of the Arduino to be bypassed, which leads to higher speeds.
There are two instructions which enable an I/O pin to be toggled:
- SBI – set a bit on an I/O port (set it to one or a high voltage)
- CBI – clear a bit on an I/O port (set it to zero or a low voltage)
Both instructions have the same format. Two values are supplied, separated by a comma. The first value is the port that is being used, and the second value is which bit on that port is being used. For example:
- SBI 0x5,5 – set bit #5 of port 5.
The following table shows the combinations of ports and bits for each digital I/O pin on my Arduino Nano. I/O pins 0 and 1 are reserved for the serial I/O port.
In my previous example, I chose I/O pin 8, here port 0x05 and bit 0. So the Arduino code becomes this:
void setup() {
// initialize digital pin 8 as an output.
pinMode(8, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
//digitalWrite(8, HIGH); // turn the LED on (HIGH is the voltage level)
//digitalWrite(8, LOW); // turn the LED off by making the voltage LOW
asm(“sbi 0x05,0”);
asm(“cbi 0x05,0”);
}
I haven’t tried to recode the pinMode instruction, because it only runs once, and so there would be not benefit in doing so. The previous digitalWrite instructions have been commented out.
The LED board was reattached, and the Analog Discovery oscilloscope was used to record the waveform.
The ‘SBI’ instruction ran in approximately 140 nanoseconds, which corresponds to 2 clock ticks of the Arduino Nano’s processor (16MHz). However, the whole cycle took 1000 nanoseconds, corresponding to 16 clock ticks. This was the time it took to return from the ‘loop’ function and return, in addition to the two clock ticks to run the ‘CBI’ instruction.
A second experiment was done, but this time with an assembler loop within the ‘loop’ function. The ‘start:’ is a label which identifies a location in the code. ‘JMP’ means go to that location.
void setup() {
// initialize digital pin 8 as an output.
pinMode(8, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
//digitalWrite(8, HIGH); // turn the LED on (HIGH is the voltage level)
//digitalWrite(8, LOW); // turn the LED off by making the voltage LOW
asm(“start:”);
asm(“sbi 0x05,0”);
asm(“cbi 0x05,0”);
asm(“jmp start”);
}
The data is not being clocked at twice the rate, corresponding to a frequency of 2.3MHz, a period of approximately 430 nanoseconds, or seven clock ticks:
- SBI for two clock ticks
- CBI for two clock ticks
- JMP for three clock ticks
As an aside, this is 20 times faster than the BeagleBone Black was able to achieve, indicating how much faster it is to access the I/O directly, even with a processor running 60 times slower (16MHz clock versus 1000 MHz clock).
Here is the Atmel datasheet for the processor in the Arduino range, including the Arduino Nano. The port details are given in section 14.4. The instruction set is given in section 37.