57-Virtuelle_EEprom-Library (STM32F4)

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 :

ub_stm32f4_ee_flash_v100

Hier der komplette CooCox-Projektordner zum Download :

Demo_57_EEprom


22 Antworten auf 57-Virtuelle_EEprom-Library (STM32F4)

  1. Jan Berg sagt:

    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

  2. lungfish sagt:

    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.

    • martin sagt:

      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ß

      • admin_ub sagt:

        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.

    • ain sagt:

      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

  3. Jim sagt:

    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

  4. Joerg sagt:

    Funktioniert die Lib auch mit dem 429 ohne Änderungen?

    • admin_ub sagt:

      bin mir nicht ganz sicher aber wahrscheinlich schon.

  5. martin sagt:

    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!

  6. Erat sagt:

    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

    • admin_ub sagt:

      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

      • Erat sagt:

        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.

          • admin_ub sagt:

            Danke für den Hinweis, wie häufig kommt es zu diesem Fehler ?

          • Erat sagt:

            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.

  7. Dominik sagt:

    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

    • admin_ub sagt:

      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

  8. Jens sagt:

    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

    • admin_ub sagt:

      das Referenz Manual gibts bei ST als download und die Seitenzahl
      ist nur eine Info und kann bei einer anderen Version vom Manual abweichen.

  9. Christian sagt:

    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.

    • admin_ub sagt:

      versuch das hier :

      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
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      
      OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
       
      /* changed for Lib : stm32_ub_ee_flash */ 
       
      /* Internal Memory Map*/
      MEMORY
      {
      /* rom (rx)  : ORIGIN = 0x08000000, LENGTH = 1024K (original F4) */
      rom0 (rx) : ORIGIN = 0x08000000, LENGTH = 32K
      eeprom (rx) : ORIGIN = 0x8008000, LENGTH = 32k /* Block2+3 = eeprom */
      rom1 (rx) : ORIGIN = 0x08010000, LENGTH = 960K 
      ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
      ram1 (rwx) : ORIGIN = 0x10000000, LENGTH = 64K
      }
       
      _eram = 0x20000000 + 0x00020000;
      SECTIONS
      {
      .text0 :
      {
      KEEP(*(.isr_vector))
      } > rom0
       
      .text :
      {
      *(.text*)
       
      KEEP(*(.init))
      KEEP(*(.fini))
       
      /* .ctors */
      *crtbegin.o(.ctors)
      *crtbegin?.o(.ctors)
      *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
      *(SORT(.ctors.*))
      *(.ctors)
       
      /* .dtors */
      *crtbegin.o(.dtors)
      *crtbegin?.o(.dtors)
      *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
      *(SORT(.dtors.*))
      *(.dtors)
       
      *(.rodata*)
       
      KEEP(*(.eh_fram e*))
      } > rom1 
       
      .ARM.extab : 
      {
      *(.ARM.extab* .gnu.linkonce.armextab.*)
      } > rom1 
       
      __exidx_start = .;
      .ARM.exidx :
      {
      *(.ARM.exidx* .gnu.linkonce.armexidx.*)
      } > rom1 
      __exidx_end = .;
      __etext = .;
       
      /* _sidata is used in coide startup code */
      _sidata = __etext;
       
      .data : AT (__etext)
      {
      __data_start__ = .;
       
      /* _sdata is used in coide startup code */
      _sdata = __data_start__;
       
      *(vtable)
      *(.data*)
       
      . = ALIGN(4);
      /* preinit data */
      PROVIDE_HIDDEN (__preinit_array_start = .);
      KEEP(*(.preinit_array))
      PROVIDE_HIDDEN (__preinit_array_end = .);
       
      . = ALIGN(4);
      /* init data */
      PROVIDE_HIDDEN (__init_array_start = .);
      KEEP(*(SORT(.init_array.*)))
      KEEP(*(.init_array))
      PROVIDE_HIDDEN (__init_array_end = .);
       
      . = ALIGN(4);
      /* finit data */
      PROVIDE_HIDDEN (__fini_array_start = .);
      KEEP(*(SORT(.fini_array.*)))
      KEEP(*(.fini_array))
      PROVIDE_HIDDEN (__fini_array_end = .);
       
      KEEP(*(.jcr*))
      . = ALIGN(4);
      /* All data end */
      __data_end__ = .;
       
      /* _edata is used in coide startup code */
      _edata = __data_end__;
      } > ram 
       
      .bss :
      {
      . = ALIGN(4);
      __bss_start__ = .;
      _sbss = __bss_start__;
      *(.bss*)
      *(COMMON)
      . = ALIGN(4);
      __bss_end__ = .;
      _ebss = __bss_end__;
      } > ram 
       
      .heap (COPY):
      {
      __end__ = .;
      _end = __end__;
      end = __end__;
      *(.heap*)
      __HeapLimit = .;
      } > ram 
       
      /* .stack_dummy section doesn't contains any symbols. It is only
      * used for linker to calculate size of stack sections, and assign
      * values to stack symbols later */
      .co_stack (NOLOAD):
      {
      . = ALIGN(8);
      *(.co_stack .co_stack.*)
      } > ram 
       
      /* Set stack top to end of ram , and stack limit move down by
      * size of stack_dummy section */
      __StackTop = ORIGIN(ram ) + LENGTH(ram );
      __StackLimit = __StackTop - SIZEOF(.co_stack);
      PROVIDE(__stack = __StackTop);
       
      /* Check if data + heap + stack exceeds ram  limit */
      ASSERT(__StackLimit >= __HeapLimit, "region ram  overflowed with stack")
      }

  10. Christian Julius sagt:

    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


Wie hat Dir dieser Artikel gefallen?

1 Stern2 Sterne3 Sterne4 Sterne5 Sterne (Noch keine Bewertungen)
Loading...

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.