Home MundoTec Software Código fuente Tutorial / pdf Minijuegos
Cerrar

Tutorial Microcontroladores

Tutorial Microcontroladores.







Microcontroladores

Programación.

En este capítulo se discute como planear y escribir un programa de computadora. Se enseñará como preparar un diagrama de flujo y como escribir un programa en lenguaje assembler. Un editor de texto o procesador de palabras se usa para escribir el programa. Luego, una herramienta de programación llamada “ensamblador” se usa para traducir el programa en una forma que la computadora pueda usar. Las herramientas de programación, son programas de PC (computadoras personales) que ayudan en el desarrollo de programas para microcontroladores. Se tratarán los ensambladores, simuladores, emuladores en tiempo real y un conjunto de herramientas muy útiles.

Escribiendo un simple programa......

Escribiremos un pequeño programa en forma de mnemónico y traduciremos esto en forma de código de máquina. El primer paso será planear el programa y documentar este plan con un diagrama de flujo. Luego, se escribirán las instrucciones en mnemónicos para cada bloque en el diagrama de flujo. Finalmente se usará un Ensamblador para traducir el programa de ejemplo, en los códigos que la computadora (MCU) necesita para ejecutar el programa.

El programa en cuestión, leerá el estado de un switch (pulsador) conectado a un pin de entrada. Cuando el pulsador es accionado (cerrado), el programa causará que un LED conectado a un pin de salida se encienda por aproximadamente 1 segundo y luego salga de ese estado. El LED no encenderá nuevamente hasta que el pulsador no sea liberado y vuelto a pulsar. El tiempo que el pulsador se encuentre presionado, no afectará el tiempo de encendido del LED.

A pesar que el programa es muy sencillo, este demuestra los elementos más comunes en cualquier programa de aplicación de un MCU. Primero, este demuestra como un programa puede sensar entradas como el cierre de un pulsador. Segundo, este es un ejemplo de cómo un programa controla una señal de salida. Tercero, el encendido del LED por un tiempo de cerca de un segundo, demuestra una forma que puede ser usada para medir tiempos reales.

Además, se verá que con la ayuda de los MCUs se puede reemplazar el uso de circuitos discretos complejos.

En la figura 6-1, podemos ver un diagrama de flujo del programa de ejemplo. Los diagramas de flujo, se usan a menudo como una herramienta de planeación para escribir el software, porque ellos muestran las funciones y flujos del programa bajo desarrollo. La importancia de las notas, comentarios, y documentación no pueden ser desestimadas. Un buen diagrama de flujo, ayudará a una correcta compresión del funcionamiento de nuestro programa, no solo para una revisión posterior de nuestra parte, sino para otras personas ajenas al proyecto que deban tomar contacto con el.

Microcontroladores

Fig. 6-1. – Diagrama de Flujo de Ejemplo.

Mnemónico del Código fuente :

Una vez que el diagrama de flujo o plan sea completado, el programador desarrollará una serie de instrucciones en lenguaje ensamblador que realizarán las funciones llamadas por cada bloque del diagrama. El programador está limitado a elegir las instrucciones desde un listado de instrucciones (set de instrucciones) correspondientes al CPU utilizado.

El programador escribe las instrucciones en forma de mnemónicos que son fáciles de entender.

La figura 6-2, muestra el Mnemónico del código fuente junto con el diagrama de flujo del programa de ejemplo, donde se puede observar las distintas instrucciones del CPU usadas en cada bloque del diagrama de flujo.

El significado de los Mnemónicos usados en este ejemplo, pueden encontrarse en el Apéndice A.

Durante el desarrollo del programa, se debe hacer notar el uso de una demora de tiempo en tres lugares distintos. Una sub-rutina fue creada para generar una demora de 50 mSeg.

La misma se usa en dos lugares directamente para producir el algoritmo de “antirrebote” de los pulsadores y además participar en la construcción de la demora de 1 segundo. Para mantener esta figura simple, los comentarios que deberían incluirse en el código fuente, fueron omitidos en forma intencional.

Los comentarios completos se muestran en el listado 6.1.

Microcontroladores

Fig. 6-2 – Diagrama de Flujo y Mnemónicos.

Software de Demora.

En la figura 6-3, se muestra un diagrama de flujo expandido de la sub-rutina de la demora de 50 mSeg.

