I2C(Inter-Integrated Circuit)
I2C는 필립스에서 개발한 직렬 컴퓨터 버스이며 여러 개의 저속의 주변 기기를 연결하기 위해 사용된다. 시리얼 통신에 사용되는 UART 또는 SPI들보다 더 단순하여 심플한 회로 구성이 가능하다.
I2C 통신에 사용되는 2 개의 라인은 다음과 같은 기능을 수행한다.
- SDA (Serial DAta)
- 데이터의 직렬 전송에 사용된다.
- SCL (Serial CLock)
- 디바이스간 신호 동기화에 사용되는 클럭이다.
다음 그림은 I2C 버스에 연결되어 있는 디바이스들을 보여준다.
i2c 버스에 연결되는 디바이스 종류들은 수백~수천 가지로 매우 다양하다. 다음 그림은 몇 가지 종류의 디바이스들을 i2c 버스에 연결하여 사용할 때의 모습을 보여준다.
I2C Specification History
i2c 스펙은 다음과 같이 발전을 해왔다. 아래 노란 박스는 새 버전이 나올 때 마다 변경된 주요 항목이다.
- 필립스에서 처음 I2C가 개발되었을 때 다음과 같은 specification으로 동작하였다.
- 가장 널리 사용되는 스탠다드 모드가 100Khz로 사용되고, 저속 모드로 운영시에는 10Khz의 속도로 동작한다.
- 7비트 주소를 사용하여 총 128개의 주소를 사용할 수 있지만 그 중 16개는 reserve되어 있고, 나머지 112개를 사용할 수 있다. 따라서 하나의 I2C 버스에는 최대 112개의 디바이스가 연결될 수 있다.
- 그 후 추가 버전들은 속도가 증가되었고, 주소가 10비트로 확장되었다.
- 속도 증가
- Fast 모드에서 400Khz, High 모드에서 3.4Mhz로 속도가 증가됨을 알 수 있다.
- 주소 확대
- 10비트 모드가 사용되면서 최대 1008개의 디바이스를 연결할 수 있게 되었다.
- 속도 증가
- (1) 3.4Mhz High-speed-mode를 사용하기 위해서는 SDAH SLCH를 사용하는 디바이스를 사용하고 arbitration 및 클럭 동기화를 사용하지 않아야 한다.
- (2) 5Mhz Ultra Fast-speed-mode를 위해서는 USDA, USCL 라인이 추가되어야하고 Master → Slave 단방향만 지원된다.
멀티 마스터 지원
I2C는 다음 그림과 같이 1개의 버스에 2개 이상의 컨트롤러가 경쟁할 수 있다. 이 때 두 컨트롤러는 버스 Master 역할을 하기 위해 경쟁하게된다.
- Multi-master
- 2개 이상의 마스터가 메시지 손상 없이 동시에 버스를 제어하는 경우.
- Arbitration
- 2개 이상의 마스터가 버스를 동시에 통제할 때 오직 하나만이 허용되며 선택된 마스터의 메시지는 손상되지 않는다.
- Synchronization
- 두 개 이상의 디바이스의 클럭들을 동기화
I2C-Mux
다음과 같이 i2c 스위치 및 i2c 멀티플레서 두 종류로 나뉜다. 각각의 기능은 다음과 같다.
- i2c 스위치
- 1:N으로 구성 시 하위 채널 각각에 대해 select 및 deselect 할 수 있다. 즉 1개 이상을 select할 수 있다.
- i2c 멀티플렉서
- 1:N 또는 N:1로 구성 시 하위 채널 하나를 select 및 deselect 할 수 있다. select 시 한 번에 하나의 채널만 select할 수 있다.
제품에 따라 다음과 같은 기능이 지원될 수도 있다.
- HW reset 핀(option)
- i2c 스위치에서 외부 HW reset(option) 핀이 지원되는 경우 버스 리커버리를 수행할 수 있다.
- i2c 스위치 및 i2c 멀티플렉서 둘 다 내부에서 POR(Power On Reset) 기능을 지원한다.
- 인터럽트(option)
- 인터럽트를 받아 mux하여 전송할 수 있다.
- HW addr 설정 핀
- i2c-mux 디바이스를 하나의 버스에 여러 개를 사용할 경우 중복되지 않게 주소 하위 비트 몇 개(3~4)를 설정할 수 있는 핀이 패키지 외부로 제공된다.
다음 그림은 외부 reset, 인터럽트, HW 주소 설정핀 등이 지원되는 모습을 보여준다.
아래 그림은 2개의 i2c 컨트롤러와 그 중 하나에 연결된 4채널 i2c 멀티플렉서를 보여준다.
I2C 통신 프로토콜
2 개의 라인만을 사용하는 I2C 버스에서 I2C 디바이스들이 사용하는 통신 프로토콜을 알아보기로 하자.
Feature 리스트
다음 표와 같이 I2C 통신 프로토콜에 사용되는 Feature들이다. 디바이스들은 싱글 마스터, 멀티 마스터 또는 슬래이브 등의 각 구성(Configuration)에 따라 사용되는 Feature들이 다르다.
주요 Feature
i2c 버스에 어떠한 통신도 없는 경우 SDA 및 SCL 라인은 high 상태에 있다. 마스터나 디바이스가 통신을 시도할 때 전류가 흐르며 low 상태로 바뀐다.
Start & Stop condition
다음 그림과 같이 데이터를 송수신하기 위한 시작과 끝 조건을 알아본다.
- Start condition
- 클럭(SCL)이 high 시그널일 때 데이터(SDA)가 low 시그널로 변경될 때 Start condition으로 인식한다.
- 표기법: S
- Stop condition
- 클럭(SCL)이 high 시그널일 때 데이터(SDA)가 high 시그널로 변경될 때 Stop condition으로 인식한다.
- 표기법: P
i2c 데이터 통신 중에는 클럭이 high 시그널일 때에는 데이터(SDA) 시그널이 변경되면 안된다. 이러한 규칙으로 인해 클럭이 high인 상태에서는 데이터를 안정적으로 읽는 것을 보장한다.
그런데 아래 그림과 같이 클럭(SCL)이 high 상태일 때 데이터(SDA)가 변경되는 특수한 상태가 있다. 이러한 경우 데이터(SDA)가 변화한 방향에 따라 low 상태로 변경되면 Start condition이고 high 상태로 변경되면 Stop condition이라 한다.
1 바이트 전송 및 Acknowledge 처리
Start condition이 발생한 후 Stop condition이 나오기 전까지 디바이스들 간에 연속으로 바이트 단위의 통신을 할 수 있다. 1 바이트를 수신할 때마다 수신측 디바이스는 전송측을 위해 응답(Acknowledge) 비트를 전송해야 한다.
- 전송측 디바이스는 1 바이트를 MSB부터 전송한다.
- 수신측 디바이스는 1 바이트 수신 완료 후 1 비트의 Ack(low)를 전송한다.
- 표기법: A 또는 ACK
아래 그림을 보면 클럭(SCL)을 마스터가 주기적으로 보내고 있음을 알 수 있다. 데이터(SDA)는 전송 디바이스측에서 8비트를 보낸 후 9번째에 high 상태를 유지하고 있다. 그리고 수신 디바이스측에서 9번째 클럭에 해당하는 위치가 오기전에 ACK(low) 상태를 유지하는 것을 알 수 있다.
Synchronization
하나의 i2c 버스에서 두 개의 마스터가 동작할 때 각각의 클럭을 and 하여 동기화 시키는 것으로 하나의 클럭을 만들어낸다.
Arbtration
하나의 i2c 버스가 free 상태일 때 두 개의 마스터가 동시에 버스를 차지하려고 각각 Start condition을 보낸 경우이다. 그 이후로 운좋게(?) 서로 데이터를 동일하게 보내다가 어느 시점에서 각자의 전송 비트가 달라지게 되는데 그 시점에서 데이터(SDA)를 high 상태로 바꾼 마스터가 arbtration의 패자가 된다. 즉 데이터(SDA)를 low 상태로 물고 있는 마스터가 승자이다.
아래 그림은 두 개의 마스터가 거의 동시에 start condtion을 보냈고, 첫 번째 마스터가 데이터를 high로 변경하였음에도 SDA 라인은 low 상태를 유지하고 있다. 이 때 첫 번째 마스터가 자신이 arbitration의 패자가 되었음을 인식한다.
Clock Streach (Clock low extending)
i2c 마스터가 규칙적으로 clock을 발생하여야 하고 이에 대해 i2c 디바이스는 클럭에 맞춰 데이터를 송/수신해야한다. 그러나 요청을 받은 후에 클럭 타임을 초과하는 처리 시간이 필요한 경우 slave 디바이스가 데이터를 준비하기 위해 클럭을 강제로 low로 설정해버린다. 그런 후 데이터가 준비되어 응답을 할 수 있으면 다시 클럭을 풀어준다. 그런 경우 마스터가 계속 하여 클럭을 진행할 수 있다.
- 예) 주소 0x45를 갖는 EEPROM 디바이스에 offset 08번에 해당하는 데이터를 요청하였다. eeprom 장치에서 데이터를 가져오는 시간이 많이 걸릴 때가 있으므로 이러한 경우 잠시 클럭을 low로 묶어놓고 데이터가 다 준비된 경우 이를 응답하는 형식으로 처리를 하는 것을 clock streaching이라 한다.
- ACK를 수신하면 인터럽트가 발생하고 이 인터럽트의 처리가 완료되기 전까지 클럭(SCL) 시그널은 low 상태로 고정되어 대기한다.
- clock streach 기능을 사용하면 adapter가 I2C_FUNC_SLAVE 기능이 지원되어야 한다.
- 클럭을 최대 100us까지 지연시킬 수 있다.
- 두 가지 지연 방법이 있다.
- Periodic clock stretching by slave
- 주기적으로 매 번 클럭을 늘려 smbus 통신 속도를 10Khz로 줄일 수 있다.
- Random clock stretching by master or slave
- ISR 루틴에서 데이터 준비를 위해 지연을 시키는 경우이다.
- Periodic clock stretching by slave
다음 그림은 특정 디바이스로부터 무조건 1 바이트 값을 읽어오는 시나리오에서 디바이스가 clock streaching을 사용하는 유무에 대해 비교하였다.
- 보통 주소를 전송하고 요청 offset 및 size를 요청하는데 이렇게 하면 길어지므로 아래 그림에서는 간단히 주소와 데이터를 수신하는 것으로 보여준다.
- slave 디바이스가 scl을 low로 강제함으로써 마스터 디바이스가 계속하여 클럭을 발생시키지 못하게 홀드시킨다.
7-bit slave addresss (슬레이브 디바이스 선택 후 데이터 전송 또는 수신)
먼저 상대 디바이스를 선택한 후 데이터를 송/수신하는데 방법은 다음과 같다.
- 7bit 주소를 전송하는 것으로 디바이스를 선택한다. 그 후 전송측을 결정하는 R/W 비트를 전송한다.
- R/W 비트
- 1=마스터가 수신
- 0=마스터가 송신
- R/W 비트
- 송수신 비트가 0인 경우 마스터가 슬레이브에 8 비트 데이터를 전송한다.
- 송수신 비트가 1인 경우 슬레이브가 마스터에 8비트 데이터를 수신한다.
- 그 후 수신측에서 응답(Ack) 1 비트를 전송한다.
- 표기법: Addr + R/W
특별 주소(Special Addresses)
다음의 16개 주소들은 다음과 같이 특별한 목적으로 사용된다.
- R/W 비트 상태여부와 관계 없이 Address만을 보면 16개의 주소가 특별 주소로 사용된다.
- smbus의 경우 다음 주소의 디바이스 용도는 다음과 같다.
- 0001-000 (0x08)
- SMBus host
- SMBus를 이용할 때 host controller의 address이다.
- 디바이스는 이 주소로 Host Notify 이벤트를 보낼 수 있다.
- 0001-001 (0x09)
- Smart Battery Charger
- 0001-010
- Smart Battery Selector
- Smart Battery System Manager
- 0001-011
- Smart Battery
- 0001-100
- SMBus
- 0001-100 (0x0c)
- ARA(Alert Response Address)
- SMBus alert 응답 주소
- 0101-000 (0x28)
- ACCESS.bus host
- 0101-100 (0x2c)
- LCD Contrast Controller로 이전에 예약되어 있었지만 미래에 다시 설정할 예정이다.
- 0101-101 (0x2d)
- CCFL Backlight Driver로 이전에 예약되어 있었지만 미래에 다시 설정할 예정이다.
- 0110-111 (0x37)
- ACCESS.bus 디폴트 주소
- 1000-0XX (0x40 ~ 0x43)
- PCMCIA Socket Controller로 이전에 예약되어 있었지만 미래에 다시 설정할 예정이다.
- 1000-100 (0x44)
- VGA Graphics Controller로 이전에 예약되어 있었지만 미래에 다시 설정할 예정이다.
- 1001-0XX (0x48 ~ 0x4b)
- 제한없이 사용할 수 있는 주소
- 1100-001 (0x61)
- SMBus 디바이스의 디폴트 주소
- 0001-000 (0x08)
참고로 ACCESS.bus는 2-wire를 사용하여 외부 장치들을 연결하여 나갈 수 있도록 설계한 시도이다.
- 전원까지 4개의 pin을 사용한다.
- RS-232C 인터페이스를 대체할 목적이다.
10-bit addressing
10비트 주소를 사용하는 경우 다음과 같이 2 바이트를 사용하여 디바이스의 주소를 지정할 수 있다.
- 첫 번째 바이트
- 10비트 중 2비트 주소
- 두 번째 바이트
- 10비트 중 8비트 주소
General Call address
General call 주소가 사용되면 디바이스에 몇 가지 특별한 요청을 할 수 있다. 이 기능은 아래 Start BYTE와 같이 대부분의 시스템에서 잘 사용되지 않는 기능이다. 이 기능은 주로 i2c 디바이스들 중 EEPROM등에서 프로그래밍등이 요구될 때 사용되는 특수 기능이다.
- 첫 번째 바이트 구성을 General call(0x0)로 사용한다.
- 두 번째 바이트에 기능 코드 또는 마스터 주소를 부여할 수 있다.
- LSB가 0일 때 다음과 같은 기능 코드가 있다.
- 0x04: Write programmable part of slave address by hardware
- 0x06: Reset and write programmable part of slave address by hardware
- LSB가 1인 경우 마스터 주소를 사용하는 경우 Hardware Generall Call을 사용할 수 있다.
- LSB가 0일 때 다음과 같은 기능 코드가 있다.
Start BYTE
Srart BYTE feature는 0x00 데이터와 dummy NACK(1)을 전송한다. 두 개 이상의 호스트 컨트롤러가 버스 Master를 경쟁하고 있는 경우 stop condition을 detect한 경우 두 호스트 컨트롤러 중 하나가 버스 마스터가 된다. 특별한 경우 어떤 호스트 컨트롤러가 계속하여 버스를 점유하여 명령(reset, EEPROM 기록 등)을 처리해야 하는 경우 stop condition을 사용하지 않아야 한다. stop condition을 하는 경우 버스를 빼앗길 염려가 있다. 이러한 경우에 stop condition 대신 START BYTE를 보내고 다시 start condition을 사용하는 방식으로 반복하여 계속 명령을 보낼 수 있다. 잘 사용되지 않는 기능이지만 드믈게 특정 시스템에서 사용된다.
Device ID
디바이스에 대한 제조사 id와 파트 id 및 리비전 정보를 알아올 수 있다.
디바이스를 알아오는 경우 다음과 같은 제조사 id를 읽어낼 수 있다.
i2c 전송 포맷
i2c 전송 포맷은 다음과 같이 3가지 방법이 있다.
- 마스터 송신
- 슬레이브로 데이터를 송신 중에 방향이 바뀌지 않는다.
- 마스터 수신
- 마스터가 주소 송신 후 슬레이브로 부터 데이터를 수신한다. 역시 방향이 바뀌지 않는다.
- Combined 포맷
- 마스터와 슬레이브의 복합 송수신이 가능하다.
마스터 송신
다음 그림은 마스터 디바이스가 슬레이브 디바이스로 N 바이트를 전송하는 모습이다.
- 슬레이브가 바이트를 받을 때 마다 ACK를 보내는데 더 이상 받을 수 없는 상황인 경우 NACK를 보낸다.
마스터 수신
다음 그림은 마스터 디바이스가 슬레이브 디바이스로 1 바이트를 수신하는 모습이다.
- 마지막 바이트를 수신한 후 마스터가 NACK를 보내야한다.
Combination 전송
다음 그림은 마스터 디바이스가 슬레이브 디바이스로 여러 개의 메시지를 송신 또는 수신하는 모습이다.
- 마스터가 버스를 계속 점유한 채로 전송 및 수신들의 combination을 완료해야 하는 경우 사용한다.
- 메시지의 전송이 끝난 후 반복 시작하고자 할 때 중간에 repeated start condition 을 전송한다.
- 표기법: Sr
SMBus(System Management Bus)
필립스의 i2c를 기반으로 동작하도록 인텔과 듀라셀이 1994년 정의한 버스이다. 특징은 다음과 같다.
- 주로 PC 마더보드에 연결된 전력 제어 장치(power on/off) 및 온도 센서등이 smbus에 연결되어 사용된다.
- 참고: System Management Bus (SMBus) Specification Version 2.0 August 3, 2000 | smbus.org – 다운로드 pdf
SMBus가 I2C와의 다른 점
타이밍
- Speed
- smbus
- 10~100Khz를 사용한다.
- 실제 대부분의 구현은 50~100Khz를 사용한다.
- PMBus로 발전되면서 400Khz 속도도 사용할 수 있다.
- 10~100Khz를 사용한다.
- i2c
- 최저 속도는 100Khz이다.
- smbus
- 클럭(scl) 라인 low 타임아웃
- smbus
- 타임아웃을 설정할 수 있다.
- Slave 디바이스의 경우 최대 25ms 까지 low 시간을 연장할 수 있다. (Clock Streaching)
- Master 디바이스의 경우 최대 10ms 까지 low 시간을 연장할 수 있다. (Clock Streaching)
- i2c
- 타임아웃을 설정할 수 없다.
- smbus
- 데이터(sda) 라인 홀드 시간
- smbus
- 최대 300ns까지 홀드 시킬 수 있다.
- i2c
- 홀드할 수 없다.
- smbus
- Power-On-Reset 후 디바이스 준비 시간
- smbus
- 최대 0.5초까지 대기해야 한다.
- smbus
ACK 및 NACK
- addr 전송 후 ACK 용도
- smbus
- 탈부착 smbus 디바이스가 버스에 연결되었는지 여부를 detect 하는 비트 용도로 항상 ACK를 요구한다.
- 슬레이브 디바이스는 자신의 주소(Addr) 비트를 인식하면 ACK를 회신한다.
- S | Addr + R/W | ACK | …
- i2c
- ACK가 없어도 된다.
- smbus
- NACK 의미
- smbus
- N잘못된 명령이나 데이터의 수신을 의미한다.
- i2c
- 더이상 데이터를 전송하지 않는다는 것을 의미한다.
- smbus
Protocol
- 탈부착 디바이스 연결
- smbus
- 통신 중 smbus 디바이스가 smbus에 연결될 수 있다.
- i2c
- 통신 중 i2c 디바이스가 i2c 버스에 연결될 수 없다.
- smbus
- ARP(Address Resolution Protocol)
- smbus
- ARP를 지원한다.
- i2c
- ARP를 지원하지 않는다.
- smbus
- PEC(Packet Error Checking)
- smbus
- PEC를 지원한다.
- 에러 체킹을 위해 CRC-8 규격으로 1 바이트를 추가 사용한다.
- i2c
- PEC를 지원하지 않는다.
- smbus
Alert
- smbus에 연결된 디바이스들이 alert라는 옵션 신호를 사용하여 host에 전달할 수 있게 하였다.
전기 규격
- 전기적인 특성이 I2C와 일부 다르다.
SMBus protocol
SMBus 프로토콜에 사용되는 명령들을 알아본다. 참고로 그림에서 사용하는 기호는 다음과 같다.
- S
- Start condition
- P
- Stop condition
- Sr
- Repeted Start condition
- Addr
- 7비트 주소
- R/W
- 1비트 (R=1, W=0)
- A
- Ack (0)
- PEC
- 8비트 Packet Error Checking
Quick, Send & Receive 명령
Quick
슬레이브 디바이스로 R/W 1 비트를 전달할 수 있고, Ack 1비트를 수신하여 detect 여부를 확인할 수 있다.
- R/W 비트를 수신하여 장치의 on/off를 제어하거나, low-power 스탠바이 모드를 enable/disable하는데 사용할 수 있다.
- 데이터 바이트의 송신 또는 수신은 없다.
Send byte
슬레이브 디바이스에 1 바이트를 송신한다. Write Byte와 다른 점은 command를 전송하지 않는다는 점이다.
- pure smbus의 ACK는 디바이스에서의 처리 결과를 호스트에 반환하는 기능을 가진다.
- pure i2c-bus의 ACK는 디바이스가 항상 1을 응답해야한다.
Receive byte
슬레이브 디바이스로부터 1 바이트를 수신한다.
- 마지막 ACK는 NACK일 수 있고 이는 마지막 바이트를 의미한다.
Write & Read 명령
Write byte
슬레이브 디바이스에 1 바이트 명령 + 1 바이트를 송신한다.
Write word
슬레이브 디바이스에 1 바이트 명령 + 워드(2 바이트)를 송신한다.
- smbus에서 워드는 2바이트이다.
Read byte
슬레이브 디바이스에 1 바이트 명령을 송신하고 1 바이트를 수신한다.
- 마지막 ACK는 NACK일 수 있고 이는 마지막 바이트를 의미한다.
Read word
슬레이브 디바이스에 1 바이트 명령을 송신하고 워드(2 바이트)를 수신한다.
- 마지막 ACK는 NACK일 수 있고 이는 마지막 바이트를 의미한다.
Block 명령
Block write
슬레이브 디바이스에 명령을 전송한다. 그런 후 다시 슬레이브 디바이스에 길이 + 블럭(최대 32바이트) 데이터를 송신한다.
Block read
슬레이브 디바이스에 명령을 전송한다. 그런 후 다시 슬레이브 디바이스로부터 길이 + 블럭(최대 32바이트) 데이터를 수신한다.
- 마지막 ACK는 NACK일 수 있고 이는 마지막 바이트를 의미한다.
Process Call 명령
Process call
슬레이브 디바이스에 명령 + 워드(2 바이트)를 전송한다. 그런 후 다시 슬레이브 디바이스로부터 워드(2 바이트)를 수신한다.
- 마지막 ACK는 NACK일 수 있고 이는 마지막 바이트를 의미한다.
Block Process Call
슬레이브 디바이스에 명령 + 길이와 블럭 데이터(32 바이트까지)를 전송한다. 그런 후 다시 슬레이브 디바이스로부터 길이와 블럭(최대 32 바이트) 데이터를 수신한다.
- 마지막 ACK는 NACK일 수 있고 이는 마지막 바이트를 의미한다.
이벤트
Host notify
슬레이브 디바이스가 호스트에 워드 데이터를 전달하고자 할 때 사용한다. 호스트는 워드 데이터를 수신하여 호스트에서 슬레이브를 담당하는 드라이버에 이벤트를 전달한다.
- pure smbus에서 host notify는 반드시 구현되어야 한다.
smbalert#
슬레이브 디바이스가 smbalert# 인터럽트를 호스트에 발생시킨다. 발생 시킨 디바이스를 확인하기 위해 아래와 같이 ARA(Alert Response Address)로부터 디바이스 주소를 알아올 수 있다.
- 만일 HW 구성 시 별도의 smbalert# 라인이 호스트에 연결되어 구성되지 않은 경우 호스트가 alert를 주기적으로 폴링하여 알아내는 방법도 사용할 수 있다.
- pure smbus에서 alert는 option 사항이다.
- alert callback을 위해 i2c_setup_smbus_alert() 함수를 사용하여 callback용 함수를 등록하여 사용한다.
I2C 서브시스템
I2C 디바이스와 드라이버를 등록하면 I2C 서브시스템은 유저 스페이스에 다양한 인터페이스를 제공한다. (i2c-N은 i2c adapter로 N은 특별히 지정하지 않으면 0번부터 시작된다.)
- i2c-core가 크게 두 가지를 관리한다.
- i2c adapter 디바이스
- /sys/devices/…/<i2c 호스트 디바이스>/i2c-N
- i2c 어댑터 디바이스 인터페이스
- /sys/class/i2c-adapter
- i2c-adapter 클래스 디바이스 인터페이스
- /sys/bus/i2c/devices/i2c-N
- 버스에 등록된 i2c 어댑터 디바이스 인터페이스
- /sys/devices/…/<i2c 호스트 디바이스>/i2c-N
- i2c client 디바이스 및 드라이버
- /sys/devices/…/<i2c 호스트 디바이스>/i2c-N/<client device>
- i2c 클라이언트 디바이스 인터페이스
- <client device>의 기본 포맷은 <버스번호>-<4자리의 addr 숫자>
- 예) 0x3f 주소를 가진 디바이스가 1번 i2c 버스에 연결된 경우 -> 1-003f
- /sys/bus/i2c/devices/<client device>
- /sys/devices/…/<i2c 호스트 디바이스>/i2c-N/<client device>
- i2c adapter 디바이스
- i2c-dev는 i2c 클라이언트 디바이스를 위해 2 개의 인터페이스를 제공한다.
- /dev/i2c-N
- 캐릭터 디바이스 인터페이스
- i2cdetect, i2cdump, i2cset, i2cget 등의 i2c-tools를 사용하려면 i2c 캐릭터디바이스가 동작해야 한다.
- /sys/class/i2c-dev/i2c-N
- i2c-dev 클래스 디바이스 인터페이스
- i2c adapter에 대한 character 디바이스를 사용할 때 생성된다.(CONFIG_I2C_CHARDEV)
- /dev/i2c-N
SoC 외부에 위치한 i2c 호스트 컨트롤러
임베디드 시스템에서 i2c 호스트 컨트롤러들은 보통 SoC 내부에 위치하여 플랫폼 디바이스로 곧바로 등록할 수 있다. 그러나 이를 사용하지 못하는 경우에는 속도가 빠른 버스에 연결하여 사용할 수 있다. 일반적인 PC에는 i2c 호스트 컨트롤러가 내장되지 않았다. 이러한 PC에는 pci 버스와 usb 버스가 연결되어 있는데 pci 버스 또는 usb 버스에 i2c 호스트 컨트롤러를 연결하여 사용하는 경우가 많다. 다음 그림은 usb 버스에 i2c 버스가 연결된 경우를 보여준다. 좌측 그림이 H/W 연결 구성이고 우측 그림이 이에 대한 각 버스 서브 시스템과 core 및 드라이버 위치를 보여준다.
- 예) PCI 버스 —–> USB 버스 —–> I2C 버스
i2c sysfs
다음 그림은 i2c adapater 디바이스 및 드라이버가 등록된 모습을 sysfs 디렉토리로 보여준다.
- 2 개의 i2c adapter가 플랫폼 디바이스로 등록되어있고, 2 개의 드라이버 역시 바인딩 되어 동작중인 상태이다.
다음 그림은 여러 개의 i2c client 디바이스 및 드라이버가 추가 등록된 모습을 sysfs 디렉토리로 보여준다.
- 여러 가지 종류의 디바이스 및 드라이버가 등록되어 있는 모습을 보여준다.
i2c 호스트 컨트롤러(Adapter) 및 i2 디바이스(Client) 등록
i2c 디바이스는 버스 마스터 기능 유무에 따라 i2c 호스트 컨트롤러와 i2c 클라이언트의 역할을 한다.
- i2c 호스트 컨트롤러(included i2c adapter)는 상위 버스와 i2c 버스간의 브릿지 기능을 수행한다.
- i2c 클라이언트는 i2c 버스에 연결되는 consumer 디바이스이다.
i2c 버스는 pci나 usb 버스와 다르게 i2c 버스를 스캔하여 디바이스를 찾는 기능이 없다. 따라서 사전에 연결하고자 하는 디바이스의 주소를 미리 등록해두고 이 주소를 사용하여 디바이스가 있는지 HW detect를 시도할 수 있다.
다음 그림은 상위 버스에 i2c 호스트 컨트롤러(included i2c adapter)를 등록시키고 i2c 클라이언트 디바이스가 등록된 모습을 보여준다.
- i2c 클라이언트 디바이스
- 하드웨어 센서 모니터링 디바이스인 lm75를 예를 들었다.
i2c 호스트 컨트롤러 등록
i2c 호스트 컨트롤러 디바이스와 드라이버를 등록하기 위해 다음과 같은 API들을 사용한다.
예) 플랫폼 버스에 등록
- 디바이스
- platform_device_register() 함수를 사용하여 등록시키거나 디바이스 트리를 파싱하여 등록을 한다.
- of_platform_device_create_pdata()
- platform_device_register() 함수를 사용하여 등록시키거나 디바이스 트리를 파싱하여 등록을 한다.
- 드라이버
- platform_register_driver() 함수를 사용하여 등록시킨다.
- 드라이버 내부 probe 함수에서 i2c_add_adapter() 함수를 사용하여 i2c 버스를 제어할 i2c_adapter 구조체를 등록시킨다.
다음 그림은 broadcom 사의 i2c 호스트 컨트롤러 드라이버가 등록되는 주요 코드 과정을 보여준다.
i2c 클라이언트 등록
- 디바이스 부분
- i2c_new_device() 함수를 사용하여 등록시키거나 디바이스 트리를 파싱하여 등록을 한다.
- of_i2c_register_device()
- 유저 스페이스에서 sysfs를 사용하여 등록하는 방법도 있다.
- 예) echo <device> 0x71 > /sys/bus/i2c/devices/i2c-0/new_device
- i2c_new_device() 함수를 사용하여 등록시키거나 디바이스 트리를 파싱하여 등록을 한다.
- 드라이버 부분
- i2c_add_driver() 또는 i2c_register_driver() 함수를 사용하여 등록시킨다.
다음 그림은 wm8750 디바이스가 i2c 클라이언트 디바이스 및 드라이버로 등록되는 과정에서의 주요 코드 부분을 보여준다.
- i2c 노드에 포함된 wm8750@1a 노드가 파싱되어 i2c_client 구조체에 포함된 후 i2c 버스에 디바이스로 이미 포함된 상태이다.
- i2c_driver 구조체가 wm8750@1a 디바이스에 대응하는
유저 스페이스 바인딩
드라이버를 i2c 디바이스와 attach하는 방법
- 기존(old) 방법
- modprobe <driver> probe=1,0×71
- modprobe <driver> force=1,0×71
- modprobe <driver> force_<device>=1,0×71
- 새로운(new) 방법
- echo <device> 0x71 > /sys/bus/i2c/devices/i2c-0/new_device
드라이버를 i2c 디바이스와 attach하지 못하게 하는 방법
- 기존(old) 방법
- modprobe <driver> ignore=1,0×71
- 새로운(new) 방법
- 드라이버를 dummy 디바이스와 연결해두는 방법을 사용한다.
- echo dummy 0x2f > /sys/bus/i2c/devices/i2c-1/new_device
- modprobe <driver>
- 드라이버를 dummy 디바이스와 연결해두는 방법을 사용한다.
I2C Tools
i2c 툴을 설치하기 위해서는 다음과 같이 i2c-tools를 설치한다.
- sudo apt-get install i2c-tools
i2cdetect
명령어 사용법
$ i2cdetect Error: No i2c-bus specified! Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST] i2cdetect -F I2CBUS i2cdetect -l I2CBUS is an integer or an I2C bus name If provided, FIRST and LAST limit the probing range.
i2c 버스 및 i2c Adapter명 확인
x86 서버
다음과 같이 pc server는 두 개의 i2c adapter가 서로 다름을 알 수 있다.
$ i2cdetect -l i2c-0 unknown AST i2c bit bus N/A i2c-1 unknown SMBus I801 adapter at 0580 N/A
broadcom ns2(Cortex-A57)
$ i2cdetect -l i2c-0 i2c Broadcom iProc I2C adapter I2C adapter i2c-1 i2c Broadcom iProc I2C adapter I2C adapter
rpi3(Cortex-A53)
$ i2cdetect -l i2c-1 i2c 3f804000.i2c I2C adapter
i2c 버스별 Functionalities 확인
x86 서버
0번 버스는 i2c 전송이 가능하고 smbus 전송도 에뮬레이션 처리가 가능하다. 반면에 1번 버스는 smbus 전송만 가능하다.
$ i2cdetect -F 0 Functionalities implemented by /dev/i2c-0: I2C yes SMBus Quick Command yes SMBus Send Byte yes SMBus Receive Byte yes SMBus Write Byte yes SMBus Read Byte yes SMBus Write Word yes SMBus Read Word yes SMBus Process Call yes SMBus Block Write yes SMBus Block Read yes SMBus Block Process Call yes SMBus PEC yes I2C Block Write yes I2C Block Read yes $ sudo i2cdetect -F 1 Functionalities implemented by /dev/i2c-1: I2C no SMBus Quick Command yes SMBus Send Byte yes SMBus Receive Byte yes SMBus Write Byte yes SMBus Read Byte yes SMBus Write Word yes SMBus Read Word yes SMBus Process Call no SMBus Block Write yes SMBus Block Read yes SMBus Block Process Call no SMBus PEC yes I2C Block Write yes I2C Block Read yes
broadcom ns2(Cortex-A57)
$ i2cdetect -F 0
Functionalities implemented by /dev/i2c/0:
I2C yes
SMBus Quick Command yes
SMBus Send Byte yes
SMBus Receive Byte yes
SMBus Write Byte yes
SMBus Read Byte yes
SMBus Write Word yes
SMBus Read Word yes
SMBus Process Call yes
SMBus Block Write yes
SMBus Block Read no
SMBus Block Process Call no
SMBus PEC yes
I2C Block Write yes
I2C Block Read yes
$ i2cdetect -F 0
(상동)
rpi3(Cortex-A53)
$ i2cdetect -F 1
Functionalities implemented by /dev/i2c-1:
I2C yes
SMBus Quick Command yes
SMBus Send Byte yes
SMBus Receive Byte yes
SMBus Write Byte yes
SMBus Read Byte yes
SMBus Write Word yes
SMBus Read Word yes
SMBus Process Call yes
SMBus Block Write yes
SMBus Block Read no
SMBus Block Process Call no
SMBus PEC yes
I2C Block Write yes
I2C Block Read yes
i2c 버스별 디바이스 스캔
x86 서버
$ sudo i2cdetect -y -r 0 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- $ sudo i2cdetect -y -r 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- 44 -- -- -- 48 -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
broadcom ns2(Cortex-A57)
- HW 스캔된 디바이스는 숫자로 표기되고, UU로 표기된 디바이스는 이미 바인딩(dummy 포함)된 디바이스이다.
$ i2cdetect -y -r 0 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- UU -- -- -- 50: 50 51 -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- -- 70: UU -- UU -- -- -- -- -- # i2cdetect -y -r 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- UU 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
rpi3(Cortex-A53)
$ i2cdetect -y -r 0 Error: Could not open file `/dev/i2c-0' or `/dev/i2c/0': No such file or directory $ i2cdetect -y -r 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
i2cdump
명령어 사용법
$ i2cdump Error: No i2c-bus specified! Usage: i2cdump [-f] [-o] [-y] [-r first-last] I2CBUS ADDRESS [MODE [BANK [BANKREG]]] I2CBUS is an integer or an I2C bus name ADDRESS is an integer (0x03 - 0x77) MODE is one of: b (byte, default) w (word) W (word on even register addresses) s (SMBus block) i (I2C block) c (consecutive byte)
데이터 dump
1번 버스의 0x4f 디바이스 덤프
$ i2cdump -f -y 1 0x4f 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 00: 22 11 ff 00 00 ff 00 00 ff 00 ff 0f 00 00 00 00 "?.........?.... 10: 00 ff 04 00 00 00 00 00 00 00 00 00 00 00 00 00 ..?............. … e0: 22 11 ff 00 00 ff 00 00 ff 00 ff 0f 00 00 00 00 "?.........?.... f0: 00 ff 04 00 00 00 00 00 00 00 00 00 00 00 00 00 ..?.............
i2cget
명령어 사용법
$ i2cget Usage: i2cget [-f] [-y] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE]] I2CBUS is an integer or an I2C bus name ADDRESS is an integer (0x03 - 0x77) MODE is one of: b (read byte data, default) w (read word data) c (write byte/read byte) Append p for SMBus PEC
1 바이트 읽기
0번 버스의 0x4f 디바이스에서 offset 0x0b에 해당하는 바이트를 읽어온다.
$ i2cget -f -y 0 0x4c 0x0b 0x0f
i2cset
명령어 사용법
$ i2cset Usage: i2cset [-f] [-y] [-m MASK] I2CBUS CHIP-ADDRESS DATA-ADDRESS [VALUE] ... [MODE] I2CBUS is an integer or an I2C bus name ADDRESS is an integer (0x03 - 0x77) MODE is one of: c (byte, no value) b (byte data, default) w (word data) i (I2C block data) s (SMBus block data) Append p for SMBus PEC
1바이트 쓰기
1번 버스의 0x72 디바이스 0 offset에 0x01을 기록한다. (i2c 스위치에 0번 채널을 선택하게 한다.)
$ i2cset -f -y 1 0x72 0 0x01
i2c trace
i2c를 trace하기 위해 ftrace를 사용해본다. ftrace를 사용하기 위해 다음의 커널 옵션이 사용된다.
- CONFIG_FTRACE
- CONFIG_ENABLE_DEFAULT_TRACERS
먼저 tracefs를 /sys/kernel/tracing 디렉토리에 마운트한다.
$ mount -t tracefs nodev /sys/kernel/tracing
마운트한 후 해당 디렉토리를 살펴보면 다음과 같다.
# cd /sys/kernel/tracing/ # ls -la total 0 drwx------ 6 root root 0 Jan 1 09:00 . drwxr-xr-x 8 root root 0 Jan 1 09:31 .. -r--r--r-- 1 root root 0 Jan 1 09:00 README -r--r--r-- 1 root root 0 Jan 1 09:00 available_events -r--r--r-- 1 root root 0 Jan 1 09:00 available_tracers -rw-r--r-- 1 root root 0 Jan 1 09:00 buffer_size_kb -r--r--r-- 1 root root 0 Jan 1 09:00 buffer_total_size_kb -rw-r--r-- 1 root root 0 Jan 1 09:00 current_tracer drwxr-xr-x 45 root root 0 Jan 1 09:00 events --w------- 1 root root 0 Jan 1 09:00 free_buffer drwxr-xr-x 2 root root 0 Jan 1 09:00 instances drwxr-xr-x 2 root root 0 Jan 1 09:00 options drwxr-xr-x 6 root root 0 Jan 1 09:00 per_cpu -r--r--r-- 1 root root 0 Jan 1 09:00 printk_formats -r--r--r-- 1 root root 0 Jan 1 09:00 saved_cmdlines -rw-r--r-- 1 root root 0 Jan 1 09:00 saved_cmdlines_size -rw-r--r-- 1 root root 0 Jan 1 09:00 set_event -rw-r--r-- 1 root root 0 Jan 1 09:00 set_event_pid -rw-r--r-- 1 root root 0 Jan 1 09:00 trace -rw-r--r-- 1 root root 0 Jan 1 09:00 trace_clock --w--w---- 1 root root 0 Jan 1 09:00 trace_marker -rw-r--r-- 1 root root 0 Jan 1 09:00 trace_options -r--r--r-- 1 root root 0 Jan 1 09:00 trace_pipe -rw-r--r-- 1 root root 0 Jan 1 09:00 tracing_cpumask -rw-r--r-- 1 root root 0 Jan 1 09:00 tracing_on -rw-r--r-- 1 root root 0 Jan 1 09:00 tracing_thresh
사용할 수 있는 i2c 이벤트를 확인해본다.
$ cat available_events | grep i2c i2c:smbus_result i2c:smbus_reply i2c:smbus_read i2c:smbus_write i2c:i2c_result i2c:i2c_reply i2c:i2c_read i2c:i2c_write
다음 명령을 통해 i2c 이벤트 전체를 trace 하도록 enable 한다.
$ echo 1 > events/i2c/enable
만일 i2c-adapter(i2c-bus 호스트 컨트롤러)가 여러 개 준비되어 있는 경우 그 중 하나만 선택하려면 다음과 같은 명령을 사용할 수 있다.
$ echo adapter_nr==0 > events/i2c/filter
다음은 0x72 주소를 사용하는 i2c 스위치 디바이스에 0x80 데이터를 기록하여 총 8개 채널 중 마지막 채널을 연결한다.
- trace에 0 값을 기록하는 경우 기존 trace 로그를 삭제한다.
$ echo 0 > trace; i2cset -f -y 0 0x72 0x80; cat trace # tracer: nop # # entries-in-buffer/entries-written: 4/4 #P:2 # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | i2cset-168 [000] .... 2027.045809: smbus_write: i2c-0 a=072 f=0000 c=80 BYTE l=0 [] i2cset-168 [000] .... 2027.045814: i2c_write: i2c-0 #0 a=072 f=0000 l=1 [80] i2cset-168 [000] .... 2027.046032: i2c_result: i2c-0 n=1 ret=1 i2cset-168 [000] .... 2027.046033: smbus_result: i2c-0 a=072 f=0000 c=80 BYTE wr res=0
기록된 값을 확인해본다.
$ echo 0 > trace; i2cget -f -y 0 0x72; cat trace 0x80 # tracer: nop # # entries-in-buffer/entries-written: 6/6 #P:2 # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | i2cget-176 [000] .... 2326.033639: smbus_read: i2c-0 a=072 f=0000 c=0 BYTE i2cget-176 [000] .... 2326.033641: i2c_read: i2c-0 #0 a=072 f=0001 l=1 i2cget-176 [000] .... 2326.033856: i2c_reply: i2c-0 #0 a=072 f=0001 l=1 [80] i2cget-176 [000] .... 2326.033857: i2c_result: i2c-0 n=1 ret=1 i2cget-176 [000] .... 2326.033857: smbus_reply: i2c-0 a=072 f=0000 c=0 BYTE l=1 [80] i2cget-176 [000] .... 2326.033858: smbus_result: i2c-0 a=072 f=0000 c=0 BYTE rd res=0
위의 방법과 유사하지만 tracefs를 마운트하지 않고 /sys/kernel/debug/tracing 디렉토리에서 동일한 동작을 할 수도 있다.
U-Boot에서 I2C 제어
명령어 헬프
u-boot> i2c i2c - I2C sub-system Usage: i2c crc32 chip address[.0, .1, .2] count - compute CRC32 checksum i2c dev [dev] - show or set current I2C bus i2c loop chip address[.0, .1, .2] [# of objects] - looping read of device i2c md chip address[.0, .1, .2] [# of objects] - read from I2C device i2c mm chip address[.0, .1, .2] - write to I2C device (auto-incrementing) i2c mw chip address[.0, .1, .2] value [count] - write to I2C device (fill) i2c nm chip address[.0, .1, .2] - write to I2C device (constant address) i2c probe [address] - test for and show device(s) on the I2C bus i2c read chip address[.0, .1, .2] length memaddress - read to memory i2c write memaddress chip address[.0, .1, .2] length [-s] - write memory to I2C; the -s option selects bulk write in a single transaction i2c reset - re-init the I2C Controller i2c speed [speed] - show or set I2C bus speed
버스 선택 및 probe
u-boot> i2c dev 0 Setting bus to 0 u-boot> i2c probe Valid chip addresses: 00 4C 68 70 72
u-boot> i2c dev 1 Setting bus to 1 u-boot> i2c probe Valid chip addresses: 4F
데이터 덤프
u-boot> i2c md 0x4f 0 0000: 22 11 ff 00 00 ff 00 00 ff 00 ff 0f 00 00 00 00 "............… u-boot> i2c md 0x4f 2 1 0002: ff
데이터 기록(0x4f 디바이스 2 offset에 0xf7 기록)
u-boot> i2c mw 0x4f 2 f7
참고
- I2C Subsystem -1- (Basic) | 문c – 현재 글
- I2C Subsystem -2- (Core) | 문c
- I2C Subsystem -3- (Transfer) | 문c
- i2C Subsystem -4- (I2C-Mux) | 문c
- I2C/SMBus Subsystem – kernel.org
- I2C | sparkfun
- I2C – What’s That? | i2c-bus.org
- What is I2C (inter-‐IC) | Piazza – 다운로드 pdf
- I2C manual – NXP Semiconductors | NXP – 다운로드 pdf
- I2C-bus specification and user manual Rev. 6 — 4 April 2014 | NXP – 다운로드 pdf
- PCA954X FAMILY OF I²C / SMBus MULTIPLEXERS and SWITCHES | Philips – 다운로드 pdf
- I + I2C = I3C, what’s hiding in this additional ’I’ | Bootlin.com – 다운로드 pdf
- MIPI I3C and I3C Basic | mipi.org