Forum: Mikrocontroller und Digitale Elektronik STM32F3 Virtual EEPROM für große Look Up


von Bert S. (kautschuck)


Angehängte Dateien:

Lesenswert?

Hi,

Ich möchte eine Look Up Table (4kByte) im virtuellen EEPROM eines 
STM32F3F303K8T6 speichern, jedoch klappt das irgendwie nicht mehr ab 
dieser Größe. 256xuint16_t=512Byte klappt noch, 512xuint16_t=1kByte 
nicht mehr. Die Library die ich verwende habe ich oben angehängt und ist 
von STM32 und ich habe bereits einige Einstellungen versucht anzupassen, 
jedoch leider ohne Erfolg. Eine Page hat die Grösse von 2kByte, daher 
sollte ja eigentlich zumindest die 512xuint16_t ohne Probleme 
reinpassen. Ich habe auch versucht bereits bei der Flash Addresse 
ADDR_FLASH_PAGE_16 zu starten, jedoch läuft mir das in einen Hardfault.

Mit dem Speicher kenne ich mich einfach zu wenig aus und frage daher 
hier im Forum um Rat, wie ich den Speicher vergrößern kann, bzw. ob das 
überhaupt so klappen kann?

Hier gibt es ja von STM32 ein Application Note dazu:
https://www.st.com/content/ccc/resource/technical/document/application_note/ee/ef/d7/87/cb/b7/48/52/CD00165693.pdf/files/CD00165693.pdf/jcr:content/translations/en.CD00165693.pdf

Und es wird beschrieben, dass in ihrem Beispiel nur 2 Pages à je 2kByte 
verwendet werden und eine Page immer in die andere kopiert wird. Daher 
kann ich in diesem Beispiel ja nur 2kByte speichern, jedoch klappt bei 
mir nicht einmal 1kByte.

Gruss Bert

von Bert S. (kautschuck)


Lesenswert?

Ok, ich sehe zumindest jetzt, warum es nicht geht. Es wird ja immer die 
Addresse und die Daten gespeichert, also immer 32Byte für eine uint16_t. 
Daher kann ich in einer 2kByte Page nur 512xuint16_t speichern. Nun 
werden ja bei einer vollen Page einfach die alten Daten in eine neue 
Page übertragen. Kann ich aber die Page auch grösser als die 2kByte 
wählen, also z.B 16kByte? Oder klappt das mit dem Flash dann nicht mehr, 
da FLASH_PAGE_SIZE standardmäßig auf 2kByte ist.

Edit: Ok habe es hinbekommen, einfach die FLASH_PAGE_SIZE erhöht und nun 
kann ich alles speicher wie gewollt.

: Bearbeitet durch User
von Bert S. (kautschuck)


Angehängte Dateien:

Lesenswert?

Leider klappt es irgendwie immer noch nicht, zumindest der Page Wechsel. 
Nachdem Page 0 voll ist und Page 1 beschrieben wird (mit HAL_OK), gibt 
es beim Neustart des Controllers bei EE_Init() immer den Flash Status 
128 (PAGE_FULL) zurück, somit einen Fehler. Ich verstehe nicht, warum 
ich nicht einfach von Page 1 nach Page 0 kopieren kann. Im Anhang sind 
die aktuellen EEPROM files.

: Bearbeitet durch User
von Bert S. (kautschuck)


Lesenswert?

Irgendwie kann Page 0 nicht transferiert werden. Sobald Page 0 voll ist, 
dann geht mit jedem Write Cycle der EEPROM Manager in die Routine 
EE_PageTransfer(), aber der Status bleibt immer PAGE_FULL.

1
uint16_t EE_WriteVariable(uint16_t VirtAddress, uint16_t Data)
2
{
3
  uint16_t Status = 0;
4
5
  /* Write the variable virtual address and value in the EEPROM */
6
  Status = EE_VerifyPageFullWriteVariable(VirtAddress, Data);
7
8
  /* In case the EEPROM active page is full */
9
  if (Status == PAGE_FULL)
10
  {
11
    /* Perform Page transfer */
12
    Status = EE_PageTransfer(VirtAddress, Data);
13
  }
14
15
  /* Return last operation status */
16
  return Status;
17
}


