找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 11103|回复: 1
打印 上一主题 下一主题
收起左侧

DHT11+Nrf24L01模块的多点温湿度采集系统设计 附单片机源码与实物图

[复制链接]
跳转到指定楼层
楼主
基于DHT11传感器和Nrf24L01模块的多点温湿度采集系统

  • 设计背景
农业大棚种植具有大范围,温湿度要求高的特点。温度,湿度严重影响农作物的生长与产量,所以,对其的测量出其重要。基于这样的考虑,设计了该套多点温湿度采集无线传输系统。
  • 系统构成
该系统由89C52RC单片机、DHT11温湿度传感器、Nrf24L01无线传输模块构成。具有多点温湿度采集、汇总的特点(本实验采用两个点采集汇总一点完成)


  • 硬件设计
  • DHT11传感器简介
DHT11产品概述
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。因此该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。每个DHT11传感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式储存在OTP内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达20米以上,使其成为各类应用甚至最为苛刻的应用场合的最佳选则。产品为4 针单排引脚封装。连接方便,特殊封装形式可根据用户需求而提供。
1传感器性能说明
参数
条件
Min
Typ
Max
单位
湿度
分辨率

1
1
1
%RH

8

Bit
重复性


±1

%RH
精度
25℃

±4

%RH
0-50℃


±5
%RH
互换性
可完全互换
量程范围
0℃
30

90
%RH
25℃
20

90
%RH
50℃
20

80
%RH
响应时间
1/e(63%)25℃1m/s 空气
6
10
15
S
迟滞


±1

%RH
长期稳定性
典型值

±1

%RH/yr
温度
分辨率


1
1
1
8
8
8
Bit
重复性


±1

精度

±1

±2
量程范围

0

50
响应时间
1/e(63%)
6

30
S
(2)接口说明  
建议连接线长度短于20米时用5K上拉电阻,大于20米时根据实际情况使用合适的上拉电阻
(3)电源引脚
DHT11的供电电压为              3-5.5V。传感器上电后,要等待 1s 以越过不稳定状态在此期间无需发送任何指令。电源引脚(VDD,GND)之间可增加一个100nF 的电容,用以去耦滤波。
(4)串行接口  (单线双向)
DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,当前小数部分用于以后扩展,现读出为零.操作流程如下:
一次完整的数据传输为40bit,高位先出。
数据格式:8bit湿度整数数据+8bit湿度小数数据
+8bi温度整数数据+8bit温度小数数据
         +8bit校验和
数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据”所得结果的末8位。
用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集,用户可选择读取部分数据.从模式下,DHT11接收到开始信号触发一次温湿度采集,如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集.采集数据后转换到低速模式。
1.通讯过程如图1所示
图1
总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测到起始信号。DHT11接收到主机的开始信号后,等待主机开始信号结束,然后发送80us低电平响应信号.主机发送开始信号结束后,延时等待20-40us后, 读取DHT11的响应信号,主机发送开始信号后,可以切换到输入模式,或者输出高电平均可, 总线由上拉电阻拉高。
            
图2
总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉高80us,准备发送数据,每一bit数据都以50us低电平时隙开始,高电平的长短定了数据位是0还是1.格式见下面图示.如果读取响应信号为高电平,则DHT11没有响应,请检查线路是否连接正常.当最后一bit数据传送完毕后,DHT11拉低总线50us,随后总线由上拉电阻拉高进入空闲状态。
            
数字0信号表示方法如图4所示
                  图4
数字1信号表示方法.如图5所示
                                                        图5
(5)测量分辨率
测量分辨率分别为 8bit(温度)、8bit(湿度)。
6电气特性
VDD=5V,T = 25℃,除非特殊标注
参数
条件
min
typ
max
单位
供电
DC
3
5
5.5
V
供电电流

测量
0.5

2.5
mA
平均
0.2

1
mA
待机
100

150
uA
采样周期
1


注:采样周期间隔不得低于1秒钟。
7应用信息
7.1工作与贮存条件
超出建议的工作范围可能导致高达3%RH的临时性漂移信号。返回正常工作条后,传感器会缓慢地向校准状态恢复。要加速恢复进程/可参阅7.3小节的“恢复处理”。在非正常工作条件下长时间使用会加速产品的老化过程。
7.2暴露在化学物质中
电阻式湿度传感器的感应层会受到化学蒸汽的干扰,化学物质在感应层中的扩散可能导致测量值漂移和灵敏度下降。在一个纯净的环境中,污染物质会缓慢地释放出去。下文所述的恢复处理将加速实现这一过程。高浓度的化学污染会导致传感器感应层的彻底损坏。
7.3恢复处理
置于极限工作条件下或化学蒸汽中的传感器,通过如下处理程序,可使其恢复到校准时的状态。在50-60℃和< 10%RH的湿度条件下保持2 小时(烘干);随后在20-30℃和>70%RH的湿度条件下保持 5小时以上。
7.4温度影响
气体的相对湿度,在很大程度上依赖于温度。因此在测量湿度时,应尽可能保证湿度传感器在同一温度下工作。如果与释放热量的电子元件共用一个印刷线路板,在安装时应尽可能将DHT11远离电子元件,并安装在热源下方,同时保持外壳的良好通风。为降低热传导,DHT11与印刷电路板其它部分的铜镀层应尽可能最小,并在两者之间留出一道缝隙。
7.5光线
长时间暴露在太阳光下或强烈的紫外线辐射中,会使性能降低。
7.6配线注意事项
DATA信号线材质量会影响通讯距离和通讯质量,推荐使用高质量屏蔽线。
(8)封装信息
(9)DHT11引脚说明            
Pin
名称
注释
1
VDD
供电 3-5.5VDC
2
DATA
串行数据,单总线
3
NC
空脚,请悬空
4
GND
接地,电源负极
(10)焊接信息
手动焊接,在最高260℃的温度条件下接触时间须少于10秒。
11注意事项
(1)避免结露情况下使用。
(2)长期保存条件:温度10-40℃,湿度60%以下。
  • nRF24L01模块简介
