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

完整驱动代码

此段为2022年3月14日应网友请求更新的,完整的驱动代码我发在另一篇博文里,请查看《nRF24L01+基于51单片机的驱动(库)实战代码分享

一、测试距离 0.软件为Keil5,不知道是否支持Keil4(如不行请手动新建Keil4工程) 1.单片机为STM32F103C8,采用硬件SPI 2.nRF24L01+采用3.3V供电,接线如下:       24L01+       STM32 CE   ——   PA3 CS   ——   PA4 SCK  ——   PA5 MISO ——   PA6 MOSI ——   PA7   IRQ未接(采用查询方式,如须用自加外部中断程序) 3.led灯为PC13控制,低电平亮(在User\led.c中修改GPIO);按键为PA0,按下后(接地)才开始发送,默认注释了,如需要可在程序中加上 4.My24L01_Tx为发送端程序,My24L01_Rx为接收端程序; 5.发送端约100ms发送一次,不要ACK;接收端每接收到一次led反转;将程序下载到单片机后可看到接收端led快速闪动,可将接收端的24L01在一定范围内走动,若led闪动变慢,则说明有丢包;led不闪,说明没有收到数据;因此大致可测得发送距离 6.24L01采用0频道,2Mbps, 0dBm, Address 3Bytes,实测距离大于10米(在不同的房间) 二、测试速率 0.软件为Keil5,不知道是否支持Keil4(如不行请手动新建Keil4工程) 
1.单片机为STM32F103C8,采用硬件SPI
 2.nRF24L01+采用3.3V供电,接线如下:
      24L01+       STM32
 CE   ——   PA3
 CS   ——   PA4
 SCK  ——   PA5
 MISO ——   PA6
 MOSI ——   PA7  
 IRQ未接(采用查询方式,如须用自加外部中断程序) 

3.led灯为PC13控制,低电平亮(在User\led.c中修改GPIO);按键为PA0,按下后(接地)才开始发送!!! 

4.My24L01_Tx为发送端程序,My24L01_Rx为接收端程序;

 5.接收端先上电,发送端上电后按下按键后才发送50KB(32B一帧 共32*50帧 32*32=1024=1K),发送端收到ACK后才发下一帧,发完后进入死循环,如须再发要先复位重新上电;接收端每收到一次led反转;(如未反转说明未成功发送,发送端接收端重新复位后再试)时间可看在接收程序中tim3Count(单位ms 16进制,定时器1ms中断)在Watch1中

 6.发送端我用的是延时等待查询STATUS寄存器,用外部中断IRQ应该会更好(未测试) 

7.24L01采用0频道,2Mbps, 0dBm, Address 3Bytes,实测速率约为50KB/s
评论 65
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

学为所用

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

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

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

打赏作者

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

抵扣说明:

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

余额充值