Forum: Mikrocontroller und Digitale Elektronik Daten ins Flash speichern mit dem STM32


von Ma B. (drumstick)


Lesenswert?

Hallo!

Ich suche ein einfaches Beispiel, wo Daten ins Flash gespeichert 
werden!?

Ich arbeite mit dem STM32F103VDT6 und dem uVision von Keil. Unser Motor 
EC45 Flat 36V / 50W fährt einfach von A nach B mit einer Last von 180Kg 
an einem Schneckengetriebe. Bei der Ersten Fahrt wird der Weg gemessen. 
Dies soll bei Stromausfall erhalten bleiben.

Leider habe ich noch nie mit dem Flash gearbeitet. Bin die Datenblätter 
am Durchlesen. Aber ein kleines Beispiel würde mir sicher sehr helfen.

Auf der ST Site gibts eins, aber nicht zu diesem uP!

Danke und Gruss!

M.B.

von AVerr (Gast)


Lesenswert?

Erstmal musst du im Linker File eine neue Section anlegen, die du für 
die Nutzdaten verwendest ( ich denke eine Memory Page wird dafür locker 
reichen ).
Die Variable wird in diese Section gepackt und kann auch schon 
problemlos gelesen werden.
Um sie zu beschreiben, musst du
1) Flash unlocken
2) Die ganze Page löschen
3) Die Variable beschreiben
4) Flash wieder locken
(für jeden diese Punkte gibt es im FLASH teil der Std Lib Funktionen)
So hat es bei mir zumindest unter CooCox schonmal funktioniert.

von Ma B. (drumstick)


Lesenswert?

Hallo AVerr!

Danke für deine Hilfe! Ich habe jetzt ein Beispiel gefunden fürs EVAL 
Board MCBSTM32C. Ich besitze dieses Board. Ich schau mir auch die Lib 
an. Aber das mit den Sections habe ich noch nicht begriffen, wo/wie ich 
dies tun muss!?

Danke und  gruss!

M.B.

von AVerr (Gast)


Lesenswert?

Da man den Flash nur seitenweise beschreiben kann, muss man eine 
komplette memory page für die Nutzdaten reservieren.
Die Größe und Adresse der pages entnimmst du dem Reference Manual.
In meinem Fall ( STM32F103C8 ) waren es 64 pages a 1K, ab 0x08000000.
D.h. die letzte Page ( die ich für Daten benutze ) fängt bei 0x0800FC00 
an.

Das Linker Script besteht aus 2 Teilen ( meist in den Dateien memory.ld 
und/oder link.ld ... je nach Entwicklungsumgebung ):
1) MEMORY Definition:
1
MEMORY
2
{
3
  rom (rx)  : ORIGIN = 0x08000000, LENGTH = 0x0000FC00
4
  rom1 (rx)  : ORIGIN = 0x0800FC00, LENGTH = 0x00000400
5
  ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00005000
6
}
Hier wird das vorhandene Memory festgelegt.
rom ist in diesem Fall das restliche Flash ( Pages 1-63 ) und rom1 die 
letzte memory page, die für die Daten verwendet wird.

2) Section Definition:
1
SECTIONS 
2
{
3
    .mydata :
4
    {
5
        *(.mydata)
6
    } > rom1
7
[...]
8
}
Hier wird eine Section namens .mydata definiert, sodass alle Variablen, 
welche in diese Section wandern im Memory Bereich rom1 landen.

Zu guter letzt wird die Variable in die section .mydata gepackt:
1
__attribute__ ((section(".mydata")))
2
volatile uint8_t meineDaten = 0;

von Ma B. (drumstick)


Angehängte Dateien:

Lesenswert?

Ich habe mein gefundenes Beispiel angehängt! Darun sehe ich niergends 
ein Hinweis, dass eine Section ausgewählt wurde! Ich bin kein 
Schnelldenker, das kapier ich noch nicht! bei uVision kann ich dies doch 
Target options --> Linker einstellen oder? Auch sehe ich im Beispiel 
nicht, welche Daten abgespeichert werden.

Sorry und danke!

von Gebhard R. (Firma: Raich Gerätebau & Entwicklung) (geb)


Lesenswert?

>Darun sehe ich niergends ein Hinweis, dass eine Section ausgewählt wurde!
Unbedigt brauchst du das nicht. Nur dann, wenn dein Programm annähhernd 
das ganze Flash aufbraucht. Es ist nur wichtig, dass sich deine Flash 
Daten nicht mit dem Programm überschneiden.Wenn du die Pages ganz am 
Ende des Flash für deine Daten nutzt, passiert das sowieso nicht. Der 
Linker bewirtschaftet (defaultmässig) das Flash von 0 an.

Grüsse

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Wenn du den Flash mehr oder weniger als EEPROM Ersatz benutzen willst, 
schau dir auch mal die EEPROM Emulationsbeispiele bei ST in der Sotware 
Library an. Hab ich in meinen Motorkontroll Projekten auch schon 
erfolgreich benutzt.
Schreiben des Flash geht natürlich nicht so oft wie ein richtiges 
EEPROM, du solltest also haushalten oder evtl. doch ein externes EEPROM 
in Betracht ziehen, wenn die Schreibvorgänge häufig stattfinden.

von M. B. (Gast)


Lesenswert?

ok, danke für den Tip! Ich hoffe ich kapier es bald :-)

Das Ziel ist, dass einmal am Tag die Zurückgelegte Strecke abgespeichert 
wird und mittels Service -Kit per CAN ausgelesen werden kann.