(1)nRF24L01芯片简介
功能描述
真正的GFSK 单片式收发芯片
内置硬件链路层
增强型ShockBurstTM 功能
自动应答及自动重发功能
地址及CRC 检验功能
无线速率: 1 或2Mbps
SPI 接口速率:0~8Mbps
125 个可选工作频道
很短的频道切换时间,可用于跳频
与nRF 24XX 系列完全兼容
I/O 可接受5V 电平的输入
20 脚QFN 4×4mm 封装
极低成本晶振±60ppm
使用低成本电感和双面PCB 板            
低工作电压:1.9~3.6V
概述
nRF24L01 是一款工作在2.4~2.5GHz 世界通用ISM 频段的单片无线收发器芯片。无线收发器包括:频
率发生器、增强型SchockBurstTM 模式控制器、功率放大器、晶体振荡器、调制器、解调器。输出功率、
频道选择和协议的设置可以通过SPI 接口进行设置。
极低的电流消耗:当工作在发射模式下发射功率为-6dBm 时电流消耗为9mA,接收模式时为12.3mA。
掉电模式和待机模式下电流消耗更低
结构框图:
引脚及其功能:
电气特性:
条件:VDD=+3V,VSS=0V,TA=-40℃到+85℃
极限范围:
VDD…………………………….+3.6V
VSS………………………………0V
输入电压
VI………………………………-0.3V~5.25V
输出电压
VO……………………………. VSS~VDD
总功耗
PD(TA=85℃)……………………… 60mW
温度
工作温度……………………-40℃~+85℃
存储器温度…………………-40℃~+125℃
注意:强行超过一项或多项极限值使用将导致器件永久性损坏。
术语表:
功能描述:
工作模式
nRF24L01可以设置为以下几种主要模式
nRF24L01在不同模式下的引脚功能
待机模式:
待机模式I 在保证快速启动的同时减少系统平均消耗电流。在待机模式I 下,晶振正常工作。在待机
模式II 下部分时钟缓冲器处在工作模式。当发送端TX FIFO 寄存器为空并且CE 为高电平时进入待机模式
II。在待机模式期间,寄存器配置字内容保持不变。
掉电模式:
在掉电模式下,nRF24L01 各功能关闭,保持电流消耗最小。进入掉电模式后,nRF24L01 停止工作,
但寄存器内容保持不变。启动时间见表格13。掉电模式由寄存器中PWR_UP 位来控制
数据包处理方式:
nRF24L01 有如下几种数据包处理方式:
ShockBurstTM(与nRF2401,nRF24E1,nRF2402,nRF24E2 数据传输率为1Mbps 时相同)
增强型ShockBurstTM 模式
ShockBurstTM 模式:
ShockBurst 模式下nRF24L01 可以与成本较低的低速MCU 相连。高速信号处理是由芯片内部的射频协议处理的,nRF24L01 提供SPI接口,数据率取决于单片机本身接口速度。ShockBurst 模式通过允许与单片机低速通信而无线部分高速通信,减小了通信的平均消耗电流。
在ShockBurstTM 接收模式下,当接收到有效的地址和数据时IRQ 通知MCU,随后MCU 可将接收到的数据从RX FIFO 寄存器中读出。
在ShockBurstTM 发送模式下,nRF24L01 自动生成前导码及CRC 校验,参见表格12。数据发送完毕后IRQ 通知MCU。减少了MCU 的查询时间,也就意味着减少了MCU 的工作量同时减少了软件的开发
时间。nRF24L01 内部有三个不同的RX FIFO 寄存器(6 个通道共享此寄存器)和三个不同的TX FIFO 寄存器。在掉电模式下、待机模式下和数据传输的过程中MCU 可以随时访问FIFO 寄存器。这就允许SPI接口可以以低速进行数据传送,并且可以应用于MCU 硬件上没有SPI 接口的情况下。
增强型的ShockBurstTM 模式:
增强型ShockBurstTM 模式可以使得双向链接协议执行起来更为容易、有效。典型的双向链接为:发送方要求终端设备在接收到数据后有应答信号,以便于发送方检测有无数据丢失。一旦数据丢失,则通过重新发送功能将丢失的数据恢复。增强型的ShockBurstTM 模式可以同时控制应答及重发功能而无需增加MCU
工作量。
nRF24L01 在接收模式下可以接收6 路不同通道的数据,见图4。每一个数据通道使用不同的地址,但是共用相同的频道。也就是说6 个不同的nRF24L01 设置为发送模式后可以与同一个设置为接收模式的nRF24L01 进行通讯,而设置为接收模式的nRF24L01 可以对这6 个发射端进行识别。数据通道0 是唯一的一个可以配置为40 位自身地址的数据通道。1~5 数据通道都为8 位自身地址和32 位公用地址。所有的数据通道都可以设置为增强型ShockBurst 模式。nRF24L01在确认收到数据后记录地址,并以此地址为目标地址发送应答信号。在发送端,数据通道0被用做接收应答信号,因此,数据通道0 的接收地址要与发送端地址相等以确保接收到正确的应答信号。
见图5 选择地址举例。
nRF24L01 配置为增强型的ShockBurstTM 发送模式下时,只要MCU 有数据要发送,nRF24L01 就会启动ShockBurstTM 模式来发送数据。在发送完数据后nRF24L01 转到接收模式并等待终端的应答信号。如果没有收到应答信号, nRF24L01 将重发相同的数据包, 直到收到应答信号或重发次数超过SETUP_RETR_ARC 寄存器中设置的值为止,如果重发次数超过了设定值,则产生MAX_RT 中断。
只要收到确认信号,nRF24L01 就认为最后一包数据已经发送成功(接收方已经收到数据),把TX FIFO中的数据清除掉并产生TX_DS 中断(IRQ 引脚置高)。在增强型ShockBurstTM 模式下,nRF24L01 有如下的特征:
当工作在应答模式时,快速的空中传输及启动时间,极大的降低了电流消耗。
低成本。nRF24L01 集成了所有高速链路层操作,比如:重发丢失数据包和产生应答信号。无需单片机硬件上一定有SPI 口与其相连。SPI 接口可以利用单片机通用I/O 口进行模拟
由于空中传输时间很短,极大的降低了无线传输中的碰撞现象
由于链路层完全集成在芯片上,非常便于软硬件的开发。
增强型ShockBurstTM 发送模式:
1、 配置寄存器位PRIM_RX 为低
2、 当MCU 有数据要发送时,接收节点地址(TX_ADDR)和有效数据(TX_PLD)通过SPI 接口写入nRF24L01。发送数据的长度以字节计数从MCU 写入TX FIFO。当CSN 为低时数据被不断的写入。发送端发送完数据后,将通道0 设置为接收模式来接收应答信号,其接收地址(RX_ADDR_P0)与接收端地址(TX_ADDR)相同。例:在图5 中数据通道5 的发送端(TX5)及接收端(RX)地址设置如下:
TX5:TX_ADDR=0xB3B4B5B605
TX5:RX_ADDR_P0=0xB3B4B5B605
RX:RX_ADDR_P5=0xB3B4B5B605
3、 设置CE 为高,启动发射。CE 高电平持续时间最小为10 us。
4、 nRF24L01 ShockBurstTM 模式:
无线系统上电
启动内部16MHz 时钟
无线发送数据打包(见数据包描述)
高速发送数据(由MCU 设定为1Mbps 或2Mbps)
5、 如果启动了自动应答模式(自动重发计数器不等于0,ENAA_P0=1),无线芯片立即进入接收模式。如果在有效应答时间范围内收到应答信号,则认为数据成功发送到了接收端,此时状态寄存器的TX_DS 位置高并把数据从TX FIFO 中清除掉。如果在设定时间范围内没有接收到应答信号,则重新发送数据。如果自动重发计数器(ARC_CNT)溢出(超过了编程设定的值),则状态寄存器的MAX_RT 位置高。不清除TX FIFO 中的数据。当MAX_RT 或TX_DS 为高电平时IRQ 引脚产生中断。IRQ 中断通过写状态寄存器来复位(见中断章节)。如果重发次数在达到设定的最大重发次数时还没有收到应答信号的话,在MAX_RX 中断清除之前不会重发数据包。数据包丢失计数器(PLOS_CNT)在每次产生MAX_RT 中断后加一。也就是说:重发计数器ARC_CNT 计算重发数据包次数,PLOS_CNT 计算在达到最大允许重发次数时仍没有发送成功的数据包个数。
6、 如果CE 置低,则系统进入待机模式I。如果不设置CE 为低,则系统会发送TX FIFO 寄存器中下一包数据。如果TX FIFO 寄存器为空并且CE 为高则系统进入待机模式II.
发送端发送完数据后,将通道0 设置为接收模式来接收应答信号,其接收地址(RX_ADDR_P0)与接收端地址(TX_ADDR)相同。例:在图5 中数据通道5 的发送端(TX5)及接收端(RX)地址设置如下:
TX5:TX_ADDR=0xB3B4B5B605
TX5:RX_ADDR_P0=0xB3B4B5B605
RX:RX_ADDR_P5=0xB3B4B5B605
增强型ShockBurstTM接受模式:
1、ShockBurstTM接受模式是通过设置寄存器中PRIM_RX 位为高来选择的。准备接收数据的通道必须被使能(EN_RXADDR 寄存器),所有工作在增强型ShockBurstTM 模式下的数据通道的自动应答功能是由(EN_AA 寄存器)来使能的,有效数据宽度是由RX_PW_Px 寄存器来设置的。地址的建立过程见增强型ShockBurstTM 发送章节。
2、 接收模式由设置CE 为高来启动。
3、 130us 后nRF24L01 开始检测空中信息。
4、 接收到有效的数据包后(地址匹配、CRC 检验正确),数据存储在RX_FIFO 中,同时RX_DR 位置高,并产生中断。状态寄存器中RX_P_NO 位显示数据是由哪个通道接收到的。
5、 如果使能自动确认信号,则发送确认信号。
6、 MCU 设置CE 脚为低,进入待机模式I(低功耗模式)。
7、 MCU 将数据以合适的速率通过SPI 口将数据读出。
8、 芯片准备好进入发送模式、接收模式或掉电模式
两种数据双方向的通讯方式
如果想要数据在双方向上通讯,PRIM_RX 寄存器必须紧随芯片工作模式的变化而变化。处理器必须保证PTX 和PRX 端的同步性。在RX_FIFO和TX_FIFO 寄存器中可能同时存有数据。
自动应答(RX):
自动应答功能减少了外部MCU 的工作量,并且在鼠标/键盘等应用中也可以不要求硬件一定有SPI 接口,因此降低成本减少电流消耗。自动重应答功能可以通过SPI 口对不同的数据通道分别进行配置。
在自动应答模式使能的情况下,收到有效的数据包后,系统将进入发送模式并发送确认信号。发送完确认信号后,系统进入正常工作模式(工作模式由PRIM_RX 位和CE 引脚决定)。
自动重发功能(ART(TX)
自动重发功能是针对自动应答系统的发送方。SETUP_RETR 寄存器设置:启动重发数据的时间长度。在每次发送结束后系统都会进入接收模式并在设定的时间范围内等待应答信号。接收到应答信号后,系统转入正常发送模式。如果TX FIFO 中没有待发送的数据且CE 脚电平为低,则系统将进入待机模式I。如果没有收到确认信号,则系统返回到发送模式并重发数据直到收到确认信号或重发次数超过设定值(达到最大的重发次数)。有新的数据发送或PRIM_RX 寄存器配置改变时丢包计数器复位。
数据包识别和CRC 校验应用于增强型ShockBurstTM 模式下:
每一包数据都包括两位的PID(数据包识别)来识别接收的数据是新数据包还是重发的数据包。PID识别可以防止接收端同一数据包多次送入MCU。在发送方每从MCU 取得一包新数据后PID 值加一。PID和CRC 校验应用在接收方识别接收的数据是重发的数据包还是新数据包。如果在链接中有一些数据丢失
了,则PID 值与上一包数据的PID 值相同。如果一包数据拥有与上一包数据相同的PID 值,nRF24L01 将
对两包数据的CRC 值进行比较。如果CRC 值也相同的话就认为后面一包是前一包的重发数据包而被舍弃。
1:接收方:
接收方对新接收数据包的PID 值与上一包进行比较。如果PID 值不同,则认为接收的数据包是新
数据包。如果PID 值与上一包相同,则新接收的数据包有可能与前一包相同。接收方必须确认CRC
值是否相等,如果CRC 值与前一包数据的CRC 值相等,则认为是同一包数据并将其舍弃。
2:发送方:
每发送一包新的数据则发送方的PID 值加一。
和CRC 校验应用在接收方识别接收的数据是重发的数据包还是新数据包。如果在链接中有一些数据丢失
了,则PID 值与上一包数据的PID 值相同。如果一包数据拥有与上一包数据相同的PID 值,nRF24L01 将
对两包数据的CRC 值进行比较。如果CRC 值也相同的话就认为后面一包是前一包的重发数据包而被舍弃。
CRC 校验的长度是通过SPI 接口进行配置的。一定要注意CRC 计算范围包括整个数据包:地址、PID和有效数据等。若CRC 校验错误则不会接收数据包,这一点是接收数据包的附加要求,在上图没有说明。
载波检测—CD
当接收端检测到射频范围内的信号时将CD 置高,否则CD 为低。内部的CD 信号在写入寄存器之前是经过滤波的,内部CD 高电平状态至少保持128us 以上。
在增强型ShockBurstTM模式中只有当发送模块没有成功发送数据时,推荐使用CD检测功能。如果发送端PLOS_CNT显示数据包丢失率太高时,可将其设置位接收模式检测CD值,如果CD为高(说明通道出现了拥挤现象),需要更改通信频道;如果CD为低电平状态(距离超出通信范围),可保持原有通信频道,但需作其它调整。
数据通道:
nRF24L01 配置为接收模式时可以接收6 路不同地址相同频率的数据。每个数据通道拥有自己的地址并且可以通过寄存器来进行分别配置。
数据通道是通过寄存器EN_RXADDR 来设置的,默认状态下只有数据通道0 和数据通道1 是开启状态的。
每一个数据通道的地址是通过寄存器RX_ADDR_Px 来配置的。通常情况下不允许不同的数据通道设置完全相同的地址。
数据通道0 有40 位可配置地址。数据通道1~5 的地址为:32 位共用地址+各自的地址(最低字节)。图7 所示的是数据通道1~5 的地址设置方法举例。所有数据通道可以设置为多达40 位,但是1~5 数据通道的最低位必须不同。
当从一个数据通道纵接收到数据,并且此数据通道设置为应答方式的话,则nRF24L01 在收到数据后产生应答信号,此应答信号的目标地址为接收通道地址。
寄存器配置有些是针对所有数据通道的,有些则是针对个别的。如下设置举例是针对所有数据通道的:
● CRC 使能/禁止
● CRC 计算
● 接收地址宽度
● 频道设置
● 无线数据通信速率
● LNA 增益
● 射频输出功率
寄存器配置:
nRF24L01 所有配置都在配置寄存器中,所有寄存器都是通过SPI 口进行配置的。
SPI 接口:
SPI 接口是标准的SPI 接口,其最大的数据传输率为10Mbps。大多数寄存器是可读的。
SPI 指令设置:
SPI 接口可能用到的指令在下面有所说明。CSN 为低后SPI 接口等待执行指令。每一条指令的执行都必须通过一次CSN 由高到低的变化。
SPI 指令格式:
<命令字:由高位到低位(每字节)>
<数据字节:低字节到高字节,每一字节高位在前>参看图8 及图9
R_REGISTER 和W_REGISTER 寄存器可能操作单字节或多字节寄存器。当访问多字节寄存器时首先要读/写的是最低字节的高位。在所有多字节寄存器被写完之前可以结束写SPI 操作,在这种情况下没有写完的高字节保持原有内容不变。例如:RX_ADDR_P0 寄存器的最低字节可以通过写一个字节给寄存器RX_ADDR_P0 来改变。在CSN 状态由高变低后可以通过MISO 来读取状态寄存器的内容。
中断:
nRF24L01 的中断引脚(IRQ)为低电平触发,当状态寄存器中TX_DS、RX_DR 或MAX_RT 为高时触发中断。当MCU 给中断源写‘1’时,中断引脚被禁止。可屏蔽中断可以被IRQ 中断屏蔽。通过设置可屏蔽中断位为高,则中断响应被禁止。默认状态下所有的中断源是被禁止的。
SPI 时序:
图8、9、10 和表9、10 给出了SPI 操作及时序。在写寄存器之前一定要进入待机模式或掉电模式。在图8 至图10 中用到了下面的符号:
Cn-SPI 指令位
Sn-状态寄存器位
Dn-数据位(备注:由低字节到高字节,每个字节中高位在前)
nRF24XX 兼容的寄存器配置:
如何建立nRF24L01 nRF2401/ nRF2402/ nRF24E1/ nRF24E2 接收数据:
使用与nRF2401/ nRF2402/ nRF24E1/ nRF24E2 相同的CRC 配置
设置PRIM_RX 位为1
相应通道禁止自动应答功能
与发射模块使用相同的地址宽度
与发射模块使用相同的频道
在nRF24L01 和nRF2401/ nRF2402/ nRF24E1/ nRF24E2 两端都选择1Mbit/s 的数据传输率
设置正确的数据宽度
设置PWR_UP 和CE 为高
如何建立nRF24L01 发射,nRF2401/ nRF2402/ nRF24E1/ nRF24E2 接收数据:
使用与nRF2401/ nRF2402/ nRF24E1/ nRF24E2 相同的CRC 配置
设置PRIM_RX 位为0
设置自动重发计数器为0 禁止自动重发功能
与nRF2401/ nRF2402/ nRF24E1/ nRF24E2 使用相同的地址宽度
与nRF2401/ nRF2402/ nRF24E1/ nRF24E2 使用相同的频道
在nRF24L01 和nRF2401/ nRF2402/ nRF24E1/ nRF24E2 两端都选择1Mbit/s的数据传输率
设置PWR_UP 为高
发送与nRF2401/ nRF2402/ nRF24E1/ nRF24E2 寄存器配置数据宽度相同的数据长度。
设置CE 为高启动发射
打包格式描述:
增强型ShockBurstTM 模式下的数据包形式。
重要的时序数据
下面是nRF24L01 部分工作时序数据。
nRF24L01 时序信息
nRF24L01 在掉电模式下转入发射模式或接收模式前必须经过1.5ms 的待机模式。注意:当关掉电源VDD 后寄存器配置内容丢失,模块上电后需重新进行配置。
(2)nRF24L01模块简介
、产品特性
2.4GHz 全球开放ISM 频段,最大0dBm 发射功率,免许可
证使用
支持六路通道的数据接收
低工作电压:1.9~3.6V低电压工作
高速率:2Mbps,由于空中传输时间很短,极大的降低了无
线传输中的碰撞现象(软件设置1Mbps或者2Mbps的空中传输速
率)
多频点:125 频点,满足多点通信和跳频通信需要
超小型:内置2.4GHz天线,体积小巧,15x29mm(包括天线)
低功耗:当工作在应答模式通信时,快速的空中传输及启动
时间,极大的降低了电流消耗。
低应用成本:NRF24L01 集成了所有与RF协议相关的高速信
号处理部分,比如:自动重发丢失数据
包和自动产生应答信号等,NRF24L01的SPI接口可以利用单片机
的硬件SPI口连接或用单片机I/O口进行模拟,内部有FIFO可以与
各种高低速微处理器接口,便于使用低成本单片机。
便于开发:由于链路层完全集成在模块上,非常便于开发。
自动重发功能,自动检测和重发丢失的数据包,重发时间及
重发次数可软件控制
自动存储未收到应答信号的数据包
自动应答功能,在收到有效数据后,模块自动发送应答信号,
无须另行编程
载波检测—固定频率检测
内置硬件CRC 检错和点对多点通信地址控制
数据包传输错误计数器及载波检测功能可用于跳频设置
可同时设置六路接收通道地址,可有选择性的打开接收通道
标准插针Dip2.54MM 间距接口,便于嵌入式应用
  • 、基本电气特性
引脚说明
说明:
1) VCC脚接电压范围为1.9V~3.6V之间,不能在这个区间之外,超
过3.6V将会烧毁模块。推荐电压3.3V左右。
2) 除电源VCC和接地端,其余脚都可以直接和普通的5V单片机IO口
直接相连,无需电平转换。当然对3V左右的单片机更加适用了。
3) 硬件上面没有SPI的单片机也可以控制本模块,用普通单片机IO
口模拟SPI不需要单片机真正的串口介入,只需要普通的单片机IO口
就可以了,当然用串口也可以了(a:51系列单片机P0口连接时候,
需要加10K的上拉电阻,与其余口连接不需要。
b:其他系列的单片机,如果是5V的,请参考该系列单片机IO口输出电流大小,如果超过10mA,需要串联电阻分压,否则容易烧毁模块! 如果是3.3V的,可以
直接和RF24l01模块的IO口线连接。比如AVR系列单片机如果是5V的,一般串接2K的电阻)
  • 单片机89C52RC
  • 软件设计
