STM32F2: Kombination aus Makefile, Linker-Skript und Startdatei ohne kommerzielle IDE

Ich arbeite seit etwa zwei Monaten mit einem STM32F2 (insbesondere dem STM32F217IGH6 auf einem Entwicklungsboard). Mein mit Abstand größtes Problem hatte mit dem "Setup" zu tun, das Makefile, Linker-Skript und Startdatei enthält.

Insbesondere war ich nicht in der Lage, meine Interrupt-Vektortabelle richtig einzurichten und Interrupt-Handler aufzurufen. ST stellt Beispiele bereit, die auf kommerzielle IDEs zugeschnitten sind. Stattdessen verwende ich die kostenlose Yagarto-Neukompilierung der GCC-Toolchain (und OpenOCD, um das Bild über JTAG zu laden).

Gibt es Beispielprojekte für mein Board (oder einen nahen Verwandten davon), die die entsprechende Kombination aus Makefile, Linker-Skript und Startdatei für nicht-kommerzielle IDEs enthalten, die für den Aufruf von Interrupt-Handlern eingerichtet sind?

Sie sollten nach Cortex M3-Beispielen suchen, das genaue Board und der Prozessor sind für die Dinge, die Sie fragen, nicht so wichtig. Sie müssen wahrscheinlich das Speicherlayout im Linker-Skript und die Methode zum Flashen im Makefile ändern, aber das sollte alles sein.
Kannst du das alles in ein Git-Repo packen und auf Github oder so?
Das ist genau der Grund, warum ich aufgehört habe, das STM zu verwenden, sobald ich es ausprobiert habe. Wenn sie mir mit der fiesen Toolchain das Leben schwer machen, dann gehe ich woanders hin. Als ich die IDE für PSoC3 und PSoC5 ausprobierte, war das ein himmelweiter Unterschied.
Engagieren Sie sich für Yagarto? Das ist völlig in Ordnung und stellt eine großartige Frage dar, aber ich bin mit der CodeSourcery Lite- Toolchain vertraut. Eine Antwort für eine andere Toolchain könnte wahrscheinlich angepasst werden, würde aber nicht sofort funktionieren.

Antworten (2)

http://github.com/dwelch67

stm32f4 und stm32vld insbesondere, aber auch die anderen können für Sie nützlich sein. mbed und das mzero-Verzeichnis unter mbed (cortex-m0).

Ich mag den einfachen, dummen Ansatz, minimale Linker-Skripte, minimalen Startcode usw. Die Arbeit wird vom Code erledigt, nicht von einer bestimmten Toolchain.

Die meisten Formen von gcc und binutils (daumenfähig) funktionieren einigermaßen mit diesen Beispielen, da ich den Compiler zum Kompilieren verwende, nicht als Ressource für Bibliotheksaufrufe, ich verwende keine Stock-Linker-Skripte usw. Ältere gcc und binutils wissen nichts davon die neueren thumb2-Teile, so dass einige Änderungen erforderlich sein können.

Ich baue meinen eigenen gcc, binutils und llvm/clang und verwende zum Beispiel Codesourcery (jetzt Mentor Graphics, aber Sie können immer noch die kostenlose/Lite-Version bekommen).

Besonders wenn Sie anfangen, ein Projekt für ein neues Ziel zusammenzustellen, müssen Sie etwas zerlegen. Insbesondere um sicherzustellen, dass die Elemente dort sind, wo Sie sie haben möchten, zum Beispiel auf dem Vektortisch.

Schauen Sie sich zum Beispiel stm32f4d/blinker02 an. Es beginnt mit vectors.s, der Ausnahme-/Vektortabelle, plus einigen asm-Unterstützungsroutinen:

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

Keine Interrupts in diesem Beispiel, aber die anderen Dinge, die Sie brauchen, sind hier.

blinker02.c enthält den Hauptteil des C-Codes mit dem C-Einstiegspunkt, den ich notmain() nenne, um zu vermeiden, dass er main genannt wird (einige Compiler fügen Junk zu Ihrer Binärdatei hinzu, wenn Sie main() haben).

