- Programming in Assembly language
At some point you will need to be in control of some parts of the hardware inside the microcontroller, or shrink the size of the program, or make it more efficient, so you will need to go for Assembly. Each line in assembly translate to a single instruction. Assembly instructions are shorter and more intuitive to remember.
First warning: Assembly is not for everyone. It is only for the brave. In Assembly, you are directly controlling the hardware. This is why I like it the most, because that means you also learn about how the hardware you are controlling works.
C vs Assembly is like going somewhere by Bus vs driving a car. Bus is more comfortable but you cannot go where you want when you want. If you want to go where the Bus service is not reaching or at a different time you have to drive a car. But driving a car requires you to steer the wheel yourself, brake and accelerate yourself, check the rpm, the water levels, and many other things. But these are awesome things, don't they?
Finally, you could even get a job at NASA if you learn Assembly.
You need an assembler compiler to convert the .asm file to a .hex file that will be uploaded to the microcontroller. I use gavrasm, a command line tool available for windows and linux. Just download and unzip it. If you want the program to be available from everywhere in the terminal move it to /usr/local/bin:
$ sudo mv gavrasm /usr/local/bin
8 bit AVR Instruction set AVR Assembler user guide
Programming in assembly requires a big knowledge of the hardware since the instructions control this hardware directly. All the information about the microcontroller can be found in the datasheet. Some important microcontroller datasheets for Fab Academy are:
Here is a good resource shared by Sibu in order to understand ports and pins in the AVR microcontroller family and how to control them with the 3 registers DDRx,PORTx and PINx.
ATMEL AVR Tutorial 2: How to access Input/Output Ports?
One of the biggest advantages of assembly over other languages is the ability to precisely control time. It is even possible to actually predict how much time a program will take to execute.
Since we know the frecuency at which the MCU is running we can determine the period. For instance, for a MCU running at f = 8 MHz (8 million cycles per second), each cycle will take T = 1/f, that is, the period T is T = 1/8000000 = 0.000000125 s = 0.125 us = 125 ns. The following are common frecuency/period in AVR microcontrollers.
| Frecuency | Period (us) | Period (ns) |
|---|---|---|
| 1 MHz | 1 | 1000 |
| 8 MHz | 0.125 | 125 |
| 16 MHz | 0.0625 | 62.5 |
| 20 MHz | 0.05 | 50 |
So at 20MHz an instruction that takes one cycle in assembly is going to take 50 ns, and this is going to be as exact as your clock source, which to recall it is:
| Clock source | Precision |
|---|---|
| Internal RC | 10% |
| Internal RC Calibrated | 1% |
| Resonator | 0.5 % |
| Crystal | 50 ppm |
Let's see how long int takes to execute this piece of assembly code:
LDI R16, 255
LDI R17, 100
ADD R17, R16
MOV R20, R17We have 3 different instructions here LDI, ADD and MOV. If we look at the above AVR Assembler user guide, from section 4-5 onwards we can see that these instructions take exactly 1 cycle to execute each one. So this piece of code takes 4 cycles in total.
Since we know the period of a cycle, it is just a matter of multiplying 4 cycles*T. So at 20 MHz this piece of code takes 200 ns.
The following is a video from Basic and Default Usage of a Timer and Counter and The Microcontroller Clock explaining the basics about timers and counters.
<iframe width="853" height="480" src="https://www.youtube-nocookie.com/embed/Tj6xGtwOlB4?rel=0&controls=0&showinfo=0" frameborder="0" allowfullscreen></iframe>mov dest,origcopies the value oforigregister todestregister
DEC regdecrease 1 the value of the registerADD reg1,reg2adds the value of reg1 to reg2
ANDOREOR
CPI value1,value2compares 2 values and results in 0 (no equal) or different from 0 (equal)
SBI reg,bitsets a bit in a registerCBI reg,bitclears a bit in a registerBRNE labeljumps to the specified label if the result ofcpiis 0LDI reg,valueloads the specified value in the registerCLI regclears the entire registerJMP labeljumps to a certain labelRJMP labeljumps to a label at a maximum distance of -+ 2k words
; buttonled.asm
;
; Fab Academy
; February 2016
; by Francisco Sanchez
; CC-BY-SA 4.0 License
;
; This program turns on the LED of a helloworld board when the button is pressed
.device attiny44 ; defines which device to assemble for
.org 0 ; sets the programs origin
SBI DDRA,7 ; sbi reg,bit Sets a bit of a register.
; Setting DDRA bit 7 makes pin PA7 a (digital) output
CBI DDRB,3 ; sets PB3 as input
SBI PORTB, 3 ; activates pull up resistor in PB3
loop: ; label for main loop
; labels must start with a letter and end with a colon
CPI PINA,0 ; compares the value of PINA (button) with 0
BRNE release ; the result was different from 0 so the button is
; released.Go to subroutine release
SBI PORTA,7 ; the result was 0 so the button is pressed. Turn
; pin PA7 to HIGH (5V)
JMP done ; avoids subroutine release
release:
CBI PORTA,7 ; Turns pin PA7 to LOW (0V)
done: ; land here after turn PA7 HIGH
RJMP loop ; relative jump to label called loopLIFO Last In First Out
PUSH reg,value copies a byte of data from to the first empty byte at the top of the Stack
POP reg removes a byte of data from the top of the stack to the specified register
A macro is a group of instructions that you code once and are able to use as many times as necessary. The main difference between a macro and a subroutine is that the macro is expanded at the place where it is used, meaning it uses program memory for each of the instances. A macro can take up to 10 parameters referred to as @0-@9 and given as a coma delimited list.
.MACRO DELAY ; This directive creates a macro called delay
.ENDMACRO ; Directive that ends the macro
DELAYIt is possible to write Assembly code within a C program by using the following syntax in C:
asm ("
// here your assembly code
")This is a sample of how an assembly program looks like.
; ledblink.asm
;
; Fab Academy
; February 2016
; by Francisco Sanchez
; CC-BY-SA 4.0 License
;
; This program blinks the LED of a helloworld board
.device attiny44 ; defines which device to assemble for
.org 0 ; sets the programs origin
sbi DDRA, 7
; sbi(reg,bit): Sets a bit of a register.
; DDRA is the data direction register A
; Setting DDRA bit 7 makes pin PA7 a (digital) output
; A digital output can be switched ON/OFF for 5V or 0V
loop:
; label for main loop
; labels must start with a letter and end with a colon
sbi PORTA,7
; Sets bit 7 of register PORTA
; Turns pin PA7 to 5V
; Here it would come delay code
cbi PORTA,7
; Clears bit 7 of register PORTA
; Turns pin PA7 to 0V
; Here it would come delay code
rjmp loop
; relative jump to label called loop