Forum: Mikrocontroller und Digitale Elektronik STM32F107 schreiben von Flash Variablen


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Martin (martin79)


Lesenswert?

Ich möchte Konfigurationsdaten, die später über ein protokoll noch 
verändert werden können in den Flash dauerhaft schreiben können. 
Allerdings weiß ich nicht wie ich dem Compiler und Linker sage, dass 
eine Variable oder Array in eine Flash Page geschrieben werden soll, da 
ich offenbar immer erst ein erase machen muss, bevor ich schreibe. Dort 
soll dann auf keinen Fall code sein.

Ich benutze STMCube. Dort gibt es zwar das EEPROM Emulation Beispiel 
aber daraus wird man nicht schlau wie man variablen Im Flash so 
definiert und dem Linker das mitteilt.

von Andreas B. (abm)


Lesenswert?

Für gcc:
Die betreffenden Variablen bekommen jeweils ein
"__attribute__((section(".eeprom")))"
und ins Linker Script kommt
"
  /* EEPROM emulation goes into EEPROM, needs flash page alignment */
  .eeprom :
  {
    . = ALIGN(2048);
    *(.eeprom*)
    . = ALIGN(2048);
  } >EEPROM
"
(je nach Typ braucht man statt 2048 auch event. ein größeres Alignment).
Und unter "MEMORY" knapst man dem "FLASH" ein wenig ab und packt das 
ganz analog ins "EEPROM".

von Monk (Gast)


Lesenswert?

Martin schrieb:
> Allerdings weiß ich nicht wie ich dem Compiler und Linker sage, dass
> eine Variable oder Array in eine Flash Page geschrieben werden soll

Ich denke, dass der Compiler dich dabei gar nicht unterstützt. Diesen 
Part musst du selbst programmieren, indem du die richtigen Register 
beschreibst. Dafür gibt es natürlich fertige Bibliotheksfunktionen.

Zum Beispiel die HAL
https://controllerstech.com/flash-programming-in-stm32/

Oder alternativ
Beitrag "STM32: EEPROM Emulator im Flash"

von Martin (martin79)


Lesenswert?

Danke ich habe den STM32F107RBT6. Ich verstehe es laut Datenblatt so, 
dass der zur Connectivity line gehört und jede Page 2kbytes groß ist. 
Der Controller hat 128k Flash also 64 verfügbare pages. Ich kann also 
die letzten zwei Pages als Konfigurationsspeicher nehmen und habe 
folgendes im Linkerskript:
1
MEMORY
2
{
3
  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 64K
4
  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 126K
5
  FLASH_CONFIG (rw)  : ORIGIN = 0x0801F800, LENGTH = 2K 
6
}
7
8
  .config_section :
9
  {
10
    . = ALIGN(4);
11
    KEEP(*(.config_section))
12
    . = ALIGN(4);
13
  } > FLASH_CONFIG

Das zeigt mir zumindest schonmal beim kompilieren die Flash Sektion 
FLASH_CONFIG in der Belegungsübersicht.

Dann habe ich gerade mal versucht eine Variable dorthin zu setzen:
1
static uint8_t flashvariable __attribute__((section(".config_section"))) = 45;

Wenn ich die irgendwo lese wird auch angezeigt, dass 4 bytes dieser 
Sektion belegt sind. Nun bin ich mir nicht sicher ob das alignment 
beliebig ist. Bei einem alignment von 4 bytes werden für ein uint8_t 
auch 4 bytes benutzt. Kann ich das auf 1 setzen?

Kann jemand einmal beurteilen ob das so richtig ist, was ich getan habe?

von Andreas B. (abm)


Lesenswert?

Martin schrieb:
> MEMORY
> {
>   RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 64K
>   FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 126K
>   FLASH_CONFIG (rw)  : ORIGIN = 0x0801F800, LENGTH = 2K
Das muss aber auch (r) oder (rx) sein, denn für Linker und Compiler
sind das erstmal konstante Daten. Das Löschen und Beschreiben geht am
Linker/Compiler vorbei.

> }
>   .config_section :
>   {
>     . = ALIGN(4);
>     KEEP(*(.config_section))
>     . = ALIGN(4);
>   } > FLASH_CONFIG
>
> Das zeigt mir zumindest schonmal beim kompilieren die Flash Sektion
> FLASH_CONFIG in der Belegungsübersicht.
>
> Dann habe ich gerade mal versucht eine Variable dorthin zu setzen:
> static uint8_t flashvariable __attribute__((section(".config_section")))
> = 45;
Das fehlt das 'const'.

> beliebig ist. Bei einem alignment von 4 bytes werden für ein uint8_t
> auch 4 bytes benutzt. Kann ich das auf 1 setzen?
Statt darüber nachzudenken, wäre es sinnvoller, alle 
Konfigurationsvariablen in eine struct zu packen, dann kann man ggf. 
über 'packed' Platz sparen.