api.h文件(采集点1、2,接收点共用)

  1. #ifndef _API_DEF_
  2. #define _API_DEF_

  3. // Define interface to nRF24L01

  4. // Define SPI pins
  5. sbit CE   = P1^0;  // Chip Enable pin signal (output)
  6. sbit CSN  = P1^1;  // Slave Select pin, (output to CSN, nRF24L01)
  7. sbit IRQ  = P1^5;  // Interrupt signal, from nRF24L01 (input)
  8. sbit MISO = P1^4;  // Master In, Slave Out pin (input)
  9. sbit MOSI = P1^3;  // Serial Clock pin, (output)
  10. sbit SCK  = P1^2;  // Master Out, Slave In pin (output)

  11. // SPI(nRF24L01) commands
  12. #define READ_REG    0x00  // Define read command to register
  13. #define WRITE_REG   0x20  // Define write command to register
  14. #define RD_RX_PLOAD 0x61  // Define RX payload register address
  15. #define WR_TX_PLOAD 0xA0  // Define TX payload register address
  16. #define FLUSH_TX    0xE1  // Define flush TX register command
  17. #define FLUSH_RX    0xE2  // Define flush RX register command
  18. #define REUSE_TX_PL 0xE3  // Define reuse TX payload register command
  19. #define NOP         0xFF  // Define No Operation, might be used to read status register

  20. // SPI(nRF24L01) registers(addresses)
  21. #define CONFIG      0x00  // 'Config' register address
  22. #define EN_AA       0x01  // 'Enable Auto Acknowledgment' register address
  23. #define EN_RXADDR   0x02  // 'Enabled RX addresses' register address
  24. #define SETUP_AW    0x03  // 'Setup address width' register address
  25. #define SETUP_RETR  0x04  // 'Setup Auto. Retrans' register address
  26. #define RF_CH       0x05  // 'RF channel' register address
  27. #define RF_SETUP    0x06  // 'RF setup' register address
  28. #define STATUS      0x07  // 'Status' register address
  29. #define OBSERVE_TX  0x08  // 'Observe TX' register address
  30. #define CD          0x09  // 'Carrier Detect' register address
  31. #define RX_ADDR_P0  0x0A  // 'RX address pipe0' register address
  32. #define RX_ADDR_P1  0x0B  // 'RX address pipe1' register address
  33. #define RX_ADDR_P2  0x0C  // 'RX address pipe2' register address
  34. #define RX_ADDR_P3  0x0D  // 'RX address pipe3' register address
  35. #define RX_ADDR_P4  0x0E  // 'RX address pipe4' register address
  36. #define RX_ADDR_P5  0x0F  // 'RX address pipe5' register address
  37. #define TX_ADDR     0x10  // 'TX address' register address
  38. #define RX_PW_P0    0x11  // 'RX payload width, pipe0' register address
  39. #define RX_PW_P1    0x12  // 'RX payload width, pipe1' register address
  40. #define RX_PW_P2    0x13  // 'RX payload width, pipe2' register address
  41. #define RX_PW_P3    0x14  // 'RX payload width, pipe3' register address
  42. #define RX_PW_P4    0x15  // 'RX payload width, pipe4' register address
  43. #define RX_PW_P5    0x16  // 'RX payload width, pipe5' register address
  44. #define FIFO_STATUS 0x17  // 'FIFO Status Register' register address
  45. #endif   /* _API_DEF_ */
