STM32F2: Makefile, script linker e combinazione di file di avvio senza IDE commerciale


16

Ho lavorato con un STM32F2 (in particolare STM32F217IGH6 su una scheda di sviluppo) per circa due mesi. Di gran lunga il mio più grande problema ha avuto a che fare con "setup", che include makefile, script di linker e file di avvio.

In particolare, non sono stato in grado di impostare correttamente la mia tabella di vettore di interrupt e di aver chiamato i gestori di interrupt. ST fornisce esempi su misura per gli IDE commerciali. Invece, sto usando la ricompilazione Yagarto gratuita della toolchain GCC (e OpenOCD per caricare l'immagine tramite JTAG).

Esistono progetti di esempio per la mia scheda (o un suo cugino stretto) che contengono il makefile, lo script linker e la combinazione di file di avvio appropriati per gli IDE non commerciali impostati per i gestori di interrupt da chiamare?


2
Dovresti cercare esempi Cortex M3, la scheda e il processore esatti non sono così importanti per le cose che chiedi. Probabilmente dovrai modificare il layout della memoria nello script del linker e il metodo per eseguire il flashing nel makefile, ma dovrebbe essere tutto.
Starblue,

1
Puoi mettere tutto questo in un repository git e metterlo su github o qualcosa del genere?
AngryEE,

1
Questo è esattamente il motivo per cui ho smesso di usare STM non appena l'ho provato. Se mi renderanno la vita difficile con la cattiva toolchain, allora andrò da qualche altra parte. Quando ho provato l'IDE per PSoC3 e PSoC5, è stato un mondo di differenza.
Rocketmagnet,

Sei impegnato in Yagarto? Va benissimo e fa un'ottima domanda, ma ho familiarità con la toolchain di CodeSourcery Lite . Una risposta per una diversa toolchain potrebbe probabilmente essere adattata, ma non funzionerebbe immediatamente.
Kevin Vermeer,

Risposte:


20

http://github.com/dwelch67

stm32f4 e stm32vld in particolare, ma anche gli altri potrebbero esserti utili. mbed e la directory mzero sotto mbed (cortex-m0).

Mi piace il semplice approccio stupido, gli script di linker minimi, il codice di avvio minimo, ecc. Il lavoro viene svolto dal codice e non da una particolare catena di strumenti.

La maggior parte delle forme di gcc e binutils (in grado di eseguire il pollice) funzionerà in qualche modo con questi esempi poiché uso il compilatore per compilare non come risorsa per le chiamate in libreria, non uso gli script di linker di stock, ecc. Gcc e binutils precedenti non conosceranno il pollice2 più recente parte quindi potrebbero essere necessarie alcune modifiche.

Costruisco il mio gcc, binutils e llvm / clang e uso ad esempio i codici sorgente (ora mentori grafici ma è ancora possibile ottenere la versione gratuita / lite).

Esp quando inizi a mettere insieme un progetto per un nuovo obiettivo devi fare un po 'di smontaggio. In particolare per assicurarsi che gli elementi siano dove li desideri, ad esempio la tabella vettoriale.

Guarda stm32f4d / blinker02 per esempio. Inizia con vectors.s la tabella di eccezioni / vettoriale più alcune routine di supporto 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

Non ci sono interruzioni in questo esempio, ma le altre cose di cui hai bisogno sono qui.

blinker02.c contiene il corpo principale del codice C con il punto di accesso C che chiamo notmain () per evitare di chiamarlo main (alcuni compilatori aggiungono spazzatura al tuo binario quando hai un main ()).

ti risparmierò un taglia e incolla. il makefile racconta la storia sulla compilazione e sul collegamento. Nota che alcuni dei miei esempi compilano due o più file binari dallo stesso codice. compilatore gcc, compilatore clang di llvm, solo pollice e pollice2, ottimizzazioni diverse, ecc.

Inizia creando file oggetto dai file di origine.

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

il linker, ld, usa uno script linker che chiamo memmap, questi possono essere estremamente dolorosi, a volte per una buona ragione, a volte no. Preferisco che meno sia più l'approccio alla taglia unica, tutto tranne l'approccio lavello della cucina.

Non uso .data in genere (ma quasi mai) e questo esempio non ha bisogno di .bss, quindi ecco lo script del linker, quanto basta per posizionare il programma (.text) dove deve essere per questo processore come sono io usandolo.

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

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

Ho una regione di memoria per definirlo, non c'è niente di speciale nel nome ram che puoi chiamarlo foo o bar o bob o ted, non importa, collega semplicemente gli elementi di memoria alle sezioni. Le sezioni definiscono cose come .text, .data, .bss, .rodata e dove vanno nella mappa della memoria.

quando lo costruisci, vedi che ho smontato tutto (objdump MrGreen) lo vedi

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 cosa fondamentale da notare è che l'indirizzo a sinistra è dove lo volevamo, il codice vectors.s è il primo nel binario (Perché è il primo nella riga di comando ld, a meno che tu non faccia qualcosa nello script del linker gli elementi mostreranno nel binario nell'ordine in cui si trovano sulla riga di comando ld). Per eseguire correttamente l'avvio, è necessario assicurarsi che la tabella vettoriale sia nel posto giusto. Il primo articolo è l'indirizzo del mio stack, va bene. Il secondo elemento è l'indirizzo di _start e dovrebbe essere un numero dispari. l'uso di .thumb_func prima di un'etichetta fa sì che ciò accada, quindi non devi fare altre cose brutte.

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

