vendredi 10 janvier 2020

Utiliser une mémoire EEPROM I2C (Arduino)


Après avoir exploré l'utilisation de la mémoire EEPROM interne de l'Arduino,  voyons maintenant comment utiliser une mémoire EEPROM externe communiquant en I2C .



Je rappelle que le sigle EEPROM signifie "Electrically-Erasable Programmable Read-Only Memory". Il s'agit d'une mémoire non-volatile: les informations persistent après que l'EEPROM ait été mis hors-tension.

Une mémoire EEPROM externe: pourquoi?

Dans le dernier article, nous avons mentionné quelques utilisations pertinentes d'une mémoire EEPROM, comme sauvegarder les préférences d'un utilisateur ou les paramètres de calibration d'un capteur. On peut toutefois se demander à quoi pourrait bien servir une mémoire EEPROM externe alors que l'Arduino dispose déjà de sa propre mémoire EEPROM interne.

Quelques possibilités:
  • ajouter de la mémoire supplémentaire, si l'espace disponible dans l'EEPROM interne est insuffisant.
  • augmenter la durée de vie de l'EEPROM (la fiche technique des circuits intégrés que j'ai testés pour cet article garantit une durée de vie de l'ordre du million de réécritures, alors que celle de  l'EEPROM interne de l'Arduino est plutôt de 100 000 réécritures).
  • utilisation de l'EEPROM externe comme une clé de sécurité amovible qui serait nécessaire pour faire fonctionner l'Arduino, ou qui donnerait à son utilisateur des privilèges d'administrateur.
Les modèles d'EEPROM I2C testés

Pour les besoins de cet article, j'ai fait l'essai de 3 mémoires EEPROM  I2C:
Avec un peu de chance, les informations que vous trouverez ici vous serons également utiles pour d'autres modèles d'EEPROM  I2C.

Il est important de mentionner que la capacité des mémoires EEPROM I2C externes est donnée en kilobits et non en kilooctets. Ainsi, une mémoire EEPROM de 1K comporte 1024 bits (128 octets) et contient donc 8 fois moins d'information que la mémoire EEPROM interne de l'Arduino qui, elle, est de 1 kilooctet!

Connexions de l'EEPROM à l'Arduino

Les EEPROM I2C prennent la forme d'un circuit intégré de 8 broches.



Les 3 modèles de mémoire EEPROM que j'ai testés se branchent à l'Arduino de la même façon:


  • Les broches 1, 2 et 3 du CI ne sont pas branchées. Pour le modèle 24C64A, on peut relier certaines d'entre elles à 5 V pour modifier l'adresse I2C. Pour les deux autres modèles, ces 3 broches ne servent à rien!
  • La broche 4 du CI doit être branchée à la masse (GND).
  • La broche 5 du CI est responsable de l'échange des données (SDA). On la branche à la broche A4 de l'Arduino Uno, ainsi qu'à 5 V par l'entremise d'une résistance de tirage de quelques kiloohms (j'ai utilisé 4,7 kΩ).
  • La broche 6 du CI reçoit le signal d'horloge (SCL). On la branche à la broche A5 de l'Arduino Uno, ainsi qu'à 5 V par l'entremise d'une résistance de tirage de quelques kiloohms (j'ai utilisé 4,7  kΩ).
  • La broche 7 du CI (WP) doit être reliée à la masse (GND) pour qu'il soit possible d'écrire dans l'EEPROM. Si vous désirez ensuite empêcher que l'information de l'EEPROM puisse être modifiée, vous pouvez brancher cette broche à 5 V.
  • La broche 8 du CI doit être reliée à 5 V.

Exemples de sketch

Même si mes 3 modèles d'EEPROM se branchent exactement de la même façon et qu'ils ont la même adresse I2C (0x50), j'ai dû écrire un sketch légèrement différent pour chaque modèle!

Dans chaque cas, j'ai écrit un premier sketch  qui écrit la valeur "100" à l'adresse numéro 5 de l'EEPROM, et un deuxième sketch qui lit la valeur enregistrée à chaque adresse de l'EEPROM.

Si l'EEPROM n'a jamais été utilisé auparavant, l'exécution du sketch de lecture affichera la valeur maximale de 255 pour chaque adresse:


Le sketch d'écriture enregistre la valeur "100" à l'adresse 5 de l'EEPROM.


On pourra ensuite exécuter à nouveau le sketch de lecture pour vérifier que l'adresse 5 contient maintenant la valeur 100:


Dans tous les cas, j'ai utilisé la bibliothèque Wire intégrée à l'IDE Arduino pour gérer la communication I2C.

Exemples de sketch pour le 24LC01B

Le 24LC01B (fiche technique) comporte 128 octets numérotés de 0 à 127. Chacun de ces 128 emplacements de mémoire peut contenir une valeur située entre 0 et 255.

Le sketch ci-dessous écrit la valeur "100" à l'adresse "5" de l'EEPROM. Cette écriture ne s'exécute qu'une seule fois, au démarrage du programme. L'essentiel du travail s'effectue dans la routine writebyte():

  • On débute la transmission en indiquant l'adresse I2C du 24LC01B:  Wire.beginTransmission(0x50);
  • On envoie au 24LC01B l'adresse de l'octet dont on désire modifier la valeur: Wire.write(5);
  • On envoie au 24LC01B la valeur qu'on désire enregistrer à cette adresse: Wire.write(100);
  • On clôt la transmission: Wire.endTransmission();

---
---

Le sketch ci-dessous affiche dans le moniteur série la valeur enregistrée à chaque adresse de l'EEPROM. L'essentiel du travail s'effectue dans la routine readbyte():
  • On débute la transmission en indiquant l'adresse I2C du 24LC01B:  Wire.beginTransmission(0x50);
  • On envoie au 24LC01B l'adresse de l'octet dont on désire lire la valeur: Wire.write(5);
  • On clôt la transmission: Wire.endTransmission();
  • On demande au 24LC01B de nous envoyer l'information: Wire.requestFrom(0x50, 1);
  • On récupère l'information reçue: if (Wire.available()) {lecture = Wire.read();}
---
---


Exemples de sketch pour le 24LC16B

Le 24LC16B comporte 8 blocs de 256 octets (donc un total de 2048 octets). Puisque 11 bits sont nécessaires pour exprimer les 2048 adresses, 3 de ces bits (ceux de poids fort) doivent être ajoutés à la fin de l'adresse I2C .

Nous devons donc faire appel aux opérations bit à bit...

Plutôt que simplement écrire Wire.beginTransmission(adressei2c), nous écrivons:

Wire.beginTransmission((int)(adressei2c | adresseMem >> 8));

L'expression "adressei2c | adresseMem >> 8" ajoute les bits de poids fort de l'adresse mémoire à la fin de l'adresse  I2C du 24LC16B.

Et plutôt que simplement écrire Wire.write(adresseMem), nous écrivons:

Wire.write((int)(adresseMem & 0b11111111));

L'expression "adresseMem & 0b11111111" ne conserve que les 8 bits de poids faible de l'adresse mémoire (ceux de poids forts ayant déjà été transmis en même temps que l'adresse  I2C.

Voici donc le sketch qui écrit la valeur 100 à l'adresse 5 du 24LC16B:

---
---

...et voici le sketch qui lit chacune des 2048 valeurs enregistrées dans le 24LC16B:

---
---

Exemples de sketch pour le 24C64A

Le 24C64A comporte 8192 octets, et deux octets sont nécessaires pour écrire une adresse. Plutôt qu'envoyer une partie de l'adresse mémoire en même temps que l'adresse I2C , on sépare les 16 bits de l'adresse mémoire en deux octets:

Wire.write((int)(adresseMem >> 8));   // bits de poids fort de l'adresse mémoire
Wire.write((int)(adresseMem & 0xFF)); // bits de poids faible de l'adresse mémoire

Voici le sketch qui écrit la valeur 100 à l'adresse 5 du 24C64A:

---
---

...et voici celui qui lit chacune des 8192 valeurs enregistrées dans le 24C64A:

---
---


Yves Pelletier (TwitterFacebook)


2 commentaires:

  1. merci pour cette publication très explicite et simple pour la compréhension, c'est un model pour celui qui veut comprendre les mémoires 24c...

    RépondreSupprimer
  2. Merci beaucoup pour ces explications, c'est ce que j'avais besoin pour programmer ma première Eeprom 24C16 !

    RépondreSupprimer