
Buenas tardes Hoy quiero decirles cómo escribir un programa mínimo que se ejecute en un ARM Cortex-M3 e imprima "¡Hola, Mundo!". Intentaremos distinguir en pasos el mínimo necesario que necesitamos para esto. Vamos a ejecutar el emulador QEMU. Por lo tanto, cualquiera puede reproducirse, incluso si no tiene un trozo de hierro a mano.
¡Entonces vamos!
El emulador QEMU es compatible con el núcleo Cortex-M3 y emula la plataforma Stellaris LM3S811 de Texas Instruments basada en él. Vamos a correr en esta plataforma. Necesitamos la cadena de herramientas arm-none-eabi- (puede descargarla
aquí ). A continuación, debemos escribir la lógica principal de nuestro programa, el código de inicio, que transferirá el control al programa y el script del vinculador.
En un habr ya hay
artículos bastante
buenos sobre cómo parpadear un diodo en una pieza de hierro desde cero. Por lo tanto, aquí no profundizaré en qué y cómo funciona, sino que daré solo el conjunto mínimo de conocimientos necesarios para comenzar.
Nuestro hola mundo en el archivo test.c:
static volatile unsigned int * const UART_DR = (unsigned int *)0x4000c000; static void uart_print(const char *s) { while (*s != '\0') { *UART_DR = *s; s++; } } void c_entry(void) { uart_print("Hello, World!\n"); while (1) ; }
Esta misma dirección 0x4000c000 se toma de la documentación, allí se encuentra el registro DR cero cero.
No participaremos en la configuración del UART (esto deberá hacerse en el hardware), pero intentaremos colocar los símbolos directamente en él.
Ahora, necesitamos transferir de alguna manera el control a nuestra función with_entry en el archivo test.c. Para hacer esto, cree el siguiente código (archivo startup.S) y luego póngalo en la imagen ELF final al principio.
.type start, %function .word stack_top .word start .global start start: ldr r1, =c_entry bx r1
La primera palabra en 0x0 debe ser un puntero a la parte superior de la pila (SP). En 0x4 hay una PC que, como SP, se carga en los registros. Tenga en cuenta que start se declara precisamente como una función, y no como una etiqueta porque el código Cortex-M se ejecuta en modo Thumb (este es un conjunto tan simplificado de comandos ARM), y se requiere que las direcciones de las funciones en el vector de interrupción estén en la forma (dirección | 0x1) - es decir el último bit de la dirección debe ser 1.
Luego, la función de inicio simplemente carga la dirección de nuestra función c_entry () del archivo test.c y pasa el control a través de "bx r1" allí.
Solo queda vincular con éxito nuestro programa. Para hacer esto, debe configurar la tarjeta de memoria de nuestro microcontrolador. En la documentación puede encontrar las direcciones y tamaños de memoria flash (ROM) y RAM (RAM). Aquí está el script del enlazador test.ld:
SECTIONS { . = 0x0; .text : { startup.o(.text) test.o(.text) } . = 0x20000000; .data : { *(.data) } .bss : { *(.bss) } . = ALIGN(8); . = . + 0x1000; stack_top = .; }
Es importante prestar atención a las direcciones. "." en la secuencia de comandos del vinculador indica la posición actual. Ponemos la sección .text al comienzo de la ROM (dirección 0x0), siguiendo el orden: startup.o (.text) va primero. A continuación, vaya a RAM (. = 0x20000000;) y coloque allí los datos (datos globales inicializados) y bss (datos globales no inicializados). A continuación vemos ALIGN (8): ARM requiere la alineación del SP (Stack Pointer) por 8. Dado que la pila crece, la asignación de espacio debajo de la pila es solo una adición ”. =. + 0x1000 ". Conocemos bien nuestro programa, por lo que una pila de 4kB es suficiente con un gran margen.
Eso es todo, queda por ponerlo todo junto. Traigo build.sh:
Todo aquí debe entenderse más o menos, con la excepción de la bandera libre. En este caso, no tiene que agregarlo (puede verificarlo), pero como estamos preparando una imagen de metal desnudo desde cero, es mejor decirle al compilador para que no preste atención a funciones como main ().
Como resultado, obtuvimos el archivo ELF test.elf. Ejecútelo en QEMU:
$ qemu-system-arm -M lm3s811evb -kernel test.elf -nographic Hello, World!
Funciona
Por supuesto, este es un estudio de caso diseñado para comprender lo que está sucediendo. Si necesita una funcionalidad más significativa, debe usar cosas preparadas. Hemos agregado soporte para esta plataforma en
Embox . Esta plantilla se llama platform / stellaris / lm3s811evb. Por lo tanto, si alguien quiere intentar ejecutar algo un poco más serio (consola, temporizador, interrupciones), puede recopilar e intentar. En este caso, repito, no es necesario tener una placa de hardware.
Y aquellos que todavía no tienen suficientes emuladores, o que quieren hacernos preguntas y jugar con las
piezas de hierro, los esperaremos este sábado y domingo en el festival de tecnología
techtrain.ru en San Petersburgo. Tendremos varias piezas de hierro en el stand, y en la zona de demostración intentaremos decir cómo programarlas.