蓝桥杯单片机学习笔记(五):DS18B20 深度解析与工程规范

发布时间:2026/5/16 21:10:17

蓝桥杯单片机学习笔记(五):DS18B20 深度解析与工程规范 蓝桥杯单片机通关秘籍DS18B20 深度解析与工程规范温馨提示内容源自米醋电子工作室培训课由本人总结前言在蓝桥杯单片机比赛中DS18B20 温度传感器是绝对的“常客”。它看起来只是一个黑色的小豆子但想要驯服它不仅需要读懂它的“心”时序还要懂得如何优雅地在代码中安顿它工程规范。本篇笔记将带你从底层驱动到上层代码架构彻底攻克这个模块。第一章避坑指南——大模板中的“隐形杀手”在开始攻克 DS18B20 之前先回顾一下数码管显示Seg.c中一个极易被忽视的错误。很多同学代码逻辑是对的但数码管就是显示乱码原因往往出在函数参数的顺序上。1.1 参数顺序的重要性在编写数码管显示函数时我们通常会定义三个参数wela(位选)决定哪一位数码管亮。dula(段选)决定显示什么数字。point(小数点)决定是否显示小数点。⚠️ 警告在Seg.c的底层驱动函数里入口参数wela、dula、point的定义顺序与调用顺序必须严格一致比喻时刻这就好比你去银行柜台办事柜员要求你依次递交“身份证、银行卡、密码”。如果你按照“银行卡、身份证、密码”的顺序递进去柜员编译器/单片机就会处理错误导致业务办理失败数码管乱码。切记怎么定义的就怎么传参顺序绝对不能乱第二章DS18B20 —— 单总线上的“独行侠”2.1 硬件初相识DS18B20 是一个非常有个性的传感器它只需要1根数据线就能完成双向通信我们称之为“单总线”One-Wire通信。接线法则GND接地地基。VCC接电源能量源。DQ (Data)数据线唯一的传声筒。2.2 它的“内心世界”寄存器当你给它上电复位时它会处于一种“刚睡醒”的状态此时温度寄存器里的默认值是85°C。如果你上电瞬间读取到了85°C别慌那是因为它还没来得及转换第一次温度。温度数据的存储格式16位二进制想象一个由16个小格子组成的抽屉Bit 0-3 (小数位)负责存储精细的温度值分辨率高达0.0625°C。Bit 4-10 (整数位)负责存储温度的整数部分。Bit 11-15 (符号位)负责告诉你现在是零上还是零下。全为0正温度暖和。全为1负温度冷。注DS18B20 只会出现这两种符号位情况不会有0和1混杂。2.3 关键功能指令核心考点想要让 DS18B20 干活单片机主机必须发送特定的“暗号”。我们将这些指令按操作流程进行了编号和拆解指令 1跳过 ROM 指令 [0xCC]解释这是“点名”环节的简化版。正常情况下总线上有多个设备主机需要叫名字序列号才能指定谁回答。但蓝桥杯板子上只有一个DS18B20它是“独生子”。发送0xCC就是对它说“别管名字了我知道只有你听我指挥”指令 2温度转换指令 [0x44]解释这是启动测量的“开关”。发送此指令后DS18B20 开始采集环境温度并进行计算。注意细节转换需要时间毫秒级。如果是“寄生电源”偷电模式主机必须在转换期间拉高总线供电。但在蓝桥杯板子上是外部供电发送完这个指令后我们可以通过读取总线状态来判断进度读到0表示正在忙转换中读到1表示忙完了转换完成。转换好的数据会暂存在内部寄存器中。指令 3读取暂存寄存器指令 [0xBE]解释这是“交作业”的命令。该指令告诉 DS18B20“把你刚刚测好、放在暂存器里的数据吐出来。”读取流程它会像流水线一样从第0字节温度低8位一直吐到第8字节CRC校验。实战技巧在比赛中我们通常只关心温度数据前两个字节。所以读完前两个字节LSB和MSB后主机可以直接发复位信号打断它不需要把后面没用的数据都读完。指令 4写入暂存寄存器指令 [0x4E]解释这是“设定规则”的命令。用于向 DS18B20 写入配置信息比如设置报警温度的上限TH和下限TL或者设置分辨率9-12位。写入顺序必须严格遵循 3 个字节的顺序TH - TL - 配置寄存器。且数据传输遵循LSB First低位先出原则。写入前通常建议先复位。第三章实战代码 —— 编写驱动与转换公式在比赛资源包中官方会提供onewire.c底层驱动我们需要把它添加到工程中并自己编写onewire.h以及上层读取函数。3.1 读取温度的核心代码// 记得包含必要的头文件#includeonewire.h// 读取温度函数floatrd_Temperature(){unsignedcharlow,high;// 用于暂存读回来的高低八位数据// --- 第一阶段命令它干活转换温度 ---init_ds18b20();// 1. 握手复位初始化Write_DS18B20(0xcc);// 2. 指令1跳过ROMWrite_DS18B20(0x44);// 3. 指令2开始温度转换// --- 第二阶段把数据读回来 ---// 注意转换需要时间为防止程序卡死通常连续调用时// 读取到的可能是上一次的转换结果这在比赛中通常是允许的。init_ds18b20();// 1. 再次握手准备读取Write_DS18B20(0xcc);// 2. 指令1再次跳过ROMWrite_DS18B20(0xbe);// 3. 指令3读取暂存器数据lowRead_DS18B20();// 4. 接收低8位 (LSB)highRead_DS18B20();// 5. 接收高8位 (MSB)// --- 第三阶段数据合成与换算 ---// high 8 : 把高8位移到它该在的位置左移8位// | low : 把低8位拼上去// / 16.0 : 核心算法return((high8)|low)/16.0;}3.2 为什么要除以 16.0这涉及到二进制小数的原理。DS18B20 的低 4 位是小数位。Bit 0 代表2−40.06252^{-4} 0.06252−40.0625Bit 1 代表2−30.1252^{-3} 0.1252−30.125Bit 2 代表2−20.252^{-2} 0.252−20.25Bit 3 代表2−10.52^{-1} 0.52−10.5将一个 16 位的整数右移 4 位相当于除以24162^4 162416就能把小数点对齐到正确的位置。例如若读出的原始 16 位二进制数代表整数 2000那么实际温度就是2000/16125.0∘C2000 / 16 125.0^\circ C2000/16125.0∘C。第四章工程规范 —— C语言中的“防重复包含”机制准备工作完毕后我们需要在main.c中引用onewire.h。这里涉及到两个至关重要的 C 语言工程概念Include Guard头文件卫士和引用路径。4.1 什么是“防重复包含机制”简单来说它的作用是保证头文件里的内容在一个.c文件编译单元里只被“抄写”一次。核心原理#include的本质是“复制粘贴”首先你要知道编译器是“傻瓜式”的。当它看到#include onewire.h时它不会思考只会把onewire.h里的所有文字原封不动地复制过来。如果没有卫士悲剧发生的场景假设你有一个config.h里面定义了sbit LED P1^0;。onewire.h引用了config.h。main.c引用了onewire.h又手滑直接引用了config.h。编译器在处理main.c时会先把config.h的内容复制一次然后处理onewire.h时又把config.h的内容复制了一次。结果编译器看到两行sbit LED P1^0;它直接崩溃报错“Redefinition重定义你到底要定义几个 LED”解决方案游乐园的“隐形印章”为了解决这个问题我们利用 C 语言预处理指令制作一个“卫士”。请看下面的代码模板#ifndef__ONEWIRE_H_// 1. 门卫检查我们要进门了吗#define__ONEWIRE_H_// 2. 盖章确认进来了先盖个章// ... 头文件内容函数声明、变量定义 ...#endif// 3. 后门结束通俗比喻游乐园检票逻辑我们将编译器比作一个只认死理的保安将头文件比作游乐园。#ifndef __ONEWIRE_H_(If Not Defined)场景你走到门口。保安问“你手上盖章了吗系统里记录过__ONEWIRE_H_这个名字了吗”如果没盖章保安放你进去。如果盖了章保安直接把你拦在门外指着旁边的快速通道说“你已经进过去一次了直接跳到出口#endif后面去吧别再进去占位置了。”#define __ONEWIRE_H_场景进门后的第一件事。动作保安在你手上盖个章在编译器内存里定义这个标记。作用标记“这个文件已经被读取过了”防止下次再被读取。#endif场景游乐园的出口。总结当你写任何一个.h文件时请形成肌肉记忆永远加上这三行代码。这就叫“防重复包含机制”。第五章尖括号 与双引号 的爱恨情仇在引用头文件时很多小白分不清#include ...和#include ...。其实它们的区别在于编译器“找书”搜索文件的路线不同。5.1 尖括号 去“公立图书馆”找含义引用标准库或编译器自带的库。搜索路线编译器会直接去它的安装目录下的include文件夹里找。它不看哪里它通常不会去你的当前工程文件夹里找。适用场景C 标准库stdio.h,math.h单片机寄存器定义STC15F2K60S2.H,reg52.h例子#include STC15F2K60S2.H这告诉编译器“别在我桌子上工程目录乱翻直接去系统库Keil 安装目录把这个芯片定义拿来。”5.2 双引号 先在“家里”找找不到再去“图书馆”含义引用你自己编写的或项目私有的头文件。搜索路线第一步编译器先在当前源文件.c所在的目录查找。第二步如果家里没找到它才会勉为其难地去系统标准库目录查找。适用场景你自己写的驱动onewire.h,key.h项目配置文件config.h例子#include onewire.h这告诉编译器“这个文件就在我现在的工程文件夹里先在这里找。如果实在找不到你再去系统目录看看。”5.3 避坑总结表符号搜索顺序推荐用途你的代码示例 仅系统目录官方库、IDE自带库#include STC15F2K60S2.H 当前目录→\to→系统目录自写.h文件、项目模块#include onewire.h5.4完整底层代码完整的底层代码如下//onewire.c文件/* # 单总线代码片段说明 1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。 2. 参赛选手可以自行编写相关代码或以该代码为基础根据所选单片机类型、运行速度和试题 中对单片机时钟频率的要求进行代码调试和修改。 *///#includeonewire.hsbit DQP1^4;voidDelay_OneWire(unsignedintt){unsignedchari;while(t--){for(i0;i12;i);}}//voidWrite_DS18B20(unsignedchardat){unsignedchari;for(i0;i8;i){DQ0;DQdat0x01;Delay_OneWire(5);DQ1;dat1;}Delay_OneWire(5);}//unsignedcharRead_DS18B20(void){unsignedchari;unsignedchardat;for(i0;i8;i){DQ0;dat1;DQ1;if(DQ){dat|0x80;}Delay_OneWire(5);}returndat;}//bitinit_ds18b20(void){bit initflag0;DQ1;Delay_OneWire(12);DQ0;Delay_OneWire(80);DQ1;Delay_OneWire(10);initflagDQ;Delay_OneWire(5);returninitflag;}//读取温度函数floatrd_Temperature(){unsignedcharlow,high;//返回温度数据的高低八位init_ds18b20();//初始化Write_DS18B20(0xcc);//跳过ROMWrite_DS18B20(0x44);//进行温度转换init_ds18b20();//初始化Write_DS18B20(0xcc);//跳过ROMWrite_DS18B20(0xbe);//读取温度lowRead_DS18B20();//读取低位highRead_DS18B20();//读取高位return((high8)|low)/16.0;}//onewire.h文件#ifndef__ONEWIRE_H_#define__ONEWIRE_H_#includeSTC15F2K60S2.H// 1. 函数声明 (只写名字不写身体)voidDelay_OneWire(unsignedintt);voidWrite_DS18B20(unsignedchardat);unsignedcharRead_DS18B20(void);bitinit_ds18b20(void);// 2. 补上你之前代码里有的温度读取函数否则主函数无法调用floatrd_Temperature(void);#endif第六章最终调用准备工作都做完了我们该如何在主程序中调用这个芯片呢在main.c文件里引入头文件#include onewire.h定义变量定义一个float类型的变量比如Temperature。循环调用voidmain(){floatTemperature;// 定义变量存储温度// 初始化系统...while(1){// 实时获取温度Temperaturerd_Temperature();// 此处可以添加显示代码将 Temperature 显示在数码管上// ...}}这条语句会让温度值实时更新在Temperature这个变量里接下来你想怎么显示它就是数码管的事情啦

相关新闻