3.24. I2C у коді¶
machine.I2C обгортає апаратний контролер I2C. Створіть екземпляр із ідентифікатором шини та необов’язковою тактовою частотою:
from machine import I2C
i2c = I2C(2, freq=400_000)
id вибирає, який апаратний блок I2C використовувати; доступні номери та виводи SDA / SCL, на які вони відображаються, залежать від плати (дивіться Плати OpenMV). freq — тактова частота SCL: 100_000 — стандартний режим, 400_000 — поширена «швидка» швидкість, яку підтримує більшість датчиків.
3.24.1. Сканування шини¶
scan() проходить по 7-бітному адресному простору та повертає список усіх пристроїв, які підтвердили свою адресу:
>>> i2c.scan()
[0x40, 0x68]
Це перше, що слід зробити після підключення нової шини. Якщо пристрій присутній у списку, монтаж і підтягуючі резистори в порядку. Якщо список порожній (або коротший за очікуваний), перевіряйте підтягуючі резистори, монтаж або виводи адреси пристрою, перш ніж підозрювати код.
3.24.2. Шаблон з відображенням регістрів¶
Більшість датчиків I2C представляють свій стан у вигляді набору внутрішніх регістрів: прапори статусу за одною адресою, байти вимірювань за іншою, конфігурація за третьою. Бібліотека має два зручних методи, що безпосередньо відповідають шаблону «адресувати пристрій, вказати регістр, потім читати або записувати дані».
Читання регістра:
addr = 0x68
reg = 0x3B
data = i2c.readfrom_mem(addr, reg, 6)
# `data` is 6 bytes starting at register 0x3B
Цей єдиний виклик видає START, надсилає адресу пристрою з бітом запису, надсилає адресу регістра (повторний старт), повторно надсилає адресу з бітом читання, зчитує 6 байт, відправляє NACK на останньому і STOP. Пристрій отримує стандартну послідовність «читання з регістра»; скрипт бачить один виклик Python.
Запис до регістра:
i2c.writeto_mem(addr, 0x6B, b"\x00")
Ця послідовність є дзеркальним відображенням попередньої: START, адреса + запис, регістр, байт даних, STOP.
Для пристроїв, у яких адреса регістра займає два байти (деякі EEPROM і пам’яті з 16-бітною адресацією), передайте addrsize=16:
data = i2c.readfrom_mem(addr, 0x0100, 32, addrsize=16)
3.24.3. Читання датчика¶
Короткий цикл, що зчитує ідентифікатор WHO_AM_I та вісь X акселерометра на типовому датчику MEMS:
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() повертає об’єкт bytes, тому читання WHO_AM_I індексує перший (і єдиний) байт через [0]. Читання акселерометра дає два «сирих» байти, які потрібно зібрати в одне знакове 16-бітне значення; формат struct ">h" робить це як знакове значення з порядком байт «big-endian» — порядком байт, який використовує більшість датчиків I2C, протилежним до little-endian, поширеного на UART-посиланнях. struct.unpack() завжди повертає кортеж (один елемент на символ формату), тому [0] витягує єдине значення.
3.24.4. Низькорівневий доступ¶
Для пристроїв, які не дотримуються шаблону з відображенням регістрів, два низькорівневих методи дають прямий контроль над фазою адресації та напрямком даних:
i2c.writeto(addr, b"\x01\x02\x03") # write 3 bytes to addr
data = i2c.readfrom(addr, 4) # read 4 bytes from addr
Вони корисні для пристроїв з потоковими інтерфейсами команд (дисплеї, деякі годинники реального часу), де зручні допоміжні методи з регістрами не зовсім підходять.
3.24.5. Програмна реалізація, коли апаратний блок недоступний¶
machine.SoftI2C надає той самий API на довільних виводах GPIO, програмно реалізуючи протокол:
from machine import SoftI2C, Pin
i2c = SoftI2C(scl=Pin("P5"), sda=Pin("P4"), freq=100_000)
Використовуйте її, коли пристрій потрібно підключити до виводів, не пов’язаних з апаратним блоком I2C, або коли всі апаратні блоки вже зайняті. Програмний цикл працює повільніше за апаратний I2C, і ЦП залишається зайнятим протягом усієї транзакції, але API ідентичний I2C, тому наявний код перемикається лише зміною конструктора.