3.24. I2C în cod

machine.I2C încapsulează un controler I2C hardware. Construiți unul cu id-ul magistralei și o frecvență de ceas opțională:

from machine import I2C

i2c = I2C(2, freq=400_000)

id selectează ce bloc I2C hardware se folosește; numerele disponibile și pinii SDA / SCL la care se mapează depind de placă (consultați Plăci OpenMV). freq este rata ceasului SCL – 100_000 este modul standard, 400_000 este viteza „rapidă” obișnuită pe care o acceptă majoritatea senzorilor.

3.24.1. Scanarea magistralei

scan() parcurge spațiul de adrese pe 7 biți și returnează o listă cu fiecare dispozitiv care și-a confirmat adresa:

>>> i2c.scan()
[0x40, 0x68]

Acesta este primul lucru de încercat după cablarea unei magistrale noi. Dacă un dispozitiv este pe listă, cablarea și rezistoarele de ridicare sunt în regulă. Dacă lista este goală (sau mai scurtă decât se aștepta), suspectați rezistoarele de ridicare, cablarea sau pinii de adresă ai dispozitivului înainte de a suspecta codul.

3.24.2. Modelul memory-mapped

Majoritatea senzorilor I2C își expun starea ca un set de registre interne: indicatori de stare la o adresă, octeți de măsurare la alta, configurație la a treia. Biblioteca are două metode utilitare care se mapează direct pe modelul „adresează dispozitivul, indică un registru, apoi citește sau scrie date”.

Citirea unui registru:

addr = 0x68
reg  = 0x3B
data = i2c.readfrom_mem(addr, reg, 6)
# `data` is 6 bytes starting at register 0x3B

Acel singur apel emite un START, trimite adresa dispozitivului cu bitul de scriere, trimite adresa registrului (un repeated start), retrimite adresa cu bitul de citire, citește 6 octeți, emite NACK pentru ultimul și apoi STOP. Dispozitivul vede secvența standard „citire din registru”; scriptul vede un singur apel Python.

Scrierea într-un registru:

i2c.writeto_mem(addr, 0x6B, b"\x00")

Acea secvență este aceeași în imagine oglindită: START, adresă + scriere, registru, octet de date utile, STOP.

Pentru dispozitivele a căror adresă de registru are doi octeți lățime (unele EEPROM-uri și memorii adresate pe 16 biți), transmiteți addrsize=16:

data = i2c.readfrom_mem(addr, 0x0100, 32, addrsize=16)

3.24.3. Citirea unui senzor

O buclă scurtă care citește identificatorul WHO_AM_I și axa X a accelerometrului pe un senzor MEMS tipic:

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() returnează un obiect bytes, așa că citirea WHO_AM_I indexează primul (și singurul) octet cu [0]. Citirea accelerometrului constă în doi octeți bruți care trebuie asamblați într-o singură valoare cu semn pe 16 biți; formatul struct ">h" face acest lucru ca valoare cu semn big-endian – ordinea octeților folosită de majoritatea senzorilor I2C, opusă convenției little-endian comune pe legăturile UART. struct.unpack() returnează întotdeauna un tuplu (un element pentru fiecare caracter de format), așa că [0] extrage singura valoare.

3.24.4. Acces la nivel inferior

Pentru dispozitivele care nu urmează modelul de hartă de registre, două metode de nivel inferior oferă control direct asupra fazei de adresă și a direcției datelor:

i2c.writeto(addr, b"\x01\x02\x03")    # write 3 bytes to addr
data = i2c.readfrom(addr, 4)          # read 4 bytes from addr

Acestea sunt utile pentru dispozitivele cu interfețe de tip flux de comenzi (afișaje, unele ceasuri în timp real), unde metodele utilitare ale hărții de registre nu se potrivesc tocmai bine.

3.24.5. Bit-banging atunci când nu este disponibil niciun bloc hardware

machine.SoftI2C oferă aceeași API pe pini GPIO arbitrari, implementând protocolul în software prin bit-banging:

from machine import SoftI2C, Pin

i2c = SoftI2C(scl=Pin("P5"), sda=Pin("P4"), freq=100_000)

Folosiți-o atunci când dispozitivul trebuie să fie pe pini care nu sunt cablați la un bloc I2C hardware sau când toate blocurile hardware sunt în uz. Bucla de bit-banging este mai lentă decât I2C hardware, iar CPU-ul rămâne ocupat pe toată durata tranzacției, dar API-ul este identic cu cel al I2C, astfel încât codul existent este migrat schimbând doar constructorul.