Una sub-rutina, es un programa relativamente pequeño que realiza algunas funciones comunes requeridas. Siempre que una función, necesita ser realizada muchas veces en el curso de un programa, la sub-rutina solamente es escrita una sola vez. En cada lugar donde se necesita esta función el programador podría utilizarla por medio de instrucciones como “salto corto a sub-rutina” ( Branch to Subroutine – BSR ) o por medio de un “salto largo a sub-rutina” ( Jump to Subroutine – JSR ).

Microcontroladores

Fig. 6-3 – Diagrama de Flujo de la rutina de Demora y Mnemónicos.

Antes de comenzar a ejecutar la instrucción en la sub-rutina , la dirección de la instrucción que sigue al JSR ( o BSR) se almacena automáticamente en el STACK (PILA) que está ubicado dentro del mapa de la memoria RAM temporaria. Cuando el CPU finaliza la ejecución de las instrucciones dentro de la sub-rutina, una instrucción llamada “Retorno desde Sub-rutina” (Return fron Subroutine – RTS ) se ejecuta como última instrucción dentro de la sub-rutina. La instrucción RTS, causa que el CPU recobre la dirección de “retorno” previamente salvada (guardada en el STACK o pila), de esta forma el CPU continua el programa con la instrucción siguiente a la instrucción JSR (o BSR) que originalmente llamo a la sub-rutina.

La rutina de demora de la figura 6-3 involucra un “loop interno o inherente” (INNRLP) dentro de otro loop (OUTLP) . El loop interno está formado por dos instrucciones que se ejecutan 256 veces antes que X (puntero índice) alcance el valor $00 y el salto condicional BNE falle (o sea la condición sea por igual). Esto suma la cantidad de 6 ciclos a 500 nS por ciclo ejecutados 256 veces, lo que dá un valor del loop interno de 0,768 mS cada vez que el mismo es ejecutado. El loop externo se ejecuta 65 veces. El tiempo total de ejecución para el loop externo es de 65 x (1536 + 9) o 100.425 ciclos que dan un total de 50,212 mS.

Las instrucciones “misceláneas” en esta sub-rutina de demora suman un total de 21 ciclos, lo que arroja un tiempo total para la rutina DLY50 de 50,223 mS incluyendo el tiempo requerido por la instrucción JSR que llama a DLY50.

El sistema de timer (temporizador) incluido dentro del MCU MC68HC705J1A también se puede usar para medir tiempos. El uso de timers es siempre aconsejable, ya que el CPU puede realizar otras tareas durante la demora o temporización, y el tiempo de la demora no es afectado por el número exacto de instrucciones ejecutadas como lo es en el ejemplo en DLY50.

Listado Assembler.

Después de que un programa completo o sub-rutina se escribe, debe convertirse ello desde los mnemónicos al código binario de máquina que el CPU pueda luego ejecutar. Un sistema de computación separado tal como una PC (Personal Computer o computadora personal), se usa para convertir ello en un lenguaje de máquina. El programa para realizar dicha conversión se denomina “ensamblador o Compilador Assembler”. El ensamblador lee la versión en mnemónico del programa (también llamada la versión “fuente” o “Source” del programa) y produce una versión código de máquina del programa en el formato que pueda ser programado dentro de la memoria del MCU.

El ensamblador también produce un listado “compuesto” que muestra al mismo tiempo el programa original en mnemónicos y la traslación en código objeto. Este listado se usa durante la fase de depuración del programa y es parte de la documentación del programa de software.

El listado 6-1, muestra como resulta el “ensamblado” del programa de ejemplo. Los comentarios fueron agregados antes de ejecutar el programa de ensamblado.

Microcontroladores

Listado 6-1 – Listado Assembler.

Ahora nos referiremos a la figura 6-4 para la siguiente discusión. Esta figura muestra algunas líneas del listado con números de referencia indicando varias partes de la línea. La primera línea es un ejemplo de una “línea de directiva” del assembler. Esta línea no es realmente parte del programa; sin embargo, esta provee información al ensamblador de tal forma que el programa real pueda ser correctamente convertido en código binario de máquina.

Microcontroladores

Fig. 6-4 – Explicación del listado Assembler.

