Senthil Arivudainambi

Programming Arduino with Assembly

23 Oct 2016

Arduino is typically programmed with Arduino IDE with a set of C/C++ functions. The canonical blink sketch which turns a led on/off every 1 second looks like:

void setup() {
  pinMode(13, OUTPUT);      // initialize digital pin 13 as an output
}

void loop() {
  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);              // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);              // wait for a second
}

pinMode, digitalWrite and delay functions are defined by the Arduino standard library. For the most part this works pretty well, and there are plenty of libraries and example sketches.

For the sake of getting close to the metal, let’s write the above sketch in assembly instead. Before we do that we need to install couple depedencies. Assuming you’re on OSX and you have brew install you’ll need to do:

brew tap osx-cross/avr
brew install avr-libc avrdude

Here’s an example from here. Save it as simple_led_blink.s

.equ RAMEND, 0x8ff
.equ SREG, 0x3f
.equ SPL, 0x3d
.equ SPH, 0x3e
.equ PORTB, 0x05
.equ DDRB, 0x04
.equ PINB, 0x03

.org 0
rjmp main

main:
  ldi r16,0     ; reset system status
  out SREG,r16  ; init stack pointer
  ldi r16,lo8(RAMEND)
  out SPL,r16
  ldi r16,hi8(RAMEND)
  out SPH,r16

  ldi r16,0x20  ; set port bits to output mode
  out DDRB,r16

  clr r17

mainloop:
  eor r17,r16   ; invert output bit
  out PORTB,r17 ; write to port
  call wait     ; wait some time
  rjmp mainloop ; loop forever

wait:
  push r16
  push r17
  push r18

  ldi r16,0x40 ; loop 0x400000 times
  ldi r17,0x00 ; ~12 million cycles
  ldi r18,0x00 ; ~0.7s at 16Mhz

_w0:
  dec r18
  brne _w0
  dec r17
  brne _w0
  dec r16
  brne _w0

  pop r18
  pop r17
  pop r16
  ret

Explaining what the above assembly code does is out of scope of this small blog post.

Now connect your Arduino to your computer with usb and run the following commands from your terminal:

avr-as -g -mmcu=atmega328p -o simple_led_blink.o simple_led_blink.s
avr-ld -o simple_led_blink.elf simple_led_blink.o
avr-objcopy -O ihex -R .eeprom simple_led_blink.elf simple_led_blink.hex
avrdude  -p atmega328p -c arduino -P /dev/tty.usbmodem14221 -b 115200 -D -U flash:w:simple_led_blink.hex:i

You’ll most likely need to change /dev/tty.usbmodem14221 to whatever usb port your Arduino is connected to. To find which one run ls /deve/tty.* and pick the one without Bluetooth in the file name.

Most of the above instructions are from here, frankly without it this process would’ve been much longer and way more tedious.