Nun kommt der also in EE_PageTransfer() und dort in folgendes:

1
validpage = EE_FindValidPage(READ_FROM_VALID_PAGE);
2
3
  if (validpage == PAGE1)       /* Page1 valid */
4
  {
5
    /* New page address where variable will be moved to */
6
    newpageaddress = PAGE0_BASE_ADDRESS;
7
8
    /* Old page ID where variable will be taken from */
9
    oldpageid = PAGE1_BASE_ADDRESS;
10
  }
11
  else if (validpage == PAGE0)  /* Page0 valid */
12
  {
13
    /* New page address  where variable will be moved to */
14
    newpageaddress = PAGE1_BASE_ADDRESS;
15
16
    /* Old page ID where variable will be taken from */
17
    oldpageid = PAGE0_BASE_ADDRESS;
18
  }

wobei er nun Page 1 als die Valid Page ansieht und nicht Page 0 und 
somit geht er in die erste if() rein. Ist das ein Fehler in der Library 
von STM32?

: Bearbeitet durch User
von Johnny B. (johnnyb)


Lesenswert?

Also wenn Du nur eine Lookup-Table schreiben willst, dann brauchst Du 
die "EEPROM-Emulation" eigentlich nicht, denn die ist dafür gemacht, 
dass Du einzelne Werte immer wieder ändern kannst, ohne dass jeweils 
immer eine ganze Page gelöscht/beschrieben werden muss.

Aber Du musst ja nur die ganze Page löschen und kannst dann einmalig, 
Byte für Byte Deine Lookuptable reinschreiben und gut ist. Lesen kannst 
Du dann direkt mittels einem Pointer und brauchst den ganzen Kram von 
der EEPROM-Emulation auch nicht.

von Bert S. (kautschuck)


Lesenswert?

Johnny B. schrieb:
> Also wenn Du nur eine Lookup-Table schreiben willst, dann brauchst Du
> die "EEPROM-Emulation" eigentlich nicht, denn die ist dafür gemacht,
> dass Du einzelne Werte immer wieder ändern kannst, ohne dass jeweils
> immer eine ganze Page gelöscht/beschrieben werden muss.

Die Look Up wird eben über eine Software angepasst, aber nur sehr 
selten. Daher wäre der virtuelle EEPROM ideal mit den wenigen Write 
Cycles.

Johnny B. schrieb:
> Aber Du musst ja nur die ganze Page löschen und kannst dann einmalig,
> Byte für Byte Deine Lookuptable reinschreiben und gut ist. Lesen kannst
> Du dann direkt mittels einem Pointer und brauchst den ganzen Kram von
> der EEPROM-Emulation auch nicht.

Im Speicher befinden sich noch etwa 64Byte andere Parameter, daher ist 
das nicht mehr so praktisch. Ich verstehe aber nicht, wieso das nicht 
hinhaut mit den Bibliotheken von STM32.

von foobar (Gast)


Lesenswert?

> Kann ich aber die Page auch grösser als die 2kByte wählen,
> also z.B 16kByte?

Nein, dafür sind die Routinen nicht ausgelegt.

Darf ich frage, warum du eine Lookup-Tabelle in diese verquerte 
EEPROM-Nachbildung packen willst? So ne Tabelle ist doch normalerweise 
statisch und braucht den Pipapo gar nicht. Und falls sie doch mal alle 
Jubeljahre angepasst werden muß, schreib direkt ins Flash, ohne den 
Emulationslayer.

von Bert S. (kautschuck)


Lesenswert?

foobar schrieb:
> Und falls sie doch mal alle
> Jubeljahre angepasst werden muß, schreib direkt ins Flash, ohne den
> Emulationslayer.

Das könnte man schon, aber die Emulationslayer erlaubt natürlich 
wesentlich mehr Schreibzyklen bevor der EEPROM den Geist aufgibt. Mich 
hat aber auch der Ehrgeiz gepackt das Problem zu lösen :D.

foobar schrieb:
> Nein, dafür sind die Routinen nicht ausgelegt.