08000056 <hang>:
 8000056:       e7fe          

quindi 0x08000051 e 0x08000057 sono le voci vettoriali appropriate per _start e hang. avvia chiamate notmain ()

08000098 <notmain>:
 8000098:       b510            push    {

Sembra buono (non mostrano l'indirizzo numerato dispari nello smontaggio).

Tutto bene.

Passa all'esempio blinker05, questo supporta gli interrupt. e ha bisogno di qualche ram, quindi è definito .bss.

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

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

ricorda che ram e rom sono nomi arbitrari, bob e ted, foo e bar funzionano tutti bene.

Non mostrerà tutti i vettori. Perché la corteccia-m3 ha un miliardo di voci nella tabella vettoriale se ne fai una completa (varia da core a core e forse all'interno dello stesso core a seconda delle opzioni scelte dal fornitore del chip) Le parti pertinenti sono qui dopo lo smontaggio:

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}

richiede un po 'di tentativi ed errori per posizionare quel gestore esattamente nel punto giusto, verificare con il proprio chip dove deve trovarsi che non si trova necessariamente nello stesso posto di questo, e con così tanti interrupt si potrebbe comunque cercare un interrupt diverso. i processori cortx-m, a differenza dei normali bracci, lo fanno in modo da NON AVERE BISOGNO del codice del trampolino per gli interrupt, conservano un certo numero di registri e gestiscono la commutazione delle modalità del processore attraverso il contenuto del registro dei collegamenti. purché l'hardware e l'abi per il compilatore siano abbastanza vicini, tutto funziona. In questo caso ho fatto il gestore in C, a differenza di altre piattaforme e in passato non è necessario fare nulla di speciale con il compilatore / sintassi semplicemente creare una funzione (ma non fare cose stupide nella funzione / gestore)

//-------------------------------------------------------------------
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
//-------------------------------------------------------------------

Il makefile per blinker05 dovrebbe assomigliare all'esempio blinker02, principalmente taglia e incolla per la maggior parte di questi. trasforma i singoli file sorgente in oggetti, quindi collega. Costruisco per pollice, pollice2 usando gcc e clang. puoi cambiare la linea all: al momento per includere solo gli oggetti gcc se non hai / vuoi clang (llvm) coinvolti. Uso binutils per assemblare e collegare l'uscita clang tra l'altro.

Tutti questi progetti utilizzano strumenti gratuiti, pronti all'uso, open source. nessun IDE, solo riga di comando. Sì, ho solo problemi con Linux e non con Windows, ma questi strumenti sono disponibili anche per gli utenti di Windows, cambio cose come rm -f qualcosa per eliminare qualcosa nel makefile, cose del genere quando si costruisce su Windows. Quello o eseguire Linux su VMware o Virtualbox o Qemu. Non usare un IDE significa che scegli anche il tuo editor di testo, non ci penso, ho i miei preferiti. Nota che una caratteristica estremamente fastidiosa del programma gnu make è che richiede schede effettive nel makefile, odio le schede invisibili con passione. Quindi un editor di testo per makefile che lascia le schede, l'altro per il codice sorgente che crea spazi. Non so di Windows,

Spero che questo aiuti, non è l'esatto chip / board ma una corteccia-m4 ben m4 non m3, abbastanza vicina per questa discussione. vedere la dir mbed o stm32vld per un effettivo cortex-m3 (non abbastanza differenze da m4 per makefile e codice di avvio, ecc.), ma non fatto da st. Il core cortx-m3 dovrebbe essere lo stesso per tutti i fornitori, cortex-m3 e cortex-m4 sono entrambi ARMv7m e sono più vicini che diversi. Il cortx-m0 è un ARMv6m, non ha quasi mai le istruzioni per il pollice2 da preoccuparsi, i compilatori non l'hanno raggiunto, quindi usa solo il pollice (fai finta che stai costruendo per un ARMv4T (solo pollice) se necessario). Il mio simulatore di thumbulator è solo thumb, no thumb2, potrebbe essere utile anche a te, penso di averlo fatto eseguire interruzioni in qualche forma o modo.


Stavo leggendo la risposta e immagino che l'autore di questa risposta saresti TU. Le tue risposte mi hanno aiutato molto e mi hanno motivato a passare ai processori ARM piuttosto che essere un fanboy AVR e PIC. Grazie
MaNyYaCk


2

Puoi dare un'occhiata a questo sito in cui cerca di spiegare le basi del linker e low_level_init nel codice.

Si noti che la pagina è focalizzata sulla descrizione del problema, quindi il vettore nvic è un po 'minimale.

Quindi hai degli esempi più completi nella "libreria di periferiche standard STM32F2xx", basta guardare nelle sezioni gcc (poiché Yagarto è basato su gcc). E c'è un codice di esempio che ti aiuterà con una corretta configurazione di nvic (tabella vettoriale di interrupt).

Quindi, anche se questa non è una risposta completa, spero che sia comunque utile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.