Also Ich muss immer

das Flash unlocken,
die page löschen,
die Variable beschreiben,
das flash locken.

Der Linker setzt die Variable dann automatisch an nullter Stelle im 
Flash, so muss ich also auch nur die page 0 löschen?

Gibt es eine Kontrollmöglichkeit, um sicher zu sein, dass es geklappt 
hat?
Gewisse teilen ja den Flashbereich und speichern alles doppelt?

Danke und Gruss!

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Mit der o.a. EEPROM Emulation sieht das in der EEPROM Schreiberoutine 
dann so aus(aus einem PMSM Motorcontroller):
1
#include "eeprom.h"
2
// write values to EEPROM
3
void EEPROMWrite(void)
4
{
5
uint16_t ErrValue = 0;
6
7
  /* Unlock the Flash Program Erase controller */
8
  FLASH_Unlock();
9
  /* EEPROM Init */
10
  ErrValue = EE_Init();
11
// now write our bytes
12
  ErrValue = EE_WriteVariable(ee_DEAD_TIME_HALF,DEAD_TIME_HALF);
13
  ErrValue = EE_WriteVariable(ee_motor_fwd_commutation,motor_fwd_commutation);
14
  ErrValue = EE_WriteVariable(ee_motor_rev_commutation,motor_rev_commutation);
15
// finally write our checksum
16
  ErrValue = EE_WriteVariable(ee_checksum,CalcChecksum());
17
}
ErrValue kannst du noch untersuchen und bei <> 0 als Fehler auswerten.

von AVerr (Gast)


Lesenswert?

M. B. schrieb:
> Der Linker setzt die Variable dann automatisch an nullter Stelle im
> Flash, so muss ich also auch nur die page 0 löschen?

Der Linker setzt sie nicht an Stelle 0, dort liegt der Einstiegspunkt in 
dein Hauptprogramm. Vom Löschen von Page 0 ist DRINGEND abzuraten.
Es stimmt, man kann auch ohne Sections arbeiten, allerdings besteht dann 
halt die Gefahr, dass auf der Page, auf der man arbeitet, weitere Daten 
abgelegt werden. Aber wie Gebhard Raich (geb) gesagt hat, ist das eher 
unwahrscheinlich.
Die saubere Lösung wäre natürlich, alles fein säuberlich in Sections 
aufzuteilen, damit nichts passieren kann. Für den Testbetrieb kann man 
das aber auch weglassen.

> Gibt es eine Kontrollmöglichkeit, um sicher zu sein, dass es geklappt
> hat?
Naja, wenn es funktioniert, weisst du es ;)
Du kannst dir die map-File ansehen, die der Compiler erstellt. Darin 
stehen die Adressen aller deiner Variablen nachdem der Linker sie 
verteilt hat.

Als erstes solltest du rausfinden wie viele pages dein Controller hat 
und wie groß diese sind.
Dann suchst du die Adresse der letzten page raus und probierst 
testweise, dort daten abzulegen und auszulesen. Wenn das klappt, kannst 
du dich immer noch um das Sectioning kümmern wenn du willst.

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Schaue Dir auch den "Backup RAM" einmal näher an. Per Batterie kann man 
dort 42 Worte für 10 Jahre speichern.

Der STM32F4xx hat sogar 4KB Backup-RAM und kann wie ein gewöhnlicher RAM 
genutzt werden.

Wenn man die Batterie heraus nimmt, sind natürlich die Daten auch weg.
Mit parallel 100µF kann man aber schon einige Sekunden für den 
Batteriewechsel überbrücken.

von AVerr (Gast)


Lesenswert?

Hier auch mal ein einfaches, aber funktionierendes Beispiel:
1
uint32_t data = 0xFFCCAA99;
2
3
...
4
5
  FLASH_Unlock();
6
  FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
7
  FLASH_ErasePage(0x0800FC00);
8
  FLASH_ProgramWord(0x0800FC00, data);
9
  FLASH_Lock();
Hier wird die Seite beginnend bei 0x0800FC00 gelöscht und an der Stelle 
0x0800FC00 werden 4 byte geschrieben ( der Inhalt der Variable data ).
Man könnte nun mittels eines Zeigers darauf zugreifen:
1
uint32_t *pData = 0x0800FC00;
2
3
printf("%d", *pData);

von Ma B. (drumstick)


Lesenswert?

Morgen!

Der STM32F103VDT6 hat im Block Main memory 256 Pages a 2Kbytes (0 - 
255). Base Addresse 0x0807 F800 - 0x0807 FFFF.
1
void set_in_Flash(int data)
2
{
3
  FLASH_Unlock();
4
  FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR |   FLASH_FLAG_WRPRTERR);
5
  FLASH_ErasePage(0x0807F800);
6
  FLASH_ProgramWord(0x0807F800, data);
7
  FLASH_Lock();
8
}

jetzt habe ich alle Daten, die ins Flash gehören in einn Struct gefasst.
1
typedef struct  {
2
                  float motspd_max;     // [mm/s] normal full travelling speed
3
                  float motspd_cal_max; // [mm/s] Reference speed value 
4
                  float motspd_inc;     // [mm/s] Speed added when ramping up
5
                  float motspd_dec;     // [mm/s] Speed added when ramping down
6
  
7
                  tMotorCurrentSample MotI_TableCW[TRAV_MAXDIST_SAMPLES];  // = 2100
8
                  tMotorCurrentSample MotI_TableCCW[TRAV_MAXDIST_SAMPLES];
9
                  BOOL                cal_OK;
10
                  int                 MotDistCal_PulsesMax;
11
                  float               MotDistCal_pmm;
12
                  tPIDparameter       PIDpar;
13
                  int                 ShttrueIdx[CNTSHT];
14
                  int                 Nmbr_of_Shttr;
15
                }
