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
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 ....
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
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
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
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
Viktor B. schrieb: > Außerdem hab ich > immer noch nicht die Vektortabelle im Klartext gefunden Durchsuche mal die Assembler Dateien *.s
Guck dir auch mal Cortex M0 devices generic user guide an.
es gibt ein define #define VECT_TAB_OFFSET 0x00 definiere das einfach in den einstellungen mit dem offfset auf die 0x8000000
das wird normalerweise irgendwo in der SystemInit() aufgerufen
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
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!
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.