Programming Arduino in Assembly

The Arduino is a microcontroller board based on the ATmega 328p (168 on older models). As such, it is programmable with standard Atmel assembly code. Further, the GCC compiler allows the use of inline assembly with other C-code, making programming the Arduino in assembly straightforward.

Example Code
The following code turns the LED attached to pin 13 "on": void setup { 	asm volatile( 	"SBI %0, %1 \n\t"   //pinMode(13, OUTPUT);  	:: "I" (_SFR_IO_ADDR(DDRB)), "I" (DDB5) 	); } void loop { 	asm volatile( 	"SBI %0, %1 \n\t"  //digitalWrite(13, HIGH);  	:: "I" (_SFR_IO_ADDR(PORTB)), "I" (PORTB5) 	); }

As with standard Arduino, void setup is executed once then void loop is executed repeatedly.

First, the function asm volatile starts with a string containing your assembly code.

In this case, it is "SBI %0, %1 \n\t". This is a normal line of assembly code that sets the bit in port %0, pin %1. "\n\t" in this case is the end-of-line character that notifies the GCC compiler that the current line of assembly has ended and that the next line has begun (similar to the action of the ";" in C).

Next there is a "I" (_SFR_IO_ADDR(PORTB)). . . In making your own programs, this portion will contain all the ports and pins that your program will use. "I" notifies the compiler to return a "port/pin" datatype for (_SFR_IO_ADDR) or (PORTB/C/D); your program will then use %0, %1. . . etc. to use the first/second. . .etc port/pin specified at the bottom.

For exampe, in the case of the lower asm code, %0 means the address given by the compiler to (_SFR_IO_ADDR(PORTB)) and %1 means the address given by the compiler to (PORTB5). If the program were to have more pins, more %numbers would be used.

Addressing Pins
Each pin on the Arduino technically has 3 registers:


 * DDRB/C/D - read/write this register to read/set direction (pinMode INPUT or OUTPUT)


 * PORT/B/C/D - read/write this register read/set the voltage (digitalWrite HIGH or LOW)


 * PIN/B/C/D - read only pin. This register's bits are changed externally and read for digital input (digitalRead)

It is important to note that pin 0 (RX) should NEVER be set to "OUTPUT" unless absolutely necessary. Unlike C, assembly does not have any safety settings to prevent you setting the RX pin to OUTPUT permanently and thus disabling serial communications. It is important to set it back to INPUT as soon as possible to prevent unwanted behavior.

Time Delays
There are no built-in time delay instructions on the AVR instruction set other than NOP, which halts the processor for one cycle (62.5 nanoseconds). Thus it is the smallest time delay possible. In order to make a time delay on the length of milliseconds or more, loops must be used.

For example, the following code is a subroutine that delays by (approximately) one second. "OneSecondDelay:            \n\t"    //subroutine declaration "LDI  r16, 80            \n\t" "0: CPI  r16, 0             \n\t" "BREQ 1f                 \n\t" //=================================================================   "LDI   r17, 200           \n\t" "2: CPI  r17, 0             \n\t" "BREQ 3f                 \n\t" //-   "LDI   r18, 200           \n\t" "4: CPI  r18, 0             \n\t" "BREQ 5f                 \n\t" "SUBI r18, 1             \n\t" "RJMP 4b                 \n\t" "5:                         \n\t" //-   "SUBI  r17, 1             \n\t" "RJMP 2b                 \n\t" "3:                         \n\t" //=================================================================   "SUBI  r16, 1             \n\t" "RJMP 0b                 \n\t" "1: RET                     \n\t" //1 second Delay

Instead of writing thousands of lines of NOP, this bit of code contains 3 loops nested inside each other to provide an exponential increase in delay time, to conserve space.

The two inner loops iterate 200 times each, which gives 40 000 iterations.

The outer loop then iterates 80 times, which gives a total number of 3 200 000 iterations.

This many iterations, multiplied by 5 operations per iteration, gives 16 000 000 processor cycles

With the 16MHz clock speed of the Arduino, it gives an approximate 1-second delay.