Das wird wohl schon so sein, denn sobald Page 1 1kByte (2kByte mit 
Addressen inklusive) ist, wird erneut versucht die ganze Page zu 
transferieren. Ich schaue mal noch wo genau die Routinen angepasst 
werden müssen, dass werden ja wohl nur ein paar Defines sein.

: Bearbeitet durch User
von foobar (Gast)


Lesenswert?

> die Emulationslayer erlaubt natürlich wesentlich mehr Schreibzyklen
> bevor der EEPROM den Geist aufgibt.

Nicht, wenn jedesmal die gesamte Tabelle überschrieben wird. Im 
Gegenteil, da die doppelte Menge an Daten geschrieben wird, geht die 
Lebensdauer runter. Dazu kommt, dass das Lesen über die Routinen 
schnarchlangsam ist - nicht gerade das, was man bei ner Lookup-Tabelle 
haben möchte.

> Ich schaue mal noch wo genau die Routinen angepasst werden müssen,

Ob sich das wirklich lohnt? Ich fand die Routinen qualitativ nicht 
gerade berauschend ...

von Bert S. (kautschuck)


Lesenswert?

foobar schrieb:
> Nicht, wenn jedesmal die gesamte Tabelle überschrieben wird. Im
> Gegenteil, da die doppelte Menge an Daten geschrieben wird, geht die
> Lebensdauer runter. Dazu kommt, dass das Lesen über die Routinen
> schnarchlangsam ist - nicht gerade das, was man bei ner Lookup-Tabelle
> haben möchte.

Du meinst wegen der Addresse für jede Variabel, die auch geschrieben 
wird? Das vielleicht schon, aber dafür werden immer andere Addressen 
beschrieben bis die Page voll ist und nicht immer die gleiche, denn die 
Lebensdauer ist ja direkt abhängig von der am meisten beschriebenen 
Addresse.

Die LookUp wird sowiso am Anfang in den SRAM geladen, daher kein Problem 
bezüglich Speed.

Mit 2kByte Page size klappt es übrigens prima mit dem Übertragen von 
einer Page in die nächste. Was ich nicht ganz verstehe ist folgende 
Funktion:
1
static uint16_t EE_VerifyPageFullWriteVariable(uint16_t VirtAddress, uint16_t Data)
2
{
3
  HAL_StatusTypeDef flashstatus = HAL_OK;
4
  uint16_t validpage = PAGE0;
5
  uint32_t address = EEPROM_START_ADDRESS, pageendaddress = EEPROM_START_ADDRESS+PAGE_SIZE;
6
7
  /* Get valid Page for write operation */
8
  validpage = EE_FindValidPage(WRITE_IN_VALID_PAGE);
9
  
10
  /* Check if there is no valid page */
11
  if (validpage == NO_VALID_PAGE)
12
  {
13
    return  NO_VALID_PAGE;
14
  }
15
16
  /* Get the valid Page start address */
17
  address = (uint32_t)(EEPROM_START_ADDRESS + (uint32_t)(validpage * PAGE_SIZE));
18
19
  /* Get the valid Page end address */
20
  pageendaddress = (uint32_t)((EEPROM_START_ADDRESS - 1) + (uint32_t)((validpage + 1) * PAGE_SIZE));
21
22
  /* Check each active page address starting from begining */
23
  while (address < pageendaddress)
24
  {
25
    /* Verify if address and address+2 contents are 0xFFFFFFFF */
26
    if ((*(__IO uint32_t*)address) == 0xFFFFFFFF)
27
    {
28
      /* Set variable data */
29
      flashstatus = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, address, Data);       
30
      /* If program operation was failed, a Flash error code is returned */
31
      if (flashstatus != HAL_OK)
32
      {
33
        return flashstatus;
34
      }
35
      /* Set variable virtual address */
36
      flashstatus = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, address + 2, VirtAddress);       
37
      /* Return program operation status */
38
      return flashstatus;
39
    }
40
    else
41
    {
42
      /* Next address location */
43
      address = address + 4;
44
    }
45
  }
46
47
  /* Return PAGE_FULL in case the valid page is full */
48
  return PAGE_FULL;
49
}

