Hallo,
ich habe ein paar grundlegende Fragen zum Schreiben in das FLASH, wenn
ich das "per Hand" machen will. Das Programming Manual PM0075 von ST
hilft mir leider nur bedingt weiter, einige Fragen bleiben für mich
unbeantwortet.
Ich habe auch schon viele Beispiele im Netz angesehen, praktisch immer
wird für die Programmierung des FLASH-Speichers irgendeine
Bibliotheksfunktion (FLASH_Progam...) benutzt. Ich möchte es aber selber
machen und genau wissen, was passiert.
Damit zur ersten Frage: ST sagt, es kann nur ein Word programmiert
werden, also ein 16Bit-Wert. Bei den Beispielen werden jedoch immer
32Bit-Werte programmiert. Wie läuft der Schreibvorgang ganz konkret und
wie ist die Adressierung genau? Nach meiner Interpretation müsste das
Schreiben folgendermaßen ablaufen:
1
uint16_tvalue1=(uint16_t)1234;//erstes Word
2
uint16_tvalue2=(uint16_t)4567;//zweites, soll direkt dahinter landen
3
4
uint32_taddressValue1=0x0800fc00;//Adresse für erstes Word
5
uint32_taddressValue2=0x0800fc02;//Adresse für das nächste direkt dahinter?
6
7
uint32_t*addressPointer;//bekommt später die Adresse, an die geschrieben werden soll
Ich bin nicht sicher, ob die Adressierung und insbesondere das Schreiben
selber (16Bit-Wert?) so richtig sind. Mit einem 32Bit-Wert hat es
jedenfalls funktioniert, allerdings brauche ich da nach einem
Erase_Vorgang immer ein Reset, damit ich wieder in die Page schreiben
kann (ansonsten hängt sich der Controller auf). Auch das kommt mir sehr
komisch vor (habe diesbezüglich nichts im Programming Manual gefunden),
ist das richtig so?
Ja, ich weiß, der Code war mehr oder weniger nur beispielhaft, um das
Thema Adressierung und meine Unklarheiten zu beschreiben.
Die Frage ist für mich, ob ich einen 16Bit-Wert an die Adresse schreiben
kann und dann den nächsten 16Bit-Wert an die Adresswert + 2.
Und die andere Frage: Warum kann ich nicht nach einem Erase wieder
schreiben, sondern brauche ein Reset des Controllers?
Nachtrag:
Hier geht der Controller regelmäßig in den HardFaultHandler:
1
*addressPointer=value1;
value1 ist ein uint16_t. Sauberer wäre ja ein
1
*addressPointer=(uint32_t)value1;
Die Adresse liegt bei 0x800fffc, sollte also bei dem Controller, der 64k
Flash hat (64 Pages*1024 Bytes), noch im gültigen Bereich liegen (kurz
vor Ende von Page 63). Erase funktioniert ja auch (irgendwie hatte ich
es auch schon hinbekommen, da etwas hineinzuschreiben).
Wie schreibt der Controller den Wert eigentlich genau in den Flash?
Schneidet der die oberen 16 Bit einfach ab? Und wozu schreibe ich die
Adresse in das Adressregister, wenn ich den Flash dann sowieso direkt
über die Speicheradresse beschreibe? Wirkt für mich irgendwie unlogisch.
Haste die Page auch vorher gelöscht?
Bzw die Speicherzelle an der Adresse wurde nach einem löschen nie
beschrieben?
Der F103 kann nur 0xFFFF einmal zu deinem gewünschten Wert schreiben
oder Wert x nach 0x0000. Einzelne Bits von 1 nach 0 setzen (wie bei
manch anderen Flashes) geht nicht.
Ist nach
*addressPointer = value1;
das PGERR Bit gesetzt?
ich weis nicht woher du das mit den Wörtern hast das ST Dokument spricht
ja recht eindeutig von:
> In this mode the CPU programs the main Flash memory> by performing standard half-word write operations.
Mit
uint32_t *addressPointer;
haste aber einen Pointer der auf einen 32 Bit Wert zeigt und
dementsprechend wird der Compiler auch einen 32Bit Zugriff generieren,
es sollen aber nur 16 Bit geschrieben werden.
Also:
Mw E. schrieb:> Haste die Page auch vorher gelöscht?> Bzw die Speicherzelle an der Adresse wurde nach einem löschen nie> beschrieben?
Ja, das funktioniert auch problemlos. Ich hatte es schon einige Male
geschafft, etwas zu schreiben, allerdings nun nicht mehr (nach wenigen
Änderungen, kann ich nicht mehr nachvollziehen).
> Der F103 kann nur 0xFFFF einmal zu deinem gewünschten Wert schreiben> oder Wert x nach 0x0000. Einzelne Bits von 1 nach 0 setzen (wie bei> manch anderen Flashes) geht nicht.
Ich habe immer vor dem Schreiben ein Erase durchgeführt.
> Ist nach> *addressPointer = value1;> das PGERR Bit gesetzt?
Nein, danach geht er direkt in den HardFaultHandler, die FLASH-Register
sehen alle unauffällig aus. Im Control-Register steht dann noch das
PG-Bit.
>> ich weis nicht woher du das mit den Wörtern hast das ST Dokument spricht> ja recht eindeutig von:>> In this mode the CPU programs the main Flash memory>> by performing standard half-word write operations.
Das wären dann ja Bytes?
> Mit> uint32_t *addressPointer;> haste aber einen Pointer der auf einen 32 Bit Wert zeigt und> dementsprechend wird der Compiler auch einen 32Bit Zugriff generieren,> es sollen aber nur 16 Bit geschrieben werden.>> Also:uint16_t *addressPointer;> addressPointer = (uint16_t *) addressValue1;> *addressPointer = value1;> mal probieren.
Die Adressen sind aber laut Datenblatt 32 Bit breit: 0x0800fc00
Im PM0075 steht:
The Flash memory is organized as 32-bit wide memory cells that can be
used for storing both code and data constants.
Andererseits steht wieder, dass nur Half-Words geschrieben werden. Und
da ist mir eben unklar, wie man wirklich konkret an die Speicherstelle
schreiben soll, leider schweigt sich das Manual dazu komplett aus.
Und wofür fülle ich eigentlich das FLASH_AR-Register, wenn ich ohnehin
an die Adresse direkt schreibe?
Gastino G. schrieb:> Und wofür fülle ich eigentlich das FLASH_AR-Register, wenn ich ohnehin> an die Adresse direkt schreibe?
Wenn man so derart viele Fragen hat, lohnt sich ein Blick in die
Appnotes und den Beispielcode des Herstellers. Man muss natürlich
fremden C Source lesen können...
Du hast Pointer in C offensichtlich nicht verstanden.
Der Pointer selbst ist automatisch so lang wie die Adressierung auf der
Zielplattform.
Der angegebene Typ beim Pointer ist der Datentyp auf den der Pointer
drauf zeigt.
Oder wie willst du Pointer auf srtructs bauen? Sind die bei dir auch
uint32_t?
Anhand dieses Datentyps baut sich der Compiler dann die ASM
Speicherzugriffe.
Jim M. schrieb:> Wenn man so derart viele Fragen hat, lohnt sich ein Blick in die> Appnotes und den Beispielcode des Herstellers. Man muss natürlich> fremden C Source lesen können...
ACK.
Ich habe gerade interessehalber mal einen Blick in die Quellen des
Blackmagic Probe geworfen. Die können ja auch alle möglichen ARMs
flashen. Sieht auf den ersten Blick ziemlich kompliziert aus, weil sie
da verschiedene Alignments erlauben.
Ich habe mir für einen Bootloader mal eine eigene Flash.h und Flash.c
geschrieben. War für einen STM32F413VGT6, könnte aber bei dir auch
funktionieren. Sind ganz wenige Zeilen, funktioniert aber bestens.
flash.h:
Danke, das Beispiel hat mir geholfen. Mir war leider unklar, wie genau
zu schreiben ist, bei dem Pointer (Zieldatentyp vs. Adressbreite auf der
Architektur) hatte ich tatsächlich ein Brett vor dem Kopf.
Offen bleibt nur die Frage, warum im PM0075 die Rede davon ist, dass man
das Adressregister mit der Adresse beschreiben soll, die man
programmieren oder löschen will. Das obige Beispiel funktioniert ja
offensichtlich problemlos ohne das Register.
Leider geht die Kiste weiter in den HardFaultHandler, direkt nach dem
Schreiben. :(
Habe schon einige Adressen ausprobiert, die alle im gültigen Bereich
liegen müssten. Nix...
Gastino G. schrieb:> Offen bleibt nur die Frage, warum im PM0075 die Rede davon ist, dass man> das Adressregister mit der Adresse beschreiben soll, die man> programmieren oder löschen will. Das obige Beispiel funktioniert ja> offensichtlich problemlos ohne das Register.
Eventuell nfunktioniert dein µC nochmal etwas anders. Ich habe mich
explizit an die Angabe im Datenblatt meinem µC gehalten.
Die Adresse, an die ich schreibe, ist die 0x800fffd (der STM32F103C8 hat
laut Datenblatt 64k Flash). An anderen Adressen knallt es aber leider
auch.
Hinweis: Die Funktion enthält am Anfang noch einen Check der Adressen
und buffer-Länge - das habe ich aus Gründen der Übersichtlichkeit
entfernt.
Hinweis2: Wenn ich buffer[i] durch einen statischen Wert ersetze, knallt
es ebenso. Der buffer ist es also nicht.
Gastino G. schrieb:> uint8_t *flashAddressPointer = (uint8_t *) address;> *flashAddressPointer = buffer[i]; //<<<<-----------BUMMMMM!!!!! :o(((
Du benutzt einen Pointer auf ein Byte. Haben wir nicht hier
Beitrag "Re: Daten in FLASH schreiben (STM32F103C8) - ohne Bibliothek"
gelernt, dass das ein Pointer auf ein 16-Bit-Wort (half word) sein muss?
Das obige knallt wahrschenlich beim Versuch, auf eine ungerade Adresse
zu schreiben. buffer würde ich ebenso als Pointer/Array auf 16-Bit
arrangieren.
Ja mach bloß nicht das was in der dir bekannten Appnote steht und was
ich bereits schon geschrieben habe!
Erst schreibste uint32_t rein und jetzt uint8_t.
Wie soll das denn bitte funktionieren?
Der Flash will mit Halbwörtern beschrieben werden, das sind uint16_t!
0x800fffd ist auch keine Halbwort Adresse, natürlich knallt das.
Das ist der Grund, warum ich diese verf***ten Bezeichnungen wie Word und
Half-Word so hasse. Bedeutet überall etwas anderes und ein Word ist für
mich 16 Bit, ein Half-Word demzufolge die Hälfte (siehe auch
https://de.wikipedia.org/wiki/Datenwort). Den Nebensatz mit 16 Bit in
der ersten Antwort habe ich leider auch überlesen.
Die Adressen sollten dann demzufolge durch 2 teilbar sein.
Nun geht es übrigens. Danke! Einmal schön um dieses (sorry)
Drecks-Half-Word drumherum programmiert...
Mw E. schrieb:> Ja mach bloß nicht das was in der dir bekannten Appnote steht und
Ich finde die Appnote Kacke. Ich hab die rauf- und runtergelesen und die
entscheidenden Fragen hat das Ding für mich irgendwie nicht beantworten
können. Zudem sind auch noch offenbar Fehler drin (Adressregister
beschreiben beim Programmieren).
Ich habe jetzt in den ATmega- und STM32F1 und F4-Familien verschiedenste
Schnittstellen erfolgreich zu Fuß mittels der Appnotes und Datenblätter
in Betrieb nehmen können, aber das hier war echt Kacke, wenn man mit
Flash-Speicher vorher nie etwas gemacht hat.
Gastino G. schrieb:> Das ist der Grund, warum ich diese verf***ten Bezeichnungen wie Word und> Half-Word so hasse. Bedeutet überall etwas anderes und ein Word ist für> mich 16 Bit, ein Half-Word demzufolge die Hälfte
Regg disch ned uff, ein Wort ist deshalb "überall was anderes"
weil es sich auf die Registerbreite der betreffenden Maschine
bezieht.
Das Beispiel im Wiki war aber für x86 ;)
Was was ist ist in jeder Architektur anders, daher wurden auch die
stdint.h eingeführt.
Die PM0075 hat das aber im Glossar noch extra definiert gehabt.
Das Glossar habe ich tatsächlich nur bis zur Hälfte gelesen.
Aber das hauptsächliche Problem war wahrscheinlich die ungerade
Speicheradresse, daran habe ich natürlich (auch) nicht gedacht.
Aber egal, nach dem Erfolg darf ich jetzt in's Bett. :)