基于战舰V3的NRF24L01模块的原理剖析及应用

基于战舰V3的NRF24L01模块的原理剖析及应用

为什么无线发射功率的数值均为负数?

其实归根到底为什么接收的无线信号是负值,这样子是不是容易理解多了。因为无线信号多为mW级别,所以对它进行了极化,转化为dBm而已,不表示信号是负的。1mW就是0dBm,小于1mW就是负数的dBm数。

弄清信号强度的定义就行了:

RSSI(接收信号强度:Received Signal Strength Indicator)= 10logP, 只需将接收到的信号功率P代入就是接收信号强度(灵敏度)。例如:

1. 如果发射功率P为1mw,折算为dBm后为0dBm;

2. 对于40W的功率,按dBm单位进行折算后的值应为: 10lg(40W/1MW)=10lg(40000)=10lg4+10lg10+10lg1000=46dBm。

首先我们需要知道的是无线信号dBm都是负数,最大是0。因此测量出来的dBm值肯定都是负数。因为dBm值只在一种情况下为0,那就是在理想状态下经过实验测量的结果,一般我们认为dBm为0是其最大值,意味着接收方把发射方发射的所有无线信号都接收到了,即无线路由器发射多少功率,接收的无线网卡就获得多少功率。当然这是在理想状态下测量的,在实际中即使将无线网卡挨着无线路由器的发射天线也不会达到dBm为0的效果。所以说测量出来的dBm值都是负数,不要盲目的认为负数就是信号不好。

那无线发射功率(dBm)是多少合适呢?

dBm是一个表示功率绝对值的单位,他的计算公式为10lg功率值/1mw。例如如果接收到的功率为1mw,按照dBm单位进行折算后的值应该为10lg 1mw/1mw=0dBm。当然在实际传输过程中接收方是很难达到接收功率1mw的。因此我们通过这个公式就可以从dBm值反向推出接收方接收到的功率值了。

1. 越小越好?

既然前面提到了dbm值都是负数,所以很多人都认为dbm值越小越好。其实这个认知是错误的。正如前面所说dbm值最大是0,而且是理想状态。那么越接近理想状态下的dbm值,越说明无线路由器发射的功率都被无线网卡接收到了。因此dbm值应该越大越好,-50dbm说明接收到的无线信号要好于-70dbm。

2. 越大越好?

又有很多朋友认为既然dbm值是0说明接收发送信号的效果最好,那么我们就应该让企业无线网络各个地方的dbm值尽可能的大。实际上这个观点也是错误的,虽然dbm值越大发送接收信号效果越好,但是与此同时也需要我们为企业内部无线网络安装足够多的无线信号中继设备,这比费用也是不小的。经过实验表明在XP系统无线信号扫描组件中显示为“非常好”状态时是可以满足网络传输要求的,速度和稳定性都没有任何问题,而这个“非常好”状态对应的dbm值为0到-50dbm。因此我们只需要保证企业内部无线网各个地方的dbm值不大于-50dbm即可,这样建立的无线网就是一个高速稳定的网络。我们对于无线网络投入的性价比才会最高。

3. 那为啥我发出信号用的功率那么大,收到信号的功率却少的可怜?

众所周知无线路由器发射功率一般都是100mw,还有更高的。那么为什么我们接收到的功率却如此之小呢?是因为传输过程中受到干扰比较大呢?下面我们拿接收到的信号为-50dbm即0.01μW为例进行介绍,如果无线路由器发射功率为100mw,而接收到的仅仅为0.01μW,两者差别为10000000倍。

实际上这个是正常的传输,就好象太阳发出的能量只有一亿分之一被地球接收到一样。接收功率肯定要远远小于发射功率。所以网络管理员在测量时比需担心,只要你的信号强度大于-50dbm就可以没有任何问题的无线传输数据,再退一步即使到了-70dbm也可以保证无线速度为54M进行传输。

“信道堵塞”是什么意思?

我们有时候说“一个WIFI的接入设备越多,导致WIFI速度越慢,一般我们会说:WIFI卡死了”,我们明白为何卡?是因为无线传输速率下降?还是连入WIFI的其他用户设备分走了我的速度?

其实都不是,无线通信使用的是电磁波,既然是波,那就有频率,通过将电磁波的频率划分为不同的 “段”,即是频段。

可以通俗的把频段理解为,两个地方之间的高速公路。案例如下:

通常无线路由器都会有两个频段:2.4GHz和5GHz。即两条不同的路,好比汽车在高速公路,地铁在地下轨道,各有千秋。波长=波速 * 周期=波速/频率,所以频率越高,波长越短。

为什么2.4GHz穿墙能力强,5GHz传输快?正是由于2.4GHz的频率较低,所以波长更长,更容易绕过障碍物继续传播。那么问题来了:家电、无线等设备多数使用的2.4G频段,无线环境更加拥挤,干扰较大,而5GHz的频宽较宽,设备较少,从而干扰也少。

干扰多意味着:同样时间内传输成功的概率会下降,反映到无线通信中就是“丢包的数量上升,即N个数据包成功发送所需时间增加”,传输效率的降低反映到我们这里就是“无线传输速度卡死了”。

首先,信道越多,那每个信道的宽度就很窄了,信道里边终端的冲突概率就变得更大了,如果想要避免或是减少冲突,那么则需要花费更多的时间来监测冲突,有问题的情况下,还需要重发数据包,那么速度肯定也提不上去。

想想一个信道被几十、上百个无线传输设备使用,这个会导致:信道内的无线传输设备发送信号时冲突的概率就变大了,需要消耗更多的时间来检测冲突、进行重发,速度自然就上不去了,也就意味着“信道堵塞了”。

如何避开“信道堵塞”,提高传输速率呢?

NRF24L01中专门有检测“信道堵塞”的相关位,我们可以判断该位的状态来判断“传输速率慢是什么原因导致的,是信道堵塞,还是其他因素”。NRF24L01允许我们设置以2.4G为基准正负偏移0.125G的频率,即可设置的频率为2.375G~2.525G。

引脚说明

MCU与NRF24L01的引脚连接关系

表格 1

NRF24L01引脚

MCU引脚标号

说明

NRF_CE

PG8

传输使能

NRF_CS

PG7

器件片选

NRF_SCK

PB13

同步时钟传输

NRF_MOSI

PB14

主发从收

NRF_MISO

PB15

主收从发

NRF_IRQ

PG6

中断信号

其中,SPI2由于也接着W25Q128这个128K的NOR Flash,我们需要再使用一个引脚管理W25Q128片选引脚,使其失能,这样我们才可以放心的使用PB13~PB14引脚与NRF24L01通信。最终的硬件连接引脚关系如下所示:

表格 2

NRF24L01引脚

MCU引脚标号

说明

NRF_CE

PG8

传输使能

NRF_CS

PG7

器件片选

NRF_SCK

PB13

同步时钟传输

NRF_MOSI

PB14

主发从收

NRF_MISO

PB15

主收从发

NRF_IRQ

PG6

中断信号

——

PB12

W25Q128片选

NRF24L01寄存器间接

0X00-配置寄存器

只有对应功能的可屏蔽中断位处于有效电平状态,才可以触发中断(即相应功能的中断标志位才可以被置位)。PRIM_RX=0表示NRF24L01处于发送状态,PRIM_RX=1表示NRF24L01处于接收状态。

MASK_MAX_RT位表示“如果重复发送MAX_RT次数据对方还是没有给出相应的应答信号,我们就触发中断了”,MASK_TX_DS和MASK_RX_DR很好理解:一个是发送完成且应答成功就会触发中断,一个是NRF24L01的FIFO缓冲区接收到数据时触发中断,意在告诉MCU“赶紧的,可以来读取数据了”。

0X01-自动应答使能/失能寄存器

NRF24L01的Enhanced ShockBurstTM功能与ShockBurstTM功能不同之处就在于“每个通道都可以设置自动应答“。

