3.24. I2C kódban¶
A machine.I2C egy hardveres I2C vezérlőt csomagol be. Hozz létre egyet a busz azonosítójával és egy opcionális órajelfrekvenciával:
from machine import I2C
i2c = I2C(2, freq=400_000)
Az id választja ki, melyik hardveres I2C blokkot használja; az elérhető számok és a hozzájuk leképezett SDA / SCL lábak a paneltől függnek (lásd a OpenMV kártyák oldalt). A freq az SCL órajelsebessége – a 100_000 a standard mód, a 400_000 a gyakori „fast” sebesség, amelyet a legtöbb érzékelő támogat.
3.24.1. A busz beolvasása¶
A scan() végigjárja a 7 bites címteret, és visszaad egy listát minden eszközről, amely nyugtázta a címét:
>>> i2c.scan()
[0x40, 0x68]
Ez az első dolog, amit ki kell próbálni egy új busz bekötése után. Ha egy eszköz rajta van a listán, a bekötés és a felhúzások rendben vannak. Ha a lista üres (vagy a vártnál rövidebb), gyanakodj a felhúzásokra, a bekötésre vagy az eszköz címlábaira, mielőtt a kódra gyanakodnál.
3.24.2. A memóriába-leképezett minta¶
A legtöbb I2C érzékelő belső regiszterek halmazaként teszi elérhetővé az állapotát: állapotjelzők az egyik címen, mérési bájtok egy másikon, konfiguráció egy harmadikon. A könyvtárnak két kényelmi metódusa van, amelyek közvetlenül leképeződnek a „címezd meg az eszközt, mutass egy regiszterre, majd olvass vagy írj adatot” mintára.
Egy regiszter olvasása:
addr = 0x68
reg = 0x3B
data = i2c.readfrom_mem(addr, reg, 6)
# `data` is 6 bytes starting at register 0x3B
Ez az egyetlen hívás kiad egy START-ot, elküldi az eszköz címét az írásbittel, elküldi a regisztercímet (egy ismételt start), újraküldi a címet az olvasásbittel, beolvas 6 bájtot, NACK-ozza az utolsót, és STOP-ol. Az eszköz a szabványos „olvasás regiszterből” szekvenciát látja; a szkript egyetlen Python-hívást lát.
Egy regiszterbe írás:
i2c.writeto_mem(addr, 0x6B, b"\x00")
Ez a szekvencia tükörképe ugyanannak: START, cím + írás, regiszter, hasznosadat-bájt, STOP.
Olyan eszközöknél, amelyeknek a regisztercíme két bájt széles (egyes EEPROM-ok és 16 bites címzésű memóriák), add át az addrsize=16 paramétert:
data = i2c.readfrom_mem(addr, 0x0100, 32, addrsize=16)
3.24.3. Egy érzékelő olvasása¶
Egy rövid ciklus, amely beolvassa a WHO_AM_I azonosítót és a gyorsulásmérő X tengelyét egy tipikus MEMS érzékelőn:
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)
A readfrom_mem() egy bytes objektumot ad vissza, így a WHO_AM_I olvasás a [0] indexszel éri el az első (és egyetlen) bájtot. A gyorsulásmérő-olvasás két nyers bájt, amelyeket egyetlen előjeles 16 bites értékké kell összeállítani; a struct ">h" formátuma ezt big-endian előjelesként végzi – ez a bájtsorrend, amelyet a legtöbb I2C érzékelő használ, ellentétben az UART-kapcsolatoknál szokásos little-endian konvencióval. A struct.unpack() mindig egy tuple-t ad vissza (formátumkarakterenként egy elemet), így a [0] húzza ki az egyetlen értéket.
3.24.4. Alacsonyabb szintű hozzáférés¶
Olyan eszközökhöz, amelyek nem követik a regisztertérkép-mintát, két alacsonyabb szintű metódus ad közvetlen vezérlést a címfázis és az adatirány felett:
i2c.writeto(addr, b"\x01\x02\x03") # write 3 bytes to addr
data = i2c.readfrom(addr, 4) # read 4 bytes from addr
Ezek hasznosak parancsfolyam-interfészű eszközöknél (kijelzők, egyes valós idejű órák), ahol a regisztertérkép kényelmi segédfüggvényei nem egészen illeszkednek.
3.24.5. Bit-banging, amikor nincs elérhető hardverblokk¶
A machine.SoftI2C ugyanazt az API-t biztosítja tetszőleges GPIO lábakon, szoftverben bit-bangelve a protokollt:
from machine import SoftI2C, Pin
i2c = SoftI2C(scl=Pin("P5"), sda=Pin("P4"), freq=100_000)
Akkor használd, amikor az eszköznek olyan lábakon kell lennie, amelyek nincsenek egy hardveres I2C blokkhoz bekötve, vagy amikor minden hardverblokk használatban van. A bit-bang ciklus lassabb a hardveres I2C-nél, és a CPU a teljes tranzakció alatt foglalt marad, de az API azonos a I2C-vel, így a meglévő kód csak a konstruktor megváltoztatásával vált át rá.