16
                tSetup;

Ich muss jetzt aber alle Variablen einzeln ins flash laden und jede 
Variable kommt in eine neue Page, hab ich das richtig verstanden?

Was hat es eigentlich mit dem Information Block auf sich. Ich benötige 
ja nur das Main Memory!

Vielen Dank und Gruss!

M.B.

von Ma B. (drumstick)


Lesenswert?

Mit dem Debugger habe ich das Programm laufen gelassen.
Und jetzt schaue ich im Memory an der Adresse 0x0807F800: Wert: 0B000000

Die Variable, die ich abgespeichert habe oder wollte hat den Wert 2!?

Gruss!

von Ma B. (drumstick)


Lesenswert?

Mein Fehler!! Die Variable hat den Wert 11. Also hat es geklappt!!

So jetzt muss ich noch aus dem Flash lesen bzw. laden.

Vielen Dank und freundliche Grüsse!

M.B.

von Lutz (Gast)


Lesenswert?

M. B. schrieb:
> jetzt habe ich alle Daten, die ins Flash gehören in einn Struct gefasst.

> Ich muss jetzt aber alle Variablen einzeln ins flash laden und jede
> Variable kommt in eine neue Page, hab ich das richtig verstanden?

Ich habe es anders verstanden (FLASH_ErasePage z.B. habe ich im zip aber 
auch nicht gefunden).
Statt FLASH_ProgramWord kann man ja bestimmt auch eine ganze page 
schreiben. Oder in einer Schleife ein word nach dem anderen. 
Wahrscheinlich gibt es auch eine Funktion, mit der man einen Block mit 
variabler Größe schreiben kann. Oder schreibt sich die, wie gerade 
beschrieben, mit einer Schleife selbst.

von Ma B. (drumstick)


Lesenswert?

ja, das Beispiel im Zip gab nicht so viel her, habe die StandLib 
hinzugeführt. Da finde ich aber auch nur FLASH_ErasePage und kein 
anderer Löschbefehl. Zum Daten aus dem Flash laden, gibts da keinen Read 
Befehl?

Danke und Gruss!
M.B.

von AVerr (Gast)


Lesenswert?

Lutz schrieb:
> Oder in einer Schleife ein word nach dem anderen.

Genau so wird das gemacht.
Dein Struct schreibst du Word für Word in diese eine Page, hast ja 2KB 
Platz.
Ansonsten wäre das ja ein ziemlich großer Flashverbrauch.

Die Lib hat nur Funktionen zum schreiben von Word und HalfWord.

von AVerr (Gast)


Lesenswert?

M. B. schrieb:
> Zum Daten aus dem Flash laden, gibts da keinen Read
> Befehl?

Nein, weil es ja mittels Pointern ziemlich einfach ist ( siehe oben ).
Wenn du eine Linker Section für die Variable hinbekommst, kannst du sie 
sogar ganz regulär lesen.

von Ma B. (drumstick)


Lesenswert?

jetzt mal ne andere Frage: Im Datenblatt steht, dass der STM32F103VDT6 
384kByte Flash zur Verfügung hat. Wenn ich die Pages 0 - 255 a 2kB 
zusammenzähle ergibt dies ja 512kB und das nur das Main Memory.? Was 
Stimmt jetzt?

Vielen Dank für Eure Hilfe!

von AVerr (Gast)


Lesenswert?

Im Datenblatt steht wahrscheinlich "up to 255 Pages" ...
Also hast du nur 192 Seiten a 2kB, was dann deine 384kB ergibt.

von Ma B. (drumstick)


Lesenswert?

Ah Ok! Aber die Startadresse der Page stimmte trotzdem, sie ist einfach 
die Addresse der page 192!?

von AVerr (Gast)


Lesenswert?

Die Adresse 0x0807F800 wäre die Startadresse der page 255.
Weil dein Controller aber nur 192 pages hat, ist die Adresse der letzten 
Seite 0x0805F800 ( = 0x08000000 + 191 * 2048 ).

von Ma B. (drumstick)


Lesenswert?

Habe es im Datenblatt gefunden "up to..". Verstehe ich aber nicht, 
sorry!


2.3.3 Embedded Flash memory

The high-performance Flash memory module has the following key features:
● For XL-density devices: density of up to 1 Mbyte with dual bank 
architecture for readwhile-
write (RWW) capability:
– bank 1: fixed size of 512 Kbytes
– bank 2: up to 512 Kbytes


● For other devices: density of up to 512 Kbytes


● Memory organization: the Flash memory is organized as a main block and 
an
information block:

– Main memory block of size:
up to 128 Kbytes × 64 bits divided into 512 pages of 2 Kbytes each (see 
Table 6)
for XL-density devices

up to 4 Kb × 64 bits divided into 32 pages of 1 Kbyte each for 
low-density devices
(see Table 2)

up to 16 Kb × 64 bits divided into 128 pages of 1 Kbyte each for 
medium-density
devices (see Table 3)

up to 64 Kb × 64 bits divided into 256 pages of 2 Kbytes each (see Table 
4) for high-density devices

up to 32 Kbit × 64 bits divided into 128 pages of 2 Kbytes each (see 
Table 5) for connectivity line devices