自动应答只适用于增强型ShockBurstTM模式,如果对方收发的无线设备不支持增强型ShockBurstTM模式(比如nRF2401,nRF24E1,nRF2402,nRF24E2),我们为了做到兼容必须失能自动应答功能(这个需我们人为设置相应的位),并去除数据包中的一些数据(硬件自己完成我们无需关心):

 这里对于NRF24L01有重要意义的数据如下:

地址:识别特定的NRF24L01并且向其发送成功应答信号;

CRC校验:用于检验数据包的所有数据是否发送正确,如果不正确则直接舍弃;

9位标志位(PID):对于接收端有大用处,标志着接收端接收到的数据是不是重复的,如果接收是重复无用数据,接收端的NRF24L01就会直接舍弃这个数据包。

0X02-接收通道使能寄存器

0X03-地址宽度设置寄存器

每个通道最多可支持5个字节的地址,也就是40位的地址。

0X04-自动重发配置寄存器

如果应答失败,则重发ARC次,每次间隔ARD。这里一定要注意:ARD时间间隔不可以设置的过短,这段时间间隔是用于“再一次接收应答信号”用的,如果过短会导致“你这里还没收到应答,又发送了一次,那上一次发送不就白忙活了吗”。就如同说明书中所说的一样:

0X05-无线频率配置寄存器

RF_CH[6:0]位用于设置相较于2.4G的偏移频率,可取数值为0X1111110~0X0000010,共可设置125个频点,也就是说NRF24L01的工作频率可设置为2.4GHz~2.525GHz,相当于每增加单位1,NRF24L01的工作频率就增加10MHz,最多可增加125MHz=0.125GHz。

0X06-射频配置寄存器

发射功率我们前面提到过这里就不再赘叙了。

0X07-状态寄存器

中断引脚空闲时电平为高,有效电平为低电平,我们可以通过下降沿来通知MCU:“可以从NRF24L01的接收缓冲区中读取数据了”或者“NRF24L01已经成功的完成了本次发送任务(成功的发送不只是发送出去数据还要通过天线接收到对方的应答信号才算一次成功发送)”或者“已经重复MAX_RT次发送数据,但是老是接收不到对方的应答”。RX_P_NO[3:1]表示着通过天线接收到数据的通道号。

0X08-发送检测寄存器

ARC_CNT[3:0]位表示着“已经重复发送了N次数据但是还是没有等待对方应答,等达到MAX_RX次时就认为此数据包丢失,数据包丢失计数位PLOS_CNT自加一”,PLOS_CNT则是表示“本次传输一共有几次丢包行为,如果超过15次(即丢了16个数据包)则立即清零PLOS_CNT[7:4],在从零开始自加一”。注意:每次建立新的信息传输连接时,发送检测寄存器的所有位都会自动被清空。

0X09-信道堵塞检测寄存器

当接收端检测到射频范围内的信号时将 CD 置高,否则 CD 为低。内部的 CD 信号在写入寄存器之前是经过滤波的,内部 CD 高电平状态至少保持 128us 以上。

在增强型ShockBurstTM模式中只有当发送模块没有成功发送数据时,推荐使用CD检测功能。如果发送端PLOS_CNT显示数据包丢失率太高时,可将其设置位接收模式检测CD值,如果CD为高(说明通道出现了拥挤现象),需要更改通信频道;如果CD为低电平状态(距离超出通信范围),可保持原有通信频道,但需作其它调整。

0X0A~0X0F-数据通道地址配置寄存器

nRF24L01 配置为接收模式时可以接收 6 路不同地址相同频率的数据。每个数据通道拥有自己的地址 并且可以通过寄存器来进行分别配置。数据通道是通过寄存器 EN_RXADDR 来设置的,默认状态下只有数据通道 0 和数据通道 1 是开启状态的。

每一个数据通道的地址是通过寄存器 RX_ADDR_Px 来配置的。通常情况下不允许不同的数据通道设 置完全相同的地址。数据通道 0 有 40 位可配置地址。数据通道 1~5 的地址为:32 位共用地址+各自的地址(最低字节)。 图 7 所示的是数据通道 1~5 的地址设置方法举例。所有数据通道可以设置为多达 40 位,但是 1~5 数据通道的最低位必须不同。

