Nivellement d'usure sur l'EEPROM d'un microcontrôleur

15

Par exemple: La fiche technique d'ATtiny2313 (comme la plupart des fiches techniques d'Atmel AVR) indique:

Endurance EEPROM programmable dans le système de 128 octets: 100 000 cycles d'écriture / effacement

Imaginez qu'un programme ne nécessite que deux octets pour stocker une configuration, les 126 autres octets sont effectivement gaspillés. Ce qui m'inquiète, c'est que les mises à jour régulières des deux octets de configuration peuvent épuiser l'EEPROM de l'appareil et le rendre inutile. L'ensemble du périphérique deviendrait peu fiable, car à un certain moment, vous ne pouvez tout simplement pas savoir quels octets en EEPROM ne sont pas fiables.

Existe-t-il un moyen intelligent de niveler l'usure sur l'EEPROM d'un microcontrôleur lorsque vous n'utilisez efficacement qu'un ou deux octets sur les 128 disponibles?

jippie
la source
1
Si les cycles d'écriture de 100 000 étaient une contrainte, serait-il judicieux d'utiliser une autre technologie à la place? Soit un mécanisme qui intègre le nivellement en interne, soit quelque chose avec un ordre de grandeur ou une endurance plus grande?
Anindo Ghosh
1
@AnindoGhosh Je ne veux tout simplement pas gaspiller mon petit stock de microcontrôleurs juste à cause de l'usure de l'EEPROM en raison de mes tests de preuve de concept. Je ne veux pas m'inquiéter de l'octet que j'ai utilisé sur un projet précédent lors de la réutilisation du contrôleur. Et ça fait du bien de savoir que je fais un usage optimal du matériel disponible.
jippie
3
Cela pourrait aider: AVR101: Stockage EEPROM haute endurance
m.Alin
1
Peut-être jetez un œil à ma réponse sur stackoverflow .
JimmyB
Jetez un œil à la série FRSP MSP430 de TI ... 10 ^ 13 écrit !!!
geometrikal

Réponses:

19

La technique que j'utilise normalement consiste à préfixer les données avec un numéro de séquence à 4 octets où le plus grand nombre représente la dernière valeur / valeur actuelle. Dans le cas du stockage de 2 octets de données réelles qui donneraient 6 octets au total, puis je forme un arrangement de file d'attente circulaire, donc pour 128 octets d'EEPROM, il contiendrait 21 entrées et augmenterait l'endurance 21 fois.

Ensuite, lors du démarrage, le plus grand numéro de séquence peut être utilisé pour déterminer à la fois le prochain numéro de séquence à utiliser et la queue actuelle de la file d'attente. Le pseudo-code C suivant montre, cela suppose que lors de la programmation initiale, la zone EEPROM a été effacée aux valeurs de 0xFF, donc j'ignore un numéro de séquence de 0xFFFF:

struct
{
  uint32_t sequence_no;
  uint16_t my_data;
} QUEUE_ENTRY;

#define EEPROM_SIZE 128
#define QUEUE_ENTRIES (EEPROM_SIZE / sizeof(QUEUE_ENTRY))

uint32_t last_sequence_no;
uint8_t queue_tail;
uint16_t current_value;

// Called at startup
void load_queue()
{
  int i;

  last_sequence_no = 0;
  queue_tail = 0;
  current_value = 0;
  for (i=0; i < QUEUE_ENTRIES; i++)
  {
    // Following assumes you've written a function where the parameters
    // are address, pointer to data, bytes to read
    read_EEPROM(i * sizeof(QUEUE_ENTRY), &QUEUE_ENTRY, sizeof(QUEUE_ENTRY));
    if ((QUEUE_ENTRY.sequence_no > last_sequence_no) && (QUEUE_ENTRY.sequence_no != 0xFFFF))
    {
      queue_tail = i;
      last_sequence_no = QUEUE_ENTRY.sequence_no;
      current_value = QUEUE_ENTRY.my_data;
    }
  }
}

void write_value(uint16_t v)
{
  queue_tail++;
  if (queue_tail >= QUEUE_ENTRIES)
    queue_tail = 0;
  last_sequence_no++;
  QUEUE_ENTRY.sequence_no = last_sequence_no;
  QUEUE_ENTRY.my_data = v;
  // Following assumes you've written a function where the parameters
  // are address, pointer to data, bytes to write
  write_EEPROM(queue_tail * sizeof(QUEUE_ENTRY), &QUEUE_ENTRY, sizeof(QUEUE_ENTRY));
  current_value = v;
}

Pour une EEPROM plus petite, une séquence de 3 octets serait plus efficace, mais nécessiterait un découpage en bits au lieu d'utiliser des types de données standard.

