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

16

Ich arbeite seit ungefähr zwei Monaten mit einem STM32F2 (speziell dem STM32F217IGH6 auf einer Entwicklungsplatine). Bei weitem mein größtes Problem hatte mit dem "Setup" zu tun, das Makefile, Linker-Skript und Startdatei enthält.

Insbesondere konnte ich meine Interrupt-Vektortabelle nicht richtig einrichten und habe Interrupt-Handler aufgerufen. ST bietet Beispiele, die auf kommerzielle IDEs zugeschnitten sind. Stattdessen verwende ich die kostenlose Yagarto-Neukompilierung der GCC-Toolchain (und OpenOCD, um das Image über JTAG zu laden).

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

Zufälliges Blau
quelle
2
Sie sollten nach Cortex M3-Beispielen suchen, die genaue Karte 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.
Starblue
1
Kannst du das alles in ein Git-Repo stecken und es auf einen Github legen oder so?
AngryEE
1
Genau aus diesem Grund habe ich die Verwendung des STM eingestellt, sobald ich es ausprobiert habe. Wenn sie mir mit der fiesen Werkzeugkette das Leben schwer machen, dann gehe ich woanders hin. Als ich die IDE für PSoC3 und PSoC5 ausprobierte, war das ein großer Unterschied.
Rocketmagnet
Bist du Yagarto verpflichtet? Das ist völlig in Ordnung und eine gute Frage, aber ich kenne die CodeSourcery Lite- Toolchain. Eine Antwort für eine andere Toolchain könnte wahrscheinlich angepasst werden, würde aber nicht sofort funktionieren.
Kevin Vermeer

Antworten:

20

http://github.com/dwelch67

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

Ich mag den einfachen, dummen Ansatz, minimale Linker-Skripte, minimalen Startcode usw. Die Arbeit wird durch den Code erledigt, nicht durch eine bestimmte Toolchain.

Die meisten Formen von gcc und binutils (da thumb-fähig) funktionieren in gewisser Weise mit diesen Beispielen, da ich den Compiler verwende, um nicht als Ressource für Bibliotheksaufrufe zu kompilieren, die Stock-Linker-Skripte nicht verwende usw. Ältere gcc und binutils kennen sich nicht aus Die neueren Teile von thumb2 erfordern möglicherweise einige Änderungen.

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

Vor allem, wenn Sie ein Projekt für ein neues Ziel zusammenstellen, müssen Sie einige Demontagen durchführen. Insbesondere, um sicherzustellen, dass die Elemente dort sind, wo Sie sie haben möchten, beispielsweise in der Vektortabelle.

Schauen Sie sich zum Beispiel stm32f4d / blinker02 an. Es beginnt mit vectors.s der Ausnahme- / Vektortabelle und 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

In diesem Beispiel gibt es keine Interrupts, aber die anderen Dinge, die Sie brauchen, sind hier.

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

erspart Ihnen ein Ausschneiden und Einfügen. Das Makefile erzählt die Geschichte über das Kompilieren und Verknüpfen. Beachten Sie, dass einige meiner Beispiele zwei oder mehr Binärdateien aus demselben Code kompilieren. gcc compiler, llvm's clang compiler, thumb only und thumb2, verschiedene optimierungen, etc.

Beginnen Sie, indem Sie Objektdateien aus den Quelldateien 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, dass weniger mehr Ansatz für die Einheitsgröße ist, alles andere als der Ansatz für Küchenspülen.

Normalerweise verwende ich keine .data-Dateien (fast nie), und in diesem Beispiel wird keine .bss-Datei benötigt. Hier ist also das Linker-Skript, das gerade ausreicht, um das Programm (.text) dort abzulegen, wo es für diesen Prozessor sein muss, wie ich es bin es benutzen.

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

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

Ich habe einen Speicherbereich, um das zu definieren, es gibt nichts Besonderes an dem Namen RAM, den Sie als foo oder bar oder bob bezeichnen können, oder es spielt keine Rolle, dass es nur die Speicherelemente mit Abschnitten verknüpft. Die Abschnitte definieren Dinge wie .text, .data, .bss, .rodata und wo sie sich in der Speicherzuordnung befinden.

Wenn du das baust, siehst du, dass ich alles zerlege (objdump -D), was du siehst

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 genau dort ist, wo wir sie haben wollten. Der Code vectors.s steht in der Binärdatei an erster Stelle in der Binärdatei in der Reihenfolge, in der sie sich in der ld-Befehlszeile befinden). Damit Sie richtig booten können, müssen Sie sicherstellen, dass sich Ihre Vektortabelle am richtigen Ort befindet. Der erste Punkt ist meine Stapeladresse, das ist in Ordnung. Das zweite Element ist die Adresse für _start und sollte eine ungerade Zahl sein. Die Verwendung von .thumb_func vor einem Label führt dazu, dass Sie keine anderen hässlichen Dinge tun müssen.

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

08000056 <hang>:
 8000056:       e7fe          

