사전 I2C
프로토콜

I2C

gabury1고친 사람 github-actions[bot]

I2C 는 한 기판 위의 칩들이 선 두 가닥만으로 값을 주고받게 해 주는 통신 규칙입니다. 프로세서가 온도 센서나 작은 메모리 칩에 값을 묻고 설정을 적어 넣을 때 주로 씁니다. 칩 여러 개가 같은 두 가닥에 함께 매달립니다. 프로세서는 주소를 불러 그중 하나와만 이야기합니다.

쉽고 빠른 이해

I2C 는 프로세서가 선 두 가닥으로 여러 부품에 말을 거는 방법입니다. 예를 들어 작은 보드의 프로세서가 온도 센서에 지금 몇 도인지 묻고 답을 받아 옵니다.

부품마다 전용 선을 따로 깔면 프로세서의 핀과 기판의 배선이 금세 모자랍니다. I2C 는 부품이 여럿이어도 선 두 가닥을 함께 씁니다.

어떻게 도는가:

  1. 프로세서가 시작 조건이라는 신호를 내고 부품의 주소를 부릅니다
  2. 그 주소의 부품만 들었다는 신호로 답합니다
  3. 여덟 비트씩 주고받습니다. 받는 쪽은 여덟 비트마다 잘 받았다고 답합니다
  4. 프로세서가 정지 조건이라는 신호를 내면 두 가닥이 다시 비어 다음 대화를 받습니다

대가도 있습니다. 모두가 같은 두 가닥을 나눠 쓰므로 한 번에 한 대화만 오갑니다. 한 번에 나르는 양이 적고 선을 길게 끌 수 없습니다. 부품 하나가 선을 붙잡고 멈추면 같은 선의 부품이 모두 함께 멈춥니다.

상세

I2C(Inter-Integrated Circuit, 집적 회로 간 통신)는 한 기판 위의 집적 회로끼리 값을 주고받는 규칙입니다. 집적 회로는 흔히 칩이라고 부르는 반도체 부품입니다. 프로세서, 온도 센서, 작은 메모리가 모두 칩입니다.

기판은 이런 칩들을 올려 놓고 선으로 이어 둔 회로판입니다.

I2C 는 직렬 통신입니다. 직렬 통신은 비트를 한 줄로 세워 하나씩 차례로 보내는 방식입니다. 선이 적게 드는 대신 한 번에 한 비트씩만 지나갑니다.

이 절은 먼저 두 가닥의 선과 그 위에 매달린 칩들의 모양을 봅니다. 그다음 칩을 부르는 주소와 한 번의 쓰기·읽기 왕복을 봅니다. 끝으로 여러 칩이 선을 나눠 쓰는 방식이 낳는 성질과 한계를 봅니다.

두 가닥의 선 SDA 와 SCL

I2C 가 신호를 싣는 선은 두 가닥뿐입니다. 데이터 비트를 싣는 SDA(Serial Data, 직렬 데이터)와 박자를 알리는 SCL(Serial Clock, 직렬 클록)입니다.

클록은 일정한 간격으로 오르내리는 신호입니다. 받는 쪽은 이 박자를 보고 언제 SDA 를 읽을지 압니다. 그래서 두 쪽이 통신 속도를 미리 약속해 두지 않아도 됩니다.

칩들은 이 두 가닥에 나란히 매달립니다. 여러 부품이 함께 쓰는 이런 통로를 버스라고 합니다. 그래서 두 가닥을 묶어 I2C 버스라고 부릅니다.

flowchart TD
    P["프로세서"] --> B["I2C 버스 · SDA 와 SCL 두 가닥"]
    B --> S1["온도 센서"]
    B --> S2["작은 메모리 칩"]
    B --> S3["전원 관리 칩"]

그림에서 칩이 셋으로 늘어도 프로세서에서 나가는 선은 두 가닥 그대로입니다. 칩마다 전용 선을 깔았다면 프로세서의 핀과 기판의 배선이 금세 모자랐을 겁니다. I2C 는 이 배선을 아끼려고 씁니다.

컨트롤러와 타깃

버스 위의 칩은 두 역할로 나뉩니다. 대화를 여는 쪽이 컨트롤러입니다. 불려서 답하는 쪽이 타깃입니다. 그림에서는 프로세서가 컨트롤러이고 나머지 셋이 타깃입니다.

