
简介这是一套基于Proteus仿真的单片机无线温度采集报警系统设计完整资料适合单片机学习者、电子爱好者以及正在准备课程设计或毕业设计的学生使用。系统以51单片机为控制核心配合DS18B20温度传感器、LCD1602液晶显示模块和模拟无线传输手段完成温度采集、实时显示与超阈值报警等功能帮助读者理解无线温度监测装置的电路构成与程序开发流程。压缩包共包含40个文件主要提供仿真电路工程、C语言源程序、HEX固件、Keil工程配置、原理图截图以及备份文件等可对照查看发射端与接收端的电路连接及程序逻辑类型覆盖了从仿真调试到程序烧录的各个环节。整体大小仅442KB小巧齐全便于快速下载和二次修改。目前已有155位学习者下载使用对需要快速搭建无线温度采集报警系统样例的读者而言是一份值得参考的实践性资源。1. 单片机仿真无线温度报警系统先想清楚要解决什么问题调试室里要盯的设备柜、实验室烘箱、温室大棚测温点往往藏在角落或高处拉线既碍事又难维护。这个标题要解决的正是这类问题一块板子采集温度无线发出去另一块板子接收、显示并报警。所谓“基于单片机仿真的protues无线温度采集报警系统设计”拆开就是Proteus仿真电路、无线温度收发、Keil C51源代码三件事。标题里把Proteus写成了protues是这类搜索里非常常见的笔误按这个写法也能搜到同类资源。这套方案适合单片机课设/毕设、正在学51单片机的入门工程师以及想快速做出一个带无线链路的原型演示的人。项目启动前先给一句结论无线收发本身并不难真正的工时落在DS18B20单总线时序和nRF24L01寄存器配置上这两块后面会重点展开。2. proteus无线温度采集电路的元件选型与接线方法2.1 为什么是DS18B20和nRF24L01而不是其他方案做单片机仿真项目选器件第一原则是Proteus元件库里有可用模型、Keil里有人验证过、接线能收尾。这个标题里最常用的组合是DS18B20做温度采集、nRF24L01做无线收发原因很具体。DS18B20是单总线数字温度传感器三根线就能工作内部直接出12位温度结果分辨率0.0625℃。相比LM35加ADC0832的组合它省掉了一条ADC采集通道、电压换算和参考电压设置相比热敏电阻它省掉了阻值到温度的指数换算。更重要的是在Proteus里双击DS18B20元件可以直接拖动温度滑块模拟环境温度变化这一条对后面验证报警阈值和无线链路非常关键选其他传感器很难做到这种仿真便利度。nRF24L01是2.4GHz频段的SPI接口无线收发芯片Proteus元件库中按NRF24L01搜索能拿到仿真模型。SPI口上的时钟、数据引线在仿真时是实打实翻转的能清楚看到通信过程。部分资料里推荐的315M/433M无线模块在Proteus中没有对应的射频仿真模型只能停留在逻辑示意层面而nRF24L01能完成真实寄存器读写对学习状态机帮助更大。MCU选择AT89C51或AT89C52晶振12MHz这是51单片机Proteus仿真最稳的组合。Keil C51默认工程模板对这两个芯片兼容性最好网上绝大多数51单片机例程也是按12MHz机器周期写的延时函数可以直接套用。报警执行部分用BUZZER蜂鸣器和LED灯指示显示部分用LM016L这个型号是Proteus里1602字符液晶的标准模型。下表是本方案的必备元件清单元件Proteus搜索关键词数量用途单片机 AT89C51AT89C512一块发送一块接收温度传感器 DS18B20DS18B201采集环境温度无线模块 nRF24L01NRF24L012无线温度数据收发液晶屏 LCD1602LM016L1接收端显示温度和阈值蜂鸣器BUZZER1超限声音报警LED指示灯LED-RED1超限灯光报警晶振/电容/电阻排CRYSTAL、CAP、RESPACK-8若干最小系统和上拉2.1节的内容就是把元件的选型理由和仿真优势说清楚接下来看两种板卡的实际接线。2.2 发送端与接收端仿真电路接线对照Proteus工程建议建一张图左右各放一块AT89C51分别作为发送端和接收端运行起来能同时看到两边状态方便判断无线链路是否打通。如果做成两个独立工程仿真运行时还要来回切换窗口排查问题效率低。发送端要接的东西不多DS18B20的数据脚接P2.0信号线对VCC接一颗4.7kΩ上拉电阻保证单总线空闲状态是高电平。nRF24L01用P1口驱动接法如下表nRF24L01引脚单片机引脚说明CEP1.0收发模式切换CSNP1.1SPI片选低有效SCKP1.2SPI时钟MOSIP1.3主机输出从机输入MISOP1.4主机输入从机输出IRQ悬空代码里用查询方式不接外部中断VCC / GND5V / 地Proteus模型接5V实物模块需3.3V接收端除了同样接好nRF24L01之外还要接LCD1602和报警电路。LCD1602的DB0到DB7接P0口P0是开漏结构必须接一个8位排阻上拉否则屏幕上会出现满屏亮点块。RS接P2.5RW直接接地EN接P2.6蜂鸣器接P2.7指示灯接P2.4。报警器件放在P2口而不是P0口是因为P2口是准双向口输出高电平能力比P0强驱动蜂鸣器这类器件更可靠。两边的单片机最小系统也要画全31脚EA接VCC19脚和18脚之间接12MHz晶振两个引脚各接33pF对地电容9脚复位端接10μF电容到VCC、1kΩ电阻到地。这里最容易漏的是EA引脚仿真里如果EA浮空单片机默认从外部程序存储器取指烧进去的hex压根不会执行。2.3 Proteus仿真下载的3个必要配置第一用Keil编译出hex文件后在Proteus中双击单片机把hex路径填到Program File栏。填完之后单片机上会出现一个绿色小方块标记表示固件已经挂载。第二双击晶振元件确认频率是12MHz。Proteus默认新建工程的晶振频率经常是1MHz如果不改DS18B20的480μs复位时序、60μs写时序全部错位最典型的症状就是温度固定读出85℃。第三运行仿真后双击DS18B20模型会弹出温度设置滑块拖动滑块改温度观察接收端LCD数值和报警输出是否跟随。拖到阈值以上蜂鸣器应该动作拖回阈值以下蜂鸣器解除。提示部分版本的nRF24L01模型在Proteus高速动画档位下刷新会滞后表现为接收端延迟几秒才更新。遇到这种情况在System菜单的Animated Options里把仿真速度调回中档不影响调试结论。3. Keil C51源代码温度采集与nRF24L01无线收发3.1 DS18B20单总线时序与读温度源码DS18B20所有通信都发生在单总线上任何一次操作流程都是“复位→ROM命令→功能命令→读写数据”。12MHz晶振下单片机机器周期是1μs代码用_nop_()和循环变量来生成所需延时。先看复位和应答检测#include reg51.h #include intrins.h sbit DQ P2^0; // DS18B20 数据线 void delay_us(unsigned int us) { while (us--) _nop_(); // 12MHz 下 _nop_() 约 1us } unsigned char ds18b20_reset(void) { unsigned char presence; DQ 0; delay_us(500); // 复位脉冲持续 480us 以上 DQ 1; delay_us(60); // 释放总线等从设备应答 presence DQ; // DS18B20 拉低总线表示在线 delay_us(240); return presence; // 返回0表示设备在位 }这段代码的逻辑是主机先将总线拉低480μs以上制造一个复位脉冲然后释放总线此时总线由上拉电阻拉回高电平。DS18B20检测到这个下降沿之后会在15μs到60μs之间主动拉低总线60μs到240μs这个低电平就是应答信号。presence DQ这条语句读到的值低电平表示传感器在线高电平表示没接上或接线错误。写命令和读温度的代码是时序里最容易出错的部分void ds18b20_write_byte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DQ 0; _nop_(); _nop_(); // 拉低约 2us写时隙开始 DQ dat 0x01; // 写 1 就拉高写 0 保持低 delay_us(60); // 保持 60us DQ 1; // 结束该时隙 dat 1; } } unsigned char ds18b20_read_byte(void) { unsigned char i, dat 0; for (i 0; i 8; i) { dat 1; DQ 0; _nop_(); _nop_(); // 读时隙起始信号 DQ 1; delay_us(5); // 释放总线后等 5us 再采样 if (DQ) dat | 0x80; delay_us(60); // 等待时隙结束 } return dat; } unsigned int ds18b20_read_temp(void) { unsigned char tl, th; unsigned int temp; ds18b20_reset(); ds18b20_write_byte(0xCC); // 跳过 ROM ds18b20_write_byte(0x44); // 启动温度转换 delay_ms(750); // 12 位分辨率转换约 750ms ds18b20_reset(); ds18b20_write_byte(0xCC); ds18b20_write_byte(0xBE); // 读暂存器 tl ds18b20_read_byte(); // 低字节 th ds18b20_read_byte(); // 高字节 temp (th 8) | tl; return temp; // 实际温度 temp * 0.0625 }代码里的0xCC是跳过ROM命令适用于单传感器挂载省去读64位序列号0x44启动一次温度转换0xBE读取暂存器前两个字节。转换时间在12位分辨率下典型值是540μs到750ms不等代码里固定等待750ms最稳妥。读回的16位结果是带符号数正温度乘以0.0625得到摄氏温度负温度需要额外取反加一处理仿真阶段可以先用正温度验证链路。写1时隙要求主机拉低总线后1μs到15μs内释放总线代码里两个_nop_()约2μs写0时隙要全程拉低60μs。读时序时主机必须主动拉低总线至少1μs再释放之后从机驱动总线告诉主机当前bit是0还是1主机应在15μs内完成采样。delay_us(5)之后读引脚正好落在从机数据有效窗口内。3.2 nRF24L01寄存器配置和发送流程nRF24L01工作在SPI从机模式51单片机通过4根线控制它。通信前要做的第一件事是把收发双方配置成一致的信道、地址和负载长度。核心寄存器配置对照寄存器地址配置值作用CONFIG0x000x0E上电、使能CRC、16位校验EN_AA0x010x01打开通道0自动应答EN_RXADDR0x020x01只使能接收通道0SETUP_AW0x030x03收发地址宽度5字节SETUP_RETR0x040x1A重发延时500μs最多10次RF_CH0x0540工作信道2440MHzRF_SETUP0x060x0E2Mbps速率0dBm发射功率RX_PW_P00x114每个数据包有效载荷4字节STATUS0x070x70读状态后写1清除中断配置函数和发送一包数据的核心代码如下sbit CE P1^0; sbit CSN P1^1; sbit SCK P1^2; sbit MOSI P1^3; sbit MISO P1^4; #define TX_PLOAD_WIDTH 4 unsigned char code TX_ADDRESS[5] {0xE7, 0xE7, 0xE7, 0xE7, 0xE7}; unsigned char spi_transfer(unsigned char dat) { unsigned char i, recv 0; for (i 0; i 8; i) { MOSI (dat 0x80) ? 1 : 0; dat 1; SCK 1; _nop_(); _nop_(); recv 1; if (MISO) recv | 0x01; SCK 0; } return recv; } void nrf24l01_write_reg(unsigned char reg, unsigned char val) { CSN 0; spi_transfer(0x20 | reg); // 写寄存器命令 spi_transfer(val); CSN 1; } void nrf24l01_config(void) { CE 0; nrf24l01_write_reg(0x00, 0x0E); // CONFIG 上电 nrf24l01_write_reg(0x01, 0x01); // EN_AA nrf24l01_write_reg(0x02, 0x01); // EN_RXADDR nrf24l01_write_reg(0x03, 0x03); // SETUP_AW nrf24l01_write_reg(0x04, 0x1A); // SETUP_RETR nrf24l01_write_reg(0x05, 40); // RF_CH nrf24l01_write_reg(0x06, 0x0E); // RF_SETUP nrf24l01_write_reg(0x11, TX_PLOAD_WIDTH); // 写入发送地址和接收通道0地址 CSN 0; spi_transfer(0x20 | 0x10); // TX_ADDR for (i 0; i 5; i) spi_transfer(TX_ADDRESS[i]); CSN 1; CSN 0; spi_transfer(0x20 | 0x0A); // RX_ADDR_P0 for (i 0; i 5; i) spi_transfer(TX_ADDRESS[i]); CSN 1; CE 1; delay_ms(2); }发送一包温度数据的函数需要按照芯片手册的状态机顺序执行unsigned char nrf24l01_send(unsigned char *buf, unsigned char len) { unsigned char status; CE 0; // 先进入待机模式 CSN 0; spi_transfer(0xA0); // W_TX_PAYLOAD 写发送载荷 for (i 0; i len; i) spi_transfer(buf[i]); CSN 1; CE 1; // 拉高 CE 至少 10us启动发送 delay_us(15); CE 0; status spi_transfer(0xFF); // NOP 读状态 while (!(status 0x20)) { // 等待 TX_DS 置位 status spi_transfer(0xFF); if (status 0x10) { // MAX_RT 重发超限 spi_transfer(0xE1); // FLUSH_TX 清空发送缓冲 break; } } spi_transfer(0x20 | 0x07); // 清 STATUS 中断位 spi_transfer(0x70); return status; }发送状态机的关键在于CE引脚。芯片上电配置期间CE必须为低写完整包数据后CE拉高至少10μs芯片才会从待机进入发送状态发送完成后CE拉低回到待机等待下一包。代码里等待TX_DS位STATUS的第5位最可靠它表示数据包已经发射出去如果等待期间MAX_RT先置位说明自动重发超过10次要清空FIFO再继续。发射功率在RF_SETUP里配置成0dBm2Mbps速率会缩短空中传输时间减少碰撞概率适合Proteus仿真这种多节点同时工作的场景。3.3 接收端主流程无线接收、LCD显示和报警判定接收端的任务比发送端多一项报警输出。主循环先读状态寄存器检测到RX_DR置位后读出4字节有效载荷温度占前两字节后两字节可以留作阈值或备用标志。下面是可以直接改用的接收主函数框架void main(void) { unsigned char rxbuf[4]; unsigned char status, i; unsigned int temp; unsigned int threshold 40; // 默认报警阈值 40℃ lcd1602_init(); nrf24l01_config_rx(); // PRIM_RX1进入接收模式 CE 1; // 接收模式 CE 保持高 while (1) { status spi_transfer(0xFF); // NOP 读状态 if (status 0x40) { // RX_DR 接收数据就绪 CSN 0; spi_transfer(0x61); // R_RX_PAYLOAD for (i 0; i 4; i) rxbuf[i] spi_transfer(0xFF); CSN 1; spi_transfer(0x20 | 0x07); spi_transfer(0x70); // 清中断 temp rxbuf[0] | (rxbuf[1] 8); display_temperature(temp); alarm_scan(temp, threshold); } } }程序进入接收模式前必须保证CONFIG寄存器的PRIM_RX位是1并且CE引脚保持高电平。51单片机往CONFIG写0x1E就能完成“上电CRC16接收模式”三个动作。读取负载时用0x61命令每读一个字节主机要往SPI发送一个0xFF占位字节因为SPI是全双工主机不发时钟从机就不会输出后续数据。温度数据两字节高位在前与发送端打包顺序一致如果收发两端高低字节顺序不同接收端显示会出现跳变到几千的异常值排查时先看这一条。4. 双端仿真联调、报警逻辑与10个排查点4.1 在Proteus里加载两个hex的联调步骤发送端和接收端的单片机程序要分别编译成两个hex文件。Proteus图纸上两块AT89C51各自加载固件发送端烧发送程序接收端烧接收程序。双击单片机在Program File里选择对应hex然后点击左下角运行按钮。联调时先静态验证发送端双击DS18B20把温度从25℃拖到80℃观察发送端程序有没有卡死。如果温度从25℃往上走接收端LCD没有任何变化优先检查两块单片机上的nRF24L01三个关键参数RF_CH的值是否一致、地址数组是否一致、RX_PW_P0和发送端负载长度是否一致。Proteus不会因为无线参数不匹配而报错现象就是静默收不到。接收端LCD如果能显示温度再把发送端温度拖过40℃观察蜂鸣器和LED。如果只有显示没有报警问题在阈值判定逻辑往下看报警去抖。4.2 报警逻辑必须写的3个细节第一是去抖。温度在阈值边缘小幅波动时如果检测到一次超限就立刻报警蜂鸣器会反复通断仿真里听起来就像接触不良。常见的做法是连续3次采样都超限才触发报警。第二是回差。报警触发后只有温度低于“阈值减回差值”才解除报警。比如阈值40℃回差2℃则温度降到38℃以下才停止报警。这样避免温度在39.5℃附近徘徊时报警频繁开关。第三是蜂鸣器驱动不要用延时函数。蜂鸣器响的频率由定时器中断控制主循环负责判断条件并改变报警状态两者分离否则按键响应或LCD刷新会被阻塞。#define THRESHOLD 40 #define HYSTERESIS 2 bit alarm_state 0; unsigned char alarm_count 0; void alarm_scan(unsigned int temp) { if (alarm_state 0) { if (temp THRESHOLD * 16) { // 温度是0.0625℃精度 if (alarm_count 3) { alarm_state 1; alarm_count 0; } } else { alarm_count 0; } } else { if (temp (THRESHOLD - HYSTERESIS) * 16) { alarm_state 0; // 温度回落解除报警 } } if (alarm_state) { P2_7 ~P2_7; // 定时器中断里翻转产生方波 } else { P2_7 1; } }这段代码里的阈值乘16是因为DS18B20的原始返回值是12位定点数实际温度等于数值乘以0.0625也就是除以16。写成threshold * 16避免使用浮点运算51单片机算浮点会拖慢主循环。蜂鸣器引脚P2_7的翻转动作放在定时器0中断里当报警状态为1时中断服务程序里执行BEEP ~BEEP;就能输出1kHz左右的方法驱动蜂鸣器响报警解除后P2_7输出高电平蜂鸣器停。4.3 仿真高频故障按优先级排序的排查表仿真过程中遇到现象和原因对不上的情况先看下表按表格顺序检查成本最低。故障现象根本原因处理方式运行后单片机不工作EA引脚浮空EA接VCC温度一直显示85℃晶振频率不是12MHz或延时函数不对检查晶振和复位时序LCD满屏亮点块P0口无上拉接RESPACK-8排阻到VCC无线完全收不到收发信道、地址、负载长度不一致逐个核对RF_CH、地址数组、RX_PW_P0TX_DS始终不置位CE高电平时间不够CE拉高保持15μs以上蜂鸣器在阈值附近反复响没有回差逻辑加入HYSTERESIS的判断接收端温度跳变到几千高低字节组包顺序不一致检查收发两端字节序改完代码仿真没变化没有重新编译或没重新加载hexKeil重新编译后重新加载85℃这个问题值得单独说明DS18B20上电后暂存器中的温度寄存器默认值是0x0550正好对应85℃。如果读到固定85℃说明传感器通信根本没建立读到的只是默认值。遇到它不要怀疑传感器坏了先量复位时序。5. 验证无线链路与仿真到实物改造的3个实用技巧5.1 用虚拟示波器看SPI时序确认发送状态Proteus左侧工具栏的虚拟仪器里有OSCILLOSCOPE把SCK、MISO、CE三根线接过去。点击运行发送端每次发送时观察SCK是否有8个脉冲簇CE是否拉高超过10μsMISO线上是否有响应数据。这套方法能直接判断SPI通信是否建立比盲目改寄存器值快得多。如果SCK完全没有脉冲检查CSN是否为低、SPI初始化代码是否在main函数开头执行。5.2 把温度组包成Modbus RTU帧方便接上位机这个标题的系统如果只停留在仿真里价值有限。把温度数据进一步封装成Modbus RTU帧就能让接收端和组态软件、串口调试助手直接对话。温度寄存器放在30001保持寄存器功能码03CRC16校验。下面这段CRC计算代码可以直接复用unsigned int crc16_modbus(unsigned char *pdata, unsigned char len) { unsigned int crc 0xFFFF; unsigned char i, j; for (i 0; i len; i) { crc ^ pdata[i]; for (j 0; j 8; j) { if (crc 0x0001) crc (crc 1) ^ 0xA001; else crc 1; } } return crc; }组帧时按“从机地址功能码寄存器地址高字节低字节寄存器数量高字节低字节CRC低字节CRC高字节”的顺序发送8个字节一帧。CRC计算时先算前6个字节算完把低字节放前面、高字节放后面。帧接收端的判断标准是地址正确、功能码正确、CRC比对通过三者满足才解析数据。这是Modbus单片机帧接收数据程序的通用套路。5.3 实物移植要注意的三个改动点仿真图里nRF24L01接5V没问题但实物模块必须3.3V供电5V供电会烧毁模块。51单片机P1口输出5V电平和3.3V的模块通信时MOSI、SCK、CSN、CE四根线各串一个100Ω到1kΩ的电阻限流MISO是模块输出可以直连51。第二实物布局时nRF24L01下方不要铺铜天线下方的PCB区域留空否则收发距离明显缩小仿真里不存在这个问题最容易在实物阶段被忽略。第三如果换上STC15系列1T单片机或者STM32延时函数必须重新标定DS18B20的单总线时序靠的是微秒级延时12MHz下用_nop_计数的代码在1T芯片上整体快8到12倍直接运行会稳定读85℃改成定时器或示波器标定后的延时函数再继续调试。本文还有配套的精品资源点击获取