EEPROM 24LC16 e libreria WIRE

Salve ragazzi,
ho bisogno di memorizzare alcune informazioni per un mio progetto in una sezione non volatile ed ho pensato di avvicinarmi alle EEPROM. Non sapendo quanto spazio mi permettono di memorizzare e di quanto spazio ho bisogno, ho acquistato delle EEPROM 24LC16,64,256,512.
Sto iniziando quindi ad avvicinarmi alla 24LC16 con un codice che ho trovato sul forum (è stato leggermente modificato) e che vi posto:

#include <Wire.h>
#define EE_ADDRESS 0x50

void setup() 
{ 
  Serial.begin(9600);
  Wire.begin(); // join i2c bus (address optional for master)
  
  Serial.println("Send");
  Wire.beginTransmission(EE_ADDRESS); // 24LC16 device address 
  Wire.send(0); // address within EEPROM
  Wire.send("Arduino"); // send 7 data bytes 
  Wire.endTransmission();
  
} 

void loop() 
{ 
  Serial.println("Receive");
  Wire.beginTransmission(EE_ADDRESS); // 24LC16 device address 
  Wire.send(0); // address within EEPROM
  Wire.endTransmission();
  Wire.requestFrom(0x50, 7); // request 7 bytes from device
  
  while(Wire.available())
  { 
    byte c = Wire.receive();
    Serial.print(c); // print the character 
  } 
  Serial.println();
  delay (5000);
}

Ora a riguardo ho alcune domande da farvi:

  • perché la parola Arduino occupa 7 byte (ogni lettera è 1 byte ovvero 8 bit)?
  • perché si inizia la scrittura da 0x50? Le "righe" precedenti della memoria non "utilizzabili"?
  • con vari esperimenti ho notato che su ogni riga riesco ad inserire 16 byte (sono riuscito a memorizzare la parola "0123456789ABCDEF"), ma come faccio a passare alla scrittura/lettura sulle righe successive?

Inoltre ho visto che salendo di EEPROM, devo utilizzare due parole per indicizzare la riga...E' vero ciò?
Grazie

  1. "ARDUINO" occupa 7 byte perché è memorizzata in formato ASCII quindi 1 lettera corrisponde ad 1 byte.

  2. non stai leggendo dall'indirizzo 0x50, stai chiedendo al dispositivo posto all'indirizzo 0x50 di inviarti 7 byte di dati. In realtà l'autore avrebbe dovuto scrivere:

Wire.requestFrom(EE_ADDRESS, 7);

avresti capito meglio

Ti allego un programma di esempio che trovai tempo fa in rete. Gestisce 2 chip I2C ma tu puoi adattarlo per gestirne 1 solo.

/*
Example 21.2  Reading and writing data to Microchip 24LC256 EEPROMS over I2C
tronixstuff.com/tutorials > Chapter 21  CC by-sa v3.0
*/
#include  "Wire.h"    // for I2C
#define chip1 0x50    // device address for left-hand chip on our breadboard
#define chip2 0x51    // and the right

// always have your values in variables
unsigned int pointer = 69; // we need this to be unsigned, as you may have an address > 32767
byte d=0; // example variable to handle data going in and out of EERPROMS

void setup() {
    delay(5000);
    Serial.begin(9600); // for screen output
    Wire.begin();   // wake up, I2C!
}

void writeData(int device, unsigned int add, byte data) {
// writes a byte of data 'data' to the chip at I2C address 'device', in memory location 'add'
    Wire.beginTransmission(device);
    Wire.send((int)(add >> 8));   // left-part of pointer address
    Wire.send((int)(add & 0xFF)); // and the right
    Wire.send(data);
    Wire.endTransmission();
    delay(10);
}

