NRF24L01发送接收调试记录

详情请看以下博客,受益匪浅
nRF24L01发送接收调试应用笔记
开贴详细谈NRF24L01
由于网上教程很多很详细,我这里就简单记录一下基本内容及重要的部分

1、先来看下写寄存器操作

//SPI写寄存器
//reg:指定寄存器地址
//value:写入的值
u8 NRF24L01_Write_Reg(u8 reg,u8 value)
{
	u8 status;	
   	NRF24L01_CSN=0;                 //使能SPI传输
  	status =SPI2_ReadWriteByte(reg);//发送寄存器号 
  	SPI2_ReadWriteByte(value);      //写入寄存器的值
  	NRF24L01_CSN=1;                 //禁止SPI传输	   
  	return(status);       			//返回状态值
}
//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
//由于SPI的传输特性,读取和写入必须是同时进行的,所以这个函数为ReadWriteByte,即读和写共用同一个函数,读数据的时候,一般发送一个0XFF,OXFF代表是空操作
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_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01);

上面这个就是调用写寄存器函数,第一个参数就是我们要写入的寄存器的地址,需要注意的是这个写寄存器指令是有一定的格式的:如下图
在这里插入图片描述
即这个写寄存器指令的格式为:在这里插入图片描述
即这个指令的8位数据的高三位是指定的001,而低五位才是我们实际要写入的寄存器地址,所以我们一般的做法就是定义一个变量NRF_WRITE_REG = 0x200x20也就是0010 0000,即先保证高三位是固定的001,然后我们再使用这个NRF_WRITE_REG去加上我们实际要操作的寄存器地址,比如现在要操作寄存器EN_RXADDR,(相信大家找到的程序里面肯定都有各个寄存器的地址,都是已经宏定义好的),那么我们的做法就是NRF_WRITE_REG+EN_RXADDR,这样就完成了写寄存器指令的构成,然后就可以查手册写入具体参数了,接着往下看即可。
第二个参数就是要写入寄存器中的具体数据了,这个具体数据一般大家找到的程序也都是已经写好的,如果需要自己改动的话,就需要去看看NRF24L01的数据手册查看寄存器的每一位都代表啥意思了,NRF24L01中文参考手册请点击
比如上面写的EN_RXADDR在手册中如下
在这里插入图片描述
写入0x01就代表使能了接收数据通道0,具体数据可以根据自己需要进行更改。
比如常见的接收模式的配置如下

void NRF24L01_RX_Mode(void)
{
	NRF24L01_CE=0;	 
	//共有6个通道,每一个通道都可以设置为不同的地址(上电之后有默认的地址),RX_ADDR_P0代表通道0,这里重新设置地址,RX_ADDRESS代表为通道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共有6个通道
  	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);//配置基本工作模式的参数;高四位为0,代表开启所有中断。低四位:PWR_UP,EN_CRC,16BIT_CRC,接收模式 ,最低位控制发送模式或者接收模式,1为发送模式,0为接收模式
  	NRF24L01_CE = 1; //CE为高,进入接收模式 
}

其中第一行的NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);的作用是NRF24L01_Write_Reg函数的升级版,即对指定寄存器连续写入多个字节的数据,具体程序如下:

u8 NRF24L01_Write_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++)SPI2_ReadWriteByte(*pBuf++); //写入数据	 
  	NRF24L01_CSN = 1;       //关闭SPI传输
  	return status;          //返回读到的状态值
}

NRF24L01中有些寄存器是只能够写入1个字节的数据,比如EN_RXADDR(配置接受地址是否被允许)寄存器,就可以使用NRF24L01_Write_Reg函数,而有些寄存器可以写入多个字节的数据,比如RX_ADDR_P0(数据通道0接收地址,最大长度5个字节,低字节在前)寄存器就可以写入多个字节,这是就可以使用NRF24L01_Write_Buf函数了

2、再来看下读寄存器操作

//读取SPI寄存器值
//reg:要读的寄存器
u8 NRF24L01_Read_Reg(u8 reg)
{
	u8 reg_val;	    
 	NRF24L01_CSN = 0;          //使能SPI传输		
  	SPI2_ReadWriteByte(reg);   //发送寄存器号
  	reg_val=SPI2_ReadWriteByte(0XFF);//读取寄存器内容
  	NRF24L01_CSN = 1;          //禁止SPI传输		    
  	return(reg_val);           //返回状态值
}
sta=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值
NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据

读寄存器和写寄存器基本一样,先看下读寄存器的指令格式
在这里插入图片描述
即这个指令的8位数据的高3位固定为000,低5位位要读取的寄存器地址,即我们模仿写寄存器的操作,也可以定义一个变量NRF_WRITE_REG=0x00,0x00也就是0000 0000,即高3位保证为000,然后再加上寄存器地址,比如NRF_WRITE_REG+STATUS的作用就是去读取STATUS寄存器中的内容。当然了,由于NRF_WRITE_REG=0x00,所以说我们也可以不加上NRF_WRITE_REG,而是直接写入寄存器地址即可,(任何数加上0还是它本身),也就是sta=NRF24L01_Read_Reg(STATUS);,读取到的内容就会返回到sta中。

