Tabla de contenido:

Tutorial 2 de AVR Assembler: 4 pasos
Tutorial 2 de AVR Assembler: 4 pasos

Video: Tutorial 2 de AVR Assembler: 4 pasos

Video: Tutorial 2 de AVR Assembler: 4 pasos
Video: AVR Ассемблер. Урок 2. Порты. Мигалка. AVR Assembler. Lesson 2. Ports. Flasher. 2024, Mes de julio
Anonim
Tutorial de ensamblador AVR 2
Tutorial de ensamblador AVR 2

Este tutorial es una continuación del "Tutorial 1 de ensamblador de AVR"

Si no ha pasado por el Tutorial 1, debe detenerse ahora y hacerlo primero.

En este tutorial continuaremos nuestro estudio de la programación en lenguaje ensamblador del atmega328p usado en Arduino's.

Necesitará:

  1. una placa de pruebas Arduino o simplemente un Arduino normal como en el Tutorial 1
  2. un LED
  3. una resistencia de 220 ohmios
  4. un pulsador
  5. cables de conexión para hacer el circuito en su placa de pruebas
  6. Manual del conjunto de instrucciones: www.atmel.com/images/atmel-0856-avr-instruction-s…
  7. Hoja de datos: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…

La colección completa de mis tutoriales se puede encontrar aquí:

Paso 1: construcción del circuito

Construyendo el circuito
Construyendo el circuito

Primero necesitas construir el circuito que estudiaremos en este tutorial.

Esta es la forma en que está conectado:

PB0 (pin digital 8) - LED - R (220 ohmios) - 5V

PD0 (pin digital 0) - pulsador - GND

Puede verificar que su LED esté orientado correctamente conectándolo a GND en lugar de PB0. Si no sucede nada, invierta la orientación y la luz debería encenderse. Luego, vuelva a conectarlo a PB0 y continúe. La imagen muestra cómo está conectada mi placa arduino.

Paso 2: escribir el código de ensamblaje

Escribir el código de ensamblaje
Escribir el código de ensamblaje

Escriba el siguiente código en un archivo de texto llamado pushbutton.asm y compílelo con avra como lo hizo en el Tutorial 1.

Tenga en cuenta que en este código tenemos muchos comentarios. Cada vez que el ensamblador ve un punto y coma, saltará el resto de la línea y pasará a la siguiente. Es una buena práctica de programación (¡especialmente en lenguaje ensamblador!) Comentar mucho su código para que cuando regrese a él en el futuro sepa lo que estaba haciendo. Voy a comentar muchas cosas en los primeros tutoriales para que sepamos exactamente qué está pasando y por qué. Más adelante, una vez que mejoremos un poco en la codificación de ensamblaje, comentaré las cosas con un poco menos de detalle.

;************************************

; escrito por: 1o_o7; fecha: 23 de octubre de 2014; ***********************************

.nolista

.incluya "m328Pdef.inc".list.def temp = r16; designar el registro de trabajo r16 como temp rjmp Init; primera línea ejecutada

En eso:

ser temp; establezca todos los bits en temp en 1's. fuera DDRB, temp; establecer un bit como 1 en la E / S de dirección de datos; registrarse para PortB, que es DDRB, establece eso; pin como salida, un 0 establecería ese pin como entrada; así que aquí, todos los pines de PortB son salidas (configuradas en 1) ldi temp, 0b11111110; cargue el número "inmediato" en el registro temporal; si fuera solo ld, entonces el segundo argumento; tendría que ser una ubicación de memoria en lugar de DDRD, temp; mv temp a DDRD, el resultado es que se ingresa PD0; y el resto son salidas clr temp; todos los bits en temp se establecen en 0's out PortB, temp; establezca todos los bits (es decir, pines) en PortB a 0V ldi temp, 0b00000001; cargar el número inmediato a la salida de temperatura PortD, temp; Mueva la temperatura a PortD. PD0 tiene una resistencia pull up; (es decir, establecido en 5 V) ya que tiene un 1 en ese bit; el resto son 0V desde ceros.

Principal:

en temp, PinD; PinD mantiene el estado de PortD, cópielo en temp; si el botón está conectado a PD0 esto será; 0 cuando se presiona el botón, 1 en caso contrario desde; PD0 tiene una resistencia pull-up que normalmente está a 5V de salida PortB, temp; envía los 0 y 1 leídos arriba a PortB; esto significa que queremos que el LED esté conectado a PB0; cuando PD0 es LOW, establece PB0 en LOW y gira; en el LED (ya que el otro lado del LED está; conectado a 5V y esto establecerá PB0 en 0V entonces; la corriente fluirá) rjmp Main; vuelve al inicio de Main

Tenga en cuenta que esta vez no solo tenemos muchos más comentarios en nuestro código, sino que también tenemos una sección de encabezado que brinda información sobre quién lo escribió y cuándo se escribió. El resto del código también se divide en secciones.

