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.
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".
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"
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?
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.
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.
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?
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.