Daher sind 0x08000051 und 0x08000057 die richtigen Vektoreinträge für _start und hang. Anrufe starten nichtmain ()

08000098 <notmain>:
 8000098:       b510            push    {

Das sieht gut aus (sie zeigen nicht die ungerade nummerierte Adresse in der Demontage).

Alles ist gut.

Springe zum Beispiel blinker05, dieser unterstützt Interrupts. und braucht 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, RAM und ROM sind willkürliche Namen, Bob und Ted, Foo und Bar funktionieren alle einwandfrei.

Nicht alle Vektoren anzeigen. S, da der Cortex-m3 zig Millionen Einträge in der Vektortabelle enthält, wenn Sie einen vollständigen Eintrag 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}

Um diesen Handler genau am richtigen Ort zu platzieren, müssen Sie einige Versuche unternehmen. Prüfen Sie, ob sich Ihr Chip an der Stelle befindet, an der er sich befinden muss. Bei so vielen Interrupts suchen Sie möglicherweise ohnehin nach einem anderen Interrupt. Die Cortex-M-Prozessoren machen es im Gegensatz zu normalen Armen so, dass Sie keinen Trampolin-Code für Interrupts benötigen. Sie bewahren eine bestimmte Anzahl von Registern und verwalten das Umschalten der Prozessormodi über den Link-Register-Inhalt. Solange die Hardware und das ABI für den Compiler nah genug sind, funktioniert alles. In diesem Fall habe ich den Handler in C gemacht, im Gegensatz zu anderen Plattformen und in der Vergangenheit musst du mit dem Compiler / der Syntax nichts Besonderes machen, mache nur eine Funktion (aber mache 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 von blinker02 ähneln, meistens ausschneiden und einfügen. verwandeln Sie die einzelnen Quelldateien in Objekte und verknüpfen Sie sie dann. Ich baue für thumb, thumb2 mit gcc und clang. Sie können die Zeile all: zu der Zeit so ändern, dass sie nur die gcc-Elemente enthält, wenn Sie kein clang (llvm) haben / wollen. Ich benutze Binutils, um die Clang-Ausgabe zu montieren und zu verknüpfen.

Alle diese Projekte verwenden kostenlose Open Source-Tools von der Stange. Keine IDEs, nur Befehlszeile. Ja, ich störe nur mit Linux und nicht mit Windows, aber diese Tools stehen auch Windows-Benutzern zur Verfügung. Ändern Sie Dinge wie rm -f, um etwas im Makefile zu löschen, solche Dinge, wenn Sie auf Windows aufbauen. Das oder Linux auf VMware oder VirtualBox oder QEMU ausführen. Wenn Sie keine IDE verwenden, wählen Sie auch Ihren Texteditor aus. Darauf werde ich nicht eingehen. Ich habe meine Favoriten. Beachten Sie, dass eine extrem ärgerliche Eigenschaft des Gnu make-Programms darin besteht, dass es tatsächliche Tabs im Makefile benötigt. Ich hasse unsichtbare Tabs aus Leidenschaft. Also ein Texteditor für Makefiles, der Tabulatoren hinterlässt, der andere für Quellcode, der Leerzeichen erzeugt. Ich weiß nicht über Fenster,

Ich hoffe, das hilft, es ist nicht der genaue Span / Brett, aber ein Cortex-M4-Brunnen M4 nicht m3, nah genug für diese Diskussion. Siehe das Verzeichnis mbed oder stm32vld für einen aktuellen cortex-m3 (nicht genügend Unterschiede zum m4 für Makefiles und Boot-Code usw.), aber nicht von st. Die Kerne von Kortex-m3 sollten bei allen Anbietern gleich sein. Kortex-m3 und Kortex-m4 sind beide ARMv7m und eher näher als unterschiedlich. Der cortex-m0 ist ein ARMv6m, hat kaum thumb2-Anweisungen, die die Compiler noch nicht verstanden haben. Verwenden Sie also nur den Daumen (tun Sie so, als würden Sie einen ARMv4T bauen (nur den Daumen), wenn nötig). Mein Daumen-Simulator ist nur ein Daumen, kein Daumen2. Es könnte auch für Sie nützlich sein. Ich glaube, ich habe ihn dazu gebracht, Interrupts in irgendeiner Form oder auf irgendeine Art und Weise auszuführen.

Oldtimer
quelle
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 motiviert, zu ARM-Prozessoren zu wechseln, anstatt AVR- und PIC-Fanboy zu sein. Thanks
MaNyYaCk
Gern geschehen
2

Sie können sich diese Site ansehen, auf der versucht wird, die Grundlagen des Linkers und von low_level_init im Code zu erläutern.

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

Dann haben Sie ein vollständigeres Beispiel in der "STM32F2xx Standard Peripherals Library", schauen Sie sich einfach die gcc-Abschnitte an (da Yagarto gcc-basiert ist). Und es gibt dort einen Beispielcode, der Ihnen bei der korrekten Einrichtung der NVIC (Interrupt-Vektortabelle) hilft.

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

Johan
quelle