Temario:
● Lo más importante es recordar que ningún miembro de la familia AVR puede acceder a
más de 4M palabras de opcode, porque el contador de programa (PC) en el AVR puede
tener un ancho de 22 bits como máximo (desde 0x000000 hasta 0x3FFFFF)
128Kx2Bytes
1FFFF
Mega2560
(FLASH)
● Como se sabe, la memoria FLASH es un tipo de memoria de solo lectura (ROM), y por
este motivo hace falta un procedimiento especial para escribir en ella. Las formas más
común de almacenar el programa en la FLASH es mediante un programador o con un
programa bootloader (un programa que “carga” otros programas al microcontrolador).
● De forma similar, la instrucción “ADD R16, R17” es traducida al código máquina “0F01”.
Las imágenes en las siguientes diapositivas brindan mayores detalles.
● Las instrucciones en los AVR pueden ser de 2 bytes (16 bits) o 4 bytes (32 bits). Casi todas
las instrucciones en los AVR son de 2 bytes. Las excepciones son LDS, STS, JMP y
algunas otras.
En términos simples, la instrucción SUBI sustrae un valor de 8 bits del valor almacenado en
un registro de propósito general. Esta instrucción tiene el siguiente formato:
SUBI Rd, K ; Resta K del contenido de Rd y almacena
; el resultado en Rd
K es un valor de 8 bits que puede encontrarse entre 0 y 255 en decimal, ó entre 0x00 y 0xFF
en hexadecimal, y Rd está entre R16 y R31 .
Ejemplo: El siguiente código almacena el valor 29 en el registro R21 y luego sustrae 18 del
mismo registro.
LDI R21, 29 ; carga R21 con 29 (R21 = 29)
SUBI R21, 18 ; R21 = R21 – 18 = 29 – 18 = 11
MUL Rd, Rr ; multiplica Rr y Rd y almacena el resultado en
; R1(MSB) y R0(LSB)
La instrucción MUL le dice a la CPU que multiplique el valor de Rr con el valor de Rd.
Dado que el valor contenido en ambos registros es de 8 bits, se espera que el resultado
tenga un máximo de 16 bits (2 bytes). Por esta razón, el byte más significativo (MSB) y el
byte menos significativo (LSB) se almacenan por separado, en los registros R1 y R0,
respectivamente
Para multiplicar dos números, por ejemplo 0x25 y 0x34, uno puede hacer lo siguiente:
LDI R16, 0x25 ; carga 0x25 dentro de R16
LDI R17, 0x34 ; carga 0x34 dentro de R17
MUL R16, R17 ; multiplica los valores de R16 y R17
Debe quedar claro que la instrucción MUL sólo se emplea para multiplicar números enteros
sin signo (naturales). Si se desea trabajar con números enteros con signo, entonces se deben
emplear otras instrucciones, tales como MULS y MULSU.
Por otro lado, el microcontrolador ATmega2560 cuenta con diferente instrucciones para llevar
a cabo operaciones booleanas (lógicas) . Las instrucciones que revisaremos a continuación
son:
AND (Operación AND, bit a bit, entre los valores almacenados en dos registros)
ANDI (Operación AND entre un valor almacenado en un registro y un inmediato)
OR (Operación OR, bit a bit, entre los valores almacenados en dos registros)
ORI (Operación AND entre un valor almacenado en un registro y un inmediato)
EOR (Operación XOR, bit a bit, entre los valores almacenados en dos registros)
SER (Forzar a ‘1’ todos los bits en un registro)
CLR (Forzar a ‘0’ todos los bits en un registro)
AND Rd, Rr ; Rd = Rd AND Rr
La instrucción AND le dice a la CPU que lleve a cabo la operación lógica AND sobre los dos
operandos (bit a bit), y que almacene el resultado en Rd.
Por ejemplo, para hacer un AND sobre los números 0xF0 y 0x0F, uno puede hacer lo
siguiente:
LDI R16, 0xF0 ; carga 0xF0 dentro de R16
LDI R17, 0x0F ; carga 0x0F dentro de R17
AND R16, R17 ; R16 = R16 AND R17 = 0xF0 AND 0x0F = 0x00
En términos simples, la instrucción ANDI lleva a cabo un AND lógico entre un valor
almacenado en un registro y un valor constante. Esta instrucción tiene el siguiente formato:
ANDI Rd, K ; AND sobre K y el valor almacenado en Rd
; almacena el resultado en Rd
K es un valor de 8 bits que puede encontrarse entre 0 y 255 en decimal, ó entre 0x00 y 0xFF
en hexadecimal, y Rd está entre R16 y R31 .
Ejemplo: El siguiente código almacena el valor 0xAA en el registro R21 y luego aplica un
AND lógico entre el número 0xCC y el contenido del registro.
LDI R21, 0xAA ; carga R21 con 0xAA (R21 = 0xAA)
ANDI R21, 0xCC ; R21 = R21 AND 0xCC = 0xAA AND 0xCC = 0x88
OR Rd, Rr ; Rd = Rd OR Rr
La instrucción OR le dice a la CPU que lleve a cabo la operación lógica OR sobre los dos
operandos (bit a bit), y que almacene el resultado en Rd.
Por ejemplo, para hacer un OR sobre los números 0xF0 y 0x0F, uno puede hacer lo siguiente:
LDI R16, 0xF0 ; carga 0xF0 dentro de R16
LDI R17, 0x0F ; carga 0x0F dentro de R17
OR R16, R17 ; R16 = R16 OR R17 = 0xF0 OR 0x0F = 0xFF
En términos simples, la instrucción ORI lleva a cabo un OR lógico entre un valor almacenado
en un registro y un valor constante. Esta instrucción tiene el siguiente formato:
ORI Rd, K ; OR sobre K y el valor almacenado en Rd
; almacena el resultado en Rd
K es un valor de 8 bits que puede encontrarse entre 0 y 255 en decimal, ó entre 0x00 y 0xFF
en hexadecimal, y Rd está entre R16 y R31 .
Ejemplo: El siguiente código almacena el valor 0xAA en el registro R21 y luego aplica un OR
lógico entre el número 0xCC y el contenido del registro.
LDI R21, 0xAA ; carga R21 con 0xAA (R21 = 0xAA)
ORI R21, 0xCC ; R21 = R21 OR 0xCC = 0xAA OR 0xCC = 0xEE
EOR Rd, Rr ; Rd = Rd XOR Rr
La instrucción EOR le dice a la CPU que lleve a cabo la operación lógica XOR sobre los dos
operandos (bit a bit), y que almacene el resultado en Rd.
Por ejemplo, para hacer un XOR sobre los números 0xAA y 0xCC, uno puede hacer lo
siguiente:
LDI R16, 0xAA ; carga 0xAA dentro de R16
LDI R17, 0xCC ; carga 0xCC dentro de R17
EOR R16, R17 ; R16 = 0xAA XOR 0xCC = 0xF0 XOR 0x0F = 0x66
SER Rd ; Rd = 0xFF
La instrucción SER fuerza todos los bits del registro Rd a ‘1’. La acción de forzar un bit a ‘1’,
en Inglés, se conoce como “set” (setear).
CLR Rd ; Rd = 0x00
La instrucción CLR fuerza todos los bits del registro Rd a ‘0’. La acción de forzar un bit a ‘0’,
en Inglés, se conoce como “clear” (limpiar).
Por ejemplo, para cargar valores a los registros R16 y R17, y luego “setear” todos los bits de
R16 y “limpiar” todos los bits de R17, se puede hacer los siguiente:
LDI R16, 0xAA ; carga 0xAA dentro de R16
LDI R17, 0xCC ; carga 0xCC dentro de R17
SER R16 ; R16 = 0xFF
CLR R17 ; R17 = 0x00
Las instrucciones para manipulación de bits pueden llevar a cabo diferentes operaciones,
tales como desplazamiento lógico, desplazamiento aritmético, rotación, forzar un bit
específico a 1, forzar un bit específico a 0, entre otras.
Por practicidad, solo se revisarán las instrucciones más relevantes para los objetivos del
curso:
La instrucción LSL es usada para desplazar (desplazamiento lógico) un bit hacia la izquierda
el valor almacenado en un GPR. La instrucción LSL tiene el siguiente formato
LSL Rd ; Desplazamiento lógico hacia la izquierda
Por ejemplo, el siguiente código almacena el valor 0x26 en el registro R20 y luego aplica 03
desplazamientos consecutivos hacia la izquierda:
LDI R20, 0x26 ; R20 = 0010 0110 (38)
LSL R20 ; R20 = 0100 1100 (76)
LSL R20 ; R20 = 1001 1000 (152)
LSL R20 ; R20 = 0011 0000 (48)
Se debe tener claro que, esta instrucción lleva a cabo un desplazamiento y no una rotación.
La diferencia fundamental es que para una rotación debe tomarse en cuenta un bit de
acarreo, mientras que para un desplazamiento este bit no es relevante.
La instrucción LSR es usada para desplazar (desplazamiento lógico) un bit hacia la derecha
el valor almacenado en un GPR. La instrucción LSR tiene el siguiente formato
LSR Rd ; Desplazamiento lógico hacia la derecha
Por ejemplo, el siguiente código almacena el valor 0x26 en el registro R20 y luego aplica 03
desplazamientos consecutivos hacia la derecha:
LDI R20, 0x26 ; R20 = 0010 0110 (38)
LSR R20 ; R20 = 0001 0011 (19)
LSR R20 ; R20 = 0000 1001 (9)
LSR R20 ; R20 = 0000 0100 (4)
ASR Rd ; Desplazamiento aritmético hacia la derecha
LDI R20, 0x8C ; R20 = 1000 1100 (116)
LSR R20 ; R20 = 1100 0110 (58)
LSR R20 ; R20 = 1110 0011 (29)
LSR R20 ; R20 = 1111 0001 (15)
La instrucción SBI es usada para forzar a ‘1’ un bit en particular de un registro de función
específica (SFR). La instrucción SBI tiene el siguiente formato:
SBI A, n ; Forzar el bit n de A a 1
; 0 < A < 63
Esta instrucción es útil cuando se tienen diferentes señales conectadas a un puerto del
microcontrolador, y solo se desea modificar una señal en particular.
Por ejemplo, el siguiente código configura el PUERTO F como salida, almacena el valor
0x8C en un GPR, muestra el valor del GPR en PORTF y fuerza el bit 5 de PORTF a ‘1’ :
LDI R20, 0xFF ; Cofigurar todos los pines del
OUT DDRF, R20 ; PUERTO F como salida
LDI R20, 0x8C ; R20 = 1000 1100 (0x8C)
OUT PORTF, R20 ; PORTF = 1000 1100 (0x8C)
SBI PORTF, 5 ; PORTF = 1010 1100 (0xAC)
La instrucción CBI es usada para forzar a ‘0’ un bit en particular de un registro de función
específica (SFR). La instrucción CBI tiene el siguiente formato:
CBI A, n ; Forzar el bit n de A a 0
; 0 < A < 63
Esta instrucción es útil cuando se tienen diferentes señales conectadas a un puerto del
microcontrolador, y solo se desea modificar una señal en particular.
Por ejemplo, el siguiente código configura el PUERTO F como salida, almacena el valor
0x8C en un GPR, muestra el valor del GPR en PORTF y fuerza el bit 3 de PORTF a ‘1’ :
LDI R20, 0xFF ; Cofigurar todos los pines del
OUT DDRF, R20 ; PUERTO F como salida
LDI R20, 0x8C ; R20 = 1000 1100 (0x8C)
OUT PORTF, R20 ; PORTF = 1000 1100 (0x8C)
CBI PORTF, 3 ; PORTF = 1000 0100 (0x84)
Ejercicio 01:
Conectar 08 LEDs al PUERTO F y otros 08 LEDs al PUERTO K del microcontrolador. Luego,
escribir un programa que lleve a cabo lo siguiente: Crear dos etiquetas para valores
constantes VALOR1 y VALOR2, cargar una de las constantes a R16 y la otra a constante a
R17, llevar a cabo la multiplicación (sin signo) de ambos valores y, finalmente, mostrar el
resultado de la multiplicación en los LEDs del PUERTO F (LSB) y del PUERTO K (MSB).
Ejercicio 02:
Conectar 08 LEDs al PUERTO F del microcontrolador. Luego, escribir un programa que lleve
a cabo lo siguiente: Crear una etiqueta para un valor constante NUMERO, cargar esta
constante al registro R18, calcular el valor del número representado en código Gray de 8 bits,
cargar el resultado del cálculo en el registro R12, mostrar el resultado en los LEDs del
PUERTO F.
Ejercicio 03:
Conectar 08 LEDs al PUERTO F del microcontrolador. Luego, escribir un programa que lleve
a cabo lo siguiente: Definir una etiqueta VALOR_INICIAL = 0xAA, cargar el valor inicial al
registro R19, mostrar el valor inicial en los LEDs del PUERTO F. Después, empleando las
instrucciones SBI y CBI, modificar el programa para encender los LEDs en las posiciones 0, 4
y apagar los LEDs en las posiciones 1, 3 y 7 (todo esto sin alterar el estado de los demás
LEDs). Una vez que el programa funcione, intente cumplir la misma tarea mediante el uso de
las instrucciones AND/ANDI y OR/ORI.
Para los objetivos del curso no es necesario el manejo de todas estas instrucciones, por lo
que solamente se tratará el uso de la instrucción JMP.
Por otro lado, existen 02 instrucciones “especiales” que también permiten realizar saltos
incondicionales: CALL (Llamar) y RET (Volver). Estas instrucciones serán tratadas con
detalle cuando veamos la parte de llamada a sub-rutinas y el uso de la pila (stack).
La instrucción JMP es usada para llevar a cabo un salto incondicional hacia CUALQUIER
UBICACIÓN en la memoria de programa. Esta instrucción tiene el siguiente formato :
JMP k ; Realiza un salto condicional a la
; instrucción en la ubicación k
Para emplear esta instrucción es necesario el uso del campo “etiqueta” de la instrucción hacia
la que se desea realizar el salto.
Por ejemplo, el siguiente código configura el PUERTO F como salida, lee el PUERTO K y
muestra su valor en el PUERTO F. La lectura y escritura de los puertos se realiza de manera
permanente:
LDI R20, 0xFF ; Cofigurar todos los pines del
OUT DDRF, R20 ; PUERTO F como salida
BUCLE: LDS R3, PINK ; Leer PINK y guardar en R3
OUT PORTF, R3 ; Mostrar el valor de R3 en PORTF
JMP BUCLE ; Repetir indefinidamente
Ejercicio 04:
Conectar 08 LEDs al PUERTO F y conectar 08 pulsadores (con resistencias pull-down) al
PUERTO K del microcontrolador. Luego, escribir un programa que lleve a cabo lo siguiente:
Leer el valor del PINK, calcular el complemento a dos del valor leído, mostrar el resultado en
los LEDs del PUERTO F. Repetir la lectura del puerto permanentemente.
NOTA: Si no cuenta con resistencias pull-down, una alternativa es activar las resistencias
pull-up internas del MCU e invertir el valor leído de PINK antes del procesamiento.
Ejercicio 05:
Conectar 08 LEDs al PUERTO F y conectar 08 pulsadores (con resistencias pull-down) al
PUERTO K del microcontrolador. Luego, escribir un programa que lleve a cabo lo siguiente:
leer el valor en PINK, forzar a ‘1’ los bits en las posiciones 0 y 7 en el valor leído, mostrar el
valor (con los bits modificados) en los LEDs del PUERTO F. Repertir la lectura del puerto
permanentemente.
Ejercicio 06:
Conectar 08 LEDs al PUERTO F y conectar 08 pulsadores (con resistencias pull-down) al
PUERTO K del microcontrolador. Luego, escribir un programa que lleve a cabo lo siguiente:
leer el valor en PINK, invertir los bits en las posiciones 0, 5 y 7 en el valor leído, y mostrar el
valor (con los bits modificados) en los LEDs del PUERTO F. Repetir la lectura del puerto
permanentemente.
Cada una de las banderas condicionales puede ser usada para llevar a cabo un salto
condicional, como se verá más adelante.
Algunas instrucciones afectan los 06 bits bandera (por ejemplo ADD). Sin embargo, otras
funciones no afectan ninguno de los bits bandera (las instrucciones de carga como LDI y LDS
están en esta categoría). Otras instrucciones afectan solo algunos de los bits bandera (las
instrucciones lógicas están en esta categoría).
En algunas ocasiones deseamos evaluar si un número es positivo, negativo, mayor que otro
número, etc. Para estos casos resulta más práctico emplear las instrucciones de
comparación, las cuales fueron diseñadas especialmente diseñadas para este propósito.
Las instrucciones de comparación afectan los bits bandera, pero sin modificar el contenido de
ningún registro de propósito general (a diferencia de las instrucciones aritméticas y lógicas).
Las instrucciones de comparación más importantes, con sus respectivos formatos, son las
siguientes:
CP Rd, Rr ; Opera internamente Rd Rr
Las instrucciones de salto condicional son aquellas que llevan a cabo un salto si y solo si una
bandera determinada tiene un valor determinado. Si esta condición no se cumple, entonces el
salto no es llevado a cabo y el programa ejecuta la siguiente instrucción.
Cada instrucción de salto evalúa una bandera en particular, tal y como se muestra en la
siguiente tabla:
Salta si es menor
Salta si es mayor o igual
Salta si es igual
Salta si no es igual
Salta si es negativo
Salta si es positivo
Salta si hubo desbordamiento
Salta si no hubo desbordamiento
Ejemplo:
Escribir un programa que limpie el registro R20, luego que sume 3 a R20 diez veces y que
muestre el resultado final en el PUERTO F. Utilice la bandera Z y la instrucción BRNE.
.INCLUDE “m2560def.inc”
.ORG 0x00
R16 = 10
R20 = 0
R20 = R20 + 3
R16 = R16 - 1
F
¿R16 ≠ 0?
Mostrar
V
resultado
FIN
Las instrucciones de salto vistas anteriormente nos permiten realizar un salto hacia cualquier
parte del código, por ese motivo necesitan una ubicación (etiqueta) como operando. En
contraste, las instrucciones de salto pequeño (skip) solamente pueden saltar una instrucción.
Por ejemplo: si nos encontramos en la instrucción 35 y la condición de salto se cumple,
entonces el procesador pasará a ajecutar la instrucción 37, saltándose la instrucción 36. Si la
condición de salto no se cumple, entonces todas las instrucciones son ejecutadas de manera
regular.
Estas instrucciones se caracterizan por traer “incorporado” el test y por no requerir de una
etiqueta de ubicación para realizar el salto (ya que la ubicación de destino está implícita).
La instrucciones SBRC y SBRS son usadas para saltarse una instrucción si, en un registro
de propósito general (GPR), un bit en particular es ‘0’ o es ‘1’, respectivamente. Estas
instrucciones tienen el siguiente formato:
SBRC Rd, n ; Salta una instrucción si el bit n
; de Rd es 0
SBRS Rd, n ; Salta una instrucción si el bit n
; de Rd es 1
Por ejemplo, el siguiente código configura el pin 7 PUERTO B como salida, y fuerza dicho pin
a ‘1’ solo si el pin 3 del PUERTO K se encuentra en ‘0’ :
La instrucciones SBRC y SBRS son usadas para saltarse una instrucción si, en un registro
de función específica (SFR), un bit en particular es ‘0’ o es ‘1’, respectivamente. Estas
instrucciones tienen el siguiente formato:
SBIC A, n ; Salta una instrucción si el bit n
; de A es 0, 0 < A < 63
SBIS A, n ; Salta una instrucción si el bit n
; de A es 1, 0 < A < 63
Por ejemplo, el siguiente código configura el pin 7 PUERTO B como salida, y fuerza dicho pin
a ‘1’ solo si el pin 3 del PUERTO F se encuentra en ‘0’ :
Ejercicio 07:
Conectar 08 LEDs al PUERTO F del microcontrolador. Luego, escribir un programa que lleve
a cabo lo siguiente: Crear una etiqueta N que defina un valor constante que valga entre 1 y
22, implementar un código que calcule la suma de los N primeros números naturales
mediante un bucle, y que muestre el resultado en los LEDs del PUERTO F.
Ejercicio 08:
Conectar 08 LEDs al PUERTO F del microcontrolador y 08 pulsadores (con resistencias pull-
down). Luego, escribir un programa que lleve a cabo lo siguiente: Leer el valor de entrada del
PUERTO K, interpretar ese valor como un número entero con signo de 8 bits, calcular y
mostrar el valor absoluto de este valor en los LEDs del PUERTO F.
Ejercicio 09:
Conectar 02 pulsadores a cualquiera de los puertos del microcontrolador. Luego, escribir un
programa que lleve a cabo lo siguiente: configurar el pin 7 del PUERTO B (PB7) como salida,
activar las resistencias pull-up internas correspondientes a los pines donde están los botones,
y hacer que el LED conectado a PB7 responda a las entradas de acuerdo a una lógica de
“control bimanual”. Una vez que el programa funcione correctamente, modificarlo para que
funcione con una lógica de “luz de escalera”.
NOTA: Si no está familiarizado con los términos “control bimanual” y “luz de escalera”,
preguntar al profesor.