复制代码

(1)采集点1发送程序:
#include <reg51.h>
#include <intrins.h>
#include <api.h>

#define uchar unsigned char
#define uint unsigned int

/***************************************************/
#define TX_ADR_WIDTH   5  // 5字节宽度的发送/接收地址
#define TX_PLOAD_WIDTH 6  // 数据通道有效数据宽度


uchar code TX_ADDRESS[TX_ADR_WIDTH] = {0x34,0x43,0x10,0x10,0x01};  // 定义一个静态发送地址
uchar RX_BUF[TX_PLOAD_WIDTH];
uchar TX_BUF[TX_PLOAD_WIDTH];
uchar flag;
uchar str_1[TX_PLOAD_WIDTH]="AZainm",outdata[TX_PLOAD_WIDTH];                            //第一位为地址位
uchar bdata sta;
int                count,h;
uchar U8FLAG,k;
uchar U8count,U8temp;
uchar U8T_data_H,U8T_data_L,U8RH_data_H,U8RH_data_L,U8checkdata;
uchar U8T_data_H_temp,U8T_data_L_temp,U8RH_data_H_temp,U8RH_data_L_temp,U8checkdata_temp;
uchar U8comdata;
uint  U16temp1,U16temp2;
sbit  RX_DR              = sta^6;
sbit  TX_DS              = sta^5;
sbit  MAX_RT = sta^4;
sbit  P2_3  = P2^3 ;                                                        //DHT11信号
/**************************************************/

/**************************************************
函数: init_io()

描述:
    初始化IO
/**************************************************/
void init_io(void)
{
              CE  = 0;        // 待机
              CSN = 1;        // SPI禁止
              SCK = 0;        // SPI时钟置低
              IRQ = 1;        // 中断复位
}
/**************************************************/
/**************************************************
函数: init_ser()

描述:
    初始化串口
/**************************************************/
void init_ser(){
              TMOD = 0x20;                //定时器T1使用工作方式2
              TH1 = 253;        // 设置初值
              TL1 = 253;
              TR1 = 1;          // 开始计时
              SCON = 0x50;                //工作方式1,波特率9600bps,允许接收  
              ES = 1;
              EA = 1;           // 打开所以中断  
              TI = 0;
              RI = 0;
}
/**************************************************
函数:delay_ms()

描述:
    延迟x毫秒
/**************************************************/
void delay_ms(uchar x)
{
    uchar i, j;
    i = 0;
    for(i=0; i<x; i++)
    {
       j = 250;
       while(--j);
                 j = 250;
       while(--j);
    }
}
/**************************************************/

/**************************************************
函数:SPI_RW()

描述:
    根据SPI协议,写一字节数据到nRF24L01,同时从nRF24L01
              读出一字节
/**************************************************/
uchar SPI_RW(uchar byte)
{
              uchar i;
                 for(i=0; i<8; i++)          // 循环8次
                 {
                               MOSI = (byte & 0x80);   // byte最高位输出到MOSI
                               byte <<= 1;             // 低一位移位到最高位
                               SCK = 1;                // 拉高SCK,nRF24L01从MOSI读入1位数据,同时从MISO输出1位数据
                               byte |= MISO;                     // 读MISO到byte最低位
                               SCK = 0;                          // SCK置低
                 }
    return(byte);                         // 返回读出的一字节
}
/**************************************************/

/**************************************************
函数:SPI_RW_Reg()

描述:
    写数据value到reg寄存器
/**************************************************/
uchar SPI_RW_Reg(uchar reg, uchar value)
{
              uchar status;
                CSN = 0;                   // CSN置低,开始传输数据
                status = SPI_RW(reg);      // 选择寄存器,同时返回状态字
                SPI_RW(value);             // 然后写数据到该寄存器
                CSN = 1;                   // CSN拉高,结束数据传输
                return(status);            // 返回状态寄存器
}
/**************************************************/

/**************************************************
函数:SPI_Read()

描述:
    从reg寄存器读一字节
/**************************************************/
uchar SPI_Read(uchar reg)
{
              uchar reg_val;
                CSN = 0;                    // CSN置低,开始传输数据
                SPI_RW(reg);                // 选择寄存器
                reg_val = SPI_RW(0);        // 然后从该寄存器读数据
                CSN = 1;                    // CSN拉高,结束数据传输
                return(reg_val);            // 返回寄存器数据
}
/**************************************************/

/**************************************************
函数:SPI_Read_Buf()

描述:
    从reg寄存器读出bytes个字节,通常用来读取接收通道
              数据或接收/发送地址
/**************************************************/
uchar SPI_Read_Buf(uchar reg, uchar * pBuf, uchar bytes)
{
              uchar status, i;
                CSN = 0;                    // CSN置低,开始传输数据
                status = SPI_RW(reg);       // 选择寄存器,同时返回状态字
                for(i=0; i<bytes; i++)
                  pBuf[ i] = SPI_RW(0);    // 逐个字节从nRF24L01读出
                CSN = 1;                    // CSN拉高,结束数据传输
                return(status);             // 返回状态寄存器
}
/**************************************************/

