基于Proteus的无线温度采集报警系统仿真设计与实现
简介一套基于51单片机与Proteus的无线温度采集报警系统完整设计方案适合单片机学习者、课程设计开发者以及想了解DS18B20温度采集与LCD1602显示的读者。系统包含温度采集端与显示报警端两大部分采集端完成温度检测与模拟无线发送接收端完成数据显示和超阈值报警可快速验证完整工作流程。压缩包共40个文件体积约442KB内有Proteus仿真工程pdsprj/pdsbak、C语言源代码c/hex/uvproj、编译过程生成的obj/lst/m51等中间文件以及仿真截图和说明文档能够对照电路与程序直接运行和改进。目前已有155人学习下载适合用于课程设计、毕业设计或单片机入门实践。资源把原理图、程序代码和无线传输机制整合在一起便于从整体上理解温度监测系统的软硬件协同设计也方便在此基础上扩展阈值设置、多节点采集等功能。1. 为什么这个题目值得用 Proteus 做仿真系统毕业设计里有一大半的“无线温度采集报警系统”都是同一个骨架一个单片机读 DS18B20把温度打包成帧经过无线模块发给另一个单片机另一个单片机负责显示和报警。这个题目的难点从来不是温度转换而是“无线链路”在仿真环境里怎么落地。题目里写的 protues 其实是 Proteus 的笔误真正让项目停在第一步的是打开 Proteus 找不到 nRF24L01 模型。答案是不死磕模型把“无线”理解成一条可替换的通信链路用串口直通先跑通协议后续换真实模块时只动驱动层。下面按这个思路做覆盖仿真图构建、源代码改写到移植要点适合课设/毕设起步也适合想把现有代码快速迁到 Proteus 的人。2. 系统拆分与 Proteus 里可落地的“无线”方案2.1 发送节点、无线链路、接收报警三块怎么分系统拆成两个节点更符合题目里的“无线采集”语义。发送节点以 STC89C52RCProteus 里可用 AT89C51 代替为主控外接 DS18B20 温度传感器定时读取温度后按自定义帧格式从串口发出。接收节点用另一片同型号单片机串口接收帧、解析温度在 4 位七段数码管上显示并在超过阈值时驱动蜂鸣器报警。选型上不选 STM32 有两个原因。Proteus 8 以上确实能完整仿真部分 STM32但外围配置时钟树、启动文件、库函数会让这个题目变成“先学会 STM32 再调仿真”偏离了温度采集和报警逻辑本身。51 单片机的寄存器少、中断嵌套简单DS18B20 的单总线时序用延时函数就能调出来接收节点的显示和按键切换也更直观。对于课程设计、毕业设计这类时间有限的场景51 是性价比最高的选择。2.2 无线链路在仿真里的等效模型串口直通先回答一个最常见的问题Proteus 里没有 nRF24L01 模型还能叫“无线”吗多数题目要求验证的是应用层协议和报警策略不是射频链路本身。真实无线模块比如 HC-12、nRF24L01在单片机上最终呈现出来是一个可以跨节点收发字节的管道。因此在 Proteus 中最常见也最可靠的做法是把发送节点的 P3.1/TXD 接到接收节点的 P3.0/RXD把发送节点的 P3.0/RXD 接到接收节点的 P3.1/TXD两个单片机共地用这两根交叉连线模拟“无线信道”。连线可以用网络标签RF_TX、RF_RX区分后期换成真实硬件时只改这两条线对应的驱动协议和显示代码不用动。这种串口直通方案有个前提波特率固定且双方一致。推荐 9600bps、11.0592MHz 晶振原因在串口初始化代码里能直接看出来// 串口初始化9600bps8 数据位1 停止位无校验 void UART_Init(void) { SCON 0x50; // 串口方式1REN1 允许接收 TMOD 0x0F; // 清空 T1 的控制位 TMOD | 0x20; // T1 工作方式28 位自动重装 TH1 0xFD; // 11.0592MHz 下对应 9600 TL1 0xFD; TR1 1; // 启动定时器1 ES 1; // 开串口中断 EA 1; // 开总中断 }这里TH1 0xFD是公式256 - 晶振频率 / (384 * 波特率)的计算结果。用 11.0592MHz 时256 - 11059200/(384*9600) 253也就是 0xFD分频完全整除波特率误差为 0。如果手里只有 12MHz 晶振9600 也能出但累计误差可能让长帧错位所以我一般不用 12MHz 做串口通信。2.3 通信协议帧格式与校验参考 Modbus 帧接收思路有了串口直通接下来要定义通信协议。帧格式越简单越好但要能区分帧头和帧尾还要能发现损坏字节。我常用的帧如下字段长度值/说明帧头20xAA 0x55类型10x01 温度帧节点号10x01 发送节点温度高字节1DS18B20 原始 16 位结果的高字节温度低字节1原始 16 位结果的低字节校验1类型到数据低字节的累加和取补码温度值直接用 DS18B20 的原始 16 位计数接收端再换算成摄氏度。这样发送端省去浮点运算接收端也只需要整数除法。校验方面发送端把类型、节点号、两个温度字节相加取低 8 位后做补码作为校验字接收端把包括校验字在内的 5 个字节累加结果等于 0 就认为帧有效。这种校验比 CRC 简单对于课设级协议足够但如果你要应对真实无线干扰建议替换成 CRC8帧结构不变。接收端的逐字节处理用状态机比 main 循环里原地等待更稳。串口中断什么时候来哪个字节不可预测状态机能保证即使帧与帧之间夹着干扰数据也能在下一帧头出现时重新同步。这个思路和 Modbus 的单片机帧接收程序一样中断里只按状态收数据收完一整帧再置标志位主循环做业务解析。unsigned char recv_state, frame_len, checksum; unsigned char recv_buffer[8]; void UART_ISR() interrupt 4 { unsigned char d; if (!RI) return; d SBUF; RI 0; switch (recv_state) { case 0: if (d 0xAA) recv_state 1; // 第一字节帧头 break; case 1: if (d 0x55) // 第二字节帧头 { recv_state 2; frame_len 0; checksum 0; } else if (d 0xAA) { recv_state 1; // 可能是新的一帧 } else { recv_state 0; } break; default: recv_buffer[frame_len] d; checksum d; if (frame_len 5) // 类型节点数据2校验 { if (checksum 0) // 含校验字总和为 0 { frame_ready 1; } recv_state 0; } break; } }这段代码里recv_state记录当前同步位置frame_len只统计帧头之后的字节checksum累加帧头之后的所有字节。要注意的是checksum 0这个判断必须在frame_len 5之后马上做因为此时d已经是校验字节校验和已经累加完成。如果你把判断放在下一帧收到数据时再做checksum就会混入下一帧的字节。3. 源代码实现DS18B20 驱动、无线发送与接收报警程序3.1 DS18B20 单总线驱动与温度采集DS18B20 是单总线器件所有指令和数据都通过一根 DQ 线按位传输。Proteus 仿真时最容易出问题的不是代码逻辑而是延时长短。下面的驱动基于 STC89C52RC 的 12MHz 晶振写法放在 Proteus 和多数 51 开发板上都能跑。sbit DQ P1^0; // 单总线引脚 void Delay_OneWire(unsigned int t) { while (t--); } unsigned char DS18B20_Reset(void) { unsigned char presence; DQ 0; Delay_OneWire(120); // 拉低约 480us DQ 1; Delay_OneWire(16); // 释放总线约 60us presence DQ; // 读取存在脉冲 Delay_OneWire(120); return presence; } void DS18B20_WriteByte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DQ 0; if (dat 0x01) DQ 1; // 写 1先拉低再拉高 Delay_OneWire(10); DQ 1; dat 1; } } unsigned char DS18B20_ReadByte(void) { unsigned char i, dat 0; for (i 0; i 8; i) { dat 1; DQ 0; DQ 1; // 释放总线让 DS18B20 驱动 DQ if (DQ) dat | 0x80; Delay_OneWire(10); } return dat; }Delay_OneWire(120)在不同晶振下的实际时间不一样这个函数不是精确延时而是靠循环次数消耗 CPU 时间。如果 Proteus 仿真中温度值恒为 85 或 0优先调整这里的延时基数而不是改读取顺序。DS18B20_Reset返回的存在脉冲在复位成功时是 0如果一直读到 1说明 DQ 引脚没有上拉或延时不对。温度读取的封装函数如下返回的是 DS18B20 原始 16 位结果单位是 1/16 摄氏度int DS18B20_GetTemp(void) { unsigned char tl, th; int temp; DS18B20_Reset(); DS18B20_WriteByte(0xCC); // 跳过 ROM 匹配单点测温 DS18B20_WriteByte(0x44); // 启动温度转换 Delay_OneWire(200); // 等待转换完成12 位分辨率下最长 750ms DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); // 读暂存器 tl DS18B20_ReadByte(); // 低字节先出 th DS18B20_ReadByte(); // 高字节后出 temp ((int)th 8) | tl; return temp; }0xCC是跳过 ROM 的指令适合总线上只挂一个 DS18B20 的情况0x44启动一次温度转换0xBE读取 9 字节暂存器的前两字节也就是温度值。th是有符号数(int)th 8这一步要把 th 先扩展成 16 位有符号数否则高字节的符号位会丢失。实际温度可以这样换算温度 temp / 16.0比如返回 480表示 30.0℃。3.2 发送节点主程序组帧、重发与状态指示发送节点的主循环逻辑很直接读取温度、按帧格式填充、通过串口发出、等待 2 秒。为了降低无线干扰导致的丢帧我一般会把同一帧连续发送 3 次接收端以最后收齐的一帧为准。这样虽然占用三倍串口带宽但对于 9600bps 和 2 秒上报周期来说毫无压力。void Send_Frame(unsigned char *buf, unsigned char len) { unsigned char i; for (i 0; i len; i) { SBUF buf[i]; while (!TI); // 等待发送完成 TI 0; } } void main(void) { int t; unsigned char frame[8]; UART_Init(); while (1) { t DS18B20_GetTemp(); frame[0] 0xAA; // 帧头 frame[1] 0x55; frame[2] 0x01; // 类型温度帧 frame[3] 0x01; // 节点号 frame[4] (unsigned char)((unsigned int)t 8); // 高字节 frame[5] (unsigned char)t; // 低字节 frame[6] 0 - (frame[2] frame[3] frame[4] frame[5]); // 校验 Send_Frame(frame, 7); Delay_Ms(2000); // 2 秒上报一次 } }这里Send_Frame用while (!TI)等待上一字节发送结束避免连续写SBUF导致丢字节。frame[6]的写法利用了 unsigned char 溢出取低 8 位的性质等价于对前四个字节做累加和的补码。帧头两字节不参与校验接收端的状态机只校验帧头之后的 5 个字节。Delay_Ms在这里是自定义的粗略延时函数用两层循环实现不需要特别精确如果你用定时器实现中断延时注意不要让定时器中断打断 DS18B20 的时序否则读到的温度值会不稳定。3.3 接收节点帧解析、数码管显示与蜂鸣器报警接收节点的主循环在检测到frame_ready后从recv_buffer里恢复温度值然后刷新显示并比较报警阈值。阈值默认 30℃用两个按键分别加减按键检测放主循环里做消抖即可。sbit BEEP P2^0; sbit KEY_UP P3^2; sbit KEY_DOWN P3^3; unsigned char frame_ready; int current_temp; int threshold 300; // 30.0℃用 0.1℃ 为单位 void parse_and_display(void) { signed char temp_h; int tenths; temp_h (signed char)recv_buffer[2]; // 温度高字节带符号 current_temp ((int)temp_h 8) | recv_buffer[3]; // 原始值 tenths (current_temp * 10 8) / 16; // 四舍五入到 0.1℃ Display_Temp(tenths); // 数码管显示 if (tenths threshold) { BEEP 0; // 低电平驱动蜂鸣器 } else if (tenths threshold - 10) // 2℃ 滞回防止边界抖动 { BEEP 1; } } void main(void) { UART_Init(); Timer0_Init(); // 数码管动态扫描用定时器 while (1) { if (frame_ready) { frame_ready 0; parse_and_display(); } if (KEY_UP 0) threshold 10; if (KEY_DOWN 0) threshold - 10; Delay_Ms(10); } }这一段里有两个容易踩的坑。第一个是温度高字节的符号扩展如果直接写(recv_buffer[2] 8)recv_buffer[2]是 unsigned char负数温度补码会被当成正数显示就会出现 190℃ 之类的值所以必须先把高字节转换成signed char。第二个是报警抖动温度在阈值附近波动时蜂鸣器会反复通断加一个 2℃ 的滞回区间比单纯比较更有实用意义。threshold用 0.1℃ 做单位显示 30.0℃ 时对应的阈值变量是 300这样按键每次加减 1.0℃ 的步进也容易算。Display_Temp需要配合共阳数码管段码表如果第一位数码管不亮检查 Proteus 里的数码管是共阳还是共阴段码表要相应取反。这里的Timer0_Init用于动态扫描中断频率最好在 2ms 左右否则温度刷新时会看到明显闪烁。4. Proteus 仿真搭建与参数设置从仿真图到联调4.1 元器件清单与连线要点Proteus 工程里至少要放两片 AT89C51一片做发送节点一片做接收节点。发送节点挂 DS18B20、上拉电阻、复位和晶振电路接收节点挂数码管、按键、蜂鸣器。完整清单如下AT89C51 x2DS18B20 x17SEG-MPX4-CA4 位共阳数码管x1BUTTON x2BUZZER x1RESPACK-810kΩ 排阻x1接 P0 口CRYSTAL 11.0592MHz x2CAP 30pF x4晶振匹配电容CAP-ELEC 10uF x2复位电路RES 10kΩ x2复位电阻RES 4.7kΩ x1DS18B20 上拉RES 1kΩ x1蜂鸣器基极电阻NPN 三极管如 2N2222x1连线时注意三件事第一AT89C51 的 EA 引脚必须接 VCC否则单片机默认从外部 ROM 取指令Proteus 会报内存错。第二P0 口内部没有上拉数码管段码接 P0 时必须接排阻否则显示乱码。第三两个单片机的地必须连在一起串口直通的参考地不一致会导致电平判断错误。4.2 元件参数设置晶振、数码管与报警器件Proteus 里双击元件即可修改参数关键设置如下表元件参数设置作用CRYSTALFrequency 11.0592MHz保证 9600 波特率整分频7SEG-MPX4-CACommon Cathode 改为 Common Anode匹配程序里的共阳段码表RESPACK-8Resistance 10kΩP0 口上拉RES 接 DS18B204.7kΩ单总线空闲时拉高CAP 接晶振30pF起振稳定Proteus 中影响不大晶振参数修改后单片机模型会自动使用新的时钟频率。如果你在发送节点用了 11.0592MHz、接收节点忘了改仍然保持 12MHz两边波特率会有偏差Virtual Terminal 上就会出现首字节正常、后续字节错位的情况。数码管的型号后缀决定了公共端极性程序里的段码表要跟你选的型号一致。报警器件在 Proteus 仿真里有个特色BUZZER 默认是直流蜂鸣器仿真时不一定真的发声但消耗电流会被仿真器计算进去。要直观看到报警可以在蜂鸣器两端并联一个 LED或者用电压表观察蜂鸣器引脚电平。真实硬件里蜂鸣器不能直接接到 P2 引脚需要三极管驱动这一点在仿真里体现不出来。4.3 联调顺序与三处常见错误不要一上来就把两个节点全连好再运行出问题会不知道从哪里查。按下面的顺序联调更稳第一步发送节点单独运行把串口 TX 接到一个 Virtual Terminal看是否能周期性输出AA 55开头的字节流。如果看到乱码先检查波特率和晶振。第二步接收节点单独运行用另一个 Virtual Terminal 模拟发送端手动敲入一帧带正确校验的数据观察数码管是否显示对应温度。这一步能验证状态机和解析逻辑。第三步把两个节点通过RF_TX、RF_RX网络标签连起来跑完整系统。常见错误集中在三处串口乱码。TH1不是 0xFD或者两个单片机晶振频率不一致。把UART_Init里的寄存器值重新对照表算一遍。DS18B20 温度恒为 85℃。这是单总线复位时序不完全正确时的典型值。检查 DQ 上拉电阻是否连接以及Delay_OneWire延时是否被编译器优化。在 Keil 里对延时函数加volatile修饰或者把优化级别改为 Level 0。接收端只有第一帧温度正确之后不再更新。这种问题多半是中断里frame_ready置位后主循环处理时间超过 2 秒导致新的帧头被状态机忽略。检查数码管动态扫描和按键消抖是否在中断里做了太多事情把耗时操作移到主循环。4.4 用 Virtual Terminal 抓帧验证协议Virtual Terminal 是 Proteus 里排查串口问题最直接的工具。它在左侧工具箱的 Instruments 面板里放置后接在单片机的 TX 引脚上RXD 引脚空着即可。运行仿真后Virtual Terminal 会把收到的二进制按 ASCII 解析所以直接看温度帧会是一堆不可读字符。更实用的做法是写一个十六进制调试函数把每个字节拆成两个 ASCII 字符输出void Debug_HexByte(unsigned char ch) { unsigned char code hex[] 0123456789ABCDEF; SBUF hex[ch 4]; while(!TI); TI 0; SBUF hex[ch 0x0F]; while(!TI); TI 0; SBUF \r; while(!TI); TI 0; SBUF \n; while(!TI); TI 0; }把这个函数放到发送节点主循环里临时调用比如只打印frame[4]和frame[5]两个温度字节Virtual Terminal 里就能看到清晰的十六进制文本。需要注意的是这个调试函数会改变发送时序调试完记得删除或放到条件编译里不要影响正式帧的发送间隔。5. 把仿真程序移植到真实硬件的 3 个关键点5.1 用真实无线模块替换“串口直通”仿真里的两根交叉连线换到硬件上时最简单的方式是使用 HC-12 无线串口模块。HC-12 工作在透明传输模式单片机的 TXD 接模块 RXD、RXD 接模块 TXD设置好相同波特率和信道后代码一行都不用改。需要注意 HC-12 与单片机之间必须共地否则会偶发乱码。如果换成 nRF24L01就不能再用串口收发函数了。nRF24L01 走 SPI 接口初始化时要配置 CE、CSN、SCK、MOSI、MISO 五个引脚发送前把帧数据写入 TX 缓冲区等待发送完成中断。这时候帧格式可以保留但Send_Frame函数要替换成 SPI 写操作接收端也要改成从 RX 缓冲区取数据。nRF24L01 是 3.3V 器件51 的 P0/P1 输出 5V 电平最好加电平转换或者用 MOSI 串电阻加稳压二极管的分压做法。5.2 电平、电源和报警驱动的现实差距Proteus 仿真不会告诉我们引脚灌电流的后果。真实 DS18B20 的 DQ 是开漏输出4.7kΩ 上拉不能省否则读回来的全是 0。51 的 P0 口也是开漏数码管段码接 P0 时排阻必须保留。蜂鸣器是最容易被忽略的硬件坑。有源蜂鸣器工作电流常见 20mA 以上直接接 P2 引脚会让引脚电压被拉低严重时触发复位。正确接法是用 NPN 三极管单片机 P2.0 通过 1kΩ 电阻接三极管基极蜂鸣器一端接 5V一端接三极管集电极发射极接 GND。程序里的BEEP 0是让三极管导通这一点和仿真里直接给蜂鸣器低电平效果相同。5.3 验证帧收发的两个工具硬件调试时不能用电脑声卡去猜串口数据。逻辑分析仪是第一个推荐工具把它并接在发送节点 TX 引脚上能看到每一字节的宽度和帧间距。正常帧间距应该一致如果某一帧中间断开说明程序里有中断长时间占用 CPU比如 DS18B20 的 750ms 转换等待。第二个工具是 USB-TTL 串口板。把串口板与单片机交叉连接跑协议分析上位机软件直接用 HEX 模式看收到的字节。如果收到的帧校验总是失败优先检查 USB-TTL 的电平跳线和供电地线。把这几个点放在硬件调试清单第一页能少走不少弯路。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →