Liebe Mikrocontroller-Gemeinde!
Ich habe einen STM32F4 und habe im Sector 0 sowie im Sector 1 jeweils
ein Programm. Nun möchte ich vom Sector 0 in den Sector 1 springen.
Meine Recherche hat ergeben, dass die neue Adresse in die Vectortabelle
geschrieben werden muss und anschließend kann der Sprung erfolgen. Dazu
soll in dem SCB->VTOR Register geschrieben werden. Leider finde ich
weder im Datenblatt noch im Reference Manual des STM32F4 etwas darüber.
Kann mir jemand einen Anstoß geben, wie man das realisiert?
Muss ich "hinter" mir auch noch aufräumen, also den Ram und ggf.
Peripherie freigeben oder wird dies durch den Programmaufruf automatisch
zurückgesetzt?
Die Funktion NVIC_SetVectorTable kennt mein Eclipse leider nicht, ist
diese nicht für den STM32F4 verfügbar? Ich konnte diese auch nicht in
den Libs finden. Ich benutze jedoch u.a. NVIC_SystemReset, welche
gefunden wird.
Liebe Grüße
Franz
Franz Sauerdorf schrieb:> Dann hängt er sich auf.
Damit legst Du ja auch nur die Vektortabelle in den zweiten Sektor -
aber deswegen springt er dort ja nicht gleich hin? Ich würde erwarten,
daß er stattdessen im Quelltext nach Deiner Zeile weitermacht.
Franz Sauerdorf schrieb:> Ja, richtig. Man muss den Stack laden und dann eine Funktion mit der> Adresse aufrufen
Naja den Stack laden, dazu braucht's ein, zwei Zeilen inline-Assembler.
Wenn die Resetvektortabelle im zweiten Sektor richtig gebaut ist, dann
findest Du die zu ladende Stack-Adresse an der Adresse 0x08004000. Der
Resetvektor, der im zweiten Sektor dann angesprungen werden soll, ist
dann an 0x08004004.
Also:
- VTOR setzen
- ein DSB machen
- 0x08004000 in ein Register laden
- Stackregister mit dem laden, worauf voriges Register zeigt ("[Rx]")
- Register mit 0x08004004 laden
- einen branch auf das machen, worauf voriges Register zeigt ("[Rx]")
So würd ich's jedenfalls erstmal probieren.
Leider hilft mir dieser nicht wirklich weiter, da ich nicht weiß, wie
ich diesen Schnipsel anwenden muss. Da ist auch dein Hinweis mit dem
Reset-Vektor berücksichtigt.
Franz Sauerdorf schrieb:> Danke für die Hinweise. Bisher habe ich mir das> zusammengebastelt:uint32_t ApplicationAddress = 0x4000;
Nee, das geht so nicht.
> __set_MSP(*(__IO uint32_t*)ApplicationAddress); // setze Main Stack> Pointer
Da dereferenzierst Du dann die Adresse 0x4000, nicht 0x08004000 .
> __set_PSP(*(__IO uint32_t*)ApplicationAddress); // setze Process Stack> Pointer
Ebenfalls.
> jumpToApplication = (Function) * (__IO uint32_t*)(ApplicationAddress + 4);
Und da dereferenzierst Du 0x4004 statt 0x08004004.
Wenn das aber so klappen würde, dann würdest Du den Sprung einfach mit
jumpToApplication(); machen. Muß halt die Funktion als void-void
deklariert sein. Problem: Dabei werden dann schonmal die Register
gesichert. Deswegen würd ich das als inline-Assembler machen, die paar
Befehle beißen nicht.
Franz Sauerdorf schrieb:> Oder verstehe ich das falsch?
Von der Idee her ja, aber prüf nochmal, ob ein MOV mit Ziel PC überhaupt
geht und man das nicht als branch/jump schreiben muß.
Außerdem muß das jeweils [r0] und [r1] sein, weil einmal dereferenziert
werden muß. Man will ja nicht den SP auf 0x08004000 setzen, sondern auf
das, was an der Adresse 0x08004000 steht. PC genauso.
Danke.
Ich denke das MOV schon das richtige ist. Ich will ja den Pointer
setzen.
Wenn ich die dereferenzieren möchte mit eckigen Klammern, wirft der
Compiler fehlt aus.
Laut dem sollte es eigentlich so funktionieren:
http://stackoverflow.com/questions/16490315/stm32f4-memory-jumping
Leider hängt er sich auf. Hast du noch einen Tipp für mich?
hängt es sich auch beim Singlesteppen auf? Was ist direkt vor dem Sprung
in den Registern?
Du brauchst bei derartigen Dingen in jedem Fall ein ISB wichtiger als
ein DSB (dazu gibt es von ARM ausführliche Dokumentation).
Das weiß ich leider nicht genau. Ich programmiere mit Eclipse und habe
schon 2 Tage (alleine heute 2h) damit verbracht OpenOCD zum Laufen zu
bringen jedoch ohne Erfolg.
Gibt es andere Tools die du empfehlen kannst? Gerne auch stand-alone,
also außerhalb von Eclipse, wenn es dafür funktioniert.
An Adresse 0x08000000 liegt mein Bootloader. Soll kein Bootload-Vorgang
stattfinden springe ich an Adresse 0x08020000 wo mein Hauptprogramm
liegt.
Die Funktion "__set_MSP()" befindet sich in der "core_cmFunc.h".
Hallo Franz:
Du findest für deine Plattform funktionierenden Beispielcode hier:
http://www.springer.com/de/book/9783658148492#otherversion=9783658148508
(runterscrollen zum link "Zusatzmaterial," entpacken und Kapitel 9
Beispiel nutzen). Eine genau Diskussion findet sich in Kapitel 9 in dem
Buch.
HTH
Danke, mein Bootloader liegt ebenfalls bei 0x08000000 und mein
Hauptprogramm bei 0x08004000. Mein Hauptprogramm ist zur Zeit ein LED
Beispiel, was ich mit dem ST-Link geschrieben habe. Vor dem Compilen
jedoch die Flash-Adresse geändert. Ist das soweit korrekt ausgeführt?
Ich sehe mit ST Link auch bei 0x08004000 den Code vom LED Beispiel.
Ganz oben in meiner Hauptanwenung, also das erste was in der main()
drinsteht ist folgende Zeile:
1
/* First 0x20000 bytes reserved for bootloader, eeprom, etc.
2
application start at 0x08020000 (flash sector 5) */
3
NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x20000);
Und außerdem habe ich in der "STM32F407ZE_MemoryMap.xml"
folgende Änderung drin (ganz unten in der Datei):
<MemorySegment size="0x20000" access="ReadOnly" name="FLASH"
start="0x08020000"/>
Also die Startadresse auf 0x08020000 statt 0x08000000.
Kann man je nach IDE vermutlich auch an anderer Stelle unter den
Projekt-Parametern eintragen.
Die Funktion "NVIC_SetVectorTable()" ist in der StdPeriph_Driver in der
"misc.c/h"
Sorry Feierabend, weitere Fragen morgen.
Danke für die Buchempfehlung!
Ich habe in meinem Hauptprogramm lediglich die Memory Zuordnung
geändert:
1
MEMORY
2
{
3
RAM(xrw):ORIGIN=0x20000000,LENGTH=128K
4
CCMRAM(xrw):ORIGIN=0x10000000,LENGTH=64K
5
FLASH(rx):ORIGIN=0x08004000,LENGTH=1008K
6
FLASHB1(rx):ORIGIN=0x00000000,LENGTH=0
7
EXTMEMB0(rx):ORIGIN=0x00000000,LENGTH=0
8
EXTMEMB1(rx):ORIGIN=0x00000000,LENGTH=0
9
EXTMEMB2(rx):ORIGIN=0x00000000,LENGTH=0
10
EXTMEMB3(rx):ORIGIN=0x00000000,LENGTH=0
11
MEMORY_ARRAY(xrw):ORIGIN=0x20002000,LENGTH=32
12
13
14
}
Damit fängt meine Hauptanwendung erst bei 0x08004000 also Sector 1 an
und sollte den Bootloader in Ruhe lassen :-) .
Beim Bootloader habe ich nichts verändert. Ist das soweit korrekt?
So er springt erfolgreich ins Hauptprogramm.
Nur im Hauptprogramm können noch keine Interrupts verwendet werden,
ansonsten hängt er sich auf. Die Interrupts laden wohl noch im
Bootloader.
Ich verstehe noch nicht ganz, wie ich den Interrupt-Vector des
Hauptprogramms mit einem Offset versehe.
Hat da jemand einen Tipp für mich, was ich mir mal ansehen muss?
Bevor du die Interrupts im Hauptprogramm wieder aufdrehst musst du das
VTOR Register deiner neuen Tabelle zuweisen. Unter Verwendung von CMSIS
wäre das wie Eingangs eh schon jemand geschrieben hat
1
SCB->VTOR=&"Tabelle"
Ob die Tabelle im Flash oder im RAM steht ist egal. Die Tabelle selbst
ist ein Mischmasch aus M4-Core Exceptions und Hersteller abhängiger
Interrupts.
Näheres etwa hier:
http://infocenter.arm.com/help/topic/com.arm.doc.dui0553a/DUI0553A_cortex_m4_dgug.pdf
/edit
Pass auf, dass der SysTick nicht zu früh aufgedreht wird. Führ auf
keinen Fall irgendwelche Initialisierungen in irgendwelchen Bibliotheken
(etwa HAL) aus, bevor VTOR richtig gesetzt ist!
Danke die Infos haben mich schon weiter gebracht.
Es kann sein, dass meine Umgebung (Eclipse, GCC) schon etwas
initialisiert.
Dazu habe ich eine _initialize_hardware.c Datei gefunden, wo die Clocks
gesetzt werden. Sollte ich den Interrupt Vector nicht schon dort setzen?
Ich habe es zunächst hiermit versucht, was ich direkt oben in die Main
gesetzt habe:
1
SCB->VTOR=0x00004000;
Das funktioniert leider nicht. D.h. hatte ich mich auf die Suche nach
den Initialisierungen davor gemacht.
Ist mein Vector Befehl überhaupt korrekt? Das müsste doch funktionieren?
Vielen Dank.
... guck Dir doch einfach mal den Beispielcode aus meinem Buch an, der
tut's. Du musst dazu nix kaufen, einfach nur den Code runterladen (der
ist frei zugänglich).
Danke, das Buch habe ich bereits. Ich habe nun erneut reingeschaut,
jedoch kann ich deinen Code nicht ganz nachvollziehen und habe dazu
einige Fragen.
Du änderst die IVT bevor du in das Hauptprogramm springst, also im
Bootloader? Ich dachte man muss dies erst im Hauptprogramm tun (so früh
wie möglich)?
Dein Code ist folgend dargestellt. Leider ist der für mich schlecht
lesbar, ich habe ihn versucht an meinen Code anzupassen, jedoch verstehe
ich nicht, was und wie du dein Hauptprogramm als "g_pfnVectorsFWStubs"
verwendest. Sieht für mich aus wie eine Funktion mit einem Array? Noch
nie gesehen.
1
// reprogram the IVT. The Instruction Barrier is necessary here (cf. ARM documentation)
2
__asmvolatile
3
(
4
" cpsid i \n"
5
" isb \n"
6
);
7
*((unsignedlong*)0xe000ed08)=((unsignedlong)(&g_pfnVectorsFWStubs)&(unsignedlong)0x1FFFFF80);// set SCB->VTOR
8
9
// redirect stack pointer to vector 0 in the application's IVT
10
__asmvolatile
11
(
12
" ldr r0,pcrelSP \n"
13
" ldr r13,[r0] \n"
14
" .align 2 \n"
15
"pcrelSP: .word g_pfnVectorsFWStubs \n"
16
);
17
// dispatch to app entry
18
(g_pfnVectorsFWStubs[1])();
Wieso hast du das so realisiert? Die C-Funktion welche auch in den
Bibliotheken verwendet wird erscheint mir einfacher, folgend dargestellt
wie ich es versucht habe.
Hallo Franz,
ich habe bei dem Beispielcode bewusst versucht, Abhängigkeiten zu
bestimmten Ökosystemen und Bibliotheken zu vermeiden (steht auch
irgendwo) und den Code mit möglichst vielen Toolchains und Ökosystemen
übersetzbar zu machen, deswegen keine Bibliotheksfunktionen und auch
keine selbstgebastelten symbolischen Identifier... ist natürlich für
realen Code ein Nono, aber Lesbarkeit und Knappheit sind für mich
vorrangig imperativ gewesen.
Die Philosophie hinter dem Bootloaderbeispiel ist die der Parallelwelten
- der Bootloader läuft mit seiner eigenen IVT und mit seinem Standalone
Code, und sobald er sich sicher ist, daß eine gültige Applikation
geladen ist, wird die Kontrolle an die Applikation übergeben, wobei die
Applikation genauso gebaut ist als wenn es keinen Bootloader gebe; sie
(die Applikation) hat also eine eigene IVT, die genau so aufgebaut ist
wie wenn sie standalone funktionieren würde (also mit gültigem Stack
Pointer in Vector 0 und gültigem "Reset ISR" in Vektor 1, wobei die
Applikation dann natürlich auch mit den dort hinterlegten Werten
hochstartet).
Damit das so realisiert werden kann, müssen zwei dem Bootloader bekannte
IVT Adressen existieren (das ist recht genau in Kapitel 9 beschrieben;
sollte es unklar sein, würde ich mich gerne mit Dir Offline über die
mißverständlichen Stellen auseinandersetzen - natürlich gegen
Dankeschön! ;-)):
Im Linker Layout ist die Position der "Applikations IVT" bindend
festgezurrt, so daß der Bootloader immer genau weiß, wo die liegt,
nämlich in dem Identifier, der mit g_pfnVectorsFWStubs bezeichnet ist.
Sobald eine gültige Applikation geladen ist, verbirgt sich an der Stelle
natürlich auch genau die gültige Applikations IVT; es kann aber
natürlich auch passieren, daß da (noch) nichts steht, z.B. wenn initial
nur der Bootloader im Flash ist aber noch keine Applikation (in dem Fall
darf natürlich der Bootloader nicht verzweigen, sondern muß auf die
Kommunikation eines gültigen Images warten). Deswegen ist der Bezeichner
mit "stub" (also Platzhalter) gekennzeichnet. Die Deklaration
extern void (* const g_pfnVectorsFWStubs[IVTENTRYCT])(void);
als Array von Funktionspointern ist natürlich in C völlig legitim (Du
wirst feststellen, daß bei in C geschriebenen Startup files eine IVT in
der Regel ganz genau so deklariert wird).
Ich biege als "letzte Amtshandlung" des Bootloaders vor Übergabe der
Kontrolle an die Applikation die IVT um, weil der Bootloader natürlich
keine Annahmen darüber treffen kann und darf, ab wann die
Applikationsfirmware Interrupts benutzen will; die IVT sollte sich also
ab dem ersten Befehl der Applikation so verhalten, wie die Applikation
es erwartet. Natürlich läßt sich (wie bei Allem) darüber debattieren, ob
eine andere Implementation besser wäre, aber unter der o.g. Prämisse,
daß sich der Applikationscode so wenig wie möglich von einer "standalone
Firmware" unterscheidet (die aus einem physikalischen Reset heraus
läuft), halte ich die Implementation in meinem Code für konsequenter.
Wichtig ist natürlich, daß Interrupts definitiv disabled sind UND die
instruction Barriere gesetzt wird (also die Pipeline geflusht wird),
bevor die IVT versetzt wird.
Ich benutze g_pfnVectorsFWStubs für alle drei in diesem Zusammenhang
relevanten Operationen - als Basisadresse für das Umsetzen der IVT, als
Zieladresse für den SP in Vektor 0 und als Zieladresse für den PC in
Vektor 1 -, weil es logisch eben genau dieses ist: Die Applikations IVT
mit diesen Initialvektoren. Würde ich die (in meinem Code aufgelöste)
SCB->VTOR Adresse mit etwas Anderem als g_pfnVectorsFWStubs belegen,
dann müßte ich entweder einen Alias für dieselbe Adresse anlegen oder
aber sicherstellen, daß bei einem Umlegen der IVT auf eine andere
Adresse (z.B. bei einem zweiten Projekt mit einem anderen Controller)
die Änderung an beiden Stellen parallel geführt werden müßte, was immer
eine unnötige Fehlerquelle ist.
Macht das Sinn? Wenn nicht frage gerne weiter (Offline oder Online).
Schönen Dank fürs Buch kaufen; ich hatte gehofft, die Architektur
verständlich genug im Kapitel 9 dargelegt zu haben...
Danke für die ausführliche Antwort.
Nunja, das Buch ist verständlich geschrieben, jedoch ist der Code für
mich dann doch etwas unübersichtlich. Vor allem der der Linker. Ich weiß
was du vor hast, verstehe jedoch die Notation nicht. Ich denke aber, das
ist nicht so schlimm. Klar deine Argumentation wieso du es so gemacht
hast ist vollkommen richtig und gut so, jedoch für meinen Anwendungsfall
reicht dies abgespeckt. Ich habe vorher noch nie etwas in der Richtung
angewendet und sammle gerade erst meine Erfahrungen.
Für das Hauptprogramm:
1
/*
2
* Memory Spaces Definitions.
3
*
4
* Need modifying for a specific board.
5
* FLASH.ORIGIN: starting address of flash
6
* FLASH.LENGTH: length of flash
7
* RAM.ORIGIN: starting address of RAM bank 0
8
* RAM.LENGTH: length of RAM bank 0
9
*
10
* The values below can be addressed in further linker scripts
11
* using functions like 'ORIGIN(RAM)' or 'LENGTH(RAM)'.
12
*/
13
14
MEMORY
15
{
16
RAM(xrw):ORIGIN=0x20000000,LENGTH=128K
17
CCMRAM(xrw):ORIGIN=0x10000000,LENGTH=64K
18
FLASH(rx):ORIGIN=0x08004000,LENGTH=1008K
19
FLASHB1(rx):ORIGIN=0x00000000,LENGTH=0
20
EXTMEMB0(rx):ORIGIN=0x00000000,LENGTH=0
21
EXTMEMB1(rx):ORIGIN=0x00000000,LENGTH=0
22
EXTMEMB2(rx):ORIGIN=0x00000000,LENGTH=0
23
EXTMEMB3(rx):ORIGIN=0x00000000,LENGTH=0
24
MEMORY_ARRAY(xrw):ORIGIN=0x20002000,LENGTH=32
25
}
Wie man erkennen kann, fängt der Flash beim Sector 1 an, der Bootloader
liegt im Sector 0. Das ist dann auch die einzige Veränderung, die ich
vorgenommen habe. Das Springen vom Bootloader in das Hauptprogramm
funktioniert auch, wenn das Hauptprogramm keine ISR's enthält.
Ich bin jetzt total verwirrt, ich möchte wie in deinem Buch Abschnitt
9.5.1 Abbildung 9.3 meinen Programmspeicher aufbauen.
Im Web habe ich wenig darüber gelesen, dass weitere Änderungen in der
Memory-Definition notwendig sind. Also bisher habe ich gedacht, das
reicht zur Anpassung des Speicherlayoutes, den Flash nach hinten zu
schieben und der IVT-Vector rückt dann automatisch mit. Diesen muss man
man dann im Bootloader vor Programmaufruf noch auf die neue Adresse
schieben und fertig.
Ist es also weiterhin notwendig das Speicherlayout wie in deinem Buch
beschrieben anzupassen? Kann ich den IVT Vektor nicht auch so
verschieben (vermutlich habe ich bisher noch einen kleinen Fehler
irgendwo)?
Ansonsten würde ich im Bootloader das Memorylayout wie bei dir
beschrieben übernehmen.
Also ich vermute das ich bisher lediglich die Startadresse der App-IVT
falsch setze.
Liebe Grüße
Das Einstellen der Vector Tabelle musst du im Hauptprogramm machen.
Wenn man z.B. die Std. Library Bon ST benutzt gib es eine Init funktion,
die noch vor Main aufgerufen wird. Und in dieser ist sogar schon die
passende Zeile für die Tabelle vorhanden ( nur im Fall eines
Bootloader's halt mit der falschen Adresse ).
Schau dir mal das startup Script deines Hauptprogramms an (meist ein asm
File) da musste sich der eigentliche Boot Ablauf finden lassen.
Hallo Franz,
Du must UNBEDINGT(!)
- sämtliche Interrupts disablen (cpsied). Ist das das wohin
__disable_irq() auflöst?
- eine ISB setzen (DSB ist nicht unbedingt nötig)
bevor Du die Tabelle umsetzt!
Wenn Du einen Debugger hast, der Singlesteppen auf Assemblerebene
erlaubt, steppe bis zum Aufruf von Jump_To_Application(), sieh Dir die
Register an der Stelle an, wo der bl Aufruf steht (speziell die stack
pointer), dann mach einen step into und sieh wo Du landest. Wirklich am
Einsprung der Applikation, oder direkt am Fault Handler? Was genau steht
zu dem Zeitpunkt an der Adresse 0x8004000 und 0x8004004? Hast Du da
vielleicht ein little endian/big endian Problem?
Soweit sieht es für mich ok aus; das einzige, woran es vielleicht hakeln
könnte ist daß die Sprungadresse mit dem LSB gesetzt (also ungerade)
erwartet wird (Thumb convention).
BTW, in deinem Codefragment benutzt Du drei verschiedene Wege, dieselbe
Adresse zu referenzieren (ApplicationAddress,ADDR_FLASH_SECTOR_1 und
(0x08000000 | 0x4000)). Das ist enorm fehleranfällige Codierung, weil Du
dann bei jeder Änderung drei Stellen umschreiben musst, ausserdem ist es
nicht gut lesbar... aus genau dem Grunde gehe ich über
g_pfnVectorsFWStubs, der muss im Falle genau an einer Stelle geändert
werden.
Danke an Mike und Ruediger für eure Antworten.
@Mike: Ich überprüfe das die Tage.
@Ruediger: Ich habe im Bootloader lediglich für den UART2 und für den
Timer2 einen Interrupt verwendet, welche ich im NVIC->ICER wieder
deaktiviere. Weiterhin deaktiviere ich Global alle Interrupts
(CPSR/cpsid) mit __disable_irq().
ISB fehlte danke, die habe ich nun hinter die DSB getan.
Leider habe ich das "neue" Board vom Discovery mit dem Flash Laufwerk in
Windows und anderem Treiber. Mit diesem funktioniert OpenOCD nicht mehr
korrekt. Welchen Debugger benutzt du/ihr?
Zum BTW: Danke, das sieht bis zum Funktionstest oft bei mir so aus :-/
soweit ich weiss, haben alle discovery boards einen On-Board STLink, d.h
Du kannst alle Debugger verwenden, die mit einem ST Link kommunizieren
können. Ich benutze für das 407 discovery board WinIdeaOpen ohne
jegliche Probleme. Btw, Du kannst sogar den On Board ST Link durch
umflashen zum Segger J-Link machen und Dir damit zusätzlich die J-Link
Vorteile für umme sichern.
Wo "nur" eine JTAG/SWD Schnittstelle zur Verfügung steht, habe ich einen
iTAG50 fertig montiert und geflasht gekauft, Du kannst Dir den aber auch
selber basteln.
Wo genau hängt es sich denn bei Dir auf? Wie gesagt, wenn Du direkt vor
dem bl den Prozessorstatus inspizieren kannst, sollte relativ schnell
klar werden, wo das Problem ist...
@Mike: Ja die stdlib wird bei mir verwendet. Ich finde jedoch nur die
Header-Datei und keinerlei Code zur Initialisierung. Wo hast du das mal
gesehen? Ich habe noch eine _initialize_hardware.c wo jedoch eigentlich
nur die Takte gesetzt werden.
Ja das haben sie.
Er startet das Debugging und hält bei dem Einstieg der Main an, wenn ich
ihn dann rennen lasse, bricht die JTAG Verbindung ab. Achja und jedes
mal vorher muss ich manuell den Chip erasen :-/ .
Bei einem Freund mit dem älteren ST-Link drauf funktioniert das.
Die Fehlermeldung ist:
Error: JTAG failure -4
Error: jtag status contains invalid mode value - communication failure
Polling target stm32f4x.cpu failed, trying to reexamine
Examination failed, GDB will be halted. Polling again in 100ms
Also Du meinst mit der main() die main() des Bootloaders, in dem dann
die Magie mit dem Umsetzen vorgenommen wird? Oder die main() der
Applikation?
Vermutlich das Erstere. Was passiert, wenn Du versuchst single zu
steppen? Verabschiedet sich die Kommunikation zur Probe dann ebenfalls?
So wie Du es schilderst, ist es ein Problem im Zusammenspiel zwischen
Debugger und Probe. Lad Dir doch einfach mal WinIdeaOpen herunter, das
spielt mit dem EVB (und meiner Beispielapp) aus der Box heraus.
Herauszufinden, warum eine besteimmte Combo Debugger/Probe es nicht tut,
ist eine Menge Zeit für Nichts zu verschwenden.
BTW, was genau heisst "Bei einem Freund mit dem älteren ST-Link drauf
funktioniert das?" WAS funktioniert? Singlesteppen? Kannst Du deinen
Code mit dem STLink deines Kumpels debuggen?
Ja genau. Die Main des Bootloaders. Singlesteppen funktioniert leider
auch nicht. Ich habe mir WinIdeaOpen heruntergeladen und es funktioniert
auf Anhieb Beispielprojekte zu debuggen. Dein Projekt irgendwie nicht so
richtig. Sobald ich aber unter Debug/Files die LED App von dir
aktiviere, blinkt er gar nicht mehr. Vorher wenigstens einmal in der
Sekunde. Er bleibt mit dem Hauptprogramm von dir in Adresse 0 stehen.
Ist ja auch nicht so wichtig, dass dein Projekt funktioniert.
Ich kann morgen von meinem Kumpel das Board probieren. Ich habe gelesen,
dass es eine Änderung gab. ST-Link V2-1 und das Board von meinem Kumpel
wird auch nicht als Massenspeicher unter Windows erkannt, meins schon.
Das ist ein Feature und gewollt. Jedoch macht OpenOCD da irgendwie nicht
mit. Darüber habe ich leider sehr wenig im Internet gefunden. Die JTAG
Fehlermeldung hatten auch einige bei zu geringer Versorgungsspannung.
Mit externer Spannung habe ich es ebenfalls probiert, hat nicht zum
Erfolg geführt.
Kann man ein Discovery mit einem anderen Debuggen (weil ich bekomme den
fest verbauten STLink nicht vollständig vom Chip getrennt)? BTW: ich
habe sogar noch einen einzelnen ST Link V2 da... ich such mir mal die
Pins raus und probiere es mal.
Ich habe wie in der readme zunächst das Hauptprogramm gebaut, dann den
Bootloader. Anschließend im Bootloader den Haken vom Hauptprogramm
entfernt und wieder hinzugefügt. Wie gesagt, ohne HP blinkt es, mit
nicht.
aha, also funktioniert es prinzipiell. Das Blinken ist ein Indikator,
dass der Bootloader kein gültiges Applikationsimage gefunden hat. Wenn
die Booloader LED NICHT blinkt, heisst es entweder, dass sich die
Geschichte aufgehängt hat und gar nichts mehr geht - oder eben das
Applikationsprogramm läuft aber nichts sichtbares tut. Mein
Applikationsprogramm blinkt andereLEDs; wenn es das bei Dir nicht
tut, kann es sein daß Du ein Anderes Eval Board mit Anderen Peripherien
hast, so dass man schlicht und einfach nicht sieht, was es tut?
Aber mit WinIdea Debuggern funktioniert? Kannst Du im Bootloader bis zur
Übergabe an das Hauptptogramm durchsteppen?
Also das Debugging funktioniert nun. Ich musste an mehreren Stellen
Anpassungen vornehmen, was es wie ein Zufall aussehen lässt, wenn man
dann doch mal die richtigen Anpassungen zusammen hat. Ich musste das
Script vom OpenOCD anpassen, mit den Windows Treibern kämpfen usw.
Nun habe ich auch ziemlich sicher herausgefunden, wieso das
Hauptprogramm mit ISR nie funktioniert hat. Die Standardbibliotheken
setzen den Takt und anscheinend noch einiges mehr. Mir ist aufgefallen,
dass obwohl ich im Bootloader die globalen Interrupts ausschalte, sie im
HP wieder aktiv waren. Dann habe ich ein Programm erstellt, wo ISRs im
HP funktionierten, ich wusste zunächst nicht wieso. Ich habe dann
stückweise Teile vom Bootloader auskommentiert, es hat funktioniert bis
nur noch folgendes übrig blieb (im HP habe ich keine Anpassungen
gemacht, außer in der Memory-Linker-File die Flash-Adresse):
Ich musste nicht einmal den Stackpointer umsetzen. Ich habe dann mehrere
Anläufe gebraucht um herauszufinden wieso das nur mit dem einen Projekt
funktioniert hat. Ich habe die gleichen Interrupts wie im Bootloader
verwendet z.B. Timer2, aber andere LEDs im ISR blinken lassen, somit
wusste ich, dass er wirklich meine HP ISRs auch ausführt.
Jedenfalls ist mir aufgefallen, dass wenn ich die Memory-Linker-File
bearbeite, er selbst nach einem clean und rebuilt die hex nicht auf die
neue Flashadresse korrigiert. Ich musste den gesamten "debug" bzw.
"release" Ordner löschen, dann hat er auch die Flash-Adresse übernommen.
Anscheinend initialisiert er jedoch woanders die anderen Adressen, die
ich nicht mehr ändern könnte.
Ich konnte es jetzt lösen indem ich ein neues Projekt erstelle und vor
dem ersten Build direkt das Memory-File anpasse, also lediglich die
Flash-Adresse ändere. Er muss bei der Initialisierung die Adresse z.B.
für den ISR-Vector und Stack überschreiben, weshalb es nie funktioniert
hat.
Ich würde nun gerne herausfinden, wo er die Adresse im Projekt
speichert, sodass ich zunächst weiß was er genau macht und dies auch
manuell noch einmal anpassen kann.
Hat jemand dazu eine Idee?
Dazu hatte Mike schon etwas geschrieben, ich bin die stdlib usw.
durchgegangen, konnte jedoch nur den Teil finden, wo er den Takt setzt.
Den Teil wo er die Adressen setzt, konnte ich bisher nicht finden.
Glückwunsch erstmal, dass Du Dich durch die Probleme mit den tools
durchgebissen hast!
Worauf zeigt denn der jetzt 0x8004004? Was genau macht die Funktion, die
dahinter steckt? Nicht zufällig irgendwas, das über Dir nicht
kontrollierbare Bibliotheksfuntionen den Prozessor reinitialisiert? Dann
kannst Du Dir natürlich die ganze Aktion mehr oder weniger sparen.
Mit dem SP: Das habe ich ja genauer in meinem Buch ausgeführt; wenn Du
sicherstellen kannst, dass der vom Bootloader vorbereitete Stack auch
für die Applikation ausreicht, kannst Du natürlich auf dem selben Stack
weiterarbeiten. Aber davon rate ich ganz stark ab, weil ja eine spätere
Version der Applikation den Stack völlig Anders benutzen kann, die müßte
dann den SP direkt nach Übergabe der Kontrolle selber umsetzen.
Wie meinst du das, worauf die zeigt? Ab 0x08004000 beginnt mein HP was
momentan lediglich den Timer, UART und den SysTick verwendet, alles was
auch der Bootloader benutzt (zur Kontrolle verwende ich diese
ebenfalls).
Ich habe gelesen, dass man zur HP-Adresse +4 springen muss, weil man
sonst zur initial Stack Pointer Position springen würde. Man würde also
zur Basisstackadresse springen, wenn ich das richtig verstanden habe.
Ab +4 beginnen dann die Adressen der reset handler procedure. So ganz
geläufig ist mir das noch nicht.
Ich meine mit dem SP, dass ich glaube im HP initialisiert einer der
Standardbibs den Stack eh auf 0x08004000, somit ist es egal was ich im
Bootloader mache. Ich müsste die Stelle finden, wo es im HP bei der
Initialisierung gemacht wird.