DS18B20技术文档

DS18B20数字温度传感器技术总结文档

文档说明
本文档基于DS18B20中文手册整理,适用于蓝桥杯嵌入式开发学习
创建日期:2026-02-01


:bookmark_tabs: 目录

  1. 器件概述
  2. 引脚定义与电路连接
  3. 工作原理
  4. 操作流程详解
  5. 关键时序参数
  6. 编程实现要点
  7. 常见问题与解决方案
  8. 实战代码模板

1. 器件概述

1.1 核心特性

特性 参数说明
通信接口 单总线(1-Wire),仅需1个I/O口
测温范围 -55℃ ~ +125℃
精度 -10℃~+85℃范围内 ±0.5℃
分辨率 9/10/11/12位可编程(0.5/0.25/0.125/0.0625℃)
转换时间 93.75ms(9位) ~ 750ms(12位)
供电方式 外部供电(3.0-5.5V) 或 寄生供电
唯一标识 64位ROM序列号(支持多点总线)

1.2 典型应用场景

  • :white_check_mark: 温度监控与报警系统
  • :white_check_mark: 工业过程控制
  • :white_check_mark: 环境监测
  • :white_check_mark: HVAC系统
  • :white_check_mark: 蓝桥杯嵌入式竞赛项目

2. 引脚定义与电路连接

2.1 引脚说明

TO-92封装(3引脚):

   正面视图
   ┌─────┐
   │  1  │ GND    - 接地
   │  2  │ DQ     - 数据I/O(单总线)
   │  3  │ VDD    - 电源(寄生供电时接地)
   └─────┘

2.2 典型连接电路

方式1:外部供电(推荐)

        VCC (3.3V/5V)
         │
         ├───────[4.7kΩ上拉电阻]
         │                │
      ┌──┴──┐            │
      │ VDD │            │
      │     │         单总线
      │DS18B│◄───────────┤─────► MCU I/O
      │ 20  │
      │ GND │
      └──┬──┘
         │
        GND

优点:

  • :white_check_mark: 稳定可靠
  • :white_check_mark: 适用于全温度范围(-55℃ ~ +125℃)
  • :white_check_mark: 无需强上拉电路

方式2:寄生供电

        VCC (3.3V/5V)
         │
         └───────[4.7kΩ上拉电阻]
                        │
      ┌──────┐          │
      │ VDD  │接GND     │
      │      │       单总线
      │DS18B │◄─────────┤─────► MCU I/O
      │ 20   │                      │
      │ GND  │                  [强上拉电路]
      └──┬───┘                      │
         │                         VCC
        GND

注意事项:

  • :warning: VDD引脚必须接地
  • :warning: 温度转换和EEPROM写入时需强上拉
  • :warning: 不推荐温度>100℃时使用(漏电流大)

3. 工作原理

3.1 内部结构

┌────────────────────────────────────────┐
│              DS18B20                    │
│  ┌──────────┐    ┌──────────────┐     │
│  │温度传感器├────►│12位ADC转换器 │     │
│  └──────────┘    └──────┬───────┘     │
│                         │              │
│  ┌──────────┐    ┌──────▼───────┐     │
│  │64位ROM   │    │  暂存器      │     │
│  │(序列号)  │    │  (9字节)     │     │
│  └──────────┘    └──────┬───────┘     │
│                         │              │
│  ┌──────────┐    ┌──────▼───────┐     │
│  │EEPROM    │◄───┤ 单总线控制   │     │
│  │(TH/TL)   │    │  逻辑        │     │
│  └──────────┘    └──────┬───────┘     │
│                         │              │
│                        DQ ──────────────┼──► 单总线
└────────────────────────────────────────┘

3.2 暂存器结构(9字节)

字节 功能 读/写 说明
0 温度LSB 只读 温度低8位
1 温度MSB 只读 温度高8位(含符号位)
2 TH寄存器 读写 高温报警阈值
3 TL寄存器 读写 低温报警阈值
4 配置寄存器 读写 分辨率设置
5-7 保留位 - 读取全为1
8 CRC校验 只读 前8字节的CRC值

3.3 温度数据格式

12位模式(默认):

MSB                               LSB
┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
│S │S │S │S │S │2⁶│2⁵│2⁴│2³│2²│2¹│2⁰│2⁻¹│2⁻²│2⁻³│2⁻⁴│
└──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0

S = 符号位(0=正,1=负)
分辨率 = 0.0625℃

温度计算公式:

