Whether you want to learn what assembly is, you want to shrink your sketches or are up for a fun challenge, controlling an arduino with assembly easier than you might think. To prove that I will show you how to make a RGB led switch between its colors with a delay, written in assembly. If you don’t want to read the full tutorial, the finished code can be found at the bottom of this post.

What is assembly?

Well actually, there is no such thing as the assembly language. Assembly is any programming language that has a one to one relation with statements and machine instructions. This assembly is than taken to a assembler, a piece of software that turns this assembly into machine code. Each computer architecture needs it’s own assembler.

To make it easier for the developers there is the assembly language syntax. It consists of mnemonics and operands, the operations and its arguments. This is what we will be using to program our led.

How to use assembly with Arduino?

To program in assembly using the arduino IDE we only need to know about two keywords:

asm

volatile

asm is the instruction to let the compiler know that the parameter will be assembly code that can be passed to the assembler directly. The volatile keyword is a qualifier. It is used to let the compiler know that the next thing it encounters should be loaded from RAM instead of a register. Why? Because the compiler is smart. It removes all code that doesn’t seem to do anything. This could happen to our code too, because the compiler doesn’t understand the assembly code and so it seems like we are just repeating some code that has no effect.

Turning a normal sketch into an inline assembly program

We first need to create a normal sketch. I will assume that you know how to do this, else I suggest reading this book.

