3.24. I2C trong code

machine.I2C bọc một bộ điều khiển I2C phần cứng. Khởi tạo một đối tượng với id bus và tần số đồng hồ tùy chọn:

from machine import I2C

i2c = I2C(2, freq=400_000)

id chọn block I2C phần cứng nào để sử dụng; các số khả dụng và các chân SDA / SCL mà chúng ánh xạ tới phụ thuộc vào bo mạch (xem Bo mạch OpenMV). freq là tốc độ đồng hồ SCL -- 100_000 là chế độ chuẩn, 400_000 là tốc độ "nhanh" phổ biến mà hầu hết các cảm biến hỗ trợ.

3.24.1. Quét bus

scan() duyệt qua không gian địa chỉ 7 bit và trả về danh sách mọi thiết bị đã ACK địa chỉ của nó:

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

Đây là điều đầu tiên cần thử sau khi đấu dây một bus mới. Nếu một thiết bị có trong danh sách, việc đấu dây và điện trở kéo lên đều tốt. Nếu danh sách trống (hoặc ngắn hơn mong đợi), hãy nghi ngờ điện trở kéo lên, dây dẫn, hoặc các chân địa chỉ của thiết bị trước khi nghi ngờ code.

3.24.2. Mẫu ánh xạ bộ nhớ

Hầu hết các cảm biến I2C hiển thị trạng thái của chúng dưới dạng một tập hợp các thanh ghi nội bộ: cờ trạng thái tại một địa chỉ, byte đo lường tại địa chỉ khác, cấu hình tại địa chỉ thứ ba. Thư viện có hai phương thức tiện lợi ánh xạ trực tiếp đến mẫu "địa chỉ thiết bị, trỏ đến thanh ghi, sau đó đọc hoặc ghi dữ liệu".

Đọc một thanh ghi:

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

Lệnh gọi đơn đó phát START, gửi địa chỉ thiết bị với bit ghi, gửi địa chỉ thanh ghi (repeated start), gửi lại địa chỉ với bit đọc, đọc 6 byte, NACK byte cuối cùng, và STOP. Thiết bị thấy chuỗi "đọc từ thanh ghi" chuẩn; tập lệnh thấy một lệnh gọi Python.

Ghi vào một thanh ghi:

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

Chuỗi đó giống như hình ảnh gương: START, địa chỉ + ghi, thanh ghi, byte payload, STOP.

Đối với các thiết bị có địa chỉ thanh ghi rộng hai byte (một số EEPROM và bộ nhớ địa chỉ 16 bit) hãy truyền addrsize=16:

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

3.24.3. Đọc một cảm biến

Một vòng lặp ngắn đọc nhận dạng WHO_AM_I và trục X gia tốc kế trên một cảm biến MEMS điển hình:

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() trả về một đối tượng bytes, vì vậy lệnh đọc WHO_AM_I lập chỉ mục byte đầu tiên (và duy nhất) bằng [0]. Lệnh đọc gia tốc kế là hai byte thô cần được ghép lại thành một giá trị 16 bit có dấu; định dạng struct ">h" thực hiện điều đó dưới dạng big-endian có dấu -- thứ tự byte mà hầu hết các cảm biến I2C sử dụng, ngược với quy ước little-endian phổ biến trên các liên kết UART. struct.unpack() luôn trả về một tuple (một phần tử cho mỗi ký tự định dạng), vì vậy [0] lấy ra giá trị duy nhất.

3.24.4. Truy cập cấp thấp hơn

Đối với các thiết bị không theo mẫu ánh xạ thanh ghi, hai phương thức cấp thấp hơn cho phép kiểm soát trực tiếp giai đoạn địa chỉ và hướng dữ liệu:

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

Chúng hữu ích cho các thiết bị có giao diện luồng lệnh (màn hình, một số đồng hồ thời gian thực) nơi các phương thức tiện lợi ánh xạ thanh ghi không hoàn toàn phù hợp.

3.24.5. Bit-banging khi không có block phần cứng nào khả dụng

machine.SoftI2C cung cấp cùng API trên các chân GPIO tùy ý, bit-banging giao thức trong phần mềm:

from machine import SoftI2C, Pin

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

Sử dụng nó khi thiết bị cần được kết nối trên các chân không được đấu dây đến một block I2C phần cứng, hoặc khi mọi block phần cứng đều đang được sử dụng. Vòng lặp bit-bang chậm hơn I2C phần cứng và CPU bận rộn trong suốt giao dịch, nhưng API giống hệt với I2C vì vậy code hiện có có thể chuyển sang chỉ bằng cách thay đổi constructor.