/**************************************************
函数:SPI_Write_Buf()

描述:
    把pBuf缓存中的数据写入到nRF24L01,通常用来写入发
              射通道数据或接收/发送地址
/**************************************************/
uchar SPI_Write_Buf(uchar reg, uchar * pBuf, uchar bytes)
{
              uchar status, i;
                CSN = 0;                    // CSN置低,开始传输数据
                status = SPI_RW(reg);       // 选择寄存器,同时返回状态字
                for(i=0; i<bytes; i++)
                  SPI_RW(pBuf[ i]);        // 逐个字节写入nRF24L01
                CSN = 1;                    // CSN拉高,结束数据传输
                return(status);             // 返回状态寄存器
}
/**************************************************/

/**************************************************
函数:RX_Mode()

描述:
    这个函数设置nRF24L01为接收模式,等待接收发送设备的数据包
/**************************************************/
void RX_Mode(void)
{
              CE = 0;
                SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH);  // 接收设备接收通道0使用和发送设备相同的发送地址
                SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);               // 使能接收通道0自动应答
                SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);           // 使能接收通道0
                SPI_RW_Reg(WRITE_REG + RF_CH, 40);                 // 选择射频通道0x40
                SPI_RW_Reg(WRITE_REG + RX_PW_P0, TX_PLOAD_WIDTH);  // 接收通道0选择和发送通道相同有效数据宽度
                SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);            // 数据传输率1Mbps,发射功率0dBm,低噪声放大器增益
                SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f);              // CRC使能,16位CRC校验,上电,接收模式
                CE = 1;                                            // 拉高CE启动接收设备
}
/**************************************************/

/**************************************************
函数:TX_Mode()

描述:
    这个函数设置nRF24L01为发送模式,(CE=1持续至少10us),
              130us后启动发射,数据发送结束后,发送模块自动转入接收
              模式等待应答信号。
/**************************************************/
void TX_Mode(uchar * BUF)
{
              CE = 0;
                SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);     // 写入发送地址
                SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH);  // 为了应答接收设备,接收通道0地址和发送地址相同
                SPI_Write_Buf(WR_TX_PLOAD, BUF, TX_PLOAD_WIDTH);                  // 写数据包到TX FIFO
                SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);       // 使能接收通道0自动应答
                SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);   // 使能接收通道0
                SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x0a);  // 自动重发延时等待250us+86us,自动重发10次
                SPI_RW_Reg(WRITE_REG + RF_CH, 40);         // 选择射频通道0x40
                SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);    // 数据传输率1Mbps,发射功率0dBm,低噪声放大器增益
                SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e);      // CRC使能,16位CRC校验,上电
              CE = 1;
}
/**************************************************/

/**************************************************
函数:SendData(uchar *a)

描述:
    串口发送数据
/**************************************************/
SendData(uchar a[TX_PLOAD_WIDTH])
{              int i;
              for(i=0;i<TX_PLOAD_WIDTH;i++){
              outdata[ i] = a[ i];
              }
              count = 1;
              SBUF=outdata[0];
}
/**************************************************/

/**************************************************
函数:Check_ACK()

描述:
    检查接收设备有无接收到数据包,设定没有收到应答信
              号是否重发
/**************************************************/
uchar Check_ACK(bit clear)
{
              while(IRQ);
              sta = SPI_RW(NOP);                    // 返回状态寄存器
              if(MAX_RT)
                            if(clear)                         // 是否清除TX FIFO,没有清除在复位MAX_RT中断标志后重发
                                          SPI_RW(FLUSH_TX);
              SPI_RW_Reg(WRITE_REG + STATUS, sta);  // 清除TX_DS或MAX_RT中断标志
              IRQ = 1;
              if(TX_DS)
                            return(0x00);
              else
                            return(0xff);
}
/**************************************************/

/**************************************************/
//DHT11部分


//----------------------------------------------//
//----------------定义区--------------------//
//----------------------------------------------//

void Delay(uint j)
{              uchar i;
              for(;j>0;j--)
              {              
              for(i=0;i<27;i++);

              }
}
void  Delay_10us(void)
{
              uchar i;
    i--;
    i--;
    i--;
    i--;
    i--;
    i--;
}
void  COM(void)
{

              uchar i;

    for(i=0;i<8;i++)               
              {
                           
                            U8FLAG=2;            
                               while((!P2_3)&&U8FLAG++);
                            Delay_10us();
                            Delay_10us();
                            Delay_10us();
                              U8temp=0;
                  if(P2_3)U8temp=1;
                            U8FLAG=2;
                            while((P2_3)&&U8FLAG++);
                               //超时则跳出for循环                             
                               if(U8FLAG==1)break;
                               //判断数据位是0还是1            
                            // 如果高电平高过预定0高电平值则数据位为 1
                            U8comdata<<=1;
                               U8comdata|=U8temp;        //0
                  }//rof
               
}

              //--------------------------------
              //-----湿度读取子程序 ------------
              //--------------------------------
              //----以下变量均为全局变量--------
              //----温度高8位== U8T_data_H------
              //----温度低8位== U8T_data_L------
              //----湿度高8位== U8RH_data_H-----
              //----湿度低8位== U8RH_data_L-----
              //----校验 8位 == U8checkdata-----
              //----调用相关子程序如下----------
              //---- Delay();, Delay_10us();,COM();
              //--------------------------------

void RH(void)
{
                //主机拉低18ms
              P2_3=0;
              Delay(180);
              P2_3=1;
              //总线由上拉电阻拉高 主机延时20us
              Delay_10us();
              Delay_10us();
              Delay_10us();
              Delay_10us();
              //主机设为输入 判断从机响应信号
              P2_3=1;
              //判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行               
              if(!P2_3)                            //T !               
              {
                            U8FLAG=2;
                            //判断从机是否发出 80us 的低电平响应信号是否结束            
                            while((!P2_3)&&U8FLAG++);
                            U8FLAG=2;
                            //判断从机是否发出 80us 的高电平,如发出则进入数据接收状态
                            while((P2_3)&&U8FLAG++);
                            //数据接收状态                           
                            COM();
                            U8RH_data_H_temp=U8comdata;
                            COM();
                            U8RH_data_L_temp=U8comdata;
                            COM();
                            U8T_data_H_temp=U8comdata;
                            COM();
                            U8T_data_L_temp=U8comdata;
                            COM();
                            U8checkdata_temp=U8comdata;
                            P2_3=1;
                            //数据校验
                            U8temp=(U8T_data_H_temp+U8T_data_L_temp+U8RH_data_H_temp+U8RH_data_L_temp);
                            if(U8temp==U8checkdata_temp)
                            {
                            U8RH_data_H=U8RH_data_H_temp;
                            U8RH_data_L=U8RH_data_L_temp;
                            U8T_data_H=U8T_data_H_temp;
                            U8T_data_L=U8T_data_L_temp;
                            U8checkdata=U8checkdata_temp;
                            }//fi
              }//fi

}

/**************************************************
函数:main()

描述:
    主函数
/**************************************************/
void main(void)
{
              init_io();                                          // 初始化IO
              init_ser();                                                                        // 初始化串口
              RX_Mode();                                                                        // 设置为接收模式
              Delay(1);                                       
              while(1)
              {            
                            //------------------------
                            //调用温湿度读取子程序
                            RH();
                            //串口显示程序
                            //--------------------------
                            str_1[0]=0x01;                                                        //地址位
                            str_1[1]=U8RH_data_H;
                            str_1[2]=U8RH_data_L;
                            str_1[3]=U8T_data_H;
                            str_1[4]=U8T_data_L;
                            str_1[5]=U8checkdata;
                            //读取模块数据周期不易小于 2S
                            Delay(20000);
                            for(h=0;h<TX_PLOAD_WIDTH;h++){
                                          TX_BUF[h] = str_1[h];          // 数据送到缓存
                            }
                            TX_Mode(TX_BUF);                                          // 把nRF24L01设置为发送模式并发送数据
                            Check_ACK(1);               // 等待发送完毕,清除TX FIFO
                            delay_ms(500);
                            RX_Mode();                                                                // 设置为接收模式
                            delay_ms(500);
                            SendData(TX_BUF);
              }
}
/**************************************************/
/**************************************************
函数:RISINTR()

描述:
    中断函数
/**************************************************/
void RSINTR() interrupt 4 using 2
{
              if(TI==1) //发送中断               
              {
                            TI=0;
                            if(count!=TX_PLOAD_WIDTH) //发送完TX_PLOAD_WIDTH位数据            
                            {
                                          SBUF= outdata[count];
                                          count++;
                            }
              }
}
(2)采集点2程序;
#include <reg51.h>
#include <intrins.h>
#include <api.h>