正数:温度(℃) = 原码值 × 0.0625
负数:温度(℃) = (补码-1取反) × 0.0625 × (-1)

示例:

0x0191 (十六进制) = 0000 0001 1001 0001 (二进制)
     = 1×16 + 9×1 + 1×0.0625 = 25.0625℃

0xFF5E (十六进制) = 1111 1111 0101 1110 (二进制)
     = -(取反+1) = -10.125℃

4. 操作流程详解

4.1 标准操作序列(必须遵守!)

┌─────────────┐
│  步骤1:    │
│  初始化     │  发送复位脉冲 → 接收存在脉冲
└──────┬──────┘
       │
       ▼
┌─────────────┐
│  步骤2:    │
│  ROM指令    │  选择特定器件或跳过ROM
└──────┬──────┘
       │
       ▼
┌─────────────┐
│  步骤3:    │
│  功能指令   │  执行温度转换、读写等操作
└─────────────┘

4.2 ROM指令详解

指令1:搜索ROM [F0h]

用途: 识别总线上所有器件的64位序列号

// 应用场景:系统初始化时扫描总线
void SearchROM(void) {
    DS18B20_Reset();        // 初始化
    DS18B20_WriteByte(0xF0); // 搜索ROM指令
    // 执行64位序列号搜索算法...
}

指令2:读取ROM [33h]