byte readData(int device, unsigned int add) {
// reads a byte of data from memory location 'add' in chip at I2C address 'device'
    byte result;  // returned value
    Wire.beginTransmission(device); //  these three lines set the pointer position in the EEPROM
    Wire.send((int)(add >> 8));   // left-part of pointer address
    Wire.send((int)(add & 0xFF)); // and the right
    Wire.endTransmission();
    Wire.requestFrom(device,1); // now get the byte of data...
    result = Wire.receive();  return result; // and return it as a result of the function readData
}

void loop() {
    Serial.println("Writing data...");
    for (int a=0; a<20; a++) {
        writeData(chip1,a,a);
        writeData(chip2,a,a); // looks like a tiny EEPROM RAID solution!
    }
    Serial.println("Reading data...");
    for (int a=0; a<20; a++) {
        Serial.print("chip1 pointer ");
        Serial.print(a);
        Serial.print(" holds ");
        d=readData(chip1,a);
        Serial.println(d, DEC);
    }
    for (int a=0; a<20; a++) {
        Serial.print("chip2 pointer ");
        Serial.print(a);
        Serial.print(" holds ");
        d=readData(chip2,a);
        Serial.println(d, DEC);
    }
    delay(500);
}

Se Ti serve meno di 1025 Byte usa la EEprom interna del ATmega328. La usi con la libreria eeprom.

con vari esperimenti ho notato che su ogni riga riesco ad inserire 16 byte (sono riuscito a memorizzare la parola "0123456789ABCDEF"), ma come faccio a passare alla scrittura/lettura sulle righe successive?

Il buffer per scrivere un blocco di dati in una volta (Page Write) é lungo 16 Byte. Per quello puoi scriverci solo 16 byte e se provi di scriverne di piú il resto sovvrascrive i primi byte trasmessi.
Devi scrivere dati a blocchi di 16 lasciandoil tempo tra una scrittura e l' altra perché l'eeprom riesca a scrivere i dati visto che il ciclo di scritura é lento.

Ciao Uwe

Oppure inviare i dati sequenzialmente, 1 byte alla volta, incrementando via via l'indirizzo, come nell'esempio che ti ho allegato. Tutto dipende da che tipo di dati devi scrivere e da come ti stai interfacciando alla memoria.

leo72:
Oppure inviare i dati sequenzialmente, 1 byte alla volta, incrementando via via l'indirizzo, come nell'esempio che ti ho allegato. Tutto dipende da che tipo di dati devi scrivere e da come ti stai interfacciando alla memoria.

Solo la differenza tra scrivere 16 byte singolarmente e 16 Byte( una pagina) alla volta é che l' ultima é 16 volte piú veloce e con un tempo di 5mS per ogni ciclo di scrittura i tempi sono 5 e 80 msecondi.
Comunque usando altri modelli di EEprom le cose cambiano perché hanno un buffer piú grande.

E adesso ricomincio a rompere con i FRAM della RAMTRON che scrivono in "tempo reale" senza pausetta necessaria. sono pincompatibile con gli EEPROM.

Ciao Uwe

uwefed:
Solo la differenza tra scrivere 16 byte singolarmente e 16 Byte( una pagina) alla volta é che l' ultima é 16 volte piú veloce e con un tempo di 5mS per ogni ciclo di scrittura i tempi sono 5 e 80 msecondi.

E' vero però ho anche detto:

Tutto dipende da che tipo di dati devi scrivere e da come ti stai interfacciando alla memoria.

Quindi, come al solito, ci ritroviamo a parlare e dare consigli senza sapere poi cosa chi ha posto la domanda voglia effettivamente fare XD
Usare un buffer di 16 byte può essere conveniente come no, bisogna capire se hai l'effettiva necessità di riempire tutti quei byte in una volta sola.

Ciao LEO
Sí hai ragione: Non si vende la pelle prima che s'ammazzi l'orso.
Ma é un problema nostro; rispondiamo troppo in fretta e cosí chi fa la domanda non guard le risposte e non puó reagire al nostro aiuto.
Dalla sua domanda sembra che memorizzi stringhe.
La scrittura di una pagina non vuol dire che devi per forza scrivere tutti i 16 Byte possibili, puoi scrivere ance solo 2 o 3 .
Ciao Uwe

