arduino-cli
: Command Line Interface for Arduino
- ARM cross compiler:
sudo apt install gcc-arm-none-eabi
Sample program
We want to create the most simple program which goal is to light on the built-in LED, located at port PB27 on the Arduino Due.
Arduino
1
2
3
4
5
6
7
8
|
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
}
void loop()
{
digitalWrite(LED_BUILTIN, HIGH);
}
|
Embedded C
1
2
3
4
5
6
7
|
int main()
{
PIOB->PIO_PER = 1<<27; /* Enable port PB27 */
PIOB->PIO_OER = 1<<27; /* Configure PB27 as output */
PIOB->PIO_ODSR = 0xFFFFFFFF; /* Write 1 in all PORTB ports */
return 0;
}
|
Codes and compilation
1
2
3
4
5
6
7
8
9
|
$ ls -lR
./led-arduino:
total 4
-rw-r--r-- 1 pascal pascal 104 mai 1 12:58 led-arduino.ino
./led-embedded:
total 4
-rw-r--r-- 1 pascal pascal 81 mai 1 13:00 led-embedded.ino
$ arduino-cli compile --fqbn arduino:sam:arduino_due_x --output-dir led-arduino led-arduino
$ arduino-cli compile --fqbn arduino:sam:arduino_due_x --output-dir led-embedded led-embedded
|
Binary comparison
|
Arduino |
Embedded C |
Storage use |
10660 bytes |
2544 bytes |
The embedded C code is 5 times smaller than the Arduino one which is a bit “weird” as both codes do the same thing! Let’s find why by reversing binaries and analyzing assembly codes.
arduino-cli
produces the *.elf
binary and the *.hex
file which is just a series of bytes to be loaded in the Arduino.
Reversing binaries
1
2
|
$ arm-none-eabi-objdump -S led-arduino/led-arduino.ino.elf > led-arduino.asm
$ arm-none-eabi-objdump -S led-embedded/led-embedded.ino.elf > led-embedded.asm
|
Arduino binary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
void init( void )
{
80194: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr}
SystemInit();
// Set Systick to 1ms interval, common to all SAM3 variants
if (SysTick_Config(SystemCoreClock / 1000))
80198: 4d3f ldr r5, [pc, #252] ; (80298 <init+0x104>)
SystemInit();
8019a: f000 f9f1 bl 80580 <SystemInit>
if (SysTick_Config(SystemCoreClock / 1000))
8019e: 682b ldr r3, [r5, #0]
801a0: f44f 727a mov.w r2, #1000 ; 0x3e8
801a4: fbb3 f3f2 udiv r3, r3, r2
*/
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
[...]
|
Here is a part of the Arduino objdump. The assembly code is really long for such a program… It is easy to understand when we look at the source code of pinMode()
and digitalWrite()
(https://github.com/arduino/ArduinoCore-sam/blob/master/cores/arduino/wiring_digital.c). It does not only write a value into a register…
Embedded C binary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
61 00080148 <main>:
62 int main()
63 {
64 PIOB->PIO_PER = 1<<27;
65 80148: 4b04 ldr r3, [pc, #16] ; (8015c <main+0x14>)
66 8014a: f04f 6200 mov.w r2, #134217728 ; 0x8000000
67 8014e: 601a str r2, [r3, #0]
68 PIOB->PIO_OER = 1<<27;
69 80150: 611a str r2, [r3, #16]
70 PIOB->PIO_ODSR = 0xFFFFFFFF;
71 80152: f04f 32ff mov.w r2, #4294967295 ; 0xffffffff
72 80156: 639a str r2, [r3, #56] ; 0x38
73 return 0;
74 }
|
At first sight, it’s clearly easier. Step-by-step instruction decoding using mainly [2] and [6]:
- mov.w r2, #134217728. Move the immediate value
134217728
in r2
, a general purpose register. 134217728
= 0x8000000
(hex) = 0b1000000000000000000000000000
(bin) which is 1 on the 27th bit ! 😉
- str r2, [r3, #0]. Store the value of
r2
in r3
, another general purpose register, with offset=0. According to the register mapping: PIO_PER
is at the offset 0x0000
.
- str r2, [r3, #16]. Store the value of
r2
in r3
with offset=16 (or 0x10
in hexadecimal). According to the register mapping: PIO_OER
is at the offset 0x0010
.
- mov.w r2, #4294967295. Move the immediate value
4294967295
in register 2. 4294967295
= 0xFFFFFFFF
(hex). In this case, we put all bits to 1. It doesn’t really matter as only the 27th has been enabled 😉
- str r2, [r3, #56]. Store the value of
r2
in r3
with offset=56 (or 0x38
in hexadecimal). According to the register mapping: PIO_ODSR
is at the offset 0x0038
.
Conclusion
Embedded C code is usually quicker and smaller as long as we make some effort to study the datasheet. But it’s worth it when we’re working with embedded systems! 😉
References
- Cortex-M3 Revision r2p0Technical Reference Manual
- SAM3X datasheet
- ARMv7-M ArchitectureReference Manual
- A simplified ISA document
- Cortex-M3 TRM
- ARM assembly basics