Forum: Mikrocontroller und Digitale Elektronik EEPROM Emulieren auf Atmel SAM4E APP Note


von Jannik (Gast)


Angehängte Dateien:

Lesenswert?

Ich versuche mich in der EEPROM Emulation bei einem Atmel SAM4E8C.
Zunächst einmal habe ich die Funktionen aus dem Atmel APP Note AT4066 
übernommen siehe Source unten.
Funktionieren tut es auch auf anhieb, zuerst wird die eep_init() 
aufgerufen, dann kann man ein paar Bytes aus einem Buffer über 
eep_write() in den Flash schreiben und über eep_read() auslesen.
eep_read() liest übrigens nicht immer aus dem flash - nach dem 
eep_init() ist der Flash Inhalt im RAM => eep_buffer Array.

Folgendes Problem:

eep_init();
eep_write(3Byte siehe Abbildung Nr. 1);
eep_write(3Byte siehe Abbildung Nr. 2);
eep_write(3Byte siehe Abbildung Nr. 3);

=> Im Flash sind nur die 3 Bytes vom letzten eep_write passend, die 
anderen Bytes wurden wieder auf 0xff gelöscht.
Das ist asynchron zum eep_buffer, bei dem der Inhalt aller 3 eep_write 
korrekt ist siehe Abbildung.

