zoukankan      html  css  js  c++  java
  • 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+作为接收端的的常见问题,先分享到这里,其他方面另外分享。

  • 相关阅读:
    LCA+链式前向星模板
    truffle编译合约常见问题及其在私链上的部署与交互
    RMQ入门解析
    最短路_搜索
    无向图边双联通分量+缩点
    有向图+强联通分量
    染色法判二分
    邻接表存图
    贪心算法
    贪心算法
  • 原文地址:https://www.cnblogs.com/xueweisuoyong/p/11084536.html
Copyright © 2011-2022 走看看