当前位置 : 主页 > 手机开发 > 无线 >

nRF24L01+不能接收或接收偶尔异常等问题实战分享

来源:互联网 收集:自由互联 发布时间:2021-06-10
nRF24L01+接收异常问题综述 在调试nRF24L01+无线收发模块的时候,最具标志性的环节就是在接收端可以收到数据。在实际应用调试中,会出现很多意想不到的情况,造成nRF24L01+模块接收端无

nRF24L01+接收异常问题综述

在调试nRF24L01+无线收发模块的时候,最具标志性的环节就是在接收端可以收到数据。在实际应用调试中,会出现很多意想不到的情况,造成nRF24L01+模块接收端无法收到发送端发出的数据。

根据以往对nRF24L01+模块的N多次调试的经验,总结大致可以分为如下几种情况:
现象1:一次也收不到发送端发送的数据
现象2:只能在发送端或接收端重新上电的时候收到一次
现象3:偶尔在发送完数据转为接收模式后就不能接收了
现象4:大功率带PA的模块工作一段时间就不能接收了
现象5:无规律偶发不能接收
下面将根据这5中现象分别有针对性的分享实战中的解决方法。

如何快速判断nRF24L01+通信失败问题是出在接收端

在调试nRF24L01+模块通信过程中,当发生通信失败时,如何才能快速确定问题是出在接收端,而不是发送端的问题呢?这里给他家推荐个方法:
首先使用厂家提供的测试代码,不做任何改动,烧录到两个模块中,如果硬件电路没有问题,一般都是可以正常发送接收的,如发生不能通信,请检查单片机型号变动带来的IO口设置是否正确,千万不要随意改动逻辑代码和nRF24L01+驱动部分代码,只查找IO口相关配置的宏定义修改正确即可。

如果这样可以通信,说明模块硬件电路没有问题。否则问题出在硬件电路上,仔细检查电路部分,参见下面的“正常接收数据时硬件电路必需的基本保障”。

当利用厂家提供的测试代码可以通信之后,把作为接收端的模块里的代码烧录成自己的代码。烧录前参照厂家测试代码中的接收配置部分,更改你项目中的频道、速率、地址、是否自动应答等,与测试项目保持一致。

经过这样调整后,如果还是收不到,那就是你的接收端代码有问题,参见下面的几种现象。

正常接收数据时硬件电路必需的基本保障

在调试代码前,首先要保证硬件电路是正常的,下面是必需具备的前提条件:
1、单片机与模块之间的IO口电路连接正常。单片机代码SPI脚序配置与电路连接实际相符。
2、nRF24L01+模块电源脚附近(越近越好)有滤波电容,最好是两个,一个104,一个22uF或以上。
3、处于接收模式时,CE引脚应为高电平(足够高,3.1V以上)。
4、电源电压稳定在3.3V,波纹低于80mV,波纹越小越好。

以上条件必需具备,否则不能接收那就再正常不过了。

现象1:一次也收不到发送端发送的数据

这种现象多数发生在我们最初调试模块的时候,那么我们要分两个方向来排除。

先排除硬件部分,参照上面“正常接收数据时硬件电路必需的基本保障”一节提到的,仔细检查有没有不能满足的。根据以往经验,最容易被大家忽视的就是nRF24L01+模块电源脚附近的两个电容,如果已经有了,那也更换一套新的试试,排除元件本身问题,最好用电容表或万用表电容档测量一下。如果有但是距离模块不是很近(应小于0.5CM的距离),最好直接在模块引脚上先临时焊上一个,以排除这个原因。

然后测量CE脚是否为3.3V,如果是大于0V且低于3.1V以下,说明CE电平不正常,应检查单片机IO口的上拉能力。如果单片机IO口模式是可以配置上拉模式的(如STC单片机),请使用推挽模式。如果单片机是不能配置上拉模式的,请加上拉电阻,以满足在CE脚拉高时,能维持到3.1V以上的电平。

如果硬件部分没有问题,CE脚也能正常上拉,那重点检查设置接收模式的代码,参考代码如下:

