MicroPython: Utiliser une EEPROM avec Pyboard et PYBStick

Bonjour à tous,
Aujourd'hui, nous allons nous pencher sur une nouvelle bibliothèque MicroPython permettant de lire et écrire des données dans une EEPROM.

EEPROM I2C
AT24C512 (64Ko), AT24C02 (256o), 24LC256

Si les EEPROM autorisent des accès octet par octet, la bibliothèque propose des méthode permettant d'écrire des types de données comme integer, float, string, etc qui sont eux réparti sur plusieurs octets!
Il est important de garder à l'esprit que le stockage d'un type de donnée nécessite un à plusieurs octets en fonction du type de données à stocker. Un octet permet de stocker une valeur entre 0 et 255. Pour stocker un float (nombre avec décimale), il faudra 4 octets consécutif. Il est donc important de garder à l'esprit qu'il ne faut pas écrire n'importe quel type de donnée à n'importe quelle adresse dans la mémoire de l'EEPROM au risque de corrompre d'autres données.

Quelle utilité pour une EEPROM de nos jours?

Nos microcontrôleurs sont surpuissants et disposent d'un système de fichier en mémoire flash, EEPROM de stockage et autre moyens de stockage.
Alors pourquoi utiliser une EEPROM I2C?

La raison est simple: pour stocker des données relatives à la carte d'extension que vous allez brancher sur votre microcontrôleur. Certaines données sont rattachée à l'extension comme capteur, carte d'extension, carte d'interface spécialisée. C'est le cas de données de calibrations, le type d'interface disponible sur la carte, etc.

Cas pratique: c'est le cas de la carte UniPi (voir image-ci dessous) avec son EEPROM qui contient les quelques informations d'identification et les facteurs de conversions de l'interface analogique
Carte d'extension UniPi

Si la carte UniPi devait être remplacée suite à un problème électrique (ce qui arrive parfois en industrie), les nouveaux facteurs de conversion analogique de la nouvelle carte (présent dans l'EEPROM de la carte UNIPI) seront automatiquement utilisé par le microcontrôleur (branché sur la carte).

C'est d'ailleurs pour le projet UniPi-MicroPython-Automation que cette bibliothèque fut initialement crée (je voulais également pouvoir y écrire des information).

Comment brancher

Brancher l'EEPROM est relativement simple, voici un schéma pour la PYBStick... le GitHub reprend également le schéma pour MicroPython Pyboard.
EEPROM sur PYBStick
Ajouter une EEPROM I2C sur la PYBStick - Source: esp8266-upy GitHub

Pour une fois, je ne vais pas m'attarder sur les opérations de lectures/écritures d'octets mais plutôt sur le stockage et relecture de types plus complexes.

Ecriture de données typées dans l'EEPROM

Le script d'exemple test_24c02c_datawrite.py effectue les différents opérations d'écritures.
from machine import I2C
from eeprom24cxx import Eeprom_24C02C, dump

i2c = I2C( 1 )

eeprom = Eeprom_24C02C( i2c, addr=0x50 ) # 256 bytes
# eeprom.debug = True
print( "Erasing %s bytes" % eeprom.capacity )
eeprom.erase()

print( "Writing data...")
mem_addr = 0
mem_addr += eeprom.write_magic( mem_addr, bytearray('MCHOBBY') ) # Ecrire la Magic Key @ addr 0
mem_addr += eeprom.write_magic( mem_addr, [35,36,37] ) # Utiliser une liste de valeur pour #$%
mem_addr += eeprom.byte_write ( mem_addr, 15 ) # No de version = 15
mem_addr += eeprom.sint_write ( mem_addr, -4435 ) # Un entier signé (signed int, -32768 à 32767)
mem_addr += eeprom.usint_write ( mem_addr, 65123 ) # Un entier non signé (usint, 0 à 65535)
mem_addr += eeprom.int_write ( mem_addr, 1000222 ) # Un entier long -2 millard à 2 milliard
mem_addr += eeprom.float_write ( mem_addr, 3.141592 ) # Un float
mem_addr += eeprom.string_write( mem_addr, 'Belgium', 20) # Une chaine de caractère (20 max)
a_bool = True
mem_addr += eeprom.byte_write( mem_addr, 1 if a_bool else 0 )

dump( eeprom )
Cet exemple est très instructif avec la méthode erase() qui permet d'effacer le contenu de l'EEPROM. Chaque opération d'écriture retourne le nombre d'octets utilisés... et donc d'incrémenter l'adresse mem_addr pour les différentes écritures.

La fonction dump() permet d'inspecter le contenu de l'EEPROM.
0x0 : 4D 43 48 4F 42 42 59 23 : MCHOBBY#
0x8 : 24 25 0F EE AD FE 63 00 : $%....c.
0x10 : 0F 43 1E 40 49 0F D8 14 : .C.@I...
0x18 : 42 65 6C 67 69 75 6D 00 : Belgium.
0x20 : 00 00 00 00 00 00 00 00 : ........
0x28 : 00 00 00 00 01 FF FF FF : ........
0x30 : FF FF FF FF FF FF FF FF : ........
0x38 : FF FF FF FF FF FF FF FF : ........
A noter qu'une chaîne de caractères (limitée à 255 caractères) est stockée sur la longueur + 1 octets car le premier octets indique la longueur de la chaîne.
Dans cet exemple, il faut donc 21 octets pour stocker la chaîne de caractère. L'adresse 0x17 (début de la chaîne) contient la valeur 0x14 (soit 20 en décimal) qui n'est autre que la chaîne qui suit.

Lecture de données typées depuis l'EEPROM

Le script d'exemple test_24c02c_dataread.py effectue les différents opérations de relecture des informations. Il faut évidement lire les différentes données à partir des bonnes adresses (sinon le résultat sera imprévisible).

La méthode check_magic() est bien pratique pour vérifier si l'EEPROM a été effacée et formatée avec les données attendues.
Personnellement, je recommande d'écrire un numéro de version juste derrière la Magic Key. Avec un simple octet, il est déja possible d'indiquer 256 versions différentes.
Avec un numéro de version il est possible de faire évoluer une structure de donnée en fonction des besoins sans que votre programme soit imprévisible (puisque les anciennes versions de l'EEPROM ne contiennent pas toutes les données).

Pour le reste, lire le bon type de donnée à la bonne adresse mémoire permet de retrouver les données stockées.
from machine import I2C
from eeprom24cxx import Eeprom_24C02C, dump

i2c = I2C( 1 )

eeprom = Eeprom_24C02C( i2c, addr=0x50 ) # 256 bytes
# eeprom.debug = True

print( "Read data from addr 0x00 ...")
if eeprom.check_magic( 0x00, bytearray('MCHOBBY') ):
	print("Magic key detected!")
else:
	raise Exception('No magic key! See test_mcp24c02c_datawrite.py to initialize EEPROM' )

print("Read 3 bytes. Should be 35,36,37")
data = eeprom.read( 0x07, count=3 )
for value in data:
	print( '   %i' % value )

print("Read version. Expected 15")
print( '   %i' % eeprom.byte_read( mem_addr=0x0A ) ) # 1 byte length

print("Read short signed int. Expected -4435")
print( '   %i' % eeprom.sint_read ( mem_addr=0x0B ) ) # 2 bytes length

print("Read unsigned short int. Expected 65123")
print( '   %i' % eeprom.usint_read ( mem_addr=0x0D ) ) # 2 bytes length

print("Read signed int. Expected 1000222") # 4 bytes length
print( '   %i' % eeprom.int_read ( mem_addr=0x0F ) ) # 4 bytes length

print("Read float. Expected 3.141592") # 4 bytes length
print( '   %f' % eeprom.float_read ( mem_addr=0x13 ) ) # 4 bytes length

print( "Read string. Expected Belgium")
print( '   %s' % eeprom.string_read( mem_addr=0x17 ) ) # storage_length was 20. So 21 bytes.

print( "Read boolean. Expected True")
# Address = 0x17 + 1 (storage_length) + 20 (storage_space)
a_bool = True if eeprom.byte_read( mem_addr=0x2c ) > 0 else False
print( '   %s' % a_bool )

Aucun commentaire