Forum: Mikrocontroller und Digitale Elektronik Daten in FLASH schreiben (STM32F103C8) - ohne Bibliothek


von Gastino G. (gastino)


Lesenswert?

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_t value1 = (uint16_t) 1234;          //erstes Word
2
uint16_t value2 = (uint16_t) 4567;          //zweites, soll direkt dahinter landen
3
4
uint32_t addressValue1 = 0x0800fc00;        //Adresse für erstes Word
5
uint32_t addressValue2 = 0x0800fc02;        //Adresse für das nächste direkt dahinter?
6
7
uint32_t *addressPointer;                   //bekommt später die Adresse, an die geschrieben werden soll
8
FLASH_TypeDef *flashRegisters = FLASH;
9
10
while (flashRegisters->SR & FLASH_SR_BSY) {};  //funktioniert
11
12
if (flashRegisters->CR & FLASH_CR_LOCK) {      //funktioniert auch
13
14
    flash_registers->KEYR = 0x45670123u;
15
    flash_registers->KEYR = 0xcdef89abu;
16
}
17
18
flashRegisters->AR = addressValue1;            //funktioniert auch
19
20
flashRegisters->CR |= FLASH_CR_PG;             //funktioniert auch
21
22
if (flashRegisters->SR & FLASH_SR_WRPRTERR) {  //write protection entfernen
23
    return ERROR;
24
}
25
26
if (flashRegisters->SR & FLASH_SR_PGERR) {     //erase machen
27
   return ERROR;
28
}
29
30
 addressPointer = (uint32_t *) addressValue1;
31
*addressPointer = value1;                      //richtig oder falsch?
32
33
flashRegisters->AR = addressValue2;
34
 addressPointer = (uint32_t *) addressValue2;
35
*addressPointer = value2;                      //richtig oder falsch?

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?

von µBootLoader (Gast)


Lesenswert?

Gastino G. schrieb:
>
1
>  addressPointer = (uint32_t *) addressValue1;
2
> *addressPointer = value1;                      //richtig oder falsch?
3
4
Hier fehlt IMO ein delay oder ein warten-auf-Status-Register Funktion.
5
6
> flashRegisters->AR = addressValue2;
7
>  addressPointer = (uint32_t *) addressValue2;
8
> *addressPointer = value2;                      //richtig oder falsch?
9
>

von Gastino G. (gastino)


Lesenswert?

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?

von Gastino G. (gastino)


Lesenswert?

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.

: Bearbeitet durch User
von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

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:
1
uint16_t *addressPointer;
2
addressPointer = (uint16_t *) addressValue1;
3
*addressPointer = value1;
mal probieren.

von Gastino G. (gastino)


Lesenswert?

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?

: Bearbeitet durch User
von Jim M. (turboj)


Lesenswert?

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...

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

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.

von Johnny B. (johnnyb)


Lesenswert?

Gastino G. schrieb:
> ich habe ein paar grundlegende Fragen zum Schreiben in das FLASH, wenn
> ich das "per Hand" machen will.

Ich würde mir mal die "EEPROM Emulation" von ST selbst angucken, denn 
die funktioniert einwandfrei und kann nicht so verkehrt sein, da von ST 
selbst.

Doku:
https://www.st.com/content/ccc/resource/technical/document/application_note/ee/ef/d7/87/cb/b7/48/52/CD00165693.pdf/files/CD00165693.pdf/jcr:content/translations/en.CD00165693.pdf

Sourcecode:
Ist im Firmware-Package zu Cube dabei

von Axel S. (a-za-z0-9)


Lesenswert?

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.

von Bimbo. (Gast)


Lesenswert?

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:
1
#ifndef _FLASH_
2
#define _FLASH_
3
4
#include "stm32f4xx.h"
5
6
void Flash_Unlock();
7
void Flash_Lock();
8
void Flash_Erase(uint8_t ucSector);
9
void Flash_Write(uint32_t u32Addr, uint8_t * pucSrc, uint16_t usSize);
10
11
#endif