当从一个数据通道中接收到数据,并且此数据通道设置为应答方式的话,则 nRF24L01 在收到数据后 产生应答信号,此应答信号的目标地址为接收通道地址。

0X10-发送端地址配置寄存器

为什么说发送端地址寄存器中的地址必须与接收端地址寄存器中的地址一样呢?

当从一个数据通道中接收到数据,并且此数据通道设置为应答方式的话,则 nRF24L01 在收到数据后产生应答信号,此应答信号的目标地址为接收通道地址。

我们从发送端地址配置寄存器和接收端地址配置寄存器中所存储的数据就可以发现:NRF24L01作为接收设备时可以接收来自多个无线设备(外部设备须小于等于5台,因为nRF24L01 在确认收到数据后记录地址,并以此地址为目标地址发送应答信号。在发送端,数据通道 0 被用做接收应答信号,因此,数据通道 0 的接收地址要与发送端地址相等以确保接收到正确的应答信号)发送的信息,但是NRF24L01作为发送设备时仅可以向一个外部设备发送信息(因为发送端地址配置寄存器中只能设置一个目标设备地址)。

0X11~0X16-发送端数据宽度配置寄存器

6个通达的发送端数据宽度配置大同小异。

0X17-FIFO状态寄存器配置

NRF24L01操作指令

R_REGISTER 和 W_REGISTER 寄存器可能操作单字节或多字节寄存器。当访问多字节寄存器时首先要读/写的是最低字节的高位。NOP指令可以用来清空寄存器的值,我们这里以清空状态寄存器为例进行说明:

状态寄存器中RX_DR,TX_DS,MAX_RT都是写1才会清零,因此我们向状态寄存器写入NOP指令(0XFF,指令处于只读状态的RX_P_NO和TX_FULL位不会被影响),RX_DR,TX_DS,MAX_RT标志位都会被清零。

当然NOP也可以清空其他寄存器中的位数据,原因就在于有些位必须要我们人为的写入1改位才会被清零,写入NOP相当于正好一次性写入8个位的1,这样就清空了一个8位寄存器的可以清空的所有位(只读位由于不支持写操作因此不会受影响)。

NRF24L01工作模式

待机模式 I 在保证快速启动的同时减少系统平均消耗电流。在待机模式 I 下,晶振正常工作。在待机模式 II 下部分时钟缓冲器处在工作模式。当发送端 TX FIFO 寄存器为空并且 CE 为高电平时进入待机模式 II。在待机模式期间,寄存器配置字内容保持不变。

在掉电模式下,nRF24L01 各功能关闭,保持电流消耗最小。进入掉电模式后,nRF24L01 停止工作, 但寄存器内容保持不变。启动时间见表格 13。掉电模式由寄存器中 PWR_UP 位来控制。

各种工作模式的转换流程图如下所示:

我们不要求低功耗,因此NRF24L01上电后的初始工作状态我们设置为待机模式II,这里要注意NRF24L01动作时工作模式的变化:NRF24L01发送数据时首先从待机模式I转至发送模式,发送完成后再回到并一直呆在待机模式II等待下一次数据发送;NRF24L01接收数据时工作在接收模式,接收完数据后呆在待机模式I并等待下一次接收。

但是注意:nRF24L01 在掉电模式下转入发射模式或接收模式前必须经过 1.5ms 的待机模式。注意:当关掉电源 VDD 后寄存器配置内容丢失,模块上电后需重新进行配置。也就是说掉电模式不可以直接跳跃至发送/接收模式,必须中间经过待机模式I或者II。

增强型ShockBurstTM模式简介

nRF24L01 配置为增强型的 ShockBurstTM 发送模式下时,只要 MCU 有数据要发送,nRF24L01 就会启 动 ShockBurstTM模式来发送数据。在发送完数据后 nRF24L01 转到接收模式并等待终端的应答信号。如果 没有收到应答信号, nRF24L01 将重发相同的数据包,直到收到应答信号或重发次数超过 SETUP_RETR_ARC 寄存器中设置的值为止,如果重发次数超过了设定值,则产生 MAX_RT 中断。

