기술 가이드

DS18B20 scratchpad 검증: CRC-8·음수 온도·해상도 비트 처리

DS18B20의 9바이트 scratchpad CRC와 음수 온도, 9비트 미정의 비트를 처리하고 72가지 단일 비트 오류를 C11로 확인합니다.

목차

DS18B20을 연결한 뒤 음수가 큰 양수로 보이거나 간헐적으로 온도가 튄다면, 9바이트 scratchpad의 CRC와 온도 부호를 먼저 검증하세요. 이 글은 1-Wire 수신이 끝난 바이트 배열을 해석하는 MCU 코드에 집중합니다. 명령 타이밍·배선·기생 전원 제어는 Analog Devices DS18B20 데이터시트의 별도 절차에 따라 구현해야 합니다.

9바이트를 받은 뒤 검증하는 순서

바이트 인덱스 내용 처리
0·1 온도 LSB·MSB LSB 먼저 결합한 16비트 2의 보수
2·3 TH·TL 사용자 알람 설정
4 설정 레지스터 비트 6·5로 9~12비트 해상도 확인
5·6·7 예약 바이트 CRC 계산에 포함
8 CRC 원본 바이트 0~7 계산값과 비교

수신 길이가 9인지 확인하고 받은 바이트를 그대로 CRC 검사한 뒤 해상도에 따른 미정의 하위 비트를 제거합니다. CRC 전에 온도 바이트를 수정하면 유효한 수신 데이터도 불일치로 판단할 수 있습니다. 설정 레지스터는 bit7=0, bit4~0=1이라는 예약 비트 형식도 검사합니다. 모든 바이트가 0인 배열은 CRC만 보면 통과할 수 있으므로 CRC를 유일한 유효성 조건으로 삼지 않습니다.

CRC-8과 부호 확장

공식 1-Wire CRC 설명의 생성 다항식은 x^8 + x^5 + x^4 + 1입니다. 초기값 0에서 각 바이트의 LSB부터 처리하는 오른쪽 이동 구현에는 반사된 상수 0x8C를 사용합니다. SHT3x 등 다른 센서의 CRC 상수와 초기값을 그대로 가져오지 마세요. CRC는 전송 오류 검출용이며 보안 인증이나 측정 시각 검증 기능은 없습니다.

온도는 12비트 해상도에서 1LSB=1/16°C입니다. 0x0191은 401/16=25.0625°C, 0xFF5E는 (65374-65536)/16=-10.125°C입니다. C에서 범위를 넘는 unsigned 값을 signed 16비트로 바로 캐스팅하는 대신, 32비트 정수에서 부호 비트가 있으면 65,536을 빼면 변환 의도가 명확합니다.

해상도 설정 비트 R1:R0 제거할 하위 비트 최대 변환 시간
9비트 00 bit2~0 93.75ms
10비트 01 bit1~0 187.5ms
11비트 10 bit0 375ms
12비트 11 없음 750ms

해상도와 최대 시간은 데이터시트의 설정 표를 기준으로 합니다. 전원이 들어온 직후 온도 레지스터의 85°C 초기값은 CRC가 맞을 수 있습니다. 85°C라는 값만으로 무조건 센서 오류라고 단정하지 말고, 해당 센서에서 새 변환이 시작·완료됐는지 확인해야 합니다.

PC에서 검증할 C11 예제

아래 함수는 온도를 q16, 즉 1/16°C 단위의 정수로 반환합니다. 실수 계산이나 음수 정수 반올림을 섞지 않아 변환 규칙을 확인하기 쉽습니다. 인자로 받은 온도 출력은 성공할 때만 갱신되므로 호출자는 false일 때 이전 온도를 새 측정으로 사용하지 않아야 합니다. -55~125°C 범위를 벗어난 값도 거부합니다.

ds18b20_decode.c로 저장하고 gcc -std=c11 -Wall -Wextra -Werror ds18b20_decode.c -o ds18b20_decode로 빌드합니다. Linux는 ./ds18b20_decode, Windows는 ./ds18b20_decode.exe를 실행합니다.

#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

static uint8_t crc8(const uint8_t *data, size_t length)
{
uint8_t crc = 0u;
for (size_t i = 0u; i < length; ++i) {
uint8_t byte = data[i];
for (unsigned bit = 0u; bit < 8u; ++bit) {
uint8_t mix = (uint8_t)((crc ^ byte) & 1u);
crc >>= 1;
if (mix != 0u) crc ^= 0x8cu;
byte >>= 1;
}
}
return crc;
}

/* Output is in units of 1/16 degree C, not rounded milli-degrees. */
static bool decode(const uint8_t *sp, size_t length, int32_t *q16)
{
if (sp == NULL || q16 == NULL || length != 9u)
return false;
if (crc8(sp, 8u) != sp[8])
return false;
if ((sp[4] & 0x9fu) != 0x1fu)
return false;
unsigned resolution = 9u + ((sp[4] >> 5) & 3u);
unsigned undefined_bits = 12u - resolution;
uint16_t raw = (uint16_t)((uint16_t)sp[0] | ((uint16_t)sp[1] << 8));
raw = (uint16_t)(raw & (uint16_t)~((1u << undefined_bits) - 1u));
int32_t value = (raw & 0x8000u) != 0u ? (int32_t)raw - 65536 : (int32_t)raw;
if (value < -55 * 16 || value > 125 * 16)
return false;
*q16 = value;
return true;
}

int main(void)
{
const uint8_t digits[] = "123456789";
assert(crc8(digits, 9u) == 0xa1u);
uint8_t sp[9] = {0x91u,0x01u,0x4bu,0x46u,0x7fu,0xffu,0x0cu,0x10u,0x70u};
int32_t value = 0;
assert(decode(sp, sizeof sp, &value) && value == 401);
unsigned detected = 0u;
for (unsigned bit = 0u; bit < 72u; ++bit) {
uint8_t damaged[9];
memcpy(damaged, sp, sizeof sp);
damaged[bit / 8u] ^= (uint8_t)(1u << (bit % 8u));
assert(!decode(damaged, sizeof damaged, &value));
++detected;
}
assert(!decode(sp, 8u, &value));
const uint8_t all_zero[9] = {0u};
assert(!decode(all_zero, sizeof all_zero, &value));
sp[0] = 0x5eu; sp[1] = 0xffu; sp[8] = crc8(sp, 8u);
assert(decode(sp, sizeof sp, &value) && value == -162);
/* Low 3 bits are undefined at 9-bit resolution; mask them after CRC. */
sp[0] = 0x97u; sp[1] = 0x01u; sp[4] = 0x1fu; sp[8] = crc8(sp, 8u);
assert(decode(sp, sizeof sp, &value) && value == 400);
sp[0] = 0u; sp[1] = 0x10u; sp[8] = crc8(sp, 8u);
assert(!decode(sp, sizeof sp, &value));
assert(detected == 72u);
puts("DS18B20: +25.0625C, -10.125C, 9-bit mask and 72 bit flips passed");
return 0;
}

검증 출력:

DS18B20: +25.0625C, -10.125C, 9-bit mask and 72 bit flips passed

ASCII 123456789의 독립 CRC 검증값 0xA1, 고정 scratchpad의 CRC 0x70, 양수·음수 변환, 9비트 하위 비트 제거, 짧은 프레임, 0으로 채운 프레임, 범위 초과를 검사했습니다. 9바이트 각 비트를 하나씩 바꾼 72가지 오류도 모두 거부했습니다. 이 검증은 합성 데이터 해석 시험이며 실제 1-Wire 파형을 측정한 결과는 아닙니다.

MCU 드라이버에 붙일 때

  1. Reset 후 Presence 응답을 확인하고 대상 ROM을 선택합니다. 여러 센서가 있는 버스에서 Skip ROM으로 모두의 scratchpad를 동시에 읽으면 충돌할 수 있습니다.
  2. Convert T 명령 이후 사용한 전원 방식과 해상도에 맞춰 변환 완료를 확인합니다. 기생 전원에서는 변환 동안 필요한 strong pull-up과 제한 시간을 데이터시트대로 적용합니다.
  3. 대상 센서의 scratchpad 9바이트를 읽고 위 함수를 호출합니다. 실패 시 실패 카운터와 센서 식별자를 기록하고 제한된 횟수로 다시 읽습니다.
  4. 정상 데이터에는 ROM ID·측정 시각·변환 완료 상태를 함께 보관합니다. CRC가 맞더라도 전원 투입 초기값이나 이전 변환 값일 수 있습니다.
  5. CRC 오류가 계속되면 1-Wire 슬롯 타이밍, 케이블 길이, 풀업·전원, 버스 충돌을 점검합니다. 소프트웨어 재시도만 늘리면 배선 문제를 가릴 수 있습니다.

공식 자료 확인: 2026-10-11. 본문의 C 예제는 실제 컴파일·실행해 검증했습니다.

관련 글

댓글

첫 댓글을 남겨 보세요.

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

처리 중입니다...

잠시만 기다려주세요.