Lavorare con la memoria EEPROM.

EEPROM (Electrically Erasable Programmable Read-Only Memory ), è anche memoria non volatile. Ricordiamo gli altri tipi di memoria Flash e SRAM e le loro capacità di archiviazione dei dati:
Tipo              Leggi dal programma      Scrivi dal programma     Cancella al riavvio
Flash             Sì PROGMEM                   Possibile ma difficile       No
SRAM           Sì                                         Sì                                         Sì
EEPROM      Sì                                         Sì                                         No
La EEPROM è una memoria a cui abbiamo pieno accesso da un programma in esecuzione, ad es. possiamo leggere e scrivere dati lì in fase di esecuzione e questi dati non vengono ripristinati al riavvio di microcontrollore.
  • Memorizzazione delle impostazioni che cambiano "da menu" del dispositivo, senza riprogrammazione.
  • Calibrazione salvataggio dei dati di calibrazione.
  • Utilizzare come memoria SRAM aggiuntiva in caso di carenza.
  • "Black box" - registrazione permanente delle letture dei sensori per un'ulteriore decodifica dei guasti.
  • Acquisizione dello stato del flusso di lavoro per il ripristino da un riavvio improvviso.
Punto importante: la EEPROM ha una risorsa per il numero di celle di riscrittura. Il produttore garantisce 100.000 cicli di scrittura per ogni cella, infatti questo numero dipende dallo specifico chip e dalle condizioni di temperatura, test indipendenti hanno mostrato 3-6 milioni di cicli di scrittura a temperatura ambiente prima che si verifichi il primo errore, ovvero i 100.000 dichiarati vengono presi con un margine molto ampio. Ma c'è una piccola precisazione: con i 100.000 cicli di riscrittura dichiarati, la sicurezza dei dati registrati è garantita per 100 anni ad una temperatura di 24°C, se ne sovrascrivi un milione i dati si deterioreranno più velocemente. Allo stesso tempo, il numero di letture di ciascuna cella è illimitato.
La EEPROM è un'area di memoria costituita da celle elementari della dimensione di un byte (come la SRAM). Il volume della EEPROM è diverso per i diversi modelli di microcontrollore:
  • ATmega328 (Arduino UNO, Nano, Pro Mini): 1 kB.
  • ATmega2560 (Arduino Mega): 4 kB.
  • ATtiny85 (Digispark): 512 B
Il compito principale quando si lavora con la EEPROM è non confondere con gli indirizzi, perché ogni byte ha il proprio indirizzo. Se stai scrivendo dati a due byte, occuperanno due byte e i dati successivi dovranno essere scritti a un indirizzo almeno +2 rispetto a quello precedente, altrimenti "si confonderanno". Consideriamo un esempio di memorizzazione di un insieme di dati di diversi tipi posti in sequenza uno dopo l'altro in memoria (tra parentesi scrivo la dimensione del tipo di dati corrente, di cui aumenterà l'indirizzo per il "blocco" successivo):
  • byte - indirizzo 0 (+1)
  • byte - indirizzo 1 (+1)
  • int - indirizzo 2 (+2) (+4 per esp8266)
  • byte - indirizzo 4 (+1)
  • float - indirizzo 5 (+4)
  • int - indirizzo 9 (+2)
  • ... e così via