클록도 컨트롤러가 만듭니다. 타깃은 먼저 말을 걸지 못하고 컨트롤러가 부를 때까지 기다립니다. 한 버스에 컨트롤러가 둘 이상 있는 구성도 있습니다. 이 경우는 아래 「컨트롤러가 여럿일 때의 중재」에서 봅니다.

칩 제조사가 내는 설명서를 데이터시트라고 합니다. 예전 문서와 데이터시트에서는 컨트롤러를 마스터, 타깃을 슬레이브라고 부르는 일이 흔합니다. 같은 역할입니다.

칩을 부르는 주소

한 버스에 칩이 여럿이니 컨트롤러는 누구에게 말하는지 밝혀야 합니다. 그래서 타깃마다 주소가 있습니다. 주소는 대개 7비트입니다. 7비트로 만들 수 있는 값은 128개입니다.

이 가운데 16개는 특별한 용도로 따로 떼어 둡니다. 그래서 보통 칩에 줄 수 있는 주소는 112개입니다. 주소를 넓힌 10비트 주소 방식도 있습니다.

주소는 대개 칩을 만들 때 정해져 나옵니다. 같은 칩 둘을 한 버스에 달면 주소가 겹쳐 둘이 한꺼번에 답합니다. 그래서 많은 칩이 주소의 끝 비트 몇 개를 핀 연결로 바꿀 수 있게 해 둡니다.

대화를 여닫는 시작 조건과 정지 조건

버스가 비어 있을 때 SDA 와 SCL 은 둘 다 높은 전압에 머뭅니다. 높은 전압이 1 이고 낮은 전압이 0 입니다. 까닭은 아래 오픈 드레인 소절에서 봅니다.

데이터 비트를 보낼 때 SDA 는 SCL 이 낮은 동안에만 바뀝니다. SCL 이 높은 동안 SDA 는 가만히 있습니다. 받는 쪽은 이때 비트를 읽습니다.

이 약속을 일부러 어기는 두 신호가 대화의 경계입니다. SCL 이 높은 동안 SDA 를 높음에서 낮음으로 내리면 시작 조건입니다. SCL 이 높은 동안 SDA 를 낮음에서 높음으로 올리면 정지 조건입니다. 데이터 비트와 헷갈릴 일이 없으므로 버스 위의 모든 칩이 대화가 언제 열리고 닫히는지 압니다.

바이트마다 붙는 ACK

비트는 여덟 개씩 묶여 한 바이트로 오갑니다. 큰 쪽 비트부터 보냅니다.

여덟 번째 비트 뒤에 아홉 번째 클록이 하나 더 옵니다. 이 클록 동안 받는 쪽이 SDA 를 낮게 끌어내리면 잘 받았다는 뜻입니다. 이 신호를 ACK(acknowledge, 확인 응답)라고 합니다.

받는 쪽이 SDA 를 끌어내리지 않으면 SDA 는 높은 채로 남습니다. 이 신호가 NACK(not acknowledge, 부정 응답)입니다. 주소를 불렀는데 NACK 가 돌아오면 그 주소에 칩이 없거나 칩이 지금 답할 수 없다는 뜻입니다.

시작 조건 바로 뒤에 컨트롤러가 보내는 첫 바이트는 주소 바이트입니다. 앞 7비트가 타깃 주소입니다. 마지막 1비트는 방향입니다. 0 이면 컨트롤러가 쓰고 1 이면 컨트롤러가 읽습니다.

아래 그림은 이 아홉 클록 동안 SDA 에 차례로 실리는 것을 보입니다.

packet-beta
0-6: "타깃 주소 7비트"
7: "방향"
8: "ACK"

그림의 0~7 번은 컨트롤러가 SDA 에 싣습니다. 8 번 한 칸만 타깃이 SDA 를 잡고 답합니다.

레지스터에 값을 쓰는 한 왕복

타깃 칩 안에는 번호가 붙은 작은 저장 칸이 여럿 있습니다. 이 칸을 레지스터라고 부릅니다. 온도 센서라면 한 칸에는 지금 온도가 듭니다. 다른 칸에는 측정 주기 같은 설정이 듭니다.

I2C 로 하는 일은 대부분 이 칸을 읽고 쓰는 일입니다. 쓰기는 주소 바이트, 레지스터 번호, 쓸 값 순서로 보냅니다. 아래는 주소가 0x48 인 온도 센서의 1번 레지스터에 0x60 을 쓰는 왕복입니다.

sequenceDiagram
    participant C as 컨트롤러
    participant T as 타깃 · 주소 0x48
    C->>T: 시작 조건 · 주소 0x48 · 쓰기
    T-->>C: ACK
    C->>T: 레지스터 번호 0x01
    T-->>C: ACK
    C->>T: 값 0x60
    T-->>C: ACK
    Note over C,T: 컨트롤러가 정지 조건을 내고 버스를 놓는다

그림에서 컨트롤러가 바이트 셋을 보냅니다. 타깃은 바이트마다 ACK 로 답합니다. 선은 SDA 한 가닥입니다. 타깃은 아홉 번째 클록 동안만 그 선을 잡습니다.

레지스터를 읽는 왕복과 반복 시작

읽기는 한 단계가 더 붙습니다. 어느 레지스터를 읽을지 먼저 타깃에 알려야 하기 때문입니다. 그래서 쓰기 방향으로 레지스터 번호만 보낸 뒤 방향을 바꿔 다시 부릅니다.

방향을 바꿀 때 컨트롤러는 정지 조건을 내지 않습니다. 곧바로 시작 조건을 한 번 더 냅니다. 이것을 반복 시작(repeated start)이라고 합니다.

정지 조건을 내면 버스가 비므로 다른 컨트롤러가 그 틈에 버스를 차지할 수 있습니다. 반복 시작은 버스를 놓지 않은 채 방향만 바꿉니다.

읽는 동안에는 보내는 쪽과 받는 쪽이 바뀝니다. 타깃이 데이터 바이트를 보냅니다. 컨트롤러는 더 읽을 바이트가 남았으면 ACK 를, 마지막 바이트에는 NACK 를 답합니다. 그 뒤에 정지 조건을 냅니다.

sequenceDiagram
    participant C as 컨트롤러
    participant T as 타깃 · 주소 0x48
    C->>T: 시작 조건 · 주소 0x48 · 쓰기
    T-->>C: ACK
    C->>T: 레지스터 번호 0x00
    T-->>C: ACK
    C->>T: 반복 시작 · 주소 0x48 · 읽기
    T-->>C: ACK
    T->>C: 데이터 0x19
    C-->>T: NACK
    Note over C,T: 컨트롤러가 정지 조건을 낸다

그림의 데이터 0x19 는 십진수로 25 입니다. 이 센서가 0번 레지스터에 섭씨 온도를 한 바이트로 담는다면 25도를 읽은 셈입니다. 마지막 NACK 는 오류가 아닙니다. 이제 그만 보내라는 신호입니다.

선을 끌어내리기만 하는 오픈 드레인

I2C 칩의 출력은 선을 낮은 전압으로 끌어내리기만 합니다. 높은 전압으로 밀어 올리지는 못합니다. 이런 출력을 오픈 드레인이라고 부릅니다.

선을 높게 만드는 일은 풀업 저항이 맡습니다. 풀업 저항은 선과 전원 사이에 달아 둔 저항입니다. 아무도 선을 끌어내리지 않으면 이 저항이 선을 높은 전압으로 당겨 둡니다. 버스가 비어 있을 때 두 가닥이 높은 전압에 머무는 까닭이 이것입니다.

이 방식 덕분에 여러 칩이 한 선을 동시에 건드려도 부딪히지 않습니다. 누구든 하나라도 끌어내리면 선은 낮아집니다. 모두가 놓아야만 선이 높아집니다. 한 칩은 높게 밀고 다른 칩은 낮게 당기는 충돌이 생기지 않습니다.

앞에서 본 ACK 가 이 성질을 씁니다. 보내는 쪽이 SDA 를 놓아 두면 받는 쪽이 끌어내려 답합니다. 아래 세 소절도 같은 성질에 기댑니다.

준비가 덜 된 타깃이 쓰는 클록 늘이기

타깃이 아직 준비가 안 됐으면 SCL 을 낮게 붙잡아 둘 수 있습니다. 컨트롤러는 SCL 을 놓았는데도 선이 올라가지 않는 것을 보고 기다립니다. 이것을 클록 늘이기(clock stretching)라고 합니다.

컨트롤러의 박자를 못 따라가는 타깃이 박자를 늦추는 방법입니다. 대가는 컨트롤러가 언제까지 기다려야 할지 모른다는 점입니다. 그래서 기다림에 타임아웃을 두는 구현이 많습니다.

컨트롤러가 여럿일 때의 중재

한 버스에 컨트롤러가 둘 있고 둘이 한꺼번에 시작 조건을 냈다고 해 봅니다. 그러면 누가 버스를 쓸지 정해야 합니다. 이 일을 버스 중재라고 합니다.

각 컨트롤러는 비트를 보내면서 SDA 를 함께 봅니다. 자기는 1 을 보내려고 선을 놓았습니다. 그런데 선이 0 이면 다른 컨트롤러가 0 을 보낸 것입니다. 1 을 보낸 쪽은 진 것을 알고 물러납니다.

주소는 큰 쪽 비트부터 나가므로, 두 주소가 처음 갈리는 비트에서 0 을 보낸 쪽, 곧 주소가 작은 쪽이 이깁니다. 이긴 쪽이 보낸 비트는 선 위에서 한 번도 흐트러지지 않습니다. 그래서 이긴 쪽은 아무 일 없었던 듯 대화를 이어 갑니다. 진 쪽은 버스가 빌 때까지 기다렸다가 처음부터 다시 보냅니다.

버스가 멈추는 경우와 풀어내는 법

여러 칩이 선을 나눠 쓰면 약점도 나눠 가집니다. 칩 하나가 SDA 나 SCL 을 낮게 붙잡은 채 멈추면 다른 칩은 아무도 대화를 열지 못합니다. 타깃이 바이트를 보내던 도중에 컨트롤러가 재시작하면, 타깃은 SDA 를 낮게 붙잡은 채 다음 클록을 기다리며 남습니다.

SDA 가 붙잡혔을 때는 컨트롤러가 SCL 에 클록을 아홉 번 넣어 봅니다. 타깃은 그 사이에 보내던 바이트를 마저 내보내고 SDA 를 놓게 됩니다. 그래도 안 풀리면 칩을 리셋하거나 전원을 껐다 켭니다.

SCL 이 붙잡혔을 때는 클록을 넣을 방법이 없습니다. 이때는 곧바로 리셋이나 전원을 껐다 켜는 쪽으로 갑니다.

속도와 선의 길이

I2C 는 적은 양을 가끔 나르려고 만든 통신입니다. 처음에는 초당 100킬로비트가 상한이었습니다. 그 뒤로 상한을 올린 모드가 차례로 더해졌습니다.

아래 표는 모드마다 한 방향으로 나를 수 있는 속도의 상한입니다. 모드 이름은 원어로 부르는 일이 많아 그대로 적습니다.

모드 상한
Standard-mode 100 kbit/s
Fast-mode 400 kbit/s
Fast-mode Plus 1 Mbit/s
High-speed mode 3.4 Mbit/s

Standard-mode 에서는 바이트 하나에 ACK 까지 아홉 클록이 듭니다. 그래서 초당 1만 바이트 남짓을 나릅니다. 센서 값 몇 바이트를 자주 읽기에는 넉넉합니다. 사진이나 소리처럼 큰 데이터를 나르기에는 모자랍니다.

선의 길이도 짧게 씁니다. 선이 길어질수록 풀업 저항이 선을 높은 전압까지 끌어올리는 데 시간이 더 걸립니다. 전압이 제때 오르지 못하면 받는 쪽이 비트를 잘못 읽습니다. 그래서 I2C 는 대개 한 기판 안이나 짧은 케이블 안에서 씁니다.

이웃 방식 SPI 와 UART

기판 위에서 I2C 와 자주 견주는 방식이 둘 있습니다. SPI(Serial Peripheral Interface, 직렬 주변 장치 인터페이스)와 UART(Universal Asynchronous Receiver-Transmitter, 범용 비동기 송수신기)입니다. 셋 다 직렬 통신입니다. 아래 표는 셋이 선을 몇 가닥 쓰고 상대를 어떻게 고르는지 견줍니다.

I2C SPI UART
신호선 두 가닥 세 가닥 + 칩마다 선택선 한 가닥 보내는 선 · 받는 선
상대를 고르는 법 주소 칩 선택선 상대가 하나뿐
클록 선 있음 있음 없음. 속도를 미리 약속
받았다는 확인 바이트마다 ACK 없음 없음

