DS18B20温度传感器串口输出与C/C++开发实战
简介:DS18B20是一款流行的数字温度传感器,支持单总线通信,能够高精度输出温度数据。本资料提供C/C++源码,涵盖DS18B20的初始化、温度读取、数据解析与串口打印全流程,适用于智能家居、环境监控等应用场景。通过示例代码,开发者可快速掌握传感器通信协议与实际开发技巧。 
1. DS18B20数字温度传感器简介
DS18B20 是由 Maxim Integrated(现为 Analog Devices)推出的一款高精度、数字输出的温度传感器。它采用单总线(1-Wire)通信协议,仅需一根数据线即可完成供电与通信,极大地简化了硬件连接。
该传感器的测温范围为 -55°C 至 +125°C,分辨率可编程设置为 9~12 位,其中 12 位分辨率时精度可达 0.0625°C,非常适合对温度精度要求较高的应用场景。
DS18B20 被广泛应用于工业控制、环境监测、智能家居、农业温室控制等领域。其内置唯一的 64 位 ROM ID,支持多设备挂载在同一总线上,便于构建分布式温度采集系统。本章为后续开发实践打下理论基础。
2. 单总线(1-Wire)通信协议详解
单总线(1-Wire)通信协议是一种独特的半双工串行通信协议,仅需一根数据线和一根地线即可实现设备间的通信。这种协议由Dallas Semiconductor(现为Maxim Integrated)开发,广泛应用于DS18B20、DS2431等数字传感器和存储器芯片中。本章将深入剖析单总线协议的核心原理、命令集与寻址机制,并探讨其在复杂环境下的电气特性和抗干扰设计。
2.1 单总线协议的基本原理
单总线协议的核心在于其“单线复用”特性:同一根数据线既用于发送数据,也用于接收响应。这种设计极大地简化了硬件连接,但也对通信时序和逻辑判断提出了更高的要求。
2.1.1 单总线的通信机制
单总线采用半双工方式,通信过程由“主机”发起,设备(从机)响应。主机通过控制数据线的高低电平来发送“0”或“1”,同时在适当的时间读取从机的响应信号。所有通信都基于时间窗口内的电平变化进行识别。
以下为单总线通信的基本单元——位(bit)的传输机制:
| 位类型 | 主机操作 | 从机响应 | 说明 |
|---|---|---|---|
| 写0 | 拉低线并保持至少60μs | 不响应 | 主机主动拉低 |
| 写1 | 短暂拉低后释放 | 不响应 | 数据线自然恢复高电平 |
| 读位 | 拉低后释放,等待15μs读取电平 | 从机决定电平 | 高电平表示1,低表示0 |
单总线通信流程(以主机读取从机ID为例):
sequenceDiagram
participant Host as 主机
participant Slave as 从机
Host->>Slave: 发送复位脉冲(持续480μs低电平)
Slave->>Host: 回复存在脉冲(60~240μs低电平)
Host->>Slave: 发送ROM命令(如READ ROM 0x33)
Host->>Slave: 读取64位唯一ID
2.1.2 数据传输的时序要求
单总线通信的时序极为严格,任何时间误差都可能导致通信失败。以下是写位和读位的标准时序图示:
gantt
title 单总线写位时序
dateFormat HH:mm:ss
axisFormat %H:%M:%S
写0: a1, 00:00:00, 60us
写1: a2, 00:00:00, 5us
释放线: a3, 00:00:05us, 60us
以下为写1位的代码实现(以GPIO控制为例):
void OW_WriteBit(uint8_t bit) {
OW_LOW(); // 拉低数据线
Delay_us(2); // 保持2μs
if (bit) {
OW_HIGH(); // 若为1,释放线
}
// 否则保持低电平,形成写0
Delay_us(60); // 总周期约60μs
OW_HIGH(); // 恢复高电平
}
逐行分析与参数说明:
- OW_LOW() :宏定义,控制GPIO输出低电平,表示开始写位。
- Delay_us(2) :微秒级延时,确保建立时间满足协议要求。
- if (bit) :判断写入的位是0还是1。
- OW_HIGH() :释放总线,恢复高电平。
- Delay_us(60) :确保整个写周期不少于60μs。
- OW_HIGH() :再次确认总线恢复为高电平,防止残留低电平影响后续通信。
2.2 单总线命令集与设备寻址
单总线支持ROM命令和功能命令两类指令。ROM命令用于设备识别和寻址,功能命令用于执行具体操作(如温度转换)。
2.2.1 ROM命令与功能命令
ROM命令用于识别和选择从机,其常用命令如下:
| 命令名称 | 命令代码(十六进制) | 功能说明 |
|---|---|---|
| SEARCH ROM | 0xF0 | 搜索总线上的所有从机ID |
| READ ROM | 0x33 | 读取单一从机的64位ROM |
| MATCH ROM | 0x55 | 匹配指定从机进行通信 |
| SKIP ROM | 0xCC | 跳过ROM,对所有从机广播操作 |
功能命令用于执行具体操作,如DS18B20的温度转换命令如下:
| 命令名称 | 命令代码(十六进制) | 功能说明 |
|---|---|---|
| CONVERT T | 0x44 | 启动温度转换 |
| WRITE SCRATCHPAD | 0x4E | 写入配置寄存器 |
| READ SCRATCHPAD | 0xBE | 读取温度寄存器和配置信息 |
2.2.2 多设备挂载与唯一ID识别
单总线支持多设备挂载,每个设备拥有唯一的64位ROM ID,格式如下:
8位家族码 + 48位唯一序列号 + 8位CRC校验
例如,DS18B20的家族码固定为0x28,后续48位为芯片唯一ID,最后8位用于CRC校验。
多设备通信流程示例:
void OW_SearchDevices(uint8_t *roms, uint8_t *num_devices) {
uint8_t i, j;
uint8_t rom_no = 0;
uint8_t last_zero = 0;
OW_Reset(); // 发送复位
OW_WriteByte(0xF0); // SEARCH ROM 命令
while (rom_no < MAX_DEVICES) {
uint8_t rom[8] = {0};
uint8_t discrepancy = 0;
for (i = 0; i < 64; i++) {
uint8_t bit1 = OW_ReadBit();
uint8_t bit2 = OW_ReadBit();
if (bit1 && bit2) {
// 没有设备存在
break;
} else if (bit1 != bit2) {
// 所有设备该位相同
rom[i / 8] |= (bit1 << (i % 8));
OW_WriteBit(bit1);
} else {
// 分支点,需要选择路径
if (i == last_zero) {
rom[i / 8] |= (1 << (i % 8));
OW_WriteBit(1);
last_zero = discrepancy;
} else {
rom[i / 8] |= (0 << (i % 8));
OW_WriteBit(0);
discrepancy = i;
}
}
}
if (i == 64) {
memcpy(&roms[rom_no * 8], rom, 8);
rom_no++;
} else {
break;
}
}
*num_devices = rom_no;
}
逐行分析与参数说明:
- OW_Reset() :发送复位脉冲并等待从机响应。
- OW_WriteByte(0xF0) :发送搜索ROM命令,启动搜索流程。
- for (i = 0; i < 64; i++) :逐位读取ROM ID。
- bit1 和 bit2 :两次读取以判断总线状态。
- rom[i / 8] |= (bit1 << (i % 8)) :将位写入ROM数组。
- OW_WriteBit(bit1) :写入该位值以继续搜索。
- last_zero 和 discrepancy :记录分支点,用于回溯搜索。
- memcpy :将完整ROM ID保存至输出数组。
2.3 单总线的电气特性与抗干扰设计
单总线在物理层面上具有严格的电气要求,特别是在长距离通信或多设备挂载时更需注意抗干扰设计。
2.3.1 总线电平与驱动能力
单总线采用开漏输出结构,数据线需通过上拉电阻连接至VCC。典型上拉电阻值为4.7kΩ。总线驱动能力有限,因此不能连接过多设备或过长线路。
| 参数 | 典型值 | 说明 |
|---|---|---|
| 上拉电阻 | 4.7kΩ | 常用于标准速率 |
| 最大设备数 | 10个 | 取决于总线负载 |
| 最大通信距离 | 100米 | 使用增强驱动器可延长 |
2.3.2 长距离通信的注意事项
在长距离通信中,需注意以下问题:
- RC延迟 :长线缆引入的RC延迟会导致信号上升沿变缓,可能引起误判。
- 干扰噪声 :外界电磁干扰可能破坏通信数据。
- 终端匹配 :可在远端加入RC滤波或使用差分转换单元提升抗干扰能力。
长距离通信优化方案:
| 优化方式 | 描述 |
|---|---|
| 使用MAX3393E等驱动芯片 | 增强驱动能力,延长通信距离 |
| 增加上拉电阻数量 | 并联多个上拉电阻,提高上升速度 |
| 使用屏蔽线或双绞线 | 降低电磁干扰 |
| 降低通信速率 | 延长每位时间,提高容错能力 |
以下为使用MAX3393E驱动芯片的典型电路连接示意:
graph TD
A[MCU GPIO] --> B[MAX3393E EN)
B --> C[1-Wire Bus]
D[VCC] --> B
D --> C
E[GND] --> C
本章从单总线协议的基本通信机制、命令集与设备寻址方式,到电气特性与抗干扰设计进行了系统阐述。下一章将深入探讨DS18B20的硬件连接与上拉电阻配置,进一步夯实开发基础。
3. DS18B20硬件连接与上拉电阻配置
DS18B20作为一款基于单总线协议的数字温度传感器,其硬件连接方式和上拉电阻的配置对通信的稳定性和可靠性起着决定性作用。本章将深入解析DS18B20的引脚定义、接线方式、上拉电阻的选择与配置方法,并结合实际电路设计与常见问题进行分析,帮助开发者构建稳定可靠的温度采集系统。
3.1 DS18B20引脚定义与接线方式
DS18B20提供多种封装形式,最常见的有TO-92、TSOC和8-Pin SOIC封装。尽管封装不同,其核心引脚功能保持一致。
3.1.1 三种封装形式的引脚排列
下表列出三种主要封装形式的引脚定义:
| 封装类型 | 引脚编号 | 功能描述 |
|---|---|---|
| TO-92 | 1 | GND |
| 2 | DQ(数据线) | |
| 3 | VDD(电源) | |
| TSOC | 1 | DQ |
| 2 | GND | |
| 3 | NC(无连接) | |
| 8-Pin SOIC | 1 | GND |
| 2 | DQ | |
| 3 | VDD | |
| 其余 | NC |
⚠️ 注意 :DS18B20支持寄生供电(Parasitic Power)模式,即不使用VDD引脚,仅通过DQ线供电。该模式在低功耗场景中非常实用,但在温度转换期间可能需要额外的上拉电路。
3.1.2 典型连接电路设计
DS18B20的典型连接方式分为两种: 寄生供电模式 和 外部供电模式 。
外部供电模式(推荐)
- VDD接电源(通常为3.3V或5V)
- GND接地
- DQ通过一个4.7kΩ上拉电阻接到VDD
- 多个DS18B20可并联在同一根DQ线上
graph TD
A[MCU] -- DQ --> B(DS18B20)
C[VDD 3.3V] --> B
D[GND] --> B
E[4.7kΩ] -- Pull-up --> B
寄生供电模式
- VDD引脚悬空或接地
- DQ通过上拉电阻连接到VDD
- 在温度转换时,DQ线需保持高电平以维持供电
⚠️ 适用场景 :适用于引脚资源紧张、低功耗需求高的场合,但需要注意通信时序和上拉电路的设计。
3.2 上拉电阻的选择与电路稳定性
单总线协议依赖DQ线在通信期间进行双向数据传输。由于该协议使用“漏极开路”结构,因此必须通过外部上拉电阻将DQ线拉高,确保数据稳定传输。
3.2.1 上拉电阻的作用与计算方法
作用分析
- 保证DQ线在空闲状态为高电平
- 在主机或从机释放总线时,提供高电平维持能力
- 抑制总线上的噪声干扰,提高通信稳定性
计算方法
根据单总线协议的电气特性,上拉电阻值通常在 1kΩ ~ 10kΩ 之间选择,最常用为 4.7kΩ。
计算公式如下:
R_pullup ≥ (VDD - V_OH) / I_leakage
其中:
- VDD :系统供电电压(如3.3V或5V)
- V_OH :高电平输出电压(通常为0.7V)
- I_leakage :总线漏电流(通常为1μA~5μA)
✅ 经验法则 :大多数应用场景中,4.7kΩ是经过验证的稳定选择。
3.2.2 不同通信速率下的上拉需求
DS18B20支持标准速率(15.4kbps)和高速模式(125kbps)。通信速率越高,上拉电阻对信号上升时间的影响越大。
不同速率下的推荐上拉值:
| 通信速率 | 推荐上拉电阻 | 上升时间要求 |
|---|---|---|
| 标准模式 | 4.7kΩ | ≤ 10μs |
| 高速模式 | 1.5kΩ~2.2kΩ | ≤ 2.5μs |
⚠️ 注意 :高速模式下若使用过大的上拉电阻,可能导致信号上升沿过缓,引发通信失败。
示例代码:GPIO模拟单总线驱动中上拉控制
void set_pullup(int enable) {
if (enable) {
GPIOB->CRH |= GPIO_CRH_MODE10_1; // 设置为输出推挽模式
GPIOB->ODR |= GPIO_ODR_ODR10; // 输出高电平
} else {
GPIOB->CRH &= ~GPIO_CRH_MODE10; // 设置为输入模式(释放总线)
GPIOB->CRH |= GPIO_CRH_MODE10_0; // 输入上拉/下拉模式
}
}
📌 逐行解读 :
- 第2行:函数入口,参数控制是否启用上拉。
- 第4行:将GPIO设置为推挽输出模式。
- 第5行:输出高电平,实现上拉功能。
- 第7行:切换为输入模式,释放总线控制权。
- 第8行:设置为上拉/下拉输入,确保总线状态稳定。
3.3 硬件连接中的常见问题排查
尽管DS18B20的连接看似简单,但实际应用中常因接线错误、电源不稳或阻抗匹配不当导致通信失败。
3.3.1 接线错误导致的初始化失败
常见错误包括:
- DQ线未正确连接或虚焊
- 上拉电阻未接入或阻值过大
- 使用寄生供电模式但未正确配置DQ线
排查方法:
- 测量DQ线电压 :空闲状态应为高电平(接近VDD)。
- 示波器观察通信波形 :是否存在上升沿缓慢、脉冲不完整。
- 更换上拉电阻 :尝试1kΩ、4.7kΩ、10kΩ不同阻值。
3.3.2 电源电压波动对通信的影响
DS18B20工作电压范围为2.8V~5.5V。当使用寄生供电时,DQ线需在温度转换期间持续提供能量,若电压不稳定,可能导致传感器无法正常工作。
解决方案:
- 使用外部供电,避免依赖DQ线供电
- 增加0.1μF去耦电容靠近VDD与GND引脚
- 保证电源稳定,使用LDO稳压器或滤波电路
📌 电路优化建议 :
graph TD
A[VDD 3.3V] --> B(LDO稳压)
B --> C(DS18B20 VDD)
C --> D[0.1μF电容]
D --> E[GND]
此结构可有效减少电源噪声,提高DS18B20在复杂环境下的稳定性。
本章小结
本章从DS18B20的引脚定义出发,详细讲解了其在不同供电模式下的接线方式,深入分析了上拉电阻的作用、选型方法以及在不同通信速率下的配置要求,并结合实际电路设计与常见问题,提供了硬件调试与优化的实用建议。下一章将围绕DS18B20的初始化与复位操作展开,进一步深入其通信机制与实现细节。
4. DS18B20初始化与复位操作
在嵌入式系统中,DS18B20的初始化和复位是启动温度采集流程的第一步。只有正确完成初始化操作,主机(MCU)才能与DS18B20建立通信,进而发送命令、读取温度数据。本章将深入解析DS18B20的复位时序、常见失败原因、基于C/C++的初始化代码实现,以及调试与优化技巧。
4.1 初始化过程的时序分析
DS18B20采用单总线协议进行通信,其初始化过程依赖于严格的时序控制。主机(MCU)必须按照规范发送复位脉冲,随后等待DS18B20的响应(存在脉冲),以确认设备已准备就绪。
4.1.1 主机发送复位脉冲
主机初始化过程的第一步是发送复位脉冲(Reset Pulse)。该脉冲由主机将单总线拉低至少480μs,随后释放总线,使其回到高电平。该操作表示主机准备开始通信,并通知所有连接的DS18B20设备准备响应。
时序要求如下:
- 复位脉冲持续时间:480μs ≤ tRST ≤ 960μs
- 释放总线后等待时间:15~60μs
- DS18B20响应存在脉冲的起始时间:15~60μs后
时序流程图:
sequenceDiagram
participant MCU
participant DS18B20
MCU->>DS18B20: 拉低总线≥480μs (复位脉冲)
MCU->>DS18B20: 释放总线,进入输入模式
DS18B20->>MCU: 拉低总线60~240μs (存在脉冲)
MCU->>DS18B20: 读取总线状态,检测是否存在脉冲
4.1.2 从机响应存在脉冲
在主机释放总线后的15~60μs内,DS18B20将总线拉低60~240μs作为存在脉冲(Presence Pulse)。主机通过检测总线电平变化,判断是否有DS18B20设备连接。
如果主机在规定时间内未检测到存在脉冲,可能意味着以下情况之一:
- DS18B20未正确连接
- 上拉电阻失效
- 总线短路或断路
- DS18B20电源异常
存在脉冲检测逻辑表:
| 总线状态 | 含义 |
|---|---|
| 高电平 | 无设备响应,初始化失败 |
| 低电平 | 存在至少一个DS18B20设备响应 |
该阶段是整个初始化流程中的关键检测点,任何偏差都可能导致后续通信失败。
4.2 初始化失败的常见原因
初始化失败在实际开发中较为常见,主要源于硬件连接问题或时序控制不准确。以下是两个最常见的故障原因。
4.2.1 时序不匹配与通信中断
DS18B20对时序要求非常严格,尤其是复位脉冲的持续时间与响应窗口时间。如果主机的延时函数不准确,可能导致以下问题:
- 复位脉冲过短 :MCU释放总线太快,DS18B20未识别复位命令。
- 响应检测窗口不匹配 :MCU在DS18B20尚未响应前读取总线,导致误判。
示例:复位脉冲持续时间不足导致的失败
// 错误代码示例
void reset_ds18b20() {
GPIO_ResetBits(GPIOB, GPIO_PIN_0); // 拉低总线
Delay_us(100); // 延时不足(应≥480μs)
GPIO_SetBits(GPIOB, GPIO_PIN_0); // 释放总线
}
逻辑分析:
- 该函数中的 Delay_us(100) 仅延时100μs,远小于DS18B20所需的最小复位时间(480μs)。
- DS18B20无法正确识别复位脉冲,导致不响应存在脉冲。
- 主机检测不到存在脉冲,认为设备不存在或通信失败。
4.2.2 硬件连接异常导致无响应
硬件问题通常是初始化失败的主要原因,包括:
- 上拉电阻未连接或阻值不正确 :DS18B20需要5kΩ左右的上拉电阻,否则总线无法正常释放。
- 电源电压不足或不稳定 :DS18B20工作电压范围为3.0V~5.5V,低于3V可能导致无法工作。
- 引脚接错或焊接不良 :数据引脚(DQ)与MCU的GPIO未正确连接。
常见问题排查方法:
- 使用万用表测量DQ引脚在释放总线时的电压是否为高电平(3.3V或5V)。
- 检查是否存在短路或虚焊。
- 更换上拉电阻,测试不同阻值(如4.7kΩ、10kΩ)。
4.3 初始化代码实现(C/C++)
初始化代码需要精确控制GPIO状态和延时时间,适用于嵌入式平台如STM32、ESP32或Arduino。
4.3.1 延时函数的编写与精度控制
由于DS18B20的时序要求精确到微秒级别,延时函数必须具备高精度。在裸机系统中,通常使用定时器或循环方式实现微秒级延时。
基于循环的微秒延时函数示例:
void Delay_us(uint32_t us) {
us *= 72; // 假设系统时钟为72MHz,1个循环≈1/72μs
while(us--) {
__NOP(); // 空操作,确保编译器不优化
}
}
参数说明:
- us :延时微秒数
- __NOP() :插入空操作指令,防止被编译器优化掉
- 该函数适用于72MHz主频的MCU,若主频不同需重新计算循环次数
4.3.2 GPIO操作与状态检测
初始化代码主要包括:
- 拉低总线并延时480μs
- 释放总线并延时15~60μs
- 读取总线状态以判断是否存在脉冲
初始化函数实现(基于STM32 HAL库):
uint8_t ds18b20_reset() {
GPIO_InitTypeDef GPIO_InitStruct;
// 设置GPIO为输出模式
GPIO_InitStruct.Pin = DQ_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DQ_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(DQ_PORT, DQ_PIN, GPIO_PIN_RESET); // 拉低总线
Delay_us(480); // 延时480μs
HAL_GPIO_WritePin(DQ_PORT, DQ_PIN, GPIO_PIN_SET); // 释放总线
// 设置为输入模式以读取存在脉冲
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(DQ_PORT, &GPIO_InitStruct);
Delay_us(60); // 等待DS18B20响应
// 检测总线是否为低电平
if(HAL_GPIO_ReadPin(DQ_PORT, DQ_PIN) == GPIO_PIN_RESET) {
Delay_us(240); // 等待存在脉冲结束
return 1; // 初始化成功
} else {
return 0; // 初始化失败
}
}
逻辑分析:
- 先将GPIO设为输出模式,拉低总线并延时480μs完成复位脉冲。
- 释放总线后切换为输入模式,等待60μs以接收存在脉冲。
- 如果检测到低电平,则表示DS18B20已响应,初始化成功。
- 否则表示通信失败,需检查硬件或时序。
4.4 初始化优化与调试技巧
在实际开发中,仅靠代码实现往往难以确保初始化稳定可靠。使用示波器或逻辑分析仪进行时序分析和波形抓取,是排查问题的有效手段。
4.4.1 示波器抓取通信波形
示波器可用于捕获DS18B20的复位脉冲和存在脉冲信号,验证时序是否符合规范。
测量步骤:
1. 将示波器探头连接到DQ引脚与GND之间。
2. 设置触发条件为下降沿触发,捕捉复位脉冲。
3. 观察复位脉冲持续时间是否满足480μs以上。
4. 观察存在脉冲是否在复位后60~240μs范围内出现。
示波器截图示意:
[复位脉冲]-------------------------------[存在脉冲]-----
|<---480μs--->| |<--60~240μs-->|
4.4.2 使用逻辑分析仪验证时序
逻辑分析仪可以捕获多个信号通道的时序关系,适用于调试多设备通信或复杂协议。
推荐工具:
- Saleae Logic Analyzer
- Sigrok CLI + USBee
- STM32CubeMonitor(配合STM32)
使用流程:
1. 将DQ信号接入逻辑分析仪。
2. 运行初始化代码,触发一次复位操作。
3. 在逻辑分析仪软件中查看通信波形,验证是否存在脉冲是否符合标准。
4. 可以导出波形数据用于进一步分析或调试。
示例截图分析:
Time [μs] 0 480 540 780
Signal LOW HIGH LOW HIGH
Action Reset Release Presence Idle
通过逻辑分析仪可以直观查看主机与从机之间的通信状态,帮助定位初始化失败的具体原因。
本章从DS18B20初始化的基本时序入手,深入解析了主机发送复位脉冲和从机响应存在脉冲的过程,分析了初始化失败的常见原因,并给出了基于C语言的初始化代码实现,最后介绍了使用示波器和逻辑分析仪进行调试的技巧。下一章将进入温度转换流程,详细介绍温度采集与转换命令的使用。
5. DS18B20温度转换流程
DS18B20作为一款数字温度传感器,其核心功能是将环境温度转换为数字信号输出。该过程不仅涉及传感器内部的温度采集与转换机制,还包括主机对转换命令的下发、转换时间的控制以及数据状态的轮询或中断处理。本章将深入解析DS18B20的温度转换流程,包括命令启动方式、数据刷新机制、延时控制策略以及实时系统中的优化应用,帮助开发者构建高效、稳定的温度采集系统。
5.1 温度转换命令与启动方式
DS18B20的温度转换流程由主机通过单总线协议下发特定命令启动。本节将重点介绍温度转换命令的选择与使用方式,分析其在不同应用场景下的性能表现。
5.1.1 SKIP ROM与CONVERT T命令
DS18B20支持多设备挂载,因此主机在与设备通信时通常需要先发送ROM命令来选择目标设备。但在仅有一个设备连接时,可使用 SKIP ROM 命令(0xCC)跳过ROM地址选择,直接发送功能命令。
温度转换命令为 CONVERT T (0x44),其作用是启动温度转换过程。主机在发送该命令后,DS18B20将开始进行温度测量与数字转换。该过程需要一定时间,具体取决于温度分辨率设置。
示例代码:发送温度转换命令
void ds18b20_start_conversion() {
onewire_reset(); // 发送复位脉冲
onewire_write_byte(SKIP_ROM); // 跳过ROM选择
onewire_write_byte(CONVERT_T); // 发送温度转换命令
}
onewire_reset():执行单总线复位操作。onewire_write_byte(SKIP_ROM):跳过ROM地址选择。onewire_write_byte(CONVERT_T):启动温度转换。
注意 :若系统中存在多个DS18B20设备,应使用
MATCH ROM命令并提供设备的64位ROM地址,以确保命令仅发送给目标设备。
5.1.2 温度转换时间与精度选择
DS18B20支持9~12位的温度分辨率配置,默认为12位。分辨率越高,温度测量精度越高,但转换时间也相应增加。以下是不同分辨率下的典型转换时间:
| 分辨率 (bits) | 转换时间 (ms) |
|---|---|
| 9 | 94 |
| 10 | 188 |
| 11 | 375 |
| 12 | 750 |
温度分辨率设置示例代码
DS18B20的分辨率通过配置寄存器(TH、TL、CFG)中的CFG字段设置,具体如下:
void ds18b20_set_resolution(uint8_t resolution) {
uint8_t config;
onewire_reset();
onewire_write_byte(MATCH_ROM); // 匹配指定设备
onewire_write_bytes(device_rom, 8); // 写入设备ROM地址
onewire_write_byte(READ_SCRATCHPAD); // 读取暂存寄存器
uint8_t scratch[9];
onewire_read_bytes(scratch, 9);
config = scratch[4]; // 获取配置寄存器
config &= 0x9F; // 清除分辨率位(bit 6~5)
config |= (resolution << 5); // 设置新分辨率
scratch[4] = config;
// 写入暂存寄存器并复制到EEPROM
onewire_write_byte(WRITE_SCRATCHPAD);
onewire_write_bytes(scratch, 9);
onewire_write_byte(COPY_SCRATCHPAD); // 保存配置到EEPROM
}
resolution取值范围为0~3,对应9~12位。config &= 0x9F:保留其他配置位,仅清除分辨率位。config |= (resolution << 5):将分辨率值写入配置寄存器。
建议 :在需要快速响应的系统中,建议使用9位分辨率;在需要高精度测量的场景中,使用12位分辨率。
5.2 温度数据的存储与刷新机制
DS18B20完成温度转换后,将结果存储在暂存寄存器(Scratchpad Memory)中。主机通过读取这些寄存器获取温度数据。本节将介绍温度数据的更新周期、存储结构以及高低温报警机制。
5.2.1 温度寄存器的更新周期
每次执行 CONVERT T 命令后,DS18B20会将温度转换结果写入暂存寄存器(地址0~8)。该寄存器内容在下一次温度转换前保持不变,因此主机应确保在下一次转换前读取数据,否则可能读取到过时数据。
温度寄存器结构(部分)
| 地址 | 内容 |
|---|---|
| 0 | 温度低位(LSB) |
| 1 | 温度高位(MSB) |
| 2 | TH(高温报警) |
| 3 | TL(低温报警) |
| 4 | 配置寄存器 |
| 5~8 | 保留 |
| 9 | CRC校验值 |
数据更新流程 :
1. 主机发送CONVERT T命令;
2. DS18B20开始转换;
3. 转换完成后,结果写入地址0~1;
4. 主机读取地址0~1获取温度值。
5.2.2 高低温报警阈值的设置
DS18B20内置高低温报警功能,用户可通过设置TH(高温阈值)和TL(低温阈值)寄存器来定义报警范围。当温度超过设定值时,设备可在主机轮询时返回报警标志。
设置TH和TL示例代码
void ds18b20_set_thresholds(int8_t th, int8_t tl) {
onewire_reset();
onewire_write_byte(MATCH_ROM);
onewire_write_bytes(device_rom, 8);
onewire_write_byte(WRITE_SCRATCHPAD);
onewire_write_byte(th); // 写入TH
onewire_write_byte(tl); // 写入TL
onewire_write_byte(0x7F); // 保持配置寄存器默认值
onewire_write_byte(COPY_SCRATCHPAD); // 保存到EEPROM
}
th和tl为8位有符号整数,单位为摄氏度。- 设置完成后,可通过读取暂存寄存器验证配置。
5.3 转换过程的延时控制与轮询机制
DS18B20的温度转换过程需要一定时间,主机在发送转换命令后必须等待足够时间,才能读取有效数据。本节将探讨不同延时控制策略的优缺点,并分析在实时系统中使用中断方式的实现方法。
5.3.1 固定延时与状态轮询的比较
固定延时 是最简单的方式,即在发送转换命令后,主机等待预设时间(如750ms)再读取数据。这种方式适用于非实时系统或精度要求不高的场景。
ds18b20_start_conversion();
delay_ms(750); // 等待转换完成
状态轮询 则是通过读取设备状态位来判断转换是否完成。DS18B20在转换完成后会释放总线,主机可检测总线状态判断是否完成。
void ds18b20_wait_for_conversion() {
uint8_t status = 0;
while (!status) {
status = onewire_read_bit(); // 检测状态位
}
}
比较分析 :
| 方法 | 优点 | 缺点 |
|---|---|---|
| 固定延时 | 简单易实现 | 延时过长影响效率 |
| 状态轮询 | 准确判断转换完成 | 实现稍复杂,需硬件支持 |
5.3.2 实时系统中的中断方式应用
在实时系统中,长时间延时会影响系统响应。为此,可以使用中断方式监测转换完成信号。DS18B20在转换完成后会拉高总线电平,可将总线连接到MCU的外部中断引脚,触发中断服务函数。
中断方式流程图(mermaid)
graph TD
A[主机发送 CONVERT T 命令] --> B[DS18B20 开始转换]
B --> C{转换是否完成?}
C -- 是 --> D[触发外部中断]
C -- 否 --> E[继续等待]
D --> F[读取温度数据]
中断服务函数示例(STM32平台)
void EXTI0_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
// 读取温度数据
ds18b20_read_temperature();
EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断标志
}
}
EXTI0_IRQHandler:外部中断0服务函数。ds18b20_read_temperature():读取并解析温度值。EXTI_ClearITPendingBit():清除中断标志位,避免重复触发。
优势 :中断方式可有效减少CPU等待时间,提高系统实时性,适合嵌入式实时系统。
小结
第五章详细解析了DS18B20的温度转换流程,从命令发送、转换时间控制到数据读取与中断机制,覆盖了温度采集过程中的关键环节。通过本章内容,开发者可以掌握如何在不同应用场景中合理配置DS18B20,优化温度采集效率与精度。在下一章中,我们将进一步探讨DS18B20寄存器的读取与数据解析方法,为最终实现温度采集与输出打下坚实基础。
6. DS18B20寄存器读取与数据解析
在前几章中,我们已经完成了DS18B20的硬件连接、初始化、温度转换等核心操作。本章将深入探讨如何从DS18B20中读取温度寄存器中的原始数据,并对这些数据进行解析,最终转化为人类可读的温度值。DS18B20的温度数据存储在内部的“Scratchpad”寄存器中,我们需要通过单总线协议读取该区域的数据,并进行CRC校验和格式转换。
本章内容将围绕以下三个核心环节展开:
1. 温度寄存器的数据结构 :了解DS18B20内部温度寄存器的存储格式和编码方式;
2. 寄存器读取的通信流程 :掌握通过单总线协议读取Scratchpad寄存器的具体步骤;
3. 数据解析与浮点数转换 :将读取到的16位二进制数解析为带有小数精度的温度值,并在C/C++中实现格式化输出。
6.1 温度寄存器的数据结构
DS18B20的温度测量结果以16位有符号整数的形式保存在Scratchpad寄存器的前两个字节(Byte 0和Byte 1)中。这种数据格式支持从-55°C到+125°C的测温范围,并且支持9~12位分辨率的选择。
6.1.1 16位温度值的组成方式
DS18B20的温度寄存器结构如下所示:
| 字节位置 | 内容说明 |
|---|---|
| Byte 0 | 温度低字节(LSB) |
| Byte 1 | 温度高字节(MSB) |
| Byte 2 | 高温报警阈值(TH) |
| Byte 3 | 低温报警阈值(TL) |
| Byte 4 | 配置寄存器 |
| Byte 5 | 保留 |
| Byte 6 | 保留 |
| Byte 7 | 保留 |
| Byte 8 | CRC 校验码 |
其中,Byte 0和Byte 1组合成16位的温度值,其位定义如下:
MSB Byte 1: S S S S S T10 T9 T8
LSB Byte 0: T7 T6 T5 T4 T3 T2 T1 T0
S:符号位,表示正负温度(1为负数,0为正数);T10~T0:实际温度数据位;- 最终温度值的计算公式为:
$$
\text{Temperature} = (MSB \times 256 + LSB) \times 0.0625
$$
6.1.2 正负温度的表示方法
由于温度寄存器采用有符号的16位补码表示法,因此对于负数的处理需要特别注意。
-
正温度示例 :
若LSB=0x50(80),MSB=0x01(1),则合成16位值为0x0150(十进制336),计算温度为336 × 0.0625 = 21.00°C。 -
负温度示例 :
若LSB=0xC0(192),MSB=0xFF(-1),则合成16位值为0xFFC0(十进制-64),计算温度为-64 × 0.0625 = -4.00°C。
6.2 寄存器读取的通信流程
在温度转换完成后,主机需要通过READ SCRATCHPAD命令(0xBE)读取Scratchpad寄存器中的数据。该流程遵循单总线协议的严格时序要求。
6.2.1 READ SCRATCHPAD命令的使用
通信流程如下:
- 主机发送复位脉冲;
- DS18B20响应存在脉冲;
- 主机发送ROM命令(通常为SKIP ROM 0xCC);
- 主机发送功能命令READ SCRATCHPAD(0xBE);
- 主机连续读取9个字节的数据;
- 对Byte 8的CRC进行校验,确保数据完整性。
该流程可以通过Mermaid流程图表示如下:
graph TD
A[主机发送复位脉冲] --> B[DS18B20响应存在脉冲]
B --> C[主机发送SKIP ROM命令]
C --> D[主机发送READ SCRATCHPAD命令]
D --> E[主机连续读取9个字节]
E --> F{CRC校验是否通过}
F -- 是 --> G[继续处理温度数据]
F -- 否 --> H[报错或重试]
6.2.2 校验CRC确保数据完整性
DS18B20使用CRC8算法对前8个字节进行校验。CRC多项式为 x^8 + x^5 + x^4 + 1 ,即多项式值为0x31。
在C语言中,CRC8校验函数可实现如下:
uint8_t crc8(const uint8_t *data, uint8_t len) {
uint8_t crc = 0;
while (len--) {
crc ^= *data++;
for (uint8_t i = 0; i < 8; i++) {
if (crc & 0x80)
crc = (crc << 1) ^ 0x31;
else
crc <<= 1;
}
}
return crc;
}
代码逻辑分析:
crc ^= *data++:将当前字节与CRC异或;for (uint8_t i = 0; i < 8; i++):逐位处理当前CRC;if (crc & 0x80):最高位为1时执行异或操作;crc = (crc << 1) ^ 0x31:应用CRC多项式;- 最终返回CRC值。
参数说明:
data:指向Scratchpad前8个字节的指针;len:数据长度(8字节);- 返回值为CRC8校验码。
6.3 数据解析与浮点数转换
读取到Scratchpad数据后,下一步是将16位温度数据转换为浮点数,以便在用户界面或串口终端中显示。
6.3.1 整数部分与小数部分的提取
将读取到的Byte 0和Byte 1组合为16位值:
int16_t raw = (data[1] << 8) | data[0];
然后根据该值计算温度:
float temperature = (float)raw / 16.0;
逻辑分析:
data[1] << 8:将高字节左移8位;| data[0]:与低字节进行按位或,合成16位值;/ 16.0:因为DS18B20以1/16°C为单位存储温度,故除以16得到实际温度。
6.3.2 C/C++实现温度值的格式化输出
我们可以将温度值格式化为带有两位小数的字符串,便于串口输出:
char buffer[20];
snprintf(buffer, sizeof(buffer), "%.2f", temperature);
代码解释:
snprintf:将浮点数格式化写入字符串;"%.2f":保留两位小数;buffer:目标字符串缓冲区;sizeof(buffer):防止缓冲区溢出。
完整数据解析函数示例:
float parse_temperature(const uint8_t *data) {
// 计算CRC校验
uint8_t crc = crc8(data, 8);
if (crc != data[8]) {
// CRC校验失败
return -1000.0; // 错误标志
}
// 提取温度数据
int16_t raw = (data[1] << 8) | data[0];
float temperature = (float)raw / 16.0;
return temperature;
}
逻辑分析:
if (crc != data[8]):判断CRC是否一致;- 若一致则继续解析;
return temperature:返回解析后的温度值;- 若失败返回-1000.0作为错误标志。
总结与延伸
本章详细讲解了DS18B20的Scratchpad寄存器结构、通信流程、CRC校验机制以及温度值的解析方式。通过对16位温度值的解码,我们实现了从原始数据到浮点温度值的转换,并提供了C语言代码实现。
在下一章中,我们将把这些解析后的温度值通过串口进行输出,构建完整的嵌入式温度采集系统。你将看到如何在STM32或Arduino平台上整合GPIO、单总线协议、CRC校验与串口通信模块,实现完整的温度采集与显示功能。
7. C/C++实现DS18B20温度采集与串口打印
7.1 嵌入式平台下的驱动开发框架
在嵌入式开发中,为了实现对DS18B20的高效驱动,通常需要将底层GPIO操作和单总线协议进行模块化封装。这样不仅提高了代码的可读性和可维护性,也便于在不同平台(如STM32、ESP32、Arduino等)之间移植。
7.1.1 GPIO操作接口封装
以GPIO操作为例,我们需要封装以下基本操作函数:
// gpio_driver.h
#ifndef GPIO_DRIVER_H
#define GPIO_DRIVER_H
#include <stdint.h>
typedef enum {
GPIO_OUTPUT,
GPIO_INPUT
} gpio_mode_t;
void gpio_set_mode(int pin, gpio_mode_t mode);
void gpio_write(int pin, int level);
int gpio_read(int pin);
void gpio_delay_us(uint32_t us);
#endif // GPIO_DRIVER_H
这些函数在不同平台中实现方式不同,例如在STM32中使用HAL库,而在Arduino中使用 pinMode() 、 digitalWrite() 等函数。
7.1.2 单总线协议驱动实现
单总线协议的实现需要严格遵循时序要求。例如,复位操作如下所示:
// onewire.c
#include "gpio_driver.h"
#define ONEWIRE_PIN 2
void onewire_reset() {
gpio_set_mode(ONEWIRE_PIN, GPIO_OUTPUT);
gpio_write(ONEWIRE_PIN, 0); // 拉低总线
gpio_delay_us(480); // 保持480us
gpio_set_mode(ONEWIRE_PIN, GPIO_INPUT); // 释放总线,进入输入模式
gpio_delay_us(70); // 等待从机响应
// 检测是否存在脉冲(省略具体判断逻辑)
gpio_delay_us(410); // 等待总线恢复
}
该实现为后续DS18B20通信提供了基础协议层支持。
7.2 DS18B20驱动模块的结构设计
为了提升代码的复用性和可扩展性,DS18B20驱动模块应设计为可支持多设备并行采集的结构。
7.2.1 初始化、读取与解析函数设计
DS18B20的驱动主要包括以下核心函数:
// ds18b20.c
#include "onewire.h"
void ds18b20_init() {
onewire_reset();
// 发送SKIP ROM命令 0xCC
onewire_write_byte(0xCC);
// 发送START CONVERT命令 0x44
onewire_write_byte(0x44);
}
float ds18b20_read_temperature() {
uint8_t data[9];
onewire_reset();
onewire_write_byte(0xCC); // SKIP ROM
onewire_write_byte(0xBE); // READ SCRATCHPAD
for(int i = 0; i < 9; i++) {
data[i] = onewire_read_byte();
}
// CRC校验(省略实现)
int16_t raw = (data[1] << 8) | data[0];
float temperature = (float)raw / 16.0;
return temperature;
}
该函数实现了从设备读取原始数据并解析为浮点温度值。
7.2.2 支持多设备并行采集
通过ROM搜索命令(SEARCH ROM 0xF0),可以遍历总线上所有设备的唯一64位ROM地址。这样可实现多设备并行采集:
// rom_search.c
uint64_t devices[10];
int device_count = 0;
void search_devices() {
onewire_reset();
onewire_write_byte(0xF0); // SEARCH ROM
// 实现ROM地址搜索逻辑(省略具体实现)
}
每个设备通过其唯一地址访问,实现独立读取温度值。
7.3 串口通信模块的搭建
为了将采集到的温度数据显示出来,需要通过串口发送到调试助手或PC端。
7.3.1 UART初始化与波特率设置
以STM32为例,初始化串口的基本代码如下:
// uart.c
#include "stm32f1xx_hal.h"
UART_HandleTypeDef huart2;
void uart_init() {
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
HAL_UART_Init(&huart2);
}
7.3.2 温度数据的ASCII格式发送
将温度数据转换为ASCII字符串并发送:
// uart_send.c
#include <stdio.h>
#include "uart.h"
void send_temperature(float temp) {
char buffer[32];
sprintf(buffer, "Temperature: %.2f C\r\n", temp);
HAL_UART_Transmit(&huart2, (uint8_t*)buffer, strlen(buffer), 100);
}
这种方式便于在串口调试助手(如XCOM、串口助手等)中直观查看温度数据。
7.4 完整工程示例与调试输出
将上述模块整合为一个完整的工程,主函数结构如下:
7.4.1 基于STM32或Arduino的完整源码
以Arduino为例,主函数逻辑如下:
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
void setup() {
Serial.begin(115200);
sensors.begin();
}
void loop() {
sensors.requestTemperatures();
float temp = sensors.getTempCByIndex(0);
if (temp != DEVICE_DISCONNECTED_C) {
Serial.print("Temperature: ");
Serial.print(temp);
Serial.println(" C");
} else {
Serial.println("Error: Could not read temperature data");
}
delay(1000);
}
7.4.2 串口调试助手查看温度数据
使用串口调试助手(如Arduino IDE自带串口监视器、XCOM等)可以看到如下输出:
Temperature: 23.12 C
Temperature: 23.12 C
Temperature: 23.19 C
Temperature: 23.25 C
7.4.3 实际测试中的问题与解决方法
在实际测试过程中,可能会遇到如下问题:
| 问题现象 | 原因分析 | 解决方案 |
|---|---|---|
| 无温度输出 | DS18B20未检测到 | 检查GPIO电平、上拉电阻、总线初始化 |
| 温度异常波动 | 环境干扰或CRC校验失败 | 加强滤波、检查CRC校验机制 |
| 多设备识别失败 | ROM地址冲突或搜索逻辑错误 | 优化ROM搜索算法,检查设备连接 |
通过上述模块化设计与问题排查方法,可以稳定实现DS18B20的温度采集与串口输出。
简介:DS18B20是一款流行的数字温度传感器,支持单总线通信,能够高精度输出温度数据。本资料提供C/C++源码,涵盖DS18B20的初始化、温度读取、数据解析与串口打印全流程,适用于智能家居、环境监控等应用场景。通过示例代码,开发者可快速掌握传感器通信协议与实际开发技巧。
更多推荐




所有评论(0)