用途: 读取64位序列号(仅单器件有效

// 应用场景:单器件系统获取序列号
uint8_t rom[8];
void ReadROM(void) {
    DS18B20_Reset();
    DS18B20_WriteByte(0x33);
    for(int i=0; i<8; i++) {
        rom[i] = DS18B20_ReadByte();
    }
    // rom[0] = 系列码(0x28)
    // rom[1-6] = 48位序列号
    // rom[7] = CRC校验
}

指令3:匹配ROM [55h]

用途: 选择特定器件操作

// 应用场景:多器件系统中选择特定传感器
void MatchROM(uint8_t *rom) {
    DS18B20_Reset();
    DS18B20_WriteByte(0x55);
    for(int i=0; i<8; i++) {
        DS18B20_WriteByte(rom[i]);
    }
}

指令4:跳过ROM [CCh](常用!)

用途: 忽略ROM选择,操作总线上所有器件

// 应用场景:单器件系统快速操作
void SkipROM(void) {
    DS18B20_Reset();
    DS18B20_WriteByte(0xCC);
}

指令5:报警搜索 [ECh]

用途: 查找温度超限的器件

// 应用场景:温度报警系统
void AlarmSearch(void) {
    DS18B20_Reset();
    DS18B20_WriteByte(0xEC);
    // 仅报警器件会响应...
}

4.3 功能指令详解

指令1:温度转换 [44h] :star:最常用

// 外部供电模式
void ConvertTemperature(void) {
    DS18B20_Reset();
    DS18B20_WriteByte(0xCC);  // 跳过ROM
    DS18B20_WriteByte(0x44);  // 温度转换指令

    // 方式1:等待固定时间
    Delay_ms(750);  // 12位模式需750ms

    // 方式2:读忙标志(推荐)
    while(DS18B20_ReadBit() == 0); // 转换中返回0,完成返回1
}

// 寄生供电模式(需强上拉!)
void ConvertTemp_Parasite(void) {
    DS18B20_Reset();
    DS18B20_WriteByte(0xCC);
    DS18B20_WriteByte(0x44);

    // ⚠️ 关键:在10μs内启动强上拉
    DQ_StrongPullup_Enable();  // 将DQ直接连接VCC
    Delay_ms(750);
    DQ_StrongPullup_Disable();
}

指令2:读暂存器 [BEh] :star:最常用

// 读取温度值
float ReadTemperature(void) {
    uint8_t temp_l, temp_h;
    int16_t temp;

    DS18B20_Reset();
    DS18B20_WriteByte(0xCC);  // 跳过ROM
    DS18B20_WriteByte(0xBE);  // 读暂存器

    temp_l = DS18B20_ReadByte();  // 温度LSB
    temp_h = DS18B20_ReadByte();  // 温度MSB

    temp = (temp_h << 8) | temp_l;
    return temp * 0.0625;  // 12位分辨率
}

指令3:写暂存器 [4Eh]

// 设置报警温度和分辨率
void WriteConfig(int8_t th, int8_t tl, uint8_t config) {
    DS18B20_Reset();
    DS18B20_WriteByte(0xCC);
    DS18B20_WriteByte(0x4E);  // 写暂存器
    DS18B20_WriteByte(th);    // 高温阈值
    DS18B20_WriteByte(tl);    // 低温阈值
    DS18B20_WriteByte(config);// 配置寄存器
}

// 配置寄存器格式:
// bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
//  0    R1   R0   1    1    1    1    1
//
// R1 R0 | 分辨率 | 转换时间
//  0  0 |  9位   | 93.75ms
//  0  1 | 10位   | 187.5ms
//  1  0 | 11位   | 375ms
//  1  1 | 12位   | 750ms(默认)

指令4:拷贝暂存器 [48h]

// 将TH、TL、配置保存到EEPROM
void CopyScratchpad(void) {
    DS18B20_Reset();
    DS18B20_WriteByte(0xCC);
    DS18B20_WriteByte(0x48);

    // 寄生供电需强上拉10ms
    #ifdef PARASITE_POWER
        DQ_StrongPullup_Enable();
        Delay_ms(10);
        DQ_StrongPullup_Disable();
    #else
        Delay_ms(10);  // 外部供电也需等待
    #endif
}

指令5:召回EEPROM [B8h]

// 从EEPROM恢复TH、TL、配置(上电自动执行)
void RecallEEPROM(void) {
    DS18B20_Reset();
    DS18B20_WriteByte(0xCC);
    DS18B20_WriteByte(0xB8);

    while(DS18B20_ReadBit() == 0); // 等待召回完成
}

指令6:读电源模式 [B4h]

// 检测供电方式
bool IsPowerExternal(void) {
    DS18B20_Reset();
    DS18B20_WriteByte(0xCC);
    DS18B20_WriteByte(0xB4);

    return DS18B20_ReadBit();  // 1=外部供电,0=寄生供电
}

5. 关键时序参数

5.1 初始化时序(复位/存在脉冲)

主机              ─────┐         ┌──────────────
                       │         │
                       └─────────┘
                       ├─480μs─►├──►
                                ├15-60μs
从机  ────────────────────┐   ┌───────────────
                          └───┘
                          60-240μs

代码实现:

bool DS18B20_Reset(void) {
    bool presence;

    DQ_OUT();          // DQ设为输出
    DQ_LOW();          // 拉低总线
    Delay_us(480);     // 保持≥480μs

    DQ_IN();           // DQ设为输入(释放总线)
    Delay_us(70);      // 等待70μs
    presence = !DQ_READ();  // 读取存在脉冲(低电平有效)

    Delay_us(410);     // 完成复位时序(480μs总计)
    return presence;   // true=器件存在
}

5.2 写时序

写0时序

主机  ─────┐                       ┌───
           └───────────────────────┘
           ├60-120μs────────────────►
           ├─►├←15μs采样窗口

写1时序

主机  ─────┐   ┌───────────────────────
           └───┘
           ├1-15μs
               ├60-120μs────────────►

代码实现:

void DS18B20_WriteBit(uint8_t bit) {
    DQ_OUT();
    DQ_LOW();          // 启动写时序

    if(bit) {
        Delay_us(2);   // 写1:快速释放(2μs内)
        DQ_IN();
        Delay_us(58);  // 总共60μs
    } else {
        Delay_us(60);  // 写0:保持低电平60μs
        DQ_IN();
    }
    Delay_us(2);       // 恢复时间
}

void DS18B20_WriteByte(uint8_t data) {
    for(int i=0; i<8; i++) {
        DS18B20_WriteBit(data & 0x01);
        data >>= 1;
    }
}

5.3 读时序

主机  ─────┐ ┌─────────────────────
           └─┘
           1μs ├15μs采样窗口►

从机  ──────┐ ┌───  (读1)
           └─┘

       ──────────────  (读0)

代码实现:

uint8_t DS18B20_ReadBit(void) {
    uint8_t bit;

    DQ_OUT();
    DQ_LOW();          // 启动读时序
    Delay_us(2);       // 保持≥1μs

    DQ_IN();           // 释放总线
    Delay_us(10);      // 等待从机响应
    bit = DQ_READ();   // 采样(15μs内)

    Delay_us(50);      // 完成60μs时序
    return bit;
}

uint8_t DS18B20_ReadByte(void) {
    uint8_t data = 0;
    for(int i=0; i<8; i++) {
        data >>= 1;
        if(DS18B20_ReadBit()) {
            data |= 0x80;
        }
    }
    return data;
}

5.4 时序参数表

参数 符号 最小值 典型值 最大值 单位
复位脉冲低电平时间 tRSTL 480 - - μs
复位脉冲高电平时间 tRSTH 480 - - μs
存在脉冲低电平时间 tPDLOW 60 - 240 μs
写时序总时间 tSLOT 60 - 120 μs
写0低电平时间 tLOW0 60 - 120 μs
写1低电平时间 tLOW1 1 - 15 μs
读时序总时间 tSLOT 60 - 120 μs
读采样时间 - - - 15 μs
恢复时间 tREC 1 - - μs

6. 编程实现要点

6.1 GPIO配置要求

// 单总线必须配置为开漏输出或准双向I/O

// STM32示例
void DS18B20_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct;

    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;  // 开漏输出
    GPIO_InitStruct.Pull = GPIO_NOPULL;          // 外部上拉
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

// 51单片机示例
sbit DQ = P1^0;
void DS18B20_GPIO_Init(void) {
    DQ = 1;  // P1^0配置为准双向口,初始高电平
}

6.2 延时函数精度要求

// ⚠️ 微秒级延时必须精确!建议使用定时器或汇编实现

// 方法1:使用SysTick定时器(STM32)
void Delay_us(uint32_t us) {
    uint32_t ticks = us * (SystemCoreClock / 1000000);
    SysTick->LOAD = ticks;
    SysTick->VAL = 0;
    SysTick->CTRL = 0x01;  // 使能,时钟源=系统时钟
    while(!(SysTick->CTRL & 0x10000));
    SysTick->CTRL = 0;
}

// 方法2:使用硬件定时器
void Delay_us(uint16_t us) {
    TIM_SetCounter(TIM2, 0);
    TIM_Cmd(TIM2, ENABLE);
    while(TIM_GetCounter(TIM2) < us);
    TIM_Cmd(TIM2, DISABLE);
}

// 方法3:汇编循环(51单片机,12MHz晶振)
void Delay_us(uint16_t us) {
    while(us--) {
        _nop_();  // 1个机器周期 = 1μs
    }
}

6.3 CRC校验实现

// CRC-8/MAXIM算法(多项式:x^8 + x^5 + x^4 + 1)
uint8_t DS18B20_CRC8(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;  // 0x8C = 多项式
            } else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

// 读取并校验温度
float ReadTemp_WithCRC(void) {
    uint8_t data[9];

    DS18B20_Reset();
    DS18B20_WriteByte(0xCC);
    DS18B20_WriteByte(0xBE);

    for(int i=0; i<9; i++) {
        data[i] = DS18B20_ReadByte();
    }

    // CRC校验
    if(DS18B20_CRC8(data, 8) != data[8]) {
        return -999.0;  // 校验失败
    }

    int16_t temp = (data[1] << 8) | data[0];
    return temp * 0.0625;
}

6.4 温度转换函数(支持负温度)

float DS18B20_GetTemperature(void) {
    uint8_t temp_l, temp_h;
    int16_t temp_raw;
    float temperature;

    // 启动转换
    DS18B20_Reset();
    DS18B20_WriteByte(0xCC);
    DS18B20_WriteByte(0x44);
    Delay_ms(750);  // 等待转换完成

    // 读取温度
    DS18B20_Reset();
    DS18B20_WriteByte(0xCC);
    DS18B20_WriteByte(0xBE);
    temp_l = DS18B20_ReadByte();
    temp_h = DS18B20_ReadByte();

    // 合并为16位有符号数
    temp_raw = (int16_t)((temp_h << 8) | temp_l);

    // 转换为摄氏度(12位分辨率)
    temperature = temp_raw * 0.0625;

    return temperature;
}

// 提取整数和小数部分(用于显示)
void GetTempParts(float temp, int *integer, int *decimal) {
    if(temp < 0) {
        temp = -temp;
        *integer = -(int)temp;
    } else {
        *integer = (int)temp;
    }
    *decimal = (int)((temp - (int)temp) * 10000);  // 保留4位小数
}

7. 常见问题与解决方案

问题1:读不到存在脉冲

症状: DS18B20_Reset() 返回false

可能原因:

  1. :cross_mark: 硬件连接错误
  2. :cross_mark: 上拉电阻未接或阻值不对
  3. :cross_mark: 延时函数不准确
  4. :cross_mark: GPIO配置错误

解决方案:

// 调试代码
void Debug_Reset(void) {
    // 1. 检查GPIO配置
    DQ_OUT();
    DQ_LOW();
    Delay_ms(100);  // 示波器应看到低电平
    DQ_IN();
    Delay_ms(100);  // 示波器应看到被上拉到高电平

    // 2. 测试复位时序
    DQ_OUT();
    DQ_LOW();
    Delay_us(480);
    DQ_IN();
    Delay_us(70);
    if(DQ_READ() == 0) {
        // 存在脉冲检测到
    }
}

硬件检查清单:

  • 上拉电阻4.7kΩ已接到VCC
  • DQ、GND、VDD三根线连接正确
  • 寄生供电模式下VDD已接地
  • 电源电压3.0-5.5V范围内

问题2:温度读数错误或不变

症状: 始终读到+85℃或奇怪的值

可能原因:

  1. :cross_mark: 未等待转换完成
  2. :cross_mark: 读写时序不正确
  3. :cross_mark: 温度数据处理错误

解决方案:

// 方法1:增加转换等待时间
DS18B20_WriteByte(0x44);
Delay_ms(800);  // 增加到800ms

// 方法2:轮询忙标志
DS18B20_WriteByte(0x44);
while(DS18B20_ReadBit() == 0);  // 等待转换完成

// 方法3:检查数据有效性
float temp = DS18B20_GetTemperature();
if(temp == 85.0) {
    // 可能是上电默认值,需重新转换
}
if(temp < -55 || temp > 125) {
    // 超出范围,数据无效
}

问题3:多个传感器冲突

症状: 多器件时只有一个正常工作

解决方案:

// 正确的多器件操作流程
uint8_t ROM1[8] = {0x28, 0xAA, ...};  // 器件1的ROM
uint8_t ROM2[8] = {0x28, 0xBB, ...};  // 器件2的ROM

float ReadTemp_MultiDevice(uint8_t *rom) {
    // 步骤1:匹配特定器件
    DS18B20_Reset();
    DS18B20_WriteByte(0x55);  // 匹配ROM
    for(int i=0; i<8; i++) {
        DS18B20_WriteByte(rom[i]);
    }

    // 步骤2:启动转换
    DS18B20_WriteByte(0x44);
    Delay_ms(750);

    // 步骤3:再次匹配器件
    DS18B20_Reset();
    DS18B20_WriteByte(0x55);
    for(int i=0; i<8; i++) {
        DS18B20_WriteByte(rom[i]);
    }

    // 步骤4:读取温度
    DS18B20_WriteByte(0xBE);
    uint8_t temp_l = DS18B20_ReadByte();
    uint8_t temp_h = DS18B20_ReadByte();

    return ((int16_t)((temp_h << 8) | temp_l)) * 0.0625;
}

问题4:寄生供电模式不工作

症状: 温度转换失败或读数错误

解决方案:

// 强上拉电路实现(使用额外GPIO)

// 硬件连接:
// GPIO_STRONG_PULLUP ──[MOSFET]── VCC
//                              │
//                             DQ

void EnableStrongPullup(void) {
    GPIO_SetBits(GPIOA, GPIO_Pin_1);  // 导通MOSFET
}

void DisableStrongPullup(void) {
    GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}

// 温度转换
DS18B20_WriteByte(0x44);
EnableStrongPullup();   // ⚠️ 10μs内启动
Delay_ms(750);
DisableStrongPullup();

8. 实战代码模板

8.1 完整驱动程序(STM32)

/************************ ds18b20.h ************************/
#ifndef __DS18B20_H
#define __DS18B20_H

#include "stm32f1xx_hal.h"
#include <stdbool.h>

// 硬件连接定义
#define DS18B20_GPIO_PORT       GPIOA
#define DS18B20_GPIO_PIN        GPIO_PIN_0
#define DS18B20_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()

// GPIO操作宏
#define DQ_OUT() do { \
    GPIO_InitTypeDef GPIO_InitStruct = {0}; \
    GPIO_InitStruct.Pin = DS18B20_GPIO_PIN; \
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; \
    GPIO_InitStruct.Pull = GPIO_NOPULL; \
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; \
    HAL_GPIO_Init(DS18B20_GPIO_PORT, &GPIO_InitStruct); \
} while(0)

#define DQ_IN() do { \
    GPIO_InitTypeDef GPIO_InitStruct = {0}; \
    GPIO_InitStruct.Pin = DS18B20_GPIO_PIN; \
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT; \
    GPIO_InitStruct.Pull = GPIO_NOPULL; \
    HAL_GPIO_Init(DS18B20_GPIO_PORT, &GPIO_InitStruct); \
} while(0)

#define DQ_LOW()  HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET)
#define DQ_HIGH() HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET)
#define DQ_READ() HAL_GPIO_ReadPin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN)

// ROM指令
#define CMD_SEARCH_ROM    0xF0
#define CMD_READ_ROM      0x33
#define CMD_MATCH_ROM     0x55
#define CMD_SKIP_ROM      0xCC
#define CMD_ALARM_SEARCH  0xEC

// 功能指令
#define CMD_CONVERT_T     0x44
#define CMD_WRITE_SCRATCHPAD  0x4E
#define CMD_READ_SCRATCHPAD   0xBE
#define CMD_COPY_SCRATCHPAD   0x48
#define CMD_RECALL_EEPROM     0xB8
#define CMD_READ_POWER        0xB4

// 分辨率配置
#define RESOLUTION_9BIT   0x1F  // 0001 1111
#define RESOLUTION_10BIT  0x3F  // 0011 1111
#define RESOLUTION_11BIT  0x5F  // 0101 1111
#define RESOLUTION_12BIT  0x7F  // 0111 1111 (默认)