/*-----------------------------------------------------------------------------
函数名称:NRF24_RxMode
输入参数:pSelfAddr:本机硬件地址;ch:通信频道
功能描述:设置nRF24L01+工作在接收模式
------------------------------------------------------------------------------*/
void NRF24_RxMode(U8 *pSelfAddr, U8 ch){
    CE = 0;                                                                 // 拉低CE进入配置模式
    nRF24L01P_Write_Buf(WRITE_REG + RX_ADDR_P0, pSelfAddr, TX_ADR_WIDTH);   // 设置接收设备自己的通道0地址
    nRF24L01P_Write_Reg(WRITE_REG + EN_AA, 0x01);                           // 使能接收通道0自动应答
    nRF24L01P_Write_Reg(WRITE_REG + EN_RXADDR, 0x01);                       // 使能接收通道0
    nRF24L01P_Write_Reg(WRITE_REG + RF_CH, ch);                             // 选择射频通道为变量ch
    nRF24L01P_Write_Reg(WRITE_REG + RX_PW_P0, TX_PLOAD_WIDTH);              // 设置接收通道0有效数据宽度
    nRF24L01P_Write_Reg(WRITE_REG + RF_SETUP, RF_date_rate|dBm);            // 设置数据传输率、发射功率      
    nRF24L01P_Write_Reg(WRITE_REG + CONFIG, 0x0f);                          // 使能16位CRC校验,上电,接收模式
    nRF24L01P_Write_Reg(WRITE_REG + STATUS, 0xff);                          // 清除所有的中断标志位
    CE = 1;                                                                 // 拉高CE启动接收设备
}

在上述代码中,要注意:
pSelfAddr:接收端自己的硬件设备地址,要与发送端的发送目标地址一致,否则收不到。
ch:射频工作频道号,可设置值为1-125,要与发送端保持一致,否则收不到。
RF_date_rate:数据的传输速率,可设置值为0x00、0x08、0x20,分别代表1Mbds、2Mbds、250Kbds,必须与发送端的设置保持一致,否则收不到。
dBm:发射功率,在接收模式下没有实际作用,但设置值不能大于0x07,否则影响到速率的配置位。
TX_ADR_WIDTH:描述地址宽度,必须与发送端一致,建议测试时设置为5。
TX_PLOAD_WIDTH:描述传输数据宽度,必须与发送端一致,建议测试时设置为32。

现象2:只能在发送端或接收端重新上电的时候收到一次

原因1:一直发送相同的数据包,也就是每次发送的数据都是相同。
nRF24L01+的接收端首先是根据PID位来区分数据包是否相同的,如果第二次接收的PID与第一次相同,那就视为数据重发,会直接丢弃不处理,也不会产生接收成功的中断。
数据手册中说这个PID是每次写发送FIFO的时候会自动累加,也就是每写一次就会改变一次,但在多次的实验中(使用的是SI24R1模块),发现如果两次写入的是相同的内容,PID并没有变化,接收端会直接抛弃后面接收到的相同数据。
这样就产生了只能接收成功一次,然后就收不到了这个现象。重新上电后,所有寄存器的值都是重新配置的,之前的PID也不一样了,所以就可以再接收到一次,之后就又不能接收了。
解决方法:在你发送的数据内容中选一个字节让它每次都是变的,就不会出现了。

原因2、发送端发送速度比较快而连续,接收端处理比较慢切没有清接收FIFO。
发送端连续发送3组数据后,接收端的96字节缓冲器就会填满,这个时候如果没有读出,那就不会再接收了。发送端仍然以同样的速度再发送几个,在这个时候接收端虽然读出一组32个字节,如果处理的比较慢,那很快接收缓冲区可能被又填满了。
而接收端处理完逻辑代码再去查接收数据的时候,如中间有过模式转换,清除了中断标志,因没有再产生接收成功的中断标志(使用中断IRQ首先判断接收成功的,接收缓冲区满就不会再产生中断了),则认为没有收到数据,所以就再也收不到数据了。
这样就产生了只能接收一次就收不到了这个现象,重新上电后,所有寄存器的值都是重新配置的,中断和缓冲器都是被清除过,所以可以再接收。
解决方法:在每次收到数据读出之后,在处理逻辑完成后,如果经过接收发送模式转换的,转换后清除一下接收FIFO。

原因3、没有及时清除中断标志,接收一次后IRQ一直维持在低电平。
如果单片机使用外部中断,在接收到一次有效数据没有清除中断标志的话,再收到有效数据就不会再产生下一个中断。
解决方法:接收成功一次,清除一次中断。
下面单片机通过查询IRQ的方式识别是否收到数据的代码,结合中断与查询两种方式:

/*-----------------------------------------------------------------------------
函数名称:RF24_RxData
返回数据:是否收到数据,1收到,0未收到
功能描述:nRF24L01P+判断是否收到数据,若收到直接取出,应在主循环中调用
------------------------------------------------------------------------------*/
bit RF24_RxData(){
    U8 state;

    if(IRQ == 0){                                                       //有中断触发,提高CPU效率,减少SPI通讯量
        state = nRF24L01P_Read_Reg(STATUS);                             //读取状态寄存器的值       
        nRF24L01P_Write_Reg(WR_STATUS, state);                          //清除RX_DS中断标志
        if(state & RX_DR)                                               //接收到数据
        {
            nRF24L01P_Read_Buf(RD_RX_PLOAD, RF24Buf, TX_PLOAD_WIDTH);   //读取数据
            nRF24L01P_Write_Reg(FLUSH_RX,0xFF);                         //清除RX FIFO寄存器
            return TRUE; 
        }
    }                                                                   
    return FALSE;                                                       //没收到任何数据
}