Frage: Soll das so? Gibt es hier einen Sinn warum nicht immer der ganze 
eep_buffer in den Flash geschrieben wird? Meine Erwartung war, dass der 
eep_buffer synchron zum Flash gehalten wird.
1
static int8_t get_eep_block(uint8_t * data)
2
{
3
  #if(EEPROM_SIZE == 511)
4
  if(data[0] & 0x01)
5
  return 0;
6
  #elif (EEPROM_SIZE == 255)
7
  if(data[0] & 0x01)
8
  return 0;
9
  else if(data[256] & 0x01)
10
  return 1;
11
  #elif((EEPROM_SIZE == 127))
12
  if(data[0] & 0x01)
13
  return 0;
14
  else if(data[128] & 0x01)
15
  return 1;
16
  else if(data[256] & 0x01)
17
  return 2;
18
  else if(data[384] & 0x01)
19
  return 3;
20
  #endif
21
  return -1;
22
  
23
  
24
}
25
26
uint8_t eep_init(void)
27
{
28
  
29
  uint8_t page_walker;
30
  uint8_t page_buffer[PAGE_SIZE];
31
  
32
  for(int i =0; i < EEPROM_SIZE ;i++)
33
  {
34
    eep_buffer[i] = 0xff;
35
  }
36
  
37
  //Find the page and block index for the valid writable page.
38
  for(page_walker = 0; page_walker < NUMBER_PAGES; page_walker++)
39
  {
40
    memcpy(page_buffer, (const void *)(FLASH_START + page_walker*PAGE_SIZE), sizeof(page_buffer));
41
    current_eep_index = get_eep_block(page_buffer);
42
    current_page = page_walker;
43
    if(current_eep_index >= 0)
44
    break;
45
  }
46
  
47
  
48
  //if no page found, then the last page was the valid one. select the last block and put the data into buffer
49
  if(page_walker == NUMBER_PAGES)
50
  {
51
    for(int i  = 0; i < EEPROM_SIZE; i++)
52
    #if(EEPROM_SIZE == 511)
53
    eep_buffer[i] = page_buffer[i + 1];
54
    #elif(EEPROM_SIZE == 255)
55
    eep_buffer[i] = page_buffer[i + 257];
56
    #elif(EEPROM_SIZE == 127)
57
    eep_buffer[i] = page_buffer[i + 385];
58
    #endif
59
    current_eep_index = 0;
60
    current_page = 0;
61
    
62
    //since all pages are dirty, erase before next write to page 0
63
    erase_flag = 1;
64
    
65
  }
66
  // if page was found, move one block back and copy the data
67
  else
68
  {
69
    //First block of a page, means data to be read in the last block of the previous page
70
    if(current_eep_index == 0)
71
    {
72
      memcpy(page_buffer, (const void *)(FLASH_START + ((current_page-1)*PAGE_SIZE)), sizeof(page_buffer));
73
74
      for(int i  = 0; i < EEPROM_SIZE; i++)
75
      #if(EEPROM_SIZE == 511)
76
      eep_buffer[i] = page_buffer[i + 1];
77
      #elif(EEPROM_SIZE == 255)
78
      eep_buffer[i] = page_buffer[i + 257];
79
      #elif(EEPROM_SIZE == 127)
80
      eep_buffer[i] = page_buffer[i + 385];
81
      #endif
82
    }
83
    //if it is within a page, go to previous block and fetch the data
84
    else
85
    {
86
      for(int i  = 0; i < EEPROM_SIZE; i++)
87
      eep_buffer[i] = page_buffer[((current_eep_index-1)*(EEPROM_SIZE+1))+1 + i];
88
    }
89
    
90
  }
91
  
92
  return 1;
93
  
94
}
95
96
uint8_t eep_write(uint8_t* data, uint16_t address, uint16_t size )
97
{
98
  uint32_t flash_start = FLASH_START;
99
  uint8_t page_buffer[PAGE_SIZE];
100
  
101
  for(int i = 0; i < PAGE_SIZE; i++){
102
//     if ()
103
//     page_buffer[i] = eep_buffer[i];
104
    page_buffer[i] = 0xff;
105
  }
106
  
107
  //
108
  
109
  if(size + address > EEPROM_SIZE)
110
  return 0;
111
  
112
  //If we have wrapped around the reserved 16 pages, delete everything
113
  if(current_page == 0 && (erase_flag) && (current_eep_index == 0))
114
  {
115
    //These commands could be different for different controllers.
116
    flash_erase_page(FLASH_START, IFLASH_ERASE_PAGES_8);
117
    flash_erase_page(FLASH_START + (PAGE_SIZE*8), IFLASH_ERASE_PAGES_8);
118
  }
119
  
120
  for(int i = 0; i < size; i++)
121
  {
122
    //clear valid flag
123
    page_buffer[(current_eep_index*(EEPROM_SIZE+1))] = 0x00;
124
    page_buffer[i + address + (current_eep_index*(EEPROM_SIZE+1)) + 1] = data[i];
125
    eep_buffer[i + address] = data[i];
126
  }
127
  flash_write(((FLASH_START + current_page*PAGE_SIZE)), (const void *)page_buffer, sizeof(page_buffer), 0);
128
  
129
  #if(EEPROM_SIZE == 511)
130
  current_page++;
131
  
132
  #elif(EEPROM_SIZE == 255)
133
  current_eep_index++;
134
  if(current_eep_index > 1)
135
  {
136
    current_page++;
137
    current_eep_index = 0;
138
  }
139
  #elif(EEPROM_SIZE == 127)
140
  current_eep_index++;
141
  if(current_eep_index > 3)
142
  {
143
    current_page++;
144
    current_eep_index = 0;
145
  }
146
  #endif
147
  if(current_page > 15)
148
  {
149
    current_page = 0;
150
    erase_flag = 1;
151
    current_eep_index = 0;
152
  }
153
  return 1;
154
}
155
156
uint8_t eep_read(uint8_t* data, uint16_t address, uint16_t size )
157
{
158
  if(size + address > EEPROM_SIZE)
159
  return 0;
160
  for(int i = 0; i < size; i++)
161
  {
162
    data[i] = eep_buffer[address + i];
163
  }
164
  return 1;
165
}

von Jannik (Gast)


Lesenswert?

Ich habs jetzt einfach geändert sodass der alte Inhalt immer wieder 
mitgeschrieben wird. Aber wenn einem was einfällt, warum man soetwas wie 
unten steht gebrauchen kann oder es ggf. mehr Sinn macht lasst es mich 
bitte wissen. Ist für mich Neuland. Vielleicht gibt es hier Vorteile die 
ich nicht sehe.

in der eep_write() habe ich folgendes angepasst:
1
//   for(int i = 0; i < PAGE_SIZE; i++){
2
//     page_buffer[i] = 0xff;
3
//   }
4
  