#define uchar unsigned char
#define uint unsigned int

/***************************************************/
#define TX_ADR_WIDTH   5  // 5字节宽度的发送/接收地址
#define TX_PLOAD_WIDTH 6  // 数据通道有效数据宽度


uchar code TX_ADDRESS[TX_ADR_WIDTH] = {0xC2,0xC2,0xC2,0xC2,0xC2};  // 定义一个静态发送地址
uchar RX_BUF[TX_PLOAD_WIDTH];
uchar TX_BUF[TX_PLOAD_WIDTH];
uchar flag;
uchar str_1[TX_PLOAD_WIDTH]="BZainm",outdata[TX_PLOAD_WIDTH];
uchar bdata sta;
int                count,h;
uchar U8FLAG,k;
uchar U8count,U8temp;
uchar U8T_data_H,U8T_data_L,U8RH_data_H,U8RH_data_L,U8checkdata;
uchar U8T_data_H_temp,U8T_data_L_temp,U8RH_data_H_temp,U8RH_data_L_temp,U8checkdata_temp;
uchar U8comdata;
uint  U16temp1,U16temp2;
sbit  RX_DR              = sta^6;
sbit  TX_DS              = sta^5;
sbit  MAX_RT = sta^4;
sbit  P2_3  = P2^3 ;                                                        //DHT11信号
/**************************************************/

/**************************************************
函数: init_io()

描述:
    初始化IO
/**************************************************/
void init_io(void)
{
              CE  = 0;        // 待机
              CSN = 1;        // SPI禁止
              SCK = 0;        // SPI时钟置低
              IRQ = 1;        // 中断复位
              //LED = 0xff;                            // 关闭指示灯
}
/**************************************************/
/**************************************************
函数: init_ser()

描述:
    初始化串口
/**************************************************/
void init_ser(){
              TMOD = 0x20;                //定时器T1使用工作方式2
              TH1 = 253;        // 设置初值
              TL1 = 253;
              TR1 = 1;          // 开始计时
              SCON = 0x50;                //工作方式1,波特率9600bps,允许接收  
              ES = 1;
              EA = 1;           // 打开所以中断  
              TI = 0;
              RI = 0;
}
/**************************************************
函数:delay_ms()

描述:
    延迟x毫秒
/**************************************************/
void delay_ms(uchar x)
{
    uchar i, j;
    i = 0;
    for(i=0; i<x; i++)
    {
       j = 250;
       while(--j);
                 j = 250;
       while(--j);
    }
}
/**************************************************/

/**************************************************
函数:SPI_RW()

描述:
    根据SPI协议,写一字节数据到nRF24L01,同时从nRF24L01
              读出一字节
/**************************************************/
uchar SPI_RW(uchar byte)
{
              uchar i;
                 for(i=0; i<8; i++)          // 循环8次
                 {
                               MOSI = (byte & 0x80);   // byte最高位输出到MOSI
                               byte <<= 1;             // 低一位移位到最高位
                               SCK = 1;                // 拉高SCK,nRF24L01从MOSI读入1位数据,同时从MISO输出1位数据
                               byte |= MISO;                     // 读MISO到byte最低位
                               SCK = 0;                          // SCK置低
                 }
    return(byte);                         // 返回读出的一字节
}
/**************************************************/

/**************************************************
函数:SPI_RW_Reg()

描述:
    写数据value到reg寄存器
/**************************************************/
uchar SPI_RW_Reg(uchar reg, uchar value)
{
              uchar status;
                CSN = 0;                   // CSN置低,开始传输数据
                status = SPI_RW(reg);      // 选择寄存器,同时返回状态字
                SPI_RW(value);             // 然后写数据到该寄存器
                CSN = 1;                   // CSN拉高,结束数据传输
                return(status);            // 返回状态寄存器
}
/**************************************************/

/**************************************************
函数:SPI_Read()

描述:
    从reg寄存器读一字节
/**************************************************/
uchar SPI_Read(uchar reg)
{
              uchar reg_val;
                CSN = 0;                    // CSN置低,开始传输数据
                SPI_RW(reg);                // 选择寄存器
                reg_val = SPI_RW(0);        // 然后从该寄存器读数据
                CSN = 1;                    // CSN拉高,结束数据传输
                return(reg_val);            // 返回寄存器数据
}
/**************************************************/

/**************************************************
函数:SPI_Read_Buf()

描述:
    从reg寄存器读出bytes个字节,通常用来读取接收通道
              数据或接收/发送地址
/**************************************************/
uchar SPI_Read_Buf(uchar reg, uchar * pBuf, uchar bytes)
{
              uchar status, i;
                CSN = 0;                    // CSN置低,开始传输数据
                status = SPI_RW(reg);       // 选择寄存器,同时返回状态字
                for(i=0; i<bytes; i++)
                  pBuf[ i] = SPI_RW(0);    // 逐个字节从nRF24L01读出
                CSN = 1;                    // CSN拉高,结束数据传输
                return(status);             // 返回状态寄存器
}
/**************************************************/

/**************************************************
函数:SPI_Write_Buf()

描述:
    把pBuf缓存中的数据写入到nRF24L01,通常用来写入发
              射通道数据或接收/发送地址
/**************************************************/
uchar SPI_Write_Buf(uchar reg, uchar * pBuf, uchar bytes)
{
              uchar status, i;
                CSN = 0;                    // CSN置低,开始传输数据
                status = SPI_RW(reg);       // 选择寄存器,同时返回状态字
                for(i=0; i<bytes; i++)
                  SPI_RW(pBuf[ i]);        // 逐个字节写入nRF24L01
                CSN = 1;                    // CSN拉高,结束数据传输
                return(status);             // 返回状态寄存器
}
/**************************************************/

/**************************************************
函数:RX_Mode()

描述:
    这个函数设置nRF24L01为接收模式,等待接收发送设备的数据包
/**************************************************/
void RX_Mode(void)
{
              CE = 0;
                SPI_Write_Buf(WRITE_REG + RX_ADDR_P1, TX_ADDRESS, TX_ADR_WIDTH);  // 接收设备接收通道0使用和发送设备相同的发送地址
                SPI_RW_Reg(WRITE_REG + EN_AA, 0x02);               // 使能接收通道1自动应答
                SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x02);           // 使能接收通道1
                SPI_RW_Reg(WRITE_REG + RF_CH, 40);                 // 选择射频通道0x40
                SPI_RW_Reg(WRITE_REG + RX_PW_P0, TX_PLOAD_WIDTH);  // 接收通道0选择和发送通道相同有效数据宽度
                SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);            // 数据传输率1Mbps,发射功率0dBm,低噪声放大器增益
                SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f);              // CRC使能,16位CRC校验,上电,接收模式
                CE = 1;                                            // 拉高CE启动接收设备
}
/**************************************************/

/**************************************************
函数:TX_Mode()

描述:
    这个函数设置nRF24L01为发送模式,(CE=1持续至少10us),
              130us后启动发射,数据发送结束后,发送模块自动转入接收
              模式等待应答信号。
/**************************************************/
void TX_Mode(uchar * BUF)
{
              CE = 0;
                SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);     // 写入发送地址
                SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH);  // 为了应答接收设备,接收通道0地址和发送地址相同
                SPI_Write_Buf(WR_TX_PLOAD, BUF, TX_PLOAD_WIDTH);                  // 写数据包到TX FIFO
                SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);       // 使能接收通道1自动应答
                SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);   // 使能接收通道1
                SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x0a);  // 自动重发延时等待250us+86us,自动重发10次
                SPI_RW_Reg(WRITE_REG + RF_CH, 40);         // 选择射频通道0x40
                SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);    // 数据传输率1Mbps,发射功率0dBm,低噪声放大器增益
                SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e);      // CRC使能,16位CRC校验,上电
              CE = 1;
}
/**************************************************/

/**************************************************
函数:SendData(uchar *a)

描述:
    串口发送数据
/**************************************************/
SendData(uchar a[TX_PLOAD_WIDTH])
{              int i;
              for(i=0;i<TX_PLOAD_WIDTH;i++){
              outdata[ i] = a[ i];
              }
              count = 1;
              SBUF=outdata[0];
}
/**************************************************/

/**************************************************
函数:Check_ACK()

描述:
    检查接收设备有无接收到数据包,设定没有收到应答信
              号是否重发
/**************************************************/
uchar Check_ACK(bit clear)
{
              while(IRQ);
              sta = SPI_RW(NOP);                    // 返回状态寄存器
              if(MAX_RT)
                            if(clear)                         // 是否清除TX FIFO,没有清除在复位MAX_RT中断标志后重发
                                          SPI_RW(FLUSH_TX);
              SPI_RW_Reg(WRITE_REG + STATUS, sta);  // 清除TX_DS或MAX_RT中断标志
              IRQ = 1;
              if(TX_DS)
                            return(0x00);
              else
                            return(0xff);
}
/**************************************************/

