Autor: Fernando Rafael Filipuzzi
E-mail: fernando_6867@yahoo.com.ar
Aquí he traducido y agregado algunas cosas en el documento del ejemplo dado en mc68hc11.serveftp.org
Algunos puntos importantes antes de empezar:
Se necesita tener lo siguiente:
ToolChain GNU 68HC1X |
Librerias y ejemplos GNU para los 68HC11 y 68HC12 (GEL) Contiene los fuentes del ejemplo hello. |
Este capítulo ilustra a través de un ejemplo como puedes construir un programa bootstrap 68HC11, compilarlo, enlazarlo y tenerlo corriendo en una "plataforma de desarrollo" (target board), en el depurador GNU o el simulador.
Una vista rápida del ejemplo hello. | |
Presenta paso por paso el ejemplo. | |
Explica como compilar y enlazar el ejemplo. | |
Corriendo el ejemplo. |
Esta sección describe paso por paso las diferentes partes del ejemplo 'hello.c'
.
El ejemplo hello escribe un mensaje en la linea serial. Para hacer esto tenemos que controlar el SCI del 68HC11 y debemos acceder a los puerto I/O y controlarlos luego.
Una primera parte define varias banderas y valores que representan los registros SIO del 68HC11. La lista completa de banderas están disponibles cuando incluyas el fichero '<sys/ports.h>'
y es recomendado usar lo siguiente en tu propios programas:
#include <sys/ports.h>
Las banderas son definidas en '<asm-m68hc11/ports_def_F.h>'
, '<asm-68hc11/ports_def_E.h>'
or '<asm-m68hc12/ports_def.h>'
dependiendo del microcontrolador.
Debajo está la lista de banderas usadas por el ejemplo hello (varias banderas SCI han sido removidos para mayor claridad).
#define M6811_BAUD 0x2B /* SCI Baud register */ #define M6811_SCCR1 0x2C /* SCI Control register 1 */ #define M6811_SCCR2 0x2D /* SCI Control register 2 */ #define M6811_SCSR 0x2E /* SCI Status register */ #define M6811_SCDR 0x2F /* SCI Data (Read => RDR, Write => TDR) */ /* Flags of the SCCR2 register. */ #define M6811_TE 0x08 /* Transmit Enable */ #define M6811_RE 0x04 /* Receive Enable */ /* Flags of the SCSR register. */ #define M6811_TDRE 0x80 /* Transmit Data Register Empty */ /* Flags of the BAUD register. */ #define M6811_SCP1 0x20 /* SCI Baud rate prescaler select */ #define M6811_SCP0 0x10 #define M6811_SCR2 0x04 /* SCI Baud rate select */ #define M6811_SCR1 0x02 #define M6811_SCR0 0x01 #define M6811_BAUD_DIV_1 (0) #define M6811_BAUD_DIV_3 (M6811_SCP0) #define M6811_BAUD_DIV_4 (M6811_SCP1) #define M6811_BAUD_DIV_13 (M6811_SCP1|M6811_SCP0) #define M6811_DEF_BAUD M6811_BAUD_DIV_4 /* 1200 baud */
Los registros SCI 68HC11 pueden ser accedidos en C leyendo y escribiendo la memoria. En el programa, los puertos I/O son representados por arrays de bytes los cuales son mapeados en direcciones fijas (given fixed address). Estos array comienzan en el principio del mapa de registros I/O. Esto es declarado como externo así que las direcciones son definidas en tiempo de enlace.
extern volatile unsigned char _io_ports[ ];
La palabra clave volatile es importante, le dice a GCC evitar cualquier optimización cuando lea la memoria. Esto es necesario, de otro modo GCC simplemente elimina el bucle de espera ocupada para detectar que el transmisor está listo.
Por ejemplo para escribir el registro SSR2 del SCI haremos lo siguiente:
_io_ports[M6811_SCCR2] = M6811_TE;
Mientras que para leer el registro SCCR1 del SSI haremos lo siguiente:
unsigned char flags = _io_ports[M6811_SCSR];
Una segunda parte define algunas funciones para escribir caracteres en el SIO. Estas funciones acceden al registro IO a través de la variable global '_io_ports'
.
Estas operaciones pueden ser usadas en tu programa usando el include siguiente:
#include <sys/sio.h>
Una primera operación denominada 'serial_send'
es definida al enviar el carácter en la linea serie. Esta función es definida como sigue:
static inline void serial_send (char c) { /* Wait until the SIO has finished to send the character. */ while (!(_io_ports[M6811_SCSR] & M6811_TDRE)) continue; _io_ports[M6811_SCDR] = c; _io_ports[M6811_SCCR2] |= M6811_TE; }
Esto envía el carácter 'c'
en la linea serial. Después de enviar, este espera para que el transmisor esté disponible. Una vez que la función retorna, el carácter está en el cola del SCI y este no puede haber sido enviado completamente sobre la linea serial.
Una segunda función llamada 'serial_print'
usa 'serial_send'
para enviar un string completo sobre la linea serial. Este itera sobre la cadena de caracteres y envía cada uno de los caracteres hasta el final de la cadena representándo a este final con un 0.
void serial_print (const char *msg) { while (*msg != 0) serial_send (*msg++); }
La última parte representa la función principal. En C y en C++, la función principal debe ser llamada 'main'
. Cuando se entra en esta función 'main'
, la pila es inicializada, las variables globales son inicializadas pero el SIO también como otros dispositivos 68HC11 no son todavía inicializados. En este ejemplo, primero configuraremos el SIO fijando el baud rate y el formato de caracter. El SIO debe entonces ser inicializado habilitando al transmisor y receptor.
La inicialización es inlined aquí por claridad pero es implementada por la operación 'serial_init'
disponible cuando el fichero incluido '<sys/sio.h>'
es usado.
Una vez que el SIO es inicializado, podemos escribir mensajes usando 'serial_print'
.
int main () { /* Configure the SCI to send at M6811_DEF_BAUD baud. */ _io_ports[M6811_BAUD] = M6811_DEF_BAUD; /* Setup character format 1 start, 8-bits, 1 stop. */ _io_ports[M6811_SCCR1] = 0; /* Enable receiver and transmitter. */ _io_ports[M6811_SCCR2] = M6811_TE | M6811_RE; serial_print ("Hello world!\n"); return 0; }
En este ejemplo, la función 'main'
retornar´ y devolverá el control al código startup que a su vez llama a 'exit'
. El 'exit'
quedaró dando vueltas siempre alrededor de una instrucción wait. Verdaderamente, un hardware 68hc11 real no tiene un modo de cerrar!
Ahora que tenemos el fichero fuente en C, necesario para compilar, ensamblar y enlazarlo. La compilación es el proceso por el cual las instrucciones 68HC11 son generadas desde el fichero fuente 'hello.c'
. El ensamblado es el proceso en el cual transforma las instrucciones 68HC11 en código binario 68HC11. El enlazado es el proceso final el cual colecta el conjunto de todos los ficheros binarios, organiza luego en la memoria y asigna luego las direcciones finales. Estos procesos pueden ser corrido en comandos separados o con un simple comando.
Para compilar el programa, usarás el controlador (driver) 'm6811-elf-gcc'
(m68ch11-gcc). Este controlador involucra el pre-procesador, el compilador, el ensamblador y el enlazador dependiendo en las opciones y ficheros que des como argumentos. Para el ejemplo, seguirás los siguientes comandos.
m6811-elf-gcc -g -Os -mshort -Wl,-m,m68hc11elfb -o hello.elf hello.c m68hc11-gcc -g -Os -mshort -Wl,-m,m68hc11elfb -o hello.elf hello.c (en debian)
El controlador (gcc) usa la extensión del archivo para decidir que debe hacer para un fichero dado. Para el fichero 'hello.c'
el driver usará el compilador C. Nosotros usaremos las siguientes opciones para controlar los pasos de compilación y ensamblado:
-g | Genera información de depuración simbólica |
-Os | Optimiza para el espacio de velocidad |
-mshort | Usar para enteros de 16-bit (tipo int) |
Una vez que el fichero 'hello.c'
es compilado y ensamblado, el driver (gcc) correrá el enlazador . El paso del enlazador es un proceso non-trivial aunque este ha sido simplificado. Un rol importante del enlazador es mapear el programa en memoria y asignar a cada símbolo una dirección. Para decir al enlazador donde debe poner el programa nosotros usaremos el fichero 'memory.x'
describiendo la memoria. Este fichero es utiliza por el enlazador cuando usamos la opción '-Wl,-m,m68hc11elfb'
. Este contiene las siguientes definiciones:
MEMORY { page0 (rwx) : ORIGIN = 0x0, LENGTH = 256 text (rx) : ORIGIN = 0x0, LENGTH = 256 data : ORIGIN = 0x0, LENGTH = 0 }
La parte 'MEMORY'
describe las regiones de memoria que la placa (board) provee. El ejemplo usa el modo bootstrap 68HC11 como este está disponible para mas placas objetivos (target boards). Esto no depende de las características de RAM y ROM de la placa. En el modo bootstrap, podemos confiar el mapeado de la RAM interna del 68hc11 en la dirección 0. La definición importante para esto es que la región 'text'
. Esta indica que comienza en la 0 y puede contener 256 bytes. La bandera 'rx'
indica que la región 'text'
es a la vez de lectura ('r'
) y ejecutable ('x'
). La región 'text'
es usada por el enlazador para poner código de programa en esta. La región 'data'
es usado por el enlazador para poner variables globales y estáticas. En este ejemplo, definimos la región como vacía así que podemos detectar problemas en el uso en el modo bootstrap.
El fichero 'memory.x'
también define las siguientes partes:
PROVIDE (_stack = 0x0100-1); PROVIDE (_io_ports = 0x1000);
Estas dos definiciones crean dos símbolos nombrados '_stack'
y '_io_ports'
y asignan luego un valor. El símbolo '_stack'
representa el top de la pila y el símbolo '_io_ports'
representa los registros I/O 68HC11 que usamos en el programa. Los registros I/O son mapeados en la dirección 0x1000 por defecto.
Para el paso del enlazador, las siguientes opciones son importantes:
-Wl,-m,m68hc11elfb |
Dice al enlazador usar una configuración de enlace basada en el archivo especifico 'memory.x' . m68hc12elfb para hc12. |
-o hello.elf |
Dice al Enlazador usar 'hello.elf' como nombre de fichero de salida. |
-mshort |
Usa enteros de 16-bit (tipo int). Esto es no realmente usado por el enlazador pero necesariamente para un enlace final así 'm6811-elf-gcc' de a el enlazador el correcto codigo startup y las librerías compatibles con el programa. |
Ahora que el programa hello esta compilado y enlazado, necesita probar y poder correrlo en alguna parte. Hay varios modos de correr el ejemplo:
2.4.1 Simulador
El simulador es el modo mas rápido y fácil para probar el ejemplo. No necesita tener un plataforma de desarrollo. Solo tipiando el siguiente comando:
m6811-elf-run hello.elf
El simulador crear un 68HC11 virtual corriendo en 8Mhz y simula todas sus instrucciones con los devices internos. El programa imprime lo siguiente:
Hello world!
2.4.2 Depurador GNU
El depurador GNU puede ser usado para probar y depurar el ejemplo. Provee el simulador y permitir el depurado a nivel de fuente con esto. El proceso es mas complejo pero el resto es mas facil. Comienza el depurador con lo siguiente:
m6811-elf-gdb hello.elf
Luego, debes decirle al GDB que conecte a el simulador. Esto se hace usando el comando 'target sim':
(gdb) target sim
El simulador está disponible y el programa debe ser cargado en memoria. Esto se hace con el comando 'load':
(gdb) load
Podemos ahora ejecutar el programa comenzando el simulador:
(gdb) run
El simulador imprime el mensaje hello en la consola de salida GDB.
2.4.3 Plataforma 68HC11
Usando una plataforma 68HC11 real es un poco mas complejo. Primero debes configurar tu plataforma para comenzar en el modo bootstrap 68HC11.
Ahora, debes conectar la plataforma 68HC11 y el host con el cable serial. La linea serial debe ser configurada en 100 baud, sin paridad y un bit de stop. No Debes usar el flujo de control RTS/CTS.
Para cargar el programa deberas usar un programa cargador en el host. Algunos cargadores permiten usar el archivo ELF 'hello.elf' directamente mientras algunos otros pueden solamente leer el archivo 'hello.s19'.
loader -d /dev/ttyS0 hello.elf