Hallo, verstehe ich es richtig, dass durch die Boot Pins veranlasst wird, dass der System Memory in 0x1fff0000 - 0x1fff77ff auf die Adresse 0x00000000 gemappt wird? Heißt das, ich kann meinen Bootloader wie ein gewöhnliches Programm schreiben und muss es nur an die Position 0x1fff0000 schreiben und aus Sicht des µC ist es das normale Programm an Adresse 0x00? Inkl. Interrupt Vectoren? Dann muss ich, wenn der Bootloader beendet wird, dem µC ja auch mitteilen, dass er jetzt an Adresse 0x00 ohne Bootpin starten soll oder? Führe ich dann den Code ab Adresse 0x08000000 aus, inkl. verbiegen der Interrupt Vectoren? Oder resete ich quasi irgendwie den µC, dass jetzt aber nicht der System Memory gemappt wird? Ich glaube über SYSCFG kann ich im Betrieb das Mapping wieder ändern? im NVIC gibt es ja ein Register zum mappen der IRQ. Die Positionierung des Bootloaders im Flash mache ich bestimmt mit einem Linkerkommando oder gleich das Linker-Script? Mein Prozessor ist ein STM32F413VGT6. Danke für eure Hilfe!
Nils N. schrieb: > und muss es nur an die Position 0x1fff0000 schreiben Zeig mal, wenn du das geschafft hast ;)
Steht das etwas von Flash? Nein. Und einen ROM zu beschreiben ist schwierig.
Ich habe mal einen Bootloader für stm32f411 (mit CubeMX und gcc) geschrieben. In der CubeMX repository befindet sich ein Beispiel für einen Bootloader. bei mir (Ubuntu) wars zu finden unter: ~/STM32Cube/Repository/STM32Cube_FW_F4_V1.16.0/Projects/STM324xG_EVAL/Ap plications/LwIP/LwIP_IAP Das war aber tückisch, habe mehrere Tage debuggen müssen, um herauszufinden was am User Programm dann zu beachten ist. Bootloader wird als normales Programm geschrieben. Startadresse 0x8000000. zu ladendes Anwendungsprogramm: Startadresse bei mir 0x8010000 dann den linkerfile entsprechend anpassen und jetzt kommt das merkwürdige: im sourcefile system_stm32f4xx.c, das von CubeMX generiert wird, musst Du noch einen offset anpassen Zeile suchen: #define VECT_TAB_OFFSET 0x00 jetzt umdefinieren in 0x10000 (bei mir startete das User programm bei 0x8010000, musst Du entsprechend anpassen) Wenn Du das nicht machst laufen keine interrupts mehr, nur blinky geht. Das Ganze wird in einer UM17xx von STM beschrieben. Was die sich bei STM dabei gedacht haben ist mir allerdings schleierhaft. Startadresse des Anwendungsprogramms als offset (!) im Anwendungsprogramm hardcoded in der source notwendig. Portabel ist anders.
Das Thema hatten wir doch schon mal. Es wäre besser das dem CubeMX Schöpfer mitzuteilen. Weiß nicht mehr genau wo, aber der hat in einem Beitrag auf Anfragen und Kritik zu CubeMX reagiert und die Vorschläge in der nächsten Version eingebracht.
pegel schrieb: > Es wäre besser das dem CubeMX Schöpfer mitzuteilen. Oh, das wird dann aber ein längerer Erguss. Bin recht enttäuscht von CubeMX. Idee ist ja gut, aber performance . . .
Ich finde, über die Versionen gesehen, hat es sich aber schon recht gut entwickelt. Und mindestens einer ist aktiv dran.
Wenn ich den vorprogrammierten Bootloader nicht verändern kann verstehe ich es so, dass ich meinen Bootloader ganz normal auf den Prozessor lade. Für diesen sind z.B. die ersten 16kByte reserviert. Das eigentliche Programm startet erst ab der Adresse 0x4000. Wenn der Bootloader, welcher dann ja immer zuerst startet ab Adresse 0x00, entscheidet, dass das Hauptprogramm gestartet werden soll, springt dieser zur Adresse 0x4000. Das Hauptprogramm muss dann nur noch die IRQ Tabelle umbiegen. Geht diese Vorangehensweise?
Ja, so geht das schon. Die Linkerfiles müssen halt zusätzlich passen, für Bootloader und Applikation. Interrupts vor dem Sprung abschalten und den Watchdog zumindest länger stellen hat mir geholfen.
Im Prinzip ja. Bootloader und Programm müssen auf die Adressen 0x08000000 bzw. 0x08004000 gelinkt werden müssen. Die Adressen 0x0 und 0x4 werden nur unmittelbar nach dem Reset von der CPU benutzt um SP und PC zu initialisieren. 0x4000 wird nicht benutzt. Der Bootloader muss nicht zu 0x4000 und auch nicht zu 0x08004000 springen, sondern zu der Adresse, die in 0x08004004 steht. Der Stack Pointer muss mit dem Inhalt von 0x08004000 geladen werden. Das kann der Bootloader oder die crt0 des Hauptprogramms machen. Wenn du eine vorgefertigte crt0 benutzt, solltest du das heraus finden, weil, wenn die das macht, kannst du den Bootloader zu 100% in C schreiben. Die Vektortabelle des Hauptprogramms kann irgendwo stehen, sie muss nur auf 512-Byte aligned sein (oder 1024- oder 2048-Byte, je nach NVIC). Das Hauptprogramm könnte sogar die vom Bootloader mit benutzen wenn der keine Interrupts braucht.
stromverdichter schrieb: > Interrupts vor dem Sprung abschalten und den Watchdog zumindest länger > stellen hat mir geholfen. würde ich schärfer formulieren. (Alle Einträge in der IVC bekommen ja praktisch neue Behandlungsroutinen) Alles, was Du vor dem Sprung an Hardware initialisiert hast, wieder deinitialisieren. Dann erst springen.
http://www.springer.com/de/book/9783658148492 , runterscrollen auf Zusatmaterial, herunterladen, Kapitel 9. Beschreibung der Internals im Buch. Man kann den internen BL für sich mitnutzen, sehe ich aber äusserst selten. Normalerweise ist der gelieferte Bootloader resident und per Jumper aktivierbar, um im Notfall (zerschossener Applikationsbootloader) eine Notfallebene zu haben sowie der Produktion eine Hilfe zum Flashen des Urimages zu geben. Ist äusserst praktisch! Frohe Feiertage!
Prima, dann werde ich erstmal diesen Weg begehen und schauen, wie weit ich komme.
Nils N. schrieb: > Dann muss ich, wenn der Bootloader beendet wird, dem µC ja auch > mitteilen, dass er jetzt an Adresse 0x00 ohne Bootpin starten soll oder? > > Führe ich dann den Code ab Adresse 0x08000000 aus, inkl. verbiegen der > Interrupt Vectoren? Deinen normalen Code kannst du getrost ab Adresse 0 beginnen lassen. Alles was im eingebauten User-Flash (ab 0x08000000) steht, ist auch ab Adresse 0 les- und ausführbar. Lediglich dann, wenn man den eingebauten Bootlader benutzen will und deshalb die "BOOT"-Pin(s) beim Reset entsprechend setzt, wird der Bootlader ab Adresse 0 eingeblendet. Dann kann logischermaßen auf den Flash nur über den Adreßbereich ab 0x08000000 zugegriffen werden. Aber im Normalfall brauchst du dich um den Bereich ab 0x08000000 nicht zu kümmern. Deinen Code kannst du getrost ab 0 linken. W.S.
W.S. schrieb: > Deinen normalen Code kannst du getrost ab Adresse 0 beginnen lassen. > Alles was im eingebauten User-Flash (ab 0x08000000) steht, ist auch ab > Adresse 0 les- und ausführbar. > Aber im Normalfall brauchst du dich um den Bereich ab 0x08000000 nicht > zu kümmern. Deinen Code kannst du getrost ab 0 linken. Was wäre der Vorteil vom Code ab 0? Normalerweise will man auf Adressen unterhalb von 0x08000000 garnicht zugreifen und Zugriffe darauf per MPU verbieten. Der eingebaute Bootloader hat zwei entscheidende Vorteile: das Flashen funktioniert mit jedem Chip, das Programm auf dem PC muss nicht die verschiedenen Flash-Algorithmen kennen. Und man kann ggf. den SWD-Stecker weglassen und die Pins für etwas nützliches nutzen. Also, ich bleibe bei 0x08000000...
Ich nochmal. Wenn ich mit folgendem Code ein Offset von 10 für die IRQ Tabelle vorgebe und dann an die Adresse 0x0800000A mein Programm flashe, wird dieses nicht korrekt ausgeführt. Auch wenn ich gar keine Interrupts verwende. Was mache ich da falsch? Ich mache vorher einen Chiperase, es sollte dann ja überall vorher 0xFF stehen.
IRQ Nils N. schrieb: > Ich nochmal. Wenn ich mit folgendem Code ein Offset von 10 für die > IRQ > Tabelle vorgebe und dann an die Adresse 0x0800000A mein Programm flashe, > wird dieses nicht korrekt ausgeführt. Auch wenn ich gar keine Interrupts > verwende. > > Was mache ich da falsch? Ich mache vorher einen Chiperase, es sollte > dann ja überall vorher 0xFF stehen. Der Offset braucht immer ein bestimmtes alignment. Hängt vom konkreten chip ab. Sprich Offset von 10 geht nicht. Beim STM32F411 sind es z.B. 512 byte alignment. Also Offset muss durch 512 teilbar sein.
Ok, das sehe ich auch grade, dass der Wert "1" 128 Wörtern entspricht, also der Adresse 0x08000100. Aber auch ohne Benutzung der Interrupts wird mein Code nicht mehr ausgeführt, wenn ich diesen an 0x08000100 schreibe und vorher den Flash lösche. Es handelt sich um ein simples LED-Hello World.
Nils N. schrieb: > Ok, das sehe ich auch grade, dass der Wert "1" 128 Wörtern > entspricht, > also der Adresse 0x08000100. > > Aber auch ohne Benutzung der Interrupts wird mein Code nicht mehr > ausgeführt, wenn ich diesen an 0x08000100 schreibe und vorher den Flash > lösche. Es handelt sich um ein simples LED-Hello World. Ok, raffe gerade nicht, was Du gerade probierst. Wenn der STM aus dem reset kommt erwartet er eine Sprungtabelle ab Adresse 0x8000000. Das ist die Sprungtabelle Deines bootloaders. Dein Anwendungsprogramm liegt beispielweise bei Adresse 0x8010000. Dort brauchst Du die Sprungtabelle Deines Anwendungsprogramms. Wenn Du das lädst, darfst Du den bootloader natürlich nicht löschen. Also keinen chip erase machen. Sondern nur den Teil des flash löschen, den dein Anwendungsprogramm braucht. Den Teil sollte Dein bootloader Programm bewerkstelligen.
Ich versuche ledichlich erstmal ein Programm an eine andere Adresse zu schreiben, manuell ohne irgendeinen Bootloader. LED-Hello World Programm an Adresse 0x08000100 schreiben. Der STM32 bootet und mappt 0x08000100 an Adresse 0x00000000. Dann kommt an Adresse 0x00 bis 0xff nur "nop" und dann das Programm. Ich benutze da noch gar keine Interrupts, aber bereits das LED blinken wird nicht ausgeführt.
Nils N. schrieb: > LED-Hello World Programm an Adresse 0x08000100 schreiben. Das alleine reicht nicht. Damit es funktioniert muss es auch für diese Basisadresse compiliert bzw gelinkt sein.
STM Apprentice schrieb: > Nils N. schrieb: >> LED-Hello World Programm an Adresse 0x08000100 schreiben. > > Das alleine reicht nicht. Damit es funktioniert muss es auch > für diese Basisadresse compiliert bzw gelinkt sein. das reicht auch noch nicht. An den ersten Stellen ist nämlich kein ausführbarer Code, sondern eine Tabelle. Die fängt an mit dem Inhalt des Stackpointers, dann der Adresse des ausführbaren Programms, dann interrupt Vektoren. Ich befürchte, TO muss noch einiges lesen. Wenn die CPU aus dem Reset kommt, erwartet sie vernünftige Werte an der Stelle 0x8000000. Es sei denn, man benutzt die internen Bootloader des STM
Also: In den Systemspeicher (also an die Stelle des internen Bootloaders) kannst du nicht schreiben, da es ein Nur-Lese-Speicher (sprich ROM) ist. Der interne Bootloader ist für alle STM32-Controller fest einprogrammiert. Einen eigenen Bootloader kompilierst du vorzugsweise für die übliche Flash-Startaddresse 0x08000000. In diesem Bootloader machst du zunächst deine ganze Boot-Magie (z.B. Systemcheck, Application-Image verifizieren o.ä.), sofern notwendig. In diesem Bootloader definierst du bereits den Offset zur Startaddresse der Vektortabelle deines Anwendungsprogramms (z.B. 0x08004000). Wenn ich mich recht erinnere, wird VECT_TAB_OFFSET relativ zur Basisaddresse angeben, hier also 0x4000. Beachten musst du hierbei, dass die Vektortabelle immer an einer bestimmten Bytegrenze ausgerichtet sein muss (je nach Controller bei Vielfachen von 512, 1024 oder 2048 Byte; das musst du im Datenblatt nachsehen). Bevor nun der Sprung zum Anwendungsprogramm erfolgt, musst du sämtliche initialisierte Peripherie deinitialisieren sowie alle Interrupts abschalten. Das Anwendungsprogramm soll ja ein System vorfinden, so als ob vorher nie was anderes gelaufen ist (quasi ein vorgegaukelter Kaltstart). Dein Anwendungsprogramm entwickelst du nun, wie du es sonst auch gewohnt bist, mit einer Änderung: in deinen Linker-Einstellungen musst du dein Programm nun bei der vorher festgelegten Startaddresse 0x08004000 starten lassen und natürlich die verfügbare Flash-Größe um diese 0x4000 Bytes reduzieren. Nun noch den Bootloader an die Addresse 0x08000000 und dein Anwendungsprogramm fortan an 0x08004000 flashen und fertig ist die Laube. Natürlich solltest du beim Chip Erase darauf achten nicht die Sektoren mit deinem Bootloader mitzulöschen ;) Gruß
A.. P. schrieb: > Dein Anwendungsprogramm entwickelst du nun, wie du es sonst auch gewohnt > bist, mit einer Änderung: in deinen Linker-Einstellungen musst du dein > Programm nun bei der vorher festgelegten Startaddresse 0x08004000 > starten lassen und natürlich die verfügbare Flash-Größe um diese 0x4000 > Bytes reduzieren. Wenn ich mein LED Blink Programm, welches noch komplett ohne Interrupts arbeitet über das Linkerscript von 0x8000000 auf 0x8000100 umpositioniere arbeitet es nicht, sprich: kein LED blinken. Auch nicht beim vorherigen Chip erase. Im generierten Hex sehe ich auch, dass nun korrekt alles an 0x8000100 gelinkt wurde. Sollte der Prozessor bei einem vorherigen Chiperase nicht bis 0x8000100 durchgehen und dann mein korrekt gelinktes Programm ausführen, z.B. wenn der Bootloader noch gar nicht programmiert ist? Meinn Programm ist klein und simpel (noch für einen STM32F407, weil ich dafür ein Discoveryboard hoer habe):
1 | #include "stm32f4xx.h" |
2 | |
3 | #define T_OUTPUT 0x01
|
4 | #define MODE(a,b) ((b)<<(2*a))
|
5 | #define SET(a) (1<<(a))
|
6 | |
7 | #define F_CPU 16000000
|
8 | |
9 | volatile static uint16_t temp = 0; |
10 | |
11 | int main(void){ |
12 | //SCB->VTOR = (1UL<<9);
|
13 | |
14 | RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; |
15 | RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; |
16 | |
17 | GPIOD->MODER |= MODE(12, T_OUTPUT); |
18 | GPIOD->MODER |= MODE(13, T_OUTPUT); |
19 | GPIOD->MODER |= MODE(14, T_OUTPUT); |
20 | GPIOD->MODER |= MODE(15, T_OUTPUT); |
21 | |
22 | TIM6->PSC = 256; |
23 | TIM6->ARR = F_CPU/256; |
24 | TIM6->CR1 |= TIM_CR1_CEN; |
25 | |
26 | while(1){ |
27 | if(TIM6->SR & TIM_SR_UIF){ |
28 | TIM6->SR &= ~TIM_SR_UIF; |
29 | GPIOD->ODR ^= SET(14); |
30 | GPIOD->ODR ^= SET(15); |
31 | }
|
32 | }
|
33 | }
|
Ich habe auch mal ein zweites Programm zusätzlich reinprogrammiert als Bootloader Ersatz, welches einfach nur an die Adresse 0x8000100 springen soll. Das geht leider auch nicht:
1 | #include "stm32f4xx.h" |
2 | void (*start)(void) = 0x8000100; |
3 | |
4 | int main(void) |
5 | {
|
6 | start(); |
7 | for(;;); |
8 | }
|
>>Sollte der Prozessor bei einem vorherigen Chiperase nicht bis 0x8000100 >>durchgehen und dann mein korrekt gelinktes Programm ausführen, z.B. wenn >>der Bootloader noch gar nicht programmiert ist? So ein Verhalten kenne ich nur vom AVR. Da ist zufällig(?) 0xff ein NOP. Das ist beim ARM komplett anders. Der erwartet ab 0x8000000 eine Tabelle, keinen ausführbaren Code. Bei Deinem Beispiel ab 0x8000000 stehen erstmal 0xff macht der folgendes: er lädt 0xffffffff in sein Stackpointer (aber da ist gar kein RAM) dann lädt er 0xffffffff in ein Register und springt an diese Adresse 0xffffffff, (aber da ist gar kein Code.) So springe ich aus dem bootloader in das applikationsprogramm und das funzt /snip /* Jump to user application */ JumpAddress = *(__IO uint32_t*) (MY_PROGRAM_START_ADDRESS + 4); Jump_To_Application = (pFunction) JumpAddress; /* Initialize user application's Stack Pointer */ __set_MSP(*(__IO uint32_t*) MY_PROGRAM_START_ADDRESS); Jump_To_Application(); /* do nothing */ while(1); /snap Der Code holt sich aus dem Applikationsprogramm die Adresse für den Stackpointer und die Einsprungpunkt für das Programm und springt dann dahin. Falls Dein Applikationsprogramm den VTOR nicht setzt, musst Du den auch noch vor dem JumpTo_Application() setzen.
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.