EQU, es la forma corta de escribir EQUATE, se usa para proporcionar una ubicación específica de memoria o a un número binario dotarlo de un nombre que pueda usarse en otra instrucción de programa. En este caso, la directiva EQU está siendo usada para asignarle el nombre PORTA al valor $00, el cuál es la dirección del registro del puerto A en el MC68HC705J1A. De esta forma, es más fácil para el programador, recordar el mnemónico con el nombre PORTA que un valor numérico anónimo de $00. Cuando el ensamblador encuentra uno de estos nombres, el nombre es automáticamente reemplazado por su correspondiente valor binario de la misma forma que un mnemónico es reemplazado por un código de instrucción binario.

La segunda línea mostrada en la figura 6-4 es otra directiva del ensamblador. El mnemónico ORG, el cuál es la forma corta de escribir ORIGINATE, le dice al ensamblador donde el programa comenzará (la dirección del comienzo de la primera instrucción siguiente a la directiva ORG). Más de una directiva ORG puede usarse en un programa para decirle al ensamblador donde poner diferentes partes de un programa en lugares específicos de la memoria. Referirse al mapa de memoria del MCU elegido para seleccionar un localización apropiada de memoria donde el programa debería comenzar.

En el listado de assembler dado como ejemplo, los dos primeros campos, [1] y [2], son generados por el ensamblador, y los últimos 4 campos, [3], [4],[5], y [6], son del programa fuente original escrito por el programador. El campo [3] es una etiqueta o label (TOP) la cuál puede ser referida por otra instrucción dentro del programa. En nuestro programa de ejemplo, la última instrucción fue “BRA TOP”, la cuál simplemente significa que el CPU continuará ejecutando las instrucciones dentro del loop marcado por esa etiqueta “TOP”.

Cuando el programador está escribiendo un programa, las direcciones donde las instrucciones serán alojadas no son típicamente conocidas. Peor aun, en las instrucciones de salto, raramente se usa la dirección de destino, el CPU usa un “offset” (diferencia) entre el valor corriente del PC (Program Counter) y la dirección de destino. Afortunadamente, el programador no debe preocuparse por esos problemas, porque el ensamblador toma cuenta de esos detalles por medio de un sistema de “etiquetas”.

Este sistema de etiquetas es una manera conveniente para el programador de identificar puntos específicos en el programa (sin conocer las direcciones exactas); el ensamblador puede luego convertir esas etiquetas mnemónicos en direcciones específicas de memoria y siempre calcular los offsets para las instrucciones de salto que el CPU puede utilizar luego.

El campo [4] es una instrucción de campo. El mnemónico LDA es la forma corta de escribir LOAD ACCUMULATOR (carga del acumulador). Como hay 6 variantes (diferentes códigos) de la instrucción “load accumulator”, se requiere información adicional antes que el ensamblador pueda elegir el código binario correcto para que el CPU utilice durante la ejecución del programa. El campo [5] es el campo del “operando”, que provee información sobre locación específica de memoria o valores a ser operados por la instrucción. El ensamblador usa ambos, el mnemónico de instrucción y el operando especificado en el programa fuente para determinar el código específico para la instrucción.

Las diferentes maneras de especificar el valor a ser operado es llamado “modos de direccionamiento”(una más completa discusión al respecto, fue presentada en el capítulo 5).

La sintaxis del campo de operando es sutilmente diferente para cada modo de direccionamiento de esta forma el ensamblador puede determinar el código correcto para cada modo de direccionamiento desde la sintaxis del operando. En este caso, el operando [5] es PORTA, el cuál el ensamblador convierte en forma automática a $00 (re-llamando a directiva EQU). El ensamblador interpreta $00 como un modo de direccionamiento directo entre $0000 y $00FF, de este modo selecciona el código (opcode) $B6,el cuál es la variación correspondiente al modo de direccionamiento directo de la instrucción LDA.

Si PORTA fuera precedida por el símbolo “#”, la sintaxis debería ser interpretada por el ensamblador como un modo de direccionamiento “inmediato”, y el código $A6 debería ser elegido en lugar de $B6.

El campo [6] es conocido como el campo de comentarios y no es utilizado por el ensamblador para trasladar el programa en código de máquina. En lugar de ello, el campo de comentarios es usado por el programador para documentar el programa. Aunque el CPU no utilice esta información durante la ejecución del programa, un buen programador sabe que ello es una de las partes más importantes de un buen programa. El comentario [6] para esa línea del programa dice “; Lee sw en el LSB del port A”. Este comentario le dice a alguien que está leyendo el listado porque el port A está siendo leído, lo cuál es esencial para entender como trabaja el programa. El punto y coma (;) indica que el resto de la línea debería ser tratado como un comentario (no todos los ensambladores requieren el “;” ) .

Una línea entera puede ser comentario usando un asterisco “*” como primer carácter de la línea. Además de utilizar buenos comentarios en un programa, es bueno recomendar el uso de diagramas de flujo u otro tipo de anotaciones que sirva para entender el flujo de ejecución de un programa.

Archivo de Código Objeto.

Nosotros aprendimos en el capítulo 4 que la computadora espera que el programa sea una serie de valores de 8 bits en memoria. Nada tan alejado de ello en su aspecto, ya que nuestro programa fue escrito para personas. La versión que la computadora necesita cargar en su memoria es llamada “archivo de código objeto” (en inglés, Object Code File).

Para los microcontroladores Freescale, la forma más común de archivo de código objeto es el archivo “S-Record”. El ensamblador puede generar en forma directa un archivo de listado y / o un archivo de código objeto.

Un archivo S-record es un archivo de texto que puede ser leído por un editor de texto o un procesador de palabras. Usted no debería tratar de editar este tipo de archivo, porque la estructura y el contenido de este archivo son críticos para el buen funcionamiento del mismo. Cada línea de un archivo S-record es un registro. Cada registro comienza con una S mayúscula seguida por un número de código desde 0 hasta 9. Los únicos números de código que son importantes para nosotros son S0, S1 y S9. S0 es un registro encabezado opcional (header record) que puede contener el nombre del archivo para beneficio del personal humano que necesita mantener dicho archivo. S1 es un registro que contiene los datos principales del programa. Un registro S9 se usa para marcar el fin de un archivo S-record.

Para el trabajo que estamos haciendo con microcontroladores de 8 bits, la información en un registro S9 no es importante, pero un registro S9 siempre es requerido en el fin de nuestros archivos S-record. La figura 6-5 nos muestra la sintaxis de un registro S1.

Microcontroladores

Fig. 6-5 – Sintaxis de un Archivo S1 – Record.

Todos los números en un archivo S-record están en hexadecimal. Nosotros usaremos tipos de campos S0, S1 y S9 para nuestros archivos S-record (1er campo).El largo del campo es el número de pares de dígitos hexadecimales en el registro excluyendo los campos de tipo y longitud. El campo de dirección es una dirección de 16 bits donde el primer byte de dato será almacenado en memoria. Cada par de dígitos hexadecimales en el campo de datos de código de máquina representa un valor de dato de 8 bits a ser almacenado en sucesivas posiciones de memoria. El campo de “Checksum” es un valor de 8 bits que representa el complemento a uno de la suma de todos los bytes en el archivo S-record a excepción de los campos de tipo y checksum respectivamente. Este Checksum se usa durante la carga de un archivo S-record para verificar que los datos están completos y correctos en cada registro.

La figura 6-6 es el archivo S-record resultante de compilar (pasar por el ensamblador) el programa de ejemplo del listado 6-1. Los dos bytes de dato de código de máquina que están resaltados son los mismos dos bytes que fueron resaltados en la figura 4-2 y el texto que sigue a la figura 4-2. Estos bytes fueron localizados buscando en el listado y viendo que la dirección donde comienza esa instrucción era $0323. En el archivo S- record encontramos el registro S1 con la dirección $0320. Moviéndonos a la derecha encontramos el dato $23 para la dirección $0320, $20 para la dirección $0321, $E3 para $0322, y finalmente el byte que buscamos para la dirección $0323 y $0324.

Microcontroladores

Fig. 6-6 – Archivo S- Record para el Programa de Ejemplo

Directivas del Ensamblador.

En esta sección se discutirán seis de las más importantes directivas del programa ensamblador. Los ensambladores de los diferentes vendedores, difieren en el número y clase de directivas soportadas por cada uno de ellos. Debe siempre referirse al manual de usuario o documentación pertinente para conocer las particularidades del ensamblador en uso.

ORIGINATE (ORG).

