I2C
고친 사람 github-actions[bot]
I2C 는 한 기판 위의 칩들이 선 두 가닥만으로 값을 주고받게 해 주는 통신 규칙입니다. 프로세서가 온도 센서나 작은 메모리 칩에 값을 묻고 설정을 적어 넣을 때 씁니다. 칩 여러 개가 같은 두 가닥에 함께 매달립니다. 프로세서는 주소를 불러 그중 하나와만 이야기합니다.
쉽고 빠른 이해
I2C 는 프로세서가 선 두 가닥으로 여러 부품에 말을 거는 방법입니다. 예를 들어 기판의 프로세서가 온도 센서에 지금 온도를 묻고 답을 받아 옵니다.
부품마다 전용 선을 깔면 기판 위의 배선이 금세 늘어납니다. I2C 는 부품이 여럿이어도 선 두 가닥을 함께 씁니다.
어떻게 도는가:
- 프로세서가 대화를 연다는 신호를 내고 부품의 주소를 부릅니다
- 그 주소의 부품만 들었다는 신호로 답합니다
- 여덟 비트씩 주고받습니다. 받는 쪽은 여덟 비트마다 받았다고 답합니다
- 프로세서가 대화를 닫는 신호를 내면 대화가 끝납니다
대가도 있습니다. 모두가 같은 두 가닥을 나눠 쓰므로 한 번에 나르는 양이 적습니다. 부품 하나가 선을 붙잡은 채 멈추면 같은 선의 다른 부품도 값을 주고받지 못합니다.
상세
I2C(Inter-Integrated Circuit, 집적 회로 간 통신)는 한 기판 위의 칩끼리 값을 주고받는 규칙입니다. 집적 회로는 흔히 칩이라고 부르는 반도체 부품입니다. 프로세서도, 온도 센서도, 작은 메모리도 칩입니다. 기판은 칩들을 올려 놓고 선으로 이어 둔 회로판입니다.
여러 장치가 함께 쓰는 통로를 버스라고 부릅니다. I2C 는 그 통로 위에서 주고받는 차례를 정하는 버스 프로토콜입니다. 필립스(Philips)가 만들었습니다. I2C 버스 하나에는 칩 여러 개가 함께 매달립니다.
I2C 가 쓰는 신호선은 두 가닥뿐입니다. 칩마다 전용 선을 깔지 않으니 기판 면적을 아낍니다. 신호 품질 문제도 줄어듭니다. 가끔 적은 양만 주고받는 장치를 값싼 버스 하나에 여럿 이을 수 있습니다.
I2C 는 허가를 받아 쓰는 상표이기도 합니다. 상표 요건을 맞추지 않은 변종은 I2C 라는 이름을 내걸지 않습니다. 대신 TWI(Two-Wire Interface, 두 가닥 인터페이스)나 IIC 같은 다른 이름을 씁니다. 공식 명세는 「I²C-bus specification and user manual」(문서 번호 UM10204)입니다. 현재로서는 7판이 가장 최신 판입니다.
이 절은 먼저 선 두 가닥과 그 위의 두 역할을 다룹니다. 그다음 대화를 여닫는 신호와 주소, 바이트마다 붙는 확인 비트를 짚습니다. 끝으로 속도와 쓰임, 선을 나눠 쓰는 방식이 낳는 성질을 봅니다.
두 가닥의 선 SDA 와 SCL
I2C 의 두 신호는 데이터 비트를 싣는 SDA(Serial Data, 직렬 데이터)와 박자를 알리는 SCL(Serial Clock, 직렬 클록)입니다.
I2C 는 직렬 통신입니다. 직렬 통신은 비트를 한 줄로 세워 하나씩 차례로 보내는 방식입니다. 클록은 일정한 간격으로 오르내리는 신호입니다. 받는 쪽은 SCL 의 박자에 맞춰 SDA 의 비트를 읽습니다.
컨트롤러와 타깃
버스 위의 칩은 두 역할로 나뉩니다. 컨트롤러는 대화를 시작하고 클록을 내는 칩입니다. 타깃은 컨트롤러가 주소로 부르면 답하는 칩입니다. 한 버스에는 컨트롤러가 하나 이상, 타깃이 하나 이상 매달립니다.
flowchart TD
C["컨트롤러 · 대화를 열고 클록을 낸다"] --> B["I2C 버스 · SDA 와 SCL 두 가닥"]
B --> T1["타깃 · 온도 센서"]
B --> T2["타깃 · 메모리 칩"]
B --> T3["타깃 · 하드웨어 감시 칩"]
그림에서 타깃은 셋이지만 컨트롤러에서 나가는 선은 두 가닥입니다. 세 타깃이 같은 두 가닥을 나눠 씁니다. 컨트롤러는 주소로 그중 하나를 골라 부릅니다.
예전 명세는 컨트롤러를 마스터(master), 타깃을 슬레이브(slave)라고 불렀습니다. 2021년 10월에 나온 7판이 이 낱말을 컨트롤러와 타깃으로 바꿨습니다. 아직 고치지 않은 문서에는 옛 낱말이 남아 있습니다.
대화를 여닫는 시작 조건과 정지 조건
대화 한 번은 컨트롤러가 내는 시작 조건(start condition)으로 열립니다. 그리고 정지 조건(stop condition)으로 닫힙니다. 아래 기호열에서는 시작 조건을 S, 정지 조건을 P 로 적습니다.
시작 조건과 정지 조건 사이에서 바이트가 오갑니다. 정지 조건 없이 시작 조건을 한 번 더 내 대화를 이어 가는 반복 시작도 있습니다. 반복 시작은 「교환 순서」가 다룹니다.
타깃을 부르는 주소
한 버스에 타깃이 여럿이니 컨트롤러는 누구에게 말하는지 밝혀야 합니다. 그래서 타깃마다 주소가 있습니다. 대부분의 I2C 장치는 7비트 주소를 씁니다. 7비트로 만들 수 있는 값은 128개입니다.
주소를 넓힌 10비트 주소도 있습니다. 10비트 주소는 7비트 주소와 겹치지 않는 별도의 묶음입니다. 7비트 주소 0x10 과 10비트 주소 0x10 은 서로 다른 주소입니다. 한 장치가 둘 다에 답할 수는 있습니다.
같은 주소를 쓰는 칩 둘이 한 버스에 달리면 주소가 부딪힙니다. 이 충돌을 막으려고 버스 중간에 멀티플렉서를 두기도 합니다. 멀티플렉서는 여러 갈래 중 하나만 골라 잇는 스위치 부품입니다.
버스는 어떤 칩이 어느 주소에 달렸는지 스스로 알려 주지 않습니다. 소프트웨어가 미리 알고 있어야 합니다. 운영체제는 이 정보를 디바이스 트리나 보드 파일 같은 설정에서 받습니다.
바이트마다 붙는 ACK
시작 조건 바로 뒤에 컨트롤러는 타깃 주소 7비트와 방향 비트 1비트를 보냅니다. 방향 비트가 0 이면 쓰기(Wr), 1 이면 읽기(Rd)입니다.
그 뒤에 타깃이 1비트로 답합니다. 받았다는 답이 ACK(Acknowledge, 확인 응답)입니다. 받지 않았다는 답은 NACK(Not Acknowledge, 부정 응답)입니다. ACK 를 보내는 쪽은 SDA 를 낮은 전압으로 끌어내립니다.
아래 그림은 주소를 부를 때 SDA 에 차례로 실리는 비트입니다.
packet-beta 0-6: "Addr · 타깃 주소 7비트" 7: "Rd/Wr" 8: "A 또는 NA"
그림의 0~7 번은 컨트롤러가 보냅니다. 8 번 한 비트만 타깃이 보냅니다. A 는 ACK, NA 는 NACK 의 기호입니다.
데이터 바이트 8비트 뒤에도 같은 1비트 답이 따라옵니다. 이번에는 데이터를 받은 쪽이 답합니다.
속도
I2C 는 속도를 바꿀 수 있는 프로토콜입니다. 대부분의 I2C 장치는 400 kHz 까지의 버스 속도를 씁니다. 3.4 MHz 고속 확장은 아직 널리 쓰이지 않습니다. 높은 쪽은 5 MHz 까지 있습니다.
임베디드 운영체제 Zephyr 의 I2C API 는 속도 설정 다섯을 이런 이름으로 정의합니다.
| 속도 설정 | 값 |
|---|---|
| I2C Standard Speed | 100 kHz |
| I2C Fast Speed | 400 kHz |
| I2C Fast Plus Speed | 1 MHz |
| I2C High Speed | 3.4 MHz |
| I2C Ultra Fast Speed | 5 MHz |
쓰임과 한계
I2C 는 데이터가 조금씩만 오가도 되는 장치에 널리 씁니다. 임베디드 시스템에서 널리 씁니다. 센서와 직렬 메모리가 흔한 예입니다. 직렬 메모리는 값을 한 비트씩 차례로 읽고 쓰는 메모리 칩입니다.
요즘 PC 메인보드는 SMBus(System Management Bus, 시스템 관리 버스)를 씁니다. SMBus 는 I2C 를 바탕으로 만든 형제 프로토콜입니다. SMBus 로 가장 흔히 붙는 장치는 둘입니다. 메모리 모듈의 설정을 담은 EEPROM 과 하드웨어 감시 칩입니다.
EEPROM(Electrically Erasable Programmable Read-Only Memory)은 전기로 지우고 다시 쓸 수 있는 메모리 칩입니다. 메모리 모듈은 자기 설정 값을 이 칩에 담아 둡니다.
I2C 의 한계를 넘으려고 따로 표준으로 정한 프로토콜이 I3C(Improved Inter-Integrated Circuit, 개량 집적 회로 간 통신)입니다. I3C 쪽이 꼽는 I2C 의 한계로는 속도가 낮은 것, 장치가 컨트롤러를 부르는 인터럽트를 내려면 선이 따로 필요한 것, 버스에 어떤 장치가 달렸는지 저절로 알아내지 못하는 것 등이 있습니다.
선을 끌어내리기만 하는 오픈 드레인
I2C 는 오픈 드레인 방식으로 신호를 냅니다. 오픈 드레인은 출력이 선을 낮은 전압으로 끌어내리기만 하는 방식입니다. 높은 전압으로 밀어 올리지는 않습니다.
아무도 끌어내리지 않을 때 선을 높은 전압으로 되돌리는 일은 선에 따로 단 풀업 저항이 맡습니다. 이 방식에서는 칩 하나만 끌어내려도 선이 낮아집니다. 선의 낮은 전압은 비트 0 으로, 높은 전압은 비트 1 로 읽힙니다.
I2C 는 이 성질로 세 가지를 합니다. 앞에서 본 ACK 주고받기가 첫째입니다. 박자를 못 따라가는 타깃에 클록을 맞추는 일과 컨트롤러 사이의 중재가 나머지 둘입니다. 아래 두 소절이 둘을 다룹니다.
박자를 늦추는 클록 늘이기
오픈 드레인이라 타깃도 클록 선 SCL 을 끌어내릴 수 있습니다. 컨트롤러의 박자를 못 따라가는 타깃은 이 성질로 클록을 자기 속도에 맞춥니다. 이것을 클록 늘이기(clock stretching)라고 부릅니다.
타깃이 클록을 너무 오래 늘이면 SMBus 는 시간 초과로 끊습니다. I2C 자체에는 그런 시간 제한이 없습니다. 다만 I2C 컨트롤러도 대개 SMBus 보다 훨씬 긴 상한을 임의로 둡니다.
컨트롤러가 여럿일 때의 중재
I2C 는 한 버스에 컨트롤러를 여럿 둘 수 있습니다. 두 컨트롤러가 같은 때에 서로 다른 데이터를 보내면 하나만 남아야 합니다. 누가 남을지 가리는 일을 버스 중재라고 부릅니다. 버스 중재도 오픈 드레인 신호로 합니다.
1 을 보내려던 컨트롤러는 SDA 를 끌어내리지 않습니다. 그때 다른 컨트롤러가 SDA 를 끌어내리면 선은 0 이 됩니다. 1 을 보내던 컨트롤러는 자기가 보낸 비트가 흐트러진 것을 알아챕니다. 그 컨트롤러는 중재에서 진 것입니다.
sequenceDiagram
participant C1 as 컨트롤러 1
participant L as SDA 선
participant C2 as 컨트롤러 2
C1->>L: 비트 1 · 끌어내리지 않는다
C2->>L: 비트 0 · 끌어내린다
Note over L: 선은 0 이 된다
L-->>C1: 보낸 비트가 흐트러져 돌아온다
Note over C1: 중재에서 졌다
진 컨트롤러가 받는 값은 「실패」가 다룹니다.
교환 순서
이 절은 컨트롤러와 타깃이 대화 한 번에 무엇을 어떤 차례로 주고받는지 다룹니다. 대상은 쓰기 · 읽기 · 둘을 이은 결합, 세 왕복입니다. 왕복마다 기호열을 먼저 적습니다. 같은 흐름을 문장과 그림으로 다시 짚습니다.
기호열에 나오는 기호는 아래와 같습니다. 대괄호 [..] 안은 타깃이 보낸 것입니다. 대괄호 밖은 컨트롤러가 보낸 것입니다.
| 기호 | 뜻 |
|---|---|
S |
시작 조건 |
Sr |
반복 시작 조건. 쓰기에서 읽기로 방향을 바꿀 때 씁니다 |
P |
정지 조건 |
Addr |
타깃 주소 7비트 |
Rd · Wr |
방향 비트 1비트. Rd 는 1, Wr 은 0 |
A · NA |
ACK 와 NACK 1비트 |
Data |
데이터 1바이트 |
Comm |
명령 바이트 1바이트. 흔히 타깃 안의 레지스터를 고릅니다 |
레지스터는 칩 안에 번호를 붙여 둔 작은 저장 칸입니다. 온도 센서라면 한 칸에 지금 온도가 듭니다. 다른 칸에는 설정 값이 듭니다.
쓰기
S Addr Wr [A] Data [A] Data [A] ... [A] Data [A] P
컨트롤러가 시작 조건을 내고 주소와 Wr 를 보냅니다. 타깃이 A 로 답합니다. 이어서 컨트롤러가 데이터 바이트를 보냅니다. 타깃은 바이트마다 A 로 답합니다. 다 보내면 컨트롤러가 정지 조건을 냅니다.
sequenceDiagram
participant C as 컨트롤러
participant T as 타깃
C->>T: S · Addr · Wr
T-->>C: A
C->>T: Data
T-->>C: A
C->>T: Data
T-->>C: A
Note over C,T: 컨트롤러가 P 를 내고 대화를 닫는다
읽기
S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P
주소 뒤의 A 까지는 쓰기와 같습니다. 방향 비트만 Rd 입니다. 그 뒤로 보내는 쪽이 바뀝니다.
이제 타깃이 데이터 바이트를 보냅니다. 컨트롤러는 바이트마다 A 로 답합니다. 마지막 바이트에만 NA 로 답합니다. 그다음 컨트롤러가 정지 조건을 냅니다.
sequenceDiagram
participant C as 컨트롤러
participant T as 타깃
C->>T: S · Addr · Rd
T-->>C: A
T->>C: Data
C-->>T: A
T->>C: Data
C-->>T: NA
Note over C,T: 컨트롤러가 P 를 내고 대화를 닫는다
읽기 끝의 NA 는 고장이 아닙니다. 더 받지 않겠다는 답입니다.
쓰고 나서 읽는 결합
결합 왕복은 쓰기와 읽기를 한 대화 안에서 잇습니다. 앞 조각이 끝날 때 정지 조건을 내지 않습니다. 대신 시작 조건을 한 번 더 내 대화를 이어 갑니다. 이 둘째 시작 조건을 반복 시작이라고 부릅니다.
시작 조건으로 열리는 이런 조각 하나를 메시지라고 부릅니다. 결합 왕복은 메시지 둘 이상으로 이루어집니다. 리눅스 커널의 i2c_transfer() 도 메시지 여럿을 받아 한 왕복으로 보냅니다. 반복 시작은 문서에 따라 S 로도, Sr 로도 적습니다.
레지스터 번호를 쓰고 그 칸을 곧바로 읽는 왕복이 결합의 한 꼴입니다. SMBus 의 바이트 읽기가 그 꼴입니다. 한 바이트를 읽는 기호열은 이렇습니다.
S Addr Wr [A] Comm [A] Sr Addr Rd [A] [Data] NA P
컨트롤러가 쓰기 방향으로 타깃을 부릅니다. 이어서 레지스터 번호 Comm 을 보냅니다. 정지 조건은 내지 않습니다. 반복 시작으로 같은 타깃을 읽기 방향으로 다시 부릅니다. 타깃이 그 레지스터의 값 한 바이트를 보냅니다. 컨트롤러가 NA 로 답한 뒤 정지 조건을 냅니다.
sequenceDiagram
participant C as 컨트롤러
participant T as 타깃
C->>T: S · Addr · Wr
T-->>C: A
C->>T: Comm · 레지스터 번호
T-->>C: A
C->>T: Sr · Addr · Rd
T-->>C: A
T->>C: Data
C-->>T: NA
Note over C,T: 컨트롤러가 P 를 낸다
정지 조건은 모든 메시지가 끝난 뒤 한 번만 나갑니다. 메시지마다 다른 타깃 주소로 보내도 됩니다. 다만 같은 주소로 보내는 꼴이 가장 흔합니다.
NA 가 돌아올 때와 비튼 왕복
보통은 타깃이 NA 로 답하면 그 메시지가 곧바로 끊깁니다. 이 규칙을 비롯해 왕복을 일부러 비튼 변형도 있습니다.
NA 를 A 처럼 여기고 메시지를 끝까지 보내는 변형이 있습니다. 읽기에서 컨트롤러의 A·NA 비트를 건너뛰는 변형도 있습니다. 결합 왕복의 어느 대목에서 S Addr Rd/Wr [A] 를 빼는 변형도 있습니다. 둘째 메시지 앞에서 빼는 것이 그 보기입니다.
이런 변형은 대개 장치의 결함을 피해 가는 데 씁니다.
예시
이 절은 리눅스에서 사용자 프로그램이 I2C 타깃 하나에 값을 쓰고 읽는 실물을 다룹니다. 대상은 리눅스 커널 문서의 예제 코드입니다. 주소 0x40 의 타깃, 레지스터 0x10, 16비트 값 0x6543 이 나옵니다. 그 코드가 선 위에서 어떤 왕복이 되는지를 바이트 단위로 짚습니다. 끝으로 실제 칩 하나를 봅니다.
리눅스에서 레지스터에 16비트 워드 쓰기
리눅스는 I2C 컨트롤러마다 0부터 번호를 붙입니다. i2c-dev 모듈을 올리면 컨트롤러마다 /dev/i2c-0 · /dev/i2c-1 같은 장치 파일이 생깁니다.
리눅스 커널 안에서는 컨트롤러를 어댑터(adapter), 타깃을 클라이언트(client)라고도 부릅니다.
프로그램은 이 장치 파일을 연 뒤 말을 걸 타깃 주소부터 정합니다. 아래는 오류 처리를 뺀 예제입니다.
int addr = 0x40; /* 타깃 주소 */
ioctl(file, I2C_SLAVE, addr);
__u8 reg = 0x10; /* 쓸 레지스터 */
char buf[10];
/* i2c_smbus_write_word_data(file, reg, 0x6543) 과 같은 일 */
buf[0] = reg;
buf[1] = 0x43;
buf[2] = 0x65;
write(file, buf, 3);
write() 가 세 바이트를 보냅니다. 첫 바이트는 레지스터 번호 0x10 입니다. 나머지 둘은 16비트 값 0x6543 을 쪼갠 것입니다. 낮은 바이트 0x43 이 먼저, 높은 바이트 0x65 가 나중에 나갑니다.
선 위에서는 이 쓰기가 아래 기호열이 됩니다. SMBus 의 워드 쓰기가 정한 순서 Comm [A] DataLow [A] DataHigh [A] 와 같습니다.
S 0x40 Wr [A] 0x10 [A] 0x43 [A] 0x65 [A] P
sequenceDiagram
participant C as 컨트롤러
participant T as 타깃 · 주소 0x40
C->>T: S · 0x40 · Wr
T-->>C: A
C->>T: 0x10 · 레지스터 번호
T-->>C: A
C->>T: 0x43 · 낮은 바이트
T-->>C: A
C->>T: 0x65 · 높은 바이트
T-->>C: A
Note over C,T: 컨트롤러가 P 를 낸다
같은 레지스터에서 16비트 워드 읽기
같은 예제는 0x10 레지스터의 워드를 SMBus 호출로 읽습니다.
__s32 res = i2c_smbus_read_word_data(file, reg);
타깃은 I2C 장치인데 호출은 SMBus 쪽입니다. I2C 컨트롤러에서는 SMBus 명령이 저절로 I2C 로 옮겨집니다. 그래서 I2C 타깃에도 이 호출을 쓸 수 있습니다. 이 호출은 SMBus 의 워드 읽기 왕복을 냅니다.
S 0x40 Wr [A] 0x10 [A] Sr 0x40 Rd [A] [DataLow] A [DataHigh] NA P
앞의 「쓰고 나서 읽는 결합」 꼴입니다. 레지스터 번호를 쓴 뒤 반복 시작으로 방향을 바꿉니다. 타깃이 낮은 바이트와 높은 바이트를 차례로 보냅니다. 읽어 온 값은 예제에 없습니다.
read() 와 write() 만으로는 결합 왕복을 만들지 못합니다. 그래서 이 둘만 쓰는 방식은 사용자 프로그램이 거의 쓰지 않습니다. 커널 문서의 예제는 두 방식을 다 보이려고 쓰기는 write() 로, 읽기는 SMBus 호출로 적었습니다. 말을 걸 장치가 SMBus 명령을 지원하면 SMBus 호출을 쓰는 편을 권합니다.
National Semiconductor LM75 온도 센서
이 소절은 실제 칩 하나가 어느 주소에 달리고 무엇을 레지스터로 주고받는지 봅니다. LM75 는 National Semiconductor 가 만든 온도 센서 칩입니다. 칩 안에 온도 센서가 하나 있습니다. 리눅스의 lm75 드라이버는 이 칩을 I2C 주소 0x48 부터 0x4f 사이에서 찾습니다.
온도는 섭씨 −55 도부터 +125 도까지 보장됩니다. 한계 온도는 과열 차단(Overtemperature Shutdown) 레지스터와 히스테리시스(Hysteresis) 레지스터에 적습니다. 두 값 모두 반 도 단위로 쓰고 읽습니다. 온도가 과열 차단 값을 넘으면 경보가 납니다. 경보는 온도가 히스테리시스 값 아래로 내려갈 때까지 이어집니다.
원래 LM75 는 PC 메인보드에서 프로세서 온도를 재는 데 흔히 썼습니다. 지금은 복제 칩이 여러 임베디드 설계에 들어갑니다. 원래 칩의 레지스터 동작을 똑같이 따라 하지 않는 복제 칩은 드라이버가 찾아내지 못합니다. 그런 칩은 어느 주소에 있는지 소프트웨어에 따로 알려 줘야 합니다.
실패
이 절은 I2C 전송이 깨지는 조건과 그때 리눅스 커널이 돌려주는 값을 짝으로 다룹니다. 대상은 주소에 답이 없을 때, 중재에서 졌을 때, 버스가 붙잡혀 멈췄을 때입니다.
고장 보고가 늘 오류는 아닙니다. 고장 보고는 작업의 결과가 잘 된 길에서 벗어났다는 통지입니다. 뭔가 잘못됐다는 뜻이 아닐 때도 있습니다.
소프트웨어는 일시적인 고장 뒤에 같은 작업을 다시 시도하는 일이 많습니다. 여러 번 해도 결과가 같은 작업일 때 그렇게 합니다. 다시 초기화하거나 리셋까지 하는 복구가 맞는 경우도 있습니다. 고장 보고를 받아 그렇게 복구한 뒤에는 오류가 없습니다.
| 깨지는 조건 | 돌려주는 값 | 덧붙임 |
|---|---|---|
| 주소 단계에서 ACK 가 안 왔다 | ENXIO |
장치가 잠시 답을 못 했을 수도 있습니다. 대개는 그 주소에 아무도 없다는 뜻입니다 |
| 컨트롤러가 데이터를 보내던 중 중재에서 졌다 | EAGAIN |
다른 컨트롤러가 같은 때에 다른 데이터를 보낸 것입니다 |
| 작업이 너무 오래 걸려 끝나기 전에 끊었다 | ETIMEDOUT |
SMBus 컨트롤러는 타깃이 클록을 너무 오래 늘일 때 이 값을 돌려줄 수 있습니다 |
| SMBus 컨트롤러에서 버스가 허용보다 오래 바빴다 | EBUSY |
대개 둘 중 하나입니다. 어느 장치에 리셋 같은 복구가 필요합니다. 또는 리셋이 실패했습니다 |
| 10비트 주소를 못 다루는 컨트롤러에 10비트 주소를 줬다 | EAFNOSUPPORT |
EAGAIN 은 다른 때에도 돌아옵니다. 잠들 수 없는 실행 흐름(atomic context)에서 I2C 작업을 부른 경우입니다. 그때 다른 작업이 이미 그 버스를 쓰고 있으면 이 값이 돌아옵니다.
전송이 끊겨 붙잡힌 SDA
전송이 ACK 단계에서 멈추는 경우가 있습니다. 그러면 ACK 를 보내던 타깃이 SDA 를 낮게 끌어내린 채 남습니다. 주소에 ACK 하던 중이든 데이터 바이트에 ACK 하던 중이든 같습니다. SDA 가 낮게 붙잡혀 있으면 버스로 데이터를 보낼 수 없습니다.
이 상태를 푸는 일을 버스 복구라고 부릅니다. 컨트롤러는 이 상태를 알아채고 버스 복구를 시작해야 합니다. 컨트롤러가 SCL 을 흔들면 타깃이 SDA 를 놓아야 합니다. 버스 복구는 클록 펄스를 최대 9번 넣습니다.
쓰기 도중에 멈춘 타깃은 그다음 SCL 펄스를 데이터로 받습니다. 레지스터가 있는 칩이라면 0번 레지스터에 쓸 데이터로 받습니다. 복구하려고 넣는 펄스도 그 타깃에는 데이터로 보입니다. 확인하지 않으면 아무 값이나 그 타깃에 써집니다.
그래서 복구할 때는 버스가 풀렸는지 확인해야 합니다. SDA 를 읽어 보거나 정지 조건을 더 보내 확인합니다.
flowchart TD
A["컨트롤러가 SDA 가 낮게 붙잡힌 것을 알아챈다"] --> B["SCL 에 클록 펄스를 넣는다 · 최대 9번"]
B --> C["SDA 를 읽거나 정지 조건을 더 보내 확인한다"]
C --> D{"버스가 풀렸나"}
D -->|풀렸다| E["버스를 다시 쓴다"]
D -->|아직 낮다| F["버스 복구가 성공하지 못한다"]
SDA 가 계속 낮게 고정돼 있으면 버스 복구는 성공하지 못합니다. 반대로 SDA 가 몇 밀리초 붙잡히면 스스로 놓는 타깃도 있습니다. 버스를 지켜보다 스스로 버스 복구를 시작하는 외부 장치가 달려 있을 수도 있습니다.
리눅스 커널의 버스 복구 장치
리눅스 커널은 컨트롤러 드라이버가 버스 복구를 하도록 struct i2c_bus_recovery_info 를 둡니다. 드라이버는 recover_bus 에 자기 복구 루틴을 주거나 공용 루틴 i2c_generic_scl_recovery() 를 줍니다.
공용 루틴은 SCL 을 직접 읽고 씁니다. 그래서 SCL 의 값을 읽는 get_scl 과 SCL 을 세우거나 내리는 set_scl 이 반드시 있어야 합니다. SDA 쪽은 get_sda 와 set_sda 중 하나가 있어야 합니다.
경계
SMBus 장치는 I2C 장치인가. 대개 I2C 장치처럼 다룰 수 있습니다. 그러나 늘 그렇지는 않습니다. SMBus 는 I2C 를 바탕으로 만든 형제 프로토콜입니다. 그 프로토콜과 신호는 대부분 I2C 의 부분집합입니다.
겹치는 쪽부터 봅니다. 대부분의 SMBus 시스템은 I2C 규칙에도 맞습니다. 많은 I2C 장치가 SMBus 위에서도 돕니다.
갈리는 쪽은 SMBus 가 더 얹은 규칙입니다. I2C 상표를 받으려면 지켜야 하는 것 밖의 규칙입니다. 아래 표가 그 규칙과, 규칙이 어긋날 때 리눅스 커널이 돌려주는 값을 보입니다.
| SMBus 가 더한 규칙 | 어긋날 때 돌아오는 값 |
|---|---|
| 시간 제한. 타깃이 클록을 너무 오래 늘이면 끊습니다 | ETIMEDOUT |
| PEC(Packet Error Code, 패킷 오류 코드) 바이트 | EBADMSG |
| 블록 응답 길이는 1~32 바이트 | EPROTO |
PEC 는 대화의 모든 바이트를 덮는 CRC(Cyclic Redundancy Check, 순환 중복 검사)입니다. 정지 조건 바로 앞에 보냅니다. EBADMSG 는 읽기에서만 돌아옵니다.
블록 작업은 바이트 여럿을 한 묶음으로 주고받는 SMBus 작업입니다. 묶음 앞에 길이를 적은 바이트가 붙습니다. 타깃이 돌려주는 블록의 길이가 1~32 바이트 밖이면 EPROTO 가 돌아옵니다.
전기 조건도 SMBus 가 I2C 보다 빡빡합니다.
컨트롤러 쪽에서도 둘이 갈립니다. I2C 를 하는 컨트롤러는 SMBus 작업 대부분을 할 수 있습니다. 반대로 SMBus 만 하는 컨트롤러는 대개 일반 I2C 명령을 못 합니다. 리눅스 커널이 컨트롤러에 일반 I2C 명령(I2C_FUNC_I2C)과 SMBus 작업을 따로 묻는 까닭입니다.
판정이 실무에서 뜻하는 바는 이렇습니다. 장치가 SMBus 와 같은 부분집합만 쓰면 그 장치를 SMBus 버스에 달 수 있습니다. 드라이버도 그런 장치에는 SMBus 명령으로 짜기를 권합니다. 그러면 SMBus 컨트롤러와 I2C 컨트롤러 양쪽에서 같은 드라이버를 쓸 수 있습니다. 그 부분집합 밖을 쓰는 장치는 SMBus 만 하는 컨트롤러 대부분에서 돌지 못합니다.
다만 어디에 달아도 된다는 말은 아닙니다. 두 버스의 전기 조건을 함께 맞추지 못하는 시스템이 있습니다. SMBus 의 프로토콜 의미를 다 구현하지 못하는 시스템도 있습니다.
관련 항목
I2C 가 속하는 상위 분류
버스 · 직렬 통신 · 프로토콜 · 임베디드 · 하드웨어 인터페이스
I2C 와 같은 일을 두고 겨루는 통신 방식
SPI · UART · CAN · 1-Wire · USB
I2C 를 바탕으로 만든 규격
SMBus · PMBus · DDC · EDID · I3C · PEC
I2C 대화를 이루는 신호
시작 조건 · 정지 조건 · 반복 시작 · ACK · NACK · 주소 바이트 · 10비트 주소 · 클록 늘이기 · 버스 중재
I2C 버스를 이루는 전기 부품
SDA · SCL · 오픈 드레인 · 풀업 저항 · 클록 · GPIO
I2C 버스를 나누거나 잇는 부품
I2C 멀티플렉서 · I2C 게이트 · I2C 주소 변환기 · I2C 중재기
I2C 로 붙는 칩과 그 안의 저장 칸
센서 · EEPROM · RTC · 전원 관리 IC · 마이크로컨트롤러 · 레지스터 · LM75 · 하드웨어 감시 칩
I2C 전송이 깨질 때 돌아오는 오류 코드
ENXIO · EAGAIN · ETIMEDOUT · EBUSY · EBADMSG · EPROTO · EAFNOSUPPORT
I2C 를 다루는 소프트웨어
디바이스 드라이버 · 리눅스 커널 · 장치 파일 · i2c-dev · i2c-tools · i2c-stub · 버스 복구 · 로컬 타깃 · 비트 뱅잉 · 디바이스 트리 · Zephyr · 펌웨어
다른 이름: I²C · IIC · TWI · Inter-Integrated Circuit · 집적 회로 간 통신 · I2C 버스