class I2C – un protocole série à deux fils

I2C est un protocole à deux fils permettant la communication entre périphériques. Au niveau physique, il se compose de 2 fils : SCL et SDA, respectivement la ligne d’horloge et la ligne de données.

Les objets I2C sont créés en étant rattachés à un bus spécifique. Ils peuvent être initialisés lors de leur création, ou initialisés ultérieurement.

L’affichage de l’objet I2C fournit des informations sur sa configuration.

Il existe des implémentations matérielles et logicielles de l’I2C via les classes I2C et SoftI2C. L’I2C matériel utilise le support matériel sous-jacent du système pour effectuer les lectures/écritures ; il est généralement efficace et rapide, mais peut imposer des restrictions sur les broches utilisables. L’I2C logiciel est implémenté par bit-banging et peut être utilisé sur n’importe quelle broche, mais il est moins efficace. Ces classes proposent les mêmes méthodes et diffèrent principalement par la façon dont elles sont construites.

Note

Le bus I2C nécessite un circuit de pull-up sur SDA et SCL pour fonctionner. Il s’agit généralement de résistances comprises entre 1 et 10 kOhm, reliant chaque ligne SDA/SCL à Vcc. Sans elles, le comportement est indéfini et peut aller du blocage, à une réinitialisation inattendue par chien de garde, jusqu’à de simples valeurs erronées. Souvent, ce circuit de pull-up est déjà intégré à la carte du MCU ou aux cartes d’extension de capteurs, mais ce n’est pas une règle. Veuillez donc le vérifier en cas de problème. Voir aussi cet excellent guide d’apprentissage d’Adafruit sur le câblage I2C.

Exemple d’utilisation

from machine import I2C

i2c = I2C(freq=400000)          # create I2C peripheral at frequency of 400kHz
                                # depending on the port, extra parameters may be required
                                # to select the peripheral and/or pins to use

i2c.scan()                      # scan for peripherals, returning a list of 7-bit addresses

i2c.writeto(42, b'123')         # write 3 bytes to peripheral with 7-bit address 42
i2c.readfrom(42, 4)             # read 4 bytes from peripheral with 7-bit address 42

i2c.readfrom_mem(42, 8, 3)      # read 3 bytes from memory of peripheral 42,
                                #   starting at memory-address 8 in the peripheral
i2c.writeto_mem(42, 2, b'\x10') # write 1 byte to memory of peripheral 42
                                #   starting at address 2 in the peripheral

Constructeurs

class machine.I2C(id: int, *, scl: Pin | None = None, sda: Pin | None = None, freq: int = 400000, timeout: int = 50000)

Construit et renvoie un nouvel objet I2C en utilisant les paramètres suivants :

  • id identifie un périphérique I2C particulier. Les valeurs autorisées dépendent du port/de la carte considérés

  • scl doit être un objet broche spécifiant la broche à utiliser pour SCL.

  • sda doit être un objet broche spécifiant la broche à utiliser pour SDA.

  • freq doit être un entier qui définit la fréquence maximale pour SCL.

  • timeout est le temps maximal en microsecondes autorisé pour les transactions I2C. Ce paramètre n’est pas autorisé sur certains ports.

Notez que certains ports/cartes auront des valeurs par défaut de scl et sda qui peuvent être modifiées dans ce constructeur. D’autres auront des valeurs fixes de scl et sda qui ne peuvent pas être modifiées.

Méthodes générales

init(scl: Pin, sda: Pin, *, freq: int = 400000) None

Initialise le bus I2C avec les arguments donnés :

  • scl est un objet broche pour la ligne SCL

  • sda est un objet broche pour la ligne SDA

  • freq est la fréquence d’horloge SCL

Dans le cas de l’I2C matériel, la fréquence d’horloge réelle peut être inférieure à la fréquence demandée. Cela dépend du matériel de la plateforme. La fréquence réelle peut être déterminée en affichant l’objet I2C.

scan() List[int]

Scanne toutes les adresses I2C comprises entre 0x08 et 0x77 inclus et renvoie une liste de celles qui répondent. Un périphérique répond s’il tire la ligne SDA à l’état bas après l’envoi de son adresse (y compris un bit d’écriture) sur le bus.

Opérations I2C primitives

Les méthodes suivantes implémentent les opérations de bus primitives du contrôleur I2C et peuvent être combinées pour réaliser n’importe quelle transaction I2C. Elles sont fournies si vous avez besoin de plus de contrôle sur le bus ; sinon, les méthodes standard (voir ci-dessous) peuvent être utilisées.

Ces méthodes ne sont disponibles que sur la classe SoftI2C.

start() None

Génère une condition START sur le bus (SDA passe à l’état bas pendant que SCL est à l’état haut).

stop() None

Génère une condition STOP sur le bus (SDA passe à l’état haut pendant que SCL est à l’état haut).

readinto(buf: bytearray, nack: bool = True, /) None

Lit des octets depuis le bus et les stocke dans buf. Le nombre d’octets lus correspond à la longueur de buf. Un ACK sera envoyé sur le bus après réception de tous les octets sauf le dernier. Après réception du dernier octet, si nack est vrai, un NACK sera envoyé ; sinon, un ACK sera envoyé (et dans ce cas, le périphérique suppose que d’autres octets seront lus lors d’un appel ultérieur).

write(buf: bytes) int

Écrit les octets de buf sur le bus. Vérifie qu’un ACK est reçu après chaque octet et arrête de transmettre les octets restants si un NACK est reçu. La fonction renvoie le nombre d’ACK reçus.

Opérations de bus standard

Les méthodes suivantes implémentent les opérations standard de lecture et d’écriture du contrôleur I2C qui ciblent un périphérique donné.

readfrom(addr: int, nbytes: int, stop: bool = True, /) bytes

Lit nbytes depuis le périphérique spécifié par addr. Si stop est vrai, une condition STOP est générée à la fin du transfert. Renvoie un objet bytes contenant les données lues.

readfrom_into(addr: int, buf: bytearray, stop: bool = True, /) None

Lit dans buf depuis le périphérique spécifié par addr. Le nombre d’octets lus correspond à la longueur de buf. Si stop est vrai, une condition STOP est générée à la fin du transfert.

La méthode renvoie None.

writeto(addr: int, buf: bytes, stop: bool = True, /) int

Écrit les octets de buf vers le périphérique spécifié par addr. Si un NACK est reçu après l’écriture d’un octet de buf, les octets restants ne sont pas envoyés. Si stop est vrai, une condition STOP est générée à la fin du transfert, même si un NACK est reçu. La fonction renvoie le nombre d’ACK reçus.

writevto(addr: int, vector: tuple | list, stop: bool = True, /) int

Écrit les octets contenus dans vector vers le périphérique spécifié par addr. vector doit être un tuple ou une liste d’objets prenant en charge le protocole tampon. L’adresse addr est envoyée une seule fois, puis les octets de chaque objet de vector sont écrits de façon séquentielle. Les objets de vector peuvent avoir une longueur de zéro octet, auquel cas ils ne contribuent pas à la sortie.

Si un NACK est reçu après l’écriture d’un octet de l’un des objets de vector, les octets restants ainsi que tous les objets restants ne sont pas envoyés. Si stop est vrai, une condition STOP est générée à la fin du transfert, même si un NACK est reçu. La fonction renvoie le nombre d’ACK reçus.

Opérations sur la mémoire

Certains périphériques I2C agissent comme un dispositif de mémoire (ou un ensemble de registres) accessible en lecture et en écriture. Dans ce cas, deux adresses sont associées à une transaction I2C : l’adresse du périphérique et l’adresse mémoire. Les méthodes suivantes sont des fonctions de commodité permettant de communiquer avec de tels périphériques.

readfrom_mem(addr: int, memaddr: int, nbytes: int, *, addrsize: int = 8) bytes

Lit nbytes depuis le périphérique spécifié par addr à partir de l’adresse mémoire spécifiée par memaddr. L’argument addrsize spécifie la taille de l’adresse en bits. Renvoie un objet bytes contenant les données lues.

readfrom_mem_into(addr: int, memaddr: int, buf: bytearray, *, addrsize: int = 8) None

Lit dans buf depuis le périphérique spécifié par addr à partir de l’adresse mémoire spécifiée par memaddr. Le nombre d’octets lus correspond à la longueur de buf. L’argument addrsize spécifie la taille de l’adresse en bits.

La méthode renvoie None.

writeto_mem(addr: int, memaddr: int, buf: bytes, *, addrsize: int = 8) None

Écrit buf vers le périphérique spécifié par addr à partir de l’adresse mémoire spécifiée par memaddr. L’argument addrsize spécifie la taille de l’adresse en bits.

La méthode renvoie None.

class SoftI2C – bus I2C émulé par logiciel

La classe SoftI2C implémente l’I2C par bit-banging de broches GPIO arbitraires. Elle expose la même interface de méthodes que I2C, ainsi que les opérations de bus primitives de bas niveau (start(), stop(), readinto(), write()) pour les appelants qui ont besoin d’assembler des transactions non standard. Utilisez-la lorsque les broches dont vous avez besoin ne sont pas reliées à un bloc I2C matériel, lorsque vous avez besoin de plus de bus que ce que le matériel fournit, ou pour dialoguer avec des périphériques qui exigent des séquences inhabituelles (horloges supplémentaires, redémarrages répétés après écritures, etc.).

Constructeurs

class machine.SoftI2C(scl: Pin, sda: Pin, *, freq: int = 400000, timeout: int = 50000)

Construit un bus I2C logiciel piloté par scl / sda.

freq est la fréquence d’horloge SCL cible en Hz (la fréquence réelle est généralement plus faible en raison de la surcharge de la boucle de bit-banging).

timeout est le temps maximal en microsecondes à attendre pour l’étirement d’horloge (SCL maintenu à l’état bas par un autre périphérique du bus) ; à son expiration, une exception OSError(ETIMEDOUT) est levée.

Méthodes générales

init(scl: Pin, sda: Pin, *, freq: int = 400000) None

Réinitialise le bus I2C logiciel avec les broches et la fréquence données. Équivalent à la construction d’un nouveau SoftI2C sur le même objet.

scan() List[int]

Scanne toutes les adresses I2C comprises entre 0x08 et 0x77 inclus et renvoie une liste de celles qui ont répondu.

Opérations I2C primitives

Les méthodes suivantes implémentent les opérations de bus primitives du contrôleur I2C et peuvent être combinées pour réaliser n’importe quelle transaction I2C. Elles sont propres à SoftI2C – la classe matérielle I2C ne les expose pas.

start() None

Génère une condition START sur le bus (SDA passe à l’état bas pendant que SCL est à l’état haut).

stop() None

Génère une condition STOP sur le bus (SDA passe à l’état haut pendant que SCL est à l’état haut).

readinto(buf: bytearray, nack: bool = True, /) None

Lit des octets depuis le bus dans buf. len(buf) octets sont lus ; un ACK est envoyé après chaque octet sauf le dernier. Après le dernier octet, nack=True (la valeur par défaut) envoie un NACK pour terminer le transfert ; nack=False envoie un ACK afin que le périphérique reste sélectionné pour un readinto() ultérieur.

write(buf: bytes) int

Écrit buf sur le bus, en vérifiant l’ACK après chaque octet. La transmission s’arrête au premier NACK. Renvoie le nombre d’ACK reçus.

Opérations de bus standard

Les méthodes suivantes implémentent les opérations standard de lecture et d’écriture du contrôleur I2C qui ciblent un périphérique donné.

readfrom(addr: int, nbytes: int, stop: bool = True, /) bytes

Lit nbytes depuis le périphérique à l’adresse 7 bits addr. Si stop est vrai, une condition STOP est générée à la fin du transfert.

readfrom_into(addr: int, buf: bytearray, stop: bool = True, /) None

Lit len(buf) octets depuis le périphérique à l’adresse addr dans buf. Si stop est vrai, une condition STOP est générée à la fin du transfert.

writeto(addr: int, buf: bytes, stop: bool = True, /) int

Écrit buf vers le périphérique à l’adresse addr. La transmission s’arrête au premier NACK. Si stop est vrai, une condition STOP est toujours générée à la fin du transfert (même en cas de NACK anticipé). Renvoie le nombre d’ACK reçus.

writevto(addr: int, vector: tuple | list, stop: bool = True, /) int

Écrit la concaténation des tampons de vector vers le périphérique à l’adresse addr en une seule transaction. Les tampons vides sont ignorés. Se comporte comme writeto() pour la sémantique de stop et la valeur de retour.

Opérations sur la mémoire

Certains périphériques I2C agissent comme un dispositif de mémoire (ou un ensemble de registres) accessible en lecture et en écriture. Dans ce cas, deux adresses sont associées à une transaction I2C : l’adresse du périphérique et l’adresse mémoire. Les méthodes suivantes sont des fonctions de commodité pour dialoguer avec de tels périphériques.

readfrom_mem(addr: int, memaddr: int, nbytes: int, *, addrsize: int = 8) bytes

Lit nbytes depuis le périphérique à l’adresse addr à partir du registre memaddr. addrsize est la largeur de l’adresse de registre en bits (généralement 8 ou 16).

readfrom_mem_into(addr: int, memaddr: int, buf: bytearray, *, addrsize: int = 8) None

Lit dans buf depuis le périphérique à l’adresse addr à partir du registre memaddr.

writeto_mem(addr: int, memaddr: int, buf: bytes, *, addrsize: int = 8) None

Écrit buf vers le périphérique à l’adresse addr à partir du registre memaddr.