본 게시글에서는 아나로그디바이스 DS18B20 온도 센서 여러 개를 동시에 연결하고 사용하는 방법을 설명합니다. 이 센서의 뛰어난 특징 중 하나는 CRC 데이터 무결성 검사를 지원하는 1-Wire 통신 프로토콜 인터페이스로, 데이터 라인 자체에서 동작 전력을 얻기 때문에 별도의 외부 전원 라인이 필요 없습니다. 각 센서는 고유한 64비트 레이저 각인 ROM 코드를 가지고 있어서 여러 개의 온도 센서가 하나의 1-Wire 버스를 동시에 공유할 수 있습니다.

시스템은 광범위한 영역에 분산되어 있는 많은 DS18B20 센서들을 제어할 수 있으며, 이러한 기능을 통해 주변 온도 모니터링, 건물, 산업 장비 또는 기계류 내부 온도 제어, 그리고 공정 모니터링 및 제어 시스템과 같은 다양한 응용 분야에 적용할 수 있습니다.
DS18B20 온도 센서의 주요 특징은 다음과 같습니다:
- 차별화된 1-Wire® 인터페이스 - 통신에 하나의 포트 핀만 필요
- 온도 센서와 EEPROM을 통합하여 부품 수 절감
- -55 °C ~ +125 °C (-67 °F ~ +257 °F) 범위의 온도 측정
- -10 °C ~ +85 °C 범위에서 ±0.5 °C의 정확도
- 9비트에서 12비트까지 프로그래밍 가능한 해상도 (설정 레지스터)
- 외부 부품 불필요 - 기생 전원 (parasite power) 모드 동작 시 DQ와 GND 2 개의 핀만 필요
- 멀티드롭 (multidrop) 기능으로 분산형 온도 감지 애플리케이션 간소화
- 각 소자는 온보드 ROM에 고유한 64비트 직렬 코드를 저장
- 알람 검색 명령으로 설정된 온도 범위를 벗어난 소자를 식별할 수 있는 사용자 정의 가능한 비휘발성 (NV) 알람 설정
- 8핀 SO (150 mil), 8핀 µSOP, 3핀 TO-92 패키지로 제공
이번 데모에 사용된 DS18B20 온도 센서 3개는 아래와 같은 TO-92 패키지입니다.
DS18B20 온도 센서의 블록 다이어그램은 다음과 같습니다:
DS18B20의 핵심 기능은 직접 디지털 변환 방식의 온도 센서라는 점입니다. 온도 센서의 해상도는 설정 레지스터를 통해 9, 10, 11, 또는 12비트로 사용자 설정이 가능하며, 이는 각각 0.5 °C, 0.25 °C, 0.125 °C, 0.0625 °C의 분해능에 해당합니다. 전원이 인가될 때의 기본 해상도는 12비트입니다. DS18B20이 출력하는 온도 데이터는 섭씨 (Celsius) 기준으로 보정되어 있어서, 화씨 (Fahrenheit) 단위로 사용하려면 룩업 테이블 또는 변환 루틴을 사용해야만 합니다. 온도 데이터는 다음 표와 같이 16비트 부호 확장 (sign-extended) 2의 보수 형태로 온도 레지스터에 저장됩니다.
DS18B20은 필요에 따라 다음 블록 다이어그램과 같이 VDD 핀에 외부 전원을 연결하는 전통적인 방식으로 전원을 공급받을 수도 있습니다.
DS18B20의 내부 메모리 구성은 아래와 같습니다.
메모리는 SRAM 스크래치패드와 상·하한 알람 트리거 레지스터 (TH와 TL) 및 설정 레지스터를 저장하는 비휘발성 EEPROM로 구성됩니다. DS18B20의 알람 기능을 사용하지 않는 경우, TH와 TL 레지스터를 범용 메모리 공간으로 활용할 수 있습니다.
버스 마스터가 ROM 명령 (0x55)을 사용해 통신하려는 DS18B20를 지정한 후에야, 마스터는 DS18B20의 기능 명령 중 하나를 전송할 수 있습니다. 이 명령들을 통해 버스 마스터는 DS18B20의 스크래치패드 메모리를 읽거나 쓸 수 있으며, 온도 변환을 시작하거나 전원 공급 모드를 확인할 수도 있습니다. DS18B20의 기능 명령들은 다음 표와 같습니다.
이번 데모에는 아래 사진의 구성을 사용하였습니다. 라즈베리 파이 피코 2 W의 36번 핀 3V3과 38번 핀 GND를 통해 3.3 V 전원을 공급하고, 20번 핀 GP15를 1-Wire 데이터 버스로 사용하여 여기에 3개의 DS18B20 온도 센서와 3.3 V 풀업 저항을 연결하였습니다.
동시 통신 기능을 시연하기 위해 DS18B20 온도 센서 3개를 1-Wire 버스에 연결하였습니다. 이 데모에서는 피코 2 W를 사용하여, 보드 내부의 PIO (Programmable I/O) 상태 머신에 onewire_library.pio를 로드해 맞춤형 1-Wire 통신 프로토콜 인터페이스를 구현하고 C SDK 기반 C 애플리케이션으로 이를 제어하였습니다. 다음은 본 데모에 사용된 onewire_library.pio PIO 상태 머신 정의 파일입니다.
;
; Copyright (c) 2023 mjcross
;
; SPDX-License-Identifier: BSD-3-Clause
;
; Implements a Maxim 1-Wire bus with a GPIO pin.
;
; Place data words to be transmitted in the TX FIFO and read the results from the
; RX FIFO. To reset the bus execute a jump to 'reset_bus' using the opcode from
; the provided function.
;
; At 1us per cycle as initialised below the timings are those recommended by:
; https://www.analog.com/en/technical-articles/1wire-communication-through-software.html
;
; Notes:
; (1) The code will stall with the bus in a safe state if the FIFOs are empty/full.
; (2) The bus must be pulled up with an external pull-up resistor of about 4k.
; The internal GPIO resistors are too high (~50k) to work reliably for this.
; (3) Do not connect the GPIO pin directly to a bus powered at more than 3.3V.
.program onewire
.side_set 1 pindirs
PUBLIC reset_bus:
set x, 28 side 1 [15] ; pull bus low 16
loop_a: jmp x-- loop_a side 1 [15] ; 29 x 16
set x, 8 side 0 [6] ; release bus 7
loop_b: jmp x-- loop_b side 0 [6] ; 9 x 7
mov isr, pins side 0 ; read all pins to ISR (avoids autopush) 1
push side 0 ; push result manually 1
set x, 24 side 0 [7] ; 8
loop_c: jmp x-- loop_c side 0 [15] ; 25 x 16
.wrap_target
PUBLIC fetch_bit:
out x, 1 side 0 ; shift next bit from OSR (autopull) 1
jmp !x send_0 side 1 [5] ; pull bus low, branch if sending '0' 6
send_1: ; send a '1' bit
set x, 2 side 0 [8] ; release bus, wait for slave response 9
in pins, 1 side 0 [4] ; read bus, shift bit to ISR (autopush) 5
loop_e: jmp x-- loop_e side 0 [15] ; 3 x 16
jmp fetch_bit side 0 ; 1
send_0: ; send a '0' bit
set x, 2 side 1 [5] ; continue pulling bus low 6
loop_d: jmp x-- loop_d side 1 [15] ; 3 x 16
in null, 1 side 0 [8] ; release bus, shift 0 to ISR (autopush) 9
.wrap
;; (17 instructions)
% c-sdk {
static inline void onewire_sm_init (PIO pio, uint sm, uint offset, uint pin_num, uint bits_per_word) {
// create a new state machine configuration
pio_sm_config c = onewire_program_get_default_config (offset);
// Input Shift Register configuration settings
sm_config_set_in_shift (
&c,
true, // shift direction: right
true, // autopush: enabled
bits_per_word // autopush threshold
);
// Output Shift Register configuration settings
sm_config_set_out_shift (
&c,
true, // shift direction: right
true, // autopull: enabled
bits_per_word // autopull threshold
);
// configure the input and sideset pin groups to start at `pin_num`
sm_config_set_in_pins (&c, pin_num);
sm_config_set_sideset_pins (&c, pin_num);
// configure the clock divider for 1 usec per instruction
float div = clock_get_hz (clk_sys) * 1e-6;
sm_config_set_clkdiv (&c, div);
// apply the configuration and initialise the program counter
pio_sm_init (pio, sm, offset + onewire_offset_fetch_bit, &c);
// enable the state machine
pio_sm_set_enabled (pio, sm, true);
}
static inline uint onewire_reset_instr (uint offset) {
// encode a "jmp reset_bus side 0" instruction for the state machine
return pio_encode_jmp (offset + onewire_offset_reset_bus) | pio_encode_sideset (1, 0);
}
%}
main.c 프로그램은 다음과 같습니다.
/**
* Copyright (c) 2023 mjcross
*
* SPDX-License-Identifier: BSD-3-Clause
**/
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "onewire_library.h" // onewire library functions
#include "ow_rom.h" // onewire ROM command codes
#include "ds18b20.h" // ds18b20 function codes
// Demonstrates the PIO onewire driver by taking readings from a set of
// ds18b20 1-wire temperature sensors.
int main() {
stdio_init_all();
PIO pio = pio0;
uint gpio = 15;
OW ow;
uint offset;
// add the program to the PIO shared address space
if (pio_can_add_program (pio, &onewire_program)) {
offset = pio_add_program (pio, &onewire_program);
// claim a state machine and initialise a driver instance
if (ow_init (&ow, pio, offset, gpio)) {
// find and display 64-bit device addresses
int maxdevs = 10;
uint64_t romcode[maxdevs];
int num_devs = ow_romsearch (&ow, romcode, maxdevs, OW_SEARCH_ROM);
printf("Found %d devices\n", num_devs);
for (int i = 0; i < num_devs; i += 1) {
printf("\t%d: 0x%llx\n", i, romcode[i]);
}
putchar ('\n');
while (num_devs > 0) {
// start temperature conversion in parallel on all devices
// (see ds18b20 datasheet)
ow_reset (&ow);
ow_send (&ow, OW_SKIP_ROM);
ow_send (&ow, DS18B20_CONVERT_T);
// wait for the conversions to finish
while (ow_read(&ow) == 0);
// read the result from each device
for (int i = 0; i < num_devs; i += 1) {
ow_reset (&ow);
ow_send (&ow, OW_MATCH_ROM);
for (int b = 0; b < 64; b += 8) {
ow_send (&ow, romcode[i] >> b);
}
ow_send (&ow, DS18B20_READ_SCRATCHPAD);
int16_t temp = 0;
temp = ow_read (&ow) | (ow_read (&ow) << 8);
printf ("\t%d: %f C", i, temp / 16.0);
//Convert from C to F (Simple conversion for test purposes only)
float K1 = 1.8;
float K2 = 32.0;
float temp_F = temp*K1/16.0 + K2;
printf ("\t%d: %f F", i, temp_F);
}
putchar ('\n');
}
} else {
puts ("could not initialise the driver");
}
} else {
puts ("could not add the program");
}
while(true);
}
이 프로그램을 다음과 같이 컴파일하고 피코 2 W에 플래시 하였습니다.
DigiKey_Coffee_Cup # make -j8
DigiKey_Coffee_Cup # picotool load pio_onewire.uf2
minicom 터미널을 사용하여 3개의 DS18B20 온도 센서로부터 섭씨 단위의 온도 값을 모니터링하였습니다.
DigiKey_Coffee_Cup # minicom -D /dev/ttyACM0
이전 글에 따라 설치한 SparkFun 로직 분석기를 사용하여 아래와 같이 1-Wire 프로토콜을 모니터링하였습니다. 피코 2 W를 재부팅한 후, 3개의 DS18B20 온도 센서로부터 주기적으로 폴링 된 각 센서의 측정값은 아래와 같습니다. minicom 터미널과 로직 분석기의 캡쳐를 통해 DS18B20 온도 센서 3개 각각에 대한 64비트 레이저 각인 ROM 코드도 확인할 수 있었습니다.
로직 분석기를 캡쳐한 다음 그림은 DS18B20과 유사한 DS28EA00 1-Wire 프로토콜 디코더를 사용해 1-Wire 네트워크 계층을 확대한 것으로, Reset/Presence 검출 과정과 ROM 명령 (0x55)을 보여줍니다. 64비트 ROM 코드 시퀀스가 정확히 일치하는 슬레이브만 마스터가 내린 기능 명령에 응답하며, 버스에 연결된 다른 모든 슬레이브들은 리셋 펄스를 기다립니다. 또한, 1-Wire 버스를 공유하는 DS18B20 온도 센서 3개 중 첫 번째로 조회된 센서의 응답도 아래에서 확인할 수 있습니다.
SparkFun 로직 분석기에는 1-Wire 링크 계층 및 1-Wire 네트워크 계층 디코더가 내장되어 있으며, 이번 데모에서는 위 그림의 설명과 같이 DS18B20과 유사한 DS28EA00 1-Wire 프로토콜 디코더를 사용하였습니다. 로직 분석기는 이러한 설계, 테스트 및 문제 해결 과정을 손쉽게 수행할 수 있도록 도와줍니다.
이번 데모에서는 C SDK와 라즈베리 파이 피코 2 W를 사용하여 단일 1-Wire 라인에 3개의 DS18B20 온도 센서를 인터페이스하는 방법을 보여주었습니다. DS18B20 온도 센서는 시스템에 필요한 제어선 수에 제약이 있는 분산형 온도 감지 설계에 매우 훌륭한 제품입니다. 기생 전원 모드 동작 시 DQ와 GND 2개의 핀만 필요해서 멀티드롭 기능을 갖춘 분산형 온도 감지 애플리케이션을 보다 단순하게 구현할 수 있습니다. 이런 다재다능한 DS18B20 온도 센서를 디지키에서 구매하실 수 있습니다.
이와 유사한 특성을 가진 또 다른 온도 센서로는 DS28EA00가 있으며, 히스테리시스 (Hysteresis) 및 글리치 (Glitch) 필터 기능을 갖춘 개선된 1-Wire 인터페이스와 2개의 범용 PIO 핀 그리고 네트워크 내 장치들의 물리적 순서를 감지하는 체인 상태 전이 (Chain State Transition) 기능을 갖추고 있습니다. DS28EA00 온도 센서 역시 디지키에서 구매하실 수 있습니다.
좋은 하루 보내세요!









