3.24. I2C ในโค้ด¶
machine.I2C ห่อหุ้มตัวควบคุม I2C ฮาร์ดแวร์ สร้างด้วย id ของบัสและความถี่นาฬิกาที่ไม่บังคับ:
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]
นี่คือสิ่งแรกที่ควรลองหลังจากต่อสายบัสใหม่ ถ้าอุปกรณ์อยู่ในรายการ การต่อสายและ pull-up ก็ดี ถ้ารายการว่างเปล่า (หรือสั้นกว่าที่คาด) ให้สงสัย pull-up, การต่อสาย, หรือพินที่อยู่ของอุปกรณ์ก่อนที่จะสงสัยโค้ด
3.24.2. รูปแบบ memory-mapped¶
เซนเซอร์ I2C ส่วนใหญ่เปิดเผยสถานะของตัวเองเป็นชุด register ภายใน: ธงสถานะที่ที่อยู่หนึ่ง ไบต์การวัดที่ที่อยู่อื่น การกำหนดค่าที่ที่สาม ไลบรารีมีสองวิธีอำนวยความสะดวกที่แมปโดยตรงกับรูปแบบ "กำหนดที่อยู่อุปกรณ์, ชี้ไปที่ register, จากนั้นอ่านหรือเขียนข้อมูล"
การอ่าน register:
addr = 0x68
reg = 0x3B
data = i2c.readfrom_mem(addr, reg, 6)
# `data` is 6 bytes starting at register 0x3B
การเรียกเดียวนั้นออก START, ส่งที่อยู่อุปกรณ์พร้อมบิตเขียน, ส่งที่อยู่ register (repeated start), ส่งที่อยู่ใหม่พร้อมบิตอ่าน, อ่าน 6 ไบต์, NACK ไบต์สุดท้าย และ STOP อุปกรณ์เห็นลำดับ "อ่านจาก register" มาตรฐาน; สคริปต์เห็นการเรียก Python หนึ่งครั้ง
การเขียนไปยัง register:
i2c.writeto_mem(addr, 0x6B, b"\x00")
ลำดับนั้นเป็นภาพสะท้อน: START, ที่อยู่ + เขียน, register, ไบต์ข้อมูล, STOP
สำหรับอุปกรณ์ที่ที่อยู่ register กว้างสองไบต์ (EEPROM บางตัวและหน่วยความจำที่มีที่อยู่ 16 บิต) ให้ส่ง addrsize=16:
data = i2c.readfrom_mem(addr, 0x0100, 32, addrsize=16)
3.24.3. การอ่านเซนเซอร์¶
ลูปสั้นที่อ่านตัวระบุ WHO_AM_I และแกน X ของ accelerometer บนเซนเซอร์ 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() ส่งคืน object bytes ดังนั้นการอ่าน WHO_AM_I จะนำดัชนีแรก (และเดียว) ด้วย [0] การอ่าน accelerometer เป็นสองไบต์ดิบที่ต้องประกอบเป็นค่า signed 16 บิตค่าเดียว; รูปแบบ struct ">h" ทำสิ่งนั้นเป็น big-endian signed -- ลำดับ byte ที่เซนเซอร์ I2C ส่วนใหญ่ใช้ ตรงข้ามกับแบบแผน little-endian ที่ใช้ทั่วไปบนลิงค์ UART struct.unpack() ส่งคืน tuple เสมอ (หนึ่งองค์ประกอบต่อตัวอักษรรูปแบบ) ดังนั้น [0] ดึงออกค่าเดียว
3.24.4. การเข้าถึงระดับต่ำกว่า¶
สำหรับอุปกรณ์ที่ไม่ปฏิบัติตามรูปแบบ register-map สองวิธีระดับต่ำกว่าให้การควบคุมโดยตรงเหนือเฟส address และทิศทางข้อมูล:
i2c.writeto(addr, b"\x01\x02\x03") # write 3 bytes to addr
data = i2c.readfrom(addr, 4) # read 4 bytes from addr
สิ่งเหล่านี้มีประโยชน์สำหรับอุปกรณ์ที่มี interface แบบ command-stream (จอแสดงผล, นาฬิกาเวลาจริงบางตัว) ที่วิธีอำนวยความสะดวก register-map ไม่พอดีนัก
3.24.5. การ bit-bang เมื่อไม่มีบล็อกฮาร์ดแวร์ว่าง¶
machine.SoftI2C ให้ API เดียวกันบนพิน GPIO ใดก็ได้ โดย bit-bang โปรโตคอลในซอฟต์แวร์:
from machine import SoftI2C, Pin
i2c = SoftI2C(scl=Pin("P5"), sda=Pin("P4"), freq=100_000)
ใช้เมื่ออุปกรณ์ต้องอยู่บนพินที่ไม่ได้ต่อสายไปยังบล็อก I2C ฮาร์ดแวร์ หรือเมื่อบล็อกฮาร์ดแวร์ทุกตัวกำลังใช้งาน ลูป bit-bang ช้ากว่า I2C ฮาร์ดแวร์และ CPU ยังคงยุ่งอยู่ตลอดธุรกรรม แต่ API เหมือนกับ I2C ดังนั้นโค้ดที่มีอยู่สามารถสลับได้โดยเปลี่ยนเฉพาะ constructor