Diese Library emuliert ein EEprom mit dem internem Flash vom STM32F4.
Das ermöglicht ein permanentes abspeichern von Datenwerten ohne das ein zusätzlicher externer Akku angeschlossen sein muß.
Im H-File muss eingestellt werden, wie groß das virtuelle EEprom sein soll.
Die Datenbreite beträgt 16bit (pro Adresse)
Es gibt eine Funktion zum lesen aus dem “EEprom” und eine zum schreiben.
Als Übergabeparameter muss die “Adresse” die gelesen/geschrieben werden soll angegeben werden.
Wichtig !! Der Rückgabewert ist ein Signed-INT32 damit man Fehler beim lesen/schreiben erkennen kann. (dann ist der Returnwert kleiner Null)
Die Funktionsweise wird in einer Apllication_note (AN3969) von ST erklärt.
In kurzen Worten :
1. Zum speichern der Daten werden zwei 16kByte große Flash-Blöcke benutzt
2. Bei jedem “write” werden die Daten in einer freien Flash-Zelle gespeichert, zusammen mit der “virtuellen Adresse” (es werden also 32Bit pro Datensatz belegt)
3. Beim “read” wird der Flash-Block nach dem letzten gespeicherten Datensatz durchsucht
und dieser wird dann zurückgegeben.
4. Wenn ein Flash-Block voll ist, werden die aktuellen Daten in den zweiten Block kopiert und der erste Block wird komplett gelöscht.
Vorsicht !!
Das Flash wird angegeben mit 100.000 Schreibzyklen.
Rechenbeispiel : Zehn Variabeln werden alle 10ms (100 mal pro Sekunde) in das Flash geschrieben. Mit den beiden 16k Blöcken wird damit eine einzelne Zelle alle 8 Sekunden überschrieben. Damit wäre das Flash nach 9 Tagen (24/7) defekt !!
Benutzte Pins :
1 | keine |
Voraussetzungen :
1 2 | Benutzte Module der CooCox-IDE : FLASH Benutzte Librarys : keine |
Funktionen :
1 2 3 | ErrorStatus UB_EE_FLASH_Init(void); // zum initialisieren int32_t UB_EE_FLASH_Read(uint16_t VirtAddress); // zum lesen aus dem "EEprom" int32_t UB_EE_FLASH_Write(uint16_t VirtAddress, uint16_t Data); // zum schreiben ins "EEprom" |
Beispiel :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | //-------------------------------------------------------------- // File : main.c // Datum : 05.09.2013 // Version : 1.0 // Autor : UB // EMail : mc-4u(@)t-online.de // Web : www.mikrocontroller-4u.de // CPU : STM32F4 // IDE : CooCox CoIDE 1.7.4 // GCC : 4.7 2012q4 // Module : CMSIS_BOOT, M4_CMSIS_CORE // Funktion : Demo der virtuellen EEprom Lib // Hinweis : Diese zwei Files muessen auf 8MHz stehen // "cmsis_boot/stm32f4xx.h" // "cmsis_boot/system_stm32f4xx.c" //-------------------------------------------------------------- #include "main.h" #include "stm32_ub_led.h" #include "stm32_ub_ee_flash.h" int main(void) { ErrorStatus check; int32_t ee_wert; SystemInit(); // Quarz Einstellungen aktivieren // init der LEDs UB_Led_Init(); // init vom virtuellen EEProm check=UB_EE_FLASH_Init(); if(check==SUCCESS) { // grüne LED einschalten UB_Led_On(LED_GREEN); // EEprom Adresse 0x00 auslesen ee_wert=UB_EE_FLASH_Read(0x00); // test ob Inhalt stimmt if(ee_wert==0x3AC4) { // Inhalt ist richtig UB_Led_On(LED_BLUE); } else { // wenn Inhalt nicht stimmt UB_Led_On(LED_ORANGE); // Adresse 0x00 mit Wert 0x3AC4 beschreiben UB_EE_FLASH_Write(0x00,0x3AC4); } } else { // Fehler UB_Led_On(LED_RED); } while(1) { } } |
Hier die Library zum Download :
Hier der komplette CooCox-Projektordner zum Download :
Hi,
herzlichen Dank für deine Library, echt genial, das erspart mir in Zukunft kleine Eeproms einzusetzen
Hinzufügen möchte ich noch, dass für die Benutzung der Library in eigenen Projekten die Link Time Optimization (LTO) in den Linker Optionen deaktiviert werden muss.
Liebe Grüße,
Jan
Vielen Dank für Demo. Ohne passendes Linkerscript überschreibt man aber irgendwann den Programmspeicher.
Mit einem entsprechenden Linkerscript bekommt man eine
EEPROM-Emulation auf dem F4 sauber mit jeder beliebigen Codegröße hin:
sector0 (rx) : ORIGIN = 0×08000000, LENGTH = 0×00004000
feeprom (rx) : ORIGIN = 0×08004000, LENGTH = 0×00008000
rom (rx) : ORIGIN = 0x0800C000, LENGTH = 0×00074000
sector0 enthält nur die Vektoren.
feeprom enthält Flashseiten 1 und 2.
rom enthält alle anderen Festwerte und liegt in den großen
Flashpages.
Mit diesem Setup kann man mit nur knapp 3x 16kB ein EEPROM emulieren. Ist
bei den F4 meist kein Beinbruch und spart das externe EEPROM.
Hallo lungfish/admin
Ich kann im Beispielprojetzt keine memory.ld finden in die ich den speicherbereich eintragen kann oder muss ich die Anderungnen in der arm-gcc-link.ld eintragen.
Leider ist mein projekt schon etwas angewachsen und daher muss ich den Speicherbereich freihalten.
bin für jede Hilfe dankbar
gruß
um ein eigenes Linker-Script File bei CoIDE zu benutzen, muss bei “Configuration/Link” der Haken bei “Use Memory Layout from Memory Window” entfernt werden. Danach liegt ein Linker-Script File im Projekt-Ordern. Dieses kann editiert und bei CoIDE unter “Scatter File” wieder eingebunden werden.
Damit die Lösung von lungfish funktioniert, muss mann in der stm32_ub_ee_flash.h Page 0 und 1 auch wirklich auf Sector 1 und 2 Setzten.
#define FLASH_SEKTOR_SIZE ((uint32_t)0×4000) // 16kByte
#define FLASH_SEKTOR1_START ((uint32_t)0×08004000) // Sektor_1
#define FLASH_SEKTOR2_START ((uint32_t)0×08008000) // Sektor_2
…
#define PAGE0_ID FLASH_Sector_1
…
#define PAGE1_ID FLASH_Sector_2
Hi
I have tried your virtual EEPROM library and it works great if the program is small enough to fit the first sectors. But when the program size becomes bigger it tends to end up in the sectors that are being used as virtual EEPROM. I have tried changing the linker script as lungfish said but without any luck. I have changed the linker script as follows:
OUTPUT_FORMAT (“elf32-littlearm”, “elf32-bigarm”, “elf32-littlearm”)
/* Internal Memory Map*/
MEMORY
{
ram (rwx) : ORIGIN = 0×20000000, LENGTH = 0×00020000
ram1 (rwx) : ORIGIN = 0×10000000, LENGTH = 0×00010000
sector0 (rx) : ORIGIN = 0×08000000, LENGTH = 0×00004000
feeprom (rx) : ORIGIN = 0×08004000, LENGTH = 0×00008000
rom (rx) : ORIGIN = 0x0800C000, LENGTH = 0×00074000
}
_eram = 0×20000000 + 0×00020000;
/* Section Definitions */
SECTIONS
{
.text0 :
{
KEEP(*(.isr_vector))
} >sector0
.text :
{
/* KEEP(*(.isr_vector .isr_vector.*)) */
*(.text .text.* .gnu.linkonce.t.*)
*(.glue_7t) *(.glue_7)
*(.rodata .rodata* .gnu.linkonce.r.*)
} > rom
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > rom
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > rom
__exidx_end = .;
. = ALIGN(4);
_etext = .;
_sidata = .;
.data : AT (_etext)
{
_sdata = .;
*(.data .data.*)
. = ALIGN(4);
_edata = . ;
} > ram
/* .bss section which is used for uninitialized data */
.bss (NOLOAD) :
{
_sbss = . ;
*(.bss .bss.*)
*(COMMON)
. = ALIGN(4);
_ebss = . ;
} > ram
/* stack section */
.co_stack (NOLOAD):
{
. = ALIGN(8);
*(.co_stack .co_stack.*)
} > ram
. = ALIGN(4);
_end = . ;
}
Anyone having any idea why it’s not working?
Here is a link to my cocox project: https://dl.dropboxusercontent.com/u/10684755/STM32F4_Discovery_CoOS%20eeprom%20test.rar
Funktioniert die Lib auch mit dem 429 ohne Änderungen?
bin mir nicht ganz sicher aber wahrscheinlich schon.
Hallo admin_ub,
ich wollt nur kurz berichten hab dein Programm jetzt implementiert allerdings nutze ich sectro 10 und 11 um genug platz zu lassen für den Programmcode. Leider funktioniert der tip von lungfish mit
sector0 (rx) : ORIGIN = 0×08000000, LENGTH = 0×00004000
feeprom (rx) : ORIGIN = 0×08004000, LENGTH = 0×00008000
rom (rx) : ORIGIN = 0x0800C000, LENGTH = 0×00074000
bei mir nicht. Vermute der linker erkennt es nicht richtig.
weshalb ich es so umgesetzt habe:
rom (rx) : ORIGIN = 0×08000000, LENGTH = 0x000C0000
rom1 (rx) : ORIGIN = 0x080C0000, LENGTH = 0×00040000
Mir ist immer noch nicht ganz klar wie es sich richtig gehört aber es
funktioniert erst mal.
Komplement du machst hier tolle Arbeit!
Hallo,
was ist denn der funktionale Unterschied zur flash-eeprom Implementierung von ST? Der Code scheint mir sehr ähnlich. Gibt es inhaltliche Unterschiede außer Namen, Kommentare und Formatierung? Was war der Grund für deine Neuimplementierung?
Danke
es gibt keinen Unterschied…das ist der Code von ST. Nur als Library “verpackt”…soll bedeuten : die Funktionen liegen nicht alle im Main.c Quellcode
Ok, danke für die Info. Ich bin gerade an einem code review, weil wir den Fall hatten, dass plötzlich alle Variablen aus dem Flash gelöscht waren.
Wie es scheint, ist es mit diesem Code möglich, dass abhängig vom Power Off Zeitpunkt die beiden Flash Pages in einem unerlaubten Zustand sind, und das führt dazu, dass sie beim nächsten Start (dh UB_EE_Flash_Init()) gnadenlos neu formatiert, dh gelöscht werden.
Die erste solche Stelle ist in Zeile 111: bei Power off nach Ausführung dieser Zeile sind beide pages als VALID markiert, was ein ungültiger Zustand ist. Man müsste die Reihenfolge umdrehen: zuerst löschen und dann VALID markieren.
P. S. Siehe auch: https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32Java/Flat.aspx?RootFolder=%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fSTM32Java%2fBug%20%28possible%20data%20loss%29%20in%20STM32F4xx_EEPROM_Emulation%20%28AN3969%29
Danke für den Hinweis, wie häufig kommt es zu diesem Fehler ?
Bis jetzt ist es in meinen Tests in etwa zwei Monaten zwei mal vorgekommen, dass ein Exemplar unerklärlich seine ganzen Settings verloren hatte. Ob das dieser Bug war oder noch was anderes, weiss niemand – aber die Wahrscheinlichkeit, dass genau in diesem Moment ein Power Off passiert, halte ich an sich für sehr klein. Von daher fürchte ich, dass es noch einen andere Ursache für gelöschtes Flash-EEPROM geben könnte…
Ich habe zusätzlich zur Änderung der o. g. Reihenfolge (löschen – markieren) auch noch die automatische Formatierung des Flash entfernt in allen Fällen außer wenn beide Blöcke EE_ERASED sind. Das letztere ist der einzige legitime Grund neu zu formatieren, nämlich bei einem neuen (leeren) Controller beim ersten Start der Firmware.
Hallo
Ich habe das Flash folgendermassen konfiguriert:
#define EE_FLASH_MAX_ADR 400
//————————————————————–
// Spannung der CPU muss zwischen 2.7V und 3.6V liegen
//————————————————————–
#define VOLTAGE_RANGE (uint8_t)VoltageRange_3
//————————————————————–
// Adressen der beiden Pages
// benutzt werden zwei Sektoren im Flash
// die beiden Sektoren muessen gleich gross sein
// -> Adressen und Groesse siehe (Referenz-Manual Seite 61)
//————————————————————–
#define FLASH_SEKTOR_SIZE ((uint32_t)0×4000) // 16kByte
#define FLASH_SEKTOR2_START ((uint32_t)0×08020000) // Sektor_2
#define FLASH_SEKTOR3_START ((uint32_t)0×08024000) // Sektor_3
Die Linker Config ist für ROM Start = 0×08000000 und End = 0x080FFFFF
Ich arbeite mit der IAR Embedded Workbench.
Beim Aufruf der Funktion check=UB_EE_FLASH_Init(); erhalte ich ERROR, vorher mit dieser Einstellung:
-#define FLASH_SEKTOR2_START ((uint32_t)0×08008000) // Sektor_2
-#define FLASH_SEKTOR3_START ((uint32_t)0x0800C000) // Sektor_3
hat es immer geklappt. Jetzt ist mein Code aber grösser und ich muss den Bereich schieben, aber es funktioniert nicht, warum?
Danke
wenn du statt Sektor 2+3 die Sektoren 5+6 benutzen willst (die sind 128k groß) und kann man auch nicht kleiner einstellen :
#define FLASH_SEKTOR_SIZE ((uint32_t)0x20000) // 128kByte
#define FLASH_SEKTOR5_START ((uint32_t)0x08020000) // Sektor_5
#define FLASH_SEKTOR6_START ((uint32_t)0x08040000) // Sektor_6
//—————-
// Page-0
//—————-
#define PAGE0_BASE_ADDRESS ((uint32_t)(FLASH_SEKTOR5_START))
#define PAGE0_END_ADDRESS ((uint32_t)(PAGE0_BASE_ADDRESS + (FLASH_SEKTOR_SIZE – 1)))
#define PAGE0_ID FLASH_Sector_5
//—————-
// Page-1
//—————-
#define PAGE1_BASE_ADDRESS ((uint32_t)(FLASH_SEKTOR6_START))
#define PAGE1_END_ADDRESS ((uint32_t)(PAGE1_BASE_ADDRESS + (FLASH_SEKTOR_SIZE – 1)))
#define PAGE1_ID FLASH_Sector_6
Hallo,
immer wenn ich “UB_EE_FLASH_Init();” in meinem Programm aufrufe, bleibt das Programm beim Debuggen hier stehen. Ich habe die Adresse wie hier beschrieben geändert,da mein Programm etwas größer ist. An dieser Stelle ist noch frei.
#define FLASH_SEKTOR2_START ((uint32_t)0x08036D00) // Sektor_2
#define FLASH_SEKTOR3_START ((uint32_t)0x0803AD12) // Sektor_3
Was muss ich alles beachten, wenn ich diese Bereiche verschieben möchte?
Und wo finde ich dieses “Referenz Manual Seite 61″?
//————————————————————–
// Adressen der beiden Pages
// benutzt werden zwei Sektoren im Flash
// die beiden Sektoren muessen gleich gross sein
// -> Adressen und Groesse siehe (Referenz-Manual Seite 61)
//————————————————————–
Vielen Dank und viele Grüße
Jens
das Referenz Manual gibts bei ST als download und die Seitenzahl
ist nur eine Info und kann bei einer anderen Version vom Manual abweichen.
Gibt es denn inzwischen ein Linkerscript, welches die verwendeten Sektoren für die Codeerzeugung ausspart? Mein Projekt bügelt da mit 100kb locker über die beiden Sektoren drüber. Aber es muss doch eine Möglichkeit geben, dass der Linker genau diese beiden Blöcke ausspart.
Das Manual ist heute Seite 71, dort ist die Tabelle mit den Adressen.
versuch das hier :
Ok, fehlt noch das ram1 (=CCRAM)
/* CCM-RAM section */
_siccram = LOADADDR(.ccram);
.ccram :
{
. = ALIGN(4);
_sccram = .; /* create a global symbol at ccmram start */
*(.ccram)
*(.ccram*)
. = ALIGN(4);
_eccram = .; /* create a global symbol at ccmram end */
} >CCRAM