Forum: Mikrocontroller und Digitale Elektronik Einstieg in STM32 : STM32F103C8T6 --> Kompilieren und Flashen?


von Anon A. (anon1234)


Lesenswert?

Hallo,
nach dem ich meine µC-Projekte jetzt immer mit einem 8051 gelöst habe, 
will ich jetzt mal den Schritt in die modernere Welt wagen.
Ich habe mich für STM32 und zwar konkret für einen STM32F103C8T6 
entschieden.

Nun kommen die üblichen Anfängerfragen hoch, die ich mit google und der 
Suchfunktion nur ziemlich unzufriedenstellend beantworten konnte.
Unter 8051 habe ich mit Keil einem dubiosen Flash-Programm für einen 
USBASP gearbeitet.
Hier habe ich unter Keil einfach auf Compile und im Flash-Programm auf 
Flash gedrückt und alles erst einmal nicht weiter hinterfragt.

Mittlerweile bin ich auf Linux (Debian) umgestiegen und würde ganz gerne 
auch mal die Zusammenhänge ein bisschen verstehen.
Schließlich nutzen die Entwicklungsumgebungen und so intern ja auch nur 
die gängigen Compiler, die man auch per Kommandozeile nutzen kann.

Meine erste Frage ist : Kompilieren
Um ein erstes nichts machendes programm zu kompilieren habe ich mir den 
ARM-G++ (arm-none-eabi-g++) installiert.
1
int main() {
2
        int i;
3
        while(i < 1000) {
4
                i++;
5
        }
6
}
Für meinen normalen PC kann ich das Programm per
1
g++ main.cpp -o main
kompilieren und ausführen und alles funktioniert soweit.

Für den STM32 habe ich das selbe mit dem ARM-G++ probiert und die Flags
hier aus dem Forum genutzt :
http://www.mikrocontroller.net/articles/ARM_GCC#GCC_Bin.C3.A4rdistributionen
1
arm-none-eabi-g++ main.cpp -o main -mcpu=cortex-m3 -mfloat-abi=soft -mthumb -ffunction-sections -fdata-sections -Os -flto -fno-rtti -fno-exceptions
Hier kommen natürlich iwelche Fehlermeldungen wie z.B.
1
/usr/lib/gcc/arm-none-eabi/4.8/../../../arm-none-eabi/lib/armv7-m/libc.a(lib_a-exit.o): In function `exit':
2
/home/tin/projects/debian/arm-toolchain/collab-maint/newlib/build/arm-none-eabi/armv7-m/newlib/libc/stdlib/../../../../../../newlib/libc/stdlib/exit.c:70: undefined reference to `_exit'
3
collect2: error: ld returned 1 exit status

Jetzt ist für mich erstmal die Frage, ist das der richtige Ansatz an das 
kompilieren heran zu gehen? Muss ich irgendwelche Header-Files mit 
einbinden und wenn ja wo bekommt man die am besten her?

Dann die zweite Frage : Flashen
Wie bekomme ich die ausführbaren Dateien auf den µController?
Im Forum habe ich hierzu verschiedene Meinungen gelesen. Auf der einen
Seite kann man natürlich zusätzliche Hardware
(https://www.mikrocontroller.net/articles/STM32#Programmieradapter)
kaufen.
Auf der anderen Seite soll es angeblich /vielleicht /eventuell möglich 
sein den STM32F103C8T6 direkt über USB zu programmieren.
(Beitrag "Anfänger mit STM32F103C8T6")

Hat jemand eine Idee ob das möglich ist, bzw. ob es sinnvoll ist, oder 
organisiert man sich einen typischen Flash-Adapter mit 
Debugging-Funktion?

Dann bleibt letztendlich für mich noch die Frage welche Software kann 
zum flaschen genutzt werden.  Teilweise bin ich im Internet darauf 
gestoßen, dass anscheinend die Arduino IDE iwie dafür verwendet werden 
kann.
Am liebsten wäre es mir allerdings etwas altbewärtes zu nutzen?
Gibt es so etwas wie den GCC zum flashen? Oder irgendetwas 
vergleichbares.

Ich hoffe irgendjemand kann mir ein bisschen helfen.
Prinzipiell brauche ich aktuell erst einmal einen Überblick, was man 
braucht, wie Dinge ablaufen und wonach ich weiter suchen muss um mich 
einzulesen in das ganze Thema.
Ich würde mich auf jeden Fall sehr über ein paar Antworten freuen :)

Viele Grüße
anon1234

von hp-freund (Gast)


Lesenswert?

Sehr gut sind die Grundlagen hier erklärt:

http://fun-tech.se/stm32/index.php

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

Vor ca. einer Woche hab' ich ein Blinky-Programm für diese China-Boards 
mit C8T6 drauf auf github hochgeladen. Mit Makefile, Linker-File, ...

https://github.com/spacerace/stm32f103c8t6-blinky

Flashen mit st-util auf Linux, geht mit STLink V2 und den China-Clones 
problemlos.

: Bearbeitet durch User
von Pete K. (pete77)


Lesenswert?