Wenn ich mir diesen Ausschnitt aus dem Datenblatt ansehe, so würde ich 
doch annehmen, dass ich 256 Pages habe --> high-density device

Liege ich da falsch oder lese ich das Datenblatt falsch? 192 Pages wäre 
schon logischer!



danke für die Formel!

von AVerr (Gast)


Lesenswert?

Also, dein Controller ist laut Datenblatt aus der High-density 
performance line.
Von dem gibt es mehrere Versionen, z.B. deine STM32F103VDT6 mit 384 kB 
Flash oder auch STM32F103VET6 mit 512 kB Flash ( nur das E ist hier in 
der Bezeichnung anders ).
Nun könnten ST zu jeder dieser Versionen eine eigene Tabelle machen, das 
wäre aber zu viel Arbeit.
Daher geht man im Reference Manual von der Vollbestückung ( in diesem 
Fall 512 kB ) aus, für alle anderen Modelle muss man ein wenig rechnen.

In Abschnitt 3.3.3 vom Reference Manual steht:
1
Memory organization: the Flash memory is organized as a main block and an 
2
information block:
3
[...]
4
up to 64 Kb × 64 bits divided into 256 pages of 2 Kbytes each (see Table 6) for 
5
high-density devices
6
[...]
Also wieder das "up to".
Da dies 512 kB Flash entsprechen würde, musst du halt deine 192 Pages a 
2 kB selbst ausrechnen und kommst wie oben erklärt zur Adresse der 
letzten page.
Die wichtigsten Informationen hier sind nur die page size und der Start 
( 0x08000000 ). Den Rest kann man dann selbst berechnen.

von Ma B. (drumstick)


Lesenswert?

Super danke, jetzt hats geklappt! Musste sicher sein, dass...
>> Also, dein Controller ist laut Datenblatt aus der High-density
>>performance line.

...ist, aber trotzdem nur 192 Pages hat.

Vielen Dank für Deine Geduld und Hilfe!

Gruss

M.B.

von Ma B. (drumstick)


Lesenswert?

Hallo

AVerr schrieb:

>Man könnte nun mittels eines Zeigers darauf zugreifen:
1
uint32_t *pData = 0x0800FC00;
2
3
printf("%d", *pData);


Ich habe jetzt einen struct ins flash gespeichert.
1
typedef struct  {
2
                  BOOL                cal_OK;
3
                  int                 ShttrueIdx[CNTSHT];
4
                  int                 Nmbr_of_Shttr;
5
                  int                 MotDir;
6
                }
7
                tSetup2;

1
extern tSetup2     Setup2;

1
void set_in_Flash(tSetup2 *setup)
2
{
3
  
4
  uint32_t *pData = (uint32_t *)setup;
5
  uint8_t i;
6
  int x = (sizeof(tSetup2) / sizeof(int));
7
   
8
  FLASH_Unlock();
9
  FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
10
  FLASH_ErasePage(FLSHBSEADRPG192);
11
 
12
  for(i=0;i<x;i++)
13
  {
14
    FLASH_ProgramWord(FLSHBSEADRPG192, pData[i]);
15
  }
16
  FLASH_Lock();
17
  
18
}


Jetzt interessiert mich nur die Variable "cal_OK". Wie kann ich jetzt 
mittels Pointer auf die einzelne Variable im struct im Flash 
draufzeigen? Das habe ich noch nicht ganz geschnallt!

Dein Beispiel oben greift doch auf die ganze Page des flash zu. oder?

Danke und Gruss!

M.B.

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Ich glaube das Lesen geht ganz einfach...
1
extern tSetup2     Setup2;
2
3
: :
4
5
Setup2 = *((tSetup2*)FLSHBSEADRPG192);

von Ma B. (drumstick)


Lesenswert?

Vielen dank für die Hilfe!

Mich interessiert eine einzelne Variable, kann ich auf eine einzelne 
Variable zugreifen?

von Ma B. (drumstick)


Lesenswert?

Ah, nach dem Lesen kann ich normal auf einzelne Variablen zugreifen, 
super danke viel mal!!

Grüsse

M.B.

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

xxx = ((tSetup2*)FLSHBSEADRPG192)->cal_OK;

von Roland H. (batchman)


Lesenswert?

Diese Berechnung:

>   int x = (sizeof(tSetup2) / sizeof(int));

und diese Schleife

>   for(i=0;i<x;i++)
>   {
>     FLASH_ProgramWord(FLSHBSEADRPG192, pData[i]);
>   }

können funktionieren.

Zwischen dem BOOL und dem ersten int ist vermutlich ein Loch von 3 Byte.

Wenn dies auf eine andere Architektur portiert würde oder jemand im 
Makefile global -fpack-struct einstellt oder dem struct ein 
_attribute_ ((packed)) hinzufügt, dann fliegt es Dir vermutlich um die 
Ohren.

Eine korrekte Serialisierung im Sinne eines definierten "byte streams" 
ist das nicht.

von Ma B. (drumstick)


Lesenswert?

Es funktioniert nicht immer die erste Variable BOOl erfasst er fast 
immer, die restlichen bytes sind immer null:

0x0805F800: 01 00 00 00 FF FF FF FF FF FF FF FF ect.

Obwohl die Variablen im Struct Setup2 mit Werten gefüllt wurden.

Was könnte das jetzt sein.

Roland H. schrieb

> Eine korrekte Serialisierung im Sinne eines definierten "byte streams"
>ist das nicht.

von AVerr (Gast)


Lesenswert?