erspart Ihnen ein Ausschneiden und Einfügen. Das Makefile erzählt die Geschichte über das Kompilieren und Linken. Beachten Sie, dass einige meiner Beispiele zwei oder mehr Binärdateien aus demselben Code kompilieren. gcc-Compiler, Clang-Compiler von llvm, nur Daumen und Daumen2, verschiedene Optimierungen usw.

Beginnen Sie damit, Objektdateien aus den Quelldateien zu erstellen.

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

Der Linker, ld, verwendet ein Linker-Skript, das ich memmap nenne, diese können extrem schmerzhaft sein, manchmal aus gutem Grund, manchmal nicht. Ich bevorzuge den Ansatz „weniger ist mehr“ für die Einheitsgröße, alles außer dem Ansatz für Küchenspülen.

Ich verwende normalerweise keine .data (also fast nie) und dieses Beispiel benötigt keine .bss, also ist hier das Linker-Skript, gerade genug, um das Programm (.text) dort zu platzieren, wo es für diesen Prozessor sein muss, so wie ich bin es benutzen.

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

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

Ich habe eine Speicherregion, um das zu definieren, es gibt nichts Besonderes an dem Namen ram, du kannst ihn foo oder bar oder bob oder ted nennen, es spielt keine Rolle, er verknüpft nur die Speicherelemente mit Abschnitten. Die Abschnitte definieren Dinge wie .text, .data, .bss, .rodata und wo sie in der Speicherzuordnung hinkommen.

Wenn Sie dies erstellen, sehen Sie, dass ich alles zerlege (objdump -D), Sie sehen dies

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}

Das Wichtigste ist, dass die Adresse auf der linken Seite dort ist, wo wir sie haben wollten, der Code von vectors.s steht an erster Stelle in der Binärdatei (weil er an erster Stelle in der ld-Befehlszeile steht, werden die Elemente angezeigt, es sei denn, Sie tun etwas im Linker-Skript oben in der Binärdatei in der Reihenfolge, in der sie auf der ld-Befehlszeile stehen). Um richtig zu booten, müssen Sie sicherstellen, dass sich Ihre Vektortabelle an der richtigen Stelle befindet. Das erste Element ist meine Stapeladresse, das ist in Ordnung. Das zweite Element ist die Adresse von _start und sollte eine ungerade Zahl sein. Die Verwendung von .thumb_func vor einem Label verursacht dies, sodass Sie keine anderen hässlich aussehenden Dinge tun müssen.

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

08000056 <hang>:
 8000056:       e7fe          

also sind 0x08000051 und 0x08000057 die richtigen Vektoreinträge für _start und hang. start ruft notmain() auf

08000098 <notmain>:
 8000098:       b510            push    {

Das sieht gut aus (Sie zeigen nicht die ungeradzahlige Adresse in der Demontage).

Alles ist gut.

Fahren Sie mit dem Beispiel blinker05 fort, dieser unterstützt Interrupts. und benötigt etwas RAM, also ist .bss definiert.

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

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

Denken Sie daran, dass ram und rom willkürliche Namen sind, bob und ted, foo und bar funktionieren alle einwandfrei.

Es werden nicht die gesamten Vektoren angezeigt, da der Cortex-m3 eine Million Einträge in der Vektortabelle hat, wenn Sie eine vollständige erstellen (variiert von Kern zu Kern und möglicherweise innerhalb desselben Kerns, abhängig von den vom Chiphersteller gewählten Optionen). Die relevanten Teile sind hier nach der Demontage:

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}

Es braucht einige Versuche und Irrtümer, um diesen Handler genau an der richtigen Stelle zu platzieren, überprüfen Sie mit Ihrem Chip, wo er sein muss, er ist nicht unbedingt an derselben Stelle wie dieser, und bei so vielen Interrupts suchen Sie möglicherweise sowieso nach einem anderen Interrupt. Die Cortex-M-Prozessoren sorgen im Gegensatz zu normalen Armen dafür, dass Sie keinen Trampolincode für Interrupts benötigen, sie bewahren eine bestimmte Anzahl von Registern und verwalten das Umschalten der Prozessormodi über den Link-Registerinhalt. Solange die Hardware und das abi für den Compiler nahe genug beieinander liegen, funktioniert alles. In diesem Fall habe ich den Handler in C erstellt, im Gegensatz zu anderen Plattformen und in der Vergangenheit müssen Sie nichts Besonderes mit dem Compiler/der Syntax tun, sondern nur eine Funktion erstellen (aber machen Sie keine dummen Dinge in der Funktion/dem Handler).

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

