2015-04-23

Saying Goodbye to the Arduino IDE

Last time, we got rid of the Arduino libraries and started talking to the hardware directly. However, we were still including some Arduino code in our program, and we relied on the Arduino IDE for our build process. All of this is changing today, so prepare to say goodby to Arduino!

First, we need to add a few lines of code to our 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;
		sleep_ms(200);
		*pb_clear_output_data = pb27_mask;
		sleep_ms(800);
	}
}


// Those are interrupt handlers. They are set up by remaining Arduino code
// during initialization. All of these just halt execution.
void        NMI_Handler() { while (1) {} }
void  HardFault_Handler() { while (1) {} }
void  MemManage_Handler() { while (1) {} }
void   BusFault_Handler() { while (1) {} }
void UsageFault_Handler() { while (1) {} }
void        SVC_Handler() { while (1) {} }
void   DebugMon_Handler() { while (1) {} }
void     PendSV_Handler() { while (1) {} }
void    SysTick_Handler() { while (1) {} }
void       SUPC_Handler() { while (1) {} }
void       RSTC_Handler() { while (1) {} }
void        RTC_Handler() { while (1) {} }
void        RTT_Handler() { while (1) {} }
void        WDT_Handler() { while (1) {} }
void        PMC_Handler() { while (1) {} }
void       EFC0_Handler() { while (1) {} }
void       EFC1_Handler() { while (1) {} }
void       UART_Handler() { while (1) {} }
void        SMC_Handler() { while (1) {} }
void       PIOA_Handler() { while (1) {} }
void       PIOB_Handler() { while (1) {} }
void       PIOC_Handler() { while (1) {} }
void       PIOD_Handler() { while (1) {} }
void     USART0_Handler() { while (1) {} }
void     USART1_Handler() { while (1) {} }
void     USART2_Handler() { while (1) {} }
void     USART3_Handler() { while (1) {} }
void      HSMCI_Handler() { while (1) {} }
void       TWI0_Handler() { while (1) {} }
void       TWI1_Handler() { while (1) {} }
void       SPI0_Handler() { while (1) {} }
void        SSC_Handler() { while (1) {} }
void        TC0_Handler() { while (1) {} }
void        TC1_Handler() { while (1) {} }
void        TC2_Handler() { while (1) {} }
void        TC3_Handler() { while (1) {} }
void        TC4_Handler() { while (1) {} }
void        TC5_Handler() { while (1) {} }
void        TC6_Handler() { while (1) {} }
void        TC7_Handler() { while (1) {} }
void        TC8_Handler() { while (1) {} }
void        PWM_Handler() { while (1) {} }
void        ADC_Handler() { while (1) {} }
void       DACC_Handler() { while (1) {} }
void       DMAC_Handler() { while (1) {} }
void       TRNG_Handler() { while (1) {} }
void       EMAC_Handler() { while (1) {} }
void       CAN0_Handler() { while (1) {} }
void       CAN1_Handler() { while (1) {} }
			

The first part hasn't changed, but I've added a lot of functions at the end. Those are handler functions for various interrupts. We'll talk about where all those function names come from in a moment (and in later articles we'll talk a lot about interrupts in general), but for now, you just have to understand this: Interrupts can occur due to various events and when they do, one of these handler functions can be called. All of the handlers do the same, which is basically nothing[1].

Implementing those handlers allows us to drop the rest of the Arduino libraries and simplify our build process significantly. The two compile steps have been merged into one:

arm-none-eabi-gcc \
	-nostdlib \
	-mcpu=cortex-m3 \
	-mthumb \
	-Tlinker-script.ld \
	-Wl,--entry=Reset_Handler \
	blink.c \
	libsam_sam3x8e_gcc_rel.a \
	-o blink.elf
			

Attentive readers will notice that there are still two files being included. The first is a linker script (linker-script.ld), which tells the linker everything it needs to know about the specific hardware of the Arduino Due[2]. I've added it to the GitHub repository.

The second is a library (libsam_sam3x8e_gcc_rel.a) that came with the Arduino IDE, but doesn't seem to be Arduino-specific. Rather, I think it belongs to the Atmel Software Framework[3]. In there, the last bits of initialization code that I have yet to replace are still hiding.

The rest of the build process is, again, unchanged:

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

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

Please don't forget that you have to press the ERASE button on the Due before attempting to upload the program.

So, another small but important step towards fully controlling what happens on the hardware. For the first time, someone can clone my Git repository, build the program and upload it to the board without having to install the Arduino IDE.

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