그라운드 임베디드 EN 유튜브 구독

노트 / 통신 프로토콜

I2C: 선 두 가닥에 여러 칩 달기

SCL과 SDA 두 선, 7비트 주소, ACK, 풀업 저항. 센서 통신의 표준인 I2C의 동작과 막혔을 때 푸는 법.

GROUND EMBEDDED · 정리 노트통신 05I2C 1START → 주소(7비트)+W → ACK → 레지스터 번호 → ACK 2→ 재START → 주소+R → ACK → 데이터 → ACK … → 마…

구조

마스터 (MCU)SCL · SDA풀업 저항 필요센서0x68EEPROM0x50디스플레이0x3C
두 선에 여러 칩이 나란히 붙습니다. 마스터가 주소를 부르면 그 주소의 칩만 대답합니다.

모든 칩이 SCL(클럭), SDA(데이터) 두 선을 공유하고, 선은 오픈드레인 + 풀업 저항입니다. 누구든 선을 LOW로 당길 수 있고, 아무도 당기지 않으면 풀업이 HIGH로 만듭니다.

한 번의 읽기

START주소 + WACK레지스터 번호ACK재START주소 + RACK데이터NACKSTOP
레지스터 하나를 읽는 순서. 주소를 두 번 부릅니다. 한 번은 쓰기로 레지스터 번호를 알리고, 한 번은 읽기로 값을 받습니다.
START → 주소(7비트)+W → ACK → 레지스터 번호 → ACK
→ 재START → 주소+R → ACK → 데이터 → ACK … → 마지막 데이터 → NACK → STOP
  • ACK: 받은 쪽이 9번째 클럭에 SDA를 LOW로 당겨 "받았다"고 알림
  • 주소에 해당하는 칩이 없으면 NACK가 오고, 이것으로 연결 확인(I2C 스캔)을 할 수 있음

풀업 저항 고르기

너무 크면 너무 작으면
상승 에지가 느려 고속에서 깨짐 칩이 LOW로 당기는 전류 한계 초과

100kHz는 4.7~10kΩ, 400kHz는 2.2~4.7kΩ가 보통입니다. 버스 커패시턴스(선 길이, 칩 수)가 크면 더 작게 합니다.

클럭 스트레칭

슬레이브가 준비가 덜 되면 SCL을 LOW로 붙잡아 마스터를 기다리게 할 수 있습니다.

버스가 멈췄을 때

통신 도중 MCU가 리셋되면 슬레이브가 SDA를 LOW로 붙잡은 채 멈출 수 있습니다. 시작할 때 SCL을 GPIO로 9번 토글해 슬레이브를 풀어 주는 버스 복구 코드를 넣어 둡니다.

그림으로 보기: 시작, 주소, ACK

SCLSDA시작A6=1A5=0A4=1A3=0A2=0A1=0A0=0W=0ACK
SCL이 HIGH인 동안 SDA가 내려가면 시작입니다. 이어서 주소 7비트와 읽기/쓰기 1비트가 나가고, 9번째 클럭에서 슬레이브가 SDA를 당겨 응답(ACK)합니다.

데이터는 SCL이 LOW일 때만 바뀝니다. SCL이 HIGH인 동안 SDA가 바뀌는 경우는 시작(내려감)과 정지(올라감) 둘뿐이라, 이 두 조건을 데이터와 구분할 수 있습니다.

오픈 드레인과 풀업

I2C의 두 선은 아무도 당기지 않으면 풀업 저항 때문에 HIGH입니다. 장치는 LOW로 당기기만 할 수 있습니다. 그래서 두 장치가 동시에 구동해도 단락이 나지 않고, 슬레이브가 SCL을 붙잡아 마스터를 기다리게 할 수도 있습니다(클럭 스트레칭).

풀업 저항값은 속도와 버스 정전용량으로 정합니다.

속도 흔히 쓰는 값
100 kHz 4.7 kΩ ~ 10 kΩ
400 kHz 2.2 kΩ ~ 4.7 kΩ

저항이 크면 HIGH로 올라가는 시간이 길어져 파형 모서리가 둥글어집니다. 오실로스코프에서 상승 구간이 완만한 곡선이면 저항을 줄입니다. 모듈마다 풀업이 달려 있는 경우 여러 개를 연결하면 병렬이 되어 값이 너무 작아질 수 있습니다.

3D 실험실여기까지 읽은 내용을 직접 돌려 볼 수 있습니다→

레지스터 읽기는 두 단계

// 1) 쓰기로 "몇 번 레지스터를 읽을지" 알려 주고
// 2) 반복 시작(정지 없이 다시 시작) 뒤 읽기
i2c_start();
i2c_write((0x68 << 1) | 0);   // 주소 + 쓰기
i2c_write(0x3B);              // 레지스터 번호
i2c_start();                  // 반복 시작
i2c_write((0x68 << 1) | 1);   // 주소 + 읽기
hi = i2c_read(ACK);
lo = i2c_read(NACK);          // 마지막 바이트는 NACK
i2c_stop();

7비트 주소와 8비트 주소

데이터시트에 0x68이라고 적혀 있으면 7비트 주소이고, 실제 선에는 한 칸 밀려 0xD0(쓰기), 0xD1(읽기)로 나갑니다. 문서에 0xD0이라고 적는 회사도 있어서, 응답이 없으면 먼저 한 칸 밀거나 당겨 봅니다. 라이브러리가 어느 쪽을 받는지도 확인합니다.

멈춘 버스를 살리는 코드

슬레이브가 데이터를 보내던 중에 마스터가 리셋되면, 슬레이브는 SDA를 LOW로 잡은 채 다음 클럭을 기다립니다. 마스터는 버스가 사용 중이라고 보고 아무것도 못 합니다.

// SCL을 GPIO로 바꿔 9번 흔들어 슬레이브가 SDA를 놓게 한 뒤 정지 조건을 낸다
for (int i = 0; i < 9 && !sda_read(); i++) { scl_low(); delay_us(5); scl_high(); delay_us(5); }

제품 펌웨어에는 이 복구 코드와 모든 I2C 동작의 타임아웃을 반드시 넣습니다.

현업에서 자주 터지는 문제

  • 주소 스캔에서 아무것도 안 잡힘: 풀업 누락, SDA와 SCL 뒤바뀜, 전원 미공급 순으로 확인
  • 같은 주소의 센서 두 개: 주소 선택 핀이 없으면 I2C 멀티플렉서가 필요
  • 긴 케이블: 정전용량이 커져 400 kHz가 불안정해짐. 속도를 낮추거나 버퍼 사용
3D 실험실에서 직접 돌려 보기값을 바꾸며 이 노트의 원리를 확인합니다→영상으로 이어 보기통신 시리즈 13편UART 통신 완벽정리 | 전기전자, 전자과, 임베디드 개발자 필수 시청PROJECT COURSEI2C로 MPU6050을, UART로 블루투스 모듈을 직접 붙여 보고 싶다면밸런싱 로봇 하나를 요구사항, 회로와 PCB, 펌웨어, 앱까지 실무 순서대로 만드는 강의입니다. 10파트 165강.강의 살펴보기 →FREE PDF무료 전자책 3권 받기이 주제를 한 권으로 이어서 읽고 싶다면. BLDC 모터 제어, 직렬통신, 센서를 책 분량으로 묶었습니다. 이메일만 남기면 바로 받습니다.무료로 받기 →