Das Makefile für blinker05 sollte dem Beispiel blinker02 ähneln, meistens ausschneiden und einfügen. wandeln Sie die einzelnen Quelldateien in Objekte um und verknüpfen Sie sie dann. Ich baue für thumb, thumb2 mit gcc und clang. Sie können die Zeile all: zu diesem Zeitpunkt ändern, um nur die gcc-Elemente einzuschließen, wenn Sie kein clang (llvm) haben/wollen. Ich verwende binutils, um die Clang-Ausgabe übrigens zusammenzustellen und zu verknüpfen.

Alle diese Projekte verwenden kostenlose, handelsübliche Open-Source-Tools. keine IDEs, nur Befehlszeile. Ja, ich spiele nur mit Linux und nicht mit Windows, aber diese Tools sind auch für Windows-Benutzer verfügbar, ändern Sie Dinge wie rm -f etwas, um etwas im Makefile zu löschen, solche Dinge, wenn Sie auf Windows bauen. Das oder Linux auf VMware oder Virtualbox oder qemu ausführen. Wenn Sie keine IDE verwenden, müssen Sie auch Ihren Texteditor auswählen. Ich werde darauf nicht eingehen, ich habe meine Favoriten. Beachten Sie, dass eine äußerst ärgerliche Eigenschaft des GNU-Make-Programms darin besteht, dass es tatsächliche Tabulatoren im Makefile erfordert. Ich hasse unsichtbare Tabulatoren leidenschaftlich. Also ein Texteditor für Makefiles, die Tabulatoren verlassen, der andere für Quellcode, der Leerzeichen macht. Ich weiß nichts über Windows, ich habe normalerweise Borland Make oder Microsoft Make anstelle von GNU verwendet, als ich dort entwickelt habe (selbst wenn gcc und binutils verwendet wurden).

Ich hoffe, das hilft, es ist nicht der genaue Chip / die genaue Platine, sondern ein Cortex-m4, gut m4, nicht m3, nah genug für diese Diskussion. Siehe das mbed- oder stm32vld-Verzeichnis für einen tatsächlichen Cortex-m3 (nicht genügend Unterschiede zum m4 für Makefiles und Boot-Code usw.), aber nicht von st. Die Cortex-m3-Kerne sollten bei allen Anbietern gleich sein, Cortex-m3 und Cortex-m4 sind beide ARMv7m und eher näher als unterschiedlich. Der Cortex-m0 ist ein ARMv6m, hat kaum genug thumb2-Anweisungen, um sich damit zu beschäftigen, die Compiler haben ihn nicht eingeholt, also verwenden Sie einfach nur Daumen (tun Sie so, als würden Sie für einen ARMv4T (nur Daumen) bauen, wenn es sein muss). Mein Thumbulator-Simulator ist nur Daumen, kein Daumen2, er könnte auch für Sie nützlich sein, ich glaube, ich habe ihn dazu gebracht, Interrupts in irgendeiner Form oder Weise auszuführen.

Ich habe die Antwort gelesen und ich denke, dass der Autor für diese Antwort DU sein würdest. Ihre Antworten haben mir sehr geholfen und mich dazu motiviert, auf ARM-Prozessoren umzusteigen, anstatt ein AVR- und PIC-Fanboy zu sein. Vielen Dank
Gern geschehen ... zahlen Sie es weiter ...

Sie können sich diese Seite ansehen, wo versucht wird, die Grundlagen des Linkers und low_level_init im Code zu erklären.

Bitte beachten Sie, dass sich die Seite auf die Beschreibung des Problems konzentriert, sodass der nvic-Vektor minimal ist.

Dann haben Sie vollständigere Beispiele in der "STM32F2xx-Standardperipheriebibliothek", schauen Sie einfach in den gcc-Abschnitten nach (da Yagarto gcc-basiert ist). Und es gibt dort Beispielcode, der Ihnen bei der korrekten Einrichtung des nvic (Interrupt-Vektortabelle) hilft.

Auch wenn dies keine vollständige Antwort ist, hoffe ich, dass es trotzdem irgendwie hilfreich ist.