Getting Rid of the Arduino Libraries

In my last article, we took the build process of our program into our own hands, although we do still rely on the Arduino libraries, and therefore the Arduino IDE that builds them. But today marks another brave step towards independence from the tyranny of Arduino[1]: Those Arduino-specific function calls that made the LED blink? All gone. Check out the new version of the program:

// blink.c

// Addresses of several registers used to control parallel I/O.
volatile int *pb_pio_enable          = (int *)0x400E1000;
volatile int *pb_output_enable       = (int *)0x400E1010;
volatile int *pb_set_output_data     = (int *)0x400E1030;
volatile int *pb_clear_output_data   = (int *)0x400E1034;

// Bit mask for PB27. This is pin 13 (the built-in LED) on the Arduino Due.
int pb27_mask = 0x08000000;

// Addresses of several registers used to control the real-time timer.
volatile int *timer_mode_register  = (int *)0x400E1A30;
volatile int *timer_value_register = (int *)0x400E1A38;

// As the name suggests, this function sleeps for a given number of
// milliseconds. Our replacement for Arduino's delay function.
void sleep_ms(int milliseconds) {
	int sleep_until = *timer_value_register + milliseconds;
	while (*timer_value_register < sleep_until) {}

// The main function. A normal Arduino sketch would have setup and loop
// functions, which are normally called by Arduino's built-in main
// function. Our main here replaces all three of these.
int main() {
	// Enable PB27 (pin 13) and configure it for output.
	*pb_pio_enable    = pb27_mask;
	*pb_output_enable = pb27_mask;

	// Set the timer to a resolution of a millisecond.
	*timer_mode_register = 0x00000020;

	// Continuously set and clear output on PB27 (pin 13). This blinks
	// the Due's built-in LED, which is the single purpose of this
	// program.
	while (1) {
		*pb_set_output_data = pb27_mask;
		*pb_clear_output_data = pb27_mask;

Great, isn't it? All those highly readable function calls have been replaced by low-level fiddling with bits and pointers. Now, I understand that this looks like black magic to anyone who hasn't done this before. Don't worry! It's not really relevant yet, and when the time comes, I'll explain all of it in detail[2].

But let's not lose track of what's great about this program: No calls to Arduino functions! No Arduino headers are included! Consequently, the first step of our build process can be radically simplified:

arm-none-eabi-g++ \
	-c \
	-nostdlib \
	-mcpu=cortex-m3 \
	-mthumb \
	blink.c \
	-o blink.o

As you can see, we no longer rely on anything Arduino-specific for this first build step. However, even though the second build step has also been simplified a bit, it's not Arduino-free yet:

ARDUINO_IDE=<insert path to Arduino IDE>
ARDUINO_TMP=<insert path of the IDE's temporary build directory>

arm-none-eabi-gcc \
	-Wl,--gc-sections \
	-mcpu=cortex-m3 \
	-T$ARDUINO_IDE/hardware/arduino/sam/variants/arduino_due_x/linker_scripts/gcc/flash.ld \
	-mthumb \
	-Wl,--entry=Reset_Handler \
	-Wl,--start-group \
		blink.o \
		$ARDUINO_IDE/hardware/arduino/sam/variants/arduino_due_x/libsam_sam3x8e_gcc_rel.a \
		$ARDUINO_TMP/core.a \
	-Wl,--end-group \
	-lm \
	-o blink.elf

Even though we're no longer calling into the Arduino libraries, there is still Arduino code doing some setting up before our code can run. I'm not sure yet what that entails exactly, but I'm sure I'll figure it out[3].

The last build steps (binary conversion and upload) have stayed the same:

arm-none-eabi-objcopy \
	-O binary \
	blink.elf \

bossac --write --verify --boot -R blink.bin

That's it for today. As always, the full code is available on GitHub. See you next time!