STM32F2: Combinación de Makefile, secuencia de comandos del enlazador y archivo de inicio sin IDE comercial

He estado trabajando con un STM32F2 (en concreto, el STM32F217IGH6 en una placa de desarrollo) durante unos dos meses. Con mucho, mi mayor problema tenía que ver con la "configuración", que incluye el archivo MAKE, la secuencia de comandos del enlazador y el archivo de inicio.

En particular, no he podido configurar correctamente mi tabla de vectores de interrupción y llamar a los controladores de interrupción. ST proporciona ejemplos adaptados a los IDE comerciales. En cambio, estoy usando la recopilación gratuita de Yagarto de la cadena de herramientas GCC (y OpenOCD para cargar la imagen a través de JTAG).

¿Existen proyectos de ejemplo para mi placa (o un primo cercano de ella) que contengan el archivo MAKE apropiado, la secuencia de comandos del enlazador y la combinación de archivos de inicio para los IDE no comerciales que están configurados para llamar a los controladores de interrupción?

Debe buscar ejemplos de Cortex M3, la placa y el procesador exactos no son tan importantes para las cosas que solicita. Probablemente necesite modificar el diseño de la memoria en el script del enlazador y el método para flashear en el archivo MAKE, pero eso debería ser todo.
¿Puedes poner todo esto en un repositorio de git y ponerlo en github o algo así?
Esta es exactamente la razón por la que dejé de usar el STM tan pronto como lo probé. Si me van a complicar la vida con la desagradable cadena de herramientas, entonces me iré a otro lado. Cuando probé el IDE para PSoC3 y PSoC5, fue un mundo de diferencia.
¿Estás comprometido con Yagarto? Eso está completamente bien y es una gran pregunta, pero estoy familiarizado con la cadena de herramientas de CodeSourcery Lite . Probablemente se podría adaptar una respuesta para una cadena de herramientas diferente, pero no funcionaría de inmediato.

Respuestas (2)

http://github.com/dwelch67

stm32f4 y stm32vld en particular, pero los otros también pueden serle útiles. mbed y el directorio mzero bajo mbed (cortex-m0).

Me gusta el enfoque estúpido de mantenerlo simple, scripts de enlace mínimos, código de inicio mínimo, etc. El trabajo lo realiza el código, no una cadena de herramientas en particular.

La mayoría de las formas de gcc y binutils (capaces de usar) funcionarán de alguna manera con estos ejemplos, ya que uso el compilador para compilar, no como un recurso para las llamadas a la biblioteca, no uso los scripts del enlazador de acciones, etc. Gcc y binutils más antiguos no sabrán sobre las partes thumb2 más nuevas, por lo que es posible que se requieran algunos cambios.

Construyo mi propio gcc, binutils y llvm/clang y también uso codesourcery, por ejemplo (ahora soy mentor de gráficos, pero aún puede obtener la versión gratuita/lite).

Esp, cuando comienza a armar un proyecto para un nuevo objetivo, necesita desarmar un poco. En particular, para asegurarse de que los elementos estén donde los desea, la tabla de vectores, por ejemplo.

Mire stm32f4d/blinker02 por ejemplo. Comienza con vectores.s la tabla de excepciones/vectores más algunas rutinas de soporte de asm:

/* vectors.s */
.cpu cortex-m3
.thumb

.word   0x20002000  /* stack top address */
.word   _start      /* 1 Reset */
.word   hang        /* 2 NMI */
.word   hang        /* 3 HardFault */
.word   hang        /* 4 MemManage */
.word   hang        /* 5 BusFault */
.word   hang        /* 6 UsageFault */
.word   hang        /* 7 RESERVED */
.word   hang        /* 8 RESERVED */
.word   hang        /* 9 RESERVED*/
.word   hang        /* 10 RESERVED */
.word   hang        /* 11 SVCall */
.word   hang        /* 12 Debug Monitor */
.word   hang        /* 13 RESERVED */
.word   hang        /* 14 PendSV */
.word   hang        /* 15 SysTick */
.word   hang        /* 16 External Interrupt(0) */
.word   hang        /* 17 External Interrupt(1) */
.word   hang        /* 18 External Interrupt(2) */
.word   hang        /* 19 ...   */

.thumb_func
.global _start
_start:
    /*ldr r0,stacktop */
    /*mov sp,r0*/
    bl notmain
    b hang

.thumb_func
hang:   b .

/*.align
stacktop: .word 0x20001000*/

;@-----------------------
.thumb_func
.globl PUT16
PUT16:
    strh r1,[r0]
    bx lr
;@-----------------------
.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr
;@-----------------------
.thumb_func
.globl GET32
GET32:
    ldr r0,[r0]
    bx lr
;@-----------------------
.thumb_func
.globl GET16
GET16:
    ldrh r0,[r0]
    bx lr

.end

No hay interrupciones en este ejemplo, pero las otras cosas que necesita están aquí.

