Devin.KR

I2C 주소와 오류 처리

120분 안팎

학습 목표

가상 주소 0x48의 두 바이트 센서 읽기에 NACK·분리·시간 초과 반환을 구현합니다.

개념

I2C 주소와 오류 처리

I2C 읽기 함수를 만들 때 먼저 정할 것은 실패 계약입니다. 응답이 없는데 이전 바이트를 다시 변환하면 오래된 기록이 정상 온도처럼 보입니다. 주소·레지스터·연결·NACK·시간 초과를 다른 코드로 반환하고 실패 시 출력 두 바이트를 보존합니다. BUS_OK일 때만 변환하도록 호출 흐름을 만드는 것이 목표입니다. NACK를 0도로 대체하거나 모든 오류를 하나의 숫자로 숨기지 않습니다.

SensorBus는 connected, nack, delay_ms, word 네 필드입니다. 연결 상태, 부정 응답 주입, 가상 응답 지연, 정상 시 반환할 원시값입니다. 이 구조체는 실물 드라이버 레지스터가 아니라 테스트 장치입니다. 함수 중 실제 버스를 기다리지 않으므로 통신 지연을 PC 실행 시간으로 측정하지 않습니다. fixture를 바꾸어 동일한 오류를 반복 재현합니다. 상태를 코드 안에 숨기지 않고 입력으로 전달합니다.

hal_i2c_read 주소는 7비트 0x48입니다. 방향 비트를 붙인 0x90이나 0x91은 이 API에서 거부합니다. 다른 드라이버는 표현이 다를 수 있으므로 이름만 보고 시프트 여부를 추정하지 않습니다. 헤더·명세·호출 지점이 일치하는지 확인합니다. BUS_ADDR가 나오면 케이블을 바꾸기 전에 전달 정수를 확인합니다. 16진 표기와 십진 로그가 같은 값인지도 대조합니다.

레지스터 0x00에서 정확히 2바이트를 읽고 timeout_ms는 20입니다. null 포인터, 다른 길이, 다른 timeout은 BUS_ARG이고 지원하지 않는 레지스터는 BUS_REG입니다. 실패인데 out를 참조하는 호출 코드는 수정 대상입니다. 보존된 메모리는 이번 요청의 데이터가 아닙니다. 호출 전 sentinel을 넣고 실패 뒤 그대로인지 검사하면 부작용 계약을 검증할 수 있습니다.

인자, 주소, 레지스터, 분리, NACK, 시간 제한 순으로 판단합니다. 여러 오류가 겹쳐도 같은 코드가 나와야 재현할 수 있습니다. 각각 주입하는 기본 검사와 둘 이상을 겹치는 우선순위 검사를 구별합니다. 주소 오류와 분리가 함께면 BUS_ADDR입니다. 실제 버스가 이 순서로 오류를 알려 준다는 뜻은 아니며 가상 HAL의 판단 순서입니다. 테스트는 외부 관찰과 내부 입력을 구분합니다.

NACK는 응답해야 할 쪽이 확인 신호를 주지 않은 상황입니다. 읽기 종료 때 컨트롤러가 보내는 마지막 NACK와 fixture의 센서 응답 실패는 역할이 다릅니다. 정상 읽기 종료를 오류로 분류하지 않습니다. 실물 NACK에는 주소 오류·전원·연결 등 여러 가설이 가능합니다. BUS_DETACHED는 연결 상태를 별도 입력으로 알고 제공하는 진단이며 실물 드라이버 보장으로 옮기지 않습니다.

20ms는 허용하고 21ms는 BUS_TIMEOUT입니다. 크거나 같음 비교는 20ms를 잘못 거부합니다. starter는 제한 검사가 빠져 늦은 응답도 성공합니다. FAIL 조건을 보고 delay_ms와 timeout_ms를 관찰합니다. 오류 반환 위치는 출력 쓰기 전입니다. 출력 후 오류를 반환하면 종료 코드는 맞아도 보존 검사가 실패합니다. 기능과 부작용 조건을 함께 만족시켜야 완료입니다.

정상 시 상위 바이트는 word를 8비트 오른쪽 이동한 값이고 하위는 낮은 8비트입니다. 두 칸을 모두 채운 뒤 BUS_OK입니다. 포인터만으로 배열 크기를 알 수 없으므로 호출자가 n과 실제 공간을 일치시켜야 합니다. 여기서는 길이 2 지역 배열을 씁니다. n이 2라고 한 칸짜리 배열을 넘겨도 안전해지는 것은 아닙니다. 크기 계약은 호출자와 구현 양쪽의 책임입니다.

온도 변환은 읽기 함수 밖에 둡니다. HAL은 바이트·오류를 처리하고 temp_mc는 형식·단위를 처리합니다. 시간 초과 테스트에 온도 계산을 끼워 넣지 않고 부호 변환은 독립 입력으로 확인합니다. 장치 교체 시 바꿀 계약이 분명해집니다. raw FF80 성공과 출력 보존 실패를 다른 검사로 읽습니다. 작은 함수라도 역할을 나누면 오류를 좁히기 쉽습니다.

CHECK는 건수를 늘리고 실패하면 행 번호와 조건식을 출력합니다. FAIL line 번호는 실행 당시 test_buses.c 위치입니다. bytes[0]==9라면 출력 보존 부분이고 BUS_TIMEOUT 식이 실패하면 응답 경계를 봅니다. make의 Error 1 또는 Error 2는 최종 요약입니다. 실제 원인은 위쪽 컴파일 진단이나 FAIL 식에 있습니다. 빌드 실패와 테스트 실패를 같은 문제로 취급하지 않습니다.

실습에는 m05의 완성 코드와 회귀가 포함됩니다. events.c의 ISR·큐·순환 시간 계산을 다시 고치지 않습니다. buses.c의 제한 조건을 구현하고 make -s buses-test로 좁혀 검사합니다. 이어 make test로 버튼·버퍼·HAL·타이머도 확인합니다. 새 센서 테스트가 성공해도 앞 기능이 깨지면 완료가 아닙니다. 회귀 실패는 원본과 달라진 파일부터 확인합니다.

출력 보존 검사에서는 bytes에 9와 8을 넣고 주소 오류나 지연 초과를 호출합니다. 두 숫자는 센서 값이 아니라 쓰기 여부 표식입니다. 그대로 남았다고 요청 성공으로 처리하면 안 됩니다. BUS_OK 검사 뒤 변환하는 흐름을 적습니다. 이 검사는 부분 결과를 먼저 쓰는 구현과 정상 응답만 확정하는 구현을 구별합니다. 테스트 이름보다 조건식을 읽어 무엇을 보장하는지 확인합니다.

재현 기록은 fixture, 호출 인자, 기대 코드, 실제 코드, 출력 보존 여부입니다. 21ms 실패 다음에 정상 fixture로 성공하는 결과도 적습니다. 함수는 상태를 저장하지 않으므로 이것이 재초기화 알고리즘 검증은 아닙니다. 버스 stuck·클럭 복구·장치 재설정은 이후 범위입니다. 구현한 범위를 증거와 함께 제한해 설명합니다. 정상으로 돌아오는 조건을 문서에 빠뜨리지 않습니다.

무응답 점검은 전원·GND·신호 전압, SDA/SCL·풀업, 주소 표현, 레지스터·길이, 제한 순으로 진행합니다. 주소 변경 뒤 ACK가 나왔다고 전압 안전성이 확인된 것은 아닙니다. PC에서는 표와 fixture로 연습합니다. 실물에는 측정 장비와 보드 문서가 필요합니다. 전압을 추측하지 않고 근거를 기록하는 습관을 익힙니다. 증거가 없는 원인을 확정하지 않습니다.

완료 기준은 성공 바이트뿐 아니라 20/21ms와 실패 후 메모리 보존을 설명하는 것입니다. 요청 한 번의 제한과 100ms 전체 마감 충족은 별도 검증입니다. fixture는 전송·저장 비용을 실제 시간으로 더하지 않습니다. 응답 시간 준수라는 넓은 결론을 내리지 않습니다. 더 읽기에서는 실제 트랜잭션·배선을 보충하고 여기서는 가상 API를 실행으로 확인합니다.

순서가 함수 결과를 바꾸지 않는지도 봅니다. SensorBus는 현재 fixture만 읽고 이전 실패를 정적 변수에 남기지 않습니다. 시간 초과 뒤 정상 delay_ms로 바꾸면 다음 요청은 성공합니다. 과거 플래그를 유지하는 구현은 다른 계약이므로 여기 넣지 않습니다. 후속 상태 머신의 누적 실패 계수는 HAL 반환과 상위 상태를 구분합니다. 오류를 처리한다는 이유로 하위 읽기 함수에 무한 재시도를 넣지 않습니다.

따라하기

starter 실패 조건 읽기

starter에서 BUS_TIMEOUT 및 출력 보존 FAIL을 찾습니다. buses.c의 TODO를 수정합니다.

make -s buses-test

완성 경계 재검사

수정 뒤 solution과 같은 결과인지 비교합니다. 알려진 20ms 성공·21ms 실패 조건을 test_buses.c에서 확인합니다.

make -s buses-test

실행 결과

sensor-buses: 79 cases, 0 failures

회귀와 재현 기록

전체 회귀를 실행하고 주소·분리·NACK·시간 초과별 입력과 실제 반환값을 제출합니다.

make test

확인 문제

실습

buses.c의 20ms 초과 제한 TODO를 구현합니다. BUS-SPEC.md를 읽고 tests 및 이전 events.c를 보존합니다. make test로 기존 회귀도 통과시킵니다. 오류별 입력과 출력 보존 근거를 제출합니다.

시작 코드·테스트 내려받기

실행 명령

make test

기대 결과

기존 회귀 및 sensor-buses: 79 cases, 0 failures; 종료 코드 0

모범 답안모범 답안 내려받기

더 읽기

면접 질문

  • 센서가 응답하지 않을 때 점검할 순서를 설명해 주시면 됩니다.