简介:这套基于51单片机的温度采集与NRF905无线传输程序,适合单片机爱好者、电子类专业学生及DIY玩家。资源包含完整的Keil工程源码,覆盖发射机与接收机两端程序,并配套LCD1602显示与DS18B20温度采集模块,可学习无线通信、传感器读取、显示驱动等核心电路与代码设计思路。包内共34个文件,以C源文件、头文件、Keil工程文件(uvproj/uvopt)为主,另有备份、目标文件及hex烧写文件等,整体大小仅80KB,结构清晰,便于直接打开和对照学习。压缩包内将发射机与接收机程序分目录存放,适合用于课程设计或毕业设计参考。已有172人学习下载,对于小体积入门级单片机通信项目而言具有一定参考价值。使用者需具备基础电子技术知识和C语言能力,建议结合实际电路搭建验证,重点理解无线收发流程及传感器数据转换逻辑。
1. 51单片机+NRF905+温度采集:一个低成本无线测温方案长什么样
把一个51单片机温度采集加NRF905无线传输的工程翻出来,折腾了一整个下午,终于让接收端的1602屏稳定显示出三米外发送端探头的实时温度。这套方案朴素但完整:发送端用DS18B20采集温度,通过NRF905无线模块在433MHz频段把数据发出去,接收端解出数据再显示到LCD屏幕上。对比蓝牙和Wi-Fi方案,NRF905的协议栈简单到可以手写,射频链路没有固件黑匣子,所有寄存器都暴露在SPI总线上,非常适合想搞懂“无线传输到底怎么把字节搬过去的”开发者。它也是小型温室监控、机房温度巡检这类低成本场景里最常见的工程模板,几十块钱物料就能搭一套出来。
2. 系统架构搭建:发送端到接收端的完整链路与硬件接线
2.1 双端结构:发送端采集、接收端显示的分工
整个系统由两个独立的51单片机最小系统组成。发送端单片机负责读取DS18B20温度,通过SPI接口把数据写进NRF905的发送缓冲区,再触发无线发射。接收端单片机的NRF905一直处于接收模式,检测到空中信号后自动完成解调和CRC校验,DR引脚拉高,单片机从SPI口把数据读出来交给1602屏幕显示。
这种双端设计有个明显的好处:发送端和接收端可以完全独立开发。先单独调试发送端,用逻辑分析仪看SPI波形,确认数据真的进入了NRF905;再单独调接收端,用一个循环发送程序当信号源,验证接收端能不能正确解出数据。最后把两边合起来联调,出问题时直接判断是哪一半的锅,不用同时怀疑两个方向。这个习惯在无线项目里非常救命,后边的避坑章节还会详细说。
2.2 NRF905引脚与51单片机接线,电平问题怎么处理
NRF905工作在433/868/915MHz三个ISM频段,调制方式是GFSK,空中波特率最高50kbps,手册标称接收灵敏度在-100dBm级别。对它而言,51单片机这种低速MCU反而是合适搭配,SPI时钟不用多快,几兆赫兹以下都能稳定跑。空旷环境配合1/4波长天线,通信距离在50到100米量级,室内穿一堵墙问题不大。
从外设角度看,NRF905一共只需要接7个引脚给单片机,外加一个DR数据就绪输出。工程里用的模块引脚分布和市面上常见的433MHz版本一致,接线参考下表:
| NRF905引脚 | 51单片机引脚 | 功能说明 |
|---|---|---|
| CE | P1.0 | 发射/接收使能,控制芯片进入对应状态 |
| PWR_UP | 3.3V | 上电控制,高电平使能 |
| TX_EN | P1.1 | 1为发送模式,0为接收模式 |
| CSN | P1.2 | SPI片选,低有效 |
| SCK | P1.3 | SPI时钟,由51单片机产生 |
| MOSI | P1.4 | SPI数据输入(单片机→NRF905) |
| MISO | P1.5 | SPI数据输出(NRF905→单片机) |
| DR | P1.6 | 数据就绪标志,发送完成或收到数据时变高 |
这里最需要注意的是电平匹配。51单片机用5V供电,NRF905芯片电源范围是1.9V到3.6V,模块板载稳压后接3.3V即可。但SPI信号线如果直接拿5V电平推NRF905引脚,时间长了容易损坏射频芯片。我在工程里用了最稳妥的做法:MOSI、SCK、CSN三条信号线上各串一个1k电阻做分压,MISO由NRF905输出3.3V电平给51单片机,5V的单片机读3.3V逻辑高电平完全没有问题。如果你的模块明确标注5V容忍,可以直接连,但我建议还是串电阻,成本几毛钱,省得哪天射频芯片烧了还找不到原因。
2.3 工程文件目录:每个源文件和头文件的职责
发送端和接收端在Keil里是两个独立工程,两边的单片机运行逻辑完全不同。发送端工程包含main.c、ds18b20.c、nrf905.c、delay.c四个源文件,接收端把ds18b20.c换成lcd1602.c。头文件的分工也清晰:ds18b20.h只暴露温度获取接口,nrf905.h封装射频模块的初始化和收发,lcd1602.h仅供接收端使用。
tx/ ├── main.c // 发送端主流程:采集→组帧→发射 ├── ds18b20.c // DS18B20单总线时序,温度读取 ├── ds18b20.h ├── nrf905.c // NRF905 SPI驱动、寄存器配置、收发 ├── nrf905.h └── delay.c // 毫秒和微秒延时,依赖晶振频率 rx/ ├── main.c // 接收端主流程:接收→解析→显示 ├── nrf905.c // 这里只使用NRF905接收功能 ├── lcd1602.c // 1602液晶初始化、写命令/数据、显示 ├── lcd1602.h └── delay.c模块化拆分后,你想改成“接收端把温度通过串口转发给上位机”,只需要在rx工程里加一个uart.c,完全不碰nrf905.c的接收代码。原工程里的延时函数基于12MHz晶振、标准12T模式的51单片机编写,如果你的板子是11.0592MHz或用了6T模式,延时函数循环次数必须重新计算,否则DS18B20和SPI时序都会出问题。这个问题几乎每个跑通项目的人都会踩一次,避坑章节单独说。
3. 发送端代码解析:从DS18B20读取温度到NRF905发射
3.1 DS18B20单总线协议:复位、跳过ROM、读暂存器的关键时序
DS18B20是单总线器件,一根DQ线同时传输时钟和数据,时序要求比普通SPI设备严格很多。51单片机没有硬件单总线外设,只能靠IO模拟,读一次温度大概分三步:复位总线、写命令跳过ROM匹配、读暂存器数据。
// 复位DS18B20,从机存在时返回0 unsigned char DS18B20_Reset(void) { unsigned char presence; DQ = 0; // 主机拉低总线 delay_us(480); // 保持低电平480us以上,形成复位脉冲 DQ = 1; // 释放总线 delay_us(60); // 等待从机响应窗口 presence = DQ; // 采样:DS18B20会拉低总线60~240us delay_us(420); // 补全剩余时序,完成复位周期 return presence; }这里两个关键参数。第一个是复位脉冲宽度,手册要求480us,实际取480us到960us都可用,时间太短从机可能没反应,太长也不会出错。第二个是释放总线后必须等待60us再采样,因为DS18B20在收到复位脉冲后的15到60us内会拉低总线,采样太早读不到应答信号。项目调试时用逻辑分析仪抓过波形,主机释放总线后大约20us从机就开始拉低,60us的等待窗口完全够用。
温度转换的主流程放在ReadTemp函数里:
unsigned int DS18B20_ReadTemp(void) { unsigned char low, high; unsigned int temp; EA = 0; // 关中断,保证读取过程时序不被干扰 DS18B20_Reset(); // 复位总线 DS18B20_WriteByte(0xCC); // 跳过ROM匹配,直接寻址所有器件 DS18B20_WriteByte(0x44); // 启动温度转换,12位分辨率约耗时750ms delay_ms(750); // 等待转换完成 DS18B20_Reset(); // 再次复位,准备读操作 DS18B20_WriteByte(0xCC); // 跳过ROM DS18B20_WriteByte(0xBE); // 读暂存器,从字节0开始读9个字节 low = DS18B20_ReadByte(); // 温度低字节 high = DS18B20_ReadByte(); // 温度高字节 DS18B20_Reset(); // 结束读操作,释放总线 EA = 1; // 恢复中断 temp = ((unsigned int)high << 8) | low; return temp; // 原始值,实际温度=原始值*0.0625 }0xCC跳过ROM命令是单总线协议里最常用的指令,意思是忽略64位ROM序列号,直接对总线上唯一的一个DS18B20操作。如果总线上挂了多个DS18B20就不能用这条命令,需要先读ROM获取序列号再逐条寻址。这个场景因为只有一个传感器,跳过ROM就是标准做法,也是工程里最常见的用法。
读出来的原始值乘以0.0625才是实际温度。例如原始值是0x0191即401,对应25.0625℃。高字节的高5位是符号扩展位,0表示正温度,1表示负温度。如果做冰箱冷库应用,高字节等于0xFF开头按二进制补码处理就得了,常温场景可以不处理负温度。
3.2 NRF905初始化与SPI寄存器配置:433MHz频段怎么设
NRF905的配置通过SPI接口写入芯片内部寄存器组完成。工程里把配置数据放在一个10字节的常量数组里,初始化时一次性写入:
// NRF905配置数组,共10字节 // byte5=0x50表示433MHz频段,发射功率约+10dBm // byte6=0x0C表示接收地址宽度4字节,CRC校验8位 // byte7~10为接收地址 unsigned char code RF_CONFIG[10] = { 0x00, // byte0 保留 0x00, // byte1 保留 0x00, // byte2 保留 0x00, // byte3 保留 0x00, // byte4 保留 0x50, // byte5 频段配置 0x0C, // byte6 地址宽度与CRC配置 0xE7, 0xE7, 0xE7, 0xE7 // byte7~10 接收端地址 };写配置寄存器的代码:
void NRF905_Init(void) { unsigned char i; NRF905_CE = 0; // CE拉低,进入配置模式 NRF905_CSN = 0; // SPI片选拉低 NRF905_SPI_RW(0x00); // 写配置寄存器命令,0x00 for(i = 0; i < 10; i++) { NRF905_SPI_RW(RF_CONFIG[i]); // 逐个写入配置字节 } NRF905_CSN = 1; // 释放片选 NRF905_CE = 1; // CE拉高,进入工作模式 }SPI_RW函数用软件模拟SPI实现,这是老式51工程师最常用的手法:
unsigned char NRF905_SPI_RW(unsigned char dat) { unsigned char i, temp = 0; for(i = 0; i < 8; i++) { NRF905_SCK = 0; // 时钟拉低 if(dat & 0x80) NRF905_MOSI = 1; // 数据最高位先输出 else NRF905_MOSI = 0; dat <<= 1; // 准备下一个位 NRF905_SCK = 1; // 时钟拉高,从机在上升沿采样 temp <<= 1; if(NRF905_MISO) temp |= 1; // 同时读回MISO数据 } return temp; }循环里每次处理一个bit,MOSI在SCK上升沿之前准备好,MISO在SCK上升沿之后采样,SPI的Mode 0时序。为什么不用硬件SPI?因为51单片机的硬件SPI不是标配,至少老型号的STC89C52没有,软件模拟反而通用,管脚换到任意IO口都能跑。
两个配置参数说明一下。byte5=0x50,从二进制看是0101 0000,其中频段选择位和频道号组合起来是433MHz ISM频段的典型配置,发射功率约+10dBm。如果你的模块是868MHz版本,这一字节要换成对应值。byte6=0x0C,低两位规定了地址宽度,我写的是4字节地址模式,高4位里bit2置1代表8位CRC。这里所有值必须和接收端完全一致,是NRF905联调里最常见的翻车点。
3.3 发送主循环:组帧格式、通道地址与发射时序
发送端main.c的主循环做三件事:读温度、组帧、触发发射。工程里用了一个4字节帧格式:前面两个字节是温度原始值的高低字节,第三个字节是数据包类型标识,第四个字节预留。这样设计是为了后续扩展时有明确的位置放节点号、电池电压之类的信息。
void NRF905_SendPacket(unsigned char *buf, unsigned char len) { unsigned char i; NRF905_CE = 0; // 关闭射频,进入配置状态 NRF905_TX_EN = 1; // 设置发送模式 NRF905_CSN = 0; NRF905_SPI_RW(0x22); // WTA:写发送地址命令 for(i = 0; i < 4; i++) { NRF905_SPI_RW(0xE7); // 写入4字节地址,与接收端配置一致 } NRF905_SPI_RW(0x20); // WTP:写发送数据命令 for(i = 0; i < len; i++) { NRF905_SPI_RW(buf[i]); // 写入温度数据帧 } NRF905_CSN = 1; NRF905_CE = 1; // CE拉高启动发送 delay_us(20); // 等待RF稳定 NRF905_CE = 0; // CE拉低完成一次发射 }主循环:
void main(void) { unsigned char tx_buf[4]; unsigned int temp; delay_ms(200); // 上电等待系统稳定 NRF905_Init(); // 初始化射频,写入配置寄存器 tx_buf[2] = 0x01; // 0x01表示数据包类型为温度数据 tx_buf[3] = 0x00; // 预留字节,后续可扩展 while(1) { temp = DS18B20_ReadTemp(); // 读取温度原始值 tx_buf[0] = temp >> 8; // 温度高字节 tx_buf[1] = temp & 0xFF; // 温度低字节 NRF905_SendPacket(tx_buf, 4); // 发送4字节负载 delay_ms(2000); // 2秒采集一次,DS18B20转换需750ms } }这里帧格式和发射时序的匹配值得注意:地址0xE7E7E7E7是收发双方约定的“通信暗号”,接收端暂存器里存的接收地址必须和发送端写进去的发送地址一致,否则数据包在接收端就被硬件过滤掉。周期2秒是为了给DS18B20留出750ms的转换时间,同时让接收端屏幕上的温度刷新速度不至于太快而闪烁。如果你要更快的数据刷新率,可以考虑换用9位分辨率模式,转换时间只要90ms左右,代价是精度从0.0625℃降到0.5℃。
4. 接收端代码解析:NRF905接收、LCD1602显示与主循环
4.1 NRF905接收模式的配置与数据读取
接收端的NRF905配置和发送端几乎完全相同。这里要说一个常被忽略的点:两个板子的RF_CONFIG数组必须一模一样,地址、CRC配置、频段任何一个字节不一致,接收端即使收到射频信号也过不了地址匹配,DR永远不拉高,数据就是不出来。我最初调这个项目时在这个问题上卡了将近一小时,最后用逻辑分析仪对比两边的配置表,才发现接收端byte6被我改成了0x10。
接收数据的核心逻辑比发送简单:
void NRF905_ReceiveData(unsigned char *rx_buf) { unsigned char i; if(NRF905_DR == 0) { // DR为低表示没有数据到来 return; } NRF905_CE = 0; // 关闭射频,准备读数据 NRF905_CSN = 0; NRF905_SPI_RW(0x24); // RRP:读接收负载命令 for(i = 0; i < 4; i++) { rx_buf[i] = NRF905_SPI_RW(0x00); // 读出4字节温度帧 } NRF905_CSN = 1; NRF905_CE = 1; // 重新使能接收,等待下一个数据包 }接收模式下,NRF905持续监听空中射频信号,自动完成地址匹配、解调、CRC校验,一旦数据有效就把DR引脚拉高。51单片机的主循环只需要不断轮询DR,读到高电平就从SPI里取数据。这个过程没有中断参与,逻辑上非常直观,也适合新手理解射频链路的动作顺序。
这里有个细节必须提:读数据前要把CE拉低,读完再拉高。如果不拉低CE直接读,NRF905内部的接收状态机还在工作,读出来的数据可能不完整。工程最初实现时省掉了这两行,结果偶尔收到一次正常数据,大部分时间读出来的是某个字节重复的乱码,翻车翻得很完整。
4.2 LCD1602显示驱动:初始化序列与数值格式化
接收端把温度显示在1602液晶上。1602本身是HD44780兼容控制器,初始化需要按固定时序发送几个命令。工程里的lcd1602.c提供一个标准初始化流程:
void LCD1602_Init(void) { delay_ms(15); // 上电等待15ms LCD1602_WriteCmd(0x38); // 功能设置:8位接口、双行显示、5x7点阵 delay_ms(5); LCD1602_WriteCmd(0x38); // 重复发送,确保时序稳定 delay_ms(5); LCD1602_WriteCmd(0x08); // 关闭显示 LCD1602_WriteCmd(0x01); // 清屏 LCD1602_WriteCmd(0x06); // 光标右移,显示不位移 LCD1602_WriteCmd(0x0C); // 开显示,隐藏光标 }单字节命令和数据写入本质上是对RS、RW、E三个控制引脚以及数据总线的时序操作:
void LCD1602_WriteCmd(unsigned char cmd) { LCD1602_RS = 0; // RS=0,表示写入指令 LCD1602_RW = 0; // RW=0,写入模式 LCD1602_DB = cmd; // 数据总线输出指令 LCD1602_E = 1; // E拉高,准备写入 delay_us(50); // 保持高电平50us,让控制器锁存数据 LCD1602_E = 0; // E拉低,锁存完成 } void LCD1602_WriteData(unsigned char dat) { LCD1602_RS = 1; // RS=1,表示写入数据 LCD1602_RW = 0; LCD1602_DB = dat; LCD1602_E = 1; delay_us(50); LCD1602_E = 0; }LCD1602_DB接在P0口,需要注意P0口内部没有上拉电阻,如果外部不加4.7k到10k的上拉排阻,8位数据总线会输出高阻态,屏幕要么白屏要么显示乱码。这个坑新手踩得非常多,多数人以为是初始化命令的顺序错了,实际上就是少了上拉电阻。
显示温度数值时,接收端把原始值还原并格式化成适合显示的数字。工程里没有用sprintf,因为51单片机的Keil C51里sprintf会引入重库函数,占用大量代码空间,运行速度也慢。所以我把整数部分和小数部分拆开处理:
void LCD1602_ShowTemperature(unsigned int raw_temp) { unsigned char t_int, t_dec; unsigned char str[7]; t_int = raw_temp >> 4; // 原始值除以16得到整数部分 t_dec = (raw_temp & 0x0F) * 10; // 小数部分,乘10取一位小数 t_dec = t_dec >> 4; // 除以16,得到0~9之间的一位小数 str[0] = t_int / 10 + '0'; str[1] = t_int % 10 + '0'; str[2] = '.'; str[3] = t_dec + '0'; str[4] = 0xDF; // 1602自定义度符号 str[5] = 'C'; str[6] = '\0'; LCD1602_ShowString(0, 1, "Temp:"); LCD1602_ShowString(6, 1, str); LCD1602_ShowString(0, 0, "RX_Mode"); }整数部分计算方式:原始值乘以0.0625等于原始值除以16,所以t_int = raw_temp >> 4。小数部分是低4位的量化值,每个单位0.0625℃,乘以10再除以16得到0到9之间的一位小数。这方法避免了浮点运算,在8位单片机上跑起来很轻,是个实用的小技巧。0xDF这个字节在1602的字符表里是一个度符号,兼容性很好,你手头屏幕如果显示别的字符,可以直接换成一个普通的'°'字符。
4.3 接收端主循环:轮询、状态指示与异常处理
接收端主循环结构比发送端多了一层超时判断:
void main(void) { unsigned char rx_buf[4]; unsigned char timeout_cnt = 0; delay_ms(200); // 上电稳定 NRF905_Init(); // 和发送端相同,配置成433MHz LCD1602_Init(); // 初始化液晶 LCD1602_ShowString(0, 0, "NRF905 Receiver"); NRF905_TX_EN = 0; // 设置为接收模式 NRF905_CE = 1; // 打开接收 while(1) { if(NRF905_DR == 1) { // 有数据到达 NRF905_ReceiveData(rx_buf); LCD1602_ShowTemperature( ((unsigned int)rx_buf[0] << 8) | rx_buf[1] ); timeout_cnt = 0; // 收到数据,清零连续超时计数 } else { if(++timeout_cnt > 10) { // 大约10秒没有收到数据 LCD1602_ShowString(0, 1, "No Signal!"); timeout_cnt = 0; } } delay_ms(100); // 轮询间隔100ms } }轮询间隔选100ms是个合适的折中。DS18B20的采样周期是2秒一次,接收端用100ms轮询DR引脚,不会漏掉任何一帧,也不会因为频繁查询占用太多CPU时间。如果把轮询间隔拉大到2秒以上,就可能错过一帧数据不上屏,显示刷新看起来就会“卡一下”。
“No Signal”这个提示帮了大忙。调试接收端的时候,我一度把NRF905_CE拉低之后忘了恢复,导致接收一直关闭,屏幕一直显示无信号,这个超时提示直接指出链路问题,省了自己对着逻辑分析仪发呆的半天时间。这种“超时刷新”的思路放在任何无线接收端都适用,比单纯显示死数据要健康得多。
5. 避坑指南:无线温度采集跑不通的5个常见踩坑现场
5.1 发送端没反应,SPI配置写入失败
现象:发送端程序跑起来后,接收端完全收不到数据,用示波器看NRF905的DR引脚始终低电平。
原因:NRF905的配置寄存器根本没写进去。最常见的是初始化函数里CE和CSN的顺序写反了。进入配置模式要求CE为低、CSN拉低后才能发SPI命令,如果CSN没有拉低就直接发数据,命令字节根本进不了芯片。另一个常见原因是SPI的时钟极性不对,NRF905要求的时序和普通SPI从机不完全一样。
解决:在初始化函数开头加上NRF905_CE = 0和NRF905_CSN = 1的默认状态,确保SPI总线空闲时片选拉高。再用逻辑分析仪抓SCK和MOSI波形,确认命令字节的顺序和位序正确。调这类项目最好备一个逻辑分析仪,二三十块钱的8通道版本就够用,不用搬示波器那么重的设备。
5.2 通信距离只有三米,天线没接对
现象:发送端和接收端在同一张桌面上通信正常,拉开到三米以上就丢包,隔一堵墙直接失联。
原因:NRF905模块上的天线没接对。大多数NRF905模块留的是天线座或焊盘,需要接一根1/4波长的导线做天线。433MHz的1/4波长约17.2厘米,868MHz约8.6厘米。不少人只焊了一根两三厘米的短线,辐射效率极低,距离就停留在三米。无线通信有时候很玄学,天线差一厘米效果都能差出一倍。
解决:按模块标注的频段选天线长度,433MHz模块剪一根17厘米左右的裸铜线焊在天线焊盘上。天线周围不要在板子背面铺铜,要尽量让天线下方没有大面积地平面。如果还不行,换一根433MHz螺旋天线,网上几块钱一根,比裸铜线稳定得多。
5.3 温度读取乱码,DS18B20时序被中断干扰
现象:DS18B20读出来的温度偶尔正常25℃,偶尔飙到70多度或者直接读到0xFF,而且是随机出现。
原因:DS18B20单总线时序对时间要求严苛,读写时隙是us级别的操作,误差超过几us就可能误判。51单片机的定时器中断、串口中断如果正好在温度读取过程中触发,中断服务程序执行期间总线电平被保持,DS18B20就会认为主机在发起一个错误的读写时序。
解决:读取DS18B20之前先关总中断EA = 0,读完再恢复。工程里ReadTemp函数开头和结尾就是这两行。但要提醒自己,关闭中断的时间要算好,整个读时序大概几毫秒,如果系统里还有UART在收发数据,关中断太久会丢串口字节,这时候可以考虑只关定时器中断或者用临界区保护。
5.4 收包偶尔丢一帧,帧格式缺少包序号和校验
现象:接收端屏幕温度显示基本正常,但偶尔一帧数据就变成0或乱值,尤其在通信距离边远的时候。
原因:无线链路本身就不可靠。NRF905硬件CRC能保证数据没被改错,但区分不了这是新数据还是重复的旧数据,也不能告诉接收端哪一帧丢了。射频干扰、天线抖动、供电电压波动都可能导致丢帧。发送间隔2秒,接收端不知道中间空了一帧,就继续显示上一帧,看起来像“卡了”。
解决:在数据帧里加一个自增的包序号,接收端判断序号是否连续,不连续说明丢帧,可以提示“信号不稳定”。再给数据帧加上简单的累加和校验,校验失败就不刷新显示。这个改动只有十几行代码,却能让接收端从“哑巴收数据”变成“有判断力地收数据”。
5.5 接收端一直No Signal,收发配置不一致
现象:发送端明明在正常发,接收端却一直显示无信号。
原因:这一类问题基本都是收发两端配置不一致。最常见三种:一是两边频段配置值不同,比如发送端用0x50(433MHz),接收端用了0x58(868MHz频段);二是两边的地址字节不一致,NRF905带地址匹配功能,接收端的地址和发送端写入的发送地址对不上,数据包在接收端被硬件过滤掉;三是CRC格式设置不一致,CRC长度不同或者一边开一边关,都过不了校验。
解决:把RF_CONFIG数组做成一个公共头文件,发送端和接收端都包含同一份配置常量,只维护一处,天然不会出现两边不一致的问题。这个工程我后来在做二次开发版本时就把配置表抽了出来,放到common.h里,之后再也没在这类问题上耗过时间。
6. 二次开发思路:把单点测温改造成多节点无线传感网络
拿到工程跑通之后,大概率想的第一个问题是“这玩意儿怎么变成我自己的东西”。最直接的方向是把单点测温升级成多点组网——多个发送端往一个接收端发数据,接收端循环显示不同位置的温度。
改造的核心是把帧格式升级一下,把原来4字节的简版数据帧改成带帧头、节点号、校验的完整协议:
typedef struct { unsigned char head[2]; // 帧头 0xAA 0x55,用于接收端找帧边界 unsigned char node_id; // 节点编号,多个发送端各分配一个 unsigned char type; // 数据类型 0x01=温度 0x02=湿度 unsigned char high; // 数据高字节 unsigned char low; // 数据低字节 unsigned char checksum; // 累加和校验 } RF_FRAME;接收端收到数据后先校验帧头和累加和,再根据node_id判断来自哪个节点,选择LCD上的对应位置刷新。发送端那边的改动更小,只是把tx_buf[2]改成自己的节点号,这样接收端就能分辨多个探头。
另一个值得做的方向是把接收端数据通过串口转发给上位机。51单片机本身带UART,从NRF905读到的数据帧不做解析直接丢给串口,PC端用一个串口调试工具就能画出温度曲线。我一般是把接收端当作一个“无线网关”来用,NRF905收上来,串口发出去,上位机负责存储和可视化。
如果要做双向控制,比如远程开关一个加热器,就把接收端再增加发送功能,发送端增加接收功能,两个NRF905本来就是全双工射频芯片,代码层面只要封装一对收发函数就行。每次改完硬件,我习惯先做一次“回环测试”:把发送端和接收端放在同一张桌子上,用一根杜邦线把NRF905的DR引脚引到一个LED上,看数据到达时灯闪不闪。这段小验证能挡住八成以上的低级接线问题。从那以后我每次跑无线模块调试,都强制自己先过一遍这个流程,再去看代码。希望帮到你。
本文还有配套的精品资源,点击获取