只要收到确认信号,nRF24L01 就认为最后一包数据已经发送成功(接收方已经收到数据),把 TX FIFO 中的数据清除掉并产生 TX_DS 中断(IRQ 引脚置高)。

打包数据发送格式

各个字段的含义如下:

其中我们要着重说一下PID的作用:

每一包数据都包括两位的 PID(数据包识别)来识别接收的数据是新数据包还是重发的数据包。PID 识别可以防止接收端同一数据包多次送入 MCU。在发送方每从 MCU 取得一包新数据后 PID 值加一。PID 和 CRC 校验应用在接收方识别接收的数据是重发的数据包还是新数据包。如果在链接中有一些数据丢失 了,则 PID 值与上一包数据的 PID 值相同。如果一包数据拥有与上一包数据相同的 PID 值,nRF24L01 将 对两包数据的 CRC 值进行比较。如果 CRC 值也相同的话就认为后面一包是前一包的重发数据包而被舍弃。

这些码头的数据包对于发送端和接收端的意义又是什么呢?

对于接收端:无非就是判别“接收的数据是否是新的以防发送端重复发送一个数据”和“我接收的数据是不是对的呀”。对于第一个疑问,接收方对新接收数据包的 PID 值与上一包进行比较。如果 PID 值不同,则认为接收的数据包是新 数据包。如果 PID 值与上一包相同,则新接收的数据包有可能与前一包相同。对于第二个疑问,接收方必须确认 CRC 值是否相等,如果 CRC 值与前一包数据的 CRC 值相等,则认为是同一包数据并将其舍弃。

一定要注意:CRC 校验的长度是通过 SPI 接口进行配置的。一定要注意 CRC 计算范围包括整个数据包:地址、PID 和有效数据等。若 CRC 校验错误则不会接收数据包。

发送模式配置步骤

具体步骤

1. 首先配置PWR_UP=1以使NRF24L01处于待机模式I状态;

2. 配置发送的相关参数:当 MCU 有数据要发送时,接收节点地址(TX_ADDR)和有效数据(TX_PLD)通过 SPI 接口写入 nRF24L01。发送数据的长度以字节计数从 MCU 写入 TX FIFO。当 CSN 为低时数据被不断的写入,写入完成后进入一下一步的数据传输;

3. 再配置寄存器位 PRIM_RX 为低,发送TX FIFO缓冲区内的数据有两种方法:使得CE引脚先持续10us的高电平再将其置0,使得NRF24L01处于发送状态;或者直接将CE=1,那么NRF24L01也会将TX FIFO缓冲区内的数据发送出去。反正只要是CE置1后持续10us高电平,就会启动发送/接收功能;