M. B. schrieb:
> void set_in_Flash(tSetup2 *setup)
> {
>
>   uint32_t *pData = (uint32_t *)setup;
>   uint8_t i;
>   int x = (sizeof(tSetup2) / sizeof(int));
>
>   FLASH_Unlock();
>   FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | 
FLASH_FLAG_WRPRTERR);
>   FLASH_ErasePage(FLSHBSEADRPG192);
>
>   for(i=0;i<x;i++)
>   {
>     FLASH_ProgramWord(FLSHBSEADRPG192, pData[i]);
>   }
>   FLASH_Lock();
>
> }

Kein wunder, du schreibst alle deine Daten an die selbe Adresse.
1
FLASH_ProgramWord(FLSHBSEADRPG192 + i*sizeof(uint32_t), pData[i]);

von Lutz (Gast)


Lesenswert?

AVerr schrieb:
> Kein wunder, du schreibst alle deine Daten an die selbe Adresse.
> FLASH_ProgramWord(FLSHBSEADRPG192 + i*sizeof(uint32_t), pData[i]);
                                    ^^^^
Sicher?

Frage: Versuchst du mit pData[i] auf ein struct-member zuzugreifen?

von Lutz (Gast)


Lesenswert?

Etwas Ordnung bzw. Weitblick sollte dazu führen, daß du eine globale 
volatil-Variable (man weiß ja nie, was noch dazukommt oder wo man es 
noch nutzt) einführst, die dir die nächste freie Speicherstelle in 
deiner section anzeigt. Sinnvollerweise muß die natürlich selbst in die 
section deines Flash gelegt werden, um auch nach einem reset noch ihre 
Funktion erfüllen zu können; so quasi als Inhaltsverzeichnishilfe.
Da fällt einem bei Nachdenken sicher noch etwas mehr sinnvolle Struktur 
für diese section ein ...

Roland H. schrieb:
> Zwischen dem BOOL und dem ersten int ist vermutlich ein Loch von 3 Byte.
Wenn BOOL hier als uint8_t definiert ist, ja. Also sollte man ... oder 
... .

von Alex K. (shadow851)


Lesenswert?

Hi Leute,

ich beschäftige mich momentan mit exakt dem gleichen Problem, benutze 
aber einen stm32f205vg und muss ca. 1kb Daten speichern. Hardware ist 
vorgegeben, einen eeprom kann ich also nicht hinzufügen.

Meine Frage dreht sich um die Aufteilung des Flashspeichers:
4x16kb
1x64kb
7x128kb

Wenn ich die erste Page verwende würde (afaik) der Startpointer in 
meinen Speicherbereich zeigen anstatt auf den Startupcode. Die letzte 
Page will ich nicht nehmen da mein Code ziemlich viel Platz des Flashs 
belegen wird und mir 128kb Platz für 1kb Daten einfach zu schade ist. Am 
liebsten wäre es mir wenn ich dem Linker mitteilen könnte dass er die 
zweite Section für die Daten nutzen soll und den Code darum herum 
platziert. Leider habe ich keinen Ansatz wie ich dafür das Linkerfile 
modifizieren muss.

