Arduino primer/refresher - OS X perspective

I've been wanting to get back into some interesting embedded systems and electronics work - so I figured I'd dust off my old Arduino. In the process I ended up having to re-learn how to program it using C without using the Arduino IDE (it's not my favourite thing), which is what I'm going to go into here.

I downloaded CrossPack which contains GCC cross compiler for the AVR architecture, as well as a handful of other useful tools (like avrdude, which we'll use to flash the Arduino). Once that's setup we can start off with a pretty simple program which rapidly blinks the arduino's onboard LED, which is sort of the Hello World of the Arduino community:

view raw blink.c hosted with ❤ by GitHub

To build this using GCC we just need to call avr-gcc, making sure to specify the processor (using -mmcu), define the processor frequency (the F_CPU macro)

avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -o blink blink.c
view raw build-avr.sh hosted with ❤ by GitHub

This creates an ELF binary. One odd thing is that pretty much everyone building C code for Arduino seems to include the "-R .eeprom" switch which removes the ".eeprom" section from the output binary. However I've not ever seen this section does not exist in my ELF binary - see below:

$ avr-objdump -h blink
blink: file format elf32-avr
Sections:
Idx Name Size VMA LMA File off Algn
0 .data 00000000 00800100 000000b0 00000124 2**0
CONTENTS, ALLOC, LOAD, DATA
1 .text 000000b0 00000000 00000000 00000074 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .comment 00000011 00000000 00000000 00000124 2**0
CONTENTS, READONLY
3 .debug_aranges 00000020 00000000 00000000 00000138 2**3
CONTENTS, READONLY, DEBUGGING
4 .debug_info 000000be 00000000 00000000 00000158 2**0
CONTENTS, READONLY, DEBUGGING
5 .debug_abbrev 00000014 00000000 00000000 00000216 2**0
CONTENTS, READONLY, DEBUGGING
6 .debug_line 00000058 00000000 00000000 0000022a 2**0
CONTENTS, READONLY, DEBUGGING
view raw eeprom-wtf.sh hosted with ❤ by GitHub

Anyway, perhaps there's something else at play that I've not understood so I've kept this switch in (I'm making a mental note to explore this later). Again many other places seem to insist on using the avr-objcopy utility to produce intel hex file before uploading to the Arduino - so often you'll see the below:

avr-objcopy -O ihex -R .eeprom blink blink.hex

However again this is not a necessary step it seems, since the utility we use to upload the binary to the Arduino - avrdude - supports ELF so you can skip it. When we want to upload the binary to the Arduino we'll use avrdude, but first we need to know the programming device. For me this is just the USB type A to type B cable that comes with the arduino, which in the example below is /dev/tty.usbmodem1421:

avrdude -F -V -c ATMEGA328P -p arduino -P /dev/tty.usbmodem1421 -b 115200 -U flash:w:blink:e
# note: most guides will tell you to do the below, but it's not necessary
# avrdude -F -V -c ATMEGA328P -p arduino -P /dev/tty.usbmodem1421 -b 115200 -U flash:w:blink.hex

I've found it useful to create a little Makefile to tie these steps all together:

$ cat Makefile
CC = avr-gcc
PROCESSOR = atmega328p
FREQ = 16000000
AVRDUDE_CONFIG = arduino
AVRDUDE_PART = ATMEGA328P
AVRDUDE_PROGRAMMING_DEVICE = /dev/tty.usbmodem1411
all: build
clean:
rm *.o *.hex blink
compile:
$(CC) -Os -DF_CPU=$(FREQ)UL -mmcu=$(PROCESSOR) -c -o blink.o blink.c
build: compile
$(CC) -mmcu=$(PROCESSOR) blink.o -o blink
flash: build
avrdude -F -V -c $(AVRDUDE_CONFIG) -p $(AVRDUDE_PART) -P $(AVRDUDE_PROGRAMMING_DEVICE) -b 115200 -U flash:w:blink:e
$ make flash
avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o blink.o blink.c
avr-gcc -mmcu=atmega328p blink.o -o blink
avr-objcopy -O ihex -R .eeprom blink blink.hex
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/tty.usbmodem1411 -b 115200 -U flash:w:blink.hex
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e950f
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "blink.hex"
avrdude: input file blink.hex auto detected as Intel Hex
avrdude: writing flash (176 bytes):
Writing | ################################################## | 100% 0.04s
avrdude: 176 bytes of flash written
avrdude: safemode: Fuses OK (H:00, E:00, L:00)
avrdude done. Thank you.

Finally to tie this together in a slightly more existing example than blink.c, I've created a little program that will flash the LED attached to pin 13 with a message encoded in morse - "hello world" or ".... . .-.. .-.. --- / .-- --- .-. .-.. -..":


The code is on github at https://github.com/smcl/arduino_morse and only requires a bare Arduino and USB cable.