La memoria arduino ha un numero limitato di celle, 1024 su arduino uno, per cui è necessario ottimizzare la EEPROM per cercare ridurne al minimo il suo utilizzo e poter memorizzare un maggior numero di informazioni.
Il tuo progetto potrebbe dover memorizzare dati sulla EEPROM di arduino.
In questo articolo apprenderai come memorizzare un intero arbitrariamente grande e ottimizzare la EEPROM e lo spazio.
Un po’ di teoria sugli interi con arduino
Esistono diversi tipi di interi che potrai utilizzare con arduino.
Tutti i tipi hanno diverse dimensioni in BYTE, gruppi di 8 bit che ne contengono il valore in base 2, detto comunemente valore binario, per esempio 241 in base 10 equivale a 11110001 binario.
Un breve elenco di alcuni tipi disponibili chiarisce meglio le idee:
int | Numeri interi a 16bit(2 BYTE) (in alcuni modelli di arduino a 32bit) Variano da -32.768 a +32.767. (nei modelli a 32 bit da -2.147.483.648 a +2.147.483.647) |
long | Numeri interi a 32bit(4 BYTE) Variano da -2.147.483.648 a +2.147.483.647 |
long long | Numeri interi a 64bit(8 BYTE) Variano da −9.223.372.036.854.775.808 a +9.223.372.036.854.775.807 |
unsigned int | Numeri interi a 16bit(2 BYTE) o in alcuni modelli di arduino a 32bit(4 BYTE) Variano da 0 a +65.535 oppure, nei casi in cui siano a 32 bit da 0 a +4.294.967.295 |
unsigned long | Numeri interi a 32bit(4 BYTE) Variano da 0 a +4.294.967.295 |
unsigned long long | Numeri interi a 64bit(8 BYTE) Variano da 0 a +18.446.744.073.709.551.615 |
come puoi osservare, hai a disposizione per gestire quasi tutto ciò che ti si presenterà nei tuoi progetti per ottimizzare la EEPROM.
Dovrai prestare attenzione ai tipi a 64bit per due motivi:
- anche se gestiti correttamente, possono fare esplodere le dimensioni dello sketch compilato.
- Non sono supportati direttamente da metodi come print() e println() dell’oggetto serial.
Potrai comunque gestirli con alcuni trucchi che ti spiegherò in un prossimo articolo.
Tornando al tema principale, come visto nell’articolo sulle EEPROM, ogni cella ha le dimensioni di 1 BYTE, ti serviranno quindi 2 celle per un intero a 16bit, 4 per quelli a 32bit ed 8 per quelli a 64bit.
Definizione del dominio e dei limiti per ottimizzare la EEPROM
Definisci il dominio come il range che contiene il numero massimo che vorrai memorizzare.
Vediamo come puoi definire i limiti del dominio di una determinata applicazione partendo con un esempio pratico un esempio:
Supponi di voler misurare la rotazione con un encoder ad impulsi, se l’albero di rotazione è fissato in modo che ad ogni giro del albero rotore l’encoder emetta 3000 impulsi e la velocità massima del motore è di 1500 giri al minuto potresti voler memorizzare 2 giorni consecutivi.
Definiti tutti i dati per definire il limite massimo del dominio usa la seguente moltiplicazione ed otterrai il valore limite del tuo dominio:
3000 impulsi * 1500 giri/minuto * 60 minuti * 24 ore * 2 giorni = 12.960.000.000
Questo numero equivale alla massima quantità di impulsi che dovrai memorizzare se la macchina mantenesse il suo massimo regime per tutti e due i giorni, inoltre gli impulsi daranno origine a un conteggio sempre positivo.
Nello sketch avrai bisogno di una variabile a 64bit, in quanto tutte quelle più piccole non saranno sufficienti, sarà indifferente la scelta di una variabile unsigned piuttosto che signed, in quanto i numeri saranno solo positivi e sufficientemente grandi.
Con variabili signed potresti incappare in problemi legati al bit di segno ti consiglio di usare variabili unsigned.
Per memorizzare i dati invece potrai ottimizzare ulteriormente, in quanto avrai a disposizione quante celle da 8bit vorrai.
Nella seguente tabella è indicato il valore massimo solo positivo per diversi multipli di celle (BYTE):
1 CELLA o 1 BYTE | 255 |
2 CELLE o 2 BYTES | 65.535 |
3 CELLE o 3 BYTES | 16.777.215 |
4 CELLE o 4 BYTES | 4.294.967.295 |
5 CELLE o 5 BYTES | 1.099.511.627.775 |
6 CELLE o 6 BYTES | 281.474.976.710.655 |
come puoi osservare è necessario usare almeno 5 celle, in quanto 4 sarebbero poche e 6 sarebbero insufficienti.
L’algoritmo per ottimizzare la EEPROM con gli operatori booleani
A questo punto hai a disposizione una variabile a 64 bit e le 5 celle per memorizzare la lettura in essa contenuta.
Il problema consiste nel trasferire il contenuto di un grosso contenitore (variabile) in 5 contenitori più piccoli (celle).
La soluzione è più semplice di quanto pensi !
Devi spezzare il numero in pezzi più piccoli e memorizzarlo per ottimizzare la EEPROM.
Puoi spezzare il numero grazie agli operatori booleani ereditati dal linguaggio C.
Il primo operatore di cui avrai bisogno è il prodotto booleano ( AND ) indicato negli sketch con il simbolo & (come in wiring, C++ e C).
Notice
NON CONFONDERE tale operatore con il passaggio per referenza delle variabili, oppure con l’operatore indirizzo. Entrambi usano lo stesso simbolo e ciò può creare confusione.
Il secondo operatore che ti servirà è lo scorrimento a destra indicato negli sketch con il simbolo >> (come in wiring, C++ e C).
Anche in questo caso non dovrai confonderlo con l’operatore per lo streaming di dati, in quanto assolve tutt’altro compito.
Cosa fanno gli operatori booleani
Ti mostro cosa fanno questi operatori con degli esempi partendo dal prodotto logico:
167.665 & 255 = 241
Scritto così ha poco significato. La stessa operazione fatta in colonna, con i numeri tradotti in base2 ti sarà molto più chiara:
Puoi intuire l’analogia con l’applicazione di una maschera, infatti tutto ciò che è sopra gli 1 rimane tutto ciò che è sopra agli zero vene azzerato (mascherato). In questo caso preservi solo i primi 8 bit del numero di partenza.
Passa all’operatore di scorrimento:
167.655 >> 8 = 654
Analizzando in colonna questo esempio, capirai cosa succede:
Gli 8 bit meno significativi sono usciti a destra, 8 nuovi bit posti a 0 ed indicati in neretto sono entrati. In giallo puoi osservare i bit spostati a destra.
Riapplica la maschera AND ed otterrai il secondo pezzo del numero di partenza e così via per quanti pezzi ti serviranno.
Lo sketch per ottimizzare la EEPROM
Ora hai a disposizione un metodo risolutivo, dovrai solo tradurlo in uno scketch. Ecco come si fa con il seguente esempio:
#include <EEPROM.h> #define ENCODER_PIN 2 // Sul pin 2 è collegato l'encoder #define PUSH_BUTTON_STORE 4 // Il pulsante al pin 2 indica quando memorizzare il valore conteggiato #define COUNTER_INDEX 200 // Un valore a caso di esempio che indica l'indirizzo in cui memorizzare il dato #define DEBOUNCE_TIME_US 5 // Tempo antirimbalzo per evitare doppie letture tenendo conto che tra due impulsi passano minimo 13 microSecondi unsigned long long value=0; // Variabile che terrà il conto void setup() { pinMode(PUSH_BUTTON_STORE, INPUT); pinMode(ENCODER_PIN, INPUT); } void loop() { if(digitalRead(PUSH_BUTTON_STORE)==HIGH) { for(int indice = 0;indice < 5;indice ++) { EEPROM.write(COUNTER_INDEX + indice, byte(value & 255)); delay(5); value=value >> 8; } value=0; } if(digitalRead(ENCODER_PIN)==HIGH) { value ++; delayMicroseconds(DEBOUNCE_TIME_US); } }
Leggendo lo sketch riga per riga:
linea 1: includi la libreria EEPROM;
linee 2-6: definisci delle costanti per rendere più leggibile il codice;
linea 8: definisci la variabile che conterrà un valore sufficientemente grande e azzerala allo stesso tempo;
linee 11-12: indica che i due piedini saranno input, al primo sarà collegato un pulsante di memorizzazione ed azzeramento, al secondo l’encoder;
linea 16: rileva la pressione del tasto o non perdere tempo;
linea 17: sei qua se il tasto è stato premuto, inizia quindi un ciclo che si ripeterà per i 5 BYTE(Celle) che devi memorizzare;
linea 18: memorizza a partire da COUTER_INDEX i 5 byte, uno per ogni ripetizione del ciclo for(). Prendi gli ultimi 8 bit a destra (Il BYTE meno significativo);
linea 19: assicurati che la EEPROM abbia il tempo di memorizzare il dato;
linea 20: sposta a destra di 8 bit value, ora gli 8 bit meno significativi sono quelli che precedevano gli 8 memorizzati;
linea 21: chiudi il ciclo for() iniziato alla linea 16;
linea 22: azzera il conteggio e preparati a ricominciare tutto;
linee 25-26: se ricevi un impulso incrementa il contatore value, attendi qualche microSecondo per evitare rimbalzi del conteggio e procedi;
Con questo articolo abbiamo visto come procedere alla memorizzazione. Prossimamente spiegheremo come rileggere i dati memorizzati e ricostruire un intero, vedremo anche alcuni trucchi inviare numeri a 64 bit sulla seriale.
13 commenti
Vai al modulo dei commenti
Complimenti Mauro, bell’articolo.
Sei il Luciano de Crescenzo del 21° secolo, che sa rendere semplice e comprensibile l’informatica e l’elettronica.
Autore
Ciao Alberto,
grazie per il complimento, ma questo articolo è di Marco .. un appassionato molto in gamba che ha voluto condividere le sue conoscenza sul mio blog con tutti noi 🙂
Mauro
Il complimento per te é sempre valido perché nei tuoi articoli non sei da meno peró in questo caso specifico mi devo complimentare con Marco. Ottimo lavoro!
Grazie mille… 🙂 Spero ancora di poter contribuire con altri articoli…
Caro Mauro mi é piaciuto molto questo tuo articolo sulle EEPROM pero,x poterlo usare dovresti dirmi dove trovo
l”esempio x poterle leggere che tu dici, pubblicherai piu avanti. grazie
distinti saluti robi
Autore
Ciao Robi,
cercando nel blog non hai trovato nulla?
Sai che mi viene il dubbio di non aver mai scritto la seconda parte ?
Ciao Mauro ,
non hai più scritto l’articolo per la lettura dei dati memorizzati in modo ottimizzato?
Autore
No Andrea,
hai ragione, sono stato preso da altri progetti ed ho tantissime cose che vorrei approfondire, se ti va scrivilo tu e me lo invii io lo pubblicherò a tuo nome.
ciao Mauro,
intanto volevo farti i complimenti per il sito e per le tue spiegazioni chiare e dirette.
E’ da poco tempo che sono entrato nel mondo di arduino, quindi non ho molta dimestichezza … comunque volevo chiederti un’informazione riguardante la memoria della EEPROM: cosa succede nel momento in cui la memoria è satura di dati ? blocca il funzionamento della scheda o non salva più nessun altro dato ?
Grazie
Autore
Ciao Giacomo,
il funzionamento della EEPROM è simile ad uno spazio di archiviazione dati.
Per cui quando avrai riempito tutte le aree disponibili potrai leggerle, o sovrascrivere quelle già utilizzate con dati nuovi, il tuo arduino/genuino continuerà a funzionare senza difficoltà.
grazie mauro … gentile come sempre
mauro volevo farti un’altra domanda…
io sto usando un encoder (di una stampante) per un progetto scolastico e voglio salvare l’ultimo valore del mio encoder.. sto usando la libreria EEPROMex.h perchè ho la necessità di usare valori superiori a 255 e sto usando le funzioni EEPROM.readLong ed EEPROM.updateLong perchè ho anche numeri negativi..
tuttavia dopo pochi cicli mi compare la scritta :” EXCEEDED MAXIMUM NUMBERS OF WRITES”.
come faccio a risolvere questo problema?
grazie
Autore
Ciao Giacomo,
non sono un esperto di calcoli in numeri binari, tuttavia ricordo di aver pubblicato un articolo di un appassionato arduino che ha scritto e condiviso sul mio blog dei ragionamenti molto interessanti sull’argomento.