Forum: Mikrocontroller und Digitale Elektronik STM32, Atollic, C: Tutorialempfehlung & einige Fragen


von Viktor B. (coldlogic)


Lesenswert?

Hey Leute,

Nach ein paar Projekten hab ich mich an die Implementierung eines 
Bootloaders dran gesetzt. Diesen will ich für einen STM32 programmieren 
- bei den STM8S, die ich ansonsten verwende, hab ich leider keine Option 
des On-Chip Debuggers (und das Ding ist bei solchen Unterfangen echt 
nützlich).

Also hab ich mich durch eine Menge Literatur und Beispielcode 
durchgelesen. Was in der Theorie passieren soll ist, soweit ich das 
verstehe, folgendes:

-Reset Vector wird angesprungen

-Durch den Linker Script an die Adresse 0x80000000 gesetzter Code des 
bootloaders wird direkt ausgeführt

- Erste Frage: bei all den Tutorials wird gesagt, main.c wäre der 
Eintrittspunkt im Programm. Muss der Bootloader main.c heißen oder wie 
wird ansonsten der Eintrittspunkt definiert? Oder gibt es da keine 
strenge Konvention und die Änderung am Linkerscript reicht aus/wird beim 
Build nicht überschrieben?

-Der Bootloader konfiguriert die Peripherie, in meinem Fall RCC, NVIC, 
I2C und TIM6. An diesem Punkt muss man die Interrupttabelle noch nicht 
für die eigentliche Application vorbereiten, man nutzt die vorhandene.

-Der Bootloader empfängt die Daten, schreibt sie ins Flash, führt das 
CRC durch etc..

-Falls alles erfolgreich verlaufen ist, schreibt der Bootloader auf eine 
einsame Adresse hinten im Flash die Firmwareversion als Flag dafür, dass 
alles geklappt hat.

-Dann sperrt er alle Interrupts und deconfiguriert die Peripherie. Die 
Interrupttabelle wird mit der Application schon mitgeschrieben? Wie 
verlinkt man die Interrupts von der ersten zu der zweiten Tabelle?

- Wie kann man zwischen Bootloader und der Application springen? Ich hab 
Konstrukte wie pFunction = &main(); gesehen, kann man die main wirklich 
durch einen Funktionspointer aufrufen?

- Und zu guter Letzt: Gibt es einen verständlichen Tutorial für STM32 in 
C, am besten mit auf Atollic basierender Toolchain? Hauptsache, dass 
alles auch an einem Beispiel erklärt wird. Die meisten bis jetzt sind 
nur Theorie mit ein wenig Pseudocode dazwischen.

Mit freundlichen Grüßen,
coldlogic

von STM32 Proggie (Gast)


Lesenswert?

Deine Fragestellung(en) verwirrt mich ein wenig.

Du schreibst so also ob du den kompletten Bootloader-Mechanismus
selbst konfigurieren und schreiben willst, also das Rad das
schon längst herumsteht neu erfinden willst.

Oder willst du nur bis ins letzte Bit alles nur begreifen was
nach dem Power-up passiert.

Ich habe es nicht richtig verstanden was du wirklich willst ....

von au Backe (Gast)


Lesenswert?

Viktor B. schrieb:
> -Reset Vector wird angesprungen
>
> -Durch den Linker Script an die Adresse 0x80000000 gesetzter Code des
> bootloaders wird direkt ausgeführt
>
> - Erste Frage: bei all den Tutorials wird gesagt, main.c wäre der
> Eintrittspunkt im Programm. Muss der Bootloader main.c heißen oder wie
> wird ansonsten der Eintrittspunkt definiert? Oder gibt es da keine
> strenge Konvention und die Änderung am Linkerscript reicht aus/wird beim
> Build nicht überschrieben?
>
> -Der Bootloader konfiguriert die Peripherie, in meinem Fall RCC, NVIC,
> I2C und TIM6. An diesem Punkt muss man die Interrupttabelle noch nicht
> für die eigentliche Application vorbereiten, man nutzt die vorhandene.

Sorry, aber aus den Punkten erkennt man, dass dir die Grundlagen über 
den Cortex (oder uC generell?) fehlen. Natürlich kann man einen 
Bootloader für den STM8 schreiben. Das ist zwar nicht ganz so 
komfortabel, aber wenn du den kleinen Kerl schon kennst, sicherlich 
einfacher für dich.
https://www.google.de/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&ved=2ahUKEwilnPrd__vkAhVPbVAKHVtwDoIQFjABegQIChAF&url=https%3A%2F%2Fwww.st.com%2Fresource%2Fen%2Fapplication_note%2Fcd00176595.pdf&usg=AOvVaw1GFsLGSH-MFj6gWAnpeFw0

von hfhd (Gast)


Lesenswert?

im prinzip kannst du mehrere applikationen im cortex laufen lassen

wichtig ist immer das die vector tabelle geladen wird und Stackpointer 
und Programmcounter auf die richtigen adressen zeigen
dann switcht er auch korrekt um

