__________________________________________________________________________ITSP
Sentencias de Control de Programación 5.1 INTRODUCCIÓN Las instrucciones en ensamblador son pequeñas, por ejemplo out PortD,r15, la cual escribe el contenido del registro 15 en el Puerto D (el cual es un registro de 8 líneas de I/O). La mayoría de las instrucciones al ser ejecutadas dejan ciertos bits en cero o puestos a uno en el Registro de Estado [SREG (5Fh)]. Estos bits se pueden usar para instrucciones de salto o instrucciones aritméticas. Las instrucciones de salto se dirigen a ciertas líneas de código, si el microcontrolador está en un estado específico o solamente se dirigen a la próxima línea de código. Si la variable de conteo en un lazo no ha alcanzado el valor deseado, el microcontrolador tiene que repetir el ciclo. Aquí se tiene un ejemplo sencillo, que muestra las instrucciones aritméticas, de salto y de I/O. ldi for_loop:
r16,0
;carga el registro 16 con cero ;etiqueta para saltar inc r16 ;se incrementa registro r16 out PortD,r16 ;escribe el contenido de r16 a PortD cpi r16,10 ;compara el valor de r16 con 10 Brlo for_loop ;si no ha llegado a 10, salta a for_loop ;de lo contrario se dirige a la siguiente ;línea
En el lazo, r16 actúa como un contador que se incrementa en cada iteración y se escribe en el puerto D (PortD). Cuando el contador alcanza el valor de 10, la instrucción brlo no saltará al inicio del lazo, solamente se dirigirá a la siguiente instrucción. Los comentarios son importantes, especialmente cuando no se tiene un diagrama de flujo. Un comentario se escribe en la misma línea de código con “;” antes del comentario. DIAGRAMA DE FLUJO El Diagrama de Flujo es una representación grafica del código, el estado del programa o incluso el contenido de la SRAM. Especialmente cuando se escribe código en ensamblador ellos son de gran ayuda, debido a que las instrucciones en ensamblador no siempre son del todo explicativas.
Documento traducido de la pagina AVRfreaks.net
1
__________________________________________________________________________ITSP Aquí se tiene un ejemplo del diagrama de flujo: contador = 0
for_loop
contador = contador + 1
PortD = counter
Falso contador = 10?
Verdadero
siguiente línea
5.2 ESTRUCTURA “CASE” A menudo, por ejemplo cuando se reciben comando de valores vía el Puerto serial (USART), es necesario construir una estructura “case” que determine que función se necesita llamar. La estructura “case” compara un valor para varios casos de valores. Como la instrucción de salto no cambian las banderas, esto puede se implementado de la siguiente manera: in r16,UDR cpi r16,0 breq case_0 cpi r16, 1 breq case_1 cpi r16, 2 breq case_2
; ; ; ; ; ; ; ;
se obtiene el dato del se compara r16 con 0 Si son iguales salta a se compara r16 con 1 Si son iguales salta a se compara r16 con 2 Si son iguales salta a y así sucesivamente.
Documento traducido de la pagina AVRfreaks.net
USART case_0 case_1 case_2
2
__________________________________________________________________________ITSP Después de que todos los casos se hayan comparado, tu puedes escribir la directiva “default” la cual se ejecutara si ninguno de los casos han resultado iguales. La estructura “case” no solamente compara valores unicos, también de pueden verificar un rango específico de valores o comparar cadenas. Aquí tenemos un ejemplo: in r16, UDR chk_case_03: cpi r16, 4 brlo case_03 cpi r16, 20 brlo case_419 default: ;aquí va código por ;default
; se obtiene el dato del USART ; ; ; ;
se si se si
compara r16 con 4 el valor es menor a (0,1,2,3) salta a case_03 compara r16 con 20 el valor está entre (4 a 19), salta a case_419
; si ninguno de los casos se llevan a cabo, ; se ejecuta el código por default
Para esté último ejemplo también se puede usar la instrucción brlt (salta si es menor que) si estas usando números con signo.
5.3 ESTRUTURA “LOOP” Para esta instrucción, no es necesario explicar esta instrucción si usamos lenguaje c o Pascal, pero para el ensamblador se requiere tomar con cuidado el conteo de registros. Una versión flexible cuenta de cero hacia arriba hasta un número requerido de iteraciones. Es posible usar el registro de conteo ldi r16,0 loop: out PortB,r16 inc r16 cpi r16,10 brne loop
; se limpia el registro r16
ldi r16, 0 loop1: inc r16 out PortB, r16 cpi r16, 10 brne loop1
; básicamente este lazo actúa como el ; anterior, con una excepción ; Encuéntrala!
; ; ; ;
se se se Si
escribe el conteo en el Puerto B incrementa el contador compara el contador con 10 es diferente a 10, salta a loop
¿Cual es la diferencia entre los dos lazos? El primer lazo incrementa el contador antes de escribirse en el Puerto B. Así los valores en el puerto van de 0..9. Mientras que en el segundo lazo se incrementa el contador antes de escribirse en el puerto B. Así que vemos los valores en el puerto que van de 1..10.
Documento traducido de la pagina AVRfreaks.net
3
__________________________________________________________________________ITSP Recuerda que siempre que utilices los registros en los lazos de control, tendrás que incrementar el registro, de lo contrario entrarías a un ciclo infinito. También podrás utilizar la operación de decremento en un ciclo “loop”. ldi r16, 10 loop2: (inserta el código de lazo)
; se carga r16 con el numero deseado de iteraciones
dec r16 brne loop2
; se decrementa el contador de loop ; si no es cero, se repite el lazo
; se ejecuta el lazo...
5.4 ESTRUTURA “WHILE” El ciclo while()...do{} revisa si una condición resulta verdadera, de ser así se ejecutan las instrucciones que se encuentran dentro del lazo. while1: in R16,PinD cpi r16,1 brne while1_end rcall port_is_1 rjmp while1 while1_end:
; ; ; ; ; ;
mientras (PinD = 1) manda a llamar a subrutina port_is_1 lee el PinD y lo almacena en R16 lo compara con 1 Si no es verdadero, se va al final del lazo, de lo contrario, manda a llamar a port_is_1 al final la rutina port_is_1 se repite el lazo
Este tipo de lazo solamente ejecutara las instrucciones del lazo si la condición es verdadera, de lo contrario nunca hará nada. El lazo do{}...while() ejecuta las instrucciones del lazo al menos una vez: while2: rcall port_is_1 in r16, PinD cpi r16, 1 breq while2 while2_end:
; ; ; ; ; ;
manda a llamar a la rutina port_is_1 mientras(PinD = 1) llama a rutina port_is_1 lee el PinD y lo almacena a R16 compara con 1 Si es verdadera, se repite el lazo si no es verdadera; se procede con el código siguiente
Estos dos ejemplos demuestran dos diferentes instrucciones de salto usados básicamente para lo mismo. “Saltos condicionales”.
5.5 MACROS EN ENSAMBLADOR AVR Las macros son una mejor manera de hacer el código más legible, por ejemplo, si existe código que es a menudo reutilizado o si alrededor de 16 cálculos de bits se ejecutan.
Documento traducido de la pagina AVRfreaks.net
4
__________________________________________________________________________ITSP Las macros en el ensamblador AVR pueden definirse en cualquier lugar dentro del código, pero tendrán que ser usadas después de ser definidas. La directiva MACRO le dice al ensamblador que este es el inicio de una macro. Las macros pueden tomar hasta 10 argumentos. Estos argumentos son referidos como @0@9 dentro de la definición de la macro. Estos argumentos son reemplazados durante el ensamblado pero no pueden cambiarse durante el tiempo de ejecución. Los argumentos que maneja el ensamblador son casi todos: enteros, caracteres, registros, direcciones de I/O, enteros de 16 o 32 bits, expresiones binarias, etc. Macro definida antes de mandarla a llamar: .macro ldi16 ldi @0,low(@2) ldi @1,high(@2) .endmacro
;la macro se llama ldi16, utiliza dos registros de 8 bits ;se carga el primer argumento en (@0) con el byte bajo de @2 ;se carga el segundo argumento en (@1) y el byte alto de @2 ;fin de la macro
ldi16 r16,r17,1024
;se manda a llamar así: ldi16 arg 0,arg 1,arg 2 ;r16 = 0x00 r17 = 0x04
Mientras que esto no, macro se manda a llamar primero sin haber sido definida: ldi16 r16, r17, 1024
; r16 = 0x00 r17 = 0x04
.macro ldi16 ldi @0, low(@2) ldi @1, high(@2) .endmacro
Lo siguiente deberá estar más claro: ldi16 r16, r17, 1024 ; se ensambla así: ldi r16, 0 ldi r17, 0x04
; la macro trabaja así: ; ldi @0, low(@2) ; ldi @1, high(@2)
Las macros pueden también usarse para reemplazar los cálculos a 16 bits. .macro addi subi @0,-(@1) .endmacro .macro addi16 subi @0, low(-@2) sbci @1, high(-@2) .endmacro
; Esta es la instrucción "Suma inmediata a registro” ; que no se encuentra en el set de instrucciones! ; La versión a 16 bits ”Suma inmediata a registro”
Las macros pueden ser más complejas, tener más argumentos. Si muchas macros se definen una sola línea, las últimas no podrán encontrarse.
Documento traducido de la pagina AVRfreaks.net
5