4. 如果启动了自动应答模式(自动重发计数器不等于 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 计算在达到最大允许重发次数时仍没有发送成功的数据包个数;

5. 我们的代码中再发送完毕后还是保持CE=1,故发送结束后应该返回待机模式II,这里我们如果将CE=1持续10us再将CE=0,那么发送结束后就会回到待机模式I。

配置代码

//启动NRF24L01发送一次数据  
//txbuf:待发送数据首地址  
//返回值:发送完成状况  
u8 NRF24L01_TxPacket(u8 *txbuf)  
{  
    u8 sta;  
    SPI2_SetSpeed(SPI_BaudRatePrescaler_8);//spi速度为9Mhz(24L01的最大SPI时钟为10Mhz)  
    NRF24L01_CE=0;  
    NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF  32个字节  
    NRF24L01_CE=1;//启动发送  
    while(NRF24L01_IRQ!=0);//等待发送完成  
    sta=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值  
    NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志  
    if(sta&MAX_TX)//达到最大重发次数  
    {  
        NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器  
        return MAX_TX;  
    }  
    if(sta&TX_OK)//发送完成  
    {  
        return TX_OK;  
    }  
    return 0xff;//其他原因发送失败  
}  

代码解析

1. 在传输之前,我们必须设置一次发送的字节数量;

2. 然后,等待IRQ引脚输出中断则标志着可能发送成功,也可能发送失败(超过了最大重发次数对方仍未回复有效应答信号),我们读取状态寄存器的数值并保存在sta变量中;

3. 若接下来判断状态寄存器的MAX_TX位被置1,说明刚才的中断引脚的有效输出信号是由于发送失败(超过了最大重发次数对方仍未回复有效应答信号)导致的,此时函数返回MAX_TX;

4. 若接下来判断状态寄存器的TX_DS位被置1,说明刚才的中断引脚的有效输出信号是由于发送成功(对方接收到信息并且回复了有效的应答信号)引起的,此时函数返回TX_OK;

5. 若是其他原因引起的中断信号,则返回0XFF。

接收模式配置步骤

具体步骤

1. 首先配置PWR_UP=1以使NRF24L01处于待机模式I状态;

2. 设置接收的相关参数:ShockBurstTM接收模式是通过设置寄存器中 PRIM_RX 位为高来选择的。准备接收数据的通道必须被使能(EN_RXADDR 寄存器),所有工作在增强型 ShockBurstTM模式下的数据通道的自动应答功能是由(EN_AA 寄存器)来使能的,有效数据宽度是由 RX_PW_Px 寄存器来设置的, 使能自动确认信号,等到接收到数据后,通道0则会自动发送确认信号至发送设备;

3. 接收模式由设置 CE =1来启动;

4. 130us 后 nRF24L01 开始检测空中信息;

5. 接收到有效的数据包后(地址匹配、CRC 检验正确),数据存储在 RX_FIFO 中,同时 RX_DR 位 置高,并产生中断。状态寄存器中 RX_P_NO 位显示数据是由哪个通道接收到的;

6. MCU 将数据以合适的速率通过 SPI 口将数据读出。

配置代码

//启动NRF24L01发送一次数据  
//txbuf:待发送数据首地址  
//返回值:0,接收完成;其他,错误代码  
u8 NRF24L01_RxPacket(u8 *rxbuf)  
{  
    u8 sta;  
    SPI2_SetSpeed(SPI_BaudRatePrescaler_8); //spi速度为9Mhz(24L01的最大SPI时钟为10Mhz)  
    sta=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值  
    NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志  
    if(sta&RX_OK)//接收到数据  
    {  
        NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据  
        NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器  
        return 0;  
    }  
    return 1;//没收到任何数据  
}  

代码解析

1. 首先,我们设置SPI的通信速率,然后,我们读出状态寄存器里面的值(sta变量在这里起到记录状态寄存器中值的作用),然后再将已读出的值写入状态寄存器中,这样做是为了清楚状态寄存器中的标志位:

2. 然后我们从sta变量记录的状态寄存器的数值中提取数据:由于NRF24L01当前处于接收模式,因此我们这里只关心RX_DR位(标志着接收完成),只要RX_DR位置1就意味着我们的本次发送圆满完成,但是如果RX_DR=0,那么就意味着“我们还需要使用命令R_RX_PAYLOAD来轮询RX FIFO缓冲区,如果有数据NRF24L01则会向MCU发送数据”,并且在读取完后使用FLUSH_RX指令清空RX FIFO缓冲区。

切记:R_RX_PAYLOAD是读操作时使用的指令,而FLUASH_RX是写操作时用的指令(用于清空RX FIFO缓存区数据用的)。

这段代码的谜之操作就在于:不仅写入了FLUSH_RX指令还写入了0XFF指令?0XFF指令可不是用来清空缓冲区用的,这样写是由于我们通常的SPI写操作都是先写入一个寄存器地址在写入一个数据,但是我们这里的SPI指令只是8个位,后面不用再发送数据/指令,因此我们发送一个空操作0XFF来填补函数参数的空缺。

等下面的代码解析,你会发现“正点原子的代码中,使用不到的形参全部设置为0XFF”。

配置NRF24L01接收模式的相关参数

配置代码

//该函数初始化NRF24L01到RX模式  
//设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR  
//当CE变高后,即进入RX模式,并可以接收数据了  
void NRF24L01_RX_Mode(void)  
{  
    NRF24L01_CE=0;  
    NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);//写RX节点地址  
  
    NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01);    //使能通道0的自动应答  
    NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01);//使能通道0的接收地址  
    NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40);      //设置RF通信频率  
    NRF24L01_Write_Reg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度  
    NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启  
    NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG, 0x0f);//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式  
    NRF24L01_CE = 1; //CE为高,进入接收模式  
}  