// API函数
void DS18B20_Init(void);
bool DS18B20_Check(void);
float DS18B20_GetTemperature(void);
void DS18B20_SetResolution(uint8_t resolution);
void DS18B20_SetAlarm(int8_t th, int8_t tl);
bool DS18B20_ReadROM(uint8_t *rom);

#endif

/************************ ds18b20.c ************************/
#include "ds18b20.h"

// 延时函数(使用DWT计数器,需在main中调用 CoreDebug->DEMCR |= 0x01000000)
static void Delay_us(uint32_t us) {
    uint32_t ticks = us * (SystemCoreClock / 1000000);
    DWT->CYCCNT = 0;
    while(DWT->CYCCNT < ticks);
}

// 复位并检测存在脉冲
static bool DS18B20_Reset(void) {
    bool presence;

    DQ_OUT();
    DQ_LOW();
    Delay_us(480);

    DQ_IN();
    Delay_us(70);
    presence = (DQ_READ() == GPIO_PIN_RESET);

    Delay_us(410);
    return presence;
}

// 写一位
static void DS18B20_WriteBit(uint8_t bit) {
    DQ_OUT();
    DQ_LOW();

    if(bit) {
        Delay_us(2);
        DQ_IN();
        Delay_us(58);
    } else {
        Delay_us(60);
        DQ_IN();
    }
    Delay_us(2);
}

// 读一位
static uint8_t DS18B20_ReadBit(void) {
    uint8_t bit;

    DQ_OUT();
    DQ_LOW();
    Delay_us(2);

    DQ_IN();
    Delay_us(10);
    bit = (DQ_READ() == GPIO_PIN_SET);

    Delay_us(50);
    return bit;
}

// 写一字节
static void DS18B20_WriteByte(uint8_t data) {
    for(int i=0; i<8; i++) {
        DS18B20_WriteBit(data & 0x01);
        data >>= 1;
    }
}

// 读一字节
static uint8_t DS18B20_ReadByte(void) {
    uint8_t data = 0;
    for(int i=0; i<8; i++) {
        data >>= 1;
        if(DS18B20_ReadBit()) {
            data |= 0x80;
        }
    }
    return data;
}

// CRC校验
static uint8_t DS18B20_CRC8(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;
}

// 初始化
void DS18B20_Init(void) {
    DS18B20_GPIO_CLK_ENABLE();
    DQ_IN();

    // 使能DWT计数器(用于精确延时)
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}

// 检测器件是否存在
bool DS18B20_Check(void) {
    return DS18B20_Reset();
}

// 读取温度
float DS18B20_GetTemperature(void) {
    uint8_t data[9];
    int16_t temp;

    // 启动转换
    if(!DS18B20_Reset()) return -999.0;
    DS18B20_WriteByte(CMD_SKIP_ROM);
    DS18B20_WriteByte(CMD_CONVERT_T);

    // 等待转换完成
    HAL_Delay(750);  // 12位模式

    // 读取温度
    if(!DS18B20_Reset()) return -999.0;
    DS18B20_WriteByte(CMD_SKIP_ROM);
    DS18B20_WriteByte(CMD_READ_SCRATCHPAD);

    for(int i=0; i<9; i++) {
        data[i] = DS18B20_ReadByte();
    }

    // CRC校验
    if(DS18B20_CRC8(data, 8) != data[8]) {
        return -999.0;
    }

    // 转换温度
    temp = (data[1] << 8) | data[0];
    return temp * 0.0625;
}