Esta directiva se usa para “setear” (fijar, establecer) el contador de locación para el ensamblador. El contador de locación mantiene el seguimiento de la dirección donde el próximo byte de código de máquina será almacenado en memoria. En nuestro programa de ejemplo había una directiva ORG que fijaba el comienzo de nuestro programa en $0300.

Como el ensamblador traslada sentencias de programa en instrucciones código de máquina y dato, el contador de locación es avanzado a apuntar a la próxima locación en memoria disponible.

Todo programa tiene, al menos, una directiva ORG que establece el lugar de comienzo en memoria para el programa. Programas más completos también tendrán una segunda directiva ORG cerca del fin del programa para establecer el contador de locación a la dirección donde están localizados los “vectores de Reset e Interrupciones” ($07F8 - $07FF en el MC68HC705J1A). El Reset Vector (vector de Reset) debe SIEMPRE especificarse y es una buena práctica también especificar los vectores de interrupciones aún si no se usaran los mismos. Un error muy común en los programadores noveles (recién iniciados) es omitir los vectores de Reset e interrupciones, originando de esta forma que el ensamblador no incluya los mismos en la memoria de programa del MCU. Este error genera que el MCU al ser alimentado y salir de la etapa de Power On Reset (inicialización interna) busque el vector de Reset con un contenido frecuente de $0000 (memoria de programa virgen del MCU tipo OTP ROM ) o $FFFF (memoria de programa virgen del MCU tipo HC908 FLASH) que NO SON POSICIONES DE MEMORIA DE PROGRAMA VALIDAS y dan como resultado que el MCU quede en un loop errático sin posibilidad de salir de él.

EQUATE (EQU).

Esta directiva se usa para asociar un valor binario con una “etiqueta” o “label”. El valor puede ser tanto un valor de 8 bits de longitud como una dirección de 16 bits. Esta directiva NO genera un código objeto.

Durante el proceso de ensamblado, el ensamblador debe mantener una lista cruzada de referencia donde este almacena el equivalente binario de cada etiqueta. Cuando una etiqueta aparece en el programa fuente, el ensamblador mira en esta tabla cruzada de referencia para encontrar el equivalente binario. Cada directiva EQU genera una entrada en la tabla cruzada de referencia.

Un ensamblador lee el programa fuente dos veces. En la primera pasada, el ensamblador cuenta bytes del código objeto e internamente construye la tabla cruzada de referencia. En la segunda pasada el ensamblador genera un archivo listado y / o el archivo objeto S-record.

Este arreglo de dos pasadas permite al programador generar etiquetas de referencia que son definidas más tarde en el programa.

Las directivas EQU deberían aparecer cerca del comienzo de un programa, antes que las etiquetas allí definidas sean usadas por otros pasos del programa. Si el ensamblador encuentra una etiqueta antes que esta sea definida, este no tiene elección pero asume el peor caso de un valor de 16 bits de dirección. Esto causaría que se use el modo de direccionamiento extendido en lugar de un modo más eficiente como el de direccionamiento directo donde podría ser usado.

En otros casos, el modo de direccionamiento indexado con 16 bits de offset, podría ser usado por el ensamblador, en donde un direccionamiento indexado de 8 bits o un indexado sin offset mucho más eficiente podría usarse.

En el programa de ejemplo donde había dos directivas EQU para definir las etiquetas PORTA y DDRA a sus páginas de direccionamiento directas. Otro uso de las directivas EQU es el de identificar la posición de un bit con una etiqueta como esta.

Microcontroladores

El símbolo % (porcentaje) indica que el valor que sigue está expresado en binario. Si movemos el LED a otro pin durante el desarrollo solo necesitaríamos cambiar la sentencia EQU y re-ensamblar el programa.

Formulario Bytes de Constantes (FCB).

Los argumentos para esta directiva son etiquetas o números separados por comas, que pueden ser convertidos en un solo byte de datos. Cada byte especificado en una directiva FCB, genera un byte de código de máquina en el archivo de código objeto. Las directivas FCB se usan para definir constantes en un programa, como por ejemplo una tabla de constantes, etc. .

Formulario de doble bytes (FDB).

Los argumentos para esta directiva son etiquetas o números separados por comas, que pueden ser convertidos en valores de 16 bits de datos. Cada argumento especificado en una directiva FDB, genera dos bytes de código de máquina en el archivo de código objeto.

Las siguientes líneas de un listado assembler demuestran las directivas ORG y FDB.

Microcontroladores