代码解析

这段代码配置了NRF24L01接收模式所需的“自动应答功能是否使能”,“应答信号的发送功率和发送速度以及NRF24L01器件的发送/接收模式选择(由于NRF24L01工作在接收模式,因此这里使能接收模式)”,“CRC相关配置(使能/失能,CRC校验位的字节宽度,这些个位是根据发送端数据打包格式设置的,这些配置与发送端的配置一样就行)”,“通道0发送应答信号的无线通信频率”,“应答信号的目标接收端地址”,“收发双方通信的频率”。

这里要强调一下:读/写寄存器的SPI指令格式为“读/写指令+寄存器地址”。

配置NRF24L01发送模式的相关参数

配置代码

//该函数初始化NRF24L01到TX模式  
//设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,选择RF频道,波特率和LNA HCURR  
//PWR_UP,CRC使能  
//当CE变高后,即进入RX模式,并可以接收数据了  
//CE为高大于10us,则启动发送.  
void NRF24L01_TX_Mode(void)  
{  
    NRF24L01_CE=0;  
    NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址  
    NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK  
  
    NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01);     //使能通道0的自动应答  
    NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址  
    NRF24L01_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次  
    NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40);       //设置RF通道为40  
    NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);  //设置TX发射参数,0db增益,2Mbps,低噪声增益开启  
    NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG,0x0e);    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断  
    NRF24L01_CE=1;//CE为高,10us后启动发送  
}  

代码解析

此时由于NRF24L01配置为发送模式,因此这里就要求通道0接收来自目标接收端发送过来的成功应答信号(该信号表明“目标接收端已经成功接收到我方发给他的数据包”)。需要配置的通道0的相关参数:“通道0的接收地址”,“使能通道0的接收功能”,以及NRF24L01的相关属性:“配置发送通道的功率,速度”,“CRC校验的相关配置-CRC校验位的数据宽度,CRC使能/失能”,“掉电模式使能/失能”,“收发双方通信的频率”。

SPI发送/接收byte的函数

代码示例

//SPIx 读写一个字节  
//TxData:要写入的字节  
//返回值:读取到的字节  
u8 SPI2_ReadWriteByte(u8 TxData)  
{  
    u8 retry=0;  
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位  
    {  
        retry++;  
        if(retry>200)return 0;  
    }  
    SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据  
    retry=0;  
  
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)//检查指定的SPI标志位设置与否:接受缓存非空标志位  
    {  
        retry++;  
        if(retry>200)return 0;  
    }  
    return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据  
}  

代码解析

我们在nrf24l01.c的代码中经常遇见该函数的如下调用形式:

//在指定位置读出指定长度的数据  
//reg:寄存器(位置)  
//*pBuf:数据指针  
//len:数据长度  
//返回值,此次读到的状态寄存器值  
u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len)  
{  
    u8 status,u8_ctr;  
    NRF24L01_CSN = 0;           //使能SPI传输  
    status=SPI2_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值  
    for(u8_ctr=0; u8_ctr<len; u8_ctr++)pBuf[u8_ctr]=SPI2_ReadWriteByte(0XFF); //读出数据  
    NRF24L01_CSN=1;       //关闭SPI传输  
    return status;        //返回读到的状态值  
}  

这段代码中函数SPI2_ReadWriteByte的实参是0XFF,是一个NOP空操作,这是由于该函数集收发功能于一体,因此当调用该函数接收数据时,该函数的发送数据的部分应该被屏蔽,因此将参数TxData置为无效参数,即NOP空指令(0XFF)。

这里只重点剖析一下正点原子代码中与NRF24L01寄存器的配置相关的代码,其他代码结合模拟SPI的操作时序很容易就会看明白,期待大家共同进步和不吝赐教!

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肥肥胖胖是太阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值