// 设置分辨率
void DS18B20_SetResolution(uint8_t resolution) {
    DS18B20_Reset();
    DS18B20_WriteByte(CMD_SKIP_ROM);
    DS18B20_WriteByte(CMD_WRITE_SCRATCHPAD);
    DS18B20_WriteByte(0);  // TH
    DS18B20_WriteByte(0);  // TL
    DS18B20_WriteByte(resolution);

    // 保存到EEPROM
    DS18B20_Reset();
    DS18B20_WriteByte(CMD_SKIP_ROM);
    DS18B20_WriteByte(CMD_COPY_SCRATCHPAD);
    HAL_Delay(10);
}

// 设置报警温度
void DS18B20_SetAlarm(int8_t th, int8_t tl) {
    DS18B20_Reset();
    DS18B20_WriteByte(CMD_SKIP_ROM);
    DS18B20_WriteByte(CMD_WRITE_SCRATCHPAD);
    DS18B20_WriteByte(th);
    DS18B20_WriteByte(tl);
    DS18B20_WriteByte(RESOLUTION_12BIT);

    DS18B20_Reset();
    DS18B20_WriteByte(CMD_SKIP_ROM);
    DS18B20_WriteByte(CMD_COPY_SCRATCHPAD);
    HAL_Delay(10);
}

// 读取ROM序列号
bool DS18B20_ReadROM(uint8_t *rom) {
    if(!DS18B20_Reset()) return false;

    DS18B20_WriteByte(CMD_READ_ROM);
    for(int i=0; i<8; i++) {
        rom[i] = DS18B20_ReadByte();
    }

    // 校验系列码
    if(rom[0] != 0x28) return false;

    // CRC校验
    if(DS18B20_CRC8(rom, 7) != rom[7]) return false;

    return true;
}

8.2 主程序示例

/************************ main.c ************************/
#include "main.h"
#include "ds18b20.h"
#include <stdio.h>

int main(void) {
    HAL_Init();
    SystemClock_Config();

    // 初始化DS18B20
    DS18B20_Init();

    // 检测器件
    if(!DS18B20_Check()) {
        printf("DS18B20 not found!\n");
        while(1);
    }

    // 读取ROM序列号
    uint8_t rom[8];
    if(DS18B20_ReadROM(rom)) {
        printf("ROM: ");
        for(int i=0; i<8; i++) {
            printf("%02X ", rom[i]);
        }
        printf("\n");
    }

    // 设置12位分辨率
    DS18B20_SetResolution(RESOLUTION_12BIT);

    // 设置报警温度(25℃~30℃)
    DS18B20_SetAlarm(30, 25);

    while(1) {
        // 读取温度
        float temp = DS18B20_GetTemperature();

        if(temp != -999.0) {
            printf("Temperature: %.4f °C\n", temp);

            // 提取整数和小数部分
            int temp_int = (int)temp;
            int temp_dec = (int)((temp - temp_int) * 10000);
            printf("Display: %d.%04d °C\n", temp_int, temp_dec);
        } else {
            printf("Read error!\n");
        }

        HAL_Delay(1000);
    }
}

8.3 蓝桥杯LCD显示示例

// 温度显示函数(适配蓝桥杯开发板)
void Display_Temperature(void) {
    float temp = DS18B20_GetTemperature();
    char str[16];

    if(temp != -999.0) {
        // 处理负温度
        if(temp < 0) {
            temp = -temp;
            sprintf(str, "-%.2f", temp);
        } else {
            sprintf(str, " %.2f", temp);
        }

        // 显示到LCD(假设LCD_ShowString函数已定义)
        LCD_ShowString(0, 0, "Temp:");
        LCD_ShowString(6, 0, str);
        LCD_ShowString(12, 0, "C");
    } else {
        LCD_ShowString(0, 0, "Error!");
    }
}

附录A:快速参考表

A.1 指令速查

指令类型 指令码 功能 应用场景
ROM指令
搜索ROM F0h 识别所有器件序列号 系统初始化
读取ROM 33h 读取单个器件序列号 单器件系统
匹配ROM 55h 选择特定器件 多器件系统
跳过ROM CCh 操作所有器件 单器件快速操作:star:
报警搜索 ECh 查找报警器件 温度监控系统
功能指令
温度转换 44h 启动AD转换 测温:star:
读暂存器 BEh 读取9字节数据 获取温度:star:
写暂存器 4Eh 写入TH/TL/配置 设置参数
拷贝暂存器 48h 保存到EEPROM 掉电保存
召回EEPROM B8h 从EEPROM恢复 上电初始化
读电源模式 B4h 检测供电方式 自适应程序

A.2 常用代码片段

