DS18B20 温度传感器

STM32F103C8T6  ·  HAL库开发  ·  1-Wire单总线通信

3.0-5.5V
工作电压
-55~125°C
测温范围
±0.5°C
测量精度
12-bit
分辨率
1-Wire
通信协议
向下滚动查看详情 ↓
CHAPTER 01

传感器原理与功能

1.1 工作原理

DS18B20是Dallas/Maxim公司推出的单总线数字温度传感器。其内部集成了温度传感电路、模数转换器和数字接口逻辑。温度测量基于带隙温度传感器原理:芯片内部有一个温度敏感振荡器和一个温度不敏感振荡器,通过计数两个振荡器的周期差值来获取温度数据,并将结果以16位数字量(二进制补码形式)存储在暂存器(Scratchpad)中。

DS18B20采用Dallas 1-Wire单总线协议,仅需一根数据线(DQ)即可实现双向通信。主机(STM32)通过严格的时序控制,在数据线上产生复位脉冲、写入命令字节和读取数据字节。芯片支持9~12位可编程分辨率,12位模式下精度达0.0625°C,转换时间约750ms。每个DS18B20出厂时拥有唯一的64位ROM序列号,支持单总线上挂载多个传感器。内置CRC8校验机制(多项式X⁸+X⁵+X⁴+1),确保数据传输的可靠性。

1.2 核心功能

📡

单总线通信

仅需一根数据线(DQ)即可完成双向数据传输,大幅节省MCU引脚资源和布线成本,支持长距离通信。

🌡️

高精度测温

12位分辨率下精度0.0625°C,测温范围-55°C~+125°C,在-10°C~+85°C范围内最大误差仅±0.5°C。

🔒

CRC8数据校验

暂存器第9字节为前8字节的CRC校验码,通信时自动校验数据完整性,有效避免传输错误导致的误读。

🔗

多设备挂载

每颗芯片拥有唯一64位ROM ID,单条总线上可挂载多个DS18B20,通过ROM命令逐一寻址,适合分布式测温。

1.3 引脚定义

DS18B20 引脚定义与连接关系

引脚号引脚名称功能描述STM32连接
1GND地线,电源负极GND
2DQ单总线数据通信引脚(开漏模式,需上拉电阻)PA0
3VDD电源正极(2.5V~5.5V)3.3V
CHAPTER 02

模块外观展示

DS18B20外观1
DS18B20 TO-92封装芯片
DS18B20外观2
DS18B20模块正面
CHAPTER 03

原理图与PCB封装

原理图
PCB封装
CHAPTER 04

程序分析

4.1 系统时钟配置 — SystemClock_Config()

配置STM32F103C8T6系统时钟为72MHz。使用外部高速晶振(HSE 8MHz),通过PLL倍频9倍得到72MHz系统时钟。AHB=72MHz,APB1=36MHz,APB2=72MHz,Flash等待周期2。

Core/Src/main.c — SystemClock_Config()
/* HSE外部晶振 8MHz → PLL×9 → SYSCLK 72MHz */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState       = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue  = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.PLL.PLLState  = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL    = RCC_PLL_MUL9;       // 8MHz × 9 = 72MHz

  HAL_RCC_OscConfig(&RCC_OscInitStruct);

  RCC_ClkInitStruct.SYSCLKSource  = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;      // HCLK  = 72MHz
  RCC_ClkInitStruct.APB1CLKDivider= RCC_HCLK_DIV2;        // PCLK1 = 36MHz
  RCC_ClkInitStruct.APB2CLKDivider= RCC_HCLK_DIV1;        // PCLK2 = 72MHz

  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}

4.2 DS18B20 GPIO初始化 — Ds18b20_GPIO_Init()

DS18B20数据引脚DQ连接STM32的PA0。配置为开漏输出模式(GPIO_MODE_OUTPUT_OD),启用内部上拉电阻,高速模式。开漏模式是1-Wire协议的要求——主机和从机都可以拉低总线,释放时由上拉电阻恢复高电平,实现"线与"逻辑。

HAL/DS18B20/ds18b20.c — Ds18b20_GPIO_Init()
#define DQ_Pin        GPIO_PIN_0      // PA0
#define DQ_GPIO_Port  GPIOA

void Ds18b20_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  DQ_GPIO_CLK_ENABLE();  // 使能GPIOA时钟

  GPIO_InitStruct.Pin    = DQ_Pin;                  // PA0
  GPIO_InitStruct.Mode   = GPIO_MODE_OUTPUT_OD;    // 开漏输出(1-Wire必需)
  GPIO_InitStruct.Pull   = GPIO_PULLUP;            // 内部上拉
  GPIO_InitStruct.Speed  = GPIO_SPEED_FREQ_HIGH;  // 高速
  HAL_GPIO_Init(DQ_GPIO_Port, &GPIO_InitStruct);
}

4.3 1-Wire复位时序 — DS_rest()

