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.

## Notes

1. ↑ The Arduino has 3 ports B, C, and D which contain pins 8-13, A0-A7, and 0-7, respectively. More information may be found in the "See Also" section.

## See Also

* Instruction Set Manual.
* Pin Mapping of the ATmega328/168.
* AVR Playground.
* Port Manipulation.
* GCC Inline Assembly Tutorial
* Assembly Codings