STM32 Parte 1: Lo básico

No puede confiar en el código que no escribió completamente usted mismo. - Ken Thompson
Quizás mi cita favorita. Fue ella quien se convirtió en la razón por la que decidí sumergirme en las profundidades de la madriguera del conejo. Comencé mi viaje al mundo de la programación hace muy poco, solo pasó un mes y decidí escribir artículos para consolidar el material. Todo comenzó con una tarea simple, sincronizar las lámparas en su estudio fotográfico usando Arduina. El problema se resolvió, pero ya no entré al estudio fotográfico, no hay tiempo. Desde ese momento, decidí participar a fondo en la programación de microcontroladores. Arduin, aunque atractivo en su simplicidad, no me gustó la plataforma. La elección recayó en la empresa ST y sus productos populares. En ese momento, todavía no tenía idea de cuál era la diferencia, pero como consumidor típico comparé la velocidad del "procesador" y la cantidad de memoria, me compré una placa impresionante con una pantalla STM32F746NG: Discovery.Extrañaré los momentos de desesperación y voy directo al grano.

Inmerso en la imagen de un programador, leí mucho, estudié, experimenté. Y como ya describí anteriormente, quería estudiar bien, eso es todo. Y para esto establecí una meta, no ninguna solución preparada, solo la mía. Y si todo salió bien para mí, entonces tendrás éxito.

Lista de todo lo que necesitas:

  1. Ubuntu 16+ máquina virtual o lo que sea
  2. compilador de brazo : descárguelo en developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads
  3. depurador y programador de openocd : no podrá descargar desde el enlace, recopilamos de las fuentes, se adjuntan las instrucciones

    git clone https://git.code.sf.net/p/openocd/code openocd
  4. Editor de texto a tu gusto

Después de que todo esté instalado y ensamblado, ¡podemos proceder al primer proyecto! Y no, ni siquiera es una bombilla parpadeante. Para empezar, debemos profundizar en el proceso de inicialización del propio micropocillo.

Lo que necesitamos:

  1. Makefile
  2. Linker.ld
  3. Init.c

Comencemos con el último párrafo Init.c. En primer lugar, nuestro mk debe cargar la dirección "puntero de pila" es un puntero a la dirección de memoria que se utiliza para las instrucciones PUSH y POP. Le recomiendo que estudie detenidamente estas dos instrucciones, ya que no explicaré en detalle todas las instrucciones. Cómo implementar esto, ver abajo.

extern void *_estack;
void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    &_estack
}

Ahora veamos este ejemplo. Extern significa que el símbolo es externo, y declaramos este símbolo en el archivo Linker.ld, volveremos a él un poco más tarde.

 __attribute__((section(".isr_vector"), used))

Aquí usamos un atributo que le dice al compilador que coloque la matriz en la sección isr_vector y que incluso si no la usamos en el código, aún debe incluirse en el programa. Y su primer elemento será ese mismo puntero.

Ahora veamos por qué es esta matriz y con qué se comerá. A diferencia de un procesador convencional, la arquitectura micron en el brazo inicia la ejecución cuando no es desde la dirección cero, sino desde la dirección apuntada por el puntero en esta matriz, todo es complicado. Además, el primer puntero en esta matriz siempre apunta al comienzo de la pila, pero el segundo ya apunta al comienzo de nuestro código.

Daré un ejemplo. se da que la pila comienza con 0x20010000 y el código del programa es 0x0800008. entonces la matriz se puede escribir como

void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    0x20010000,
    0x08000008
}

Es decir, el controlador primero inicializa la pila y luego considera la dirección de la primera instrucción y la carga en el registro del contador del programa. Ahora lo más importante, dependiendo del modelo, estos números pueden ser diferentes, pero con el ejemplo de stm32f7 puedo decir con confianza que esta matriz debería estar en la memoria en la dirección 0x08000000. Es desde esta dirección que mk comenzará su trabajo después de encender o reiniciar.

Ahora nos detendremos y prestaremos atención a cómo poner esta matriz en la sección que necesitamos. Esto se hace mediante "ld" o enlazador. Este programa recopila todo nuestro programa y el script Linker.ld se usa para esto. Doy un ejemplo y lo analizo más a fondo.

MEMORY{
	ROM_AXIM (rx) : ORIGIN = 0x08000000, LENGTH = 1M
	RAM_DTCM (rwx): ORIGIN = 0x20000000, LENGTH = 64K
}

_estack = LENGTH(RAM_DTCM) + ORIGIN(RAM_DTCM);

SECTIONS{
	.isr_vector : {
	KEEP(*(.isr_vector))
	} >ROM_AXIM</code>
}

Veamos qué pasa aquí. MEMORY define secciones de memoria y SECTIONS define secciones. aquí vemos nuestro _stack y el hecho de que es igual a la suma del comienzo de la memoria y su longitud, es decir, el final de nuestra memoria. La sección .isr_vector en la que ponemos nuestra matriz también está definida. >ROM_AXIMal final de nuestra sección significa que esta sección debe colocarse en la sección de memoria que comienza con 0x08000000 según lo requerido por nuestras micras.

Ponemos la matriz donde es necesario, ahora necesitamos algún tipo de instrucción para que nuestro micron funcione. Aquí está el init.c aumentado:

extern void *_estack;

void Reset_Handler();

void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    &_estack,
    &Reset_Handler
};

void __attribute__((naked, noreturn)) Reset_Handler()
{
    while(1);
}

Como mencioné anteriormente, la segunda dirección debe ser un puntero a la primera función o instrucción. Y nuevamente, los atributos, pero todo es simple, la función no devuelve ningún valor y sigue cualquier ABI en la entrada, es decir, desnudo “desnudo”. En tales funciones, se empuja el ensamblador habitual.

Ahora es el momento de compilar nuestro código y ver qué hay debajo del capó. No tocaremos el Makefile por ahora.

arm-none-eabi-gcc -c init.c -o init.o -mthumb

arm-none-eabi-gcc -TLinker.ld -o prog.elf init.o -Wl,--gc-sections -nostartfiles -nodefaultlibs -nostdlib

Y aquí vemos muchas cosas incomprensibles. en orden:

  1. -mthumb compila para armv7, que usa solo instrucciones de pulgar
  2. -TLinker.ld especifica el script del vinculador. de forma predeterminada, se compila para su ejecución en el entorno Linux
  3. -Wl, - gc-secciones -nostartfiles -nodefaultlibs -nostdlib elimina todas las bibliotecas estándar, los archivos de inicialización del entorno C y todas las demás bibliotecas auxiliares, como las matemáticas.

Por supuesto, no tiene sentido cargar esto en micras. Pero tiene sentido ver y estudiar el binario.

objcopy -O ihex prog.elf prog.bin

hexdump prog.bin

Y luego veremos la conclusión. "00000000: 00 01 00 02 09 00 00 08" que simbolizará nuestro éxito. Este es mi primer artículo y no pude revelar completamente todo el material y la esencia, por lo que en el siguiente describiré los mecanismos con más detalle y nosotros, los amigos, podremos ir a un programa que no parpadeará, sino que configurará el reloj del procesador y los buses.

All Articles