1-Wire通信的起始步骤。主机拉低DQ线750μs产生复位脉冲,然后释放总线。DS18B20检测到复位脉冲后,在15~60μs内拉低总线60~240μs产生存在脉冲(Presence Pulse)。主机通过检测此脉冲确认从机在线。

HAL/DS18B20/ds18b20.c — DS_rest()
void DS_rest(void)
{
  HAL_GPIO_WritePin(DQ_GPIO_Port, DQ_Pin, GPIO_PIN_SET);    // 释放总线
  delay_us(1);
  HAL_GPIO_WritePin(DQ_GPIO_Port, DQ_Pin, GPIO_PIN_RESET); // 拉低750us(复位脉冲)
  delay_us(750);
  HAL_GPIO_WritePin(DQ_GPIO_Port, DQ_Pin, GPIO_PIN_SET);    // 释放总线,等待存在脉冲
  delay_us(100);

  while(HAL_GPIO_ReadPin(DQ_GPIO_Port, DQ_Pin))     // 等待DS18B20拉低(存在脉冲)
  {
    delay_us(1);
    ds_cnt++;
    if(ds_cnt==200) { ds_cnt=0; break; }             // 超时退出(200us)
  }
  delay_us(300);
}

4.4 1-Wire写时序 — write_byte()

向DS18B20写入一个字节,LSB优先(低位先发)。每位时序:拉低总线2μs启动写时隙,然后将数据位写入DQ,保持30μs。写"1"时释放总线(由上拉拉高),写"0"时保持低电平。

HAL/DS18B20/ds18b20.c — write_byte()
void write_byte(uint8_t dat)
{
  unsigned char i;
  for(i=0; i<8; i++)
  {
    HAL_GPIO_WritePin(DQ_GPIO_Port, DQ_Pin, GPIO_PIN_SET);    // 释放
    delay_us(1);
    HAL_GPIO_WritePin(DQ_GPIO_Port, DQ_Pin, GPIO_PIN_RESET); // 拉低2us(启动写时隙)
    delay_us(2);
    HAL_GPIO_WritePin(DQ_GPIO_Port, DQ_Pin, (GPIO_PinState)(dat&0x01));  // 写入数据位
    delay_us(30);                                               // 保持30us
    dat = dat >> 1;                                           // 右移,LSB优先
  }
}

4.5 1-Wire读时序 — read_byte()

从DS18B20读取一个字节,LSB优先。每位时序:主机拉低总线2μs启动读时隙,然后释放总线2μs,接着读取DQ引脚电平。DS18B20在此时隙内将数据位驱动到总线上。读取后保持30μs完成时序。

HAL/DS18B20/ds18b20.c — read_byte()
unsigned char read_byte(void)
{
  unsigned char i, dat, tmp=0;
  for(i=0; i<8; i++)
  {
    HAL_GPIO_WritePin(DQ_GPIO_Port, DQ_Pin, GPIO_PIN_SET);    // 释放
    delay_us(1);
    HAL_GPIO_WritePin(DQ_GPIO_Port, DQ_Pin, GPIO_PIN_RESET); // 拉低2us(启动读时隙)
    delay_us(2);
    HAL_GPIO_WritePin(DQ_GPIO_Port, DQ_Pin, GPIO_PIN_SET);    // 释放,等待数据
    delay_us(2);
    dat = HAL_GPIO_ReadPin(DQ_GPIO_Port, DQ_Pin);           // 读取DQ电平
    tmp |= (dat<<i);                                            // 组装,LSB优先
    delay_us(30);
  }
  return tmp;
}

4.6 温度转换与读取 — Ds18b20_Read_Temp()

完整的温度读取流程:①复位 → Skip ROM(0xCC) → Convert T(0x44),触发温度转换;②复位 → Skip ROM(0xCC) → Read Scratchpad(0xBE),读取暂存器前2字节温度数据。温度原始值为16位补码,乘以0.0625得到实际温度。

  • 0xCC — Skip ROM:跳过ROM寻址(单设备时使用)
  • 0x44 — Convert T:启动温度转换
  • 0xBE — Read Scratchpad:读取9字节暂存器
HAL/DS18B20/ds18b20.c — Ds18b20_Read_Temp()
unsigned int Ds18b20_Read_Temp(void)
{
  unsigned char i, j;
  unsigned int tem = 0;

  /* 第一步: 触发温度转换 */
  DS_rest();              // 复位脉冲
  write_byte(0xCC);      // Skip ROM (跳过ROM寻址)
  write_byte(0x44);      // Convert T (启动温度转换)

  /* 第二步: 读取暂存器温度数据 */
  DS_rest();              // 再次复位
  write_byte(0xCC);      // Skip ROM
  write_byte(0xBE);      // Read Scratchpad (读暂存器)

  i = read_byte();       // 温度低字节 LSB (data[0])
  j = read_byte();       // 温度高字节 MSB (data[1])

  tem |= j & 0x0F;
  tem <<= 8;
  tem |= i;
  tem = (tem * 0.0625);   // 原始值 × 0.0625 = 实际温度
  return tem;
}

