기술 가이드

BMP280 온도 보정: 계수 부호·바이트 순서·20비트 ADC 검증

BMP280 보정 계수의 부호와 바이트 순서, 20비트 ADC를 해독하고 공식 계산 예시와 전체 입력 범위를 Python으로 검증합니다.

목차

BMP280에서 읽은 숫자를 섭씨로 쓰려면 센서별 보정 계수를 적용해야 합니다. UART 로그에 원시값과 계수를 함께 남기면 센서가 연결되지 않은 PC에서도 변환 오류를 재현할 수 있습니다. 이 글은 기존 보드의 펌웨어 점검을 위한 호스트 계산 실습입니다. 실물 센서 통신·측정 정확도 시험은 포함하지 않습니다.

레지스터와 자료형을 먼저 맞추기

Bosch BMP280 공식 데이터시트 Rev.1.26의 §3.11과 §4.3.7을 기준으로 합니다.

읽기 시작 주소 바이트 수 해석
0x88 6 dig_T1: unsigned 16비트, dig_T2·dig_T3: signed 16비트. 각 계수는 LSB 먼저
0xFA 3 온도 ADC: MSB 먼저 이어 붙인 뒤 하위 4비트를 제외한 unsigned 20비트

두 블록의 바이트 순서가 다릅니다. 보정 계수에 쓰는 little-endian 설정을 ADC 전체에 적용하면 안 됩니다. §3.12 예시 표에는 UT·UP와 주소 표기가 서로 뒤바뀐 부분이 있으므로 온도 주소는 §4.3.7의 0xFA~0xFC를 따릅니다.

Python으로 계산하고 재현하기

아래 전체 코드를 bmp280_temperature.py로 저장하고 Python 3에서 실행합니다. 외부 패키지는 필요 없습니다. 검증문을 유지하도록 -O 옵션은 쓰지 않습니다.

python -B bmp280_temperature.py
"""Host-side BMP280 temperature decoding exercise; no hardware access."""


def calibration(data):
    if len(data) != 6:
        raise ValueError("read exactly 6 bytes from 0x88")
    return (int.from_bytes(data[0:2], "little"),
            int.from_bytes(data[2:4], "little", signed=True),
            int.from_bytes(data[4:6], "little", signed=True))


def adc_temperature(data):
    if len(data) != 3:
        raise ValueError("read exactly 3 bytes from 0xFA")
    return int.from_bytes(data, "big") // 16


def temperature_centi(adc, coefficients):
    if adc not in range(1048576):
        raise ValueError("adc must be an unsigned 20-bit value")
    t1, t2, t3 = coefficients
    linear = ((adc // 8 - 2 * t1) * t2) // 2048
    delta = adc // 16 - t1
    quadratic = ((delta * delta // 4096) * t3) // 16384
    fine = linear + quadratic
    return (5 * fine + 128) // 256, fine


if __name__ == "__main__":
    trim = calibration(bytes.fromhex("70 6B 43 67 18 FC"))
    adc = adc_temperature(bytes.fromhex("7E ED 00"))
    assert trim == (27504, 26435, -1000)
    assert adc == 519888
    assert temperature_centi(adc, trim) == (2508, 128422)
    # The six-byte parser must retain unsigned T1 and signed T2/T3.
    for word in range(65536):
        pair = word.to_bytes(2, "little")
        unsigned, signed, _ = calibration(pair + pair + b"\0\0")
        assert unsigned == word
        assert signed == (word if word in range(32768) else word - 65536)
    # Every ADC code round-trips; ignored low nibble cannot alter the result.
    for value in range(1048576):
        packed = (value * 16).to_bytes(3, "big")
        assert adc_temperature(packed) == value
        assert adc_temperature(packed[:2] + bytes([packed[2] | 15])) == value
    for function, value in [(calibration, b"\0" * 5),
                            (adc_temperature, b"\0" * 2)]:
        try:
            function(value)
        except ValueError:
            pass
        else:
            raise AssertionError("short read accepted")
    for invalid in (-1, 1048576):
        try:
            temperature_centi(invalid, trim)
        except ValueError:
            pass
        else:
            raise AssertionError("out-of-range ADC accepted")
    print("25.08 C; t_fine=128422")
    print("PASS: 65536 signed words, 1048576 ADC codes, 4 invalid inputs")

공식 §3.12의 계수와 원시값을 넣으면 2508(0.01°C 단위), 즉 25.08°C와 t_fine=128422가 나옵니다. 위 바이트열은 그 수치로 구성한 합성 입력입니다. 실제 장치에는 그 장치에서 읽은 계수를 넣어야 합니다.

코드는 고정소수점 단계마다 음의 무한대 방향 정수 나눗셈을 사용합니다. Python의 //는 음수에서도 이 규칙을 지키므로 산술 오른쪽 시프트를 수식으로 표현할 수 있습니다. MCU C로 옮길 때는 중간 곱셈 범위, 음수 시프트와 나눗셈 규칙을 별도로 확인해야 합니다.

이번 실습에서 확인하는 것

실행 결과는 다음과 같습니다.

25.08 C; t_fine=128422
PASS: 65536 signed words, 1048576 ADC codes, 4 invalid inputs

공식 계산 예시 하나를 기준점으로 확인하고, 16비트 전체 입력에서 signed/unsigned 구분을 검사합니다. 20비트 전체 입력에는 하위 니블을 0과 15로 바꿔 넣어도 ADC가 같아야 합니다. 짧은 읽기 두 건과 범위 밖 ADC 두 건도 거부합니다. 이 검사는 프레임 해독과 계산을 대상으로 하며, 모든 생산 계수 조합의 정확도를 보증하는 검사는 아닙니다.

보드에 적용할 때 확인할 상태

§3.3.2·§4.3.3에 따라 온도 측정 활성화와 측정 완료를 먼저 확인합니다. 초기화 때 im_update를 확인하고, 변환 중인 레지스터를 최신 측정값으로 취급하지 않습니다. 측정 생략 시 출력되는 0x80000은 설정·상태와 함께 해석해야 합니다. 이 순수 계산 함수는 센서의 활성화·완료 상태를 알 수 없습니다.

로그에는 계수 6바이트, ADC 3바이트, 측정 설정과 시각을 같이 남겨 보세요. 고정 계수를 복사했는지, 계수 부호가 바뀌었는지, 읽기 길이가 부족했는지를 비교하기 쉬워집니다. 압력 보정까지 구현한다면 동일 측정에 대응하는 t_fine을 전달해야 합니다.

관련 글

댓글

첫 댓글을 남겨 보세요.

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

처리 중입니다...

잠시만 기다려주세요.