Skip to content

Instantly share code, notes, and snippets.

@mhitza
Last active April 6, 2024 17:20
Show Gist options
  • Save mhitza/8a4608f4dfdec20d3879 to your computer and use it in GitHub Desktop.
Save mhitza/8a4608f4dfdec20d3879 to your computer and use it in GitHub Desktop.
Programming Arduino Uno (ATmega386P) in assembly

There are a few different sources online that describe the ways that you can program Arduino Uno/ATmega328P in assembly, and in the last couple of days I've had to switch through most of them just to get the basic setup and initial programs working. This is an attempt to aggregate all those sources in a centralized document, with two simple example programs.

Requirements

There are two major options for the task (of which I'm aware of), and the setup I've settled on is accidental in retrospect1. But you might research on your own the alternative setup based around the AVR Libc project.

For the setup I use you need: avrdude and avra. Do note that I'm using a Linux box, and in case you're on another operating system you'll be on your own with the setup/configuration of these tools.

Optional (but must read)

When connected via USB my Uno would have it's serial interface mapped to /dev/ttyACM0. Because I wanted a more intuitive device name (and the fact that it took me some time to find out what device name it had in the first name), I wrote an udev rule to map my Uno board to /dev/arduino-uno.

$ cat /etc/udev/rules.d/10-arduino.rules
KERNEL=="ttyACM0", SYMLINK+="arduino-uno", OWNER="mhitza"

If you do the symlink, be sure to also include the OWNER directive; otherwise every time you upload the new program to your Uno you will have to call avrdude with sudo.

NOTE If you skipped this step, be sure to replace any occurence of /dev/arduino-uno with /dev/ttyACM0, and prefix all avrdude calls with sudo in the followup Makefile.

Also you'll see a reference to picocom (program used for serial communication with the board) in the Makefile I use, and it should work but I haven't tested it yet.

A simple Makefile

NOTE There is already a popular Makefile project for Arduino, however that one relies on the AVR Libc project. And as with other "prepackaged" solutions, like oh-my-zsh, I prefer to start from a small base. I find it easier to understand and maintain.

The makefile is available towards the end of the page.

Given a program named blink.asm

  • make (or make blink.hex) - compile it to a hex file
  • make program=blink upload - upload the program to your Arduino board. If you didn't run the previous step manually it will also compile the program for you
  • make monitor - monitor serial data

Where do the names come from?

When reading other example assembly code online you will find references to named constants that are not built into the assembler. Those usually come from m328Pdef.inc. From where you download that file doesn't really matter, since basically everyone is carrying a copy around with them; or so it seems.2

I personally use it as a learning reference, and I would recommend you copy only the definitions you need in your program until you get familiar with the names. That's the way I approach it, as you'll see in my example programs.

The project for the sample code

Arduino Uno LED project on a breadboard Project rendered on 123d.circuits.io

Three LEDs and a simple switch. With each press on the switch the LEDs turn on (from left to right), one by one, and once the red LED is reached further presses will turn off the LEDs in reverse order until we reach the initial state. Rinse and repeat.

Two implementations will be shown, the first one is the way most people - I think - would write the implementation (in terms on reacting to button presses, not necessary how they'd toggle the LEDs) (assuming they don't know about interrupts yet), and the second version will use an interrupt instead of "polling" the pin for voltage (state) changes.

References

NOTE the assembler manual is for AVRASM32.exe, not avra. However avra is a compatible assembler, with just a few extra features

NOTE if you see online references to avrasm2, you have to know that piece of software is different from AVRASM32.exe and avra. And avra is highly unlikely to be able to compile code written for that assembler.

You need to keep these references close by when programming AVR in assembly:


  1. Initially I started with avr-as, but the helpfull stackoverflow answers I've found pointed to avra and as soon as I was able to write a simple program I didn't look back. jump to reference
  2. Maybe it could matter where you download it from. Some dastardly individual might provide that file in an altered format where the mappings wouldn't be correct and you'd write to the wrong memory bits and spend hours/days debugging the assembly code. jump to reference

While mentioned in the previous document that you should download the assembly instruction set manual, for the following examples I'd like to detail the instructions I will use (so that you can follow along the code examples).

  • jmp label or rjmp label - jump to a label
  • sbi memory_location, bit_position - set bit immediate1
  • cbi memory_location, bit_position - clear bit immediate
  • clr register - clear register
  • sbis memory_location, bit_position - skip branch immediate (bit) set2
  • sbic memory_location, bit_position - skip branch immediate (bit) clear
  • tst register - test register for zero or negative number, if true, the Z flag is set3
  • brne label - branch not equal
  • ldi register, byte_value - load data immediate
  • reti - return from interrupt
  • out memory_location, register - write a register to a memory location
  • lds register, data_space - load from data space (register, I/O memory, SRAM) into register
  • ori register, byte_value - bitwise OR immediate
  • sts register, data_space - store to data space from register
  • sei - enable interrupts
  • in register, memory_location - read memory location into register

  1. Immediate here means that a value (bit_position/byte_value) can be specified directly for the operation. jump to reference
  2. With skip branch instructions, when the comparison turns out to be true the followup instruction is skipped. Most of the time that followup instruction would be a jmp. jump to reference
  3. There are a couple of instructions that work on implicit values inside the SREG registry. For example the Z flag is bit 1 in that register. tst is one of the many instructions that sets that value, while the brne/breq instructions are two example instructions that make use of the Z bit value. jump to reference
%.hex: %.asm
avra -fI $<
rm *.eep.hex *.obj *.cof
all: $(patsubst %.asm,%.hex,$(wildcard *.asm))
upload: ${program}.hex
avrdude -c arduino -p m328p -P /dev/arduino-uno -b 115200 -U flash:w:$<
monitor:
picocom --send-cmd "ascii_xfr -s -v -l10" --nolock /dev/arduino-uno
.PHONY: all upload monitor

You are encouraged to implement the simple Arduino project presented, download the files (there's a download button at the top) and build the project. If you're unfamiliar with the resistor values from the attached image, here's the handy cheat sheet I'm using.

Resistor cheat sheet

Taken from the "Arduino Projects Book", licensed under CreativeCommons BY-NC-SA 3.0


Except where otherwise noted, this work is licensed under a Creative Commons Attribution 4.0 International License.

@lapingenieur
Copy link

Hi! I added links to references in the different files (like the [1] goes to the reference and jump to reference goes back to the text) with a bit of html in my fork : it would be useful if you merged these changes in your own gist [1].

@mhitza
Copy link
Author

mhitza commented Apr 19, 2021

@lapingenieur I took in your changes in spirit, but "markdownified it" back a bit, dropped square brackets on the backreferences, and switched to superscripts for references. If you haven't tried it already, I highly recommend Vimwiki or any other markdown vim plugin, the first two changes just made it a better reading experience in vim 🔥
vimwiki

Thanks, and I hope you got some use out of this document.

@lapingenieur
Copy link

I thought we couldn't write markdown on lines containing html, it always broke what I did. Is there something to do or am I just stupid 😂 ?

Yes, this gist is so useful, thank you really much !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment