class I2C – ein serielles Zweidraht-Protokoll

I2C ist ein Zweidraht-Protokoll zur Kommunikation zwischen Geräten. Auf physikalischer Ebene besteht es aus 2 Leitungen: SCL und SDA, also der Takt- bzw. der Datenleitung.

I2C-Objekte werden mit einem bestimmten Bus verbunden erstellt. Sie können bei der Erstellung oder auch später initialisiert werden.

Das Ausgeben des I2C-Objekts liefert Informationen über seine Konfiguration.

Es existieren sowohl Hardware- als auch Software-I2C-Implementierungen über die Klassen I2C und SoftI2C. Hardware-I2C nutzt die zugrundeliegende Hardware-Unterstützung des Systems, um die Lese-/Schreibvorgänge durchzuführen, und ist üblicherweise effizient und schnell, kann aber Einschränkungen dahingehend haben, welche Pins verwendet werden können. Software-I2C wird per Bit-Banging implementiert und kann an jedem Pin verwendet werden, ist aber nicht so effizient. Diese Klassen bieten dieselben Methoden und unterscheiden sich hauptsächlich in der Art, wie sie konstruiert werden.

Bemerkung

Der I2C-Bus benötigt für seinen Betrieb eine Pull-up-Beschaltung an SDA und SCL. Üblicherweise sind dies Widerstände im Bereich von 1 - 10 kOhm, die jeweils von SDA/SCL nach Vcc geschaltet werden. Ohne diese ist das Verhalten undefiniert und kann von Blockieren über unerwartetes Watchdog-Reset bis hin zu schlicht falschen Werten reichen. Oft ist diese Pull-up-Beschaltung bereits auf der MCU-Platine oder auf Sensor-Breakout-Platinen integriert, doch eine Regel gibt es dafür nicht. Bitte prüfen Sie dies daher im Problemfall. Siehe auch diesen hervorragenden Lernleitfaden von Adafruit zur I2C-Verdrahtung.

Beispielverwendung:

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

Konstruktoren

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

Konstruiert ein neues I2C-Objekt mit den folgenden Parametern und gibt es zurück:

  • id identifiziert ein bestimmtes I2C-Peripheriegerät. Die zulässigen Werte hängen vom jeweiligen Port/Board ab

  • scl sollte ein Pin-Objekt sein, das den für SCL zu verwendenden Pin angibt.

  • sda sollte ein Pin-Objekt sein, das den für SDA zu verwendenden Pin angibt.

  • freq sollte eine Ganzzahl sein, die die maximale Frequenz für SCL festlegt.

  • timeout ist die maximale Zeit in Mikrosekunden, die für I2C-Transaktionen erlaubt ist. Dieser Parameter ist auf einigen Ports nicht zulässig.

Beachten Sie, dass einige Ports/Boards Standardwerte für scl und sda haben, die in diesem Konstruktor geändert werden können. Andere haben feste Werte für scl und sda, die nicht geändert werden können.

Allgemeine Methoden

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

Initialisiert den I2C-Bus mit den angegebenen Argumenten:

  • scl ist ein Pin-Objekt für die SCL-Leitung

  • sda ist ein Pin-Objekt für die SDA-Leitung

  • freq ist die SCL-Taktrate

Im Fall von Hardware-I2C kann die tatsächliche Taktfrequenz niedriger sein als die angeforderte Frequenz. Dies hängt von der Plattform-Hardware ab. Die tatsächliche Rate lässt sich durch Ausgeben des I2C-Objekts ermitteln.

scan() List[int]

Scannt alle I2C-Adressen zwischen 0x08 und 0x77 einschließlich und gibt eine Liste derjenigen zurück, die antworten. Ein Gerät antwortet, wenn es die SDA-Leitung auf Low zieht, nachdem seine Adresse (einschließlich eines Schreib-Bits) auf den Bus gesendet wurde.

Primitive I2C-Operationen

Die folgenden Methoden implementieren die primitiven I2C-Controller-Busoperationen und können kombiniert werden, um jede beliebige I2C-Transaktion zu bilden. Sie werden bereitgestellt, falls Sie mehr Kontrolle über den Bus benötigen; andernfalls können die Standardmethoden (siehe unten) verwendet werden.

Diese Methoden sind nur in der Klasse SoftI2C verfügbar.

start() None

Erzeugt eine START-Bedingung auf dem Bus (SDA wechselt auf Low, während SCL High ist).

stop() None

Erzeugt eine STOP-Bedingung auf dem Bus (SDA wechselt auf High, während SCL High ist).

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

Liest Bytes vom Bus und speichert sie in buf. Die Anzahl der gelesenen Bytes entspricht der Länge von buf. Nach dem Empfang aller Bytes außer dem letzten wird ein ACK auf den Bus gesendet. Nachdem das letzte Byte empfangen wurde, wird, falls nack true ist, ein NACK gesendet, andernfalls ein ACK (in diesem Fall geht das Peripheriegerät davon aus, dass in einem späteren Aufruf weitere Bytes gelesen werden).

write(buf: bytes) int

Schreibt die Bytes aus buf auf den Bus. Prüft, ob nach jedem Byte ein ACK empfangen wird, und stoppt die Übertragung der verbleibenden Bytes, falls ein NACK empfangen wird. Die Funktion gibt die Anzahl der empfangenen ACKs zurück.

Standard-Busoperationen

Die folgenden Methoden implementieren die Standard-I2C-Controller-Lese- und Schreiboperationen, die auf ein bestimmtes Peripheriegerät abzielen.

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

Liest nbytes von dem durch addr angegebenen Peripheriegerät. Wenn stop true ist, wird am Ende der Übertragung eine STOP-Bedingung erzeugt. Gibt ein bytes-Objekt mit den gelesenen Daten zurück.

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

Liest von dem durch addr angegebenen Peripheriegerät in buf. Die Anzahl der gelesenen Bytes entspricht der Länge von buf. Wenn stop true ist, wird am Ende der Übertragung eine STOP-Bedingung erzeugt.

Die Methode gibt None zurück.

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

Schreibt die Bytes aus buf an das durch addr angegebene Peripheriegerät. Wenn nach dem Schreiben eines Bytes aus buf ein NACK empfangen wird, werden die verbleibenden Bytes nicht gesendet. Wenn stop true ist, wird am Ende der Übertragung eine STOP-Bedingung erzeugt, selbst wenn ein NACK empfangen wird. Die Funktion gibt die Anzahl der empfangenen ACKs zurück.

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

Schreibt die in vector enthaltenen Bytes an das durch addr angegebene Peripheriegerät. vector sollte ein Tupel oder eine Liste von Objekten mit dem Buffer-Protokoll sein. Die addr wird einmal gesendet, und dann werden die Bytes aus jedem Objekt in vector nacheinander ausgegeben. Die Objekte in vector dürfen eine Länge von null Bytes haben, in welchem Fall sie nichts zur Ausgabe beitragen.

Wenn nach dem Schreiben eines Bytes aus einem der Objekte in vector ein NACK empfangen wird, werden die verbleibenden Bytes sowie alle verbleibenden Objekte nicht gesendet. Wenn stop true ist, wird am Ende der Übertragung eine STOP-Bedingung erzeugt, selbst wenn ein NACK empfangen wird. Die Funktion gibt die Anzahl der empfangenen ACKs zurück.

Speicheroperationen

Einige I2C-Geräte verhalten sich wie ein Speichergerät (oder ein Satz von Registern), das gelesen und beschrieben werden kann. In diesem Fall sind mit einer I2C-Transaktion zwei Adressen verbunden: die Peripherieadresse und die Speicheradresse. Die folgenden Methoden sind Komfortfunktionen für die Kommunikation mit solchen Geräten.

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

Liest nbytes von dem durch addr angegebenen Peripheriegerät, beginnend bei der durch memaddr angegebenen Speicheradresse. Das Argument addrsize gibt die Adressgröße in Bits an. Gibt ein bytes-Objekt mit den gelesenen Daten zurück.

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

Liest von dem durch addr angegebenen Peripheriegerät in buf, beginnend bei der durch memaddr angegebenen Speicheradresse. Die Anzahl der gelesenen Bytes entspricht der Länge von buf. Das Argument addrsize gibt die Adressgröße in Bits an.

Die Methode gibt None zurück.

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

Schreibt buf an das durch addr angegebene Peripheriegerät, beginnend bei der durch memaddr angegebenen Speicheradresse. Das Argument addrsize gibt die Adressgröße in Bits an.

Die Methode gibt None zurück.

class SoftI2C – ein softwareemulierter I2C-Bus

Die Klasse SoftI2C implementiert I2C durch Bit-Banging beliebiger GPIO-Pins. Sie bietet dieselbe Methodenoberfläche wie I2C zuzüglich der primitiven Low-Level-Busoperationen (start(), stop(), readinto(), write()) für Aufrufer, die nicht standardmäßige Transaktionen zusammenstellen müssen. Verwenden Sie sie, wenn die benötigten Pins nicht mit einem Hardware-I2C-Block verdrahtet sind, wenn Sie mehr Busse benötigen, als die Hardware bietet, oder um mit Geräten zu kommunizieren, die ungewöhnliche Sequenzen erfordern (zusätzliche Takte, wiederholte Starts nach Schreibvorgängen usw.).

Konstruktoren

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

Konstruiert einen Software-I2C-Bus, der von scl / sda angesteuert wird.

freq ist die angestrebte SCL-Taktrate in Hz (die tatsächliche Rate ist aufgrund des Overheads der Bit-Bang-Schleife typischerweise niedriger).

timeout ist die maximale Zeit in Mikrosekunden, die auf Clock-Stretching gewartet wird (SCL wird von einem anderen Gerät auf dem Bus auf Low gehalten); bei Ablauf wird ein OSError(ETIMEDOUT) ausgelöst.

Allgemeine Methoden

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

Initialisiert den Software-I2C-Bus mit den angegebenen Pins und der angegebenen Frequenz neu. Entspricht dem Konstruieren eines neuen SoftI2C auf demselben Objekt.

scan() List[int]

Scannt alle I2C-Adressen zwischen 0x08 und 0x77 einschließlich und gibt eine Liste derjenigen zurück, die geantwortet haben.

Primitive I2C-Operationen

Die folgenden Methoden implementieren die primitiven I2C-Controller-Busoperationen und können kombiniert werden, um jede beliebige I2C-Transaktion zu bilden. Sie sind ausschließlich für SoftI2C verfügbar – die Hardware-Klasse I2C stellt sie nicht bereit.

start() None

Erzeugt eine START-Bedingung auf dem Bus (SDA wechselt auf Low, während SCL High ist).

stop() None

Erzeugt eine STOP-Bedingung auf dem Bus (SDA wechselt auf High, während SCL High ist).

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

Liest Bytes vom Bus in buf. Es werden len(buf) Bytes gelesen; nach jedem Byte außer dem letzten wird ein ACK gesendet. Nach dem letzten Byte sendet nack=True (der Standard) ein NACK, um die Übertragung zu beenden; nack=False sendet ein ACK, sodass das Gerät für ein nachfolgendes readinto() ausgewählt bleibt.

write(buf: bytes) int

Schreibt buf auf den Bus und prüft nach jedem Byte auf ACK. Die Übertragung stoppt beim ersten NACK. Gibt die Anzahl der empfangenen ACKs zurück.

Standard-Busoperationen

Die folgenden Methoden implementieren die Standard-I2C-Controller-Lese- und Schreiboperationen, die auf ein bestimmtes Peripheriegerät abzielen.

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

Liest nbytes von dem Gerät mit der 7-Bit-Adresse addr. Wenn stop true ist, wird am Ende der Übertragung eine STOP-Bedingung erzeugt.

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

Liest len(buf) Bytes von dem Gerät mit der Adresse addr in buf. Wenn stop true ist, wird am Ende der Übertragung eine STOP-Bedingung erzeugt.

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

Schreibt buf an das Gerät mit der Adresse addr. Die Übertragung stoppt beim ersten NACK. Wenn stop true ist, wird am Ende der Übertragung immer eine STOP-Bedingung erzeugt (auch bei einem frühen NACK). Gibt die Anzahl der empfangenen ACKs zurück.

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

Schreibt die Verkettung der Puffer in vector als eine einzige Transaktion an das Gerät mit der Adresse addr. Leere Puffer werden ignoriert. Verhält sich bezüglich der stop-Semantik und des Rückgabewerts wie writeto().

Speicheroperationen

Einige I2C-Geräte verhalten sich wie ein Speichergerät (oder ein Satz von Registern), das gelesen und beschrieben werden kann. In diesem Fall sind mit einer I2C-Transaktion zwei Adressen verbunden: die Peripherieadresse und die Speicheradresse. Die folgenden Methoden sind Komforthelfer für die Kommunikation mit solchen Geräten.

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

Liest nbytes von dem Gerät mit der Adresse addr, beginnend beim Register memaddr. addrsize ist die Breite der Registeradresse in Bits (typischerweise 8 oder 16).

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

Liest von dem Gerät mit der Adresse addr in buf, beginnend beim Register memaddr.

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

Schreibt buf an das Gerät mit der Adresse addr, beginnend beim Register memaddr.