Come usare una memoria 24LC256 su bus I2C

Il limite di memoria di una scheda Arduino può essere superato utilizzando delle memorie esterne che permettono di aggiungere capacità di memorizzazione per tutte quelle applicazioni che richiedono la registrazione di dati. Possiamo scegliere tantissimi tipi di memorie a seconda del tipo di interfaccia (I2C, SPI, parallela…) e dalla loro capacità.

Per questo tutorial ho scelto una comunissima EEPROM Seriale prodotta da Microchip, la 24LC256. Questa memoria può essere collegata alle schede Arduino tramite bus I2C e offre 256 Kbits di memoria (32KB). La pin function della memoria è rappresentata in figura:

24LC256

i pin A0, A1, A2 permettono di specificare l’indirizzo del dispositivo sul bus I2C, i pin Vcc e Vss sono rispettivamente l’alimentazione (5Vdc) e la massa (Gnd), il pin WP (Write Protect) abilita (se collegato a Gnd) o disabilita (se collegato a Vcc) la scrittura della EEPROM, i pin SCL e SDA sono relativi al collegamento I2C.

secondo il datasheet del produttore possiamo riassumere alcune caratteristiche fondamentali:

Massimo tempo di scrittura 5mS
Cicli di scrittura/lettura 100000
Conservazione dei dati >200 anni
Consumo di corrente in scrittura 3mA (alimentazione a 5Vdc)
Consumo di corrente in lettura 400uA (alimentazione a 5Vdc)
Consumo di corrente in stand-by 100nA (alimentazione a 5Vdc)
Intervallo di temperature versione industriale da -40°C a +85°C
Intervallo di temperature versione automotive da -40°C a +125°C

Per questo tutorial ho utilizzato una Arduino Leonardo. La figura seguente mostra i collegamenti del circuito.

Schema elettrico Leonardo 24LC256

Nello schema possiamo osservare che i pin A0, A1, A2 sono connessi a massa. Questi pin permettono di personalizzare l’indirizzo del dispositivo all’interno del bus I2C. Nel data sheet verifichiamo come viene formato il control byte address:

Control Byte Addressi 4 bit più significativi sono preimpostati (Control Code) mentre i bit A0, A1 e A2 sono personalizzabili da noi. Il bit meno significativo R/W, serve per selezionare una operazione di scrittura o una operazione di lettura.
Avendo a massa i pin A0, A1 e A2 i campi Chip Select Bits sono a zero. L’indirizzo del dispositivo è quindi il seguente:

Set address bit

in esadecimale otteniamo il valore:

0x50

Per poter eseguire operazioni di scrittura e lettura della memoria EEPROM del 24LC256, è necessario inviare, oltre al Control Byte (che specifica l’indirizzo del dispositivo nel busI2C), altri due byte che indicano l’indirizzo della locazione di memoria EEPROM del 24LC256 che vogliamo scrivere o leggere:

Memoria EEPROM 24LC256

il dispositivo 24LC256 possiede 256Kbit di memoria, quindi le locazioni di memoria che possiamo scrivere e leggere sono 32000 byte (256000 / 8).
Avendo a disposizione 2 byte per selezionare l’indirizzo (Address High Byte e Address Low Byte) possiamo utilizzare una variabile int (formata appunto da 2 Byte) che impiegheremmo per specificare il valore dell’indirizzo della locazione di memoria (da 0 a 31999).

La libreria Wire non permette comunque di passare un valore intero perciò potremmo semplicemente effettuare una operazione bitwise per dividere la variabile intera in due byte. Fortunatamente esistono già delle funzioni che semplificano questa operazione, highByte e lowByte.

Il codice di esempio seguente permette di scrivere tutti i  32000 byte della mem0rie a di eseguire una lettura degli stessi:

#include <Wire.h>

int indirizzo = 0;
int data = 0;
int ciclo = 0;
void setup()
{
  //init seriale
  Serial.begin(9600);
  //per la Arduino Leonardo devo
  //attendere l'inizializzazione del modulo
  while (!Serial)
  {
    ;
  }

  //attendo ulteriori 5 secondi
  //prima di inviare un dato sulla seriale
  delay(5000);
  //invio un messaggio al serial monitor
  Serial.println("Start");
  //inizializzo la porta I2C della Arduino
  Wire.begin();
}

void loop()
{
  //avviso dell'inizio della
  //scrittura dei dati sulla seriale
  Serial.println("Inizio Scrittura");
  //init variabile
  indirizzo = 0;
  //eseguo ciclicamente la scrittura di
  //tutte e 32000 locazioni di memoria
  for (ciclo = 0; ciclo & lt; 32000; ciclo++)
  {
    //invio il control byte specificando
    //una richiesta di scrittura
    Wire.beginTransmission(0x50);
    //invio due byte contenenti l'indirizzo della
    //locazione di memoria che voglio scrivere un byte
    Wire.write(highByte(indirizzo));
    Wire.write(lowByte(indirizzo));
    //scrivo un byte nella locazione di memoria
    //precedentemente selezionata
    Wire.write(byte(data));
    //fine trasmissione
    Wire.endTransmission();
    //incrementa il valore di data
    data++;
    //quando la variabile data arriva a 255
    //resetto il suo valore a zero
    if (data == 255)
      data = 0;

    //incrementa il valore dell'indirizzo
    indirizzo++;
    //attendi 5ms per poter terminare le operazioni di
    //scrittura sulla memoria
    delay(5);
  }

  //avvisami quando la scrittura di 32000 byte
  //è terminata
  Serial.println("Fine Scrittura");
  //attendi 1/2 secondo
  delay(500);
  //avvisami dell'inizio della lettura
  Serial.println("Inizio lettura");

  //init variabile
  indirizzo = 0;
  for (ciclo = 0; ciclo & lt; 32000; ciclo++)
  {
    //invio il control byte specificando
    //una richiesta di scrittura
    Wire.beginTransmission(0x50);
    //invio due byte per selezionare la locazione
    //di memoria da leggere
    Wire.write(highByte(indirizzo));
    Wire.write(lowByte(indirizzo));
    Wire.endTransmission();
    //incremento la variabile indirizzo
    indirizzo++;
    //richiedo un byte al 24LC256
    Wire.requestFrom(0x50, 1);
    //attendo la disponibilità di dati sul bus i2c

    while (Wire.available())
    {
      Serial.print(ciclo);
      Serial.print(": ");
      //leggo dal bus il dato relativo alla
      //locazione di memoria precedentemente specificata
      byte c = Wire.read();
      //invio il dato al serial monitor
      Serial.println(c);
    }
  }
  delay(1000);
}

 

la scrittura di 32000 byte richiederà qualche minuto poiché il codice impiega circa 5ms per scrivere un byte (32000 * 0.005 => 160 secondi). La lettura invece risulta più fluida ed è anche influenzata dalla velocità della seriale, che per questo esempio è impostata a 9600bps.