Als Linkerfile verwende ich aktuell das File für das Atollic True Studio 
das in der StdPeriphLib von ST 
(http://www.st.com/web/en/catalog/tools/PF257898) enthalten ist.

Vielen Dank für eure Hilfe im Voraus,

viele Grüße
Alex

von ♪Geist (Gast)


Lesenswert?

Was du brauchst, ist die definierte Pageadresse(n). Ich würde immer das 
Ende vom Flash dafür nutzen.

von Alex K. (shadow851)


Lesenswert?

Hi,

wie ich die letzte Page des Flashspeichers nutzen kann ist mir klar, 
mein Problem ist jedoch dass die letzte Page mit 128kB für mein 1kB 
Daten überdimensioniert ist, vor allem in Anbetracht des doch recht 
umfangreichen Codes der noch mit auf den Flash muss. Deshalb würde ich 
gerne eine der kleinsten Pages verwenden, bevorzugt eine der ersten mit 
16kB.
Nur wie bringe ich dem Linker bei dass er für den Programmcode z.B. Page 
1-3&5-12 benutzen soll, also im Prinzip um meine gespeicherten Daten 
herum mappt?

Danke für die Antwort im Voraus,
viele Grüße
Alex

von ♪Geist (Gast)


Lesenswert?

Eine Page ist je nach Device entweder 1KByte oder 2KByte groß. Dem 
linker musst du nicht beibringen, sondern "nur" in deinem Programmcode 
auf die gewünschte Page zugreifen. So in der Art:
1
int i;
2
uint16_t data = 0;
3
FLASH_Unlock();
4
5
// Clear Flags
6
FLASH_ClearFlag(FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);
7
8
FLASH_ErasePage(0x0801F000);
9
10
for (i = 0; i < 512; i++) {
11
 FLASH_ProgramHalfWord(0x0801F000 + (i * 2), (uint16_t)(0x00FF & data));
12
 data++;
13
}

Also recht simpel.

von ♪Geist (Gast)


Lesenswert?

Ach ja und beim Download "Erase affected" Option verwenden, so wird beim 
Programmieren nur der beschriebene Bereich des uC's gelöscht.

von Alex K. (shadow851)


Lesenswert?

Das Problem ist wie beschrieben dass ich glaube das der stm32f205 eben 
keine pagesize von 1-2kB besitzt, sondern die riesigen 128kB im Falle 
des letzten "Sector" (Terminologie im Datenblatt, denke aber das damit 
Pages gemeint sind). In dem Falle kann ich nicht die letzte Page 
verwenden sondern muss eine aus der Mitte herausgreifen und weis nicht 
wie ich dem Linker das bei bringen soll.

Liege ich mit meiner Interpretation richtig?
http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/CD00225773.pdf 
Seite 53

Vielen Dank für die Antworten im Voraus,
viele Grüße
Alex

von Kai (Gast)


Lesenswert?

Ich hänge mich mal hier an.

Das folgende:
1
uint32_t *pData = 0x0800FC00;
2
3
printf("%d", *pData);

funktioniert beim STM32F4 NICHT. Ich bekomme dort völlig irrationale 
Werte.

Gibt es bei der F4 Serie eigene Lesebefehle von ST?

von Artur F. (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe so etwas ähnliches bereits realisiert, da mir das EEPROM 
Emulation Beispiel von ST nicht wirklich gefallen hat. Ich teile es 
gerne. Eine Beschreibung ist nicht dabei und ist nicht ausgiebig auf 
Bugs getestet. Gespeichert wird ein Byte/Word im Flash, um die 
Überschreibzyklen zu reduzieren, also bei einer Page von 1024 Byte 
stehen 512 Bytes zu Verfügung.

von Kai (Gast)


Lesenswert?

Das kann doch nicht funktionieren. Woher weiss der Compiler denn, dass 
ich mit "0x0800FC00" eine Adresse meine? Gibt es keine Spezielle 
Lesefunktion beim STM32F4? Page und solche Dinge gibt es beim F4 nicht.

von Phantomix X. (phantomix)


Lesenswert?

Das Ganze habe ich für ein Firmware-Update auf dem F103 schonmal 
gebraucht, sollte auf dem F4xx aber genauso funktionieren:


Falls du mit dem ARM GCC arbeitest, kannst du im C-Code Variablen in 
Sektionen mappen:


Beispiel:
1
__attribute__((__section__(".software_version")))
2
const volatile uint32_t software_version = SOFTWARE_VERSION;

Damit findet der Linker später die Variable in der entsprechenden 
"Sektion". Jetzt musst du dem Linker nur noch klar machen, wo die 
Sektion liegen soll. Das geht im Memory Mapping / Linker-File:
1
MEMORY
2
{
3
  staticrom_reset (rx) : ORIGIN = 0x08000000, LENGTH = 0x00000040
4
/* ... */
5
  isrvectors (rx) : ORIGIN = 0x08001000, LENGTH = 0x0000EFFD
6
  softwareversion (rx) : ORIGIN = 0x0800110C, LENGTH = 0x00000004
7
  rom (rx)  : ORIGIN = 0x08001110, LENGTH = 0x0000EEF1
8
/* ... */
9
}
10
11
SECTIONS
12
{
13
  .staticrom_reset :
14
  {
15
    KEEP(*(.static_rom_vect .static_rom_vect.*))
16
    *(.static_rom_reset .static_rom_reset.*)
17
  } > staticrom_reset
18
19
/* ... */
20
  .isrvectors :
21
  {
22
    KEEP(*(.isr_vector .isr_vector.*))
23
  } > isrvectors
24
  .softwareversion :
25
  {
26
    *(.software_version .software_version.*)
27
  } > softwareversion
28
29
  .text
30
  {
31
/* ... */
32
  } > rom
33
/* ... */
34
}


Problematisch wird es aber, .text in zwei Sektionen zu teilen. Ich hatte 
alles was vor 0x1000 war in extra Sektionen gepackt und .text erst ab 
0x1110 anfangen lassen.

von Kai (Gast)


Lesenswert?

Ich will aber nicht die Variablen in so eine selbst definiert Sektion 
schreiben, sondern aus beliegen Speicherstellen lesen können, um Z.B 
zeigen zu können, dass mein Flashvorgang geklappt hat. Und um Linkerfile 
schreibt die Keil IDE schon herum. Vielleicht kann ich da in den 
Einstellungen etwas übergeben. Ok, mal sehen...

von Marcus H. (mahpong)


Lesenswert?

@Phantomix:
Vielen Dank für Deinen Post, Du bringst die Lösung für den GCC auf den 
Punkt. Mit einem entsprechenden Linkerscript bekommt man eine 
EEPROM-Emulation auf dem F4 sauber mit jeder beliebigen Codegröße hin:

  sector0  (rx)  : ORIGIN = 0x08000000, LENGTH = 0x00004000
  feeprom  (rx)  : ORIGIN = 0x08004000, LENGTH = 0x00008000
  rom  (rx)  : ORIGIN = 0x0800C000, LENGTH = 0x00074000

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 3x16kB ein EEPROM emulieren. Ist 
bei den F4 meist kein Beinbruch und spart das externe EEPROM.

von Flasher (Gast)


Lesenswert?

Hallo zusammen,

ich beschäftige mich momentan auch damit, Daten während der Laufzeit in 
den Flash-Speicher zu schreiben.

Ich verwende einen STM32F429 mit der Entwicklungsumgebung µVision und 
dem Compiler von Keil.

Ich schreibe Daten aus einem Array ab einer bestimmten Adresse im 
Flash-Speicher.
Ich schreibe die Daten dazu Wort für Wort in den Speicher ohne vorher 
den Speicherbereich zu löschen.
Er beschreibt auch die gewünschten Adressen allerdings sind die Daten im 
Flash verändert gegenüber den Werten im Array.

Beispielsweise soll 0xC8 geschrieben werden und im Flash-Speicher steht 
0xC0 oder 0x48 oder statt eines Wertes 0x1AC2 (Array) steht 0x1A82 im 
Flash-Speicher. Also irgendwie scheinen die Werte leicht verändert zu 
sein.

Folgender Code verwende ich zum Schreiben der Daten:

/* Enable the flash control register access */
FLASH_Unlock();

/* Calculate the end address */
u32l_start_address = u32param_start_adr;
u32l_end_address = u32param_start_adr + u32param_length;

/*Check if the start-address is valid */
if (u32l_start_address >= ADDR_FLASH_START)
  {
  /*Check if the end-address is valid */
  if (u32l_end_address <= ADDR_FLASH_END)
    {
    /* Program the user Flash area word by word */
    while (u32l_start_address < u32l_end_address)
      { /* Write data to flash */
      if (FLASH_ProgramWord(u32l_start_address, *u32param_data) == 
FLASH_COMPLETE)
        { /* Increment address to write */
        u32l_start_address = u32l_start_address + 4;
        /* Increment the address of the data field to save */
        u32param_data++;
        }
      else
        {
        /* Error occurred while writing data in Flash memory, set error 
*/
        u32l_error_code = ERR_WRITING_FLASH;
        }
      }
    }
  else
    {
    /* Illegal end-address, set error*/
    u32l_error_code = ERR_END_ADR_FLASH;
    }
  }
else
  {
  /* Illegal start-address, set error*/
  u32l_error_code = ERR_START_ADR_FLASH;
  }

/* Lock the Flash to disable the flash control register access 
(recommended
   to protect the FLASH memory against possible unwanted operation) */
FLASH_Lock();

Also das Schreiben auf die Adressen klappt, und die Adressen stimmen 
auch, lediglich stimmt der Inhalt nicht.

Ich würde mich über alle Hilfen freuen.

Grüße vom Flasher

von Martin L. (maveric00)


Lesenswert?

Hallo,

Daten in ein nicht gelöschtes Flash schreiben ist wie auf eine schon 
bemalte Tafel zu malen - man kann immer nur weiss hinzufügen, nie jedoch 
schwarz und das dabei herauskommende Bild sieht nicht so aus wie man es 
sich vorstellt.

Flash kann nur mit "0"en beschrieben werden. Das Löschen vorher sorgt 
dafür, dass nur noch "1"en im Flash stehen (die mit "0"en überschrieben 
werden können) und keine "0"en (die nicht mit "1"en überschrieben werden 
können). Schreibst Du z.B. Deine C8 in eine Zelle, in der vorher schon 
F7 stand, wirst Du eine C0 zurücklesen.

Also: Flash vor dem neubeschreiben immer löschen.

Schöne Grüße,
Martin

von Flasher (Gast)


Lesenswert?

Hallo Martin,

vielen Dank für deine Antwort, sowas habe ich mir schon gedacht, 
allerdings ist es blöd einen ganzen Sektor zu löschen, ich würde gerne 
nur Teile eines Sektors löschen.
Doch in der Standardbiblithek habe ich noch keine derartige Funktion 
gefunden.
Den gewünschten Speicherbereich mit Nullen zu beschreiben wird nicht 
gehen oder?

Oder gibt es eine Möglichkeit einen Speicherbereich von  z.B. 100Bytes 
zu löschen?

Grüße

der Flasher

von Sepp (Gast)


Lesenswert?

Flasher schrieb:
> Oder gibt es eine Möglichkeit einen Speicherbereich von  z.B. 100Bytes
> zu löschen?

Nein, nur ganze Pages sind möglich! Warum stört Dich das Löschen??

von RoyalFlush (Gast)


Lesenswert?

Hi Flasher,

Du kannst problemlos jeden "gewünschten Speicherbereich mit Nullen zu 
beschreiben".

Weiterführende Tipps, z.B. wie man auch wieder 1en in die Sektoren 
bekommt, findest Du in den Handbüchern (RM0090 oder auch in AN3969).

Grüße,
 RoyalFlush

von Flasher (Gast)


Lesenswert?

Hallo Sepp,

die Pages sind 128kByte groß (zumindest die im oberen Speicherbereich) 
ich verwende aber für verschiedene Daten verschiedene Bereiche 
(Konfigurationsdaten, Fehler, Logging-Daten und Zeitdaten, etc.) wenn 
ich jetzt in einen Sektor mehrere Daten ablegen will, will ich davor ja 
nicht alle Daten wieder löschen.
Und so viele Datenarten und Code wie ich verwende, gibts nun mal nicht 
genügend Sektoren.

Natürlich kann ich kleinere Sektoren verwenden, aber die kleinsten haben 
nun mal auch min. 16kByte.

Oder gibt es noch ne andere Lösung für meine Daten?

Grüße

der Flasher

von Martin L. (maveric00)


Lesenswert?

Hallo,

Du musst den Flash nicht löschen, um Daten hineinzuschreiben, Du musst 
ihn nur löschen, bevor Du Daten überschreibst. Auf diese Weise 
funktioniert die EEProm-Emulation, die man z.B. in App-Note AN2594 unter

http://www.st.com/st-web-ui/static/active/en/resource/technical/document/application_note/CD00165693.pdf

findet. Mit dieser Methode können viele Variablen in eine Flash-Page 
gelegt und auch überschrieben werden, wobei es nur bei jedem x-ten 
Schreibzugriff zu einem Flash-Erase kommt.

Wenn Du zusätzlich diese Emulation an den Anfang des Flash legst, 
verschwendest Du auch nicht viel Speicher, da hier die Pages 
entsprechend kleiner sind, wie Du schon festgestellt hast. Minimum sind 
2 Pages für 1 bis 8000 (16-Bit-)Variablen.

Schöne Grüße,
Martin

von Flasher (Gast)


Lesenswert?

Hallo Martin,

also soweit alles verstanden, was die Speicherung betrifft.

Jetzt habe ich nur folgendes Problem: Ich möchte die kleinsten Sektoren 
des Flash-Speichers benutzen (Sektor0 bis Sektor3 mit jeweils 16kB und 
Sektor4 mit 64KB)
Das Programm beginnt momentan bei 0x08000000, jetzt habe ich in der 
µVision-Umgebung den Beginn des Programms auf 0x08020000 verschoben, die 
Länge angepasst im Programm habe ich das Headerfile angepasst, in dem 
beispielsweise Definitionen zum Flash-Speicher-Start und zum Ort der 
Vektortabelle angegeben sind ebenfalls auf die neue Adresse des 
Programmcodes angepasst.

Aber jetzt läuft mein Programm gar nicht mehr, wenn ich es wieder 
zurückstelle geht es wieder.

Was muss ich beachten um mein Programm (meine Interrupt-Vektortabelle) 
im Speicher nach hinten in den ersten 128kB-Sektor zu schieben?
Hab ich noch was vergessen, oder geht es ganz anders?

Grüße

der Flasher

von Martin L. (maveric00)


Lesenswert?

Hallo,

die  Vektor-Tabelle muss immer am Anfang liegen (im Sektor 0), Sektor 1 
und 2 werden dann EEProm-Emulation und ab 3 darf dann der Programmcode 
beginnen.

Um diese Aufteilung zu bekommen muss man das Linker-Script wie weiter 
oben in diesem Thread (Post von Marcus H) oder wie unter

https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=%2Fpublic%2FSTe2ecommunities%2Fmcu%2FLists%2Fcortex%5Fmx%5Fstm32%2FHow%20to%20allocate%20variable%20in%20Flash%20to%20desired%20address&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=3878

beschrieben anpassen (dabei aber natürlich Platz für 2 EEProm-Pages 
einplanen).

Schöne Grüße,
Martin

Edith sagt: Hinweis auf 2 Pages hinzugefügt

: Bearbeitet durch User
von Phantomix X. (phantomix)


Lesenswert?

> die  Vektor-Tabelle muss immer am Anfang liegen (im Sektor 0)

Nicht zwingend, man kann jede beliebige Speicherposition für die Tabelle 
benutzen, wichtig ist, dass am Anfang des Flashes steht:
0x08000000 Stack-Size
0x08000004 Reset-Vector

Auf die Weise kann man sich bspw einen eigenen Bootloader bauen, und die 
Ziel-Anwendung bringt ihre Vectortabelle selbst mit.

von Jasson (Gast)


Lesenswert?

Hallo zusammen,

wenn Daten überschrieben werden sollen, die z.B. in der Mitte einer Page 
liegen, davor befindliche Daten aber erhalten bleiben sollen, müssen 
dann alle zu erhaltenden Daten zunächst zwischengespeichert werden und 
nach dem Erase wieder geschrieben werden?

Grüßle

von Marcus H. (Firma: www.harerod.de) (lungfish) Benutzerseite


Lesenswert?

Yep, das ist der Haken an Flash.

von Jasson (Gast)


Lesenswert?

Hm... ich war "ideelich" auf dem Weg da eine Art FAT System anzulegen 
ähnlich wie bei SD Karten, aber da rappelt man sich ja nen Ast mit den 
Lösch und Schreibzugriffen...

Ich tausche die letzten 8 Pages meine Flashes gegen 16kiB internes 
EEPROM - jemand interessiert^^

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Jasson schrieb:
> Ich tausche die letzten 8 Pages meine Flashes gegen 16kiB internes
> EEPROM - jemand interessiert^^

Wenn dir das alles zu heikel ist, nimm doch einen externen EEPROM wie 
z.B. den 24C128 mit 16kB oder den 24C256 mit 32kB. Die halten auch mehr 
Schreibzyklen aus und sind bei Bedarf leicht auszutauschen - oder eben 
wirklich eine SD Karte.

: Bearbeitet durch User
von Stefan (Gast)


Lesenswert?

Sorry für's Ausgraben dieses alten Beitrags, aber es passt hier genau 
dazu, es geht um den Ausschnitt aus dem Linkerscript oben:
1
  rom (rx)  : ORIGIN = 0x08000000, LENGTH = 0x0000FC00
2
  rom1 (rx)  : ORIGIN = 0x0800FC00, LENGTH = 0x00000400
3
  ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00005000

Gibt es eine Möglichkeit eine eigene Section zu definieren, die Mitten 
in einer anderen liegt, so dass die andere Section sich drumherumlegt? 
Gemeint ist das so:
1
  rom (rx)  : ORIGIN = 0x08000000, LENGTH = 0x0000FE00
2
  rom1 (rx)  : ORIGIN = 0x08001000, LENGTH = 0x00000100
3
  ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00005000

Sinn ist dass ich ein paar Variablen an eine feste Adresse im Hexfile 
bekomme, so dass ich "von außen" (etwa in einem Firmwareupdatetool) z.B. 
die Version aus der Datei lesen kann. Wennn man rom1 ans Ende legt, hat 
man immer eine bin-Datei die so groß wie das Flash ist und zwischen der 
Applikation und dem rom1 Bereich ist alles leer.

Stefan

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.