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.
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
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.
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:
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
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.
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
intmain(void){
2
inti;
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?
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
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:
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=zFAnW7Tzu4Uhttps://www.youtube.com/watch?v=DfiwWxTRIZEhttps://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
>;VectorTableMappedtoAddress0atReset
2
>AREARESET,CODE,READONLY
3
>PRESERVE8
4
>THUMB
5
>
6
>
7
>EXPORT__Vectors
8
>;AddrVectorTyp
9
>__VectorsDCD__initial_sp;00Topof
10
>Stack
11
>DCDKaltstart;41Reset
12
>Handler
13
>DCDNMI_Handler;82Non
14
>MaskableInterrupt
15
>DCDHardFault_Handler;C3
16
>Cortex-M0SVHardFaultInterrupt
17
>DCDMemoryManagement_Handler;104
18
>Cortex-M0MemoryManagementInterrupt
19
>DCDBusFault_Handler;145
20
>Cortex-M0BusFaultInterrupt
21
>DCDUsageFault_Handler;186
22
>Cortex-M0UsageFaultInterrupt
23
>DCDReserved;1C7
24
>DCDReserved;208
25
>DCDReserved;249
26
>DCDReserved;2810
27
>DCDSVC_Handler;2C11
28
>Cortex-M0SVCallInterrupt
29
>DCDDebugMonitor_Handler;3012
30
>Cortex-M0DebugMonitorInterrupt
31
>DCDReserved;3413
32
>DCDPendSV_Handler;3814
33
>Cortex-M0PendSVInterrupt
34
>DCDSysTick_Handler;3C15
35
>Cortex-M0SystemTickInterrupt
36
>
37
>...usw.
38
>
39
>KaltstartPROC
40
>IMPORTmain
41
>IMPORTsysconfig
42
>
43
>hierkommtderersteBefelrein
44
>...
45
>WarmstarthierkommtallesfürdenWiederanlaufrein
46
>...
47
>
48
>LDRR0,=sysconfig
49
>BLXR0;Systemkonfigurationaufrufen
50
>
51
>CPSIEi
52
>LDRR0,=main
53
>BLXR0;mainaufrufen
54
>BWarmstart
55
>ENDP
56
>
57
>..hierkommendanndieDefaulthandlerrein
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
externintCSTACK$$Limit;
5
void__iar_program_start(void);
6
voidUnused_Handler(void);
7
8
9
intconst__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
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.
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?>
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 :
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
EXPORTCRP_Key
3
4
CRP_KeyDCD0xFFFFFFFF
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.
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 1Anon 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?
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
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
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.
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.
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.
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. :-)