// 1. 快速读温度(单器件,无CRC)
float QuickRead(void) {
    DS18B20_Reset();
    DS18B20_WriteByte(0xCC);  // 跳过ROM
    DS18B20_WriteByte(0x44);  // 转换
    HAL_Delay(750);
    DS18B20_Reset();
    DS18B20_WriteByte(0xCC);
    DS18B20_WriteByte(0xBE);  // 读取
    uint8_t l = DS18B20_ReadByte();
    uint8_t h = DS18B20_ReadByte();
    return ((int16_t)((h << 8) | l)) * 0.0625;
}

// 2. 设置9位分辨率(快速转换)
DS18B20_Reset();
DS18B20_WriteByte(0xCC);
DS18B20_WriteByte(0x4E);
DS18B20_WriteByte(0); DS18B20_WriteByte(0);
DS18B20_WriteByte(0x1F);  // 9位
// 转换时间缩短到94ms

// 3. 检测报警
DS18B20_Reset();
DS18B20_WriteByte(0xEC);  // 报警搜索
if(DS18B20_ReadBit()) {
    // 有器件报警
}

附录B:故障排查流程图

开始
  │
  ▼
复位失败? ──是─► 检查硬件连接
  │否              │
  ▼               ▼
温度=85°C? ──是─► 未完成转换,增加等待时间
  │否              │
  ▼               ▼
温度乱跳? ──是─► 检查延时精度/电源稳定性
  │否              │
  ▼               ▼
寄生供电失败? ──是─► 检查强上拉电路
  │否
  ▼
多器件冲突? ──是─► 使用匹配ROM指令
  │否
  ▼
正常工作

附录C:性能优化建议

C.1 提高读取速度

  1. 降低分辨率

    DS18B20_SetResolution(RESOLUTION_9BIT);  // 94ms vs 750ms
    
  2. 并行转换多个器件

    // 同时启动所有器件转换
    DS18B20_Reset();
    DS18B20_WriteByte(0xCC);  // 跳过ROM
    DS18B20_WriteByte(0x44);  // 所有器件同时转换
    HAL_Delay(750);
    
    // 逐个读取结果
    for(int i=0; i<device_count; i++) {
        MatchROM(rom_list[i]);
        temperature[i] = ReadTemp();
    }
    
  3. 使用DMA+定时器

    • 定时器触发转换
    • DMA自动读取数据
    • CPU释放处理其他任务

C.2 提高可靠性

  1. 多次读取求平均

    float temp_sum = 0;
    for(int i=0; i<3; i++) {
        temp_sum += DS18B20_GetTemperature();
        HAL_Delay(10);
    }
    return temp_sum / 3;
    
  2. 添加异常值过滤

    float last_valid_temp = 25.0;
    float new_temp = DS18B20_GetTemperature();
    
    if(fabs(new_temp - last_valid_temp) > 10.0) {
        // 温度跳变>10℃,可能是错误
        return last_valid_temp;
    }
    last_valid_temp = new_temp;
    return new_temp;
    

附录D:学习资源

D.1 官方资料

  • :white_check_mark: DS18B20 Datasheet (Dallas Semiconductor)
  • :white_check_mark: Application Note 27 (CRC算法)
  • :white_check_mark: Application Note 74 (单总线通信)

D.2 推荐工具

  • 示波器/逻辑分析仪(调试时序)
  • Bus Pirate(单总线测试工具)
  • STM32CubeMX(快速配置GPIO)

D.3 常见开发板适配

开发板 DQ引脚 注意事项
蓝桥杯CT107D P1.4 需配置为准双向口
STM32F103 PA0 配置为开漏输出
Arduino Uno D2 pinMode(2, INPUT)
ESP32 GPIO4 避免使用下载口

文档版本: v1.0
最后更新: 2026-02-01
作者: Claude (基于DS18B20中文手册)
适用对象: 蓝桥杯嵌入式开发学习者


:pushpin: 使用提示

  1. 初学者建议先使用外部供电+跳过ROM模式
  2. 调试时使用示波器查看时序波形
  3. 遇到问题先检查硬件连接延时精度
  4. 参考实战代码模板快速上手

:bullseye: 蓝桥杯竞赛要点

  • :white_check_mark: 掌握单总线读写时序
  • :white_check_mark: 熟练使用跳过ROM指令(CCh)
  • :white_check_mark: 正确处理负温度显示
  • :white_check_mark: 实现温度报警功能
  • :white_check_mark: LCD显示温度值(保留2位小数)

END