Vous êtes sur la page 1sur 5

__________________________________________________________________________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 r16,0 ;carga el registro 16 con cero


for_loop: ;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 ; se obtiene el dato del USART


cpi r16,0 ; se compara r16 con 0
breq case_0 ; Si son iguales salta a case_0
cpi r16, 1 ; se compara r16 con 1
breq case_1 ; Si son iguales salta a case_1
cpi r16, 2 ; se compara r16 con 2
breq case_2 ; Si son iguales salta a case_2
; y así sucesivamente.

Documento traducido de la pagina AVRfreaks.net 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 ; se obtiene el dato del USART


chk_case_03:
cpi r16, 4 ; se compara r16 con 4
brlo case_03 ; si el valor es menor a (0,1,2,3) salta a case_03
cpi r16, 20 ; se compara r16 con 20
brlo case_419 ; si el valor está entre (4 a 19), salta a case_419
default:
;aquí va código por ; si ninguno de los casos se llevan a cabo,
;default ; 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 ; se limpia el registro r16


loop:
out PortB,r16 ; se escribe el conteo en el Puerto B
inc r16 ; se incrementa el contador
cpi r16,10 ; se compara el contador con 10
brne loop ; Si es diferente a 10, salta a loop

ldi r16, 0 ; básicamente este lazo actúa como el


loop1: ; anterior, con una excepción
inc r16 ; Encuéntrala!
out PortB, r16
cpi r16, 10
brne loop1

¿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 ; se carga r16 con el numero deseado de iteraciones


loop2:
(inserta el código ; se ejecuta el lazo...
de lazo)

dec r16 ; se decrementa el contador de loop


brne loop2 ; si no es cero, se repite 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: ; mientras (PinD = 1) manda a llamar a subrutina port_is_1


in R16,PinD ; lee el PinD y lo almacena en R16
cpi r16,1 ; lo compara con 1
brne while1_end ; Si no es verdadero, se va al final del lazo,
rcall port_is_1 ; de lo contrario, manda a llamar a port_is_1
rjmp while1 ; al final la rutina port_is_1 se repite el lazo
while1_end:

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: ; manda a llamar a la rutina port_is_1 mientras(PinD = 1)


rcall port_is_1 ; llama a rutina port_is_1
in r16, PinD ; lee el PinD y lo almacena a R16
cpi r16, 1 ; compara con 1
breq while2 ; Si es verdadera, se repite el lazo
while2_end: ; 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 ;la macro se llama ldi16, utiliza dos registros de 8 bits
ldi @0,low(@2) ;se carga el primer argumento en (@0) con el byte bajo de @2
ldi @1,high(@2) ;se carga el segundo argumento en (@1) y el byte alto de @2
.endmacro ;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í: ; la macro trabaja así:


ldi r16, 0 ; ldi @0, low(@2)
ldi r17, 0x04 ; ldi @1, high(@2)

Las macros pueden también usarse para reemplazar los cálculos a 16 bits.

.macro addi
subi @0,-(@1)
; Esta es la instrucción "Suma inmediata a registro”
.endmacro
; que no se encuentra en el set de instrucciones!
.macro addi16
; La versión a 16 bits ”Suma inmediata a registro”
subi @0, low(-@2)
sbci @1, high(-@2)
.endmacro

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