Un punto importante: tutte le celle hanno un valore predefinito (per il nuovo chip) di 255.
Velocità di lavoro con EEPROM (il tempo non dipende dalla frequenza dell'orologio di sistema):
  • La scrittura di un byte richiede circa 3,3 ms (millisecondi).
  • La lettura di un byte richiede ~0,4 µs (microsecondi).
Potrebbero verificarsi distorsioni durante la scrittura dei dati nella EEPROM se il VCC (tensione di alimentazione) è troppo basso, si consiglia vivamente di utilizzare un BOD o di monitorare manualmente la tensione prima di scrivere.
Quando si utilizza un generatore di clock interno a 8 MHz, la sua deviazione non deve superare il 10% (7,2-8,8 MHz), altrimenti molto probabilmente la scrittura su EEPROM o FLASH verrà eseguita con errori. Di conseguenza, non è consentito l'overclocking dell'orologio interno durante la scrittura di EEPROM o FLASH.
Per lavorare con EEPROM in ambiente Arduino, abbiamo due librerie, la seconda è un "involucro" più comoda per la prima. Consideriamoli entrambi, perché qualsiasi cosa può essere trovata in uno "schizzo di qualcun altro", e l'uso congiunto di queste due librerie rende incredibilmente conveniente lavorare con EEPROM.
Libreria avr/eeprom.h.
Ho nascosto la descrizione di questa libreria in un'altra pagina, non è molto rilevante e non è necessario saperlo. Inoltre non funziona su esp8266/32 per ovvi motivi. avr/eeprom.h 
Libreria EEPROM.h.
La libreria EEPROM.h viene fornita con il core Arduino ed è la libreria standard. Infatti, EEPROM.h è un comodo involucro per avr/eeprom.h, ampliandone leggermente le capacità e semplificandone l'utilizzo. Un punto importante: includendo EEPROM.h nello sketch, includiamo automaticamente avr/eeprom.h e possiamo usare i suoi funzioni, come EEMEM. Considera gli strumenti che la biblioteca ci offre:
  • EEPROM.write(address, data) - scrive i dati (solo byte!) nell'indirizzo.
  • EEPROM.update(address, data) - aggiorna (stesso registrazione, ma migliore) il byte di dati all'indirizzo. Non implementato per esp8266/32.
  • EEPROM.read(address) - legge e restituisce il byte di dati che si trova all'indirizzo.
  • EEPROM.put(address, data) - scrive (infatti - aggiorna, update) dati di qualsiasi tipo (tipo di variabile) all'indirizzo.
  • EEPROM.get(address, data) - legge i dati all'indirizzo e li scrive nei dati stessi - la variabile specificata.
  • EEPROM[] - la libreria permette di lavorare con la memoria EEPROM come con un normale array di tipo byte (uint8_t).
A differenza di avr/eeprom.h, non abbiamo strumenti separati per lavorare con tipi di dati specifici diversi da byte e non possiamo scrivere/aggiornare/leggere per float/long/int. Ma abbiamo put/get, che è molto comodo da usare! Considera un esempio con la lettura/scrittura di byte:
#include <EEPROM.h>
void setup() 
     Serial.begin(9600); 
      // scrivi 200 all'indirizzo 10 
     EEPROM.update(10, 200); 
     Serial.println(EEPROM.read(10)); // 200 
     Serial.println(EEPROM[10]); // 200
}
void loop() 
{
}
Presta attenzione a lavorare con EEPROM come array, puoi leggere, scrivere, confrontare e persino utilizzare operatori composti, ad esempio EEPROM[0] += 10 , ma funziona solo per celle elementari, byte. Ora vediamo come funziona put/get:
#include <EEPROM.h>
void setup() 
     Serial.begin(9600); 
      // dichiariamo le variabili dove scriveremo
     float dataF = 3.14; 
     int16_t dataI = -634; 
     byte dataArray[] = {10, 20, 30, 40}; 
     EEPROM.put(0, dataF); 
     EEPROM.put(4, dataI); 
     EEPROM.put(6, dataArray); 
      // dichiariamo le variabili dove leggeremo
     float dataF_read = 0; 
     int16_t dataI_read = 0; 
     byte dataArray_read[4];
     // si legge esattamente come scritto
     EEPROM.get(0, dataF_read); 
     EEPROM.get(4, dataI_read); 
     EEPROM.get(6, dataArray_read); 
     // controllo
     Serial.println(dataF_read); 
     Serial.println(dataI_read); 
     Serial.println(dataArray_read[0]); 
     Serial.println(dataArray_read[1]); 
     Serial.println(dataArray_read[2]); 
     Serial.println(dataArray_read[3]);
}
void loop() 
{
}
Molto più conveniente di write_block e read_block? Put e get converte i tipi e calcola autonomamente le dimensioni del blocco di dati. Funzionano sia con gli array che con le strutture.
EEPROM.h + strutture.
Il modo più conveniente per memorizzare un set di dati in EEPROM è struttura. La struttura consente di combinare tutti i dati sotto un nome e una riga per inserirli nella EEPROM e anche rileggerli. Inoltre non devi pensare all'indirizzamento. Esempio:
#include <EEPROM.h>
struct Data 
     byte bright = 0; 
     int counter = 0; 
     float fvalue = 0;
};
// istanza globale per uso personale
Data data;
void setup() 
     EEPROM.get(0, data);  // leggere dall'indirizzo 0
     // modificare 
     data.bright = 10; 
     data.counter = 1234; 
     data.fvalue = 3.14; 
     EEPROM.put(0, data); // mettere in EEPROM all'indirizzo 0
}
void loop() 
{
}
EEPROM.h + avr/eeprom.h
L'esempio non è molto rilevante, usa EEPROM + strutture. 
E naturalmente puoi utilizzare contemporaneamente tutti i vantaggi di entrambe le librerie, ad esempio EEMEM e put/get indirizzamento automatico. Considerando l'esempio precedente, invece di impostare manualmente gli indirizzi, utilizziamo EEMEM, ma il valore dovrà essere convertito in un tipo intero, prendendo prima da esso l'indirizzo, cioè (int)&address_eemem.
#include <EEPROM.h>
float EEMEM dataF_addr;
int16_t EEMEM dataI_addr;
byte EEMEM dataArray_addr[5];
void setup() 
     Serial.begin(9600); 
     // dichiariamo le variabili dove scriveremo  
     float dataF = 3.14; 
     int16_t dataI = -634; 
     byte dataArray[] = {10, 20, 30, 40};
     EEPROM.put((int)&dataF_addr, dataF); 
     EEPROM.put((int)&dataI_addr, dataI); 
     EEPROM.put((int)&dataArray_addr, dataArray);
      // dichiariamo le variabili dove leggeremo  
     float dataF_read = 0; 
     int16_t dataI_read = 0; 
     byte dataArray_read[4];
      // si legge esattamente come scritto  
     EEPROM.get((int)&dataF_addr, dataF_read); 
     EEPROM.get((int)&dataI_addr, dataI_read); 
     EEPROM.get((int)&dataArray_addr, dataArray_read); 
     EEPROM[0] += 10;
      // controllo
     Serial.println(dataF_read); 
     Serial.println(dataI_read); 
     Serial.println(dataArray_read[0]); 
     Serial.println(dataArray_read[1]); 
     Serial.println(dataArray_read[2]); 
     Serial.println(dataArray_read[3]);
}
void loop() 
{
}

Crea il tuo sito web gratis! Questo sito è stato creato con Webnode. Crea il tuo sito gratuito oggi stesso! Inizia