zb start ist default bei 0x8000000

bootloader liegt ab 0x8000000
- kann eigenes Projekt sein
- VTOR offset == 0
- bootloader hat eigene main()
- vor dem sprung in die applikation alles wieder auf default stellen
1
void boot_jump( unsigned int address ){
2
  SCB->VTOR  = address ;
3
  __asm(  "  ldr     SP, [R0]\n"
4
    "  ldr     PC, [R0, #4]");
5
}
6
7
int main(void)
8
{
9
  SystemClock_Config();
10
  HAL_Init();
11
  MX_GPIO_Init();
12
// hier tut der bootloader etwas ... was auch immer 
13
14
  HAL_DeInit();
15
  SysTick->CTRL = 0 ;
16
  // jump 
17
  boot_jump( 0x08010000 );
18
  while(1);
19
}


applikation liegt bei 0x8010000
- eigenes neues Projekt
- VTOR offset == 0x10000

zum bootloader über NvicSystemReset() oder das gleiche hier auch

alles wieder auf deinit und zur entsprechenden anwendung springen

kannst auch 10 bootloader so reinpacken .. oder 10 applikationen

man muss nur den Offset ändern , SP und PC laden


das Projekt sollte natürlich wissen das es bei 0x8010000 startet und 
nicht bei 0x8000000

von Stefan F. (Gast)


Lesenswert?

Viktor B. schrieb:
> bei all den Tutorials wird gesagt, main.c wäre der
> Eintrittspunkt im Programm.

Das ist nicht ganz richtig. Vom Reset Vektor aus
wird in der Regel ein Stück Assembler-Code angesprungen
(z.B. startup/startup_stm32.s), der den Speicher
initialisiert und dann die eventuell vorhandene
SystemInit() Funktion ausführt. Erst danach wird
die main() Funktions ausgeführt.

Wobei dieser Assembler-Code nicht in Stein gemeißelt
ist, den kann man sich so anpassen, wie man ihn haben will.

Wo du jetzt genau deinen Bootloader unterbringst, bleibt Dir
überlassen. Spannender ist die Tatsache, dass die Vektortabelle
des Bootloaders bei vielen STM32 Modellen zwangsläufig an Adresse
0x80000000 beginnen muss. Das eigentliche Anwendungsprogramm
muss daher an einer anderen Adresse beginnen und dementsprechend
gelinkt sein.

Der Bootloader muss die Position der Anwendungs-Spezifischen
Verktortabelle in irgendeinem Register (hab ich nicht im Kopf)
konfigurieren, bevor das Anwendungsprogramm angesprungen wird.

> Wie kann man zwischen Bootloader und der Application springen?

Mit Assembler, würde ich empfehlen

> Gibt es einen verständlichen Tutorial für STM32 in C

Sicher, aber bestimmt wird so ein Anfänger-Tutorial nicht
erklären, wie man Bootloader programmiert und wie man Anwendungen
programmiert, die darauf aufbauen. Das ist nämlich kein geeignetes
Thema für Anfänger.

Meine Anleitungen beziehen sich auf die System Workbench for STM32,
die dem Atollic Studio dehr ähnlich ist: 
http://stefanfrings.de/stm32/index.html

von Viktor B. (coldlogic)


Lesenswert?

STM32 Proggie schrieb im Beitrag #5990473:
> Oder willst du nur bis ins letzte Bit alles nur begreifen was
> nach dem Power-up passiert.

Genau das will ich - mit Learning by Doing den Mechanismus bis ins 
letzte Byte verstehen.

au Backe schrieb:
> Sorry, aber aus den Punkten erkennt man, dass dir die Grundlagen über
> den Cortex (oder uC generell?) fehlen.

Würden sie nich fehlen, hätte ich den Bootloader schon fertig. Also 
versuche ich das, was ich nicht weiß (und insbesondere das, wovon ich 
noch nicht weiß, dass ich es nich weiß), zu erkennen und zu lernen. Der 
erste Schritt wäre, dass mir jemand sagt, welche Grundlagen genau mir zu 
den Cortexen fehlen.

hfhd schrieb:
> bootloader liegt ab 0x8000000
> - kann eigenes Projekt sein
> - VTOR offset == 0
> - bootloader hat eigene main()
> - vor dem sprung in die applikation alles wieder auf default stellen
> void boot_jump( unsigned int address ){
>   SCB->VTOR  = address ;
>   __asm(  "  ldr     SP, [R0]\n"
>     "  ldr     PC, [R0, #4]");
> }

Danke sehr! Genau die Info habe ich gebraucht.

Stefanus F. schrieb:
> Der Bootloader muss die Position der Anwendungs-Spezifischen
> Verktortabelle in irgendeinem Register (hab ich nicht im Kopf)
> konfigurieren, bevor das Anwendungsprogramm angesprungen wird.