uwefed:
Ma é un problema nostro; rispondiamo troppo in fretta e cosí chi fa la domanda non guard le risposte e non puó reagire al nostro aiuto.

Salve,
non è che non si guardano le risposte e non si reagisce all'aiuto, ma pensate che non dispongo di una connessione Adsl e soprattutto non sono mai allo stesso posto, quindi mi risulta difficile essere collegato h24.
In ogni modo mi piaceva capire come memorizzare i dati, l'obiettivo è quello si salvare stringhe da 16 byte ma intanto sto provando lo script di leo72 ed ho visto che funziona.
Ora mi sto documentando un pochino sulla libreria Wire per capire come inivare 16 byte, tenendo conto della pausa di 5ms tra una scrittura e l'altra.
A quanto prima...
temuccio
PS: grazie mille per le delucidazioni

Ragazzi,
sto tirando un pochino le somme riguardo le eeprom ma ancora non mi è chiara una cosa...
Ho capito che un byte è vuoto se contiene al suo interno 0xff
Una pagina contiene 16 byte
L'indirizzo di scrittura della prima pagina è 0x000
L'indirizzo di lettura della prima pagina è 0x001
Le pagine successive sono ovviamente 0x010...
Ma quale è il limite massimo di indirizzi per una 24LC16?
Considerando che il 16 indica 16Kbits, ovvero 2Kbyte, dovrei avere al massimo 128 pagine giusto?
Ciò significa che il primo indizzo per la scrittura è: 0x000
Mentre l'utimo indirizzo per la scrittura vale:0x800
Ovviamente per la lettura devo aggiungere 1 alla fine, mutando gli indirizzi da 0x001 a 0x801
I conti sono giusti oppure sbaglio qualcosa?
Grazie

Scusa, togliti per un attimo la storia della "pagina" di memoria. Quello è un buffer, lascialo perdere. Per imparare puoi spedire 1 byte alla volta.

Gli indirizzi di memoria sono indicati con 1 byte per le EEPROM fino a 256 byte di capacità e con 2 byte per quelle più grandi. Per indicare la locazione di memoria posta all'indirizzo 10.000, ad esempio, che corrisponde all'esadecimale $2710 ed al binario b0010011100010001, devi spedire l'indirizzo scomposto nella parte alta e parta bassa,

Nell'esempio che ti ho postato questo viene fatto da:

Wire.send((int)(add >> 8));   // left-part of pointer address
Wire.send((int)(add & 0xFF)); // and the right