4.7 CRC8校验与温度计算 — DS18B20_Read_Temp_With_CRC()

读取完整9字节暂存器后,对前8字节进行CRC8校验(多项式0x8C,即X⁸+X⁵+X⁴+1)。校验通过后,从前2字节提取16位温度原始值,计算实际温度并放大10倍返回(便于OLED显示1位小数)。

HAL/DS18B20/ds18b20.c — DS18B20_Check_CRC() & DS18B20_Read_Temp_With_CRC()
/* CRC8校验函数 — 多项式: X^8+X^5+X^4+1 (0x8C) */
uint8_t DS18B20_Check_CRC(uint8_t *data, uint8_t len)
{
  uint8_t crc = 0;
  for (uint8_t i = 0; i < len; i++) {
    crc ^= data[i];                         // 逐字节异或
    for (uint8_t j = 0; j < 8; j++) {
      if (crc & 0x01)
        crc = (crc >> 1) ^ 0x8C;             // 多项式异或
      else
        crc >>= 1;
    }
  }
  return crc;
}

/* 读取温度数据并进行CRC校验 */
int16_t DS18B20_Read_Temp_With_CRC(void)
{
  uint8_t data[9];                            // 9字节暂存器
  Ds18b20_Read_Temp();                       // 触发温度转换
  DS18B20_Read_Scratchpad(data, 9);           // 读取9字节数据

  uint8_t crc_calc = DS18B20_Check_CRC(data, 8); // 计算前8字节CRC

  if (crc_calc == data[8]) {                   // CRC校验通过
    int16_t temp = ((data[1]<<8)|data[0]) * 10 / 16.0;
    return temp;                           // 返回温度×10
  } else {
    return 0x7FFF;                          // 校验失败,返回错误值
  }
}

温度计算公式解析

原始值 Raw = (data[1] << 8) | data[0]   // 16位补码
实际温度(°C) = Raw × 0.0625   // 12位分辨率,LSB = 0.0625°C
返回值 = Raw × 10 / 16 = Raw × 0.625 = 温度 × 10   // 放大10倍
OLED显示 = 返回值 / 10.0   // 恢复1位小数,如 250 → 25.0℃
实际温度原始值(Hex)原始值(Dec)返回值(×10)OLED显示
-55°C0xFC90-880-550-55.0℃
-10°C0xFF5E-160-100-10.0℃
0°C0x0000000.0℃
25.0°C0x019040025025.0℃
25.5°C0x019A41025625.6℃
85°C0x0550136085085.0℃
125°C0x07D020001250125.0℃

4.8 主循环逻辑 — while(1)

主循环每500ms执行一次温度读取和OLED刷新。使用HAL_GetTick()获取系统滴答定时器计数值,实现非阻塞式定时。读取温度后调用OLED_Clear()清屏,OLED_CenterPrintf()居中显示温度,最后OLED_Update()刷新显存到屏幕。

Core/Src/main.c — main() while(1)
while (1)
{
  if(HAL_GetTick() - Time_Now >= 500)             // 500ms更新一次
  {
    temp = DS18B20_Read_Temp_With_CRC();    // 读取温度(CRC校验)

    OLED_Clear();                                  // 清屏
    OLED_CenterPrintf(24, "温度:%.1f℃", temp/10.0); // 居中显示温度
    OLED_Update();                                // 更新OLED显示

    Time_Now = HAL_GetTick();                   // 更新时间戳
  }
}
CHAPTER 05

程序流程图

开始 HAL_Init() 系统初始化 SystemClock_Config() 72MHz MX_GPIO_Init() + OLED_Init() + Ds18b20_GPIO_Init() PA0 while(1) HAL_GetTick() - Time_Now ≥ 500? 是 DS18B20_Read_Temp_With_CRC() 读取温度 + CRC8校验 OLED_Clear() + CenterPrintf() 显示"温度:XX.X℃" OLED_Update() + Time_Now更新 刷新显示 + 记录时间戳 否
CHAPTER 06

交互式动画演示

DS18B20 传感器模拟

VDD DQ GND 3.3V PA0 GND DS18B20 25.0 °C
DQ (PA0) — 1-Wire
① 复位脉冲 (750μs低电平)
② Skip ROM (0xCC)
③ Convert T (0x44)
④ 复位脉冲 + Skip ROM
⑤ Read Scratchpad (0xBE)
⑥ 读取9字节 + CRC8校验
⑦ 计算温度 → OLED显示

OLED显示与数据仪表

温度:25.0℃
100 75 50 25 0
原始值 Raw0x0190
温度值 (×10)250
实际温度25.0 °C
CRC8校验✓ 通过
更新间隔500 ms
通信协议1-Wire (PA0)

动画说明

拖动滑块模拟环境温度变化,点击"开始演示"后系统将模拟STM32每500ms读取一次DS18B20温度的完整过程。左侧展示1-Wire通信协议步骤和DQ数据线脉冲,右侧OLED屏幕实时显示温度值,温度计随温度变化颜色和高度,数据面板显示原始值、计算结果和CRC校验状态。