ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STC89C51RC+DS18B20温度预警系统设计:从单总线时序到实物调试全解析

STC89C51RC+DS18B20温度预警系统设计:从单总线时序到实物调试全解析 1. 做一个温度预警系统之前先把需求讲清楚很多人一拿到温度实时检测预警系统STC89C51RCDS18B20这种题目第一反应就是去搜代码、画电路图恨不得半个小时就把东西拼出来。但我做了这么多年单片机项目最大的感触是功能越简单的题目越容易翻车在最基础的地方。DS18B20不是什么新器件STC89C51RC也是老掉牙的单片机可恰恰是这种老组合网上资料鱼龙混杂时序写得糊里糊涂的特别多照着抄完一脸懵的大有人在。这个项目的本质需求其实非常清晰用单片机周期性地读取环境温度把数值显示出来当温度超过设定范围时触发声光报警。看着简单但拆开来看就有几个绕不开的问题DS18B20是单总线器件时序要求严格哪怕你晶振频率差一点、延时函数不够精确读出来的数据就可能全盘出错预警不能死板地固定阈值用户得有办法调整上、下限报警触发后还要有清晰的指示方式不能只靠一个发光二极管糊弄了事。所以这篇文章我打算围绕一个完整可复现的项目来讲硬件选型就是标题里给的STC89C51RC加DS18B20显示部分用LCD1602报警部分用蜂鸣器加LED再加三个按键做阈值设置。我会把从DS18B20底层时序、51资源配置、电路设计、代码框架到Protues仿真和实物联调的完整链路全部展开尤其是那些别人不会写进文档、只有真正上手才会踩进去的坑我都会用自己的实际经历讲清楚。如果你是正在做课程设计、毕业设计或者是准备电子设计竞赛但想先熟悉一下51平台这篇文章可以直接当一份完整的参考文档来看。如果只是想了解DS18B20怎么驱动前面的时序部分也单独拎出来能看后面的预警逻辑和调试经验同样适用到STM32平台上。2. DS18B20单总线时序所有初学者的第一个深坑2.1 为什么时序错了读出来的温度会是85℃DS18B20这个东西最大的特点就是省线——一根数据线既能供电、又能传数据、还能传时钟也就是所谓的单总线协议。但它省的是硬件坑的是软件。不管你是用51还是STM32驱动这个传感器必须严格按照它的时序来时间单位都是微秒级的稍有不慎整个通信就打水漂。很多人在实物上遇到的最典型的现象就是温度读出来永远固定在85℃。我第一次碰到这个值的时候还以为是传感器坏了翻完数据手册才发现85℃其实是DS18B20上电复位后的默认温度寄存器值。也就是说你的单片机压根没有成功启动一次温度转换读出来的只是芯片内部ROM里的垃圾数据而已。为什么会出现这种情况归根结底是时序没有对齐。DS18B20的工作频率窗口非常窄比如复位时主机拉低总线的时间要求在480μs到960μs之间太短芯片没反应太长又可能被误判为其他信号。读写一个bit的时隙周期是60μs到120μs在60μs内必须完成相应的拉高拉低动作。51单片机跑12MHz晶振一个机器周期就是1μs理论上还算充裕但如果你的延时函数是用for循环i写的编译器的优化等级一变延时长度就跟着变了测出来的时序就会走样。所以我的建议是写DS18B20驱动时不要偷懒去copy网上的延时而是自己写一个基于NOP指令或者定时器的精确延时至少要把delay_us和delay_ms两个函数单独拎出来测试用示波器或逻辑分析仪验证一下实际延时值。2.2 复位时序的理解与代码实现DS18B20的一次完整通信流程第一步永远是复位。复位的信号交互过程是这样的主机先把总线拉低保持480μs以上实际做960μs比较稳妥主机释放总线然后立刻释放控制权等待从设备响应DS18B20检测到总线上的上升沿后等待15μs到60μs然后主动把总线拉低60μs到240μs输出一个存在脉冲主机读取这个低脉冲就知道总线上有设备可以开始通信了。这个过程非常重要因为后面所有的读写操作都是建立在设备在线这个前提上的。我见过的代码里很多人复位以后不检查存在脉冲直接往下发命令这样万一传感器没接好整个程序就会在后续的读操作里卡死表现出来就是温度乱跳。bit DS18B20_Reset(void) { bit presence; DQ 0; // 拉低总线 delay_us(960); // 延时大于480us确保复位信号有效 DQ 1; // 释放总线 delay_us(60); // 等待从设备拉低总线 presence DQ; // 读取存在脉冲0表示有设备响应 delay_us(480); // 等待存在脉冲结束 return presence; }注意看这个函数的返回值presence DQ如果DS18B20正常在线这里读到的应该是0。有些网上的例程这里逻辑是反的把存在当成正常害我当初排查了半天。2.3 写时隙和读时隙到底差在哪里复位完了主机需要向DS18B20发送命令比如0xCC跳过ROM、0x44启动温度转换、0xBE读取暂存器这些命令都是一位一位写进去的用的就是写时隙。写时隙的关键是靠拉低总线的时间长短来区分0和1。写0的时候主机把总线拉低整个时隙60μs~120μs内保持低电平写1的时候主机先把总线拉低至少1μs然后立刻释放让上拉电阻把总线拉高持续到时隙结束。写0时隙 主机拉低 ------(保持60~120us)------ 释放 写1时隙 主机拉低 1us ------ 释放总线被上拉电阻拉高很多初学者搞不懂为什么写1要先拉低再释放直接保持高电平不行吗不行因为DS18B20是靠自己检测总线上的下降沿来同步采样窗口的每个时隙必须以一个下降沿开始芯片才能判断这是新的一位数据。所以哪怕你要写1也得先拉低一下再放开这个动作不能省。读时隙其实和写1类似也是主机先拉低总线至少1μs再释放然后在释放后的15μs内去采样总线电平。DS18B20如果想输出0会继续把总线拉低如果想输出1就不动作让上拉电阻把电平拉高。这里最容易犯的错是采样时间太晚超过15μs后DS18B20已经释放总线了读到的电平基本全是1。unsigned char DS18B20_ReadByte(void) { unsigned char dat 0; for (unsigned char i 0; i 8; i) { dat 1; DQ 0; _nop_(); // 拉低至少1us DQ 1; // 释放总线 _nop_(); _nop_(); // 等待约8-10us在采样窗口内读取 if (DQ) dat | 0x80; delay_us(60); // 保证整个时隙大于60us } return dat; }实测下来这个写法在12MHz晶振的51上是稳定的。如果换到6MHz晶振或者STC的1T模式_nop_()的个数要重新调。说到底DS18B20驱动没有一招鲜吃遍天的代码必须结合自己的实际主频来调整。2.4 温度数据的拼接与负温度处理读完9个字节的暂存器数据后前两个字节就是温度值。这里的格式是第1字节是温度低字节第2字节是温度高字节。12位精度下温度寄存器里的16位数据低4位是小数部分中间7位是整数部分最高位是符号位。高字节: S S S S S 2^6 2^5 2^4 2^3 2^2 2^1 2^0 低字节: 2^-1 2^-2 2^-3 2^-4 0 0 0 0温度大于等于0时直接把两个字节拼成一个16位数乘以0.0625就是实际温度。温度小于0时寄存器里存的是补码需要先取反加一再乘以0.0625最后加上负号。float DS18B20_GetTemperature(void) { unsigned char temp_l, temp_h; int temp_raw; float temperature; DS18B20_Reset(); DS18B20_WriteByte(0xCC); // 跳过ROM DS18B20_WriteByte(0x44); // 启动温度转换 delay_ms(750); // 等待转换完成12位精度最长750ms DS18B20_Reset(); DS18B20_WriteByte(0xCC); // 跳过ROM DS18B20_WriteByte(0xBE); // 读取暂存器 temp_l DS18B20_ReadByte(); temp_h DS18B20_ReadByte(); temp_raw (temp_h 8) | temp_l; if (temp_raw 0x8000) // 负温度 { temp_raw ~temp_raw 1; temperature temp_raw * 0.0625; temperature -temperature; } else { temperature temp_raw * 0.0625; } return temperature; }这里要注意一个经典误区温度转换命令发出后不是立刻就能读的12位分辨率下最长时间是750ms9位分辨率只要93.75ms。如果你追求实时响应可以用9位精度但温度分辨率只有0.5℃。这个取舍后面在讲代码优化的时候我会再展开。3. STC89C51RC资源分配与最小系统电路设计3.1 STC89C51RC这款芯片到底够不够用STC89C51RC是国产宏晶科技的一款增强型51单片机兼容传统8051指令集片上Flash是4KBSRAM是512字节工作电压3.3V到5.5V最高主频可以到40MHz。最关键的是它支持ISP在线下载不用专门的编程器一根USB转TTL线就能烧程序这对学生党来说非常友好。有人会问现在STM32都便宜成那样了为什么还要用51来做温度预警我的看法是这种基础项目用51反而更合适。DS18B20的时序是微秒级的51的机器周期是微秒级的刚好在不紧张也不轻松的区间逼着你去理解时序的本质而STM32的主频动辄几十MHz时序反而要靠定时器去精确定时对初学者来说难度更高。另外51的IO口是准双向口直接驱动DS18B20不需要配置模式一根线搞定清清爽爽。资源方面这个项目用到的资源就是P2口接LCD1602的数据线P0口或P1口接控制线和DS18B20再接三个独立按键和蜂鸣器。4KB Flash装下整个程序毫无压力512字节RAM甚至还有富余。唯一要注意的是STC89C51RC的P0口内部没有上拉电阻如果用来做输出必须外接10kΩ排阻不然电平拉不上去。3.2 完整电路图思路与关键元器件选型先给一张实物接线对应的电路思路表方便你在Protues里或者面包板上直接照着连模块引脚连接说明DS18B20 DQ单片机P3.7必须接4.7kΩ上拉电阻到VCCDS18B20 VDD5V外部供电模式不用寄生供电DS18B20 GNDGND共地LCD1602 RSP2.0寄存器选择LCD1602 RWP2.1读写选择接地为只写LCD1602 ENP2.2使能信号下降沿锁存LCD1602 D0-D7P0.0-P0.78位数据模式蜂鸣器P1.0三极管8050驱动低电平触发LED报警灯P1.1低电平点亮按键S1/S2/S3P3.4/P3.5/P3.6阈值调整、模式切换有几个选型细节必须强调一下上拉电阻。DS18B20的DQ引脚是开漏输出的必须通过上拉电阻才能输出高电平。4.7kΩ是数据手册推荐值别为了省事直接短接也别换成1kΩ否则总线拉低的时候漏电流太大传感器可能工作不稳定。蜂鸣器驱动。51的IO口灌电流能力只有20mA左右一个蜂鸣器可能要30mA到50mA直接接IO口会导致电压被拉低、单片机复位甚至烧毁。必须用三极管做开关最普通的8050就行基极串一个1kΩ电阻到IO口集电极接蜂鸣器负极蜂鸣器正极接5V发射极接地。这样IO口只负责给基极供很小的电流蜂鸣器的电流走三极管。晶振与电容。我建议用11.0592MHz的晶振两个22pF到30pF的负载电容。为什么是这个频率因为51的串口波特率是靠定时器1产生的11.0592MHz刚好能把9600波特率算得精确无比后续如果要用串口调试这个频率能省一大堆麻烦。复位电路。典型的10μF电解电容加10kΩ电阻形式就行。STC89C51RC是高电平复位上电瞬间电容充电RST脚短暂为高单片机复位稳定后RST被10kΩ电阻拉低正常工作。如果用了STC的芯片还要注意下载程序时需要断电再上电才能进入ISP模式这个习惯要在电路设计之初就养成电源开关一定要放在板子容易摸到的位置。3.3 LCD1602的接线与驱动注意点LCD1602在这个项目里承担了所有的人机交互显示它本身不带中文库所以显示的内容要全部用ASCII字符拼出来。我的习惯是第1行显示当前温度值第2行显示报警阈值这样调试阈值逻辑的时候一目了然。显示内容的格式建议Line 1: T: 25.6 C Line 2: H:30.0 L:10.0第1行实时温度第2行上限和下限。后面如果进入阈值设置模式第2行就加上光标闪烁提示这个可以用LCD1602自带的显示开关指令0x0F开启光标闪烁实现。LCD1602驱动本身不难关键就三条初始化时序要对、使能脉冲要短、忙标志要查。我见过很多人懒得不查忙标志直接延时硬等在仿真里没问题实机上就可能出现花屏或者显示不全因为LCD的写命令时间是us级别的延时估不准就会出问题。稳妥的做法是每次写命令或数据前先读BF标志位直到其为0再操作。void LCD_WriteCmd(unsigned char cmd) { LCD_CheckBusy(); // 等待LCD空闲 LCD_RS 0; LCD_RW 0; P0 cmd; LCD_EN 1; _nop_(); LCD_EN 0; // 下降沿锁存数据 } void LCD_WriteData(unsigned char dat) { LCD_CheckBusy(); LCD_RS 1; LCD_RW 0; P0 dat; LCD_EN 1; _nop_(); LCD_EN 0; }4. 预警逻辑、按键阈值调整与完整代码架构4.1 预警逻辑的状态机设计这个项目的预警不能简单理解成温度高了就响。一个在实际场景里可以用的系统至少要区分三种状态正常状态温度在上限和下限之间蜂鸣器不响绿灯或普通LED常亮表示系统在正常工作。超上限状态温度高于设定的上限值蜂鸣器急促鸣叫比如响200ms停200ms红灯闪烁这个节奏对应的语义是快来处理温度在失控。低限状态温度低于设定的下限值蜂鸣器慢速鸣叫响500ms停500ms黄灯或另一个LED慢闪语义是温度偏低可能要启动加热设备。这个状态机的好处是后续如果你要在这个项目基础上加继电器控制风扇、加热器只需要在状态切换的地方加上对应的IO控制就行。我在做实物的时候就是在超上限状态额外控制一个继电器模拟风扇启动效果立竿见影。实现上很简单主循环先读温度然后跟存储的上下限比较得出状态号再把状态号映射到蜂鸣器和LED的控制模式上typedef enum { STATE_NORMAL 0, STATE_OVER_HIGH, STATE_OVER_LOW } SystemState; SystemState GetSystemState(float temp, float high_limit, float low_limit) { if (temp high_limit) return STATE_OVER_HIGH; if (temp low_limit) return STATE_OVER_LOW; return STATE_NORMAL; }4.2 按键阈值设置的标准交互方式预警系统本身带按键调整阈值看起来是加分项做不好反而是扣分项。这里的关键在于你要设计一套用户能理解、流程不会乱的交互逻辑。我的做法是用三个按键按键S1设置模式切换键。短按进入上限设置模式再短按进入下限设置模式再短按退出设置模式返回正常显示。按键S2数值加。在设置模式下每按一次设定值加0.5℃长按可以连续加。按键S3数值减。逻辑同上。进入设置模式后LCD第2行对应位置显示当前调整的值并且光标闪烁提示。这样即使不看说明书用户也知道正在改的是哪个值。这里有一个很实用的手感细节按键消抖不能只靠延时我用的是状态机消抖法——检测到按键按下后启动一个20ms的定时器20ms后再读一次按键状态如果还是按下才算有效。这样既消除机械抖动又不会因为主循环里delay(20)卡住温度读取的节奏。unsigned char Key_Scan(void) { static unsigned char key_state 0; unsigned char key_press 0; switch (key_state) { case 0: // 初始态检测按下 if (KEY_SET 0) key_state 1; break; case 1: // 按下后延时消抖 delay_ms(20); if (KEY_SET 0) { key_press KEY_SET_PRESSED; key_state 2; } else { key_state 0; } break; case 2: // 等待释放 if (KEY_SET 1) key_state 0; break; } return key_press; }4.3 完整代码架构定时器、主循环与中断的合理分工很多新手写这个项目时是一个死循环里连着读温度、刷LCD、扫按键结果温度转换的750ms延时期间按键按了没反应LCD也跟卡死了一样。这个体验很差而且逻辑上就是错的。正确的做法是把功能按时间片拆开主循环每秒读一次温度更新显示缓冲区定时器0中断每1ms进一次负责按键扫描和蜂鸣器响铃控制用计数的方式产生间隔的响声模式LCD显示只在缓冲区数据变化时刷新避免频繁全屏刷新产生的雪花。这个架构的好处是即使DS18B20转换需要750ms这段时间内按键依然响应蜂鸣器依然可以按节奏响系统整体的实时性感觉完全不同。主循环的伪代码如下void main(void) { System_Init(); // 时钟、GPIO、定时器、LCD初始化 while (1) { current_temp DS18B20_GetTemperature(); UpdateDisplay(current_temp, high_limit, low_limit); state GetSystemState(current_temp, high_limit, low_limit); SetAlarmOutput(state); delay_ms(1000); // 每秒刷新一次 } }定时器0中断里做按键扫描和响铃节奏控制void Timer0_ISR(void) interrupt 1 { static unsigned int ms_counter 0; ms_counter; Key_Scan_WithCounter(); // 每10ms扫描一次按键 if (alarm_beeping_mode BEEP_FAST) { if (ms_counter % 400 0) BEEP !BEEP; // 200ms切换一次 } }有人可能觉得这个项目没有必要用定时器但我要说的是用定时器管理时间片是嵌入式开发的底层思维从这个项目开始养成习惯后面写任何稍微复杂一点的系统都不会吃亏。4.4 浮点运算在51上的取舍代码里用到了float类型存储温度值这在51上其实是个挺重的操作因为51内核没有硬件浮点单元每一次浮点加减乘除都是几十条甚至上百条指令。但DS18B20的温度值本身就是0.0625的倍数所以完全可以用定点数来替代。我的做法是温度原始值直接用int类型存储单位是0.0625℃。显示的时候整数部分就是raw 4小数部分就是(raw 0x0F) * 625 / 1000。这样全程只有整数运算速度比浮点快几倍还省RAM。// raw_value 为 DS18B20 读出的原始值 int integer_part raw_value 4; int decimal_part (raw_value 0x0F) * 625 / 1000; // 拼接成字符串显示如 25.3 sprintf(display_buf, %d.%d, integer_part, decimal_part);不过如果你设的阈值精度要到0.5℃用浮点其实也能跑问题不大。这个优化属于更好而非必须看你自己的追求。我的习惯是既然做51就要有做51的样子能用整数绝对不用浮点这也是这个芯片上的基本素养。5. Protues仿真直接接线的正确姿势与仿真陷阱5.1 为什么说Protues能直接仿真DS18B20Protues从7.x版本开始就内置了DS18B20的仿真模型所以你不需要像早年那样去网上找什么DS18B20的仿真库直接在元件列表里搜DALLAS DS18B20就能找到。这个模型的行为和真芯片几乎一致包括单总线的时序响应、温度转换时间、甚至负温度的处理逻辑都能在仿真里验证。直接接线的意思是指在Protues里放置DS18B20后它的三个引脚——1脚接地、2脚接单片机IO、3脚接VCC——直接连上去就行。但别忘了在2脚和VCC之间放一个4.7kΩ的排阻或者普通电阻。很多人在仿真里不加这个上拉电阻也能跑是因为Protues模型的输入阻抗是理想化的但在实物上不加这个电阻是绝对跑不起来的。所以仿真里就把习惯做好实物才能少走弯路。5.2 仿真中的时钟设置与实际速度的关系Protues仿真51单片机时默认晶振频率是12MHz但有个细节特别容易坑人Protues里双击单片机芯片在Clock Frequency里填的数值才是实际仿真时用的主频。如果你在代码里用了delay_ms(750)等待DS18B20转换但仿真芯片的时钟频率被设成了1MHz那实际的延时会被拉长12倍整个系统的表现会很诡异。我建议你在放置STC89C51RC元件后第一件事就是把Clock Frequency改成11.0592MHz并且写一个LED闪烁测试程序验证一下秒准时不准。具体方法把P1.0接一个LED程序里延时500ms翻转一次用Protues右下角的仿真时间看LED的翻转周期是不是1秒。是说明延时基础打好了不是就要检查晶振频率设置和延时函数。5.3 Protues仿真的三个隐藏限制仿真能帮你验证逻辑但别过度迷信仿真结果。我在这个项目里踩过的仿真具体限制有三个第一DS18B20的温度模型是手动设置的。Protues里的DS18B20不会自动感知室温变化你需要双击元件在Temperature属性里手动填写当前温度值或者拖一个虚拟温度调节器DVOM来模拟变化。很多人仿真时死活读不到变化就是因为这个值一直保持在默认的25℃。第二LCD1602的对比度调节在仿真里同样不能省。LCD1602的3脚V0要接一个电位器通常调到1.5kΩ到2kΩ的分压位置屏幕才会有清晰的字迹。仿真里如果V0悬空或者直接接地LCD要么全黑要么无显示让我一度以为是驱动代码写错了。第三Protues对51的IO时序仿真比真实芯片慢。在一些极端情况下仿真里能通过的延时参数放到实物上可能需要微调。这要求你在写代码时留出充足的时序余量不要卡着上限值来比如复位低电平时间直接做960μs而不是480μs就不会有这种麻烦。5.4 仿真验收的标准流程我自己在Protues里跑这个项目的标准流程是放置全部元件按照前面表格连好线设置DS18B20温度为25℃。运行仿真确认LCD第1行显示T: 25.0 C与设定值一致允许0.5℃以内的舍入误差。动态改变DS18B20温度到35℃观察LCD是否更新、蜂鸣器是否进入急促报警模式。按下S1进入上限设置按S2把上限调低到30℃以下确认即使当前温度不变只要超过新阈值就能触发报警。再把温度调到5℃确认低限报警和状态显示正常。走完这五步仿真层面的功能就验收完毕了。点开Protues的调试菜单你甚至可以单步跟踪DS18B20的时序波形这对理解单总线协议特别有帮助。6. 实物联调复盘三次典型的失败与解决路径6.1 第一次上电LCD不亮哪里都像是坏的仿真跑得顺顺当当一到实物就翻车这是最经典的剧本。我第一次焊完板子通电LCD1602背光亮了但屏幕上什么字都没有。排查步骤我按优先级列一下先量电源5V纹波大不大单片机VCC脚和GND脚有没有虚焊。然后是复位脚用万用表测RST正常应该是0V左右如果是2V以上的高电平说明复位电路有问题单片机一直在复位循环里跑不起来。再然后是晶振用示波器量晶振两个引脚应该能看到稳定的正弦波如果波形异常或者没有查晶振和匹配电容有没有虚焊。这三个地方查完90%的不亮问题都能定位。我那次的根因特别低级——STC89C51RC的31脚EA引脚没有接VCC。这个脚是使用内部ROM的使能信号不接高电平的话单片机访问的是外部ROM而外部根本没有ROM程序自然跑不起来。6.2 第二次测试温度显示85℃的完整排查流程LCD正常工作后屏幕上稳定显示85℃。这次我没急着改代码而是按链路一层层排查。第一步用逻辑分析仪抓DS18B20的DQ引脚波形看复位是否存在。如果复位成功应该能清晰看到主机拉低960μs、释放、然后出现一个来自从机的存在脉冲低电平。我的波形里存在脉冲根本没有说明复位就没通过。第二步既然复位没通过我先测DQ引脚的静态电平。正常空闲时DQ应该是高电平被上拉电阻拉高如果测到的是0V说明要么DS18B20没供电、要么接反了引脚、要么上拉电阻虚焊。第三步查来查去发现是DS18B20的VDD和DQ两个引脚在焊接时搭锡了等于把数据线钳制在高电平上单片机发什么它都收不到。重新焊开后温度显示恢复正常。这个案例告诉我们调试时序问题的时候先用万用表排除硬件故障再上示波器看波形最后才去改代码里的延时顺序反了只能在代码里瞎改一通还找不到问题。6.3 第三次调试报警阈值边界处的抖动功能全部通了以后出现一个体验问题当温度精确等于上限值时报警器会不断在响和不响之间切换特别烦人。根源在比较逻辑上——上一秒温度比上限低0.1℃时不报警下一秒因为DS18B20的量化误差变成比上限高0.1℃就触发了报警。解决方法是引入滞回控制这也是工业报警系统里常用的思路报警触发阈值是上限值H但解除报警的阈值是H - 1℃。也就是说温度升高超过30℃才报警降到29℃以下才停止报警中间这个1℃的区间就是滞回带避免了临界点来回抖。case STATE_OVER_HIGH: if (current_temp high_limit - HYSTERESIS) state STATE_NORMAL; break;同样的逻辑应用到低温侧。这个改动很小但对手感的影响非常大也是能用的作品和好用的作品之间的一道分水岭。6.4 实机运行中的抗干扰与稳定措施实机调试时我还发现按下按键的瞬间LCD上的温度偶尔会跳动一下。这是因为按键抖动或者人手上的静电干扰通过IO口串进了DS18B20的数据线。虽然这个现象偶尔出现可以不管但有两个便宜实用的方法值得做上一是在DS18B20的VDD和GND之间加一个0.1μF的陶瓷去耦电容并且尽量靠近传感器引脚放置。DS18B20在工作时会瞬间抽取电流去耦电容能把这部分电流变化消化掉不让它通过电源线干扰其他电路。二是在DQ线上串联一个100Ω到220Ω的电阻位置靠近单片机一侧。这个电阻配合4.7kΩ上拉电阻形成低通滤波器能抑制高频干扰。STC的官方参考设计里也有这个电阻不是画蛇添足。这两步做完之后温度读数稳了很多。如果你准备把这个系统放到电机的旁边做电源柜监测还要考虑电机启停瞬间的电磁场干扰那时候可能要用屏蔽线走传感器线缆但那是另外一个话题了。7. 温度预警系统还能往哪些方向扩展这个项目做完以后即使课程设计已经验收它仍然有巨大的继续开发空间。我根据自己做过的类似项目给出几个低成本、高性价比的扩展方向你可以按需选择。串口通信与上位机。STC89C51RC有全双工UART加一个MAX232或者CH340模块就能把温度和报警状态实时传给PC。上位机用Python的pyserial写一个简单的串口读数程序20行代码就能做成一个实时温度曲线面板。这个扩展对算法岗、软件岗的面试很有帮助因为你既展示了嵌入式底子又展示了一点上位机能力。存储历史极值。STC89C51RC没有EEPROM但可以外接一片AT24C02通过I2C协议存取最近一次报警时的温度值、时间戳等数据。I2C又是一个经典协议学会它的成本很低却能在简历上多写一行熟悉I2C总线应用。用继电器控制加热/降温设备。把P1.2改成继电器的控制端配合前面做的状态机这个系统就从一个报警器升级成一个温度控制器了。当温度低于下限时继电器吸合接通加热棒温度恢复正常时断开。这就是最简版恒温箱的控制逻辑。注意继电器线圈要并一个1N4007续流二极管否则继电器断开瞬间产生的反向电动势会打坏三极管或单片机IO口。供电方案。实验板上的USB供电只适合调试如果要做成独立的桌面小设备建议加一个AMS1117-3.3加上5V直流电源适配器的双级供电方案或者用MP1584这类DC-DC模块把12V输入降到5V这样可以直接把传感器放到工业控制柜的导轨上长期运行。我自己的体会是51单片机虽然老但它把所有外设的逻辑都摆在明面上没有STM32那么多抽象的库函数和时钟树配置反而让你更容易看清每个功能背后的本质。DS18B20这个传感器又是学习单总线协议的绝佳教材时序的每一微秒都有它的含义。把这两者吃透后面再接触OneWire设备、甚至I2C和SPI都会有一种这不过是另一种规则的时序的通透感。希望这篇从时序到实物、从仿真到联调的东西能帮你少走几步弯路把更多时间留给真正有意思的功能设计。
返回列表