전자회로 실무 / 전자회로 실무·I²C 측정

레지스터가 없는 I²C ADC에서는 명령 바이트 없이 두 바이트를 읽습니다 (Commandless I²C ADC Read)

배터리 전압을 읽는 작은 보드를 연결했는데 I²C 주소 검색에는 장치가 보이고 측정 프로그램은 오류를 낸다면, 주소만 고치기 전에 프로그램이 어떤 버스 거래를 보내는지 확인해야 합니다. 10비트 아날로그-디지털 변환기(ADC, analog-to-digital converter) 중에는 읽을 레지스터 주소가 없는 제품이 있습니다. 이런 장치에 일반적인 ‘레지스터 워드 읽기’ 함수를 쓰면, 함수가 먼저 보내는 명령 바이트 때문에 원래 필요한 두 바이트 읽기와 다른 거래가 됩니다. 이 글을 따라가면 주소 확인, 거래 형식, 비트 조립, 전압 계산을 각각 따로 점검할 수 있습니다.

먼저 알아둘 용어

I²C(Inter-Integrated Circuit)는 데이터선 SDA(Serial Data)와 클록선 SCL(Serial Clock)을 공유하는 직렬 통신입니다. 제어기는 시작 조건(Start), 장치 주소와 읽기/쓰기 비트, 데이터, 종료 조건(Stop)을 순서대로 보냅니다. ACK(acknowledge)는 주소나 데이터에 대한 응답이며, ACK가 보였다고 해서 뒤따르는 명령의 의미까지 맞았다는 뜻은 아닙니다.

ADC(analog-to-digital converter)는 입력 전압을 숫자로 바꿉니다. 10비트 ADC의 결과 코드는 0부터 1023까지입니다. 여기서 다루는 예시의 데이터 형식은 16개의 전송 비트 가운데 앞 4비트와 뒤 2비트가 측정값이 아니고, 가운데 10비트만 유효합니다. 다른 ADC의 배열에는 이 식을 그대로 적용하지 않습니다.

레지스터 읽기(register read)는 먼저 레지스터 주소를 쓰고 그 위치의 데이터를 읽는 거래입니다. 명령 없는 읽기(commandless read)는 쓰기 명령 없이 장치 주소의 읽기 비트만 보내고 필요한 길이만큼 데이터를 받습니다. 라이브러리 함수 이름에 ‘word’가 들어 있어도 두 거래가 같다고 볼 수 없습니다.

명령 없는 I²C ADC의 두 바이트에서 유효한 열 비트를 조립하는 비트 배치입니다.
명령 없는 I²C ADC의 두 바이트에서 유효한 열 비트를 조립하는 비트 배치입니다. · 클릭하면 확대됩니다.

주소 응답과 실제 데이터 읽기는 서로 다른 검사입니다

먼저 보드 도면과 부품 표기로 사용 전압, 접지, SDA·SCL 핀, 풀업 저항, ADC의 주소 선택형을 확인합니다. I²C 버스 번호는 컴퓨터·운영체제 설정에 따라 달라지므로 i2cdetect -l 등으로 사용할 버스를 확인합니다. 예시 회로의 MCP3021 주소는 0x4D지만 부품의 주소 옵션과 주변 장치 구성이 다르면 이 숫자도 달라집니다. 버스 전체를 무턱대고 검색하기보다는 도면과 부품 주소를 먼저 확인하세요. 주소 검색 도구는 일부 장치의 동작에 영향을 줄 수 있습니다.

MCP3021은 쓸 수 있는 레지스터가 없습니다. 제조사 설명에 따르면 읽기 비트가 붙은 주소를 보내면 변환이 시작되고, 이어서 상위·하위 데이터 바이트를 받습니다. 쓰기 주소에 ACK를 돌려줄 수도 있지만, 그 뒤 레지스터 명령을 받아들이는 구조는 아닙니다. 따라서 ‘주소 검색에서 응답했다’고 해서 read_word_data(address, 0) 같은 호출이 맞다는 결론을 내리면 안 됩니다. 이 SMBus(System Management Bus) 워드 읽기 형식은 장치에 쓰기 주소 → 명령 바이트 → 다시 읽기 주소를 보낸 뒤 두 바이트를 받습니다. 이 ADC의 정상 거래와 순서부터 다릅니다.

Linux의 i2c-tools가 있고, 버스 1과 주소 0x4D를 도면으로 확인한 시험 보드라면 다음 읽기 거래를 확인할 수 있습니다. 명령 바이트가 전혀 들어가지 않는다는 점이 핵심입니다. i2ctransfer는 실제 장치에 거래를 보내므로, 연결된 버스와 주소를 검증한 뒤 실행하고 표시되는 경고를 읽으세요. 운영 중인 공유 버스에는 임의 탐색을 하지 마세요.

i2ctransfer 1 r2@0x4d

이 명령의 1은 버스 번호, r2는 두 바이트 읽기, @0x4d는 7비트 주소입니다. 사용 중인 장치에 이미 커널 드라이버가 붙어 있다면 강제 접근하지 말고 그 드라이버가 제공하는 측정 방법을 쓰세요. 오류가 나면 공급 전압·공통 접지·풀업과 SDA/SCL 파형, 주소 ACK를 확인한 뒤 거래 형식을 다시 봅니다.

받은 두 바이트에서 10비트 코드만 꺼냅니다

MCP3021은 먼저 0000 D9 D8 D7 D6, 다음에 D5 D4 D3 D2 D1 D0 X X를 보냅니다. D9는 결과의 최상위 비트, D0는 최하위 비트이고 X는 버리는 비트입니다. 따라서 받은 순서대로 첫 바이트를 hi, 둘째를 lo라 두면 다음과 같습니다.

code = ((hi & 0x0F) << 6) | (lo >> 2)

예를 들어 hi=0x08, lo=0x00이면 code=512입니다. 라이브러리가 16비트 정수를 만들어 돌려주더라도 바이트 순서가 전송 순서와 같은지 먼저 확인해야 합니다. ‘워드’ 값을 통째로 두 자리 이동시키면 어느 쪽 바이트가 상위에 들어갔는지에 따라 결과가 달라집니다. 알려진 입력 전압 하나만 볼 것이 아니라, 낮은 입력과 중간 입력을 함께 대조하면 바이트 순서 오류를 더 쉽게 찾을 수 있습니다.

ADC 입력 전압 V_IN의 이상적인 환산에는 실제 ADC 공급 전압 V_DD가 필요합니다. 제조사가 제시하는 1최하위 비트(LSB, least significant bit)의 크기는 V_DD/1024입니다. 따라서 근사값은 V_IN ≈ code × V_DD / 1024입니다. V_DD=3.3 V, code=512라는 설명용 조건에서는 약 1.65 V입니다. 프로그램 안의 임의 상수 5.1 V나 분모 1023을 다른 회로로 복사하지 마세요. ADC 전원, 기준 전압, 입력 분압과 보정값을 실제 보드에서 확인해야 합니다.

배터리 전압을 저항 두 개로 나눠 ADC에 넣었다면, 위 식은 ADC 핀 전압까지만 줍니다. 위쪽 저항 R_TOP, 아래쪽 저항 R_BOTTOM으로 만든 분압이라면 V_BAT ≈ V_IN × (R_TOP+R_BOTTOM)/R_BOTTOM으로 되돌립니다. 이때 ADC 입력이 허용 범위를 넘지 않는지, 분압점의 소스 저항이 ADC 샘플링 조건을 만족하는지도 확인합니다. 해당 보드의 저항 값과 기준 전압을 알 수 없으면 정확한 배터리 전압을 계산했다고 주장할 수 없습니다.

전압을 배터리 잔량으로 바꿀 때는 다른 근거가 필요합니다

여러 번 읽어 평균을 내면 우연한 잡음은 줄어들 수 있지만, 잘못된 바이트 배열·기준 전압·분압비는 고쳐지지 않습니다. 리튬이온 셀의 순간 전압을 두 임의의 끝점 사이에 직선으로 놓고 ‘잔량 %’를 계산한 값도 검증된 충전 상태(state of charge)가 아닙니다. 부하 전류, 충전 직후의 전압 회복, 온도와 셀 상태가 전압에 영향을 줍니다. 정확한 잔량이 필요하면 사용 셀과 동작 조건에서 얻은 방전 곡선이나 전용 연료 게이지(fuel gauge)의 근거를 따로 마련하세요.

마지막으로 알려진 입력을 두세 지점에 인가해 원시 두 바이트, 조립된 코드, ADC 핀 전압, 분압을 되돌린 배터리 전압을 한 줄씩 기록합니다. 이 네 값 중 어디부터 어긋나는지 찾으면 버스 거래 오류와 계산 오류를 구분할 수 있습니다. 이 절차가 통과한 다음에야 표시용 잔량 계산을 평가할 수 있습니다.