蓝桥杯单片机“野路子”:DS18B20温度读取的血泪史(任务ID#12335)
蓝桥杯单片机“野路子”:DS18B20温度读取的血泪史(任务ID#12335)
又是一年2026蓝桥杯,我的IAP15F2K61S2又要裸奔了… 没办法,谁让咱是野生选手呢,学院派的那些花里胡哨的理论,咱玩不转,只能靠着实践出真知,硬着头皮往里冲。
问题描述:DS18B20温度读取,看似简单,实则坑深
第十届蓝桥杯单片机省赛,DS18B20温度读取,这玩意儿看似简单,不就是个单总线通信嘛,网上一搜一大堆代码。但是!蓝桥杯的坑,只有踩过才知道有多深!
最常见的坑,就是时序问题。DS18B20对时序要求非常严格,稍微有点偏差,就读不出数据,或者读出来的数据是乱码。尤其是在蓝桥杯的赛场上,时间就是生命,要是死磕时序,那估计省三都没戏。
还有就是,DS18B20的初始化,必须严格按照datasheet的要求来,任何一点疏忽,都可能导致初始化失败。而且,初始化失败了,还不会报错,只会让你读出来的数据一直是85度,让你怀疑人生。
当年,任务编号#12335选手我,就死在了这个DS18B20上,硬是调试了一下午,才发现是时序的问题。当时的心情,简直比吃了苍蝇还难受。
“野路子”解决方案:不求最佳,但求够用
痛定思痛,我决定放弃学院派的那些“最佳实践”,走一条“野路子”,一切以“够用就好”为原则。
我的解决方案是:延时函数大法好!
我知道,学院派肯定会鄙视我,说我这是“阻塞式编程”,效率低下。但是,在蓝桥杯的赛场上,效率算个屁!能把温度读出来,才是王道!
我的思路是,直接用延时函数来模拟DS18B20的时序,简单粗暴,但是有效!
// DS18B20 初始化
unsigned char DS18B20_Init(void)
{
unsigned char reval=0;
DQ = 1;
delay_us(5); // 略微提前拉低
DQ = 0;
delay_us(500); // 延时 480~960us
DQ = 1;
delay_us(60); // 延时 15~60us
reval = DQ;
delay_us(500); // 延时 480~960us
DQ = 1;
return reval; // 返回值: 0=成功, 1=失败
}
// 读取一个字节
unsigned char DS18B20_ReadByte(void)
{
unsigned char i, dat = 0;
for (i = 0; i < 8; i++)
{
DQ = 0;
delay_us(2);
DQ = 1;
delay_us(12); // 延时大于15us
dat = (dat >> 1) | (DQ << 7);
delay_us(50); // 延时大于45us
}
return dat;
}
// 写入一个字节
void DS18B20_WriteByte(unsigned char dat)
{
unsigned char i;
for (i = 0; i < 8; i++)
{
DQ = 0;
delay_us(2);
DQ = dat & 0x01;
delay_us(60); // 延时大于60us
DQ = 1;
dat >>= 1;
}
}
// 读取温度
int DS18B20_ReadT(void)
{
unsigned char TL, TH;
int temp;
DS18B20_Init();
DS18B20_WriteByte(0xCC); // skip ROM
DS18B20_WriteByte(0x44); // convert temperature
delay_ms(750); // 转换时间
DS18B20_Init();
DS18B20_WriteByte(0xCC); // skip ROM
DS18B20_WriteByte(0xBE); // read scratchpad
TL = DS18B20_ReadByte();
TH = DS18B20_ReadByte();
temp = TH;
temp <<= 8;
temp |= TL;
return temp;
}
这段代码的核心思想就是,用精确的延时函数来保证时序的正确性。当然,这种方法的缺点也很明显,就是会占用大量的CPU时间,导致程序的效率降低。但是,在蓝桥杯的赛场上,只要能把温度读出来,效率什么的,都是浮云。
另外,我还发现一个坑,就是DS18B20的转换时间。datasheet上说,转换时间是750ms。但是,我在实际测试中发现,如果延时时间不够,读出来的数据还是会出错。所以,我干脆把延时时间增加到1秒,这样就万无一失了。
经验教训:实践是检验真理的唯一标准
通过这次踩坑经历,我深刻体会到,实践是检验真理的唯一标准。不要迷信datasheet,也不要迷信网上的代码,只有自己动手调试,才能找到真正的解决方案。
我还总结了一些经验教训:
- 时序是关键: DS18B20对时序要求非常严格,必须严格按照datasheet的要求来。实在不行,就用延时函数大法,简单粗暴,但是有效。
- 初始化很重要: DS18B20的初始化,必须严格按照datasheet的要求来,任何一点疏忽,都可能导致初始化失败。
- 转换时间要足够: DS18B20的转换时间,要足够长,否则读出来的数据可能会出错。datasheet上说750ms,我建议延时1秒,万无一失。
- 多用示波器: 如果有示波器,可以用示波器来观察DS18B20的时序,这样可以更准确地调试代码。当然,像任务编号#12335这种穷逼选手,只能靠猜了。
- 3σ原则的应用: 在比赛过程中, 偶尔会出现读取到的温度值突变的情况. 这可能是由于干扰或者DS18B20本身的问题导致的. 借鉴统计学中的3σ原则, 我们可以对读取到的温度值进行简单的滤波处理. 例如, 连续读取5次温度值, 计算其平均值和标准差. 如果某个温度值与平均值的偏差超过3倍标准差, 则认为该值为异常值, 舍弃不用. 这样可以有效提高系统的鲁棒性。
| 步骤 | 操作 | 注意事项 |
|---|---|---|
| 1 | 初始化DS18B20 | 检查引脚连接是否正确,延时时间是否足够 |
| 2 | 发送跳过ROM命令 | 0xCC,确保所有设备都能响应 |
| 3 | 发送温度转换命令 | 0x44,启动温度转换 |
| 4 | 延时等待转换完成 | 至少750ms,建议1秒 |
| 5 | 再次初始化DS18B20 | 确保读取数据前设备状态正常 |
| 6 | 发送跳过ROM命令 | 0xCC |
| 7 | 发送读取暂存器命令 | 0xBE,准备读取温度数据 |
| 8 | 读取温度数据 | 读取两个字节,分别代表温度的低位和高位 |
结尾:蓝桥杯的意义在于让你明白自己有多菜…
希望我的这些经验能帮到你,祝你早日脱坑,拿到省三!记住,蓝桥杯单片机省赛的意义不在于拿奖,而在于让你明白自己有多菜… 另外,吐槽一下,这国信长天单片机竞赛实训平台,是真的难用!