ARTICLE DETAIL

资讯详情

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

51单片机无线多路温度采集系统设计:DS18B20与nRF24L01实现

51单片机无线多路温度采集系统设计:DS18B20与nRF24L01实现 简介这是一份51单片机无线多路温度采集系统设计的完整工程资料面向嵌入式初学者、电子设计爱好者及课程设计人员可用于工业自动化、环境监控等多点测温场景主要解决多路温度实时采集、无线传输与主机显示等问题。压缩包共22个文件包含10个C源文件、10个H头文件及2个SCH原理图代码与硬件设计对应便于理解通信控制机与采集节点的整体架构。资源仅36KB体积小巧重点在于代码逻辑与原理图参考覆盖温度传感器驱动、无线通信协议、多路采集调度等核心代码。已有365人浏览学习适合需要快速上手51单片机多路采集与无线通信开发的读者。从中可获取通信控制机与采集节点的完整程序框架、传感器接口驱动及主机与节点原理图并参考其数据处理与显示逻辑为实际项目或课程设计提供直接借鉴。1. 51 单片机无线多路温度采集拆开这套设计后重点不是读温度而是把多路数据对上号用 51 单片机做无线温度采集真正卡人的往往不是 DS18B20 读不出来而是多路数据到了接收端之后分不清谁是谁。很多人在 Proteus 里仿真单点测温很顺利一到真实无线模块就开始丢包、串数甚至把 8 个节点的温度全部显示成同一个值。这套设计把采集节点代码、通信控制机代码和节点.Sch、主机.Sch 放在同一个压缩包里正好把无线传感器网络的“终端 网关 上位机”三段关系压缩到了两个工程里。通信控制机是接收端也是调度端采集节点是终端负责把温度打包成无线帧发出去。51 单片机在这里承担本地控制真正值得学的是单总线时序、无线收发参数还有轮询调度。适合正在做 51 单片机课程设计、基于 51 单片机的项目以及准备从裸板点灯往无线组网方向迈一步的人。这套思路换到 STM32、ESP32 上依然复用只是换一套寄存器而已。2. 系统拆解与器件选型通信控制机、采集节点和无线链路的分工拿到压缩包后第一件事不是打开 Keil 工程而是先分清两个工程分别烧给哪块板子。从目录命名上已经能看出分工code 下面有通信控制机代码和采集节点代码sch 下面有节点.Sch 和主机.Sch。这里的通信控制机就是主机放在接收端采集节点放在现场测温位置。两者的关系不是对等的主机主动发起查询节点被动应答。工程目录角色主要工作code/采集节点代码终端节点读取 DS18B20打包温度帧通过无线发送code/通信控制机代码接收/控制端无线接收轮询节点LCD 显示串口上报sch/节点.Sch节点原理图单片机最小系统、DS18B20、无线模块接口sch/主机.Sch主机原理图单片机、无线模块、显示电路、串口电路这种一问一答的轮询模式比所有节点主动上报要稳得多。主动上报的问题是 2.4G 同频段多节点同时发送会产生碰撞轻则 CRC 错包重则主机长时间收不到有效数据。而主机每次只查询一个节点节点收到查询帧后返回本机温度从协议层面就规避了冲突问题。后期如果要加节点只需要在主机端把查询循环的数组长度改大再给新节点分配一个不重复的 ID。2.1 为什么温度传感器选 DS18B20 而不是 DHT11这套系统标的是温度采集传感器选型应该优先考虑接口简单和可重复性。DS18B20 是数字单总线器件只占一个 I/O 口支持多片并联并且每片芯片内置 64 位唯一序列号。这个序列号在实际工程里可以用来做多路地址识别即使同一个采集节点上挂多片传感器也能通过 ROM 匹配区分每一路。DHT11 虽然便宜且能读湿度但温度精度只有 ±2℃采样周期最低 1 秒做环境监控还够做稍微精细点的曲线记录就差一些。DS18B20 在 12 位分辨率下能把温度量化到 0.0625℃对机房、仓库、温室这类场景足够用。51 单片机用普通 I/O 模拟单总线时序只依赖定时器和几条延时指令并不会给主频造成压力。唯一要注意的是单总线对时序敏感初学者经常把 60us 读时隙和 15us 写时隙搞混后面在代码章节会专门展开。2.2 无线模块2.4G 短距通信里的低成本方案无线部分最常用的组合不是蓝牙也不是 Zigbee而是 nRF24L01。它便宜、SPI 接口、功耗低51 单片机用软件模拟 SPI 就能跑起来。蓝牙需要配对流程从机要广播主机要扫描51 上面写起来比较绕Zigbee 虽然组网能力强但协议栈太重对课程设计来说性价比不高。nRF24L01 更像一个无线管道只要把数据帧写好发送端和接收端频率、地址一致就能稳定收发。需要注意nRF24L01 模块引脚电平是 3.3V而普通 51 单片机 I/O 是 5V。很多现成模块自带电平转换但如果是裸模块CE、CSN、MOSI、SCK 这几个由单片机输出的引脚需要串联电阻或加电平转换否则长期运行会烧模块。无线通信要对齐的参数包括射频频率、发射地址、接收地址、数据速率和 SPI 地址宽度收发两端任何一个不匹配都会表现为收不到数据。2.3 多路寻址轮询比主动上报更适合 51 做主机这套系统的“多路”既指多个采集节点也指一个节点上可能挂载多个温度探头。主机端维护一个节点表循环发送查询帧采集节点收到查询后上报本机编号和温度值。无线帧格式是两个工程共用的协议定义方式如下/* 无线数据帧节点与主机按同一份定义解析 */ typedef struct { unsigned char head; /* 0xA5 帧头 */ unsigned char node_id; /* 节点地址 1~2550 保留 */ unsigned char cmd; /* 0x01 查询, 0x02 应答 */ unsigned char temp_h; /* 温度整数部分 */ unsigned char temp_l; /* 温度小数部分 */ unsigned char crc; /* 从 frame 头到 temp_l 的校验 */ } sys_packet_t;把 node_id 放在 cmd 前面主机收到帧后可以先过滤帧头再判断节点号不用等整帧收完就能丢弃无关广播包。CRC 放在最后接收端可以边收边算收完后直接和最后一个字节比较。这个结构体在实际发送时会转换成一个 unsigned char 数组避免出现字节对齐问题。如果不想用结构体也可以直接定义成unsigned char tx_buf[6]可读性会差一些但更容易让新手理解数据在无线链路上真正传输的样子。3. 从节点.Sch 和主机.Sch 看硬件引脚映射、电平与电源原理图文件打开后不要先看密密麻麻的连线先看芯片型号和网络标号。节点板上通常是 51 单片机加 DS18B20 加无线模块排针主机板上多出 LCD 和串口。每个 .Sch 里真正需要确认的是 I/O 口的分配因为代码里的 sbit 宏定义必须和原理图网络标号一一对应。如果名字不一致要么改代码要么在 PCB 上飞线代价完全不同。3.1 采集节点电路DS18B20 接口和最小系统采集节点最小系统由晶振、复位电路和电源滤波组成DS18B20 的信号线 DQ 通常接在 P3.7 或者其他带强上拉的 I/O 口。单总线要求 DQ 线上必须有 4.7kΩ 上拉电阻否则总线无法回到高电平传感器会频繁超时。很多人在面包板上省掉这个电阻结果读回来的温度跳变非常严重。DS18B20 供电可以用 5V也可以用 3.3V但上拉电阻必须接在同一个电源域下。DS18B20 引脚接线位置说明VCC5V模块供电兼容 3.3VGNDGND与单片机共地DQP3.7经过 4.7kΩ 上拉到 VCC如果节点板上要多接几个 DS18B20就把所有 DQ 并联到同一个 I/O 口靠 ROM 码区分。实际项目里不要在一个 I/O 口上并联超过 4 个传感器因为单总线寄生电容会拉低时序边沿超过 5 米线长时尤其明显。更稳妥的做法是每路单独占用一个 I/O软件逻辑简单排查故障也方便。3.2 无线模块接口真正需要接的只有 7 根线无线模块如果采用 nRF24L0110 个引脚里有一半可以按固定对应关系接到 51 单片机上。IRQ 中断引脚在很多工程里是不接的主机通过读 STATUS 寄存器判断是否收到数据这样能少接一根线同时降低中断程序对 51 有限资源的占用。这里给出常见的引脚映射等你打开节点.Sch 时可以逐项对照。nRF24L01 引脚接 51作用VCC3.3V模块电源禁止直接接 5VGNDGND必须共地CSNP1.3SPI 片选低电平有效CEP1.2发送/接收模式切换SCKP1.1SPI 时钟MOSIP1.0单片机输出到模块MISOP1.4模块输出到单片机接线表里的引脚宏定义是这段代码的核心原理图网络标号变了这里就要同步改。5V 单片机的 I/O 高电平是 5VnRF24L01 的 I/O 耐压多数只有 3.6V稳妥做法是在 CE、CSN、MOSI、SCK 四根线上各串联一个 1kΩ 限流电阻或者直接把系统供电改成 3.3V 的 51 型号。/* 根据 sch/节点.Sch 里的网络标号修改 */ sbit DQ P3^7; /* DS18B20 数据线 */ sbit RF_CSN P1^3; /* 无线模块 SPI 片选 */ sbit RF_CE P1^2; /* 无线模块模式切换 */ sbit RF_SCK P1^1; /* SPI 时钟 */ sbit RF_MOSI P1^0; /* SPI 输出 */ sbit RF_MISO P1^4; /* SPI 输入 */这段宏定义看起来简单但很多下载下来的源码里引脚定义和实际原理图不符。拿到资源后第一件事就是用 Find 功能在 main.c 里搜索sbit把每个引脚名和原理图上的网络标号对一遍。3.3 主机板串口、LCD 和电平转换要匹配主机板上的核心除了无线模块还有串口输出和显示。串口芯片如果用的是 CH340那么和单片机之间是 TTL 电平可以直接连接如果是 MAX232则多出一组 ±12V 电平连接电脑时要走 DB9 接口。通信控制机代码里的 UART 初始化必须和晶振匹配最稳妥的是使用 11.0592MHz 晶振因为 9600 波特率在这个频率下误差最小。用 12MHz 晶振跑 9600积累到几个字节后就会出现乱码。3.4 在 Proteus 里仿真时要拆开节点验证很多人在 Proteus 里仿真 51 单片机 DS18B20 时会发现读不到温度原因是虚拟 DS18B20 对单总线时序的容差比真实芯片更严格一个延时时间偏一点就会失败。更麻烦的是 Proteus 没有成熟的 nRF24L01 模型硬仿真只能看到串口数据。我一般建议仿真时把采集节点的发送函数临时替换成串口发送这样能独立验证 DS18B20 驱动和温度换算逻辑再回到真实硬件上调无线链路。仿真只是辅助不要指望它覆盖所有硬件问题。4. Keil 工程组织与关键代码单总线驱动、无线收发和主机轮询这部分是整套代码的核心。通信控制机代码和采集节点代码虽然目录不同但很多文件是成对出现的尤其是 nRF24L01 底层发送端和接收端的驱动虽然在寄存器配置上有差异却必须保持时钟、地址、通道一致。下面从解压开始把整个代码链路串起来。4.1 解压后先看工程结构而不是直接编译把压缩包解压后先用命令确认目录层级然后再打开 Keil 工程。很多初学者双击工程文件后报一堆 missing header原因就是头文件路径没配置。unzip 51单片机无线多路温度采集系统设计.zip -d temp_system cd temp_system find . -maxdepth 3 -type f | sort命令执行后可以看到码目录下通常会有 main.c、delay.c、ds18b20.c、nrf24l01.c、lcd1602.c 等文件。如果用的是 Keil5打开工程后要在魔术棒里的 C/C 选项下把 Include Paths 指向对应源码目录。注意 Keil5 必须安装 C51 版本的编译器如果只装了 MDK工程文件能打开但编译按钮是灰色的。code/ ├── 通信控制机代码/ │ ├── main.c # 主机轮询和界面刷新 │ ├── nrf24l01.c # 无线接收模式驱动 │ ├── uart.c # 串口协议上报 │ └── lcd1602.c # 温度显示 └── 采集节点代码/ ├── main.c # 节点主循环 ├── ds18b20.c # 单总线温度驱动 ├── nrf24l01.c # 无线发送模式驱动 └── delay.c # 延时函数从这里就能看出一个工程边界DS18B20 驱动只出现在采集节点代码里通信控制机代码里没有反过来主机端的 LCD 和 UART 不会出现在节点工程里。如果下载的源码把两边的文件混在一起先按这个原则拆开再考虑编译。4.2 DS18B20 驱动复位、转换、读暂存器必须按顺序DS18B20 驱动代码看起来不长但三个步骤的顺序不能乱复位、启动温度转换、读暂存器。读取温度时如果跳过了 0x44 转换指令读到的就是上一次转换结果或者上电默认的 85℃。下面这段代码是采集节点工程里的标准读流程unsigned char ds18b20_read_temp(int *temp) { if (ds18b20_init()) return 1; /* 返回 1 表示总线无设备 */ ds18b20_write_byte(0xCC); /* 跳过 ROM因为只有一个传感器 */ ds18b20_write_byte(0x44); /* 启动温度转换 */ delay_ms(750); /* 12 位分辨率典型转换时间 750ms */ ds18b20_init(); /* 第二次复位准备读暂存器 */ ds18b20_write_byte(0xCC); ds18b20_write_byte(0xBE); /* 读暂存器从第 0 字节开始 */ *temp ds18b20_read_byte(); /* 低字节 */ *temp | (int)ds18b20_read_byte() 8; /* 高字节 */ return 0; }这段代码里最容易改动的是delay_ms(750)。12 位分辨率转换时间是 750ms如果现场节点多这个等待会拖慢整个轮询周期。常见的优化是写入配置寄存器把分辨率降到 9 位转换时间缩短到 93.75ms但温度量化步进会变成 0.5℃。另一个更实用的做法是让采集节点在空闲时不停循环启动转换收到主机查询帧时直接读暂存器这样主机端等待短节点端温度也一直是最新值。4.3 nRF24L01 发送CE 拉高前要检查配置寄存器无线发送的最小闭环代码通常是发送方把数据写入 TX FIFO然后拉高 CE 触发发送发送完成后 CE 拉低回到待机。nRF24L01 的 SPI 写负载指令是WR_TX_PLOAD收到 ACK 后会置 TX_DS 位。代码实现如下unsigned char rf_send_frame(unsigned char *buf, unsigned char len) { RF_CE 0; /* 待机模式 */ RF_CSN 0; /* 拉低片选进入 SPI 操作 */ spi_write_byte(WR_TX_PLOAD); /* 写发送负载指令 */ for (unsigned char i 0; i len; i) { spi_write_byte(buf[i]); /* 逐字节写入 */ } RF_CSN 1; /* 结束 SPI 操作 */ RF_CE 1; /* CE 拉高切换到发送模式 */ delay_us(150); /* 保持至少 10us常用 150us 更稳 */ RF_CE 0; /* 发送完成回待机 */ return (spi_read_byte(STATUS) TX_DS) ? 1 : 0; }返回 1 表示发送完成且收到对方自动应答返回 0 表示自动重发超时。如果自动重发光说明接收端地址和本端 TX_ADDR 不匹配或者接收端根本没开自动应答。参数上有个经验值数据速率不要调到 2Mbps2Mbps 虽然快但接收灵敏度变差1Mbps 是性价比最高的挡位。重发延时设 500us重发次数设 15 次单包长度控制在 610 字节内这套参数在室内环境通常能做到很低的丢包率。4.4 通信控制机端的轮询调度不要阻塞等待主机端主循环是一张循环查询表。每轮遍历所有节点 ID一个节点发完查询帧后主机只等待一小段时间等不到就标记离线然后立刻切换下一个节点。这种调度方式避免了某个节点掉线后整条链路卡死。void main(void) { unsigned char i; init_uart(); init_rf_rx(); /* nRF24L01 初始化成接收模式 */ while (1) { for (i 1; i NODE_COUNT; i) { send_query(i); /* 发出查询帧 */ if (wait_rf_packet(20)) { /* 最多等 20ms */ process_packet(i); /* 解析并保存该节点温度 */ } else { nodes[i].online 0; /* 标记离线 */ } } lcd_refresh(); /* 统一刷新显示 */ uart_report(); /* 把所有节点温度打包到串口 */ } }这里有个细节wait_rf_packet(20)内部不能使用 51 的delay_ms(20)傻等应该用定时器 1 做一个超时标志同时持续读 STATUS 寄存器。如果实在不想改可以把 20ms 拆成 20 次 1ms 的轮询每毫秒读一次IRQ引脚或 STATUS。这样主循环里还能插入按键扫描和显示刷新不会出现按一个键没反应的情况。主机端串口上报最好按行输出比如,node1,temp25.3这样 C# 写无线温度监测系统时按行解析就很轻松。4.5 把串口协议改成 Modbus 主站时的思路如果现场需要接组态屏或工控机主机端的串口协议可以改造成 Modbus 主站。51 单片机当作 Modbus 主站的常见做法是用功能码 03 读取保持在寄存器把每个节点的温度值放到一个寄存器里。CRC16 计算是半表查还是全表查都行但高低字节最容易写反很多工控屏解析乱码都是这个问题。这个改造不用动无线协议只要把 uart_report 里的纯文本输出改成 Modbus 帧即可采集节点代码完全不用改。5. 联调复盘读 85℃、距离短和数据乱先查这三个地方无线温度系统联调时遇到的现象往往很集中大部分问题可以从三个方向定位。第一个是 DS18B20 显示 85℃。这个现象不是传感器坏了而是温度寄存器还没有转换完成读到了上电默认值。DS18B20 上电后暂存器温度位默认是 0x0550换算过来就是 85℃。只要在写 0x44 之后等够 750ms或者轮询转换完成位再读暂存器数值就会更新。如果所有节点都显示 85几乎可以断定程序里没有执行完整的转换流程。第二个是无线模块近距离能通拉远几米就丢包。常见原因不是天线问题而是电源压降。nRF24L01 发射瞬间电流能达到几十毫安如果和 LCD 背光、继电器共用一根 5V 线电压跌落会直接让模块复位。主控板和无线模块最好分开供电无线模块用独立 3.3V 的 LDO 供电模块下方不要铺大片地铜皮天线附近也不要走平行电源线。还有一点容易被忽略就是模块排针接触不良插在面包板上和焊在转接板上完全是两种效果。第三个是上位机收到的数据和 LCD 显示对不上。出现这种情况时先用串口助手抓主机原始输出不要直接连 C# 上位机。以 9600 波特率8 数据位1 停止位无校验打开串口如果看到帧头不是 0xA5说明字节错位如果帧头对但 CRC 不对说明采集节点和主机的帧定义不一致。串口助手把数据恢复成分行文本后再用 C# 解析问题会清楚很多。string line sp.ReadLine(); if (line.StartsWith(,)) { string[] fields line.Split(,); int node int.Parse(fields[0].Split()[1]); double temp double.Parse(fields[1].Split()[1]); Console.WriteLine($节点{node}: {temp}℃); }这段解析逻辑的关键是主机按行输出每条数据以逗号分隔C# 端用 ReadLine 读取后逐字段拆分。实际项目里建议在节点号前加一个固定的 key比如node和temp这样即使以后增加湿度字段老版本上位机也能跳过未知字段。串口读数据时注意缓冲区要清空如果上一帧半包滞留会被误判成新一帧最简单的办法是串口收到换行符再开始处理否则要加一帧超时判断。本文还有配套的精品资源点击获取
返回列表