Bei STM32 wird das hier auch gerne genommen:
http://diller-technologies.de/stm32.html

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Anon A. schrieb:
> undefined reference to `_exit'

Ein Programm auf einem Mikrocontroller kann nirgends aussteigen und zum 
Betriebssystem zurückkehren - das Programm selber ist das 
Betriebssystem.
Wie bei deinem 8051 wird dein Programm eine Hauptschleife brauchen, aus 
der es kein Entkommen gibt:
1
int main(void) {
2
int i;
3
while (1) {
4
        while(i < 1000) {
5
                i++;
6
        }
7
  }
8
}

: Bearbeitet durch User
von Dr. No (Gast)


Lesenswert?

Anon A. schrieb:

> Für den STM32 habe ich das selbe mit dem ARM-G++ probiert und die Flags
> hier aus dem Forum genutzt :
> http://www.mikrocontroller.net/articles/ARM_GCC#GC...arm-none-eabi-g++
> main.cpp -o main -mcpu=cortex-m3 -mfloat-abi=soft -mthumb
> -ffunction-sections -fdata-sections -Os -flto -fno-rtti -fno-exceptions
> Hier kommen natürlich iwelche Fehlermeldungen wie
> z.B./usr/lib/gcc/arm-none-eabi/4.8/../../../arm-none-eabi/lib/armv7-m/li 
bc.a(lib_a-exit.o):
> In function `exit':
> /home/tin/projects/debian/arm-toolchain/collab-maint/newlib/build/arm-no 
ne-eabi/armv7-m/newlib/libc/stdlib/../../../../../../newlib/libc/stdlib/ 
exit.c:70:
> undefined reference to `_exit'
> collect2: error: ld returned 1 exit status
>
> Jetzt ist für mich erstmal die Frage, ist das der richtige Ansatz an das
> kompilieren heran zu gehen? Muss ich irgendwelche Header-Files mit
> einbinden und wenn ja wo bekommt man die am besten her?


Füge "--specs=nosys.specs" zu den Optionen hinzu.

> Wie bekomme ich die ausführbaren Dateien auf den µController?
> Im Forum habe ich hierzu verschiedene Meinungen gelesen. Auf der einen
> Seite kann man natürlich zusätzliche Hardware
> (https://www.mikrocontroller.net/articles/STM32#Pro...)
> kaufen.
> Auf der anderen Seite soll es angeblich /vielleicht /eventuell möglich
> sein den STM32F103C8T6 direkt über USB zu programmieren.
> (Beitrag "Anfänger mit STM32F103C8T6")
>
> Hat jemand eine Idee ob das möglich ist, bzw. ob es sinnvoll ist, oder
> organisiert man sich einen typischen Flash-Adapter mit
> Debugging-Funktion?


Das musst Du für Dich entscheiden. Du kannst den STM32F103 über die 
serielle Schnittstelle flashen.
Für aufwendigere Software ist aber sicherlich die Möglichkeit des 
Debuggens über SWD sinnvoll. Daher empfehle ich den Kauf eines STLinks. 
Gibt es auch für ein zwei oder drei Euro in China.


> Dann bleibt letztendlich für mich noch die Frage welche Software kann
> zum flaschen genutzt werden.  Teilweise bin ich im Internet darauf
> gestoßen, dass anscheinend die Arduino IDE iwie dafür verwendet werden
> kann.
> Am liebsten wäre es mir allerdings etwas altbewärtes zu nutzen?
> Gibt es so etwas wie den GCC zum flashen? Oder irgendetwas
> vergleichbares.


Mit st-flash geht das zum Beispiel.
https://github.com/texane/stlink

von W.S. (Gast)


Lesenswert?

Anon A. schrieb:
> Mittlerweile bin ich auf Linux (Debian) umgestiegen und würde ganz gerne
> auch mal die Zusammenhänge ein bisschen verstehen.
> Schließlich nutzen die Entwicklungsumgebungen und so intern ja auch nur
> die gängigen Compiler, die man auch per Kommandozeile nutzen kann.
>
> Meine erste Frage ist : Kompilieren
> Um ein erstes nichts machendes programm zu kompilieren habe ich mir den
> ARM-G++ (arm-none-eabi-g++) installiert.

Also erstens: Die Wahl des Betriebssystems, wo die Entwicklung drauf 
laufen soll, hat nur Bedeutung bei der Wahl der Tools, die man benutzen 
will. Manches gibt's nur für Windows, manchesn nur für Linux und manches 
für beides. Aber das ist nicht der Kern der Sache, sondern 
Nebenschauplatz.

Wichtig ist eher der Ablauf:
Phase 1: Für eine Firmware für deinen µC brauchst du erstmal einen 
Startupcode, der passend ist für deinen konkreten Controller, weil sich 
dort die Interrupt-vektor-Tafel befindet. Dazu brauchst du dann dein 
eigentliches Zeugs (main.c und Konsorten). Das alles muß compiliert und 
assembliert werden und dann zusammengelinkt. Dabei entsteht eine 
Ausgabedatei im elf Format, aus der du eine hex oder bin Datei erzeugen 
mußt, die dann der Input für deen Chip-Brenner ist. Beim Keil wäre das 
"fromelf.exe" und ein Pendant dazu gibt es auch für den Gcc. Es reicht 
also nicht aus, sich bloß mal ein main.c zu schreiben und durch den 
Compiler zu jagen.

Phase 2: Du brauchst in jedem Falle irgend ein Geschirre und ein dazu 
passendes PC-Programm, um damit dein Hex/Bin-File in den Chip zu 
kriegen. Der STM32F103C8T6 hat einen fest eingebauten Bootlader, den man 
über eine simple serielle Strippe benutzen kann, was einfach und 
kostengünstig ist.

Er hat aber auch ein Debug-Interface (JTAG bzw. SWD), was ohne den 
Bootlader auskommt. Über dieses Interface kann man ein kleines 
Progrämmchen in den chipinternen RAM laden, was dann die eigentliche 
Programmierarbeit erledigt, wobei es über die Debug-Schnitstelle mit den 
Daten versorgt wird.

Das geht schneller und man kann darüber auch debuggen, aber man braucht 
dazu eben auch mehr an Geschirre, also passende Programmier-Adapter: 
J-Link (als Klassenprimus), diverse J-Link-OB (OnBoard..), artverwandte 
XYZ-Link's von ST, NXP, Nuvoton und so weiter, die aber jeweils NUR für 
Chips aus ihrem Stammhaus vorgesehen sind.

Die dazu benötigten PC-Programme sind dann ebenso nur auf ihren 
Hersteller orientiert. Und das Ganze hat ne kommerzielle Seite: Segger 
will für seine sehr guten J-Links auch gutes Geld sehen und die diversen 
Chiphersteller achten peinlich drauf, daß ihre JTAG/SWD-Adapter eben 
NICHT so einfach auch für andere Chips benutzbar sind. ST veschlüsselt 
z.B. den Datenverkehr zwischen PC und ST-Link, wenn es um dessen Update 
usw. geht.

Natürlich kann man auch beim freundlichen Chinesen für alles und jedes 
einen Clone kaufen, weswegen an dieser Stelle ein Kleinkrieg herrscht 
und die Fetzen fliegen - aber mir z.B. will das alles nicht gefallen, 
weswegen ich mich auf's Benutzen der jeweiligen Bootlader verlegt habe. 
Nur bei Nuvoton benutze ich JTAG, weil es nicht anders geht und ich von 
Nuvoton einen passenden original-Adapter habe.

W.S.

von Anon A. (anon1234)


Lesenswert?

Vielen vielen Dank für eure super schnelle und ausführliche Hilfe.
Ich bin jedes mal wieder von dieser Community hier total überwältigt :)
Da ich hauptsächlich am Wochenende Zeit für so was habe, komme ich da 
garnicht so richtig hinterher mit dem Antworten :D

Also folgendes habe ich jetzt gelernt :
- Grundlagen-Wissen gibt es hier http://fun-tech.se/stm32/index.php
und hier http://diller-technologies.de/stm32.html (vielen Dank für die 
Tipps. Ich werde mir das in den nächsten Tagen mal zu Gemühte führen.)
- Mein Hauptprogramm muss in einer Dauerschleife gekapselt sein.
1
int main(void) {
2
        int i;
3
        while(1) {
4
                while(i < 1000) {
5
                        i++;
6
                }
7
        }
8
}
- Und mit Hilfe des Parameters --specs=nosys.specs kann ich das obige 
Programm auch erfolgreich kompilieren. Das ist schon mal ein 
Fortschritt.
Leider verstehe ich noch nicht ganz was dieser Parameter macht.
Unter http://pabigot.github.io/bspacm/newlib.html habe ich folgendes 
dazu gefunden :
"The standard solution for newlib is to add -specs=nosys.specs to the 
gcc linker command line. This links in a separate library with 
implementations for all required system functions. Most of these simple 
return an error; some (like _sbrk()) provide a usable definition."

Was bedeutet das genau? Wird beim Linken für alle wichtigen 
Standard-Funktionen aus libc erst einmal ein Dummy zur Verfügung 
gestellt, damit das Linken nicht schief läuft auch wenn die 
Funktionalität eigentlich nicht vorhanden ist?

- Auch habe ich verstanden, dass ich den STM32F103C8T6 sowohl per USB 
über die serielle Schnittstelle flashen kann, als auch über zusätzliche 
Programmier/Debug-Adapter.
Beides hat meiner Meinung nach seinen Reiz. Aus diesem Grund habe ich 
mir jetzt erst einmal einen günstigen ST-Link V2 bestellt 
(http://www.ebay.de/itm/ST-Link-V2-Link-fur-STM8-STM32-komp-USB-Programmer-Debugger-mit-SWIM-SWD-JTAG-/172332741554?hash=item281fd4f3b2:g:5cQAAOSwFdtXz1gX) 
und werde dann mit dem von euch vorgeschlagenen st-flash 
(https://github.com/texane/stlink) testen ob ich irgendetwas hinbekommen 
kann.

Kommen wir nun mal zu den Sachen die ich noch nicht ganz verstehe :

Nils S. schrieb:
> Vor ca. einer Woche hab' ich ein Blinky-Programm für diese China-Boards
> mit C8T6 drauf auf github hochgeladen. Mit Makefile, Linker-File, ...
>
> https://github.com/spacerace/stm32f103c8t6-blinky
>
> Flashen mit st-util auf Linux, geht mit STLink V2 und den China-Clones
> problemlos.
Vielen Dank für den Link, das Projekt sieht ganz nach dem aus, was ich 
zum Starten brauche.
Leider habe ich beim kompilieren immer wieder Probleme. Der Befehl make 
all führt hierbei immer zu dem folgenden Fehler. Leider habe ich bis 
jetzt noch keine Lösung gefunden um das ganze zu beheben. Hat hier 
jemand eine Idee woran das ganze liegen könnte?
1
$ make all
2
/usr/lib/gcc/arm-none-eabi/4.8/../../../arm-none-eabi/bin/ld: cannot open map file output/main.map: No such file or directory
3
collect2: error: ld returned 1 exit status
4
Makefile:61: recipe for target 'output/main.elf' failed
5
make: *** [output/main.elf] Error 1

W.S. schrieb:
> Für eine Firmware für deinen µC brauchst du erstmal einen
> Startupcode, der passend ist für deinen konkreten Controller, weil sich
> dort die Interrupt-vektor-Tafel befindet.
Was ich aktuell nicht ganz verstehe ist, wie dieser Startupcode zu 
verstehen ist und wo man ihn üblicher weise herbekommt.

An anderer Stelle hab ich das hier gefunden :
"Der Startup -Code ist nämlich ein Bestandteil des Binearys, das
der C, Pascal  oder Basic - Compiler erstellt.
In ihm werden die Grundinitialisierungen des Prozessors ausgeführt,
etwa die Zeitgeber - Initialisierung, festlegen des Stackbereiches
und andere Dinge."

Handelt es sich dann hierbei um normalen Programmcode der am Anfang 
einmal mit eingebunden werden muss und der z.B. iwelche register für die 
Peripherie initial setzt?
Und wo bekommt man so einen Startup-Code für gewöhnlich her?
Stellt so etwas der Hersteller zur Verfügung?

Vielen Dank nochmal für eure Hilfe :)
Wenn ich meine ersten Versuche mit dem St-Link gemacht habe, berichte 
ich mal über meinen Erfolg oder Misserfolg ;)

Viele Grüße
anon1234

von W.S. (Gast)


Lesenswert?

Anon A. schrieb:
> Was ich aktuell nicht ganz verstehe ist, wie dieser Startupcode zu
> verstehen ist und wo man ihn üblicher weise herbekommt.

Also:
Bei allen Cortexen ist es so, daß direkt am Anfang des Adressraumes, 
also ab Adresse 0 eine Tafel zu sein hat, welche eine Reihe von 
Adressen, also 32 Bit große Zahlen enthält. Direkt auf 0 steht die 
Adresse, die von der CPU beim Loslaufen nach dem Verschwinden des Resets 
in den Stackpointer geladen wird. Unmittelbar danach, also auf Adresse 4 
steht die Adresse, die in den Programmcounter geladen wird. Das ist 
damit die Adresse, wo die CPU mit dem Ausführen von Befehlen anfängt. 
Anschließend finden sich in der Tafel die Adressen der diversen 
Interrupt-Handler.

So, die CPU fängt also an der Stelle an, die man ihr derart vorgibt und 
das ist normalerweise der Kaltstart-Code. Bei den Cortexen hat sich gar 
viel vereinfacht gegenüber deren Vorgängern (ARM7TDMI). Früher gab es 
eine Reihe von Stacks, die in die verschiedenen Stackpointer aufgesetzt 
werden mußten, bis man endlich im Usermode war und dann main aufrufen 
konnte. Jetzt hat es nur noch einen Stack,.. aber lies dich lieber in 
den Dokumenten von ARM selber ein.

Tja, der Kaltstart mach so diverses, was sich dessen Schreiber eben so 
vorgestellt hat und dann ruft er main auf. Abgesehen davon gibt es in 
einem Startupcode noch die diversen Default-Interrupt-Handler für alles, 
was in der Tafel steht. Diese Default-Handler sind mit einem Attribut 
[WEAK] versehen, was bewirkt, daß der Linker jeden Default-Handler 
rausschmeißt, der sich auf einen Interrupt bezieht, für den es in 
deinem Code einen richtigen Handler gibt. Damit sind alle Interrupts 
irgendwie abgefangen - die von dir behandelten mit deinem Code und der 
Rest eben irgendwie, wie sich der Schreiber des Startupcodes das gedacht 
hat.

Bei den üblichen Startupcodes, wie es sie z.B. von Keil und Konsorten 
als Dreingabe a la blinky.c gibt, bestehen die Default-Handler nur aus 
einem
 B .
B heißt BRANCH und der Punkt heißt "auf dich selbst", also im Klartext 
ein Jump auf diselbe Adresse, quasi Trampeln auf der Stelle bis ewig. 
Das ist die kürzeste und zugleich dümmste Reaktion weswegen sie nur gut 
ist für jemanden, der mit seinem Debugger im Chip herumdebugt. Für das 
fertige Produkt sollte man sowas überdenken und sich ggf. ein 
intelligenteres Abfangen von unbehandelten Interrupts ausdenken.

Klaro? Ich geb dir mal nen kleinen Einblick, wie sowas typischerweise 
aussieht:
1
; Vector Table Mapped to Address 0 at Reset
2
                AREA    RESET, CODE, READONLY
3
                PRESERVE8
4
                THUMB
5
6
7
                EXPORT  __Vectors
8
;                                                   Addr Vector Typ
9
__Vectors       DCD     __initial_sp              ; 0      0    Top of Stack
10
                DCD     Kaltstart                 ; 4      1    Reset Handler
11
                DCD     NMI_Handler               ; 8      2    Non Maskable Interrupt
12
                DCD     HardFault_Handler         ; C      3    Cortex-M0 SV Hard Fault Interrupt
13
                DCD     MemoryManagement_Handler  ; 10     4    Cortex-M0 Memory Management Interrupt
14
                DCD     BusFault_Handler          ; 14     5    Cortex-M0 Bus Fault Interrupt
15
                DCD     UsageFault_Handler        ; 18     6    Cortex-M0 Usage Fault Interrupt
16
                DCD     Reserved                  ; 1C     7
17
                DCD     Reserved                  ; 20     8
18
                DCD     Reserved                  ; 24     9
19
                DCD     Reserved                  ; 28    10
20
                DCD     SVC_Handler               ; 2C    11    Cortex-M0 SV Call Interrupt
21
                DCD     DebugMonitor_Handler      ; 30    12    Cortex-M0 Debug Monitor Interrupt
22
                DCD     Reserved                  ; 34    13
23
                DCD     PendSV_Handler            ; 38    14    Cortex-M0 Pend SV Interrupt
24
                DCD     SysTick_Handler           ; 3C    15    Cortex-M0 System Tick Interrupt
25
26
...usw.
27
28
Kaltstart       PROC
29
                IMPORT  main
30
                IMPORT  sysconfig
31
               
32
                hier kommt der erste Befel rein
33
                ... 
34
Warmstart       hier kommt alles für den Wiederanlauf rein
35
                ...
36
37
                LDR     R0, =sysconfig
38
                BLX     R0             ; Systemkonfiguration aufrufen
39
40
                CPSIE   i
41
                LDR     R0, =main
42
                BLX     R0             ; main aufrufen
43
                B       Warmstart
44
                ENDP
45
46
.. hier kommen dann die Defaulthandler rein

W.S.

von Anon A. (anon1234)



Lesenswert?

Hallo noch mal :)
Vielen Dank für deine ausführliche Antwort.
Die hat mir nun glaube ich sehr geholfen.
Zugegebener Maßen habe ich beim ersten Lesen fast nichts verstanden,
aber im Sinne von "Hilfe zur Selbsthilfe" wusste ich jetzt ungefähr 
wonach ich weiter suchen muss.
Und nach ein bisschen Lesen und den folgenden Videos zum Thema 
Startup-Code auf dem STM32 verstehe ich die Hintergründe nun deutlich 
besser, verstehe auch deinen Post und bedanke mich auch für deinen 
Beispielcode :)

https://www.youtube.com/watch?v=zFAnW7Tzu4U
https://www.youtube.com/watch?v=DfiwWxTRIZE
https://www.youtube.com/watch?v=42HbCf5cz5A

Ich schreiben nun einfach mal, was ich glaube verstanden zu haben. Bei 
Fehlern bitte gerne korrigieren :


Was ich nun verstanden habe ist, dass der Build-Prozess, wie auf dem 
angehängten Bildern (Build Process.png, Build-Prozess GCC.png) zusehen 
ist, im ersten Schritt Objekt-Dateien (*.o) Datei erzeugt, indem einige 
Speicher-Adressen noch nicht fest berechnet sind ("relocatable code"). 
Im zweiten Schritt beim Linken wird dann mit Hilfe des Linkerscripts für 
alle Symbole (aus verschiedenen Objekt-Dateien und Bibliotheken) eine 
feste Adresse für die Ziel-Architektur berechnet.
So wie ich das deute, was ich im Linkerscript des weiter oben genannten 
git-repos gefunden habe, werden im Linkerscript hierbei konkrete 
Adressen für bestimmte Symbole so wie hier :
1
...
2
3
/* include the memory spaces definitions sub-script */
4
/*
5
Linker subscript for STM32F103VCT6 definitions with 256K Flash and 48K Internal SRAM */
6
7
/* Memory Spaces Definitions */
8
9
MEMORY
10
{
11
  RAM (xrw)     : ORIGIN = 0x20000000, LENGTH = 20K
12
  FLASH (rx)    : ORIGIN = 0x08000000, LENGTH = 64K
13
  FLASHB1 (rx)  : ORIGIN = 0x00000000, LENGTH = 0
14
  EXTMEMB0 (rx) : ORIGIN = 0x00000000, LENGTH = 0
15
  EXTMEMB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
16
  EXTMEMB2 (rx) : ORIGIN = 0x00000000, LENGTH = 0
17
  EXTMEMB3 (rx) : ORIGIN = 0x00000000, LENGTH = 0
18
}
19
20
/* higher address of the user mode stack */
21
_estack = 0x20004fff;
22
23
...

und Größen von Speicherbereichen so wie hier :
1
...
2
__Stack_Size = 0x1000 ;
3
4
PROVIDE ( _Stack_Size = __Stack_Size ) ;
5
6
__Stack_Init = _estack  - __Stack_Size ;
7
8
/*"PROVIDE" allows to easily override these values from an object file or the commmand line.*/
9
PROVIDE ( _Stack_Init = __Stack_Init ) ;
10
11
/*
12
There will be a link error if there is not this amount of RAM free at the end.
13
*/
14
_Minimum_Stack_Size = 0x200 ;
15
...

Aus dem Linker fällt dann eine ausführbare Datei mit Adressen für den 
Adressraum der Zielplatform heraus, die dann auf den µC geflashed werden 
kann.

Was ich außerdem verstanden habe ist, dass es am Anfang des 
Programmcodes einen Startup-Code benötigt, der in der sogenannten 
"Vector-Table" bestimmte Adressen definiert. Also z.B. bei welcher 
Adresse beginnt der Code, bei welcher Adresse beginnt der Code nach dem 
Reset, zu welcher Adresse muss gesprungen werden, wenn dieser oder jener 
Interrupt ausgelöst wird...
Die Vektor-Tabelle (zu sehen im angehängten Bild VectorTable.png) die 
ich aus dem angehängten "Programming Manual" entnommen habe ist dabei 
anscheinend für jede Prozessor-Architektur oder evtl. sogar für jeden µC 
unterschiedlich und gibt vor an welcher Adresse im Code-Segment welche 
Sprung-Adresse abgelegt werden muss.
So wie ich das sehe kann so ein Startup-Code in Assembler
W.S. schrieb:
> Klaro? Ich geb dir mal nen kleinen Einblick, wie sowas typischerweise
> aussieht:
>
1
> ; Vector Table Mapped to Address 0 at Reset
2
>                 AREA    RESET, CODE, READONLY
3
>                 PRESERVE8
4
>                 THUMB
5
> 
6
> 
7
>                 EXPORT  __Vectors
8
> ;                                                   Addr Vector Typ
9
> __Vectors       DCD     __initial_sp              ; 0      0    Top of 
10
> Stack
11
>                 DCD     Kaltstart                 ; 4      1    Reset 
12
> Handler
13
>                 DCD     NMI_Handler               ; 8      2    Non 
14
> Maskable Interrupt
15
>                 DCD     HardFault_Handler         ; C      3 
16
> Cortex-M0 SV Hard Fault Interrupt
17
>                 DCD     MemoryManagement_Handler  ; 10     4 
18
> Cortex-M0 Memory Management Interrupt
19
>                 DCD     BusFault_Handler          ; 14     5 
20
> Cortex-M0 Bus Fault Interrupt
21
>                 DCD     UsageFault_Handler        ; 18     6 
22
> Cortex-M0 Usage Fault Interrupt
23
>                 DCD     Reserved                  ; 1C     7
24
>                 DCD     Reserved                  ; 20     8
25
>                 DCD     Reserved                  ; 24     9
26
>                 DCD     Reserved                  ; 28    10
27
>                 DCD     SVC_Handler               ; 2C    11 
28
> Cortex-M0 SV Call Interrupt
29
>                 DCD     DebugMonitor_Handler      ; 30    12 
30
> Cortex-M0 Debug Monitor Interrupt
31
>                 DCD     Reserved                  ; 34    13
32
>                 DCD     PendSV_Handler            ; 38    14 
33
> Cortex-M0 Pend SV Interrupt
34
>                 DCD     SysTick_Handler           ; 3C    15 
35
> Cortex-M0 System Tick Interrupt
36
> 
37
> ...usw.
38
> 
39
> Kaltstart       PROC
40
>                 IMPORT  main
41
>                 IMPORT  sysconfig
42
> 
43
>                 hier kommt der erste Befel rein
44
>                 ...
45
> Warmstart       hier kommt alles für den Wiederanlauf rein
46
>                 ...
47
> 
48
>                 LDR     R0, =sysconfig
49
>                 BLX     R0             ; Systemkonfiguration aufrufen
50
> 
51
>                 CPSIE   i
52
>                 LDR     R0, =main
53
>                 BLX     R0             ; main aufrufen
54
>                 B       Warmstart
55
>                 ENDP
56
> 
57
> .. hier kommen dann die Defaulthandler rein
58
>
>
> W.S.

oder auch (anscheinend nicht für jeden µC) in C geschrieben werden.
1
/* startup code for TM4C MCU */
2
#include "tm4c_cmsis.h"
3
4
extern int CSTACK$$Limit;
5
void __iar_program_start(void);
6
void Unused_Handler(void);
7
8
9
int const __vector_table[] @ ".intvec" = {
10
    (int)&CSTACK$$Limit,
11
    (int)&__iar_program_start,
12
    (int)&NMI_Handler,
13
    (int)&HardFault_Handler,
14
    (int)&MemManage_Handler,
15
    (int)&BusFault_Handler,
16
    (int)&UsageFault_Handler,
17
    0,  /* Reserved */
18
    0,  /* Reserved */
19
    0,  /* Reserved */
20
    0,  /* Reserved */
21
    (int)&SVC_Handler,
22
    (int)&DebugMon_Handler,
23
    0,  /* Reserved */
24
    (int)&PendSV_Handler,
25
    (int)&SysTick_Handler,
26
27
    /* external interrupts (IRQs) ... */
28
    (int)&GPIOPortA_IRQHandler,    /* GPIO Port A                  */
29
    (int)&GPIOPortB_IRQHandler,    /* GPIO Port B                  */
30
    ...
31
    ...
32
};
33
...

Was ich außerdem glaube verstanden zu haben ist, dass Interrupt-Handler 
ebenso wie Exception-Handler per Default in eine
Dauerschleife greifen, diese allerdings, falls sie genutzt werden sollen 
überladen / überschrieben werden können.

Was ich aktuell noch nicht verstehe ist :
 - An welcher Stelle wird der Startup-Code mit eingebunden?
   Wird der irgendwo "included" oder als Parameter beim kompilieren mit 
übergeben?
 - Wie kommt man im normalen Fall an diese Dateien, also Startup-Code, 
Linkerscripte und co.?
   Werden die vom Hersteller zur Verfügung gestellt oder gibts die nur 
im Forum des Vertrauens oder aus eigener Feder?

Vielen Dank noch mal für eure super Hilfe :) :)
Ich werde mich jetzt mal an meinem neuen ST-Link Programmier-Adapter 
wagen, mal sehen ob der PC den irgendwie erkennt.

Viele Grüße und ein schönes Wochenende
anon1234

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Anon A. schrieb:

>  - An welcher Stelle wird der Startup-Code mit eingebunden?

Im Buildscript/Makefile als zusätzliche Quelltextdatei. Oder auch als 
weiteren Parameter beim direkten Kommandozeilen-Aufruf von GCC.

Ich mache den Startup-Code als C-Datei, wobei Teile davon 
Inline-Assembler sind. Besonders die Nullinitialisierung des RAM geht 
nur in Assembler. Dadurch ist es nur eine weitere C-Datei für den GCC, 
aus der ebenso wie beim Rest dann eine Objektdatei (.o) rausfällt.

>  - Wie kommt man im normalen Fall an diese Dateien, also Startup-Code

Indem man ihn schreibt oder aus bestehenden Projekten kopiert. Es gibt 
ja haufenweise STM32-Projekte im Netz, die man sich da mal ansehen und 
abändern kann.

> Linkerscripte und co.?

Dito.

von Anon A. (anon1234)


Lesenswert?

Anon A. schrieb:
> Kommen wir nun mal zu den Sachen die ich noch nicht ganz verstehe :
>
> Nils S. schrieb:
>> Vor ca. einer Woche hab' ich ein Blinky-Programm für diese China-Boards
>> mit C8T6 drauf auf github hochgeladen. Mit Makefile, Linker-File, ...
>>
>> https://github.com/spacerace/stm32f103c8t6-blinky
>>
>> Flashen mit st-util auf Linux, geht mit STLink V2 und den China-Clones
>> problemlos.
> Vielen Dank für den Link, das Projekt sieht ganz nach dem aus, was ich
> zum Starten brauche.
> Leider habe ich beim kompilieren immer wieder Probleme. Der Befehl make
> all führt hierbei immer zu dem folgenden Fehler. Leider habe ich bis
> jetzt noch keine Lösung gefunden um das ganze zu beheben. Hat hier
> jemand eine Idee woran das ganze liegen könnte?
>
1
> $ make all
2
> /usr/lib/gcc/arm-none-eabi/4.8/../../../arm-none-eabi/bin/ld: cannot 
3
> open map file output/main.map: No such file or directory
4
> collect2: error: ld returned 1 exit status
5
> Makefile:61: recipe for target 'output/main.elf' failed
6
> make: *** [output/main.elf] Error 1
7
>

Hier ein kleiner Nachtrag :
es fehlte mir um das map-file zu erzeugen der Ordner output, da dieser 
nicht über git mit gekommen war. Nach dem Erzeugen des Ordners konnte 
ich mit "make all" das Projekt erfolgreich übersetzen :
1
$ sudo mkdir output
2
$ sudo make all
3
arm-none-eabi-objcopy -Obinary output/main.elf output/main.bin
4
arm-none-eabi-objdump -S output/main.elf > output/main.lst
5
arm-none-eabi-size output/main.elf
6
   text     data      bss      dec      hex  filename
7
    908        0      512     1420      58c  output/main.elf

von Andreas R. (daybyter)


Lesenswert?

Wenn es mal schnell gehen soll, kannst Du auch einfach die Arduino IDE 
nehmen.

http://www.stm32duino.com

von W.S. (Gast)


Lesenswert?

Anon A. schrieb:
> Was ich aktuell noch nicht verstehe ist :
>  - An welcher Stelle wird der Startup-Code mit eingebunden?
>    Wird der irgendwo "included" oder als Parameter beim kompilieren mit
> übergeben?
>  - Wie kommt man im normalen Fall an diese Dateien, also Startup-Code,
> Linkerscripte und co.?
>    Werden die vom Hersteller zur Verfügung gestellt oder gibts die nur
> im Forum des Vertrauens oder aus eigener Feder?

Oh, so viele Fragen.

Also:
Mit Compiler und Assembler übersetzt du dir deine diversen Quellen ins 
Objektformat (name.o). Anschließend mußt du den Linker aufrufen und ihm 
sagen, was er denn so alles zusammenlinken soll. Das macht der dann 
auch, wobei er auch die diversen Bibliotheksfunktionen aus den 
zuständigen Bibliotheken mit dazulinkt (z.B. Integer-Division, 
double-Addition, und sonstigen Kram, den nur der Compiler kennt und 
benutzt). Dazu muß der Linker auch wissen, für welche Zielplattform das 
Ganze gut sein soll, damit er die richtige Laufzeit-Bibliothek benutzt.

Der Startupcode ist nun eben eine Quelle wie alle anderen auch in deinem 
Projekt. Es muß bloß dafür gesorgt sein, daß er an die richtige Stelle 
kommt. Zu diesem Zweck muß er entsprechend gekennzeichnet werden, also 
etwa so:
  AREA    RESET, CODE, READONLY
Im Prinzip kann man dem Linker auch anderweitig verklickern, daß man 
eben genau diese Objektdatei als allererste im Endergebnis haben will. 
Wie man das nun genau macht, hängt von der Toolchain ab. Ich benutze den 
Keil und wenn du nen Gcc benutzen willst, geht das ein bissel anders. 
Das Obige sähe daher beim Gcc etwa so oder so ähnlich aus:
 .section  .text.startup
 .func     Vectors

Ich selber arbeite nicht mit Makefiles und Linkerscripts, aber dafür mit 
.xcl also mit "extended command line" Dateien. Da sieht die link.xcl 
etwa so aus:
--cpu=Cortex-M0
--ro-base 0x00000000
--first startup_lpc11e12.o
--entry=Kaltstart
startup_lpc11e12.o
main.o
und alle anderen .o

Beachte, daß die Reihenfolge normalerweise (jedenfalls beim Keil) 
herzlich egal ist. Der Linker bildet intern aus allen Objekt-Stücken, 
die er zusammenlinken muß einen Pool, und ordnet dessen Bestandteile so 
an, daß es alles am besten zusammenpaßt. Ein typisches Beispiel ist der 
Code, der für die eventuelle Code-Protektion an eine ganz bestimmte 
Stelle plaziert werden muß, siehe sowas:
1
                AREA    |.ARM.__at_0x2FC|, CODE, READONLY
2
                EXPORT  CRP_Key
3
4
CRP_Key         DCD     0xFFFFFFFF
5
6
                ALIGN
7
                END
Das ist der Inhalt einer ganz kleinen Assemblerquelle, die eben genau 
auf 2FCh plaziert werden muß. Deshalb ist dort die Area-Adresse 
dediziert festgelegt. Den Platz zwischen Startup und diesem Code füllt 
dann der Linker nach seinem Gusto - und er schafft es normalerweise 
recht gut, ihn bis auf's letzte Tüpfelchen zu füllen, so daß man keinen 
Flash vergeigt.

So, dir ist jetzt klar, daß der Startupcode chipspezifisch ist, also auf 
den konkreten Chip zugeschnitten. Der CRP-code ist nicht nur 
chipspezfisch, sondern auch projektspezifisch.

Also, da du ja dediziert mit dem STM32F103C8T6 basteln willst, lade dir 
einfach mal dies hier herunter:
http://www.mikrocontroller.net/attachment/316790/STM32F103C8T6.ZIP
Da kannst du eigentlich alles drin sehen, was ich hier als Prosa 
geschrieben habe. Obendrein kannst du als HW-Basis einen der üblichen 
billigen chinesischen ST-Link-v2 verwenden, wenngleich auch die HW doch 
recht eingeschränkt ist, weil viele Pins nirgends herausgeführt sind.

Was man dafür braucht wäre die Freewareversion vom Keil und die 
Bastelversion von Eagle. Zum Angucken, wie da was abgeht, reicht es aber 
aus, einfach reinzugucken.

W.S.

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

Anon A. schrieb:
> $ make all
> /usr/lib/gcc/arm-none-eabi/4.8/../../../arm-none-eabi/bin/ld: cannot
> open map file output/main.map: No such file or directory
> collect2: error: ld returned 1 exit status
> Makefile:61: recipe for target 'output/main.elf' failed
> make: *** [output/main.elf] Error 1


Anon A. schrieb:
> Hat hier
> jemand eine Idee woran das ganze liegen könnte?

existiert der output-Ordner im Projektordner denn? Wenn nein, kann ich 
die ganze Ausgabe sehen?

von Anon A. (anon1234)


Lesenswert?

Hallo kruemeltee,

Nils S. schrieb:
> existiert der output-Ordner im Projektordner denn? Wenn nein, kann ich
> die ganze Ausgabe sehen?

vielen Dank für deine Antwort. Genau das war der Fehler. Hab es sogar 
schon gefunden und weiter oben auch schon gepostet. Aber das ist 
wahrscheinlich einfach in der Menge der Antworten untergegangen.
Jetzt kann ich das ganze bei mir erfolgreich kompilieren. Du hast mir 
auf jeden fall sehr geholfen.

Gruß
anon1234

von Anon A. (anon1234)


Lesenswert?

Hallo zurück :)

W.S. schrieb:
> Oh, so viele Fragen.

W.S. schrieb:
> Also, da du ja dediziert mit dem STM32F103C8T6 basteln willst, lade dir
> einfach mal dies hier herunter:
> http://www.mikrocontroller.net/attachment/316790/STM32F103C8T6.ZIP
> Da kannst du eigentlich alles drin sehen, was ich hier als Prosa

Vielen Dank für die Beantwortung meiner Fragen und die Beispielprojekte.
Das hilft mir echt sehr viel weiter.

Ich bin gerade überglücklich :) Mit eurer Hilfe habe ich jetzt ein 
erstes Blinky-Hello-World zum Laufen bekommen UND auch noch einigermaßen 
verstanden was dabei passiert und passieren muss.

Ich habe hierbei mit dem Beispiel-Projekt von kruemeltee gearbeitet, hab 
die main ein bisschen zum ausprobieren angepasst, das ganze mit dem 
make-file welches den arm-none-eabi-gcc benutzt kompiliert und mit 
st-flash und st-link V2 geflashed.
Als nächstes werde ich mir dann mal vornehmen c++ code zum laufen zu 
bekommen.
Andreas R. schrieb:
> Wenn es mal schnell gehen soll, kannst Du auch einfach die Arduino IDE
> nehmen.
>
> http://www.stm32duino.com
Und dann werde ich auch die von Arduino Variante mal austesten..

Und dann kann es losgehen mit dem Programmieren :)

Viele Grüße
anon1234

von Nop (Gast)


Lesenswert?

Anon A. schrieb:

> es fehlte mir um das map-file zu erzeugen der Ordner output, da dieser
> nicht über git mit gekommen war.

Wenn ich das recht entsinne, ist das ein Designfehler von Git, leere 
Ordner zu ignorieren. Der wurde nie behoben, und stattdessen behilft man 
sich notdürftig, indem man eine Datei ".gitignore" in einen leeren 
Ordner legt, damit er nicht mehr leer ist.

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

Anon A. schrieb:
> vielen Dank für deine Antwort. Genau das war der Fehler. Hab es sogar
> schon gefunden und weiter oben auch schon gepostet. Aber das ist
> wahrscheinlich einfach in der Menge der Antworten untergegangen.
> Jetzt kann ich das ganze bei mir erfolgreich kompilieren. Du hast mir
> auf jeden fall sehr geholfen.

Das freut mich, danke für Feedback :)

Nop schrieb:
> Wenn ich das recht entsinne, ist das ein Designfehler von Git, leere
> Ordner zu ignorieren. Der wurde nie behoben, und stattdessen behilft man
> sich notdürftig, indem man eine Datei ".gitignore" in einen leeren
> Ordner legt, damit er nicht mehr leer ist.

Da ich meine eigenen Repos eher gar nicht von github hole, sondern hier 
auf Platte habe, fiel mir das nicht auf. Ich werde dem gleich mal auf 
den Grund gehen. Danke.

von Nop (Gast)


Lesenswert?

Nils S. schrieb:
> Ich werde dem gleich mal auf den Grund gehen. Danke.

Der Fehler scheint schon beim Hochladen auf Github passiert zu sein, da 
ist nämlich auch schon kein Ordner "output". Vermutlich, weil er beim 
Autoren auf der Platte leer war und Git den deswegen nicht hochgeladen 
hat. Dann war der Autor nicht paranoid genug, um sein Projekt von Github 
wieder zu clonen und einen Build zu testen.

Eigentlich sollte man bei sowas auch durchaus die Binaries mit 
hochladen, finde ich. Das Erste, was ich mit embedded-Projekten nach dem 
Runterziehen mache, ist nämlich ein Build. Dann prüfe ich, ob (bei 
selber Compilerversion) das Binary identisch ist. Wenn ja, habe ich 
meine Toolchain erfolgreich verifiziert. Ich hab bei sowas schon Fehler 
in der Versionierung gefunden, daß relevante Dateien vergessen wurden 
einzuchecken.

von Nop (Gast)


Lesenswert?

Nop schrieb:
> Vermutlich, weil er beim Autoren auf der Platte leer war

Nochmal geguckt: im Wurzelverzeichnis ist die Datei .gitignore, und die 
gilt dann rekursiv fürs ganze Projekt. Dort werden genau die Dateitypen 
vom Tracking ausgeschlossen, die im Output-Ordner entstehen: 
Objektdateien, Binfiles, Hexfiles, Listings, Mapfiles.

Somit ist der output-Ordner für Git logisch gesehen leer, selbst wenn 
dort physikalisch Dateien drin sind, aber die werden halt nicht 
getrackt. Das erklärt auch, wieso der Autor nicht drauf kam, dort eine 
Dummy-Datei hinzulegen, weil das nicht mehr so offensichtlich ist.

Wieder was gelernt. :-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.