资讯详情

蓝桥杯CT107D DS18B20单总线驱动实战:时序、代码与避坑指南

📅 2026/10/15 18:00:26 | 华诺云谱 👁 阅读
蓝桥杯CT107D DS18B20单总线驱动实战:时序、代码与避坑指南
1. 赛前准备与DS18B20核心认知1.1 为什么CT107D平台上的DS18B20值得单独拿出来说蓝桥杯单片机组的竞赛板CT107D上DS18B20几乎是一个绕不开的考点。它不像LED、数码管、按键这些外设只要会写GPIO就能跑起来。DS18B20是单总线器件一根线既要发命令又要收数据时序全靠软件模拟稍微有一点偏差就读出85℃或者直接读不出来。很多选手在赛场上其他模块都调通了偏偏卡在温度读取上最后只能放弃这道题。我前后带过几届参加蓝桥杯的学生也自己用CT107D做过不少练习项目。DS18B20这个模块说难其实也不难核心就是三件事单总线时序的精确控制、ROM命令与功能命令的正确发送、温度数据的解析与转换。但说简单也不简单因为CT107D上的DS18B20是通过跳线帽连接到P2.7还是其他引脚不同版本的板子有差异而且官方提供的底层驱动往往不能直接拿来用需要根据实际硬件做适配。这篇文章我会从硬件连接讲起把单总线的复位、写时隙、读时隙的时序参数一个个拆开算给你看然后给出经过实测可用的完整代码最后把我踩过的坑和排查方法整理成速查表。不管你是第一次接触DS18B20还是之前调过但没调通应该都能从里面找到有用的东西。1.2 CT107D上DS18B20的硬件连接方式CT107D这块板子的外设布局比较紧凑DS18B20的接口通常位于板子边缘标着“DS18B20”或者“U6”之类的丝印。它有三个引脚VCC、GND、DQ。DQ就是单总线的数据线通过一个跳线帽或者直接走线连接到单片机的某个IO口。在大多数CT107D的版本中DS18B20的DQ引脚连接到P2.7但这个不是绝对的。有些版本用的是P1.0或者P3.7。所以拿到板子第一件事不是急着写代码而是用万用表蜂鸣档测一下DS18B20的DQ脚和哪个IO口导通。这个步骤花不了两分钟但能避免你后面花两个小时怀疑人生。另外要注意DS18B20的DQ引脚需要上拉电阻典型值是4.7kΩ到10kΩ。CT107D板子上一般已经焊好了这个上拉电阻所以你不需要外接。但如果你是用杜邦线把DS18B20引出来用那就得自己加一个上拉电阻否则总线空闲时无法保持高电平读出来的数据全是乱的。供电方面DS18B20支持寄生电源模式也就是VCC和GND都接地靠DQ线在总线高电平时偷电。但CT107D上一般是正常三线供电VCC接3.3V或5V都可以DS18B20的工作电压范围是3.0V到5.5V所以不用纠结。注意如果你用的是自己买的DS18B20模块有些模块板上已经集成了上拉电阻有些没有。买回来先用万用表量一下DQ和VCC之间的电阻如果是4.7kΩ左右就说明有上拉可以直接用如果是无穷大就得自己补一个。1.3 单总线协议的核心概念一根线怎么完成双向通信单总线英文叫1-Wire顾名思义就是只用一根数据线完成所有通信。这根线既要传命令又要传数据还要给器件供电寄生模式下。那它怎么区分什么时候是主机说话、什么时候是从机说话答案就是严格的时序划分。整个通信过程由主机单片机发起从机DS18B20响应。每一次操作都从一个复位脉冲开始然后主机发送ROM命令再发送功能命令最后进行数据读写。所有的数据位无论是0还是1都是通过拉低总线的持续时间来区分的。具体来说写一个位的时候主机先把总线拉低然后根据要写的是0还是1决定拉低多长时间。写0就拉低至少60微秒写1就拉低1到15微秒然后释放。DS18B20会在拉低开始后的15到60微秒之间采样总线电平如果这时候还是低就认为是0如果已经释放为高就认为是1。读一个位的时候主机先拉低总线至少1微秒然后释放接着在15微秒内采样总线电平。DS18B20如果要发0就把总线拉低如果要发1就释放总线让它被上拉电阻拉高。主机必须在15微秒内完成采样否则DS18B20可能已经释放了总线读到的就是错误数据。这些时间参数看起来很短但对于STC15系列单片机来说完全可以用软件延时实现。CT107D上用的单片机主频一般是11.0592MHz或者12MHz一个机器周期是1微秒左右12分频或者更短1T模式。所以延时函数需要根据实际主频来调整。2. 时序参数计算与底层驱动编写2.1 复位脉冲与存在脉冲的时序要求复位脉冲是每一次通信的起点。主机需要把总线拉低至少480微秒然后释放。DS18B20检测到这个长低电平后会等待15到60微秒然后把总线拉低60到240微秒作为应答这个应答就叫存在脉冲。在CT107D上我用的是STC15F2K60S2主频11.0592MHz工作在1T模式也就是一个时钟周期约0.09微秒。但实际写延时的时候我不会去精确算到每一个时钟周期而是用STC-ISP软件自带的延时函数生成器生成微秒级和毫秒级的延时然后根据实测微调。复位函数的逻辑是这样的bit ds18b20_reset(void) { bit presence; DS18B20_DQ 0; // 拉低总线 delay_500us(); // 保持至少480us DS18B20_DQ 1; // 释放总线 delay_70us(); // 等待15-60us让DS18B20拉低 presence DS18B20_DQ; // 采样总线如果为0说明有器件应答 delay_400us(); // 等待存在脉冲结束 return presence; }这里有个细节释放总线后不能立刻采样因为DS18B20需要15到60微秒才会拉低。如果你释放后马上读读到的肯定是高电平就会误判为器件不存在。我一般延时70微秒左右再采样这个时间落在15到60微秒的窗口之后但又不会太晚以至于错过存在脉冲。实操心得如果你发现复位函数总是返回1无器件先检查延时是否足够。有些同学用delay_10us()来凑500微秒结果实际延时只有300多微秒DS18B20根本来不及响应。用示波器或者逻辑分析仪抓一下波形是最直接的没有仪器的话就逐步加大延时试。2.2 写时隙的两种类型与代码实现写时隙分为写0和写1两种。主机拉低总线开始写时隙所有写时隙至少持续60微秒相邻两个写时隙之间至少间隔1微秒的恢复时间。写0的流程拉低总线保持60到120微秒然后释放。 写1的流程拉低总线保持1到15微秒然后释放再保持至少45微秒让总线恢复高电平。代码实现如下void ds18b20_write_bit(bit dat) { DS18B20_DQ 0; if (dat) { delay_5us(); // 拉低约5us满足1-15us要求 DS18B20_DQ 1; delay_60us(); // 保持高电平凑够60us时隙 } else { delay_60us(); // 拉低60us DS18B20_DQ 1; delay_5us(); // 恢复时间 } } void ds18b20_write_byte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { ds18b20_write_bit(dat 0x01); dat 1; } }注意写字节的时候是低位先发所以每次取dat 0x01然后右移。这个顺序不能搞反否则DS18B20收到的命令就是错的。2.3 读时隙的采样窗口与代码实现读时隙和写时隙类似也是主机拉低总线开始但拉低时间很短只有1到15微秒。然后主机释放总线在15微秒内采样。DS18B20会在主机拉低后的15微秒内把总线拉低发0或释放发1。bit ds18b20_read_bit(void) { bit dat; DS18B20_DQ 0; delay_2us(); // 拉低约2us DS18B20_DQ 1; // 释放总线 delay_2us(); // 等待DS18B20输出数据 dat DS18B20_DQ; // 采样 delay_60us(); // 等待读时隙结束 return dat; } unsigned char ds18b20_read_byte(void) { unsigned char i, dat 0; for (i 0; i 8; i) { dat 1; if (ds18b20_read_bit()) { dat | 0x80; } } return dat; }读字节的时候是低位先读所以每次把dat右移如果读到1就把最高位置1。这样8次之后第一个读到的位在最低位最后一个读到的位在最高位正好组成一个完整的字节。注意读时隙的采样点非常关键。如果你在释放总线后延时太久才采样DS18B20可能已经释放了总线你读到的就是上拉电阻拉高后的1即使它实际发的是0。我一般延时2微秒左右就采样这个时间足够DS18B20把总线拉低又不会太晚。2.4 延时函数的精度校准方法上面所有的时序都依赖延时函数。CT107D上常用的STC15系列单片机可以用STC-ISP软件生成延时函数。但生成的函数是理论值实际运行时会受到中断、编译器优化等因素影响。我的做法是先用STC-ISP生成一个大概的延时函数然后写一个测试程序让一个IO口在循环里翻转用示波器或者逻辑分析仪测量实际周期再反推延时函数的误差。如果没有仪器可以用一个简单的方法让单片机通过串口每隔一秒发送一次数据用手机秒表粗略计时如果偏差在5%以内对于DS18B20来说就够用了。另外DS18B20的时序要求虽然严格但也不是卡到纳秒级。官方数据手册给出的时间范围都有一定的余量比如写0要求拉低60到120微秒你拉低70微秒或者80微秒都能正常工作。所以延时函数不需要追求极致精确只要落在允许范围内就行。3. 完整驱动流程与温度数据解析3.1 DS18B20的ROM命令与功能命令每一次完整的温度转换和读取都需要经过以下步骤复位发送跳过ROM命令0xCC发送温度转换命令0x44等待转换完成最多750毫秒再次复位再次发送跳过ROM命令0xCC发送读取暂存器命令0xBE读取两个字节的温度数据如果总线上只挂了一个DS18B20就可以用跳过ROM命令不用读64位ROM编码。CT107D上一般只有一个DS18B20所以直接用0xCC跳过ROM是最简单的。温度转换命令0x44启动一次转换转换时间取决于分辨率。默认是12位分辨率转换时间最长750毫秒。在蓝桥杯的赛场上你不可能真的等750毫秒一般延时200到300毫秒就够读了因为实际转换时间往往比最大值短。但为了保险我一般延时750毫秒或者用循环等待的方式读暂存器的第9个字节配置寄存器来判断转换是否完成。3.2 温度数据的格式与转换公式DS18B20的温度数据是16位分为高字节和低字节。低字节的低4位是小数部分高字节的低4位是符号位其余是整数部分。数据位含义低字节bit0-3小数部分低字节bit4-7整数部分低4位高字节bit0-3整数部分高4位高字节bit4-7符号位0正1负正温度时直接把高字节左移8位加上低字节然后乘以0.0625就是摄氏度。负温度时需要先取反加一再乘以0.0625最后加上负号。float ds18b20_read_temp(void) { unsigned char tl, th; int temp; float result; ds18b20_reset(); ds18b20_write_byte(0xCC); ds18b20_write_byte(0x44); delay_750ms(); ds18b20_reset(); ds18b20_write_byte(0xCC); ds18b20_write_byte(0xBE); tl ds18b20_read_byte(); th ds18b20_read_byte(); temp (th 8) | tl; if (temp 0) { temp ~temp 1; result -(temp * 0.0625); } else { result temp * 0.0625; } return result; }在蓝桥杯的题目中通常要求显示整数温度比如25℃。这时候可以直接取temp的高字节和低字节的高4位组成整数部分。如果要显示小数就保留一位或两位小数。3.3 在CT107D上整合数码管显示温度CT107D的数码管是共阳极的通过74HC138译码器选择位选通过P0口发送段码。要把温度显示出来需要把浮点数转换成数码管能显示的段码。我一般先把温度乘以10或者100转成整数然后逐位提取。比如25.6℃乘以10得到256然后百位是2十位是5个位是6分别查表得到段码送到数码管。void display_temp(float temp) { unsigned int t; unsigned char i; unsigned char code seg[] {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90}; t (unsigned int)(temp * 10); for (i 0; i 3; i) { switch (i) { case 0: P0 seg[t / 100]; break; case 1: P0 seg[(t / 10) % 10]; break; case 2: P0 seg[t % 10]; break; } // 位选和锁存操作 // ... delay_1ms(); } }这里要注意数码管的刷新频率要足够高否则会闪烁。一般每位数码管显示1到2毫秒三位就是3到6毫秒刷新率在150Hz以上人眼就感觉不到闪烁了。实操心得在蓝桥杯赛场上如果温度显示不稳定比如跳变很大先检查是不是每次读取都重新启动了转换。DS18B20的转换需要时间如果你连续快速读取读到的可能是上一次的残留数据或者85℃的默认值。正确的做法是每次读取前都发一次0x44然后等足够的时间再读。4. 常见问题排查与赛场避坑指南4.1 读出来总是85℃怎么办85℃是DS18B20上电后的默认值表示温度转换还没有完成或者你读到的就是上电初始值。出现这个问题的原因通常有三个第一转换命令没有成功发送。检查你的写时序特别是写0和写1的拉低时间是否正确。如果写1的时候拉低时间太长DS18B20会把它当成写0命令就错了。第二转换时间不够。12位分辨率下转换最长需要750毫秒。如果你只延时了100毫秒就去读读到的就是85℃。解决办法是延时足够长或者读配置寄存器判断转换是否完成。第三复位函数有问题。如果复位时DS18B20没有正确应答后面的命令都不会被执行。用示波器看一下复位脉冲和存在脉冲的波形确认DS18B20确实拉低了总线。4.2 温度值跳变或者偶尔读错温度值偶尔跳变比如从25℃跳到0℃或者-55℃通常是时序不稳定导致的。可能的原因包括延时函数受到中断干扰。如果你在读取DS18B20的时候开了定时器中断中断服务程序可能会打乱时序。解决办法是在读取期间关闭中断读完再开。总线上的上拉电阻太大。如果上拉电阻是10kΩ总线从低电平恢复到高电平的时间会比较长可能导致读时隙采样错误。换一个4.7kΩ的电阻试试。电源不稳定。DS18B20在转换期间电流会增大如果电源滤波不好电压会跌落导致复位或者通信失败。在VCC和GND之间并一个0.1uF的电容。4.3 常见问题速查表现象可能原因排查方法解决办法复位无应答延时不足示波器看复位脉冲宽度加大延时到500us以上读出来总是85℃转换未完成检查延时是否够750ms延时750ms或读配置寄存器温度值跳变中断干扰读取时关中断读取期间关闭全局中断温度值固定不变命令发送错误检查写时序确认写0和写1的拉低时间偶尔读错上拉电阻太大测量DQ和VCC间电阻换成4.7kΩ上拉数码管显示闪烁刷新频率低计算刷新周期每位数码管显示1-2ms4.4 赛场上的时间分配与调试策略蓝桥杯单片机组的比赛时间通常是5个小时DS18B20这个模块一般会和其他模块一起考比如温度显示、温度报警、温度控制等。我的建议是先把其他模块调通比如数码管、按键、LED这些相对简单能快速拿到基础分。然后再攻DS18B20因为它的调试时间可能比较长。如果DS18B20实在调不出来也不要死磕先把其他功能做完最后再回来检查。调试DS18B20的时候我习惯先写一个最简单的测试程序只做复位、发0xCC、发0x44、延时、复位、发0xCC、发0xBE、读两个字节然后把读到的原始数据通过串口打印出来。这样能快速判断是时序问题还是数据解析问题。如果原始数据是0x055085℃的原始值说明转换没完成如果是0x0000或者0xFFFF说明通信有问题。注意CT107D上的DS18B20引脚可能和其他外设有冲突比如数码管的位选或者蜂鸣器。在写代码之前先确认跳线帽的连接避免引脚被其他外设占用。如果发现读出来的数据完全不对先检查跳线帽。4.5 从DS18B20延伸出去的单总线思维DS18B20是单总线协议的典型代表掌握了它的时序控制其他单总线器件比如DHT11、DS2431等也能触类旁通。单总线的核心思想就是用时间换引脚通过精确的时序划分来实现双向通信。这种思路在引脚资源紧张的单片机项目中非常实用。我在实际项目中遇到过需要同时接多个传感器的场景如果每个传感器都用独立的IO口引脚很快就不够用了。这时候单总线就能派上用场多个DS18B20可以挂在同一根总线上通过64位ROM编码区分。虽然CT107D上一般只用一个但理解多器件挂载的原理对以后做项目是有帮助的。最后再分享一个小技巧如果你在赛场上发现DS18B20的读数总是比实际温度高几度先检查一下单片机是不是离DS18B20太近单片机自身的发热会影响测量。把DS18B20用杜邦线引出来远离发热源读数就会准很多。这个细节在平时练习中不容易发现但在赛场上如果题目对温度精度有要求就很重要了。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