DS18B20数字温度传感器技术总结文档
文档说明
本文档基于DS18B20中文手册整理,适用于蓝桥杯嵌入式开发学习
创建日期:2026-02-01
目录
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 典型应用场景
温度监控与报警系统
工业过程控制
环境监测
HVAC系统
蓝桥杯嵌入式竞赛项目
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
优点:
稳定可靠
适用于全温度范围(-55℃ ~ +125℃)
无需强上拉电路
方式2:寄生供电
VCC (3.3V/5V)
│
└───────[4.7kΩ上拉电阻]
│
┌──────┐ │
│ VDD │接GND │
│ │ 单总线
│DS18B │◄─────────┤─────► MCU I/O
│ 20 │ │
│ GND │ [强上拉电路]
└──┬───┘ │
│ VCC
GND
注意事项:
VDD引脚必须接地
温度转换和EEPROM写入时需强上拉
不推荐温度>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]
最常用
// 外部供电模式
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]
最常用
// 读取温度值
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
可能原因:
硬件连接错误
上拉电阻未接或阻值不对
延时函数不准确
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:增加转换等待时间
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 | 操作所有器件 | 单器件快速操作 |
| 报警搜索 | ECh | 查找报警器件 | 温度监控系统 |
| 功能指令 | |||
| 温度转换 | 44h | 启动AD转换 | 测温 |
| 读暂存器 | BEh | 读取9字节数据 | 获取温度 |
| 写暂存器 | 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 提高读取速度
-
降低分辨率
DS18B20_SetResolution(RESOLUTION_9BIT); // 94ms vs 750ms -
并行转换多个器件
// 同时启动所有器件转换 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(); } -
使用DMA+定时器
- 定时器触发转换
- DMA自动读取数据
- CPU释放处理其他任务
C.2 提高可靠性
-
多次读取求平均
float temp_sum = 0; for(int i=0; i<3; i++) { temp_sum += DS18B20_GetTemperature(); HAL_Delay(10); } return temp_sum / 3; -
添加异常值过滤
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 官方资料
DS18B20 Datasheet (Dallas Semiconductor)
Application Note 27 (CRC算法)
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中文手册)
适用对象: 蓝桥杯嵌入式开发学习者
使用提示
- 初学者建议先使用外部供电+跳过ROM模式
- 调试时使用示波器查看时序波形
- 遇到问题先检查硬件连接和延时精度
- 参考实战代码模板快速上手
蓝桥杯竞赛要点
掌握单总线读写时序
熟练使用跳过ROM指令(CCh)
正确处理负温度显示
实现温度报警功能
LCD显示温度值(保留2位小数)
END