STM32 마이크로컨트롤러를 활용한 DS18B20 1-Wire 온도 센서 제어 및 데이터 읽기

활용 분야

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);
    }
}

태그: STM32 ds18b20 1-Wire Standard Peripheral Library STM32CubeHAL

6월 30일 00:23에 게시됨