mikrocontroller.net

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


Autor: Gastino G. (gastino)
Datum:

Bewertung
0 lesenswert
nicht 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:
uint16_t value1 = (uint16_t) 1234;          //erstes Word
uint16_t value2 = (uint16_t) 4567;          //zweites, soll direkt dahinter landen

uint32_t addressValue1 = 0x0800fc00;        //Adresse für erstes Word
uint32_t addressValue2 = 0x0800fc02;        //Adresse für das nächste direkt dahinter?

uint32_t *addressPointer;                   //bekommt später die Adresse, an die geschrieben werden soll
FLASH_TypeDef *flashRegisters = FLASH;

while (flashRegisters->SR & FLASH_SR_BSY) {};  //funktioniert

if (flashRegisters->CR & FLASH_CR_LOCK) {      //funktioniert auch

    flash_registers->KEYR = 0x45670123u;
    flash_registers->KEYR = 0xcdef89abu;
}

flashRegisters->AR = addressValue1;            //funktioniert auch

flashRegisters->CR |= FLASH_CR_PG;             //funktioniert auch

if (flashRegisters->SR & FLASH_SR_WRPRTERR) {  //write protection entfernen
    return ERROR;
}

if (flashRegisters->SR & FLASH_SR_PGERR) {     //erase machen
   return ERROR;
}

 addressPointer = (uint32_t *) addressValue1;
*addressPointer = value1;                      //richtig oder falsch?

flashRegisters->AR = addressValue2;
 addressPointer = (uint32_t *) addressValue2;
*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?

Autor: µBootLoader (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gastino G. schrieb:
>
>  addressPointer = (uint32_t *) addressValue1;
> *addressPointer = value1;                      //richtig oder falsch?

Hier fehlt IMO ein delay oder ein warten-auf-Status-Register Funktion.

> flashRegisters->AR = addressValue2;
>  addressPointer = (uint32_t *) addressValue2;
> *addressPointer = value2;                      //richtig oder falsch?
> 

Autor: Gastino G. (gastino)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Gastino G. (gastino)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag:

Hier geht der Controller regelmäßig in den HardFaultHandler:
*addressPointer = value1;

value1 ist ein uint16_t. Sauberer wäre ja ein
*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
Autor: Mw E. (Firma: fritzler-avr.de) (fritzler)
Datum:

Bewertung
0 lesenswert
nicht 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:
uint16_t *addressPointer;
addressPointer = (uint16_t *) addressValue1;
*addressPointer = value1;
mal probieren.

Autor: Gastino G. (gastino)
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: Jim M. (turboj)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Mw E. (Firma: fritzler-avr.de) (fritzler)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Johnny B. (johnnyb)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Axel S. (a-za-z0-9)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bimbo. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
#ifndef _FLASH_
#define _FLASH_

#include "stm32f4xx.h"

void Flash_Unlock();
void Flash_Lock();
void Flash_Erase(uint8_t ucSector);
void Flash_Write(uint32_t u32Addr, uint8_t * pucSrc, uint16_t usSize);

#endif


flash.c:
#include <flash.h>

void Flash_Unlock(){
  FLASH->KEYR = 0x45670123;
  FLASH->KEYR = 0xCDEF89AB;
}

void Flash_Lock(){
  FLASH->CR |= FLASH_CR_LOCK;
}

void Flash_Erase(uint8_t ucSector){
  while(FLASH->SR & FLASH_SR_BSY);
  FLASH->CR |= FLASH_CR_SER;
  FLASH->CR &= ~(0x0f << 3);
  FLASH->CR |= (ucSector << 3);
  FLASH->CR |= FLASH_CR_STRT;
  while(FLASH->SR & FLASH_SR_BSY);
}

void Flash_Write(uint32_t u32Addr, uint8_t * pucSrc, uint16_t usSize){
  uint8_t * pu8Dst = (uint8_t *)u32Addr;

  while(FLASH->SR & FLASH_SR_BSY);
  FLASH->CR |= FLASH_CR_PG;
  while(usSize-- > 0){
    *pu8Dst++ = *pucSrc++;
    while(FLASH->SR & FLASH_SR_BSY);
  }
}


Autor: Gastino G. (gastino)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Gastino G. (gastino)
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: Nico W. (nico_w)
Datum:

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

Autor: Bimbo. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Nico W. (nico_w)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Gastino G. (gastino)
Datum:

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

Mittlerweile sieht er so aus:

uint32_t flashWriteBuffer(uint32_t address, uint8_t *buffer, uint32_t numBytes) {

  uint8_t *flashAddressPointer = (uint8_t *) address;
  FLASH_TypeDef *flashRegisters = FLASH;
  uint32_t i, timeout;


    timeout = FLASH_IO_TIMEOUT;
    while (flashRegisters->SR & FLASH_SR_BSY) {
      if (timeout > 0u) {    
        timeout--;
      } else {      
        return ERROR;
      }
    }

    if (flashRegisters->CR & FLASH_CR_LOCK) {

      flashRegisters->KEYR = FLASH_UNLOCK_KEY1;
      flashRegisters->KEYR = FLASH_UNLOCK_KEY2;

      if (flashRegisters->CR & FLASH_CR_LOCK) {
        return ERROR;
      }

    }

    flashRegisters->AR = address;

    flashRegisters->CR = FLASH_CR_PG;

    if (flashRegisters->SR & FLASH_SR_WRPRTERR) {
      return ERROR;
    }

    if (flashRegisters->SR & FLASH_SR_PGERR) {
      return ERASE_ERROR;
    }

    for (i = 0; i < numBytes; i++) {

      //write the data into the flash
      *flashAddressPointer = buffer[i];    //<<<<-----------BUMMMMM!!!!! :o(((

      timeout = FLASH_IO_TIMEOUT;
      while (flashRegisters->SR & FLASH_SR_BSY) {
        if (timeout > 0u) {    
          timeout--;
        } else {                          
          return ERROR;
        }
      }

      if (flashRegisters->SR & FLASH_SR_EOP) {
        flashRegisters->SR |= FLASH_SR_EOP;
      }

      if (flashRegisters->SR & FLASH_SR_PGERR) {
        return FLASH_WRITE_ERASE_ERROR;
      }

      if (buffer[i] != *flashAddressPointer) {
        return ERROR;
      }

    }

    return OK;

}


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
Autor: Frank M. (ukw) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: Mw E. (Firma: fritzler-avr.de) (fritzler)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Gastino G. (gastino)
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: Erklehr Behr (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Mw E. (Firma: fritzler-avr.de) (fritzler)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Gastino G. (gastino)
Datum:

Bewertung
0 lesenswert
nicht 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. :)

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.