Después de haber compilado el código anterior, debe cargarlo en el microcontrolador y ver que funciona. El LED debe encenderse mientras presiona el botón y luego apagarse nuevamente cuando lo suelte. He mostrado cómo se ve en la imagen.

Paso 3: Análisis línea por línea del código

Saltaré las líneas que son meramente comentarios, ya que su propósito es evidente.

.nolista

.incluya "m328Pdef.inc".list

Estas tres líneas incluyen el archivo que contiene las definiciones de Registro y Bit para el ATmega328P que estamos programando. El comando.nolist le dice al ensamblador que no incluya este archivo en el archivo pushbutton.lst que produce cuando lo ensambla. Desactiva la opción de listado. Después de incluir el archivo, volvemos a activar la opción de listado con el comando.list. La razón por la que hacemos esto es porque el archivo m328Pdef.inc es bastante largo y realmente no necesitamos verlo en el archivo de lista. Nuestro ensamblador, avra, no genera automáticamente un archivo de lista y si quisiéramos uno lo ensamblaríamos usando el siguiente comando:

avra -l pushbutton.lst pushbutton.asm

Si hace esto, generará un archivo llamado pushbutton.lst y si examina este archivo encontrará que muestra el código de su programa junto con información adicional. Si observa la información adicional, verá que las líneas comienzan con una C: seguida de la dirección relativa en hexadecimal de donde se coloca el código en la memoria. Básicamente, comienza en 000000 con el primer comando y aumenta a partir de ahí con cada comando posterior. La segunda columna después del lugar relativo en la memoria es el código hexadecimal del comando seguido del código hexadecimal del argumento del comando. Analizaremos los archivos de lista con más detalle en futuros tutoriales.

.def temp = r16; designar el registro de trabajo r16 como temp

En esta línea usamos la directiva de ensamblador ".def" para definir la variable "temp" como igual al "registro de trabajo" r16. Usaremos el registro r16 como el que almacena los números que queremos copiar en varios puertos y registros (en los que no se puede escribir directamente).

Ejercicio 1: intente copiar un número binario directamente en un puerto o registro especial como DDRB y vea qué sucede cuando intenta ensamblar el código.

Un registro contiene un byte (8 bits) de información. Esencialmente, generalmente es una colección de SR-Latches, cada uno es un "bit" y contiene un 1 o un 0. Podemos discutir esto (¡e incluso construir uno!) Más adelante en esta serie. Quizás se esté preguntando qué es un "registro de trabajo" y por qué elegimos r16. Hablaremos de eso en un tutorial futuro cuando nos sumerjamos en el atolladero de las partes internas del chip. Por ahora, quiero que comprenda cómo hacer cosas como escribir código y programar hardware físico. Luego, tendrá un marco de referencia de esa experiencia que hará que la memoria y las propiedades de registro del microcontrolador sean más fáciles de entender. Me doy cuenta de que la mayoría de los libros de texto introductorios y las discusiones hacen esto al revés, pero he descubierto que jugar un videojuego por un tiempo primero para obtener una perspectiva global antes de leer el manual de instrucciones es mucho más fácil que leer el manual primero.

rjmp Init; primera línea ejecutada

Esta línea es un "salto relativo" a la etiqueta "Init" y no es realmente necesario aquí ya que el siguiente comando ya está en Init, pero lo incluimos para uso futuro.

En eso:

ser temp; establezca todos los bits en temp en 1's.

Después de la etiqueta de inicialización ejecutamos un comando "set register". Esto establece todos los 8 bits en el registro "temp" (que recuerda es r16) a unos. Entonces, temp ahora contiene 0b11111111.

fuera DDRB, temp; establecer un bit como 1 en el registro de E / S de dirección de datos

; para PortB, que es DDRB, establece ese pin como salida; un 0 establecería ese pin como entrada; así que aquí, todos los pines PortB son salidas (configurados en 1)

El registro DDRB (Registro de dirección de datos para el puerto B) indica qué pines del puerto B (es decir, PB0 a PB7) se designan como entrada y cuáles se designan como salida. Como tenemos el pin PB0 conectado a nuestro LED y el resto no conectado a nada, estableceremos todos los bits en 1, lo que significa que todas son salidas.

ldi temp, 0b11111110; cargue el número 'inmediato' en el registro temporal

; si fuera solo ld, el segundo argumento lo haría; tiene que ser una ubicación de memoria

Esta línea carga el número binario 0b11111110 en el registro temporal.

fuera DDRD, temp; mv temp a DDRD, el resultado es que se ingresa PD0 y

; el resto son salidas

Ahora configuramos el Registro de dirección de datos para PortD desde temp, ya que temp todavía contiene 0b11111110, vemos que PD0 se designará como un pin de entrada (ya que hay un 0 en el punto más a la derecha) y el resto se designan como salidas ya que hay 1 está en esos lugares.

temp clr; todos los bits en temp se establecen en 0's

hacia fuera PortB, temp; establezca todos los bits (es decir, pines) en PortB a 0V