SCB->VTOR. Genau dieses Register gibt es bei mir aufm STM32F030 nicht - 
das hätte es so viel leichter gemacht... Naja, dann schaue ich mal 
weiter, wie und ob ich die Vektoren überschreiben kann. Übrigens, danke 
für den Link, die Anleitungen hab ich schon durchgeforstet, aber nichts 
bezüglich des Bootloaders gefunden.

So, ein paar Fortschritte hab ich heute gemacht, und zwar zwei Programme 
- eins fängt bei 0x8000000 an, ist 1 KB groß und springt am Ende an die 
Adresse 0x8001000. Das zweite fängt bei 0x8001000 an, blinkt natürlich 
mit ein paar LEDs und geht in eine Endlosschleife. Problem ist: es sind 
zwei verschiedene .hex - Dateien. Die werde ich mal versuchen 
zusammenzunähen, aber ich bin mir irgenwie sicher, dass es nicht auf 
einmal funktionieren wird. Was soll man da beachten? Außerdem hab ich 
immer noch nicht die Vektortabelle im Klartext gefunden... Mensch, das 
war bei AVR und STM8 deutlich einfacher :D

von Stefan F. (Gast)


Lesenswert?

Viktor B. schrieb:
> Außerdem hab ich
> immer noch nicht die Vektortabelle im Klartext gefunden

Durchsuche mal die Assembler Dateien *.s

von nfet (Gast)


Lesenswert?

Guck dir auch mal Cortex M0 devices generic user guide an.

von gre (Gast)


Lesenswert?

es gibt ein define

#define VECT_TAB_OFFSET  0x00


definiere das einfach in den einstellungen
mit dem offfset auf die 0x8000000

von gre (Gast)


Lesenswert?

das wird normalerweise irgendwo in der

SystemInit()

aufgerufen

von Viktor B. (coldlogic)


Lesenswert?

So, hab es heute geschafft, die beiden .hex-Dateien zusammenzuführen.
An die Leute mit demselben Problem, die diesen Post durch Google finden, 
es geht folgendermaßen:
- Hex-Dateien können mit dem Notepad geöffnet werden
- Erst die bootloader-hex vor dem Öffnen kopieren
- Dann in der Kopie die letzte Zeile ":00000001FF" (oder so ähnlich) 
löschen - Dann die application.hex öffnen, alles auswählen und kopieren
- Anschließend das Kopierte dort einfügen, wo die letzte Zeile der 
bootloader.hex stand
- Die Kopie der bootloader.hex, wo die ganze Veränderung stattgefunden 
hat, umbenennen zu boot-app-merged.hex oder ähliches
-???
-Profit

Das mit dem Debuggen hat auch wieder Tricks. Debuggen vom Bootloader 
geht ohne weiteres, Debuggen von der Application ist tricky - man muss 
per Debugscript dem Debugger mitteilen, wo der Programmeintrittspunkt 
ist. Bei Atollic also Debug Settings -> Startup Scripts -> folgende drei 
Zeilen einfügen oder schon vorhandene abändern:

#Reconfigure vector table offset
set *0xE000ED08 = %hier den app-offset einfügen als Zahl%

#Set application start pointer
set $sp = *(unsigned int*)%hier den app-offset einfügen als Zahl%

#Set app entry point
set $pc = *(unsigned int*)%hier den app-offset einfügen als Zahl, um 4 
incrementiert%

wenn man das nicht macht, riskiert man äußerst lustige Nebeneffekte.
Application läuft, aber der Debugger ist nicht verbunden; Debugger läuft 
nicht, aber man kann ihn auch nicht starten, weil er als laufend vom 
Atollic erkannt wird; Debugger läuft und kann nicht gestoppt werden 
etc.. Alles schon gehabt.

So, als letztes müsste ich noch herausfinden, wie man die Vektortabelle 
neu beschreibt. Da fehlen mir ehrlich gesagt Skills im (ARM-)Assembler. 
Alles was ich gefunden hab sind ein paar .isr-table einträge, die 
allerdings keine Adressen enthalten, sondern Labels wie TIM6_IRQHandler. 
Wo diese Labels herkommen..? Bis jetzt werden aus meiner Applikation die 
Interrupts des Bootloaders angesprungen.

Grüße,
coldlogic

von Viktor B. (coldlogic)


Lesenswert?

So, habs gelöst. Statt dem SCB->VTOR gibt es beim STM32F0 ein paar Bytes 
- SCB->MEMMAP oder so - die es zulassen, dass das RAM an die Adresse 
0x00000000 gemappt wird. Heißt also die Vectortabelle aus der 
Application an Anfang des RAM (0x20000000) kopieren und MEMMAP 
entsprechend umstellen - und schon läufts. In Linkerscripts sollen 
natürlich die Anfangsadressen für den RAM entsprechend angepasst werden, 
damit die Tabelle nicht überschrieben wird. Jetzt geht es an die 
Peripherie, aber das ist eine ganz andere Geschichte! Danke für die 
Hilfe an alle Beteiligten!

von NichtWichtig (Gast)


Lesenswert?

Cool!

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.