Was genau wird damit gemacht: (*(__IO uint32_t*)address) == 0xFFFFFFFF ?
Die Adresse der Adresse verglichen? Dort scheint irgendwie der Fehler 
verborgen, denn nach 2kByte Page 1 ist die Voll und der Vergleich trifft 
nie mehr zu.

: Bearbeitet durch User
von foobar (Gast)


Lesenswert?

> Was genau wird damit gemacht: (*(__IO uint32_t*)address) == 0xFFFFFFFF?

Die Routine sucht nen leeren Slot und schreibt da das neue 
Adress/Daten-Tupel rein - wenn sie keinen freien Slot findet, gibt sie 
PAGE_FULL zurück.

Der obige Ausdruck liest nen 32-Bit-Wert ab "address" und schaut, ob 
alle Bits gesetzt sind (das Kennzeichen, dass dieser "Slot" 
unbenutzt/frei ist).

von Bert S. (kautschuck)


Lesenswert?

foobar schrieb:
> Die Routine sucht nen leeren Slot und schreibt da das neue
> Adress/Daten-Tupel rein - wenn sie keinen freien Slot findet, gibt sie
> PAGE_FULL zurück.

Hmm ok, danke. Dann muss ich nur noch rausfinden, warum kein Slot mehr 
frei ist.

von Johnny B. (johnnyb)


Lesenswert?

Bert S. schrieb:
> Du meinst wegen der Addresse für jede Variabel, die auch geschrieben
> wird? Das vielleicht schon, aber dafür werden immer andere Addressen
> beschrieben bis die Page voll ist und nicht immer die gleiche, denn die
> Lebensdauer ist ja direkt abhängig von der am meisten beschriebenen
> Addresse.

Nein, das ist falsch.
Dein Feind ist der Löschvorgang und der kann nur komplette Pages löschen 
und nicht Werte an einzelnen Adressen. Das Gute an den STM32 ist, dass 
diese Pages relativ klein sind; also je nach Derivat meistens 1k oder 
2k.
Sprich, Du kannst nur eine komplette Page löschen (alle Bytes auf 0xff 
setzen), aber dann problemlos jederzeit (nach dem unlock) einzelne Bytes 
beschreiben. Wobei beim Beschreiben lediglich Bits von 1 auf 0 gesetzt 
werden können.
Willst Du Bits von 0 auf 1 ändern, dann muss erst wieder die komplette 
Page gelöscht werden, womit alle Bits und Bytes in der Page auf 1 
gesetzt werden.

von Bert S. (kautschuck)


Lesenswert?

Johnny B. schrieb:
> Nein, das ist falsch.
> Dein Feind ist der Löschvorgang und der kann nur komplette Pages löschen
> und nicht Werte an einzelnen Adressen. Das Gute an den STM32 ist, dass
> diese Pages relativ klein sind; also je nach Derivat meistens 1k oder
> 2k.
> Sprich, Du kannst nur eine komplette Page löschen (alle Bytes auf 0xff
> setzen), aber dann problemlos jederzeit (nach dem unlock) einzelne Bytes
> beschreiben. Wobei beim Beschreiben lediglich Bits von 1 auf 0 gesetzt
> werden können.
> Willst Du Bits von 0 auf 1 ändern, dann muss erst wieder die komplette
> Page gelöscht werden, womit alle Bits und Bytes in der Page auf 1
> gesetzt werden.

Ah ok, ja dann mach das auch sinn mit dem (*(__IO uint32_t*)address) == 
0xFFFFFFFF

von Thomas E. (picalic)


Lesenswert?

Bert S. schrieb:
> Edit: Ok habe es hinbekommen, einfach die FLASH_PAGE_SIZE erhöht und nun
> kann ich alles speicher wie gewollt.

Ich würde mal annehmen, daß FLASH_PAGE_SIZE die per Hardware vorgegebene 
Pagegröße des Controllers ist. Die darfst Du sicher nicht einfach auf 
einen anderen Wert setzen!

Du solltest wohl eher Deine logische PAGE_SIZE in eeprom.h als 
mehrfaches der FLASH_PAGE_SIZE definieren. Und dann muss natürlich z.B. 
beim Löschen der logischen Page berücksichtigt werden, daß diese aus 
mehreren Flash-Pages besteht.

: Bearbeitet durch User
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.