以上代码注意“清除RX_DS中断标志”和“清除RX FIFO寄存器”的作用,保证每次设置为接收的时候,都能使nRF24L01+全新开始接收。

现象3:偶尔在发送完数据转为接收模式后就不能接收了

原因1、发送失败后,没有清除发送FIFO寄存器,造成转接收模式时异常。
nRF24L01+在发送达到最大发送次数仍然没有收到ACK的时候(启用ACK的情况下),是不会自动清除发送缓冲区的。而在发送模式转到待机模式的时候,根据手册的状态图,必须是发送缓冲区没有数据的情况下,CE=0才可进入待机模式。在待机模式,才可以设置为接收模式。但是数据手册中文字介绍部分又说任何状态下,只要CE=0就可以进入待机模式,出现了描述矛盾。
我们在调试的时候,遇到了这个问题,经过N多次实验,在发送缓冲区还有数据的情况下,直接转接收模式,是会不稳定的,所以在发送结束后,如果是发送次数达到了最大值不再继续发送,一定要记得清洗TX_FIFO。
解决方法:发送完判断发送状态,如果是达到最大发送次数,则清洗TX_FIFO。
参考代码如下:

/*-----------------------------------------------------------------------------
函数名称:RF24_TxData
输入参数:*buf:待发送的数据缓冲区指针
返回数据:是否发送成功,1发送成功,0发送失败
功能描述:nRF24L01P+发送数据
------------------------------------------------------------------------------*/
bit RF24_TxData(U8 *buf){
    U8 state;

    CE = 0;                                                 //使能24L01配置
    nRF24L01P_Write_Buf(WR_TX_PLOAD, buf, TX_PLOAD_WIDTH);  //写数据到TX FIFO,32个字节
    CE = 1;                                                 //使能发送     

    while(IRQ == 1);                                        //等待发送完成
    state=nRF24L01P_Read_Reg(STATUS);                       //读取状态寄存器的值    
    nRF24L01P_Write_Reg(WR_STATUS, state);                  //清除TX_DS或MAX_RT中断标志
    if(state&MAX_RT)
        nRF24L01P_Write_Reg(FLUSH_TX, 0xff);                //达到最大发送次数,清除TX FIFO寄存器
    if(state&TX_DS)
        return TRUE;                                        //发送完成  
    return FALSE;                                           //发送失败
}

原因2、在发送完一次就自动转为接收模式的代码逻辑下,循环发送时,中间收到3次及以上的数据。
因在发送一组数据就会自动置为接收(这样写的逻辑也是有好处的,可以避免某些地方忘记置为接收),那在发送下一组数据之前,是有一段时间间隔的,这个时候如果有数据进来,是可以接收的。但是上层代码并没有处理接收数据,也就是没有去读,而是循环发送完才会进入处理接收阶段。这样如果在循环发送期间受到3次数据,接收缓冲区就满了,而产生的中断也在每次转换模式的时候都被清除了。等循环结束,再判断是否有接收到数据的时候,因接收缓冲区满而不能再产生接收中断了,所以不能再收到数据了。
解决方法 :在每次设置接收模式的时候,清洗一下RX_FIFO。

原因3、置接收模式的代码没有操作所有相关寄存器,其他代码更改过相关寄存器。
在本次置为接收模式之前,其他代码可能更改过与接收相关的寄存器,造成与发送端不一致或更改为错误的值,造成不能正常接收。
解决方法:置为接收模式的函数,完整的操作一遍相关寄存器,这样可以规避这种问题。

现象4:大功率带PA的模块工作一段时间就不能接收了

经过测试多个厂家的多款不同nRF24L01+模块,发现带屏蔽罩并带功率放大的模块,在近距离密集应用的时候,损坏率比较高。PA会先过热然后接收不正常,接收模式下电流大增,表面温度会烫手。
建议劲量不要使用带PA的,如果必需使用,不要密集应用,尽量使用板载天线并不带屏蔽罩的。
这个是目前的实际应用测试经验,不代表全部是这样,不排除有的厂家带屏蔽罩的会更好用。

现象5:无规律偶发不能接收

以上问题都排除的话,如果还是偶尔会发生不能接收的情况,那就重点检查电源和滤波电容,建议更换一个电源,保证电源的低波纹,电压的稳定性要好。滤波电容质量要好,要尽量靠近模块,越近越好。

关于nRF24L01+作为接收端的的常见问题,先分享到这里,其他方面另外分享。

网友评论