5
  //Modifiziert, sodass alte Werte, welche bereits über eep_write beschrieben wurden erhalten bleiben
6
  page_buffer[0] = 0xff;
7
  
8
  for(int i = 0; i < PAGE_SIZE; i++){
9
    page_buffer[i+1] = eep_buffer[i];
10
  }
11
  //Modi Ende

Gruß

von Marco H. (damarco)


Lesenswert?

Das Problem besteht darin das du nicht verstanden hast wie der memory 
Controller funktioniert. Dieser ließt bzw. schreibt (ich habe nicht 
nachgeschaut) in einen Cache der z.bsp 64byte groß ist. Nicht nur das, 
du musst deine EEROM Simulation korrekt Initialisieren. So das sich auch 
richtig funktioniert. Der Controller schützt dann diese Bereiche. Man 
kann nicht wild irgend wo in den Flash schreiben, bzw. man sollte es 
nicht.

Hier wird wohl die Page gelöscht und neu geschrieben, hierzu mal in das 
Register schauen mit welchen Mode geschrieben wird. Meist wird die Page 
erst gelöscht und neu beschrieben. Das macht aber keine Routine sondern 
der Controller! Dieser löscht die Page und schreibt den Inhalt vom Cache 
dort hinein.

Damit sind alte Daten weg und das kann man verhindern wenn man den EEROM 
bzw. Flash entsprechend organisiert. So das Inhalte die Unabhängig von 
einander sind erhalten bleiben.

von Adam P. (adamap)


Lesenswert?

Hey Jannik,

ich arbeite auch mit einem SAM4E und habe die Funktionen
- flash_write(...)
- flash_read(...)
- flash_erase_page(...)

für ein Bootloader verwendet, womit ich kaum glauben konnte das es in 
dem Bsp. solche Probleme gibt...

Was mich jedoch überhaupt wundert: Bei dir dürften die Werte NIEMALS 
wirklich im Flash gespeichert werden (habe das Atmel Bsp. mal getestet)
-> es fehlt der flash_init(...) Aufruf!

Klar ist: der Flash Controller lässt es nicht zu einzelne Bytes (weniger 
wie 32 Bit) zu speichern, bzw. ist der Zustand der drumherum liegenden 
dann nicht definiert (siehe Datenblatt).

Auszug aus der flash_efc.c:
1
/* Write page.
2
* Writing 8-bit and 16-bit data is not allowed and may lead to
3
* unpredictable data corruption.
4
*/

Ich würde an deiner Stelle die Idee so aufnehmen, jedoch mir die EEPROM 
Funktionen selbst schreiben:
- Bei init() wird die aktive Page im Flash gesucht und in den globalen 
eeprom_buf kopiert.
- Bei write() kopierst die übergebenen Daten in eeprom_buf und schreibst 
ihn auf die nächste Flash Page.
- Bei read() kannst dann direkt aus eeprom_buf lesen, da dieser dann 
wirklich die Kopie der aktiven Flash Page ist.

Dann ist die Funktion die gleiche wie im Bsp., warum das Bsp. jedoch so 
programmiert wurde und dann noch fehlerhaft ...? Naja, ist nicht das 
erste mal, dass ich Fehler in Bsp. oder ASF von Atmel finde. Da muss 
wohl jeder durch ;-)

Gruß

von Marco H. (damarco)


Lesenswert?

Nunja es geht ja anscheint darum den Inhalt getrennte zu verändern. Ob 
wirklich so sinnvoll ist muss man schauen.. Man könnte zuerst auslesen, 
ändern und wieder rein schreiben.

Oder man ließt nur zum Start aus und arbeitet mit einen copy im Speicher 
weiter. Zu gegeben Anlass, Spannung bricht ein oder Zeit schreibt man 
die Kopie zurück in den Flash.

Das schont den Flash und das schreiben ist wenn man blockierende 
Funktionen benutzt nicht gerade schnell. Es ist ewt. nicht unerheblich 
an welcher Stelle des Programms man in den Flash schreibt. Es lohnt sich 
auch in die Ref. zu schauen um zu verstehen was die Funktionen des ASFs 
eigentlich machen.

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.