/**************************************************/
//DHT11部分


//----------------------------------------------//
//----------------定义区--------------------//
//----------------------------------------------//

void Delay(uint j)
{              uchar i;
              for(;j>0;j--)
              {              
              for(i=0;i<27;i++);

              }
}
void  Delay_10us(void)
{
              uchar i;
    i--;
    i--;
    i--;
    i--;
    i--;
    i--;
}
void  COM(void)
{

              uchar i;

    for(i=0;i<8;i++)               
              {
                           
                            U8FLAG=2;            
                               while((!P2_3)&&U8FLAG++);
                            Delay_10us();
                            Delay_10us();
                            Delay_10us();
                              U8temp=0;
                  if(P2_3)U8temp=1;
                            U8FLAG=2;
                            while((P2_3)&&U8FLAG++);
                               //超时则跳出for循环                             
                               if(U8FLAG==1)break;
                               //判断数据位是0还是1            
                            // 如果高电平高过预定0高电平值则数据位为 1
                            U8comdata<<=1;
                               U8comdata|=U8temp;        //0
                  }//rof
               
}

              //--------------------------------
              //-----湿度读取子程序 ------------
              //--------------------------------
              //----以下变量均为全局变量--------
              //----温度高8位== U8T_data_H------
              //----温度低8位== U8T_data_L------
              //----湿度高8位== U8RH_data_H-----
              //----湿度低8位== U8RH_data_L-----
              //----校验 8位 == U8checkdata-----
              //----调用相关子程序如下----------
              //---- Delay();, Delay_10us();,COM();
              //--------------------------------

void RH(void)
{
                //主机拉低18ms
              P2_3=0;
              Delay(180);
              P2_3=1;
              //总线由上拉电阻拉高 主机延时20us
              Delay_10us();
              Delay_10us();
              Delay_10us();
              Delay_10us();
              //主机设为输入 判断从机响应信号
              P2_3=1;
              //判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行               
              if(!P2_3)                            //T !               
              {
                            U8FLAG=2;
                            //判断从机是否发出 80us 的低电平响应信号是否结束            
                            while((!P2_3)&&U8FLAG++);
                            U8FLAG=2;
                            //判断从机是否发出 80us 的高电平,如发出则进入数据接收状态
                            while((P2_3)&&U8FLAG++);
                            //数据接收状态                           
                            COM();
                            U8RH_data_H_temp=U8comdata;
                            COM();
                            U8RH_data_L_temp=U8comdata;
                            COM();
                            U8T_data_H_temp=U8comdata;
                            COM();
                            U8T_data_L_temp=U8comdata;
                            COM();
                            U8checkdata_temp=U8comdata;
                            P2_3=1;
                            //数据校验
                            U8temp=(U8T_data_H_temp+U8T_data_L_temp+U8RH_data_H_temp+U8RH_data_L_temp);
                            if(U8temp==U8checkdata_temp)
                            {
                            U8RH_data_H=U8RH_data_H_temp;
                            U8RH_data_L=U8RH_data_L_temp;
                            U8T_data_H=U8T_data_H_temp;
                            U8T_data_L=U8T_data_L_temp;
                            U8checkdata=U8checkdata_temp;
                            }//fi
              }//fi

}

/**************************************************
函数:main()

描述:
    主函数
/**************************************************/
void main(void)
{
              init_io();                                          // 初始化IO
              init_ser();                                                                        // 初始化串口
              RX_Mode();                                                                        // 设置为接收模式
              Delay(1);                                       
              while(1)
              {            
                            //------------------------
                            //调用温湿度读取子程序
                            RH();
                            //串口显示程序
                            //--------------------------
                            str_1[0]=0x02;
                            str_1[1]=U8RH_data_H;
                            str_1[2]=U8RH_data_L;
                            str_1[3]=U8T_data_H;
                            str_1[4]=U8T_data_L;
                            str_1[5]=U8checkdata;
                            //读取模块数据周期不易小于 2S
                            Delay(20000);
                            for(h=0;h<TX_PLOAD_WIDTH;h++){
                                          TX_BUF[h] = str_1[h];          // 数据送到缓存
                            }
                            TX_Mode(TX_BUF);                                          // 把nRF24L01设置为发送模式并发送数据
                            Check_ACK(1);               // 等待发送完毕,清除TX FIFO
                            delay_ms(500);
                            RX_Mode();                                                                // 设置为接收模式
                            delay_ms(500);
                            SendData(TX_BUF);
              }
}
/**************************************************/
/**************************************************
函数:RISINTR()

描述:
    中断函数
/**************************************************/
void RSINTR() interrupt 4 using 2
{
              if(TI==1) //发送中断               
              {
                            TI=0;
                            if(count!=TX_PLOAD_WIDTH) //发送完TX_PLOAD_WIDTH位数据            
                            {
                                          SBUF= outdata[count];
                                          count++;
                            }
              }
}
(3)接收点程序
#include <reg51.h>
#include <api.h>

#define uchar unsigned char

/***************************************************/
#define TX_ADR_WIDTH   5  // 5字节宽度的发送/接收地址
#define TX_PLOAD_WIDTH 6  // 数据通道有效数据宽度

uchar code TX_ADDRESS_0[TX_ADR_WIDTH] = {0x34,0x43,0x10,0x10,0x01};  // 定义一个静态发送地址
uchar code TX_ADDRESS_1[TX_ADR_WIDTH] = {0xC2,0xC2,0xC2,0xC2,0xC2};  // 定义一个静态发送地址
uchar RX_BUF_0[TX_PLOAD_WIDTH];//0通道数据
uchar RX_BUF_1[TX_PLOAD_WIDTH];//1通道数据
uchar TX_BUF[TX_PLOAD_WIDTH];
uchar flag;
uchar str[TX_PLOAD_WIDTH],outdata[TX_PLOAD_WIDTH];
uchar bdata sta;
int                count,n;
sbit  RX_P_NO1=sta^1;
sbit  RX_P_NO2=sta^2;
sbit  RX_P_NO3=sta^3;
sbit  MAX_RT = sta^4;
sbit  TX_DS              = sta^5;
sbit  RX_DR              = sta^6;

/**************************************************/

/**************************************************
函数: init_io()

描述:
    初始化IO
/**************************************************/
void init_io(void)
{
              CE  = 0;        // 待机
              CSN = 1;        // SPI禁止
              SCK = 0;        // SPI时钟置低
              IRQ = 1;        // 中断复位
              //LED = 0xff;                            // 关闭指示灯
}
/**************************************************/
/**************************************************
函数: init_ser()

描述:
    初始化串口
/**************************************************/
void init_ser(){
              TMOD = 0x20;                //定时器T1使用工作方式2
              TH1 = 253;        // 设置初值
              TL1 = 253;
              TR1 = 1;          // 开始计时
              SCON = 0x50;                //工作方式1,波特率9600bps,允许接收  
              ES = 1;
              EA = 1;           // 打开所以中断  
              TI = 0;
              RI = 0;
}
/**************************************************
函数:delay_ms()

描述:
    延迟x毫秒
/**************************************************/
void delay_ms(uchar x)
{
    uchar i, j;
    i = 0;
    for(i=0; i<x; i++)
    {
       j = 250;
       while(--j);
                 j = 250;
       while(--j);
    }
}
/**************************************************/

/**************************************************
函数:SPI_RW()

描述:
    根据SPI协议,写一字节数据到nRF24L01,同时从nRF24L01
              读出一字节
/**************************************************/
uchar SPI_RW(uchar byte)
{
              uchar i;
                 for(i=0; i<8; i++)          // 循环8次
                 {
                               MOSI = (byte & 0x80);   // byte最高位输出到MOSI
                               byte <<= 1;             // 低一位移位到最高位
                               SCK = 1;                // 拉高SCK,nRF24L01从MOSI读入1位数据,同时从MISO输出1位数据
                               byte |= MISO;                     // 读MISO到byte最低位
                               SCK = 0;                          // SCK置低
                 }
    return(byte);                         // 返回读出的一字节
}
/**************************************************/

/**************************************************
函数:SPI_RW_Reg()

描述:
    写数据value到reg寄存器
/**************************************************/
uchar SPI_RW_Reg(uchar reg, uchar value)
{
              uchar status;
                CSN = 0;                   // CSN置低,开始传输数据
                status = SPI_RW(reg);      // 选择寄存器,同时返回状态字
                SPI_RW(value);             // 然后写数据到该寄存器
                CSN = 1;                   // CSN拉高,结束数据传输
                return(status);            // 返回状态寄存器
}
/**************************************************/

/**************************************************
函数:SPI_Read()

描述:
    从reg寄存器读一字节
/**************************************************/
uchar SPI_Read(uchar reg)
{
              uchar reg_val;
                CSN = 0;                    // CSN置低,开始传输数据
                SPI_RW(reg);                // 选择寄存器
                reg_val = SPI_RW(0);        // 然后从该寄存器读数据
                CSN = 1;                    // CSN拉高,结束数据传输
                return(reg_val);            // 返回寄存器数据
}
/**************************************************/

