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]
이것은 새 버스를 배선한 후 가장 먼저 시도해 볼 일입니다. 장치가 목록에 있으면 배선과 풀업이 정상입니다. 목록이 비어 있거나(또는 예상보다 짧으면) 코드를 의심하기 전에 풀업, 배선, 또는 장치의 주소 핀을 의심하세요.
3.24.2. 메모리 매핑 패턴#
대부분의 I2C 센서는 자신의 상태를 일련의 내부 레지스터로 노출합니다. 상태 플래그는 한 주소에, 측정 바이트는 다른 주소에, 구성은 또 다른 주소에 둡니다. 라이브러리에는 “장치를 주소 지정하고, 레지스터를 가리킨 다음, 데이터를 읽거나 쓴다”는 패턴에 직접 대응하는 두 가지 편의 메서드가 있습니다.
레지스터 읽기:
addr = 0x68
reg = 0x3B
data = i2c.readfrom_mem(addr, reg, 6)
# `data` is 6 bytes starting at register 0x3B
이 한 번의 호출은 START를 발행하고, 쓰기 비트와 함께 장치 주소를 보내고, 레지스터 주소를 보내고(반복 START), 읽기 비트와 함께 주소를 다시 보내고, 6바이트를 읽고, 마지막 바이트에 NACK를 보낸 뒤 STOP합니다. 장치는 표준 “레지스터에서 읽기” 시퀀스를 보고, 스크립트는 하나의 Python 호출을 봅니다.
레지스터에 쓰기:
i2c.writeto_mem(addr, 0x6B, b"\x00")
이 시퀀스는 거울에 비친 듯 동일합니다: START, 주소 + 쓰기, 레지스터, 페이로드 바이트, STOP.
레지스터 주소가 2바이트 폭인 장치(일부 EEPROM과 16비트 주소 지정 메모리)의 경우 addrsize=16을 전달합니다:
data = i2c.readfrom_mem(addr, 0x0100, 32, addrsize=16)
3.24.3. 센서 읽기#
일반적인 MEMS 센서에서 WHO_AM_I 식별자와 가속도계 X축을 읽는 짧은 루프:
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"가 이를 빅엔디언 부호 있는 값으로 처리하는데, 이는 대부분의 I2C 센서가 사용하는 바이트 순서로 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는 임의의 GPIO 핀에서 동일한 API를 제공하며, 프로토콜을 소프트웨어로 비트뱅잉합니다:
from machine import SoftI2C, Pin
i2c = SoftI2C(scl=Pin("P5"), sda=Pin("P4"), freq=100_000)
장치가 하드웨어 I2C 블록에 연결되지 않은 핀에 있어야 하거나, 모든 하드웨어 블록이 사용 중일 때 이를 사용하세요. 비트뱅 루프는 하드웨어 I2C보다 느리고 트랜잭션 내내 CPU가 바쁜 상태를 유지하지만, API가 I2C와 동일하므로 기존 코드는 생성자만 바꾸면 전환됩니다.