flash.c:
1
#include <flash.h>
2
3
void Flash_Unlock(){
4
  FLASH->KEYR = 0x45670123;
5
  FLASH->KEYR = 0xCDEF89AB;
6
}
7
8
void Flash_Lock(){
9
  FLASH->CR |= FLASH_CR_LOCK;
10
}
11
12
void Flash_Erase(uint8_t ucSector){
13
  while(FLASH->SR & FLASH_SR_BSY);
14
  FLASH->CR |= FLASH_CR_SER;
15
  FLASH->CR &= ~(0x0f << 3);
16
  FLASH->CR |= (ucSector << 3);
17
  FLASH->CR |= FLASH_CR_STRT;
18
  while(FLASH->SR & FLASH_SR_BSY);
19
}
20
21
void Flash_Write(uint32_t u32Addr, uint8_t * pucSrc, uint16_t usSize){
22
  uint8_t * pu8Dst = (uint8_t *)u32Addr;
23
24
  while(FLASH->SR & FLASH_SR_BSY);
25
  FLASH->CR |= FLASH_CR_PG;
26
  while(usSize-- > 0){
27
    *pu8Dst++ = *pucSrc++;
28
    while(FLASH->SR & FLASH_SR_BSY);
29
  }
30
}

von Gastino G. (gastino)


Lesenswert?

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.

von Gastino G. (gastino)


Lesenswert?

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...

: Bearbeitet durch User
von Nico W. (nico_w)


Lesenswert?

Zeig doch bitte mal den kompletten Code wie du deinen Flash beschreiben 
möchtest.

von Bimbo. (Gast)


Lesenswert?

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.

von Nico W. (nico_w)


Lesenswert?

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 braucht man um die richtige Page zu löschen. Zum Beschreiben wird 
das Register nicht benötigt. Lese ich jetzt aber aus dem PM0075 [1] aber 
auch nicht raus.

[1] 
https://www.st.com/content/ccc/resource/technical/document/programming_manual/10/98/e8/d4/2b/51/4b/f5/CD00283419.pdf/files/CD00283419.pdf/jcr:content/translations/en.CD00283419.pdf

von Gastino G. (gastino)


Lesenswert?

Nico W. schrieb:
> Zeig doch bitte mal den kompletten Code wie du deinen Flash beschreiben
> möchtest.

Mittlerweile sieht er so aus:
1
uint32_t flashWriteBuffer(uint32_t address, uint8_t *buffer, uint32_t numBytes) {
2
3
  uint8_t *flashAddressPointer = (uint8_t *) address;
4
  FLASH_TypeDef *flashRegisters = FLASH;
5
  uint32_t i, timeout;
6
7
8
    timeout = FLASH_IO_TIMEOUT;
9
    while (flashRegisters->SR & FLASH_SR_BSY) {
10
      if (timeout > 0u) {    
11
        timeout--;
12
      } else {      
13
        return ERROR;
14
      }
15
    }
16
17
    if (flashRegisters->CR & FLASH_CR_LOCK) {
18
19
      flashRegisters->KEYR = FLASH_UNLOCK_KEY1;
20
      flashRegisters->KEYR = FLASH_UNLOCK_KEY2;
21
22
      if (flashRegisters->CR & FLASH_CR_LOCK) {
23
        return ERROR;
24
      }
25
26
    }
27
28
    flashRegisters->AR = address;
29
30
    flashRegisters->CR = FLASH_CR_PG;
31
32
    if (flashRegisters->SR & FLASH_SR_WRPRTERR) {
33
      return ERROR;
34
    }
35
36
    if (flashRegisters->SR & FLASH_SR_PGERR) {
37
      return ERASE_ERROR;
38
    }
39
40
    for (i = 0; i < numBytes; i++) {
41
42
      //write the data into the flash
43
      *flashAddressPointer = buffer[i];    //<<<<-----------BUMMMMM!!!!! :o(((
44
45
      timeout = FLASH_IO_TIMEOUT;
46
      while (flashRegisters->SR & FLASH_SR_BSY) {
47
        if (timeout > 0u) {    
48
          timeout--;
49
        } else {                          
50
          return ERROR;
51
        }
52
      }
53
54
      if (flashRegisters->SR & FLASH_SR_EOP) {
55
        flashRegisters->SR |= FLASH_SR_EOP;
56
      }
57
58
      if (flashRegisters->SR & FLASH_SR_PGERR) {
59
        return FLASH_WRITE_ERASE_ERROR;
60
      }
61
62
      if (buffer[i] != *flashAddressPointer) {
63
        return ERROR;
64
      }
65
66
    }
67
68
    return OK;
69
70
}

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.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

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.

von Gastino G. (gastino)


Lesenswert?

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.

: Bearbeitet durch User
von Erklehr Behr (Gast)


Lesenswert?

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.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

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.

von Gastino G. (gastino)


Lesenswert?

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. :)

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
Noch kein Account? Hier anmelden.