Introduction: AVR Assembler Tutorial 1
I have decided to write a series of tutorials on how to write assembly language programs for the Atmega328p which is the microcontroller used in the Arduino. If people remain interested I will continue to put out one a week or so until I run out of free time or else people stop reading them.
I am running Arch linux and I am working on an atmega328p-pu set up on a breadboard. You can do it the same way as me or you can simply plug an arduino into your computer and work on the microcontroller that way.
We will be writing programs for the 328p like the one that is in most arduino's but you should note that these same programs and techniques will also work for any of the Atmel microcontrollers and later on (if there is interest) we will work with some of the other ones as well. The details of the microcontroller can be found in the Atmel data sheets and the Instruction Set Manual. I am attaching them to this instructable.
Here is what you will need:
1. A breadboard
2. An Arduino, or just the microcontroller
3. A computer running Linux
4. The avra assembler using git: git clone https://github.com/Ro5bert/avra.git or if you are using ubuntu or a debian based system just type "sudo apt install avra" and you will get both the avr assembler and avrdude. HOWEVER, if you get the latest version using github then you will also get all of the necessary include files, in other words it already has the m328Pdef.inc and tn85def.inc files.
5. avrdude http://www.nongnu.org/avrdude/
The complete set of my AVR assembler tutorials can be found here: https://www.instructables.com/id/Command-Line-AVR-T...
Step 1: Construct a Testing Board
You can simply use your arduino and do everything in these tutorials on that if you like. However, since we are talking about coding in assembly language our philosophy is inherently to strip away all the periferals and interact directly with the microcontroller itself. So don't you think it would be more fun to do it that way?
For those of you who agree, you can pull the microcontroller out of your arduino and then start by constructing a "Breadboard Arduino" by following the instructions here: http://arduino.cc/en/Main/Standalone
In the picture I show my set up which consists of two standalone Atmega328p's on a large breadboard (I want to be able to keep the previous tutorial wired and loaded on one microcontroller while working on the next one). I have the power supply set up so that the very top rail is 9V and all of the others are 5V from the voltage regulator. I also use an FT232R breakout board to program the chips. I bought them and put bootloaders on them myself, but if you just pulled one out of an Arduino then it is fine already.
Note that if you are trying this with an ATtiny85 then you can just get the Sparkfun Tiny Programmer here: https://www.sparkfun.com/products/11801# and then simply plug it into the USB port on your computer. You will need to install a bootloader on the Attiny85 first and the easiest way is just to use the Arduino IDE. However, you will need to click on file, and preferences, and then add this New Boards URL: https://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json which will enable you to install the bootloader (if your ATtiny85 didn't already come with one.)
Step 2: Install the Assembler and Avrdude
You can now download and install the assembler and avrdude from the links given on the first step of this tutorial. It is likely that if you have already been working with Arduino's then you already have avrdude installed.
After you have avra installed you will notice that there is a subdirectory that comes with it called "sources" and inside that directory are a bunch of include files. These are all of the microcontrollers that you can program with avra. You will notice right away that there is no file for the 328p that we are using here. I have attached one. The file should be called m328Pdef.inc and you should put inside the includes directory or anywhere else you like. We will be including it in our assembly language programs. All this does is give each of the registers in the microcontroller names from the data sheet so that we don't have to use their hexidecimal names. The above include file contains "pragma directives" since it was designed for C and C++ programming. If you get tired of seeing the assembler spit out "ignoring pragma directive" complaints just go into the file and delete or comment out all the lines beginning with #pragma
Okay, now that you have your microcontroller ready, your assembler ready, and your programmer ready, we can write our first program.
Note: If you are using the ATtiny85 instead of the ATmega328P then you need a different include file called tn85def.inc. I will attach it also (note I had to call it tn85def.inc.txt so that Instructables would allow me to upload it.) HOWEVER, if you got the avra assembler from github then you already have both of these files with it. So I recommend getting it and compiling it yourself:
git clone https://github.com/Ro5bert/avra.git
Step 3: Hello World
The goal of this first tutorial is to build the standard first program one writes when learning any new language or exploring any new electronics platform. "Hello World!." In our case we simply want to write an assembly language program, assemble it, and upload it to our microcontroller. The program will cause an LED to turn on. Causing an LED to "blink" like they do for the normal Arduino hello world program is actually a much more complicated program in assembly language and so we won't do that just yet. We are going to write the simplest "bare bones" code with minimal unnecessary fluff.
First connect an LED from PB5 (see the pinout diagram) which is also called Digital Out 13 on an arduino, to a 220 ohm resistor, then to GND. I.e.
PB5 ----> LED ----> R(220 ohm) ----> GND
Now to write the program. Open up your favorite text editor and create a file called "hello.asm"
;hello.asm ; turns on an LED which is connected to PB5 (digital out 13) .include "./m328Pdef.inc" ldi r16,0b00100000 out DDRB,r16 out PortB,r16 Start: rjmp Start
The above is the code. We will go through it line-by-line in a minute, but first lets make sure we can get it working on your device.
After you have created the file, then in a terminal you assemble it as follows:
this will assemble your code and create a file called hello.hex which we can upload it as follows:
avrdude -p m328p -c stk500v1 -b 57600 -P /dev/ttyUSB0 -U flash:w:hello.hex
if you are using a breadboard arduino you will have to push the reset button on the breadboard arduino just before you execute the above command. Note that you may also have to add a sudo in front or execute it as root. Also note that on some arduino's (like the Arduino UNO) you will probably have to change the bitrate to -b 115200 and the port -P /dev/ttyACM0 (if you get an error from avrdude about an invalid device signature just add a -F to the command)
If everything has worked as it should you will now have an LED lit up..... "Hello World!"
If you are using the ATtiny85 then the avrdude command will be:
avrdude -p attiny85 -c usbtiny -U flash:w:hello.hex
Step 4: Hello.asm Line-by-line
To finish this introductory tutorial we will go through the hello.asm program line-by-line to see how it works.
;hello.asm ; turns on an LED which is connected to PB5 (digital out 13)
Everything after a semicolon is ignored by the assembler and hence these first two lines are simply "comments" explaining what the program does.
This line tells the assembler to include the m328Pdef.inc file which you downloaded. You may want to put this in a directory of similar include files and then change the above line to point to it there.
ldi r16, 0b00100000
ldi stands for "load immediate" and tells the assembler to take a working register, r16 in this case, and load a binary number into it, 0b00100000 in this case. The 0b in front says that our number is in binary. If we wanted we could have chosen another base, such as hexidecimal. In that case our number would have been 0x20 which is hexidecimal for 0b00100000. Or we could have used 32 which is base 10 decimal for the same number.
Exercise 1: Try changing the number in the line above to hexidecimal and then to decimal in your code and verify that it still works in each case.
Using binary is simplest though because of the way Ports and Registers work. We will discuss the ports and registers of the atmega328p in more detail in future tutorials but for now I'll just state that we are using r16 as our "working register" meaning that we are just going to use it as a variable that we store numbers in. A "register" is a set of 8 bits. Meaning 8 spots that can either be 0 or 1 (`off' or `on'). When we load the binary number 0b00100000 into the register using the above line we have simply stored that number in the register r16.
This line tells the compiler to copy the contents of the register r16 into the DDRB register. DDRB stands for "Data Direction Register B" and it sets up the "pins" on PortB. On the pinout map for the 328p you can see that there are 8 pins labeled PB0, PB1, ... , PB7. These pins represent the "bits" of "PortB" and when we load the binary number 00100000 into the DDRB register we are saying that we want PB0, PB1, PB2, PB3, PB4, PB6, and PB7 set as INPUT pins since they have 0's in them, and PB5 is set as an OUTPUT pin since we put a 1 in that spot.
Now that we have fixed the directions of the pins we can now set the voltages on them. The above line copies the same binary number from our storage register r16 to PortB. This sets all of the pins to 0 volts except pin PB5 to HIGH which is 5 volts.
Exercise 2: Take a digital multimeter, plug the black lead into ground (GND) and then test each of the pins PB0 through PB7 with the red lead. Are the voltages on each of the pins exactly those corresponding to putting 0b00100000 in PortB? If there are any that aren't, why do you think that is? (see the pin map)
Start: rjmp Start
Finally, the first line above is a "label" which labels a spot in the code. In this case labelling that spot as "Start". The second line says "relative jump to the label Start." The net result is that the computer is placed into an infinite loop that just keeps cycling back to Start. We need this because we cannot have the program just end, or fall off a cliff, the program has to just keep running in order for the light to stay lit.
Exercise 3: Remove the above two lines from your code so that the program falls off a cliff. What happens? You should see something that looks like the traditional "blink" program used by Arduino as their "hello world!". Why do you think it acts this way? (Think about what must happen when the program falls off a cliff...)
Step 5: Conclusion
If you have gotten this far then congratulations! You are now able write assembly code, assemble it, and load it onto your microcontroller.
In this tutorial you have learned how to use the following commands:
ldi hregister, numberloads a number (0-255) into a upper half register (16-31)
out ioregister, registercopies a number from a working register to an I/O register
rjmp labeljumps to the line of the program labeled by "label" (which cannot be further than 204 instructions away -- i.e. relative jump)
Now that these basics are out of the way, we can continue to write more interesting code and more interesting circuits and devices without having to discuss the mechanics of compiling and uploading.
I hope you have enjoyed this introductory tutorial. In the next tutorial we will add another circuit component (a button) and expand our code to include input ports and decisions.
Participated in the
Participated in the
1 Person Made This Project!
- PolashR made it!
8 years ago on Introduction
Thanks. Do you know if there is an include file for the at90USB162? I'd like to try this.
Reply 8 years ago on Introduction
There isn't one that I can find. However, if you have some spare time you should write one! Here is the way to do it. Use my m328Pdef.inc file as a template and change the name to correspond to your chip. Then go to the datasheet summary file for the at90usb162 here:
starting with section 4 on page 8 you will find the Register summary. Now you can go through the various sections in the include file and change the hex addresses for the various register names and bits.
Next you can go to the full data sheet here:
and go through each of the sections and map the names to the bit locations. As an examle of what I mean, say you are on the section of the include file called
; PRR - Power Reduction Register
then you would start on page 43 (section 8.6) in the full data sheet. You will notice that the at90USB162 has two power reduction registers PRR0 and PRR1 whereas the ATmega328p only has one called PRR. So you will have to change that section of the include file accordingly and have two sections instead of one. Essentially you just go through each register and equate the name they give to each bit with the number of the bit. So, for example, I would have a line under the PRR1 section that would read:
.equ PRUSART1 = 0 ; Power reduction USART1
Which comes straight from section 8.6.2 on page 44 of the data sheet. You get the idea? I know it is a bit tedious, but then you can use avra with your chip for these tutorials. Also you will know all the in's and out's of your chip by the time you are done.
13 days ago
This was a very nice and clearly written article.
Thanks for writing it up.
I'd really like to see the assembler code that it would take to implement a delay(ms) method.
10 months ago
thanks so much for making this guide, it was super helpful and easy to understand!
Question 1 year ago
Hello here, I'm still very new at this. I'll appreciate if any one could help me get started. I have the feeling this tutorial is awesome but I couldn't work it through as I was not able to assemble my hello.asm file in the terminal
Answer 1 year ago
How far did you get? Are you using a linux computer? did you get the avra assembler? did you get avrdude also?
Reply 1 year ago
I'm using windows but I have ubuntu installed as a sub system. I've equally installed avra and avrdude using this command: sudo apt install avra
Reply 1 year ago
I am not sure how it works when you install ubuntu as a subsystem to windows. Does the linux shell have the same access to all of input output ports and attached devices? I don't know. Probably that is the issue though. If I were you I would just get a Raspberry Pi and use that, or buy a cheap laptop and completely wipe windows and install linux on it. Then you will have a real linux box to use.
Reply 1 year ago
Thanks a lot for your reply. I'll first of all try to download virtual box and install ubuntu there. If this doesn't work, then I'll have to change my OS or buy a cheaper machine as you suggested. :(
Question 1 year ago
How many lessons have you published for people to learn? Assembly language was my thing 20 years ago I wouldn't mind getting back into it.
2 years ago on Introduction
Hey, you need linux pc, avr-dude & BALLS
to program in assembly language.
Question 3 years ago
Hello! First of all, thank you for this instructable. It's a good start off point and there is none like it anywhere else. I was able to follow along and can now program my ATTiny85. Of course, I used a different include file (I used tn85def.inc, which is specifically for ATTiny85). Everything works, except for the last part where I remove the last two lines. It doesn't blink like you say it would. It just stays on. Any thoughts on this? Thanks!
4 years ago
on exercise 3... I removed "Start... rjmp Start" but my led keeps lit steady, no blinking...
I use a bare Atmega328 @ 8MHZ
Reply 3 years ago
Same thing for me. I used an ATTiny85 @16MHz.
Question 4 years ago on Step 3
Really great tutorial :)
I had issues when trying to get the code to the processor. The command suggested here gives me an error message and I don't know how to fix it:
avrdude: ser_open(): can't open device "/dev/ttyUSB0": No such file or directory
6 years ago
Hi, thanks for this instructable!
I am having trouble compiling AVRA (in Arch Linux). It seems that there is something wrong with the Makefile, but I am not sure. The whole thing is confusing, as the INSTALL file says to install one way, whereas other websites say to install other ways....
Reply 4 years ago
git clone https://aur.archlinux.org/avra.git and execute this command makepkg -si in ths git cloned folder,,,then follow the instructions ...avra would be complied
A better solution is https://github.com/timofurrer/avra-atmega2560 , it supports Atmega2560 also.
By the way. This is the best AVR assembler tutorial I've ever seen.
Reply 6 years ago
hello, I was wondering if you ever managed to resolve this, I am also having problems compiling Avra, I got 1.2.3 installed fine but then found it used a different way of referencing what board you're using and the m328Pdef.inc file would not work on its own.
1.3.0 just has an error in the when I run ./Build because it seems a directory is not created properly
4 years ago
Thanks for supplying a good tutorial, nice initiative!
I made my first "hello world" using your tutorial with an Atmega328PB Xplained Mini board and Atmel Studio.
Question 5 years ago on Introduction
How do I get the avra assembler working on my ubuntu platform? I tried following the instructions but, something seems to be missing...