SPI 의 세 가닥은 클록 · 보내는 선 · 받는 선입니다. 여기에 칩마다 선택선을 한 가닥씩 더 깔아야 합니다. 대신 주소도 ACK 도 없어 단순합니다.

클록도 I2C 보다 대개 훨씬 높게 올릴 수 있습니다. 화면이나 플래시 메모리처럼 데이터가 많은 칩은 SPI 로 붙이는 일이 흔합니다.

UART 는 두 장치만 잇는 방식입니다. 칩 여럿을 한 선에 달지 못합니다. I2C 는 선을 아끼면서 가끔 몇 바이트만 주고받는 칩을 여럿 달아야 할 때 고릅니다.

I2C 로 붙는 칩

I2C 로 붙는 칩은 대개 가끔 몇 바이트만 주고받는 칩입니다. 아래 표는 흔한 예와 그 칩과 주고받는 값입니다.

칩 주고받는 값
센서 온도 · 습도 · 기울기 같은 측정값
작은 메모리 칩 기판의 설정 값 · 일련번호
시계 칩 전원이 꺼져도 흐르는 현재 시각
전원 관리 칩 전압 설정 · 배터리 잔량

평소 쓰는 컴퓨터 안에도 I2C 가 있습니다. 모니터를 꽂으면 컴퓨터는 영상 케이블 안의 I2C 선으로 모니터가 받는 해상도 정보를 읽어 옵니다.

서버 메인보드도 I2C 를 씁니다. 메인보드는 I2C 를 바탕으로 만든 SMBus(System Management Bus, 시스템 관리 버스)로 온도와 팬 상태를 살핍니다. 꽂힌 메모리 모듈의 정보도 이 버스로 읽습니다.

운영체제에서 보이는 모습

리눅스 커널은 I2C 를 다루는 디바이스 드라이버를 둘로 나눕니다. 한쪽은 컨트롤러 하드웨어를 움직여 시작 조건과 바이트를 선에 내보냅니다. 다른 쪽은 타깃 칩 하나를 맡아 그 칩의 레지스터 번호가 무슨 뜻인지 압니다.

이렇게 나누면 같은 온도 센서 드라이버를 다른 컨트롤러가 달린 보드에서도 쓸 수 있습니다.

리눅스는 사용자 프로그램이 I2C 버스를 쓰도록 /dev/i2c-1 같은 장치 파일도 만들어 줍니다. 끝의 숫자가 버스 번호입니다. i2cdetect 같은 도구는 주소를 하나씩 불러 보고 ACK 가 돌아온 주소를 표로 보여 줍니다.

i2cget 은 앞의 읽기 왕복을 명령 한 줄로 합니다. 아래 줄은 1번 버스에서 주소 0x48 칩의 0x00 번 레지스터를 읽습니다. -y 는 확인 질문을 건너뛰는 옵션입니다.

터미널
i2cget -y 1 0x48 0x00   # 0x19

줄 오른쪽의 0x19 가 출력입니다. 앞의 읽기 그림에서 타깃이 보낸 바이트와 같은 값입니다.

관련 항목

I2C 가 속하는 상위 분류

버스 · 직렬 통신 · 프로토콜 · 임베디드 · 하드웨어 인터페이스

I2C 와 같은 일을 두고 겨루는 통신 방식

SPI · UART · CAN · 1-Wire · USB

I2C 를 바탕으로 만든 규격

SMBus · PMBus · DDC · EDID · I3C

I2C 대화를 이루는 신호

시작 조건 · 정지 조건 · 반복 시작 · ACK · NACK · 주소 바이트 · 클록 늘이기 · 버스 중재

I2C 버스를 이루는 전기 부품

SDA · SCL · 오픈 드레인 · 풀업 저항 · 클록 · GPIO

I2C 로 붙는 칩과 그 안의 저장 칸

센서 · EEPROM · RTC · 전원 관리 IC · 마이크로컨트롤러 · 레지스터

I2C 를 다루는 소프트웨어

디바이스 드라이버 · 리눅스 커널 · 장치 파일 · i2c-tools · 비트 뱅잉 · 디바이스 트리 · 펌웨어

다른 이름: I²C · IIC · TWI · Inter-Integrated Circuit · 집적 회로 간 통신 · I2C 버스