Primero, "borramos" la temperatura del registro, lo que significa poner todos los bits a cero. Luego copiamos eso en el registro PortB que establece 0V en todos esos pines. Un cero en un bit PortB significa que el procesador mantendrá ese pin a 0V, un uno en un bit hará que ese pin se establezca en 5V.

Ejercicio 2: Utilice un multímetro para comprobar si todos los pines del puerto B son realmente cero. ¿Está pasando algo extraño con PB1? ¿Alguna idea de por qué podría ser eso? (similar al Ejercicio 4 a continuación, luego siga el código…) Ejercicio 3: Elimine las dos líneas anteriores de su código. ¿El programa aún se ejecuta correctamente? ¿Por qué?

ldi temp, 0b00000001; cargar el número inmediato a la temperatura

salida PortD, temp; Mueva la temperatura a PortD. PD0 está a 5 V (tiene una resistencia pullup); como tiene un 1 en ese bit, el resto son 0V. Ejercicio 4: elimine las dos líneas anteriores de su código. ¿El programa aún se ejecuta correctamente? ¿Por qué? (Esto es diferente del ejercicio 3 anterior. Consulte el diagrama de asignación de pines. ¿Cuál es la configuración DDRD predeterminada para PD0? (Consulte la página 90 de la hoja de datos).

Primero "cargamos inmediatamente" el número 0b00000001 a temp. La parte "inmediata" está ahí, ya que estamos cargando un número directo en temp en lugar de un puntero a una ubicación de memoria que contiene el número a cargar. En ese caso, simplemente usaríamos "ld" en lugar de "ldi". Luego enviamos este número a PortD que establece PD0 en 5V y el resto en 0V.

Ahora hemos configurado los pines como entrada o salida y hemos configurado sus estados iniciales como 0V o 5V (LOW o HIGH) y ahora ingresamos a nuestro programa "loop".

Principal: en temp, PinD; PinD mantiene el estado de PortD, cópielo en temp

; si el botón está conectado a PD0, entonces esto será; un 0 cuando se presiona el botón, 1 en caso contrario desde; PD0 tiene una resistencia pull-up que normalmente es de 5 V

El registro PinD contiene el estado actual de los pines PortD. Por ejemplo, si conectó un cable de 5V a PD3, en el siguiente ciclo de reloj (que ocurre 16 millones de veces por segundo ya que tenemos el microcontrolador conectado a una señal de reloj de 16MHz) el bit PinD3 (del estado actual de PD3) se convertiría en un 1 en lugar de un 0. Así que en esta línea copiamos el estado actual de los pines a temp.

hacia fuera PortB, temp; envía los 0 y 1 leídos arriba a PortB

; esto significa que queremos que el LED esté conectado a PB0, entonces; cuando PD0 es LOW, establecerá PB0 en LOW y girará; en el LED (el otro lado del LED está conectado; a 5 V y esto establecerá PB0 en 0 V para que fluya la corriente)

Ahora enviamos el estado de los pines en PinD a la salida PortB. Efectivamente, esto significa que PD0 enviará un 1 a PortD0 a menos que se presione el botón. En ese caso, dado que el botón está conectado a tierra, ese pin estará a 0 V y enviará un 0 a PortB0. Ahora, si observa el diagrama del circuito, 0V en PB0 significa que el LED se iluminará ya que el otro lado está a 5V. Si no estamos presionando el botón, de modo que se envíe un 1 a PB0, eso significaría que tenemos 5V en PB0 y también 5V en el otro lado del LED y, por lo tanto, no hay diferencia de potencial y no fluirá corriente, por lo que el El LED no se iluminará (en este caso es un LED que es un diodo y, por lo tanto, la corriente solo fluye en una dirección independientemente de lo que sea).

rjmp Main; vuelve al inicio

Este salto relativo nos devuelve a nuestra etiqueta Main: y comprobamos PinD de nuevo y así sucesivamente. Verificando cada 16 millonésimas de segundo si se está presionando el botón y configurando PB0 en consecuencia.

Ejercicio 5: Modifique su código para que su LED esté conectado a PB3 en lugar de PB0 y vea que funciona. Ejercicio 6: Conecte su LED a GND en lugar de 5V y modifique su código en consecuencia.

Paso 4: Conclusión

En este tutorial hemos investigado más el lenguaje ensamblador para el ATmega328p y hemos aprendido cómo controlar un LED con un botón. En particular, aprendimos los siguientes comandos:

ser register establece todos los bits de un registro en unos

clr register establece todos los bits de un registro en ceros

en el registro, el registro de E / S copia el número de un registro de E / S a un registro de trabajo

En el siguiente tutorial examinaremos la estructura del ATmega328p y los diversos registros, operaciones y recursos que contiene.

Antes de continuar con estos tutoriales voy a esperar y ver el nivel de interés. Si hay varias personas que realmente disfrutan aprendiendo a codificar programas para este microprocesador en lenguaje ensamblador, continuaré y construiré circuitos más complicados y usaré un código más robusto.

Recomendado: