class I2C – 2선식 시리얼 프로토콜

I2C는 장치 간 통신을 위한 2선식 프로토콜입니다. 물리 계층에서는 2개의 선, 즉 클럭 선인 SCL과 데이터 선인 SDA로 구성됩니다.

I2C 객체는 특정 버스에 연결되어 생성됩니다. 생성 시점에 초기화할 수도 있고, 나중에 초기화할 수도 있습니다.

I2C 객체를 출력하면 해당 구성에 대한 정보를 확인할 수 있습니다.

I2CSoftI2C 클래스를 통해 하드웨어와 소프트웨어 두 가지 I2C 구현이 모두 제공됩니다. 하드웨어 I2C는 시스템의 기본 하드웨어 지원을 사용하여 읽기/쓰기를 수행하며 일반적으로 효율적이고 빠르지만, 사용할 수 있는 핀에 제약이 있을 수 있습니다. 소프트웨어 I2C는 비트뱅잉(bit-banging)으로 구현되며 어떤 핀에서도 사용할 수 있지만 효율은 떨어집니다. 이 클래스들은 동일한 메서드를 제공하며, 주로 생성 방식에서 차이가 납니다.

참고

I2C 버스가 동작하려면 SDA와 SCL 모두에 풀업 회로가 필요합니다. 보통 각 SDA/SCL에서 Vcc로 연결되는 1 - 10 kOhm 범위의 저항을 사용합니다. 이러한 풀업이 없으면 동작이 정의되지 않으며, 블로킹이나 예기치 않은 워치독 리셋부터 단순히 잘못된 값까지 다양한 현상이 나타날 수 있습니다. 이 풀업 회로는 MCU 보드나 센서 브레이크아웃 보드에 이미 내장된 경우가 많지만 항상 그런 것은 아닙니다. 따라서 문제가 발생하면 확인해 보시기 바랍니다. I2C 배선에 대한 Adafruit의 훌륭한 학습 가이드 도 참고하세요.

사용 예시:

from machine import I2C

i2c = I2C(freq=400000)          # create I2C peripheral at frequency of 400kHz
                                # depending on the port, extra parameters may be required
                                # to select the peripheral and/or pins to use

i2c.scan()                      # scan for peripherals, returning a list of 7-bit addresses

i2c.writeto(42, b'123')         # write 3 bytes to peripheral with 7-bit address 42
i2c.readfrom(42, 4)             # read 4 bytes from peripheral with 7-bit address 42

i2c.readfrom_mem(42, 8, 3)      # read 3 bytes from memory of peripheral 42,
                                #   starting at memory-address 8 in the peripheral
i2c.writeto_mem(42, 2, b'\x10') # write 1 byte to memory of peripheral 42
                                #   starting at address 2 in the peripheral

생성자

class machine.I2C(id: int, *, scl: Pin | None = None, sda: Pin | None = None, freq: int = 400000, timeout: int = 50000)

다음 매개변수를 사용하여 새 I2C 객체를 생성하고 반환합니다:

  • id 는 특정 I2C 주변장치를 식별합니다. 허용되는 값은 특정 포트/보드에 따라 다릅니다.

  • scl 은 SCL에 사용할 핀을 지정하는 핀 객체여야 합니다.

  • sda 는 SDA에 사용할 핀을 지정하는 핀 객체여야 합니다.

  • freq 는 SCL의 최대 주파수를 설정하는 정수여야 합니다.

  • timeout 은 I2C 트랜잭션에 허용되는 최대 시간(마이크로초 단위)입니다. 일부 포트에서는 이 매개변수가 허용되지 않습니다.

일부 포트/보드에는 sclsda 의 기본값이 있으며 이 생성자에서 변경할 수 있습니다. 다른 포트/보드에는 변경할 수 없는 고정된 sclsda 값이 있습니다.

일반 메서드

init(scl: Pin, sda: Pin, *, freq: int = 400000) None

주어진 인자로 I2C 버스를 초기화합니다:

  • scl 은 SCL 선에 대한 핀 객체입니다.

  • sda 는 SDA 선에 대한 핀 객체입니다.

  • freq 는 SCL 클럭 속도입니다.

하드웨어 I2C의 경우 실제 클럭 주파수가 요청한 주파수보다 낮을 수 있습니다. 이는 플랫폼 하드웨어에 따라 달라집니다. 실제 속도는 I2C 객체를 출력하여 확인할 수 있습니다.

scan() List[int]

0x08부터 0x77까지(양 끝 포함)의 모든 I2C 주소를 스캔하고 응답한 주소의 목록을 반환합니다. 장치는 버스에 자신의 주소(쓰기 비트 포함)가 전송된 후 SDA 선을 로우로 끌어내리면 응답한 것으로 간주됩니다.

기본 I2C 동작

다음 메서드들은 기본적인 I2C 컨트롤러 버스 동작을 구현하며, 이를 조합하여 임의의 I2C 트랜잭션을 만들 수 있습니다. 버스를 더 세밀하게 제어해야 할 때 제공되며, 그렇지 않으면 표준 메서드(아래 참조)를 사용할 수 있습니다.

이 메서드들은 SoftI2C 클래스에서만 사용할 수 있습니다.

start() None

버스에 START 조건을 생성합니다(SCL이 하이인 동안 SDA가 로우로 전환됨).

stop() None

버스에 STOP 조건을 생성합니다(SCL이 하이인 동안 SDA가 하이로 전환됨).

readinto(buf: bytearray, nack: bool = True, /) None

버스에서 바이트를 읽어 buf 에 저장합니다. 읽는 바이트 수는 buf 의 길이입니다. 마지막 바이트를 제외한 모든 바이트를 수신한 후에는 버스에 ACK가 전송됩니다. 마지막 바이트를 수신한 후, nack 이 참이면 NACK가 전송되고, 그렇지 않으면 ACK가 전송됩니다(이 경우 주변장치는 이후 호출에서 더 많은 바이트를 읽을 것으로 가정합니다).

write(buf: bytes) int

buf 의 바이트를 버스에 씁니다. 각 바이트 후에 ACK가 수신되는지 확인하고 NACK가 수신되면 나머지 바이트의 전송을 중단합니다. 이 함수는 수신된 ACK의 수를 반환합니다.

표준 버스 동작

다음 메서드들은 주어진 주변장치를 대상으로 하는 표준 I2C 컨트롤러 읽기 및 쓰기 동작을 구현합니다.

readfrom(addr: int, nbytes: int, stop: bool = True, /) bytes

addr 로 지정된 주변장치에서 nbytes 만큼 읽습니다. stop 이 참이면 전송 끝에 STOP 조건이 생성됩니다. 읽은 데이터를 담은 bytes 객체를 반환합니다.

readfrom_into(addr: int, buf: bytearray, stop: bool = True, /) None

addr 로 지정된 주변장치에서 buf 로 읽어 들입니다. 읽는 바이트 수는 buf 의 길이입니다. stop 이 참이면 전송 끝에 STOP 조건이 생성됩니다.

이 메서드는 None 을 반환합니다.

writeto(addr: int, buf: bytes, stop: bool = True, /) int

addr 로 지정된 주변장치에 buf 의 바이트를 씁니다. buf 의 한 바이트를 쓴 후 NACK가 수신되면 나머지 바이트는 전송되지 않습니다. stop 이 참이면 NACK가 수신되더라도 전송 끝에 STOP 조건이 생성됩니다. 이 함수는 수신된 ACK의 수를 반환합니다.

writevto(addr: int, vector: tuple | list, stop: bool = True, /) int

vector 에 담긴 바이트를 addr 로 지정된 주변장치에 씁니다. vector 는 버퍼 프로토콜을 가진 객체들의 튜플 또는 리스트여야 합니다. addr 은 한 번만 전송되고, 이어서 vector 의 각 객체의 바이트가 순차적으로 기록됩니다. vector 의 객체는 길이가 0바이트일 수 있으며, 이 경우 출력에 기여하지 않습니다.

vector 의 객체 중 하나의 바이트를 쓴 후 NACK가 수신되면 나머지 바이트와 남은 객체들은 전송되지 않습니다. stop 이 참이면 NACK가 수신되더라도 전송 끝에 STOP 조건이 생성됩니다. 이 함수는 수신된 ACK의 수를 반환합니다.

메모리 동작

일부 I2C 장치는 읽고 쓸 수 있는 메모리 장치(또는 레지스터 집합)처럼 동작합니다. 이 경우 I2C 트랜잭션에는 두 개의 주소, 즉 주변장치 주소와 메모리 주소가 연관됩니다. 다음 메서드들은 이러한 장치와 통신하기 위한 편의 함수입니다.

readfrom_mem(addr: int, memaddr: int, nbytes: int, *, addrsize: int = 8) bytes

memaddr 로 지정된 메모리 주소부터 시작하여 addr 로 지정된 주변장치에서 nbytes 만큼 읽습니다. addrsize 인자는 주소 크기를 비트 단위로 지정합니다. 읽은 데이터를 담은 bytes 객체를 반환합니다.

readfrom_mem_into(addr: int, memaddr: int, buf: bytearray, *, addrsize: int = 8) None

memaddr 로 지정된 메모리 주소부터 시작하여 addr 로 지정된 주변장치에서 buf 로 읽어 들입니다. 읽는 바이트 수는 buf 의 길이입니다. addrsize 인자는 주소 크기를 비트 단위로 지정합니다.

이 메서드는 None 을 반환합니다.

writeto_mem(addr: int, memaddr: int, buf: bytes, *, addrsize: int = 8) None

memaddr 로 지정된 메모리 주소부터 시작하여 addr 로 지정된 주변장치에 buf 를 씁니다. addrsize 인자는 주소 크기를 비트 단위로 지정합니다.

이 메서드는 None 을 반환합니다.

class SoftI2C – 소프트웨어로 에뮬레이션된 I2C 버스

SoftI2C 클래스는 임의의 GPIO 핀을 비트뱅잉하여 I2C를 구현합니다. I2C 와 동일한 메서드 인터페이스에 더해, 비표준 트랜잭션을 조립해야 하는 호출자를 위한 저수준 기본 버스 동작(start(), stop(), readinto(), write())을 노출합니다. 필요한 핀이 하드웨어 I2C 블록에 연결되어 있지 않을 때, 하드웨어가 제공하는 것보다 더 많은 버스가 필요할 때, 또는 비정상적인 시퀀스(추가 클럭, 쓰기 후 반복 시작 등)를 요구하는 장치와 통신할 때 사용하세요.

생성자

class machine.SoftI2C(scl: Pin, sda: Pin, *, freq: int = 400000, timeout: int = 50000)

scl / sda 로 구동되는 소프트웨어 I2C 버스를 생성합니다.

freq 는 목표 SCL 클럭 속도(Hz 단위)입니다(비트뱅 루프 오버헤드 때문에 실제 속도는 일반적으로 더 낮습니다).

timeout 은 클럭 스트레칭(버스의 다른 장치가 SCL을 로우로 유지하는 것)을 기다리는 최대 시간(마이크로초 단위)입니다. 만료되면 OSError(ETIMEDOUT) 이 발생합니다.

일반 메서드

init(scl: Pin, sda: Pin, *, freq: int = 400000) None

주어진 핀과 주파수로 소프트웨어 I2C 버스를 다시 초기화합니다. 같은 객체에 대해 새 SoftI2C 를 생성하는 것과 동등합니다.

scan() List[int]

0x08부터 0x77까지(양 끝 포함)의 모든 I2C 주소를 스캔하고 응답한 주소의 목록을 반환합니다.

기본 I2C 동작

다음 메서드들은 기본적인 I2C 컨트롤러 버스 동작을 구현하며, 이를 조합하여 임의의 I2C 트랜잭션을 만들 수 있습니다. 이들은 SoftI2C 전용이며, 하드웨어 I2C 클래스는 이를 노출하지 않습니다.

start() None

버스에 START 조건을 생성합니다(SCL이 하이인 동안 SDA가 로우로 전환됨).

stop() None

버스에 STOP 조건을 생성합니다(SCL이 하이인 동안 SDA가 하이로 전환됨).

readinto(buf: bytearray, nack: bool = True, /) None

버스에서 buf 로 바이트를 읽습니다. len(buf) 바이트가 읽히며, 마지막을 제외한 모든 바이트 후에 ACK가 전송됩니다. 마지막 바이트 후 nack=True (기본값)는 전송을 종료하기 위해 NACK를 전송하고, nack=False 는 ACK를 전송하여 이후의 readinto() 를 위해 장치를 선택된 상태로 유지합니다.

write(buf: bytes) int

buf 를 버스에 쓰며, 모든 바이트 후에 ACK를 확인합니다. 첫 번째 NACK에서 전송이 중단됩니다. 수신된 ACK의 수를 반환합니다.

표준 버스 동작

다음 메서드들은 주어진 주변장치를 대상으로 하는 표준 I2C 컨트롤러 읽기 및 쓰기 동작을 구현합니다.

readfrom(addr: int, nbytes: int, stop: bool = True, /) bytes

7비트 주소 addr 의 장치에서 nbytes 만큼 읽습니다. stop 이 참이면 전송 끝에 STOP 조건이 생성됩니다.

readfrom_into(addr: int, buf: bytearray, stop: bool = True, /) None

addr 의 장치에서 len(buf) 바이트를 buf 로 읽습니다. stop 이 참이면 전송 끝에 STOP 조건이 생성됩니다.

writeto(addr: int, buf: bytes, stop: bool = True, /) int

addr 의 장치에 buf 를 씁니다. 첫 번째 NACK에서 전송이 중단됩니다. stop 이 참이면 전송 끝에 항상 STOP 조건이 생성됩니다(이른 NACK가 발생하더라도). 수신된 ACK의 수를 반환합니다.

writevto(addr: int, vector: tuple | list, stop: bool = True, /) int

vector 에 담긴 버퍼들을 이어 붙인 것을 단일 트랜잭션으로 addr 의 장치에 씁니다. 빈 버퍼는 무시됩니다. stop 의 의미와 반환값은 writeto() 와 동일하게 동작합니다.

메모리 동작

일부 I2C 장치는 읽고 쓸 수 있는 메모리 장치(또는 레지스터 집합)처럼 동작합니다. 이 경우 I2C 트랜잭션에는 두 개의 주소, 즉 주변장치 주소와 메모리 주소가 연관됩니다. 다음 메서드들은 이러한 장치와 통신하기 위한 편의 헬퍼입니다.

readfrom_mem(addr: int, memaddr: int, nbytes: int, *, addrsize: int = 8) bytes

addr 의 장치에서 레지스터 memaddr 부터 시작하여 nbytes 만큼 읽습니다. addrsize 는 레지스터 주소의 너비(비트 단위, 일반적으로 8 또는 16)입니다.

readfrom_mem_into(addr: int, memaddr: int, buf: bytearray, *, addrsize: int = 8) None

addr 의 장치에서 레지스터 memaddr 부터 시작하여 buf 로 읽어 들입니다.

writeto_mem(addr: int, memaddr: int, buf: bytes, *, addrsize: int = 8) None

addr 의 장치에 레지스터 memaddr 부터 시작하여 buf 를 씁니다.