

























Estude fácil! Tem muito documento disponível na Docsity
Ganhe pontos ajudando outros esrudantes ou compre um plano Premium
Prepare-se para as provas
Estude fácil! Tem muito documento disponível na Docsity
Prepare-se para as provas com trabalhos de outros alunos como você, aqui na Docsity
Encontra documentos específicos para os exames da tua universidade
Prepare-se com as videoaulas e exercícios resolvidos criados a partir da grade da sua Universidade
Responda perguntas de provas passadas e avalie sua preparação.
Ganhe pontos para baixar
Ganhe pontos ajudando outros esrudantes ou compre um plano Premium
Texto em inglês sobre programação em Assembler de microcontroladores AVR
Tipologia: Manuais, Projetos, Pesquisas
1 / 33
Esta página não é visível na pré-visualização
Não perca as partes importantes!


























Path: Home => AVR overview => starter steps
There are lots of short or long introductions to AVR assembler. The following was written for absolute beginners who want to know why they should learn assembler and what the first steps of learning this are.
If you already know programming languages such as C, Pascal, diverse Basic dialects, Java or whatever: kick that all aside, it makes no sense in assembler and blocks your first steps in learning that. One basic rule in learning theory is: learning something that is re - ally new starts with destroying previous or old knowledge. And even if it hurts: without that you'll make no progress in learning, instead you'll always will stay at: "Why wasn't that made like I always used to do things?" and "That's too complicated for me, I'll never learn that." Previous or old knowledge stands in your way and blocks you from learning new things. So, let us first see what our standard view on computers is like. And then learn why mi - cro-controllers are a different world.
Why that? Now, assembler is very, very different from other languages and even though programs that run on a micro-controller still are bits and bytes, but the generation process is very different.
These are the four levels of a usual computer. We usually see only the red level outside of the onion: we start an office software (that produces a picture of an empty sheet of paper on the screen), we click on a key on the keyboard and the letter written on the key appears in the picture. We deal with the outer level, what happens below that is in-trans- parent and we need not to know what goes on there as long as the application software works as de- sired. Each of the above actions is asso- ciated with thousands of steps of the inner level, the Central Pro- cessing Unit or CPU. Our click onto the keyboard involves those thousands of steps, until the key- board driver has found out which key has been pressed and what letter is written on the key. The keyboard driver hands out the letter to the application software, that stores it and sends 1,000s of commands to the display screen driver to change the picture of the empty sheet, that now shows the letter of the key pressed. Fortunately the millions of CPU actions associated with that need by far less than a second so that we do not have to wait for long to see if it works.
So what changes if we do programming in C, Pascal, Basic or Java? We type in commands in that language in an editor, that stores a source code file on the disk. Then we call a so- called compiler, that reads the source code and translates it to a series of commands, packs those into an executable file, makes an application program from it and stores it on the disk. If we start that by clicking on it, the compiled commands are executed. More than 90% of the executable are calls to lower levels such as to the operating system and very seldom to drivers. Some manipulate memory spaces, via the memory adminis- tration driver. Nearly none of those directly address the CPU. How the source code com- piles is completely in-transparent for the programmer: he needs only to know if the pro- duced executable works as desired, not how the executable does this. The compiler de- cides over this and does not ask the programmer if he should do it in this or another way. Therefore usual programming in high-level languages does neither need to nor does it fac- tually increase our knowledge and understanding of how the computer finally works. We are still dump users even though we know how to program (and with that solve our appli- cation needs).
Now to the more specific properties of the AVR’s CPU. The largest difference to all other CPU types is that the AVRs have a lot of registers: 32. Registers are bit storage locations with a size of 8 bits that the AVR CPU can directly use. So the CPU can add any one of the 32 registers with any other one (even with itself) and can store the result in any one of these 32 registers. So the bit combination of the instruction word (16 bits) for adding looks like this: So, whenever the AVR CPU fetches 16 bits, of which the highest four are zeros and the next two following are ones, she knows what to do:
in the chapter Instruction Set Summary , if you really need to know the binary opcode search for the document Instruction Set Manual on ATMEL/Microchip's website. More specific to the AVRs is that the CPU can only fetch opcodes that are stored in the flash memory of the chip. No external or opcodes stored in SRAM or in EEPROM can be ex - ecuted. So if you want the CPU to execute instructions, write those to the flash memory.
Well, yes and no. Exactly is that the 16-bit opcodes are the language of the CPU. The mnemonics, such as ADD, are assembly language. A program also named assembler converts the mnemonics to opcodes: it "assembles" the bits 0000,11 and the two num- bers of the two registers in binary form and produces the binary output, to be pro- grammed to the flash memory for execution by the CPU. As the language assembler only consists of mnemonics for the CPU's binary opcodes, it is as near as possible to the CPU. So assembler is called a "hardware-near" language, in contrast to languages that are far away from the hardware, in their own world.
Other than the computer onion as shown above very different hardware is already inte - grated in a controller's CPU core:
As you can simply see, anything, and even more than that, can be done in assembler, too. More than that means: you can change the step width in between, you can manipulate the counter in between the loop (which is not allowed in several high-level languages), you can test if additional conditions are met (e. g. if a port-pin has changed polarity in bet- ween) that should stop the loop, you can even jump to any location outside the loop should a certain condition require that. Even if your loop is nested (another loop inside an outer loop) you can jump to where you want in assembler. All features are allowed in as - sembler, no limitations. But: increased freedom means increased responsibility. If your counter counts with zero steps up or down, the AVR will accept that and your loop will never end. It is up to you to find that bug and replace it with something that functions well. As you can see: better forget the FOR/NEXT construction and use whatever is the appro- priate method for your needs. 3 Simulating the hardware of micro-controllers As AVRs do not have hardware that can output a screen picture we can hardly see what happens if we program our first assembler routines. So we have to have another way to see what is going on.
As we saw a micro-con- troller (here some older types) is a little bit dif- ferent from a computer: it is a naked CPU with some additional hardware packed around it. Small storages, such as a flash memory with a few kilo-bytes or a static RAM with also a few bytes or kilo-bytes, and an even smaller EEPROM with only a few bytes. But: all that needs less than one per-mill of the current or power of a computer, and can be operated for hours, days and months from a small battery. The available hardware consists of
A simulator is an application program for a computer that behaves like a micro-controller. Other than the original, it allows to single-step through the assembler program: it exe- cutes an instruction, sets CPU flags, displays register content and displays SRAM or EEP - ROM content, and then stops. Before doing the next step we have all the time we need to read and understand what the instruction has done. The simulator avr_sim is available for free, can be downloaded as executable for 64-bit Linux and Windows versions or can be compiled using Lazarus from the Pascal source code that is also provided for many other operating systems. Each version that can be down- loaded (just use the latest one) comes with a handbook that describes all features of the simulator. So if you need other information than described here, consult the handbook. One special thing to learn here is that information on registers and other controller inter- nals is displayed in hexadecimal format. A hexadecimal digit takes four single bits and dis- plays those as 0 to 9 (0000 to 1001) or as A to F (1010 to 1111). So 16-bit numbers are represented by four hexadecimal digits. Use a programmer calculator to convert hexadeci- mal to decimal if you need it.
Start avr_sim, it should come up with an empty editor field to the right, with an empty project field to the left and a menu on the top.
The first lines that are not comments are the following: .nolist .include "tn13adef.inc" ; Define device ATtiny13A .list These are three directives, starting with a ".": those are commands for the assembler. The first switches the output in the list file off, the third one on again. The second directive in - cludes a file named "tn13adef.inc". This file holds addresses and constants for the device and switches a check on if the instructions are valid in that device type. But the file is not really read in avr_sim: all those files are implemented in avr_sim, so no extra file is nec - essary. After that two additional directives follow: .cseg instructs the assembler to produce code (code sgment), .org 000000 set the address for which the code will be assembled. As the device starts at address 0 when the operating voltage is applied, this ensures that our code is executed. In the Main program section, the first entry is a label: Main:. Such labels all end with a ":" and tell the assembler to associate the label's name with the current address (here: 000000). The label is followed by another label called Loop: and a line saying rjmp loop. This con- struction is an indefinite loop: the rjmp means "Relative Jump" and causes the controller to jump back to the label Loop. The rjmp is a jump instruction. We assemble this source code by clicking on the Assemble entry on the menu line.
Assembling opens a window, that displays the progress during assembling. Here, the gavrasm assembler is at work. You can ignore the displayed messages, but not the follow- ing window that reports success. The editor win- dow now dis- plays the listing that the assem- bler produced. It holds all rele- vant informa- tion. The most relevant here is the line that is in the red box: the rjmp loop has been trans- lated by the as- sembler to an instruction at address 000000. The instruction is hexadecimal CFFF. If we would pro- gram the hex file myfirst.hex to the flash memory of a de- vice, the con- troller forever jumps back to address 000000, and does nothing else. In case you need it: the symbol table at the end of the listing shows all relevant informa- tion on constants, labels and all other symbols. The labels Main and Loop, for example, point to address 000000. To add another instruction before the controller is send into the indefinite loop, we add the two lines before the loop label: mov R3,R1 ; Copy number 1 to register R add R3,R2 ; Add number 2 to register R
The registers now show that their con- tent has changed: R and R2 are now one, the adder result is 2. As each step sets the previous value the fact that all three registers are yellow was reached by setting a breakpoint on the rjmp (set the cursor to that line, right-click and select Break- point , the breakpoint is displayed as B in that line) and starting the simulation with the Run entry in the menu. So all changes to registers remain yellow.
If we exchange the line inc R1 by dec R1 we provoke some new effects. The DEC de- creases the content of register R1. The zero yields a 255 or hexadecimal FF. As the result will be larger than 255 we also add R4 for the higher byte. The source code now: dec R1 ; Number 1 to 255 inc R2 ; Number 2 to 1 clr R4 ; Result MSB to zero mov R3,R1 ; Copy number 1 to R add R3,R2 ; Add number 2 adc R4,R4 ; Add carry to MSB Loop: rjmp Loop The CLR instruction clears the regis- ter to all zeros. If we now execute the first five in- structions, before the adc, we'll get the following register content. The two numbers FF and 01 are ok, adding those has yielded zero in R3. But: in contrary to our previous adding operations the C flag in the status register SREG is now 1, indicating that the adding has caused an overflow or CARRY. And: as the result is zero the Z (or zero) flag is also set one. The next instruction, adc R4,R4 now adds the content of R4 with itself (which is zero) and the carry flag. As the carry flag is set, the result is 1.
That is what the flags are for: they indicate relevant states that the CPU detects, but can also be set or cleared by the programmer (the carry flag with the instructions SEC or CLC ). Another use of the flags are conditional branches. The instructions BRCS label or BRCC label jump to a label if the carry flag is one (SET) of zero (CLEAR). The same example can also be formulated like this: dec R1 ; Number 1 to 1 inc R2 ; Number 2 to 1 clr R4 ; Result MSB to zero mov R3,R1 ; Copy number 1 to R add R3,R2 ; Add number 2 brcc Loop ; Branch if carry is clear inc R4 ; Increase MSB Loop: rjmp loop That has the same effect: the increase of the MSB is not executed if the carry flag is not set. Note that not all instructions change flags. If and which flags are altered by the CPU can be seen from the Instruction Summary in the data-book of the device. So, e. g. the INC instruction sets the Z flag, but not the C flag. Consult the data-book or the Instruction Set Manual if you are not sure or in doubt. With these instruments now learned we can easily construct a 64 bit counter. We increase the lowest byte in R1 and each time, when the zero flag is set, we increase the next higher byte in R2, too. And so on for the bytes up to R8. The source code for that: Count: inc R0 ; Increase the lowest byte brne Count ; If Z flag is clear count on inc R1 ; Increase the second byte brne Count ; If Z flag is clear count on inc R2 ; Increase the third byte brne Count ; If Z flag is clear count on inc R3 ; Increase the fourth byte brne Count ; If Z flag is clear count on inc R4 ; Increase the fifth byte brne Count ; If Z flag is clear count on inc R5 ; Increase the sixth byte brne Count ; If Z flag is clear count on inc R6 ; Increase the seventh byte brne Count ; If Z flag is clear count on inc R7 ; Increase the highest byte rjmp Count ; Count on
An I/O pin can be input or output. This is controlled by clearing or setting their bit in the port register DDRp. As the ATtiny13 has only one I/O port and only five I/O pins, you can use the following instructions: Loop: cbi DDRB,DDB0 ; Clear direction bit of pin PB sbi DDRB,DDB0 ; Set direction bit of pin PB rjmp Loop This makes the external pin PB0 first input ( CBI means Set Bit I/O) and then output ( CBI means Clear Bit I/O), then restarts again. If you would attach a LED to external pin PB and tie it with a resistor of a few 100 Ohms to the operating voltage, the LED would go on (as long as the output is active) and off (as long as the output is not active). If you as - semble that code, simulate it step-by-step you'll see the effect in the port view window. A second possibility is to make PB0 an output permanently and to clear and set the PORTB bit. The source code for that: sbi DDRB,DDB0 ; Make PB0 an output Loop: cbi PORTB,PORTB0 ; Make PBO low sbi PORTB,PORTB0 ; Make PB0 high rjmp Loop ; Repeat all over Now the out- put pin PB always drives the output pin: first to low (an at- tached LED to the operating voltage is on), then to high (the LED is off). That is what you get on the PB0 pin: a
very fast signal of a few micro-seconds duration, the low period being half as long as the high period. BTW: you'll get those pulse dis- plays in avr_sim if you enable Scope , then in the scope control window choose these selections and click Show scope. A click on Time elapsed in the simulation status window clears the scope and restarts the time. An additional case occurs if you clear the direction bit in a port bit and set the port output bit: The port pin now switches an internal resistor of approximately 50 kΩ to the operatig voltage to the input pin. This ties the input pin to the operating voltage ("pull-up"), so reading it by the instruction IN R16,PINB shows a one at this bit location. If you connect a switch or a button to this input pin, you can read a zero at this bit if the switch is on, tieing the input pin to ground and over-riding the pull-up, or a one if the switch or button is off.
To exactly find out,