PeterJ
la source
+1, Belle approche. Le stockage peut-il être optimisé un peu en utilisant moins d'octets de «balises» et éventuellement en fonction d'une certaine forme de mécanisme de compartiment de hachage pour fournir une distribution supplémentaire? Un hybride entre aucun nivellement et votre approche?
Anindo Ghosh
@AnindoGhosh, oui je crois que oui. J'ai normalement utilisé cette approche sur de petits micros pour la simplicité du code et personnellement, je l'ai principalement utilisée sur des appareils plus grands comme DataFLASH. Une autre idée simple qui vient à l'esprit est également que les numéros de séquence pourraient être périodiquement abaissés pour les garder à des valeurs plus petites.
PeterJ
La note d'application Atmel mentionnée par @ m.Alin a une simplification intelligente: après un RESET, il est alors possible de parcourir le [...] tampon, en trouvant le dernier élément [...] du tampon modifié en trouvant l'emplacement où le la différence entre un élément tampon et l'élément tampon suivant est supérieure à 1 .
jippie
Write_value () ne devrait-il pas mettre l'entrée à queue_tail * sizeof (QUEUE_ENTRY)? je serai correct la première fois, mais ne devrait-il pas continuer à avancer s'il y a plusieurs écritures? i n'est pas incrémenté en dehors de load_queue ().
Marshall Eubanks
2
@ DWORD32: Oui, c'est techniquement correct, mais hors de propos dans la pratique. Au moment où cela se produit, la limite d'usure de l'EEPROM aura été dépassée d'un facteur 2000!
Dave Tweed
5

Voici une méthode qui utilise des compartiments et environ un octet de surcharge par compartiment. Les octets de compartiment et les octets de surcharge obtiennent à peu près la même quantité d'usure. Dans l'exemple présenté, compte tenu de 128 octets EEPROM, cette méthode alloue 42 compartiments de 2 octets et 44 octets d'état, augmentant ainsi la capacité d'usure d'environ 42 fois.

Méthode:

Divisez l'espace d'adressage EEPROM en k compartiments , où k = ⌊ E / ( n +1) ⌋, avec n = taille du tableau de données de configuration = taille du compartiment et E = taille EEPROM (ou, plus généralement, le nombre d'EEPROM cellules à consacrer à cette structure de données).

Initialisez un répertoire, un tableau de m octets tous définis sur k , avec m = En · k . Lorsque votre appareil démarre, il lit le répertoire jusqu'à ce qu'il trouve l'entrée actuelle, qui est un octet différent de k . [Si toutes les entrées du répertoire sont égales à k , initialisez la première entrée du répertoire à 0 et continuez à partir de là.]

Lorsque l'entrée de répertoire actuelle contient j , le compartiment j contient les données actuelles. Lorsque vous devez écrire une nouvelle entrée de données de configuration, vous stockez j +1 dans l'entrée de répertoire actuelle; si cela le rend égal à k , initialisez l'entrée de répertoire suivante à 0 et continuez à partir de là.

Notez que les octets de répertoire obtiennent à peu près la même quantité d'usure que les octets de godet car 2 · k > mk .

(J'ai adapté ce qui précède de ma réponse à la question 34189 d'Arduino SE , Comment augmenter la durée de vie de l'EEPROM?. )

James Waldby - jwpat7
la source
2

J'ai utilisé un numéro de séquence glissant pour cela (similaire à la réponse de Peter). Le numéro de séquence peut en fait être aussi petit que 1 bit, à condition que le nombre d'éléments dans le repère soit impair. La tête et la queue sont alors marquées par les 2 1 ou 0 consécutifs

Par exemple, si vous souhaitez parcourir 5 éléments, les numéros de séquence seraient:

{01010} (écrire à 0) {11010} (écrire à 1) {10010} (écrire à 2) {10110} (écrire à 3) {10100} (écrire à 4) {10101} (écrire à 5)

Mick Clift
la source
1

Il existe plusieurs options selon le type d'EEPROM dont vous disposez et la taille de vos données.

  1. Si votre EEPROM a des pages effaçables individuellement et que vous utilisez 1 page (ou plus), conservez simplement toutes les pages effacées à l'exception de celles en cours d'utilisation et réutilisez les pages de manière circulaire.

  2. Si vous n'utilisez qu'une fraction d'une page qui doit être effacée à la fois, partitionnez cette page en entrées de données. Utilisez une entrée propre à chaque fois que vous écrivez et effacez une fois que vous n'avez plus d'entrées propres.

Utilisez un bit "sale" pour faire la différence entre les entrées propres et sales si nécessaire (généralement, vous avez au moins un octet qui est garanti différent de 0xFF, qui peut être utilisé pour suivre les entrées sales).

Si votre bibliothèque EEPROM n'expose pas la fonction d'effacement (comme Arduino), voici une astuce intéressante pour l'algorithme n ° 2: puisque votre première entrée EEPROM est toujours utilisée, vous pouvez déterminer la valeur du bit "sale" en la lisant. Ensuite, une fois que vous n'avez plus d'entrées propres, vous recommencez simplement à partir de la première entrée, en inversant le bit "sale", et le reste de vos entrées devient automatiquement marqué comme "propre".

Les numéros de séquence et les catalogues sont une perte d'espace, sauf si vous voulez pouvoir suivre les mauvaises pages ou mettre à jour indépendamment différentes parties de vos données EEPROM.

Dmitry Grigoryev
la source