void setup() { pinMode(11, OUTPUT); pinMode(12, OUTPUT); pinMode(13, OUTPUT); } void loop() { // put your main code here, to run repeatedly: digitalWrite(11, LOW); digitalWrite(12, LOW); digitalWrite(13, HIGH); delay(1000); digitalWrite(11, LOW); digitalWrite(12, HIGH); digitalWrite(13, LOW); delay(1000); digitalWrite(11, HIGH); digitalWrite(12, LOW); digitalWrite(13, LOW); delay(1000); }

Connect pin 11, 12 and 13 to a RGB led. (or just 3 normal leds that will light up one after the other)

If you compile and run this program the led should turn red, wait a second, blue, wait a second, green, and rotate back to red. We can cut this program into sections to make the transition easier.

setup

In the setup we set a pinmode to output for the three pins we will be using. Normaly we use the pinmode function for that, but now we’ll have to do it manually. This is not difficult, if you know what you are doing. First we need some information. An arduino uno has an aTmega328P microprocessor. This is the core of the arduino and it comes with something called a datasheet. In this datasheet we can find all the information about the workings of this chip. On page one we find the pin configuration:

Now we need the pinmapping to know which pins we need to use. This can be found on the arduino website.

Now we know we need to use the pins PB3, PB4 and PB5. Controlling these pins is done via registers. These too, can be found in the datasheet. PB stands for port B, as discribed in 1.1.3 in the datasheet. Knowing this we head to the register summary on page 8. At the bottom of the able we find this rows:

What can we do with this information? We need to do another google search: “atmega328p DDRB”. That results in this site.

Finally, thats all data we need for now. We can set the direction of the ports using the DDRB register, and control it with the PORTB register.

To set or clear a bit we can use the assembly instructions SBI and CBI. Putting this in the arduino asm function looks like this:

asm volatile ("sbi 0x04, 5");

Turn pins on and off

To turn on and of the pins we again need SBI and CBI. Set bit to turn the pin on, clear bit to turn it of. The code is easy enough to understand:

asm volatile ("sbi 0x05, 0x05"); //set bit 5 of port 5 turn on pin 13 asm volatile ("cbi 0x05, 0x04"); //clear bit 4 of port 5 turn off pin 12 asm volatile ("cbi 0x05, 0x03");//clear bit 3 of port 5 turn off pin 11

The delay

Now we can turn on and off the pins, we only need a delay, and a way to call this delay after setting the pins. There is no such thing as a delay in assembly, so we will be using an alternative method. For this I will be using the registers r8, r9 and r16. These are free to use in your programs to. As an exercise I suggest you search for an explanation yourself. Post your questions and findings down below.

To create a delay we wil be counting down through some registers. This uses up cpu instructions that take time. The arduino uses a clock of 16 MHz, and so we need to use up 16 million cpu instructions. We will use a nested loop for this that decreases a register from 255 to 0 and repeats this over two other registers. For this we need 3 new assembly instructions: decrease (dec), branch if not equal (brne) and return (ret). The code looks like this:

asm volatile ("delay:"); //nested delay loop asm volatile ("dec r8"); //dec register 8 takes 1 tick asm volatile ("brne delay"); //branche if not equal takes 1 or 2 ticks asm volatile ("dec r9"); asm("brne delay"); asm volatile ("dec r16"); //use 16 cause 10 is in use by c asm volatile ("brne delay"); asm volatile ("ret"); //return back to the call

It looks complicated, so I will explain its behaviour via something more familiar, a nested while loop.

byte r8 = 255; byte r9 = 255; byte r16 = 255; while(r8 != 0) { while(r9 != 0) { while(r16 =! 0) { r16 = r16 - 1; } r9 = r9 - 1; } r8 = r8 -1; }

By looping through the registers and decreasing them by 1 we can calculate exactly how many cpu instructions are being used in the loops. In the example above we use to many, because we itterate 255 * 255 * 255 = 16.581.375. To decrease this, we just need to set r16 to a lower value. This again can be calculated using: frequenty / (255*255) = 246. This has to be divided by two to account for the 2 ticks every loop takes.

So if we want to call our delay we use this line of code:

asm volatile ("ldi r16,123

\t rcall delay"); //set r16 to 123 for a delay of 1 second

the ldi instruction sets the value 123 into register r16. than we use rcall to call a subsection of the code. These instructions are separated by a newline.

Final code

If we put all this code together we end up with this:

void setup() { asm volatile ("sbi 0x04, 5"); //set data direction register like pinmode(13, OUTPUT); } void loop() { asm volatile ("start:"); asm volatile ("sbi 0x05, 0x05"); //set bit 5 of port 5 turn on pin 13 asm volatile ("cbi 0x05, 0x04"); //clear bit 4 of port 5 turn off pin 12 asm volatile ("cbi 0x05, 0x03");//clear bit 3 of port 5 turn off pin 11 asm volatile ("ldi r16,123

\t rcall delay"); //set r16 to 40 for a delay of 1 second asm volatile ("cbi 0x05, 5"); //clear bit 5 of port 5 turn of pin 13 asm volatile ("sbi 0x05, 0x04");//set bit 4 of port 5 turn on pin 12 asm volatile ("cbi 0x05, 0x03");//clear bit 3 of port 5 turn off pin 11 asm volatile ("ldi r16,123

\t rcall delay"); asm volatile ("cbi 0x05, 5"); //clear bit 5 of port 5 turn of pin 13 asm volatile ("cbi 0x05, 0x04"); //clear bit 4 of port 5 turn off pin 12 asm volatile ("sbi 0x05, 0x03");//set bit 3 of port 5 turn on pin 11 asm volatile ("ldi r16,123

\t rcall delay"); asm volatile ("rjmp start"); //use rjmp instead of jmp is faster asm volatile ("delay:"); //nested delay loop asm volatile ("dec r8"); //dec register 8 takes 1 tick asm volatile ("brne delay"); //branche if not equal takes 1 or 2 ticks asm volatile ("dec r9"); asm("brne delay"); asm volatile ("dec r16"); //use 16 cause 10 is in use by c asm volatile ("brne delay"); asm volatile ("ret"); //return back to the call } //to calculate the preset of r16: frequency / (255*255 (loop tru r8 and r9)) = 246 but every loop takes 2 ticks so divide by 2 = 123

I hope you learned something new about assembly and how to use it. Go and test the code, improve it and show me your creations. If you have any questions please let me know down below!

Ruurd

The arduino peripherals I suggest

Please note, the following are associate links. Earnings go towards hosting and improving this site.

In my opinion this is the best arduino starterkit. It has all the basic sensors and actuators you need for learning and creating fun and challenging projects at a reasonable price.

The arduino used in this project For this project I used an atMega328P. You can buy one here. All the sensors you need in 1 bundle