STM32F107 schreiben von Flash Variablen

OP #7667253
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.

#7667266
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".

Gast #7667278
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"

OP #7667309
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?

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

OP #7667323
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.

OP #7667329
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?

Gast #7667331
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.

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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren