활용 분야
DS18B20 디지털 온도 센서는 정밀한 온도 측정이 필요한 다양한 임베디드 시스템에 널리 사용됩니다. 주요 적용 분야로는 실내외 환경 모니터링, 스마트 홈 기후 제어 시스템, 산업용 장비의 과열 방지 메커니즘, 서버실 및 배터리 팩 열 관리, 그리고 스마트 농업용 온실 제어 등이 있습니다. 또한, 단일 버스 통신 프로토콜의 타이밍 제어를 학습하는 교육용 프로젝트로도 매우 적합합니다.
필수 하드웨어 구성 요소
- DS18B20 디지털 온도 센서 모듈 1개
- STM32F103 기반 마이크로컨트롤러 개발 보드 1개
- 점퍼 케이블 (암-암, 수-암 등)
- 4.7kΩ 풀업 저항 (모듈에 내장되지 않은 경우 필수)
1-Wire 프로토콜 및 동작 원리
DS18B20은 단일 버스(1-Wire) 통신 방식을 사용합니다. 이 프로토콜은 단 하나의 데이터 라인(DQ)과 접지(GND)만으로 양방향 통신을 수행합니다. 데이터 라인은 오픈 드레인 구조이므로, 안정적인 논리 'High' 레벨을 유지하기 위해 반드시 VCC와 DQ 사이에 풀업 저항(일반적으로 4.7kΩ)을 연결해야 합니다.
센서 내부의 아날로그-디지털 변환기(ADC)는 측정된 아날로그 온도 값을 디지털 데이터로 변환합니다. 기본 해상도는 12비트이며, 온도 값은 다음과 같은 수식을 통해 실제 섭씨 온도로 환산됩니다.
실제 온도(°C) = 읽은 원시 데이터(Raw Data) × 0.0625
하드웨어 배선 및 핀 매핑
전원 및 접지 연결은 공통적으로 적용되며, 데이터 핀은 사용하는 라이브러리에 따라 마이크로컨트롤러의 서로 다른 포트에 할당됩니다.
- GND: 개발 보드의 GND 핀에 연결
- VCC: 3.3V 또는 5V 전원에 연결
- DQ (표준 펌웨어 라이브러리): PA4 핀에 연결
- DQ (HAL 라이브러리): PB5 핀에 연결
펌웨어 구현 예제: 표준 펌웨어 라이브러리 (Standard Peripheral Library)
다음은 표준 라이브러리를 사용하여 PA4 핀에 연결된 DS18B20 센서를 제어하고, 측정된 온도를 USART를 통해 출력하는 코드입니다. 변수명과 함수 구조를 직관적으로 재구성했습니다.
#include "stm32f10x.h"
#include "stdio.h"
#include "delay.h" // 사용자 정의 마이크로초 지연 함수 포함
#define SENSOR_PORT GPIOA
#define SENSOR_PIN GPIO_Pin_4
#define SENSOR_RCC RCC_APB2Periph_GPIOA
char uart_buffer[32];
int16_t current_temp = 0;
void Configure_Sensor_Pin(void) {
GPIO_InitTypeDef pin_config;
RCC_APB2PeriphClockCmd(SENSOR_RCC, ENABLE);
pin_config.GPIO_Pin = SENSOR_PIN;
pin_config.GPIO_Mode = GPIO_Mode_Out_PP;
pin_config.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SENSOR_PORT, &pin_config);
GPIO_SetBits(SENSOR_PORT, SENSOR_PIN);
}
void Send_Reset_Pulse(void) {
GPIO_InitTypeDef pin_config;
// 출력 모드 설정 및 Low 유지
pin_config.GPIO_Pin = SENSOR_PIN;
pin_config.GPIO_Mode = GPIO_Mode_Out_PP;
pin_config.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SENSOR_PORT, &pin_config);
GPIO_ResetBits(SENSOR_PORT, SENSOR_PIN);
Delay_Us(500); // 480us 이상 Low 유지
// High로 전환 후 존재 응답(Presence Pulse) 대기
GPIO_SetBits(SENSOR_PORT, SENSOR_PIN);
Delay_Us(60);
// 입력 모드로 전환하여 센서의 Low 응답 확인
pin_config.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(SENSOR_PORT, &pin_config);
while(GPIO_ReadInputDataBit(SENSOR_PORT, SENSOR_PIN) == SET);
while(GPIO_ReadInputDataBit(SENSOR_PORT, SENSOR_PIN) == RESET);
}
void Transmit_Byte(uint8_t data_byte) {
GPIO_InitTypeDef pin_config;
pin_config.GPIO_Pin = SENSOR_PIN;
pin_config.GPIO_Mode = GPIO_Mode_Out_PP;
pin_config.GPIO_Speed = GPIO_Speed_50MHz;
for (uint8_t i = 0; i < 8; i++) {
GPIO_Init(SENSOR_PORT, &pin_config);
GPIO_ResetBits(SENSOR_PORT, SENSOR_PIN);
Delay_Us(2);
if (data_byte & 0x01) {
GPIO_SetBits(SENSOR_PORT, SENSOR_PIN);
Delay_Us(60);
} else {
Delay_Us(60);
GPIO_SetBits(SENSOR_PORT, SENSOR_PIN);
}
data_byte >>= 1;
Delay_Us(2);
}
}
uint8_t Receive_Byte(void) {
uint8_t result = 0;
GPIO_InitTypeDef pin_out = {SENSOR_PIN, GPIO_Mode_Out_PP, GPIO_Speed_50MHz};
GPIO_InitTypeDef pin_in = {SENSOR_PIN, GPIO_Mode_IPU, GPIO_Speed_50MHz};
for (uint8_t i = 0; i < 8; i++) {
result >>= 1;
GPIO_Init(SENSOR_PORT, &pin_out);
GPIO_ResetBits(SENSOR_PORT, SENSOR_PIN);
Delay_Us(2);
GPIO_Init(SENSOR_PORT, &pin_in);
Delay_Us(10);
if (GPIO_ReadInputDataBit(SENSOR_PORT, SENSOR_PIN) == SET) {
result |= 0x80;
}
Delay_Us(50);
}
return result;
}
void Fetch_Temperature(void) {
uint8_t lsb = 0, msb = 0;
int16_t raw_temp;
Send_Reset_Pulse();
Transmit_Byte(0xCC); // Skip ROM
Transmit_Byte(0x44); // Convert T
Delay_Ms(750); // 12비트 변환 대기
Send_Reset_Pulse();
Transmit_Byte(0xCC); // Skip ROM
Transmit_Byte(0xBE); // Read Scratchpad
lsb = Receive_Byte();
msb = Receive_Byte();
raw_temp = (msb << 8) | lsb;
if (raw_temp < 0) {
current_temp = (int16_t)((~raw_temp + 1) * -0.0625 * 10);
} else {
current_temp = (int16_t)(raw_temp * 0.0625 * 10 + 0.5);
}
}
int main(void) {
SystemInit();
USART1_Init(); // 사용자 정의 UART 초기화
Delay_Init(); // SysTick 기반 지연 함수 초기화
Configure_Sensor_Pin();
while (1) {
Fetch_Temperature();
sprintf(uart_buffer, "Temp: %d.%d C\r\n", current_temp / 10, abs(current_temp % 10));
USART1_SendString(uart_buffer);
Delay_Ms(1000);
}
}
펌웨어 구현 예제: HAL 라이브러리 (Hardware Abstraction Layer)
STM32CubeHAL을 활용하여 PB5 핀을 제어하는 방식입니다. GPIO 모드 전환을 효율적으로 처리하도록 로직을 수정했습니다.
#include "main.h"
#include "usart.h"
#include
#include
#define DQ_PORT GPIOB
#define DQ_PIN GPIO_PIN_5
void Micro_Delay(uint16_t us); // 타이머 기반 마이크로초 지연 함수
static void Set_Pin_Output(void) {
GPIO_InitTypeDef gpio = {0};
gpio.Pin = DQ_PIN;
gpio.Mode = GPIO_MODE_OUTPUT_PP;
gpio.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DQ_PORT, &gpio);
}
static void Set_Pin_Input(void) {
GPIO_InitTypeDef gpio = {0};
gpio.Pin = DQ_PIN;
gpio.Mode = GPIO_MODE_INPUT;
gpio.Pull = GPIO_NOPULL;
HAL_GPIO_Init(DQ_PORT, &gpio);
}
void Sensor_Reset(void) {
Set_Pin_Output();
HAL_GPIO_WritePin(DQ_PORT, DQ_PIN, GPIO_PIN_RESET);
Micro_Delay(500);
HAL_GPIO_WritePin(DQ_PORT, DQ_PIN, GPIO_PIN_SET);
Micro_Delay(30);
}
uint8_t Verify_Presence(void) {
uint8_t presence = 0;
Set_Pin_Input();
Micro_Delay(60);
if (HAL_GPIO_ReadPin(DQ_PORT, DQ_PIN) == GPIO_PIN_RESET) {
presence = 1;
}
Micro_Delay(240);
return presence;
}
uint8_t Read_Single_Bit(void) {
uint8_t bit_val = 0;
Set_Pin_Output();
HAL_GPIO_WritePin(DQ_PORT, DQ_PIN, GPIO_PIN_RESET);
Micro_Delay(2);
Set_Pin_Input();
Micro_Delay(12);
if (HAL_GPIO_ReadPin(DQ_PORT, DQ_PIN) == GPIO_PIN_SET) {
bit_val = 1;
}
Micro_Delay(50);
return bit_val;
}
uint8_t Read_Byte_Data(void) {
uint8_t byte_val = 0;
for (uint8_t i = 0; i < 8; i++) {
byte_val >>= 1;
if (Read_Single_Bit()) {
byte_val |= 0x80;
}
}
return byte_val;
}
void Write_Byte_Data(uint8_t byte_val) {
for (uint8_t i = 0; i < 8; i++) {
Set_Pin_Output();
HAL_GPIO_WritePin(DQ_PORT, DQ_PIN, GPIO_PIN_RESET);
if (byte_val & 0x01) {
Micro_Delay(2);
HAL_GPIO_WritePin(DQ_PORT, DQ_PIN, GPIO_PIN_SET);
Micro_Delay(60);
} else {
Micro_Delay(60);
HAL_GPIO_WritePin(DQ_PORT, DQ_PIN, GPIO_PIN_SET);
Micro_Delay(2);
}
byte_val >>= 1;
}
}
uint8_t Initialize_Sensor(void) {
__HAL_RCC_GPIOB_CLK_ENABLE();
Sensor_Reset();
return Verify_Presence();
}
float Get_Temperature(void) {
uint8_t lsb, msb;
int16_t raw_data;
float temp_c;
Sensor_Reset();
Verify_Presence();
Write_Byte_Data(0xCC);
Write_Byte_Data(0x44);
HAL_Delay(750);
Sensor_Reset();
Verify_Presence();
Write_Byte_Data(0xCC);
Write_Byte_Data(0xBE);
lsb = Read_Byte_Data();
msb = Read_Byte_Data();
raw_data = (msb << 8) | lsb;
temp_c = raw_data * 0.0625f;
return temp_c;
}
int main(void) {
HAL_Init();
SystemClock_Config();
MX_USART1_UART_Init();
char tx_buffer[32];
float measured_temp = 0.0f;
while (!Initialize_Sensor()) {
HAL_UART_Transmit(&huart1, (uint8_t*)"Sensor Not Found\r\n", 18, HAL_MAX_DELAY);
HAL_Delay(1000);
}
while (1) {
measured_temp = Get_Temperature();
sprintf(tx_buffer, "Temperature: %.2f C\r\n", measured_temp);
HAL_UART_Transmit(&huart1, (uint8_t*)tx_buffer, strlen(tx_buffer), HAL_MAX_DELAY);
HAL_Delay(1000);
}
}