기술 가이드

임베디드 Linux I²C 반복 시작: 레지스터 읽기와 I2C_RDWR 검증

센서 레지스터 읽기에서 STOP과 반복 시작의 차이를 확인하고 Linux I2C_RDWR 메시지 구성을 정리합니다.

목차

레지스터 주소 한 바이트를 쓴 뒤 센서 값을 읽을 때, 두 호출 사이에 STOP이 들어가면 일부 장치는 읽기 위치를 유지하지 못합니다. 임베디드 Linux의 /dev/i2c-N에서 반복 시작(repeated START)이 필요한 장치라면 한 번의 I2C_RDWR 요청에 쓰기·읽기 메시지를 묶으세요. Linux 커널의 I²C 사용자 공간 인터페이스와 I²C 프로토콜 설명을 기준으로 정리합니다.

1. 버스에서 실제로 달라지는 점

예를 들어 7비트 주소 0x48, 레지스터 0x00에서 한 바이트를 읽는 장치의 전형적인 순서는 다음과 같습니다. S는 START, Sr는 STOP 없이 다시 만드는 START, P는 STOP입니다. 주소 뒤의 W/R은 방향 비트이며, 표의 0x48은 시프트하지 않은 7비트 주소입니다.

구간 버스 순서 의미
쓰기 메시지 S 0x48 W ACK 0x00 ACK 읽을 레지스터를 지정
읽기 메시지 Sr 0x48 R ACK [DATA] NACK P 같은 거래 안에서 값 한 바이트 수신

커널 문서의 combined transaction은 메시지 사이의 STOP 대신 START를 보냅니다. 반면 /dev/i2c-N에 write()를 한 번, read()를 한 번 따로 호출하는 방식은 이러한 묶음 거래를 표현하지 못합니다. 모든 센서가 반복 시작을 요구하는 것은 아니므로, 실제 레지스터 포인터 동작과 바이트 순서는 해당 센서 데이터시트로 결정해야 합니다.

2. PC에서 순서를 검사하는 C 예제

다음 코드는 위 버스 순서를 나타내는 작고 독립적인 모델입니다. i2c_sequence.c로 저장해 gcc -std=c11 -Wall -Wextra -Werror i2c_sequence.c -o i2c_sequence로 빌드하고 실행하세요. 메시지 사이가 STOP이 아니라 RESTART인지 검사합니다. 핀 신호나 실제 ACK를 검증하는 코드는 아닙니다.

#include <assert.h>
#include <stdio.h>
#include <string.h>

/* Bus-event model for a one-byte register read; it does not drive pins. */
static const char *const combined[] = {
"START", "ADDR 0x48 W", "REG 0x00", "RESTART",
"ADDR 0x48 R", "DATA", "NACK", "STOP"
};

int main(void) {
const size_t count = sizeof combined / sizeof combined[0];
assert(count == 8);
assert(strcmp(combined[3], "RESTART") == 0);
for (size_t i = 0; i < count; ++i) {
if (i > 0) putchar(' ');
fputs(combined[i], stdout);
}
putchar('\n');
puts("PASS: no STOP between write and read");
return 0;
}

예상 출력:

START ADDR 0x48 W REG 0x00 RESTART ADDR 0x48 R DATA NACK STOP
PASS: no STOP between write and read

3. Linux I2C_RDWR로 옮기는 순서

  1. /sys/class/i2c-dev/ 또는 i2cdetect -l로 어댑터 번호를 확인하고 해당 /dev/i2c-N을 엽니다. 번호가 재부팅 후 바뀔 수 있으므로 고정으로 가정하지 않습니다.
  2. ioctl(fd, I2C_FUNCS, &amp;funcs)로 I2C_FUNC_I2C 지원을 확인합니다. 이 기능이 없으면 이 방식의 I2C_RDWR 묶음 전송을 사용할 수 없습니다.
  3. struct i2c_msg msgs[2]를 구성합니다. 첫 메시지는 addr=0x48, flags=0, len=1, buf가 레지스터 바이트 0x00을 가리키도록 합니다. 두 번째 메시지는 같은 7비트 주소, flags=I2C_M_RD, len=1, buf가 결과 저장 바이트를 가리키도록 합니다. 각 메시지에 주소와 방향을 따로 설정해야 합니다.
  4. struct i2c_rdwr_ioctl_data의 msgs와 nmsgs=2를 채우고 ioctl(fd, I2C_RDWR, &amp;request)를 한 번 호출합니다. 실패하면 errno를 기록하고, 장치 전원·주소·풀업·어댑터 지원·권한을 점검합니다. 커널 드라이버가 이미 소유한 장치에는 임의 접근하지 않습니다.

I2C_SLAVE로 주소를 정한 뒤 별도의 write()와 read()를 호출하는 예제를 그대로 바꿔 붙이면 반복 시작이 보장되지 않습니다. 로직 애널라이저에서는 쓰기 ACK 다음에 STOP 없이 START가 이어지는지 확인하고, 마지막 데이터 바이트 뒤 NACK·STOP을 확인하세요. PC 예제는 순서만 검증했으며 실제 어댑터, 센서, 전기 신호는 시험하지 않았습니다.

공식 근거: Linux I²C dev-interface, Linux I²C protocol.

관련 글

댓글

첫 댓글을 남겨 보세요.

관리자 확인 후 게시됩니다.

처리 중입니다...

잠시만 기다려주세요.