DS18B20的STM32F103C8T6使用方法 HAL库全解(超详细时序拆解+源码逐行解析)
0. 原创前言
网上大部分DS18B20教程我都看过,基本都是丢一段源码、简单标几个注释,只告诉你怎么调用函数,根本不讲底层逻辑。很多同学照搬代码后,经常遇到各种奇葩问题:传感器初始化失败、温度数值卡死不动、负数温度数据错乱、偶尔读取超时跳数,出了问题完全不知道从哪里排查。
这篇文章是我自己用STM32F103C8T6 + HAL库实测调试、一步步踩坑改出来的完整教程。我把DS18B20的单总线时序、延时适配、正负温度补码计算、常见报错问题全部梳理透彻,所有代码都是工程实测稳定版本,适配72MHz主频,直接复制就能用,随便移植到F1系列项目里都没问题。
这里简单说下这篇教程的亮点,都是我实测总结的干货:
- 亲手拆解全套时序:复位、应答、读位、写位、读写字节全部掰开揉碎讲,不用死记硬背时序参数
- 适配HAL库精准延时:专门针对F103 72MHz主频校准微秒延时,解决大部分人时序不对、通信失败的问题
- 通俗讲解正负温度计算:很多教程不讲的补码逻辑,我用大白话讲清楚,彻底搞定负数温度乱码问题
- 汇总所有常见bug:把我踩过的坑全部整理出来,初始化失败、温度跳变、数据卡死等问题全部对应解决方案
- 代码极简好移植:模块化分层写的代码,初始化、读写、测温完全分开,课程设计、毕业设计直接套用
适配硬件:STM32F103C8T6 + DS18B20数字温度传感器
开发框架:STM32 HAL库
通信方式:单总线One-Wire(单IO双向通信)
核心难点:单总线精准时序匹配 + 微秒级延时适配 + 温度补码数据解析
1. DS18B20传感器核心原理与特性
DS18B20 是一款常用的单总线数字温度传感器,广泛用于单片机测温项目、环境监测、温控设备、毕业设计等场景,相比模拟热敏电阻,最大优势是无需AD转换、单IO双向通信、精度高、抗干扰强。
1.1 核心硬件特性
- 测温范围:-55℃ ~ +125℃,覆盖绝大多数民用测温场景
- 默认分辨率:0.0625℃,测温精度极高
- 供电方式:3.3V/5V 宽电压供电,支持寄生电源模式
- 通信方式:标准单总线协议,仅需1个IO口即可完成双向通信,极大节省单片机IO资源
- 支持多设备挂载:同一总线可挂载多个DS18B20,通过ROM地址区分设备
1.2 单总线通信核心痛点(新手必看)
我实测下来发现,DS18B20最坑的地方就是对时序要求极其严格,这也是90%的人代码跑不通的核心原因!普通的粗略延时、毫秒级延时根本满足不了通信需求,稍微差几微秒就会时序错位,直接导致初始化无应答、温度数据错乱。
另外单总线是半双工双向通信,同一个PB12引脚,需要频繁切换推挽输出和输入模式。很多新手忽略IO模式切换,一直输出或者一直输入,传感器根本不会应答,这也是通信失败的高频原因。
2. 硬件接线说明(工程实测管脚)
我工程里固定用的是PB12作为数据引脚,大家如果需要改其他IO口也很方便,只需要改对应宏定义就行,不用动底层时序代码,适配性很高。
DS18B20引脚 | STM32F103C8T6引脚 | 功能说明 |
VCC | 3.3V / 5V | 模块供电,兼容宽电压 |
GND | GND | 共地,必须严格共地否则通信异常 |
DQ | PB12 | 单总线双向通信数据引脚 |
这里提醒一下大家:DQ数据线最好接一个4.7K上拉电阻,我实测不加电阻的话,总线容易浮空,偶尔会出现时序紊乱、读取数据异常。现在很多成品模块自带上拉电阻,如果是模块可以不用额外焊接。
3. DS18B20 完整通信时序详解(必看,代码核心依据)
很多人写不好DS18B20驱动,根本原因就是没吃透整套单总线时序逻辑,只会照搬代码,稍微改一下主频、延时参数就彻底跑废。我这里用大白话把整套通信时序讲清楚,所有驱动代码都是严格按照下面这套流程写的,看懂时序就看懂了整个驱动。
DS18B20所有通信流程固定分为四步:复位应答 → 写指令 → 等待执行 → 读取数据,全程依靠微秒级精准时序,任何一步延时超标或不足,都会导致通信失败。
3.1 复位+应答时序(通信初始化前提)
这是每次通信的第一步,也是初始化成功的关键。主机(STM32)先抢占总线,将DQ引脚拉低,持续时长必须大于480us,我工程里用750us预留余量。随后释放总线,等待传感器应答。
正常工作的DS18B20,会在总线释放后的15~60us内主动拉低总线,生成60~240us的低电平应答信号。如果超时没拉低、或者拉低时长异常,直接判定传感器未接入、损坏或时序错乱。
3.2 写时序(主机发指令给传感器)
单总线写数据按位传输,分为写0和写1两种时序,时序差异非常大,不能统一延时:
写1时序:主机拉低总线 ≥1us 后立刻释放,随后保持总线高电平60us以上,完成1位数据写入;
写0时序:主机持续拉低总线 ≥60us,再释放总线,完成0位数据写入;
两次位写入之间需要预留短暂间隔,防止时序重叠紊乱,这也是我代码里长短延时区分的核心原因。我们常用的0xCC、0x44、0xBE指令,都是靠逐位写时序发送的。
3.3 读时序(主机获取传感器数据)
传感器不会主动上传数据,必须由主机发起读信号。每读取1bit数据,都需要主机先拉低总线≥1us,触发传感器输出数据,随后释放总线,在15us以内完成电平采样读取。
我工程里选择12us采样,刚好卡在最优时序窗口,既不会提前采样导致数据不准,也不会超时错过有效电平。读完1bit后补齐延时,再循环读取8次,拼接成1字节完整数据。
3.4 完整测温工作时序流程
正常读取一次温度,必须严格按照固定顺序执行,顺序错了永远读不到正确数据:
1、总线复位 + 设备应答检测,确认传感器在线;
2、发送0xCC跳过ROM指令(单设备通用);
3、发送0x44温度转换指令,启动传感器测温;
4、再次复位总线、检测应答;
5、发送0xBE读取暂存器指令;
6、连续读取低8位、高8位温度数据,拼接换算得到真实温度。
整套时序环环相扣,这也是为什么不能随便改延时、不能省略复位、不能不启动转换的原因。吃透这套时序,后续不管改IO口、改主频、移植工程,驱动都能正常跑通。
4. 工程配置与宏定义说明
我把所有硬件相关的配置全部用宏定义封装好了,这样后期改IO口特别方便,不用去底层函数里挨个改参数。大家直接在ds18b20.h头文件添加以下宏定义即可,当前适配PB12引脚:
c |
注意工程依赖:需要提前配置好HAL微秒延时函数、串口printf打印,系统时钟配置为72MHz,不然时序对不上,代码大概率跑不通。
5. 底层驱动源码逐行超详细解析
下面我把整个.c文件的代码逐函数拆解,结合我自己的实测经验,讲清楚每段代码的作用和为什么要这么写,让大家不光会复制,还能懂原理。
4.1 精准微秒延时函数(时序核心基础)
c |
我的实测总结:
DS18B20对时序精度要求极高,毫秒延时完全不够用。这个延时函数是我在72MHz主频下反复调试校准出来的,循环系数9是实测最接近1微秒的参数,搭配HAL库微秒延时,能精准匹配单总线时序,彻底避免延时偏差导致的通信失败。
我把这个函数设置为static静态函数,只允许当前文件调用,不对外暴露,代码封装性更好,工程更规范。
4.2 DS18B20复位函数
c |
时序思路(我实测优化):
官方手册要求复位拉低时长大于480us,我这里设置750us,多留一点余量,适配不同批次的传感器,兼容性更好。拉低复位后释放总线,等待15us让传感器完成应答,完全贴合官方标准时序,这是初始化成功的关键。
4.3 设备存在检测函数
c |
应答逻辑说明:
我们主机发送复位信号之后,正常的DS18B20会在15~60us内拉低总线应答,持续60~240us。我这里加了超时计数判断,超时无应答、应答时长异常,直接判定设备故障,能有效排查接线错误、传感器损坏、时序错乱等问题。
超时无应答、应答时长异常,直接判定设备未接入或通信故障,有效过滤硬件接线错误、设备损坏、时序错乱等问题。
4.4 单bit数据读取函数
c |
读位时序思路:
官方要求拉低总线至少1us触发数据输出,15us内必须采样完毕。我实测设置拉低2us、12us采样,刚好卡在有效时序窗口内,读取数据精准不出错。最后延时50us补齐周期,防止连续读位时序重叠紊乱。
4.5 单字节读取函数(低位先行)
c |
简单说下原理:
DS18B20固定是低位先行传输数据,先传最低位、再传最高位。所以我们循环读8次单bit数据,通过移位运算逐位拼接成一个完整字节,这是读取温度数据的基础逻辑。
4.6 单字节写入函数
c |
写位时序区分:
写1:短暂拉低总线,快速释放,保持高电平;
写0:长时间拉低总线,再释放;
代码里逐位判断、分开延时,严格贴合官方协议,保证每一条指令都能被传感器正常识别。
4.7 温度转换启动函数
c |
指令简单解释:
0xCC是跳过ROM指令,我们单传感器项目直接用这个指令就行,不用匹配设备地址,简化通信流程;
0x44是启动温度转换指令,发送之后传感器才会开始采集温度数据。
这里重点提醒:每次测温前必须启动转换,不然读出来的一直是上一次的旧数据,温度永远不更新,这是新手最常踩的坑!
4.8 设备初始化函数
c |
初始化流程很简单:
先开启GPIOB时钟,配置PB12为输出模式,发送复位信号,最后检测设备是否在线。返回0代表初始化成功,1代表失败,方便我们在代码里做异常判断。
4.9 温度读取与正负值解析(核心重点)
c |
大白话讲解温度解析逻辑:
DS18B20的温度数据存在两个8位寄存器里,TH是高8位、TL是低8位,拼起来是16位原始数据。高字节的最高位是符号位,用来区分正负温度:
1、正温度:符号位为0,直接拼接高低字节就能得到原始数值;
2、负温度:符号位为1,数据是以补码形式存储的,必须全部按位取反再计算负值,不然负数温度完全不准;
传感器固定分辨率是0.0625℃每刻度,我这里乘以0.625把数据放大10倍返回,保留一位小数,后续打印和屏幕显示都更方便,不用自己再二次换算。
6. 主函数调用示例(直接复制可用)
c |
7. 实测踩坑总结与解决方案
- 初始化一直失败:优先检查PB12时钟是否开启、模块是否共地、上拉电阻是否正常,大概率是时序延时不匹配72MHz主频
- 温度数值固定不变:循环内没有重复启动温度转换,一直在读取旧的缓存数据
- 负数温度数据错乱:缺少补码取反处理,这是很多开源代码都存在的小bug
- 温度轻微跳变:真实测温正常现象,可以加个简单均值滤波,多次采样取平均就能平滑数据
- 偶尔读取超时:微秒延时参数偏差,重新适配当前主频的延时系数即可
8. 原创总结
这篇文章是我自己全程实测、一步步调试总结出来的DS18B20 HAL库完整教程。从最基础的单总线原理、时序适配、IO模式切换,到指令解析、正负温度补码计算,最后到工程调用和bug排查,全部梳理到位。
我没有单纯堆砌代码,而是把新手容易疑惑的点全部用大白话讲透:为什么要精准延时、为什么要切换IO模式、负数温度为什么要取反、为什么每次都要启动转换。整套代码实测稳定性很强,不管是课程设计、毕业设计还是小型测温项目,都可以直接拿来用。
我在哔哩哔哩录制了视频,对应大家可以查看,并且有源码链接,哔哩哔哩视频地址为:https://www.bilibili.com/video/BV1uznpzmEUp/