注意:在读写寄存器的程序中我们都使用到了同一个函数SPI2_ReadWriteByte

//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
//由于SPI的传输特性,读取和写入必须是同时进行的,所以这个函数为ReadWriteByte,即读和写共用同一个函数,读数据的时候,一般发送一个0XFF,OXFF代表是空操作
u8 SPI2_ReadWriteByte(u8 TxData)
{		
	u8 retry=0;				 	
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
	{
		//当发送缓冲区有数据时,会一直进入此循环,TXE表示发送空,RESET表示否,即发送非空,也就是有数据正在等待发送
		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标志位设置与否:接受缓存非空标志位
	{
		//当接收缓存空(没有接收到数据)时,会一直进入此循环,RXNE表示非空,RESET表示否,即接收空,也就是没有接收到数据
		retry++;
		if(retry>200)return 0;
	}	  						    
	//当接收缓冲区非空时,跳出上述循环,然后返回刚刚接收到的数据
	return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据					    
}

该函数的具体工作过程请看里面的注释,应该都能够理解啥意思。
因为SPI协议的特殊性,发送和接收是同时进行的,8个时钟过去之后,发送端把数据发给接收端的同时,接收端也会给发送端发送一次数据,即8个时钟双方就完成了一次数据的交互。所以我们才把写和读的函数使用一个函数SPI2_ReadWriteByte来完成。

3、SPI初始化注意事项

void SPI2_Init(void)
{
 	GPIO_InitTypeDef GPIO_InitStructure;
  	SPI_InitTypeDef  SPI_InitStructure;

	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE );//PORTB时钟使能 
	RCC_APB1PeriphClockCmd(	RCC_APB1Periph_SPI2,ENABLE );//SPI2时钟使能 	
 
	SPI_Cmd(SPI2, DISABLE); ////必须先禁用,才能改变MODE
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PB13/14/15复用推挽输出 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB
	
	GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置SPI工作模式:设置为主SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//设置SPI的数据大小:SPI发送接收8位帧结构
	
	//注意:以下两个步骤十分重要,千万不要写错
	
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;		//串行同步时钟的空闲状态为低电平  注意这里是空闲状态是低电平
	
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;	//串行同步时钟的第1个跳变沿数据被采样
	
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
	
	//下面为配置波特率,NRF24L01采用2FSK进行通信,所以要设定合适的波特率,可设置为1M、2M,有的还可以设置250kbps
	//SPI2在APB1时钟线上,APB1上的SPI2时钟频率为36MHz
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;		//定义波特率预分频的值:波特率预分频值为256
	
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式
	SPI_Init(SPI2, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
 
	SPI_Cmd(SPI2, ENABLE); //使能SPI外设
	
	SPI2_ReadWriteByte(0xff);//启动传输		 
}   

(1)两个NRF24L01进行通信,两个模式可以都设置成主机模式,这里的主机和从机配置模式应该不是我们所理解的两个NRF24L01进行通信--------必须一个配置成主机模式,另一个配置成从机模式;而是两个NRF24L01都可以配置成主机模式,然后再把两个NRF24L01一个设置成发送模式,而另一个设置成接收模式,应该是发送方习惯称之为主机,接收方习惯称之为从机,目前我的理解是这样的,不知道有没有什么问题。
(2)当配置成主机模式后,下面这两个配置也很重要,

	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;		//串行同步时钟的空闲状态为低电平  注意这里是空闲状态是低电平
	
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;	//串行同步时钟的第1个跳变沿数据被采样

反正我在调试的时候,这两项必须这样配置,一旦更改到其他的,NRF24L01就会一直自检失败,我也不知道为啥。

4、两个NRF24L01通信调试注意事项

记住,一定不要两个同时进行调试,因为这样你根本不知道问题是出在发送方还是接收方,我们一定要一个个的调试,一般先调试发送方(禁止自动应答,禁止通道x的接收地址,禁止重复发送,也就是只开启发送方,不开启和接收相关的任何东西),再调试接收方(调试完发送方之后,让发送方重复进行发送,然后接收方禁止自动应答,即接收方只开启接收通道即可);发送方和接收方调试成功与否一般都是通过查看STATUS寄存器或者NRF_FIFO_STATUS寄存器来判断的,具体步骤请看

nRF24L01发送接收调试应用笔记

5、目前我存在的问题

发送方经过查看STATUS寄存器和NRF_FIFO_STATUS寄存器都表示发送已经成功了,就说明发送端已经没问题了,但是接收方却一直收不到数据(接收方的STATUS寄存器一直为0x0e,也就是接收缓冲为空,也就是没有收到数据),目前还不知道是因为什么,等待后续继续调试中。
另一篇NRF24L01

发布了107 篇原创文章 · 获赞 343 · 访问量 40万+

猜你喜欢

转载自blog.csdn.net/qq_36554582/article/details/104860054