Nivelamento de desgaste na EEPROM de um microcontrolador

15

Por exemplo: A folha de dados para ATtiny2313 (assim como a maioria das folhas de dados do Atmel AVR) afirma:

Resistência EEPROM programável no sistema de 128 bytes: 100.000 ciclos de gravação / exclusão

Imagine que um programa requer apenas dois bytes para armazenar alguma configuração, os outros 126 bytes são desperdiçados com eficácia. O que me preocupa é que atualizações regulares dos dois bytes de configuração podem desgastar a EEPROM do dispositivo e torná-lo inútil. Todo o dispositivo se tornaria não confiável, porque em um determinado momento você simplesmente não consegue acompanhar quais bytes na EEPROM não são confiáveis.

Existe uma maneira inteligente de nivelar o desgaste na EEPROM de um microcontrolador quando você usa efetivamente apenas um ou dois bytes dos 128 disponíveis?

jippie
fonte
1
Se os ciclos de gravação de 100k fossem uma restrição, faria sentido usar alguma outra tecnologia? Um mecanismo que incorpora nivelamento internamente ou algo com uma ordem de magnitude ou resistência maior?
Anindo Ghosh
1
@AnindoGhosh Eu só não quero desperdiçar meu pequeno estoque de microcontroladores apenas por ter desgastado a EEPROM devido ao teste de uma prova de conceito. Não quero me preocupar com qual byte tenho usado em um projeto anterior ao reutilizar o controlador. E é bom saber que eu faço o melhor uso possível do hardware disponível.
jippie
1
Talvez dê uma olhada na minha resposta no stackoverflow .
JimmyB
Dê uma olhada na série MSP430 FRAM da TI ... 10 ^ 13 escreve !!!
Geometrikal

Respostas:

19

A técnica que eu normalmente uso é prefixar os dados com um número de seqüência contínua de 4 bytes, em que o maior número representa o valor mais recente / atual. No caso de armazenar 2 bytes de dados reais que dariam 6 bytes no total e, então, formar um arranjo de filas circulares, para 128 bytes de EEPROM, ele conteria 21 entradas e aumentaria a resistência 21 vezes.

Ao inicializar, o maior número de sequência pode ser usado para determinar o próximo número de sequência a ser usado e a cauda atual da fila. O pseudocódigo C a seguir demonstra, isso pressupõe que, após a programação inicial, a área da EEPROM tenha sido apagada para valores de 0xFF, por isso ignoro um número de sequência 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;
}

Para uma EEPROM menor, uma sequência de 3 bytes seria mais eficiente, embora fosse necessário um pouco de divisão de bits em vez de usar tipos de dados padrão.

PeterJ
fonte
+1, boa abordagem. O armazenamento pode ser otimizado um pouco usando menos bytes de "tag" e, possivelmente, dependendo de alguma forma de mecanismo de hash bucket para fornecer distribuição adicional? Um híbrido entre não nivelar e sua abordagem?
Anindo Ghosh
@AnindoGhosh, sim, eu acredito que poderia. Normalmente, eu usei essa abordagem em micros pequenos para simplificar o código e, pessoalmente, principalmente em dispositivos maiores como o DataFLASH. Uma outra idéia simples que vem à mente também é que os números de sequência podem ser periodicamente reduzidos para mantê-los em valores menores.
precisa saber é o seguinte
A nota de aplicação do Atmel mencionada por @ m.Alin possui uma simplificação inteligente: após um RESET, é possível procurar no [...] buffer, localizando o último [...] elemento do buffer alterado, localizando o local onde o A diferença entre um elemento buffer e o próximo elemento buffer é maior que 1 .
jippie
Write_value () não deve colocar a entrada em queue_tail * sizeof (QUEUE_ENTRY)? estarei correto na primeira vez, mas ele não deve continuar avançando se houver várias gravações? i não é incrementado fora de load_queue ().
Marshall Eubanks
2
@ DWORD32: Sim, isso é tecnicamente correto, mas irrelevante na prática. Quando isso acontecer, o limite de desgaste na EEPROM será excedido em um fator de 2000!
Dave Tweed
5

A seguir, é apresentado um método que usa buckets e cerca de um byte overhead por bucket. Os bytes do balde e os bytes gerais recebem aproximadamente a mesma quantidade de desgaste. No exemplo em questão, dados 128 bytes de EEPROM, esse método aloca 42 buckets de 2 bytes e 44 bytes de status, aumentando a capacidade de desgaste em cerca de 42 vezes.

Método:

Divida o espaço de endereço da EEPROM em k buckets, onde k = ⌊ E / ( n +1) ⌋, com n = tamanho da matriz de dados de instalação = tamanho do bucket e E = tamanho da EEPROM (ou, geralmente, o número de EEPROM células a serem dedicadas a essa estrutura de dados).

Inicialize um diretório, uma matriz de m bytes configurada como k , com m = En · k . Quando o dispositivo é inicializado, ele lê o diretório até encontrar a entrada atual, que é um byte diferente de k . [Se todas as entradas do diretório forem iguais a k , inicialize a primeira entrada do diretório como 0 e continue a partir daí.]

Quando a entrada do diretório atual contém j , o bloco j contém dados atuais. Quando você precisar escrever uma nova entrada de dados de configuração, armazene j +1 na entrada de diretório atual; se isso for igual a k , inicialize a próxima entrada de diretório em 0 e continue a partir daí.

Observe que os bytes do diretório têm a mesma quantidade de desgaste que os bytes do bucket, porque 2 · k > mk .

( Adaptei o texto acima da minha resposta à pergunta 34189 do Arduino SE , Como aumentar a vida útil da EEPROM?. )

James Waldby - jwpat7
fonte
2

Eu usei um número de sequência contínuo para isso (semelhante à resposta de Peter). O número de sequência pode realmente ser tão pequeno quanto 1 bit, desde que o número de elementos na sugestão seja ímpar. A cabeça e a cauda são marcadas pelos 2 1 ou 0 consecutivos

Por exemplo, se você quiser rolar por 5 elementos, os números de sequência seriam:

{01010} (escreva para 0) {11010} (escreva para 1) {10010} (escreva para 2) {10110} (escreva para 3) {10100} (escreva para 4) {10101} (escreva para 5)

Mick Clift
fonte
1

Existem algumas opções, dependendo do tipo de EEPROM que você possui e do tamanho dos seus dados.

  1. Se a sua EEPROM tiver páginas apagáveis ​​individualmente e você usar 1 página (ou mais), simplesmente mantenha todas as páginas apagadas, exceto as em uso, e reutilize as páginas de maneira circular.

  2. Se você usar apenas uma fração de uma página que precise ser apagada de uma só vez, particione essa página em entradas de dados. Use uma entrada limpa sempre que estiver escrevendo e apague quando ficar sem entradas limpas.

Use um bit "sujo" para diferenciar entre entradas limpas e sujas, se necessário (normalmente, você tem pelo menos um byte que é garantidamente diferente de 0xFF, que pode ser usado para rastrear entradas sujas).

Se a sua biblioteca EEPROM não expõe a função apagar (como o Arduino), aqui está um truque interessante para o algoritmo # 2: como sua primeira entrada EEPROM sempre é usada, você pode determinar o valor do bit "sujo" lendo-o. Depois que você ficar sem entradas limpas, basta começar novamente da primeira entrada, invertendo o bit "sujo", e o restante de suas entradas automaticamente será marcado como "limpo".

Os números de sequência e os catálogos são um desperdício de espaço, a menos que você queira rastrear páginas ruins ou atualizar partes diferentes dos dados da EEPROM de forma independente.

Dmitry Grigoryev
fonte