blinker02.c contiene el cuerpo principal del código C con el punto de entrada C que llamo notmain() para evitar llamarlo principal (algunos compiladores agregan basura a su binario cuando tiene un main()).

te ahorrará un cortar y pegar. el archivo MAKE cuenta la historia sobre la compilación y la vinculación. Tenga en cuenta que varios de mis ejemplos compilan dos o más binarios del mismo código. compilador gcc, compilador clang de llvm, thumb only y thumb2, diferentes optimizaciones, etc.

Comience creando archivos de objetos a partir de los archivos de origen.

vectors.o : vectors.s
    $(ARMGNU)-as vectors.s -o vectors.o

blinker02.gcc.thumb.o : blinker02.c
    $(ARMGNU)-gcc $(COPS) -mthumb -c blinker02.c -o blinker02.gcc.thumb.o

blinker02.gcc.thumb2.o : blinker02.c
    $(ARMGNU)-gcc $(COPS) -mthumb -mcpu=cortex-m3 -march=armv7-m -c blinker02.c -o blinker02.gcc.thumb2.o

blinker02.gcc.thumb.bin : memmap vectors.o blinker02.gcc.thumb.o
    $(ARMGNU)-ld -o blinker02.gcc.thumb.elf -T memmap vectors.o blinker02.gcc.thumb.o
    $(ARMGNU)-objdump -D blinker02.gcc.thumb.elf > blinker02.gcc.thumb.list
    $(ARMGNU)-objcopy blinker02.gcc.thumb.elf blinker02.gcc.thumb.bin -O binary

blinker02.gcc.thumb2.bin : memmap vectors.o blinker02.gcc.thumb2.o
    $(ARMGNU)-ld -o blinker02.gcc.thumb2.elf -T memmap vectors.o blinker02.gcc.thumb2.o
    $(ARMGNU)-objdump -D blinker02.gcc.thumb2.elf > blinker02.gcc.thumb2.list
    $(ARMGNU)-objcopy blinker02.gcc.thumb2.elf blinker02.gcc.thumb2.bin -O binary

el enlazador, ld, usa un script de enlazador al que llamo memmap, estos pueden ser extremadamente dolorosos, a veces por una buena razón, a veces no. Prefiero el enfoque menos es más a la talla única, todo menos el enfoque del fregadero de la cocina.

Normalmente no uso .data (bueno, casi nunca) y este ejemplo no necesita .bss, así que aquí está el script del enlazador, solo lo suficiente para colocar el programa (.text) donde debe estar para este procesador tal como soy usándolo

MEMORY
{
    ram : ORIGIN = 0x08000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > ram
}

Tengo una región de memoria para definir eso, no hay nada especial en el nombre ram, puede llamarlo foo, bar, bob o ted, no importa, solo vincula los elementos de memoria a las secciones. Las secciones definen cosas como .text, .data, .bss, .rodata y dónde van en el mapa de memoria.

cuando construyes esto, ves que desarmo todo (objdump -D) ves esto

Disassembly of section .text:

08000000 <_start-0x50>:
 8000000:       20002000        andcs   r2, r0, r0
 8000004:       08000051        stmdaeq r0, {r0, r4, r6}
 8000008:       08000057        stmdaeq r0, {r0, r1, r2, r4, r6}
 800000c:       08000057        stmdaeq r0, {r0, r1, r2, r4, r6}
 8000010:       08000057        stmdaeq r0, {r0, r1, r2, r4, r6}

La clave a tener en cuenta es que la dirección de la izquierda es donde la queríamos, el código de vectores.s es el primero en el binario (porque es el primero en la línea de comando ld, a menos que haga algo en el script del enlazador, los elementos se mostrarán up en el binario en el orden en que están en la línea de comando ld). Para arrancar correctamente, debe asegurarse de que su tabla de vectores esté en el lugar correcto. El primer elemento es mi dirección de pila, eso está bien. El segundo elemento es la dirección de _start y debe ser un número impar. el uso de .thumb_func antes de una etiqueta hace que esto suceda para que no tengas que hacer otras cosas feas.

08000050 <_start>:
 8000050:       f000 f822       bl      8000098 <notmain>
 8000054:       e7ff            b.n     8000056 <hang>

08000056 <hang>:
 8000056:       e7fe          

por lo que 0x08000051 y 0x08000057 son las entradas de vector adecuadas para _start y hang. iniciar llamadas notmain()

08000098 <notmain>:
 8000098:       b510            push    {

Eso se ve bien (no muestran la dirección impar en el desmontaje).

Todo está bien.

Pase al ejemplo blinker05, este admite interrupciones. y necesita algo de RAM, por lo que se define .bss.

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x100000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1C000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .bss  : { *(.bss*) } > ram
}

Recuerde que ram y rom son nombres arbitrarios, bob y ted, foo y bar, todos funcionan bien.

No voy a mostrar los vectores completos porque el cortex-m3 tiene un trillón de entradas en la tabla de vectores si hace una completa (varía de un núcleo a otro y tal vez dentro del mismo núcleo dependiendo de las opciones elegidas por el proveedor del chip) Las partes relevantes están aquí después del desmontaje:

08000000 <_start-0x148>:
 8000000:       20020000        andcs   r0, r2, r0
 8000004:       08000149        stmdaeq r0, {r0, r3, r6, r8}
 8000008:       0800014f        stmdaeq r0, {r0, r1, r2, r3, r6, r8}
...
8000104:       0800014f        stmdaeq r0, {r0, r1, r2, r3, r6, r8}
 8000108:       08000179        stmdaeq r0, {r0, r3, r4, r5, r6, r8}
 800010c:       0800014f        stmdaeq r0, {r0, r1, r2, r3, r6, r8}

toma un poco de prueba y error para colocar ese controlador exactamente en el lugar correcto, verifique con su chip dónde debe estar, no es necesariamente en el mismo lugar que este, y con tantas interrupciones es posible que esté buscando una interrupción diferente de todos modos. los procesadores cortex-m, a diferencia de los brazos normales, hacen que no NECESITE un código de trampolín para las interrupciones, conservan una cierta cantidad de registros y administran el cambio de modos del procesador a través del contenido del registro de enlace. siempre que el hardware y el abi para el compilador estén lo suficientemente cerca, todo funciona. En este caso, hice el controlador en C, a diferencia de otras plataformas y del pasado, no necesita hacer nada especial con el compilador/sintaxis, solo haga una función (pero no haga cosas estúpidas en la función/controlador)

//-------------------------------------------------------------------
volatile unsigned int intcounter;
//-------------------------------------------------------------------
// CAREFUL, THIS IS AN INTERRUPT HANDLER
void tim5_handler ( void )
{
    intcounter++;
    PUT32(TIM5BASE+0x10,0x00000000);
}
// CAREFUL, THIS IS AN INTERRUPT HANDLER
//-------------------------------------------------------------------

El archivo MAKE para blinker05 debe parecerse al ejemplo de blinker02, en su mayoría cortar y pegar para la mayoría de estos. Convierta los archivos de origen individuales en objetos y luego vincúlelos. Construyo para thumb, thumb2 usando gcc y clang. puede cambiar la línea all: en ese momento para incluir solo los elementos gcc si no tiene/quiere clang (llvm) involucrado. Uso binutils para ensamblar y vincular la salida de clang por cierto.

Todos estos proyectos utilizan herramientas gratuitas, listas para usar y de código abierto. sin IDE, solo línea de comandos. Sí, solo me meto con Linux y no con Windows, pero estas herramientas también están disponibles para los usuarios de Windows, cambie cosas como rm -f something para eliminar algo en el archivo MAKE, cosas así cuando construyo en Windows. Eso o ejecutar linux en vmware o virtualbox o qemu. No usar un IDE significa que también elige su editor de texto, no entraré en eso, tengo mis favoritos. Tenga en cuenta que una característica extremadamente molesta del programa gnu make es que requiere pestañas reales en el archivo MAKE, odio las pestañas invisibles con pasión. Entonces, un editor de texto para makefiles que deja tabulaciones, el otro para el código fuente que crea espacios. No sé acerca de Windows, normalmente usé borland make o microsoft make en lugar de gnu cuando desarrollaba allí (incluso cuando usaba gcc y binutils).

Espero que esto ayude, no es el chip/placa exacto sino un cortex-m4 bueno m4 no m3, lo suficientemente cerca para esta discusión. vea el directorio mbed o stm32vld para un cortex-m3 real (no hay suficientes diferencias con el m4 para archivos MAKE y código de arranque, etc.), pero no hecho por st. Los núcleos de cortex-m3 deben ser los mismos entre los proveedores, el cortex-m3 y el cortex-m4 son ambos ARMv7m y están más cerca que diferentes. El cortex-m0 es un ARMv6m, apenas tiene suficientes instrucciones thumb2 para molestarse, los compiladores no lo han alcanzado, así que solo use el pulgar (suponga que está construyendo para un ARMv4T (solo pulgar) si es necesario). Mi simulador de pulgar es solo para el pulgar, no para el pulgar2, también podría ser útil para usted, creo que lo hice realizar interrupciones de alguna forma o manera.

Estaba leyendo la respuesta y supongo que el autor de esta respuesta sería USTED. Sus respuestas me ayudaron mucho y me motivaron a pasarme a los procesadores ARM en lugar de ser un fanático de AVR y PIC. Gracias
De nada... devuélvelo...

Puede echar un vistazo a este sitio donde intenta explicar los conceptos básicos del enlazador y low_level_init en el código.

Tenga en cuenta que la página se centra en describir el problema, por lo que el vector nvic es mínimo.

Luego tiene ejemplos más completos en la "biblioteca de periféricos estándar STM32F2xx", solo mire en las secciones de gcc (ya que Yagarto está basado en gcc). Y hay un código de ejemplo que lo ayudará con una configuración correcta de la nvic (tabla de vectores de interrupción).

Entonces, incluso si esta no es una respuesta completa, espero que sea útil de todos modos.