Und dann diese struct vom EEPROM ins RAM kopieren, und bei Änderungen 
die Flash-Page löschen und die struct vom RAM ins EEPROM schreiben. Da 
kann man natürlich noch über inkrementelle Änderungen nachdenken, aber 
wenn die Konfiguration selten und wenn dann gleich an mehreren Stellen 
geändert werden soll, reicht auch diese einfache Variante als erster 
Wurf.

von Martin (martin79)


Lesenswert?

Außerdem noch folgende Frage. Wenn ich nun im Programm einen Zeiger auf 
diesen Speicherbereich übergebe, also &flashvariable, wird ein lesen 
dieser Variable durch den Compiler gehandhabt oder muss ich das vorher 
manuell machen und in eine Variable im SRAM schreiben?

Ich habe mal probiert diese Variable zu beschreiben und der Compiler 
meckert nicht. Übernimmt er auch das schreiben in den Flash? So wie ich 
es verstanden habe muss man genau das selber machen.

von Martin (martin79)


Lesenswert?

Andreas B. schrieb:
> Das muss aber auch (r) oder (rx) sein, denn für Linker und Compiler
> sind das erstmal konstante Daten. Das Löschen und Beschreiben geht am
> Linker/Compiler vorbei.

Danke habe ich verstanden und geändert.

Andreas B. schrieb:
> Statt darüber nachzudenken, wäre es sinnvoller, alle
> Konfigurationsvariablen in eine struct zu packen, dann kann man ggf.
> über 'packed' Platz sparen.

Das ist der Plan. Allerdings mache ich da nie ein packed, sondern 
kopiere dann per memcpy.

Andreas B. schrieb:
> Und dann diese struct vom EEPROM ins RAM kopieren, und bei Änderungen
> die Flash-Page löschen und die struct vom RAM ins EEPROM schreiben. Da
> kann man natürlich noch über inkrementelle Änderungen nachdenken, aber
> wenn die Konfiguration selten und wenn dann gleich an mehreren Stellen
> geändert werden soll, reicht auch diese einfache Variante als erster
> Wurf.

Was bedeutet hier inkrementell. Ich muss bei einer Änderung eines Bytes 
immer die ganze flash page löschen, oder?

von Monk (Gast)


Lesenswert?

Martin schrieb:
> Kann ich das auf 1 setzen?

Soweit ich das Buch von Joseph Yiu verstanden habe unterstützt der 
Cortex M3/M4 das, aber es wirkt sich negativ auf die Performance aus. 
Was in deinem Fall vermutlich völlig egal ist.

Da steht noch etwas dazu:
https://developer.arm.com/documentation/dui0552/a/the-cortex-m3-instruction-set/about-the-instruction-descriptions/address-alignment

> Bei einem alignment von 4 bytes werden für ein uint8_t auch 4 bytes benutzt.

Ja, aber ein Array von Bytes beginnt an einer aligned Adresse und 
enthält die Bytes Lückenlos hintereinander.

Andreas B. schrieb:
> Statt darüber nachzudenken, wäre es sinnvoller, alle
> Konfigurationsvariablen in eine struct zu packen, dann kann man ggf.
> über 'packed' Platz sparen.
> Und dann diese struct vom EEPROM ins RAM kopieren, und bei Änderungen
> die Flash-Page löschen und die struct vom RAM ins EEPROM schreiben.

So bin ich das gewohnt

Martin schrieb:
> Ich habe mal probiert diese Variable zu beschreiben und der Compiler
> meckert nicht. Übernimmt er auch das schreiben in den Flash? So wie ich
> es verstanden habe muss man genau das selber machen.

So ist es. Lesen ist einfach, weil der Flash Speicher ganz normal wie 
auch RAM adressiert wird. Schreiben erfordert wie gesagt Zugriffe auf 
spezielle Register, weil es eben kein RAM ist.

von Andreas B. (abm)


Lesenswert?

Martin schrieb:
> Was bedeutet hier inkrementell. Ich muss bei einer Änderung eines Bytes
> immer die ganze flash page löschen, oder?
Man hat nur eine "Kopie" der Struct im Flash, und ein Array, wo z. B. 
Offset und neuer Wert einer Variablen abgelegt werden. Für jedes Update 
wird dann ein neuer solcher Datensatz angehängt. Sobald das Array voll 
ist, alles Löschen und aktuelle Kopie des Struct wieder ins Flash.
Beim Start muss dann natürlich nach der Kopie vom Flash ins RAM die 
Liste der Updates abgearbeitet werden.

Ob sich das so lohnt, hängt halt davon ab, wie oft Änderungen erfolgen, 
und ob diese immer nur einzelne Variablen oder ein ganzes "Paket" 
betreffen.

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.