3.24. L’I2C en code¶
machine.I2C encapsule un contrôleur I2C matériel. Instanciez-en un avec l’identifiant du bus et une fréquence d’horloge facultative :
from machine import I2C
i2c = I2C(2, freq=400_000)
id sélectionne le bloc I2C matériel à utiliser ; les numéros disponibles et les broches SDA / SCL auxquelles ils correspondent dépendent de la carte (voir la Cartes OpenMV). freq est la fréquence d’horloge SCL – 100_000 correspond au mode standard, 400_000 à la vitesse « rapide » courante prise en charge par la plupart des capteurs.
3.24.1. Balayage du bus¶
scan() parcourt l’espace d’adressage sur 7 bits et renvoie une liste de tous les périphériques ayant acquitté leur adresse :
>>> i2c.scan()
[0x40, 0x68]
C’est la première chose à essayer après avoir câblé un nouveau bus. Si un périphérique figure dans la liste, le câblage et les résistances de tirage sont corrects. Si la liste est vide (ou plus courte que prévu), suspectez les résistances de tirage, le câblage ou les broches d’adresse du périphérique avant de suspecter le code.
3.24.2. Le schéma à mappage mémoire¶
La plupart des capteurs I2C exposent leur état sous la forme d’un ensemble de registres internes : indicateurs d’état à une adresse, octets de mesure à une autre, configuration à une troisième. La bibliothèque propose deux méthodes pratiques qui correspondent directement au schéma « adresser le périphérique, pointer un registre, puis lire ou écrire des données ».
Lecture d’un registre :
addr = 0x68
reg = 0x3B
data = i2c.readfrom_mem(addr, reg, 6)
# `data` is 6 bytes starting at register 0x3B
Cet unique appel émet un START, envoie l’adresse du périphérique avec le bit d’écriture, envoie l’adresse du registre (un redémarrage répété), renvoie l’adresse avec le bit de lecture, lit 6 octets, envoie un NACK sur le dernier, puis un STOP. Le périphérique voit la séquence standard de « lecture depuis un registre » ; le script ne voit qu’un seul appel Python.
Écriture dans un registre :
i2c.writeto_mem(addr, 0x6B, b"\x00")
Cette séquence est l’image miroir de la précédente : START, adresse + écriture, registre, octet de charge utile, STOP.
Pour les périphériques dont l’adresse de registre fait deux octets de large (certaines EEPROM et mémoires adressées sur 16 bits), passez addrsize=16 :
data = i2c.readfrom_mem(addr, 0x0100, 32, addrsize=16)
3.24.3. Lecture d’un capteur¶
Une courte boucle lisant l’identifiant WHO_AM_I et l’axe X de l’accéléromètre sur un capteur MEMS typique :
import time
import struct
from machine import I2C
i2c = I2C(2, freq=400_000)
ADDR = 0x68
WHO_AM_I = 0x75
ACCEL_X_HI = 0x3B
print("WHO_AM_I:", i2c.readfrom_mem(ADDR, WHO_AM_I, 1)[0])
while True:
raw = i2c.readfrom_mem(ADDR, ACCEL_X_HI, 2)
x = struct.unpack(">h", raw)[0]
print("accel X:", x)
time.sleep_ms(100)
readfrom_mem() renvoie un objet bytes, de sorte que la lecture WHO_AM_I indexe le premier (et unique) octet avec [0]. La lecture de l’accéléromètre renvoie deux octets bruts qui doivent être assemblés en une seule valeur signée sur 16 bits ; le format ">h" de struct réalise cela en big-endian signé – l’ordre des octets utilisé par la plupart des capteurs I2C, à l’opposé de la convention little-endian courante sur les liaisons UART. struct.unpack() renvoie toujours un tuple (un élément par caractère de format), donc [0] en extrait l’unique valeur.
3.24.4. Accès de plus bas niveau¶
Pour les périphériques qui ne suivent pas le schéma de mappage de registres, deux méthodes de plus bas niveau offrent un contrôle direct sur la phase d’adressage et le sens des données :
i2c.writeto(addr, b"\x01\x02\x03") # write 3 bytes to addr
data = i2c.readfrom(addr, 4) # read 4 bytes from addr
Elles sont utiles pour les périphériques à interface par flux de commandes (afficheurs, certaines horloges temps réel) où les méthodes pratiques de mappage de registres ne conviennent pas tout à fait.
3.24.5. Bit-banging lorsqu’aucun bloc matériel n’est disponible¶
machine.SoftI2C fournit la même API sur des broches GPIO arbitraires, en générant le protocole en logiciel (bit-banging) :
from machine import SoftI2C, Pin
i2c = SoftI2C(scl=Pin("P5"), sda=Pin("P4"), freq=100_000)
Utilisez-le lorsque le périphérique doit être sur des broches non reliées à un bloc I2C matériel, ou lorsque tous les blocs matériels sont occupés. La boucle de bit-banging est plus lente que l’I2C matériel et le CPU reste occupé pendant toute la transaction, mais l’API est identique à celle de I2C, de sorte que le code existant bascule en ne modifiant que le constructeur.