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引脚功能说明VCC3.3V / 5V模块供电兼容宽电压GNDGND共地必须严格共地否则通信异常DQPB12单总线双向通信数据引脚这里提醒一下大家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// 根据实际硬件修改IO口#define DS18B20_DQ_GPIO_PORT GPIOB#define DS18B20_DQ_GPIO_PIN GPIO_PIN_12// IO模式切换、电平控制宏定义#define DS18B20_IO_OUT() HAL_GPIO_Init(DS18B20_DQ_GPIO_PORT, (GPIO_InitTypeDef){.PinDS18B20_DQ_GPIO_PIN,.ModeGPIO_MODE_OUTPUT_PP,.PullGPIO_NOPULL,.SpeedGPIO_SPEED_FREQ_HIGH})#define DS18B20_IO_IN() HAL_GPIO_Init(DS18B20_DQ_GPIO_PORT, (GPIO_InitTypeDef){.PinDS18B20_DQ_GPIO_PIN,.ModeGPIO_MODE_INPUT,.PullGPIO_NOPULL})#define DS18B20_DQ_LOW() HAL_GPIO_WritePin(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN,GPIO_PIN_RESET)#define DS18B20_DQ_HIGH() HAL_GPIO_WritePin(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN,GPIO_PIN_SET)#define DS18B20_DQ_READ() HAL_GPIO_ReadPin(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN)注意工程依赖需要提前配置好HAL微秒延时函数、串口printf打印系统时钟配置为72MHz不然时序对不上代码大概率跑不通。5. 底层驱动源码逐行超详细解析下面我把整个.c文件的代码逐函数拆解结合我自己的实测经验讲清楚每段代码的作用和为什么要这么写让大家不光会复制还能懂原理。4.1 精准微秒延时函数时序核心基础cstatic void DS18B20_Delay_Us(uint32_t us){uint32_t i;if (us 0) {// 简单循环延时根据系统时钟调整系数// 这里系数9是在72MHz主频下实测接近1us的延时for (i 0; i us * 9; i);HAL_Delay_us(1);}}我的实测总结DS18B20对时序精度要求极高毫秒延时完全不够用。这个延时函数是我在72MHz主频下反复调试校准出来的循环系数9是实测最接近1微秒的参数搭配HAL库微秒延时能精准匹配单总线时序彻底避免延时偏差导致的通信失败。我把这个函数设置为static静态函数只允许当前文件调用不对外暴露代码封装性更好工程更规范。4.2 DS18B20复位函数cvoid DS18B20_Rst(void){DS18B20_IO_OUT(); // 配置DQ引脚为推挽输出DS18B20_DQ_LOW(); // 拉低总线DS18B20_Delay_Us(750); // 保持低电平750us满足480us要求DS18B20_DQ_HIGH(); // 释放总线DS18B20_Delay_Us(15); // 等待15us等待DS18B20发送应答}时序思路我实测优化官方手册要求复位拉低时长大于480us我这里设置750us多留一点余量适配不同批次的传感器兼容性更好。拉低复位后释放总线等待15us让传感器完成应答完全贴合官方标准时序这是初始化成功的关键。4.3 设备存在检测函数cuint8_t DS18B20_Check(void){uint8_t retry 0;DS18B20_IO_IN(); // 切换为输入模式释放总线// 等待DS18B20拉低总线应答信号while (DS18B20_DQ_READ() retry 200){retry;DS18B20_Delay_Us(1);};if (retry 200) return 1; // 超时无设备响应retry 0;// 等待DS18B20释放总线while (!DS18B20_DQ_READ() retry 240){retry;DS18B20_Delay_Us(1);};if (retry 240) return 1; // 应答信号异常return 0; // 检测正常}应答逻辑说明我们主机发送复位信号之后正常的DS18B20会在15~60us内拉低总线应答持续60~240us。我这里加了超时计数判断超时无应答、应答时长异常直接判定设备故障能有效排查接线错误、传感器损坏、时序错乱等问题。超时无应答、应答时长异常直接判定设备未接入或通信故障有效过滤硬件接线错误、设备损坏、时序错乱等问题。4.4 单bit数据读取函数cuint8_t DS18B20_Read_Bit(void){uint8_t data;DS18B20_IO_OUT(); // 配置为输出DS18B20_DQ_LOW(); // 拉低总线 2usDS18B20_Delay_Us(2);DS18B20_DQ_HIGH(); // 释放总线DS18B20_IO_IN(); // 切换为输入DS18B20_Delay_Us(12); // 延时12us后采样data DS18B20_DQ_READ() ? 1 : 0; // 读取总线电平DS18B20_Delay_Us(50); // 等待读时序结束return data;}读位时序思路官方要求拉低总线至少1us触发数据输出15us内必须采样完毕。我实测设置拉低2us、12us采样刚好卡在有效时序窗口内读取数据精准不出错。最后延时50us补齐周期防止连续读位时序重叠紊乱。4.5 单字节读取函数低位先行cuint8_t DS18B20_Read_Byte(void){uint8_t i, j, dat 0;for (i 1; i 8; i){j DS18B20_Read_Bit();dat (j 7) | (dat 1); // 拼接数据低位先行}return dat;}简单说下原理DS18B20固定是低位先行传输数据先传最低位、再传最高位。所以我们循环读8次单bit数据通过移位运算逐位拼接成一个完整字节这是读取温度数据的基础逻辑。4.6 单字节写入函数cvoid DS18B20_Write_Byte(uint8_t dat){uint8_t j;uint8_t testb;DS18B20_IO_OUT(); // 配置为输出for (j 1; j 8; j){testb dat 0x01; // 取最低位dat dat 1; // 右移准备下一位if (testb) // 写 1{DS18B20_DQ_LOW();DS18B20_Delay_Us(2);DS18B20_DQ_HIGH();DS18B20_Delay_Us(60);}else // 写 0{DS18B20_DQ_LOW();DS18B20_Delay_Us(60);DS18B20_DQ_HIGH();DS18B20_Delay_Us(2);}}}写位时序区分写1短暂拉低总线快速释放保持高电平写0长时间拉低总线再释放代码里逐位判断、分开延时严格贴合官方协议保证每一条指令都能被传感器正常识别。4.7 温度转换启动函数cvoid DS18B20_Start(void){DS18B20_Rst();DS18B20_Check();DS18B20_Write_Byte(0xCC); // 跳过ROM单设备时使用DS18B20_Write_Byte(0x44); // 启动温度转换}指令简单解释0xCC是跳过ROM指令我们单传感器项目直接用这个指令就行不用匹配设备地址简化通信流程0x44是启动温度转换指令发送之后传感器才会开始采集温度数据。这里重点提醒每次测温前必须启动转换不然读出来的一直是上一次的旧数据温度永远不更新这是新手最常踩的坑4.8 设备初始化函数cuint8_t DS18B20_Init(void){__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟DS18B20_IO_OUT(); // 配置为推挽输出DS18B20_Rst();return DS18B20_Check(); // 返回检测结果}初始化流程很简单先开启GPIOB时钟配置PB12为输出模式发送复位信号最后检测设备是否在线。返回0代表初始化成功1代表失败方便我们在代码里做异常判断。4.9 温度读取与正负值解析核心重点cint16_t DS18B20_Get_Temp(void){uint8_t TL, TH; // 温度低8位、高8位int16_t tem; // 温度值DS18B20_Start(); // 启动温度转换DS18B20_Rst(); // 复位DS18B20_Check(); // 检测设备DS18B20_Write_Byte(0xCC); // 跳过ROMDS18B20_Write_Byte(0xBE); // 读暂存器TL DS18B20_Read_Byte(); // 读低8位TH DS18B20_Read_Byte(); // 读高8位// 处理正负温度if (TH 7) // 负温度符号位为1{TH ~TH; // 取反TL ~TL;tem -((TH 8) | TL); // 计算负值}else // 正温度{tem (TH 8) | TL; // 组合高低字节}tem tem * 0.625; // 转换为实际温度0.0625℃/LSBreturn tem;}大白话讲解温度解析逻辑DS18B20的温度数据存在两个8位寄存器里TH是高8位、TL是低8位拼起来是16位原始数据。高字节的最高位是符号位用来区分正负温度1、正温度符号位为0直接拼接高低字节就能得到原始数值2、负温度符号位为1数据是以补码形式存储的必须全部按位取反再计算负值不然负数温度完全不准传感器固定分辨率是0.0625℃每刻度我这里乘以0.625把数据放大10倍返回保留一位小数后续打印和屏幕显示都更方便不用自己再二次换算。6. 主函数调用示例直接复制可用cint main(void){HAL_Init();SystemClock_Config();MX_GPIO_Init();UL_PRINTF_Init(); // 初始化串口打印if(DS18B20_Init() 0){printf(DS18B20 Init Success!\r\n);}else{printf(DS18B20 Init Fail!\r\n);}while (1){int16_t temp DS18B20_Get_Temp();printf(Current Temp: %.1f ℃\r\n, temp / 10.0f);HAL_Delay(500);}}7. 实测踩坑总结与解决方案初始化一直失败优先检查PB12时钟是否开启、模块是否共地、上拉电阻是否正常大概率是时序延时不匹配72MHz主频温度数值固定不变循环内没有重复启动温度转换一直在读取旧的缓存数据负数温度数据错乱缺少补码取反处理这是很多开源代码都存在的小bug温度轻微跳变真实测温正常现象可以加个简单均值滤波多次采样取平均就能平滑数据偶尔读取超时微秒延时参数偏差重新适配当前主频的延时系数即可8. 原创总结这篇文章是我自己全程实测、一步步调试总结出来的DS18B20 HAL库完整教程。从最基础的单总线原理、时序适配、IO模式切换到指令解析、正负温度补码计算最后到工程调用和bug排查全部梳理到位。我没有单纯堆砌代码而是把新手容易疑惑的点全部用大白话讲透为什么要精准延时、为什么要切换IO模式、负数温度为什么要取反、为什么每次都要启动转换。整套代码实测稳定性很强不管是课程设计、毕业设计还是小型测温项目都可以直接拿来用。我在哔哩哔哩录制了视频对应大家可以查看并且有源码链接哔哩哔哩视频地址为https://www.bilibili.com/video/BV1uznpzmEUp/
DS18B20的STM32F103C8T6使用方法 HAL库全解(超详细时序拆解+源码逐行解析)
发布时间:2026/6/10 17:45:46
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引脚功能说明VCC3.3V / 5V模块供电兼容宽电压GNDGND共地必须严格共地否则通信异常DQPB12单总线双向通信数据引脚这里提醒一下大家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// 根据实际硬件修改IO口#define DS18B20_DQ_GPIO_PORT GPIOB#define DS18B20_DQ_GPIO_PIN GPIO_PIN_12// IO模式切换、电平控制宏定义#define DS18B20_IO_OUT() HAL_GPIO_Init(DS18B20_DQ_GPIO_PORT, (GPIO_InitTypeDef){.PinDS18B20_DQ_GPIO_PIN,.ModeGPIO_MODE_OUTPUT_PP,.PullGPIO_NOPULL,.SpeedGPIO_SPEED_FREQ_HIGH})#define DS18B20_IO_IN() HAL_GPIO_Init(DS18B20_DQ_GPIO_PORT, (GPIO_InitTypeDef){.PinDS18B20_DQ_GPIO_PIN,.ModeGPIO_MODE_INPUT,.PullGPIO_NOPULL})#define DS18B20_DQ_LOW() HAL_GPIO_WritePin(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN,GPIO_PIN_RESET)#define DS18B20_DQ_HIGH() HAL_GPIO_WritePin(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN,GPIO_PIN_SET)#define DS18B20_DQ_READ() HAL_GPIO_ReadPin(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN)注意工程依赖需要提前配置好HAL微秒延时函数、串口printf打印系统时钟配置为72MHz不然时序对不上代码大概率跑不通。5. 底层驱动源码逐行超详细解析下面我把整个.c文件的代码逐函数拆解结合我自己的实测经验讲清楚每段代码的作用和为什么要这么写让大家不光会复制还能懂原理。4.1 精准微秒延时函数时序核心基础cstatic void DS18B20_Delay_Us(uint32_t us){uint32_t i;if (us 0) {// 简单循环延时根据系统时钟调整系数// 这里系数9是在72MHz主频下实测接近1us的延时for (i 0; i us * 9; i);HAL_Delay_us(1);}}我的实测总结DS18B20对时序精度要求极高毫秒延时完全不够用。这个延时函数是我在72MHz主频下反复调试校准出来的循环系数9是实测最接近1微秒的参数搭配HAL库微秒延时能精准匹配单总线时序彻底避免延时偏差导致的通信失败。我把这个函数设置为static静态函数只允许当前文件调用不对外暴露代码封装性更好工程更规范。4.2 DS18B20复位函数cvoid DS18B20_Rst(void){DS18B20_IO_OUT(); // 配置DQ引脚为推挽输出DS18B20_DQ_LOW(); // 拉低总线DS18B20_Delay_Us(750); // 保持低电平750us满足480us要求DS18B20_DQ_HIGH(); // 释放总线DS18B20_Delay_Us(15); // 等待15us等待DS18B20发送应答}时序思路我实测优化官方手册要求复位拉低时长大于480us我这里设置750us多留一点余量适配不同批次的传感器兼容性更好。拉低复位后释放总线等待15us让传感器完成应答完全贴合官方标准时序这是初始化成功的关键。4.3 设备存在检测函数cuint8_t DS18B20_Check(void){uint8_t retry 0;DS18B20_IO_IN(); // 切换为输入模式释放总线// 等待DS18B20拉低总线应答信号while (DS18B20_DQ_READ() retry 200){retry;DS18B20_Delay_Us(1);};if (retry 200) return 1; // 超时无设备响应retry 0;// 等待DS18B20释放总线while (!DS18B20_DQ_READ() retry 240){retry;DS18B20_Delay_Us(1);};if (retry 240) return 1; // 应答信号异常return 0; // 检测正常}应答逻辑说明我们主机发送复位信号之后正常的DS18B20会在15~60us内拉低总线应答持续60~240us。我这里加了超时计数判断超时无应答、应答时长异常直接判定设备故障能有效排查接线错误、传感器损坏、时序错乱等问题。超时无应答、应答时长异常直接判定设备未接入或通信故障有效过滤硬件接线错误、设备损坏、时序错乱等问题。4.4 单bit数据读取函数cuint8_t DS18B20_Read_Bit(void){uint8_t data;DS18B20_IO_OUT(); // 配置为输出DS18B20_DQ_LOW(); // 拉低总线 2usDS18B20_Delay_Us(2);DS18B20_DQ_HIGH(); // 释放总线DS18B20_IO_IN(); // 切换为输入DS18B20_Delay_Us(12); // 延时12us后采样data DS18B20_DQ_READ() ? 1 : 0; // 读取总线电平DS18B20_Delay_Us(50); // 等待读时序结束return data;}读位时序思路官方要求拉低总线至少1us触发数据输出15us内必须采样完毕。我实测设置拉低2us、12us采样刚好卡在有效时序窗口内读取数据精准不出错。最后延时50us补齐周期防止连续读位时序重叠紊乱。4.5 单字节读取函数低位先行cuint8_t DS18B20_Read_Byte(void){uint8_t i, j, dat 0;for (i 1; i 8; i){j DS18B20_Read_Bit();dat (j 7) | (dat 1); // 拼接数据低位先行}return dat;}简单说下原理DS18B20固定是低位先行传输数据先传最低位、再传最高位。所以我们循环读8次单bit数据通过移位运算逐位拼接成一个完整字节这是读取温度数据的基础逻辑。4.6 单字节写入函数cvoid DS18B20_Write_Byte(uint8_t dat){uint8_t j;uint8_t testb;DS18B20_IO_OUT(); // 配置为输出for (j 1; j 8; j){testb dat 0x01; // 取最低位dat dat 1; // 右移准备下一位if (testb) // 写 1{DS18B20_DQ_LOW();DS18B20_Delay_Us(2);DS18B20_DQ_HIGH();DS18B20_Delay_Us(60);}else // 写 0{DS18B20_DQ_LOW();DS18B20_Delay_Us(60);DS18B20_DQ_HIGH();DS18B20_Delay_Us(2);}}}写位时序区分写1短暂拉低总线快速释放保持高电平写0长时间拉低总线再释放代码里逐位判断、分开延时严格贴合官方协议保证每一条指令都能被传感器正常识别。4.7 温度转换启动函数cvoid DS18B20_Start(void){DS18B20_Rst();DS18B20_Check();DS18B20_Write_Byte(0xCC); // 跳过ROM单设备时使用DS18B20_Write_Byte(0x44); // 启动温度转换}指令简单解释0xCC是跳过ROM指令我们单传感器项目直接用这个指令就行不用匹配设备地址简化通信流程0x44是启动温度转换指令发送之后传感器才会开始采集温度数据。这里重点提醒每次测温前必须启动转换不然读出来的一直是上一次的旧数据温度永远不更新这是新手最常踩的坑4.8 设备初始化函数cuint8_t DS18B20_Init(void){__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟DS18B20_IO_OUT(); // 配置为推挽输出DS18B20_Rst();return DS18B20_Check(); // 返回检测结果}初始化流程很简单先开启GPIOB时钟配置PB12为输出模式发送复位信号最后检测设备是否在线。返回0代表初始化成功1代表失败方便我们在代码里做异常判断。4.9 温度读取与正负值解析核心重点cint16_t DS18B20_Get_Temp(void){uint8_t TL, TH; // 温度低8位、高8位int16_t tem; // 温度值DS18B20_Start(); // 启动温度转换DS18B20_Rst(); // 复位DS18B20_Check(); // 检测设备DS18B20_Write_Byte(0xCC); // 跳过ROMDS18B20_Write_Byte(0xBE); // 读暂存器TL DS18B20_Read_Byte(); // 读低8位TH DS18B20_Read_Byte(); // 读高8位// 处理正负温度if (TH 7) // 负温度符号位为1{TH ~TH; // 取反TL ~TL;tem -((TH 8) | TL); // 计算负值}else // 正温度{tem (TH 8) | TL; // 组合高低字节}tem tem * 0.625; // 转换为实际温度0.0625℃/LSBreturn tem;}大白话讲解温度解析逻辑DS18B20的温度数据存在两个8位寄存器里TH是高8位、TL是低8位拼起来是16位原始数据。高字节的最高位是符号位用来区分正负温度1、正温度符号位为0直接拼接高低字节就能得到原始数值2、负温度符号位为1数据是以补码形式存储的必须全部按位取反再计算负值不然负数温度完全不准传感器固定分辨率是0.0625℃每刻度我这里乘以0.625把数据放大10倍返回保留一位小数后续打印和屏幕显示都更方便不用自己再二次换算。6. 主函数调用示例直接复制可用cint main(void){HAL_Init();SystemClock_Config();MX_GPIO_Init();UL_PRINTF_Init(); // 初始化串口打印if(DS18B20_Init() 0){printf(DS18B20 Init Success!\r\n);}else{printf(DS18B20 Init Fail!\r\n);}while (1){int16_t temp DS18B20_Get_Temp();printf(Current Temp: %.1f ℃\r\n, temp / 10.0f);HAL_Delay(500);}}7. 实测踩坑总结与解决方案初始化一直失败优先检查PB12时钟是否开启、模块是否共地、上拉电阻是否正常大概率是时序延时不匹配72MHz主频温度数值固定不变循环内没有重复启动温度转换一直在读取旧的缓存数据负数温度数据错乱缺少补码取反处理这是很多开源代码都存在的小bug温度轻微跳变真实测温正常现象可以加个简单均值滤波多次采样取平均就能平滑数据偶尔读取超时微秒延时参数偏差重新适配当前主频的延时系数即可8. 原创总结这篇文章是我自己全程实测、一步步调试总结出来的DS18B20 HAL库完整教程。从最基础的单总线原理、时序适配、IO模式切换到指令解析、正负温度补码计算最后到工程调用和bug排查全部梳理到位。我没有单纯堆砌代码而是把新手容易疑惑的点全部用大白话讲透为什么要精准延时、为什么要切换IO模式、负数温度为什么要取反、为什么每次都要启动转换。整套代码实测稳定性很强不管是课程设计、毕业设计还是小型测温项目都可以直接拿来用。我在哔哩哔哩录制了视频对应大家可以查看并且有源码链接哔哩哔哩视频地址为https://www.bilibili.com/video/BV1uznpzmEUp/