Ma puoi usare anche le funzioni lowByte e highByte, che restituiscono appunto un intero (16 bit) scomposto in 2 byte (8 bit l'uno).

Guardano l'esempio di prima:
highByte(10000)=$27 - b00100111
lowByte(10000)=$10 - b00010001

Ciao leo72 e grazie per la risposta,
ma allora perché per la 24LC16 si utilizza un solo byte di indirizzamento se la capacità è superiore a 256 byte?
Inoltre che differenza c'è tra buffer e pagina? Sai mi occorre sistemare i dati a gruppi e l'esempio che mi hai precedentemente postato funziona bene per singolo byte. Attualmente stavo pensando di lavorare con delle strutture dati tipo array per scomporre i gruppi in singoli byte, ma vorrei anche capire come gestirli direttamente...
In ogni modo questa discussione si fa sempre più interessante, piano piano mi si creano nuove questioni che prontamente mi delucidate. Grazie ancora :smiley:

Quella memoria è organizzata in 8 blocchi da 256 byte l'uno. Il "16" nella sigla, 24LC16, significa che è una memoria da 16 Kbit, ossia 16384 bit, ossia 2048 byte (1 byte = 8 bit). La memoria, come dice il datasheet, è organizzata in 8 blocchi da 256 byte l'uno. Per poter usare quella memoria devi spedire sempre 2 byte: se intendi indirizzare un solo byte, spedisci il suo indirizzo nella forma byte alto/byte basso; se vuoi indirizzarla a blocchi, spedisci prima l'indirizzo del blocco da usare e poi il byte.

Esiste poi la 24LC16B: la "B" indica che puoi indirizzarla per pagina, ogni pagina è di 16 byte l'uno (è quella di cui parlava Uwe). Ma tu che modello hai? Se hai una 24LC16 la usi come ti ho detto.

Io possiedo una 24LC16B

OK. La puoi usare nei 3 modi su descritti.

ciao a tutti, sto lavorando su questa eeprom e mi sono accorto che su tutti i forum al riguardo si presenta lo stesso problema, la eeprom da valori sballati e funziona a volte.
studiando attentamente il datasheet della memoria mi sono accorto che il pin wp ha un ruolo importante nel funzionamento.
infatti se esso è messo a 0 la scrittura è consentita, ma la lettura no.
se invece devi leggere devi portare questo pin a 1 e in questo modo si riesce a leggere.
nel mio sketch ho fatto in modo che durante la scrittura esso si trovasse a 0 e durante la fase di lettura a 1, questo però implica l' utilizzo di un pin in più che sia dello stesso arduino o della espansione i2c.

ciao a tutti

Non funziona come dici tu, semplicemente il pin WP, che significa "Write Protect", se messo a massa permette di leggere/scrivere, se collegato a Vcc permette solo di leggere. Considera quel bit come una "protezione da sovrascritture", un po' come le linguette rimosse delle vecchie musicassette che impedivano di registrare su un nastro contenente già della musica.

Se vuoi usare la EEPROM sia in scrittura che lettura collega semplicemente quel pin a massa, altrimenti pilotalo tu tramite sketch.

E' tutto scritto nel datasheet di queste EEPROM.

come dici tu a me non funziona, e se vado a vedere nei datasheet a pagina 6(http://ww1.microchip.com/downloads/en/DeviceDoc/21703G.pdf), mi dice esattamente come il pin lavora,dopodichè a pagina 7 dove mostra l' operazione di scrittura si vede espressamente che il bit in questione è posto a 0.
mentre a pagine 9 e 10 dove mostra le operazioni di lettura tale bit è posto su 1....

forse mi sbaglio ma è l' unica interpretazione valida che do al non funzionamento della eeprom 24lc16 col pin wp posto a massa!

ciao

Il bit R/W è una cosa, il pin WP è un'altra. Nell pagine da te citate si parla del bit R/W. Io ti parlo del pin, del piedino WP.
Del bit R/W si prende carico la lib Wire che a seconda dell'operazione (Write o Read) costruisce il comando da spedire alla EEPROM.
Ma se tu chiedi una scrittura ed il pin WP la impedisce, va in errore. Io ho sempre collegato quel pin a massa senza comandarlo dallo sketch e non ho avuto problemi.

Tutti i dispositivo I2C hanno un indirizzo a 7 Bit. l' ottavo bit segnala se il comando vuole scrivere o leggere dal dospositivo.
Ciao Uwe

perfettamente d' accordo con entrambi, non voglio certo contraddirvi, semplicemente ho avuto questo problema in fase di studio e ho notato che se il piedino wp mentre scrivevo stava a 0 la eeprom registrava il mio valore.
se lasciavo questo a 0 e chiedevo alla eeprom di restituirmi il valore precedentemente scritto mi restituiva un errore che scompariva ponendo wp a 1 e chiedendo nuovamente la lettura.

magari il problema stà proprio nella libreria a monte di questo post, non ho avuto il tempo di verificarla, sto lavorando su altro.

grazie comunque per la pazienza immensa che avete :slight_smile: