Servus zusammen, vielleich kann mir jemand einen Tipp geben: Beim STM32F303 möchte vom Applikationsprogramm auf Anforderung von außen den Bootloader starten und natürlich später auch vom Bootloader aus wieder in die App starten. Soweit ist das kein Problem - bislang wird das wie üblich durch Auslösen eines System-Resets bewerkstelligt. Nun hat diese Methode einen Nebeneffekt, den ich gerne vermeiden würde: Port B4 wird hier als Ausgang verwendet, wird aber beim Reset per Hardware erstmal als JTAG Reset-Eingang mit internem Pull-Up Widerstand konfiguriert. Die angeschlossene Schaltung sieht deshalb in dem Moment einen kurzen (> 2ms), unerwünschten High-Impuls. Um das zu vermeiden, wollte ich nun keinen Hardware-Reset benutzen, sondern einen reinen Software-Restart ausführen, d.h. einen Sprung zur Adresse des Reset-Vektors, nachdem alle Interrupts deaktiviert wurden und der Stackpointer mit dem korrekten Wert geladen wurde. Auch das wäre eigentlich kein Problem, aber der Start des Bootloaders wird aus einem Interrupt-Handler der Applikation ausgeführt. Der Bootloader-Code wird dadurch im "Handler"-Mode, statt im "Tread"-Mode gestartet. Wie bekomme ich die CPU am Besten in den "Thread"-Mode umgeschaltet? Ich bräuchte also im Prinzip einen "CPU Reset", ohne die Hardware zurückzusetzen. (Programmiere in C, Keil µVision) Danke!
Eine Möglichkeit wäre, im Interrupt nur ein Flag aufzusetzen für die Applikation und den Sprung aus der Applikation zu machen. Eine andere wäre, sich das Stack-Layout anzusehen und (in Assembler) kurzerhand die Return-Adresse umzubiegen, so daß er beim Return statt an die Stelle des Aufrufs eben in den Bootloader springt. Dann sollte man aber auch das LR mit passendem EXC_RETURN zurechtpfriemeln, weil der Plot sonst nicht mehr klappt, falls der Interrupt während eines anderen Interrupts aufgerufen wird. Das Programming Manual kann hier die Details liefern.
Ich hätte - ohne Anspruch auf Vollständigkeit - zwei Möglichkeiten anzubieten: 1. Du setzt in deinem Interrupt-Handler ein Flag und behandelst dieses dann in deiner Main-Loop, wenn der Controller wieder im Thread-Modus ist. Das ist meiner Meinung nach die gängigste Variante, weil ich auch davon ausgehe, dass der Reset nicht sonderlich zeitkritisch ist (auch wenn durch ISR ausgelöst). 2. Du manipulierst direkt den Stack z.B. mittels Inline-Assembler und setzt die Rücksprungadresse auf den Bootloader. Aus dem Handbuch (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/Babefdjc.html) geht hervor, dass während einer ISR grundsätzlich "EXC_RETURN" im Link-Register steht. Die eigentliche Rücksprungadresse holt sich der Controller dann vom Stack und die kannst du dann ändern. Außerdem solltest du dann den richtigen Modus im LR festlegen, falls EXC_RETURN auf den Handler-Mode verweisen sollte, weil deine ISR die den Bootloader anspringen soll eine andere ISR unterbrochen hat. Das klingt zwar alles ein bisschen kompliziert, sollte sich aber in < 10 Zeilen Assembler lösen lassen. PS: Nop war schneller aber schön das wir die gleiche Idee hatten ;)
:
Bearbeitet durch User
Christopher J. schrieb: > PS: Nop war schneller aber schön das wir die gleiche Idee hatten ;) Und sogar in derselben Reihenfolge. ^^
Servus, super - danke Euch für die Hinweise! Die CPU per Software aus dem Handler zu holen, ist dann ja anscheinend nur etwas aufwändiger durch Stack-Manipulationen, die dem Prozessor einen regulären Rücksprung vorspielen, möglich. Schade, ich hatte gehofft, es gibt da vielleicht sowas wie ein intrinsic, daß ich nur noch nicht gefunden hatte, a la "__setcpumode" oder so ähnlich, mit dem man entsprechende Bits im CPU-Status rücksetzen kann. Aber anscheinend gibt es hier keine solchen Bits, auf die das Programm auch noch direkten Schreibzugriff hat. Dann werde ich wohl eher die erste Möglichkeit wählen und die Softreset-Funktion irgendwie auf den Hauptthread hieven - das dürfte das kleinere Übel sein.
Thomas E. schrieb: > Schade, ich hatte > gehofft, es gibt da vielleicht sowas wie ein intrinsic, daß ich nur noch > nicht gefunden hatte, a la "__setcpumode" oder so ähnlich, mit dem man > entsprechende Bits im CPU-Status rücksetzen kann. So etwas ist mir bisher nicht bekannt. Das ganze ist vom Prinzip eigentlich relativ simple, auch wenn es ein bisschen wie ein Hack wirkt. Angenommen dein Bootloader liegt bei 0xDEADBEEF:
1 | void irgendein_IRQHandler(void) |
2 | {
|
3 | // hier besser keinen C-Code, da sich sonst der SP verändern kann
|
4 | |
5 | asm("ldr lr, =0xFFFFFFF9 \n\t" // EXC_RETURN für Thread Mode und MSP |
6 | "ldr r0, =0xDEADBEEF \n\t" // BL-Adresse in r0 laden |
7 | "str r0, [sp,#24] \n\t" // r0 in PC auf dem Stack speichern (Offset 24 Byte von SP) |
8 | );
|
9 | |
10 | // hier könnte noch weiterer C-Code stehen
|
11 | }
|
Ich habe es nicht getestet, also ohne Garantie aber wie gesagt eigentlich nicht so wild.
Man könnte an B4 auch einfach einen "stärkeren" Pull-Down Widerstand hängen. Dann stört der interne Pull-Up nicht.
Christopher J. schrieb: > Das ganze ist vom Prinzip eigentlich relativ simple Ja, Danke, das Prinzip habe ich (denke ich) verstanden und versuche auch schon den ganzen Tag, den Compiler dazu zu bewegen, irgendwelche Assembler-Befehle von mir zu akzeptieren. Leider ohne Erfolg - ich bekomme es nicht hin, auch nur einen einzigen Assembler-Befehl per Inline-Assembler in den Code einzuschleusen. Die Register kennt der Inline-Assembler offenbar nicht (nicht mal r0 oder R0) und laut Handbuch gibt es da auch jede Menge Restriktionen. Dann gibt es da auch einen "Embedded Assembler", der lt. Handbuch und meinem Verständnis davon tatsächlich einfach den ASM-Code einfügen sollte, wie er geschrieben wurde in der Form: __asm return-type function-name(parameter-list) { // ARM/Thumb assembly code instruction{;comment is optional} ... instruction } Das scheint der Compiler aber auch anders zu sehen, als sein Handbuch. Dann habe ich ein paar ASM-Befehle als "echte" Funktion in mein startup.s eingefügt. Durch EXPORT sollte dem Linker ja das Symbol dann bekannt sein - tut es aber leider nicht! Hat dazu jemand vielleicht einen Tipp und weiß wie es geht? (Keil µVision 5.24, Compiler Version 5.06, auf Compiler-Version 6 kann ich ohne größere Änderungen der Dateien nicht umsteigen). Danke! @Stefanus: das möchte ich nicht, um die Flexibilität zu behalten, den Port bei Bedarf auch als Eingang zu nutzen.
:
Bearbeitet durch User
Wenn ich mir das hier ansehe: http://www.keil.com/support/man/docs/armcc/armcc_chr1359124249383.htm "The inline assembler provides no direct access to the physical registers of an ARM processor. If an ARM register name is used as an operand in an inline assembler instruction it becomes a reference to a variable of the same name, and not the physical ARM register." Ein embedded-Compiler mit verkrüppeltem Inline-Assembler, unfaßbar. Also mit GCC ist das überhaupt kein Problem, aber der meint auch nicht, die Programmierer vor sich selbst schützen zu sollen.
Ha! Kurze Zwischeninfo: der "Embedded Assembler" funktioniert doch! Ich habe mich von der Fehleranzeige im Editor ins Boxhorn jagen lassen. Da sieht es aus, wie im Bild, bei Maus über dem roten Kreuz zeigt er "expected '(' after asm" an, aber es kompiliert fehlerfrei! Und ich bekomme tatsächlich eine Funktion, die aus dem "BX lr"-Befehl besteht (und nur aus diesem!) im Speicher! Das war jetzt nur ein Test, jetzt schreibe ich die richtige Funktion... Trotzdem wäre vielleicht nett, wenn mir evtl. noch jemand verraten könnte, wie man "richtigen" Assemblercode in einer xxx.s Datei schreiben kann, dessen Adresse der Linker dann auch kennt.
:
Bearbeitet durch User
Thomas E. schrieb: > Port > B4 wird hier als Ausgang verwendet, wird aber beim Reset per Hardware > erstmal als JTAG Reset-Eingang mit internem Pull-Up Widerstand > konfiguriert. Die angeschlossene Schaltung sieht deshalb in dem Moment > einen kurzen (> 2ms), unerwünschten High-Impuls. Dieses Problem hast Du doch auch beim Einschalten Deines Geräts. Wie löst Du das denn da? Hast Du da irgendeine Inhibit- oder Reset-Schaltung die dafür sorgt daß der Rest der Schaltung durch den Impuls nicht gestört wird? Kannst Du diese Schaltung nicht auch für das Springen in den Bootloader verwenden?
Gerd E. schrieb: > Dieses Problem hast Du doch auch beim Einschalten Deines Geräts. Das ist richtig, aber das gesamte System wird in der Regel gemeinsam eingeschaltet, da haben die falschen Impulse normalerweise noch keine negativen Auswirkungen. Kritischer sind eher die evtl. im Betrieb ausgelösten Warmstarts z.B. wegen einer Konfigurationsänderung, Firmware-Update o.ä.. Um es zu konkretisieren: es geht um die Ansteuerung von Servos, meist welche mit digitaler Elektronik, schnell und kräftig. Wenn die nach etlichen 1,5 ms Impulsen plötzlich mal einen von 2,5ms sehen, dann kann es schon sein, daß die erstmal mit ordentlicher Wucht gegen den Poller laufen, bis sie merken, daß kein neuer Impuls mehr kommt.
:
Bearbeitet durch User
Thomas E. schrieb: > es geht um die > Ansteuerung von Servos, meist welche mit digitaler Elektronik, schnell > und kräftig. Wenn die nach etlichen 1,5 ms Impulsen plötzlich mal einen > von 2,5ms sehen, dann kann es schon sein, daß die erstmal mit > ordentlicher Wucht gegen den Poller laufen, bis sie merken, daß kein > neuer Impuls mehr kommt. Das wäre mir ehrlich gesagt zu gefährlich. Wenn da - aus was für Gründen auch immer - ein Reset kommt, hast Du es gleich mit Maschinenschäden oder vielleicht sogar Personenschäden zu tun. Nicht umsonst gibt es bei Maschinen normal ein ausgeklügeltes Schutzkonzept mit E-Stops, Schutzeinhausungen mit Öffnungsschaltern, Lichtschranken und speziell zertifizierten Sicherheitssteuerungen die das ganze überwachen. Ich würde daher schaltungstechnisch dafür sorgen daß der Pin keine falschen Signale mehr auslösen kann. Eine Variante: kräftigerer Pulldown an den Pin, vielleicht noch ein kleines Logikgatter zum Puffern dahinter, z.B. 74LVC1G17 oder 74LVC2G17. Andere Variante: Du könntest die Ausgabe des Pins invertieren und dann einen 74LVC1G14 dahinterhängen, der invertiert auch wieder und Du hast dann die richtige Polarität. Der Pullup während dem Reset erzeugt damit genau die gewünschte Polarität am Ausgang Richtung Servo. Noch eine andere Idee: Der NRST-Pin des STM32 wirkt soweit ich weiß auch als Ausgang. Du könntest den mit z.B. einem RS-Flipflop nutzen und den NRST vom STM32 an das Set von dem Flipflop hängen. Der Ausgang des Flipflop bleibt solange high, bis Dein STM32 fertig initialisiert ist und Dein Programm korrekt läuft. Dann schaltest Du das Reset des Flipflops über einen anderen Pin. Der Ausgang des Flipflops schaltet z.B. den /OE eines 74xx244 und damit die Ausgänge zu den kritischen Servos etc. frei. Man könnte das auch noch leicht erweitern so daß regelmäßig ein Reset-Puls kommen muss damit das Flipflop den Kram nicht dichtmacht (Watchdog).
:
Bearbeitet durch User
Gerd E. schrieb: > Wenn da - aus was für Gründen auch immer - ein Reset kommt, hast Du es > gleich mit Maschinenschäden oder vielleicht sogar Personenschäden zu > tun. Wenn da im Betrieb unkontrolliert ein Reset auftritt, führt das sehr wahrscheinlich zum Absturz (und damit ist nicht Absturz des Programms gemeint! ;)) durch Kontrollverlust - in dem Fall ist dann ein kurz unkontrolliert zuckendes Servo das geringste Problem!
Das mit dem Keil hatte ich glatt überlesen aber habe auch nicht wirklich Ahnung was dessen Inline-Assembler kann und was nicht. Jedenfalls meine ich, dass du die Möglichkeit mit dem Flag setzen fokussieren solltest. Das mit dem Umbiegen des Program Counters ist ein dirty Hack, den man macht weil man es kann aber nicht weil es notwendig ist. In der main-loop kannst du dann noch deine sonstige Hardware in einen definierten Zustand versetzen, aus dem du dann wieder sicher starten kannst, etwa nach einem Reset nach einem Firmwareupdate.
Christopher J. schrieb: > Das mit dem Umbiegen des Program Counters ist ein dirty Hack Nö, das ist alles dokumentiert und spezifiziert, siehe Programming Manual.
Christopher J. schrieb: > Das mit dem Keil hatte ich glatt überlesen aber habe auch nicht wirklich > Ahnung was dessen Inline-Assembler kann und was nicht. Der Inline-Assembler ist offenbar ungeeignet (zumindest hierfür). Aber der Embedded Assembler (mit dem man quasi eine komplette C-Funktion in ASM schreibt) dürfte gehen. Habe mich da nur von der Fehleranzeige im Editor-Fenster fehlleiten lassen. Christopher J. schrieb: > dass du die Möglichkeit mit dem Flag setzen fokussieren solltest. Wollte ich ja dann erst auch, fand es aber schon eleganter, wenn sich die main nicht regelmäßig um dieses eigentlich nur in absoluten Ausnahmefällen (Konfiguration, Firmwareupdate) auftretende Ereignis kümmern muss. Zudem ist das irgendwie unbefriedigend, wenn man im Prinzip weiss, was man auf MPU-Seite implementieren muss, aber das Entwicklungswerkzeug dafür nicht im Griff hat. Man will sich die Lösung schließlich nicht von der IDE vorschreiben lassen! Christopher J. schrieb: > Das mit dem Umbiegen des Program Counters ist ein dirty Hack, den man > macht weil man es kann aber nicht weil es notwendig ist. Es soll eigentlich genau das gemacht werden, was bei einem Reset passiert, d.h. sowohl Stackpointer, als auch PC werden von ihren Reset-Vektoren geladen. Der alte Programm-Status incl. dessen Stack gehen den Prozessor nichts mehr an. Nur will ich einen "echten" Reset vermeiden, weil der einen ungewünschten Nebeneffekt hat. Dafür IST es notwendig.
Hmm, also bei mir hatte ich das so gelöst:
1 | NVIC_SystemReset(); |
Ist das zu kompliziert? :) Achso nochmal gelesen ... das Problem ist nicht der STM sondern quasi die restliche Schaltung, die dann einen Reset für 2ms sehen würde. Okay, dann bringt meine Lösung natürlich nichts :) Einen anderen Port-Pin verwenden, der keine JTAG-Funktion hat?
:
Bearbeitet durch User
Der Cortex M Kern kann auch alleine zurueckgesetzt werden ohne Reset der Periphrerie. Libopencm3 macht das wie folgt: void scb_reset_core(void) { SCB_AIRCR = SCB_AIRCR_VECTKEY | SCB_AIRCR_VECTRESET; while (1); }
Uwe B. schrieb: > Der Cortex M Kern kann auch alleine zurueckgesetzt werden ohne Reset der > Periphrerie. Libopencm3 macht das wie folgt: Habe ich mich nicht getraut. Das Programming Manual sagt dazu: >Bit 0 VECTRESET > Reserved for Debug use. This bit reads as 0. When writing to the register you must write 0 to > this bit, otherwise behavior is unpredictable. So geh't jetzt:
1 | __asm void pop_PC (void) __attribute__((noreturn)); |
2 | |
3 | static void PerformSoftwareReset (void) |
4 | {
|
5 | uint32_t *vectaddr = (uint32_t *)0x8000000; |
6 | uint32_t *stackframe; |
7 | |
8 | (hier: Code für disablen von interrupts, reset Peripherie, Clocks etc...) |
9 | |
10 | SCB->VTOR = (uint32_t)vectaddr; // set Vector Table for bootloader |
11 | stackframe = (uint32_t *)(vectaddr[0]-36); // vectaddr[0] is bootloader's top of stack |
12 | __set_MSP((uint32_t)stackframe); |
13 | // build up stack frame for return from exception:
|
14 | stackframe[8] = 0x01000000; // xPSR |
15 | stackframe[7] = vectaddr[1]; // PC |
16 | stackframe[6] = 0xFFFFFFFF; // LR |
17 | // skip stackframe[5..1] -> no need to restore other registers (R0..R3, R12)
|
18 | stackframe[0] = 0xFFFFFFF9; // exception return code: ->Thread, no FPU, use MSP |
19 | pop_PC (); |
20 | }
|
21 | |
22 | __asm void pop_PC (void) |
23 | {
|
24 | POP {PC} |
25 | }
|
Thomas E. schrieb: > Uwe B. schrieb: >> Der Cortex M Kern kann auch alleine zurueckgesetzt werden ohne Reset der >> Periphrerie. Libopencm3 macht das wie folgt: > > Habe ich mich nicht getraut. Das Programming Manual sagt dazu: Den Core Reset machen alle Blackmagic Debug Probes um in den DFU Bootloader zu kommen ohne Probleme.
Ok, also so:
1 | __DSB(); /* Ensure all outstanding memory accesses included |
2 | buffered write are completed before reset */
|
3 | SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | |
4 | (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | |
5 | SCB_AIRCR_VECTRESET_Msk); /* Keep priority group unchanged */ |
6 | while(1); |
geht's auch (bzw. sogar besser!) Mensch, hättest Du ja auch mal zwei Tage früher schreiben können... ;)
Uwe B. schrieb: > Den Core Reset machen alle Blackmagic Debug Probes um in den DFU > Bootloader zu kommen ohne Probleme. Die machen das doch per SWD? Evt. halten die vorher die CPU an oder schalten sie in einen speziellen Debug-Modus und dann funktioniert der AIRCR_VECTRESET definiert. Der ist doch speziell für Debugger gedacht? Nebenan gibt's ganz aktuell ein gutes Beispiel für "unpredictable behavior" Beitrag "Mikrocontroller mit Code von der alten Revision"
Bauform B. schrieb: > Nebenan gibt's ganz aktuell ein gutes Beispiel für "unpredictable > behavior" "unpredictable" ist vor allem das Problem des TO von nebenan, weil der ja gar nicht sagt wo es eigentlich hängt. Bauform B. schrieb: > Die machen das doch per SWD? Evt. halten die vorher die CPU an oder > schalten sie in einen speziellen Debug-Modus und dann funktioniert der > AIRCR_VECTRESET definiert. Der ist doch speziell für Debugger gedacht? Der Uwe meinte wohl die BMP selber, also um deren Firmware (die der Black Magic Probe) zu updaten. Nop schrieb: > Christopher J. schrieb: > >> Das mit dem Umbiegen des Program Counters ist ein dirty Hack > > Nö, das ist alles dokumentiert und spezifiziert, siehe Programming > Manual. Ich habe mich ein bisschen uneindeutig ausgedrückt. Den PC müsste man ja bei beiden von uns vorgeschlagenen Lösungen verändern. Was ich für einen dirty Hack halte ist das verändern des PC auf dem Stack, damit dann nach einem exception return der Bootloader angesprungen wird. Thomas E. schrieb: > So geh't jetzt: Ok, das ist auf jeden Fall schonmal sauberer sich erstmal den initialen Stackpointer zu schnappen und es dann darauf aufzubauen. Der Vorschlag von Uwe gefällt mir aber auch deutlich besser ;)
Christopher J. schrieb: > Was ich für einen > dirty Hack halte ist das verändern des PC auf dem Stack Was ist daran dirty? Völlig normale Assembler-Programmierung. Was meinst Du, wie man unter der Haube z.B. ein OS mit Multitasking realisiert?
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.