/**************************************************
函数:SPI_Read_Buf()

描述:
    从reg寄存器读出bytes个字节,通常用来读取接收通道
              数据或接收/发送地址
/**************************************************/
uchar SPI_Read_Buf(uchar reg, uchar * pBuf, uchar bytes)
{
              uchar status, i;
                CSN = 0;                    // CSN置低,开始传输数据
                status = SPI_RW(reg);       // 选择寄存器,同时返回状态字
                for(i=0; i<bytes; i++)
                  pBuf[ i] = SPI_RW(0);    // 逐个字节从nRF24L01读出
                CSN = 1;                    // CSN拉高,结束数据传输
                return(status);             // 返回状态寄存器
}
/**************************************************/

/**************************************************
函数:SPI_Write_Buf()

描述:
    把pBuf缓存中的数据写入到nRF24L01,通常用来写入发
              射通道数据或接收/发送地址
/**************************************************/
uchar SPI_Write_Buf(uchar reg, uchar * pBuf, uchar bytes)
{
              uchar status, i;
                CSN = 0;                    // CSN置低,开始传输数据
                status = SPI_RW(reg);       // 选择寄存器,同时返回状态字
                for(i=0; i<bytes; i++)
                  SPI_RW(pBuf[ i]);        // 逐个字节写入nRF24L01
                CSN = 1;                    // CSN拉高,结束数据传输
                return(status);             // 返回状态寄存器
}
/**************************************************/

/**************************************************
函数:RX_Mode()

描述:
    这个函数设置nRF24L01为接收模式,等待接收发送设备的数据包
/**************************************************/
void RX_Mode_0(void)
{
              CE = 0;
                SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS_0, TX_ADR_WIDTH);  // 接收设备接收通道0使用和发送设备相同的发送地址
                SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);               // 使能接收通道0自动应答
                SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);           // 使能接收通道0
                SPI_RW_Reg(WRITE_REG + RF_CH, 40);                 // 选择射频通道0x40
                SPI_RW_Reg(WRITE_REG + RX_PW_P0, TX_PLOAD_WIDTH);  // 接收通道0选择和发送通道相同有效数据宽度
                SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);            // 数据传输率1Mbps,发射功率0dBm,低噪声放大器增益
                SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f);              // CRC使能,16位CRC校验,上电,接收模式
                CE = 1;                                            // 拉高CE启动接收设备
}
/**************************************************/
/**************************************************
函数:RX_Mode()

描述:
    这个函数设置nRF24L01为接收模式,等待接收发送设备的数据包
/**************************************************/
void RX_Mode_1(void)
{
              CE = 0;
                SPI_Write_Buf(WRITE_REG + RX_ADDR_P1, TX_ADDRESS_1, TX_ADR_WIDTH);  // 接收设备接收通道0使用和发送设备相同的发送地址
                SPI_RW_Reg(WRITE_REG + EN_AA, 0x02);               // 使能接收通道0自动应答
                SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x02);           // 使能接收通道0
                SPI_RW_Reg(WRITE_REG + RF_CH, 40);                 // 选择射频通道0x40
                SPI_RW_Reg(WRITE_REG + RX_PW_P1, TX_PLOAD_WIDTH);  // 接收通道0选择和发送通道相同有效数据宽度
                SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);            // 数据传输率1Mbps,发射功率0dBm,低噪声放大器增益
                SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f);              // CRC使能,16位CRC校验,上电,接收模式
                CE = 1;                                            // 拉高CE启动接收设备
}
/**************************************************/

/**************************************************
函数:TX_Mode()

描述:
    这个函数设置nRF24L01为发送模式,(CE=1持续至少10us),
              130us后启动发射,数据发送结束后,发送模块自动转入接收
              模式等待应答信号。
/**************************************************/
/*void TX_Mode(uchar * BUF)
{
              CE = 0;
                SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);     // 写入发送地址
                SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH);  // 为了应答接收设备,接收通道0地址和发送地址相同
                SPI_Write_Buf(WR_TX_PLOAD, BUF, TX_PLOAD_WIDTH);                  // 写数据包到TX FIFO
                SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);       // 使能接收通道0自动应答
                SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);   // 使能接收通道0
                SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x0a);  // 自动重发延时等待250us+86us,自动重发10次
                SPI_RW_Reg(WRITE_REG + RF_CH, 40);         // 选择射频通道0x40
                SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);    // 数据传输率1Mbps,发射功率0dBm,低噪声放大器增益
                SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e);      // CRC使能,16位CRC校验,上电
              CE = 1;
}*/
/**************************************************/

/**************************************************
函数:SendData(uchar *a)

描述:
    串口发送数据
/**************************************************/
SendData(uchar a[TX_PLOAD_WIDTH])
{              int i;
              for(i=0;i<TX_PLOAD_WIDTH;i++){
              outdata[ i] = a[ i];
              }
              count = 1;
              SBUF=outdata[0];
}
/**************************************************/

/**************************************************
函数:Check_ACK()

描述:
    检查接收设备有无接收到数据包,设定没有收到应答信
              号是否重发
/**************************************************/
uchar Check_ACK(bit clear)
{
              while(IRQ);
              sta = SPI_RW(NOP);                    // 返回状态寄存器
              if(MAX_RT)
                            if(clear)                         // 是否清除TX FIFO,没有清除在复位MAX_RT中断标志后重发
                                          SPI_RW(FLUSH_TX);
              SPI_RW_Reg(WRITE_REG + STATUS, sta);  // 清除TX_DS或MAX_RT中断标志
              IRQ = 1;
              if(TX_DS)
                            return(0x00);
              else
                            return(0xff);
}
/**************************************************/

/**************************************************
函数:main()

描述:
    主函数
/**************************************************/
void main(void)
{
              init_io();                                          // 初始化IO
              init_ser();                                                                        // 初始化串口
              RX_Mode_0();                            // 设置为接收模式
              while(1)
              {            
                            RX_Mode_0();                                             //通道0
                            sta = SPI_Read(STATUS);                // 读状态寄存器
                            if(RX_DR)                                                          // 判断是否接受到数据
                            {
                                          SPI_Read_Buf(RD_RX_PLOAD, RX_BUF_0, TX_PLOAD_WIDTH);  // 从RX FIFO读出数据
                                          flag = 1;
                            }
                            SPI_RW_Reg(WRITE_REG + STATUS, sta);  // 清除RX_DS中断标志
                            if(flag)                                       // 接受完成
                            {
                                          flag = 0;                                   // 清标志
                                          delay_ms(250);
                                          delay_ms(250);
                            }
                            delay_ms(2500);
                            SendData(RX_BUF_0);
                            delay_ms(5000);
                            RX_Mode_1();                                            // 通道1
                            sta = SPI_Read(STATUS);                // 读状态寄存器
                            if(RX_DR)                                                          // 判断是否接受到数据
                            {
                                          SPI_Read_Buf(RD_RX_PLOAD, RX_BUF_1, TX_PLOAD_WIDTH);  // 从RX FIFO读出数据
                                          flag = 1;
                            }
                            SPI_RW_Reg(WRITE_REG + STATUS, sta);  // 清除RX_DS中断标志
                            if(flag)                                       // 接受完成
                            {
                                          flag = 0;                                   // 清标志
                                          delay_ms(250);
                                          delay_ms(250);
                            }
                            delay_ms(5000);
                            SendData(RX_BUF_1);
              }
}
/**************************************************/
/**************************************************
函数:RISINTR()

描述:
    中断函数
/**************************************************/
void RSINTR() interrupt 4 using 2
{
              if(TI==1) //发送中断               
              {
                            TI=0;
                            if(count!=TX_PLOAD_WIDTH) //发送完TX_PLOAD_WIDTH位数据            
                            {
                                          SBUF= outdata[count];
                                          count++;
                            }
              }
}

  • 实验成果

串口截图(01为采集点1地址位,02为采集点2地址位)
温湿度显示为湿度整数位,湿度小数位,温度整数位,温度小数位,校验位,串口为16进制显示
事物图片
接收点:
采集点1
采集点2
整体图片:
  • 参考资料
1、讯通                            《2.4GHz 单片高速2Mbps 无线收发芯片 nRF24L01》;
2、深圳云佳科技有限公司              《NRF24L01高速嵌入式无线数传模块说明书》。


完整的Word格式文档51黑下载地址:
基于DHT11温湿度传感器和nrf24l01模块的多点数据采集.docx (2.77 MB, 下载次数: 59)



评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏5 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:427220 发表于 2018-11-21 09:53 | 只看该作者
你好,请问你使用的晶振频率是多大的,我看到NRF的手册的晶振频率是16MHz的
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表