NRF24L01双向传输(一对一)

NRF24L01双向传输(一对一)

简介

本文章记录两个NRF24L01无线模块实现双向传输的软件设计~
为什么可以双向传输呢?这要归功于它具有Enhanced ShockBurst,可以工作在主接收和主发送模式,在主接收方可以将我们自己的数据附在ACK Packet实现双向传输,所以此次双向传输会将两个NRF分别设置成主接收和主发送。
· 实物(模块非单独芯片)
在这里插入图片描述
网上最便宜的就这种不带运放的模块,大家如果想用NRF24L01的话,建议大家使用加了运放的模块 ,传输距离会比较远一点,没运放的话距离10米就开始出现丢包现象了。另外,进口的和国产的用起来距离一样,所以优先选择国产,因为进口的还贵点。
·芯片引脚定义说明(芯片)
在这里插入图片描述
·**数据包格式 **:
在这里插入图片描述
这里没必要介绍NRF24L01了,相信要用这芯片或者模块的朋友应该提前了解过了。
· 寄存器表
该芯片有命令寄存器和功能寄存器。在使用每一款芯片之前,要养成查看数据手册的习惯。这是我自己边看边翻译的,可能有翻译地不对的,见谅哈。
命令寄存器表:

命令名命令字#数据字节操作
R_REGISTER000A AAAA1到5,低位在前读命令/状态寄存器
W_REGISTER001A AAAA1到5,低位在前写命令/状态寄存器
R_RX_PAYLOAD0110 00011到32,低位在前读RX_Payload(1到32个字节)
W_TX_PAYLOAD1010 00001到32,低位在前写TX_Payload(1到32个字节)
FLUSH_TX1110 00010Flush TX FIFO,发送模式使用
FLUSH_RX1110 00100Flush RX FIFO,接收模式使用
REUSE_TX_PL1110 00110重用上次发送的载荷(数据包)
R_RX_PL_WID0110 00001读取RX payload宽度
W_ACK_PAYLOAD1010 1PPP1到32,低位在前写载荷(这些载荷数据同应答包一起从PPP通道发出)
W_TX_PAYLOAD_NO ACK1011 00001到32,低位在前失能自动应答,用于发送模式
NOP1111 11110无操作,可用于读取状态寄存器

上面的AAAAA就是后面即将看到的功能寄存器的映射地址。
功能寄存器地址映射表:

地址助记符复位值类型描述
00CONFIG配置寄存器
Reserved70R/w只能为0
MASK_RX_DR60R/w接收中断标志。0:使能
MASK_TX_D50R/w发送中断标志。0:使能
MASK_MAX_RT40R/w最大重发次数中断。0:使能
EN_CRC31R/w使能CRC
CRCO20R/wCRC编码方案选择,1~2bytes
PWR_UP10R/w1:上电,0:下电
PRIM_RX00R/w收发控制。1:PRX;0:PTX
01EN_AA使能自动应答
Reserved7:600R/w只能为0
ENAA_P551R/w使能通道5自动应答机制
ENAA_P441R/w使能通道4自动应答机制
ENAA_P331R/w使能通道3自动应答机制
ENAA_P221R/w使能通道2自动应答机制
ENAA_P111R/w使能通道1自动应答机制
ENAA_P001R/w使能通道0自动应答机制
02EN_RXADDR使能 RX 地址
Reserved7:600R/w只能为0
ERX_P550R/w使能通道5
ERX_P440R/w使能通道4
ERX_P330R/w使能通道3
ERX_P220R/w使能通道2
ERX_P110R/w使能通道1
ERX_P000R/w使能通道0
03SETUP_AW设置地址宽度(所有通道的地址)
Reserved7:2000000R/w只能为0
AW1:011R/w收发地址宽度,’11’-5bytes
04SETUP_RETR设置自动重发射机制
ARD7:40000R/w自动重发延时’0001’-等待500us
ARC3:00011R/w自动重发计数
05RF_CH设置RF频宽
Reserved70R/w只能为0
RF_CH6:00000010R/w设置RF的工作频宽
06RF_SETUPRF设置寄存器
CONT_WAVE70R/w为1使能连续传送载波
Reserved60R/w只能为0
RF_DR_LOW50R/w设置RF数据250kbps
PLL_LOCK40R/w仅使用于测试?
RF_DR_HIGH31R/w‘00’-1M,’01’-2M,’10’-250kbps
RF_PWR2:111R/w设置RF输出增益 ’00’-18dbm,’11’-0dbm
Obsolete0无意义
07STATUS状态寄存器
Reserved70R/w只能为0
RX_DR60R/w数据就绪RXFIFO中断,写1清除
TX_DS50R/w数据发送RXFIFO中断,写1清除
MAX_RT40R/w最大字节重发中断,写1清除
RX_P_NO3:1111R数据通道号
TX_FULL00RTX FIFO满标志(满为1)
08OBSERVE_TX发射监测寄存器
PLOS_CNT7:40R丢包计数
ARC_CNT3:00R重发射数据包次数
09RPD接收电源检测
Reserved7:1000000R只能为0
RPD00R接收电源检测
0ARX_ADDR_P039:0E7E7E7E7E7R/W通道0接收地址(5个字节)
0BRX_ADDR_P139:0C2C2C2C2C2R/W通道1接收地址(5个字节)
0CRX_ADDR_P27:0C3R/W通道2接收地址(1个字节(低))
0DRX_ADDR_P37:0C4R/W通道3接收地址(1个字节(低))
0ERX_ADDR_P47:0C5R/W通道4接收地址(1个字节(低))
0FRX_ADDR_P57:0C6R/W通道5接收地址(1个字节(低))
10TX_ADDR39:0E7E7E7E7E7R/W发射地址,仅适用于PTX
11RX_PW_P0在通道0中RX_Payload的字节个数
Reserved7:600R/W只能为0
RX_PW_P05:00R/W接收数据通道0字节数
12RX_PW_P1在通道1中RX_Payload的字节个数
13RX_PW_P2在通道2中RX_Payload的字节个数
14RX_PW_P3在通道3中RX_Payload的字节个数
15RX_PW_P4在通道4中RX_Payload的字节个数
16RX_PW_P5在通道5中RX_Payload的字节个数
17FIFO_STATUSFIFIO状态寄存器
Reserved70R/W只能为0
TX_REUSE60R重用TX Payload
TX_FULL50RTX FIFO满标志
TX_EMPTY41RTX FIFO空标志
Reserved3:200R/W只能为0
RX_FULL10RRX FIFO满标志
RX_EMPTY01RRX FIFO空标志
1CDYNPD使能动态数据包长度
Reserved7:60R/W只能为0
DPL_P550R/W使能pipe 5动态数据包长度
DPL_P440R/W使能pipe 4动态数据包长度
DPL_P330R/W使能pipe 3动态数据包长度
DPL_P220R/W使能pipe 2动态数据包长度
DPL_P110R/W使能pipe 1动态数据包长度
DPL_P000R/W使能pipe 0动态数据包长度
1DFEATURER/W特征寄存器
Reserved7:30R/W只能为0
EN_DPL20R/W使能动态数据包长度
EN_ACK_PAY10R/W使能数据包应答
EN_DYN_ACK00R/W使能写发送数据包非应答命令

如果NRF24L01用作具有Enhanced ShockedBurst 的PTX设备,那么将TX_ADDR寄存器里边的数值(地址)设置成通道0接收地址,使两个相同。

接下来看看该芯片的时序图:
(1) 写时序
在这里插入图片描述
(2) 读时序
在这里插入图片描述
:C7-C0 表示命令,S7~S0表示状态,Dx为数据位。
从时序上可以知道,读写时序是:先拉低片选CSN,在SCK的第一个上升沿开始传输数据;SCK在无效的状态下为低电平。这就给我们提示:在配置硬件SPI的时候要配置成SPI模式0(时钟极性0,时钟相位0)
那么现在根据当前了解到的知识,配置STM32的SPI,这里使用标准库函数:

//初始化24L01的IO口
//这个程序是在开发板正点原子精英板F103上面调试的,SPI2的总线上挂载两个设备,将另一个设备(W25Q)的片选拉高,不选中该设备,防止它的影响
#define W25Q_CSN_PIN GPIO_Pin_12
#define NRF_CSN_PIN GPIO_Pin_7
//初始化相关的IO口(片选以及SPI)
void MySPI2_Init(void)
{ 	
	GPIO_InitTypeDef GPIO_InitStructure;
  	SPI_InitTypeDef  SPI_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG, ENABLE);	 //使能PB,G端口时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//SPI2时钟使能 	
 
	
	GPIO_InitStructure.GPIO_Pin = W25Q_CSN_PIN;				 //PB12上拉 防止W25X的干扰
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);	//初始化指定IO
 	GPIO_SetBits(GPIOB,W25Q_CSN_PIN);//上拉				
 	

	GPIO_InitStructure.GPIO_Pin = NRF_CSN_PIN|NRF_CE_PIN;	//PG8 7 推挽 	  
 	GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化指定IO
  
	GPIO_InitStructure.GPIO_Pin  = NRF_IRQ_PIN;   
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PG6 输入  
	GPIO_Init(GPIOG, &GPIO_InitStructure);

	GPIO_ResetBits(GPIOG,NRF_IRQ_PIN|NRF_CSN_PIN|NRF_CE_PIN);//PG6,7,8上拉					 
		 
  /****************************SPI2_Init*******************************/
	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);  //PB13/14/15上拉  
	SPI_Cmd(SPI2, DISABLE); // SPI外设不使能

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //SPI设置为双线双向全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//SPI主机
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;		//时钟悬空低
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;	//数据捕获于第1个时钟沿
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由软件控制
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;		//定义波特率预分频的值:波特率预分频值为16
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式
	SPI_Init(SPI2, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
 
	SPI_Cmd(SPI2, ENABLE); //使能SPI外设 
	/********************************************************************/		 	 
}
//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最近接收的数据					    
}

至此,SPI的初始化配置和通行API函数已经写好,接下来define下片选以及CE使能的IO口:

#define NRF24L01_CE   PGout(8) //24L01片选信号
#define NRF24L01_CSN  PGout(7) //SPI片选信号	  
/******下面的中断引脚程序上不用,调试的时候用过而已******/ 
#define NRF24L01_IRQ  PGin(6)  //IRQ主机数据输入

接下来就进一步根据时序图来编写读写函数了:

/***************************************************************
* 写寄存器
****************************************************************/
uint8_t Write_Reg(uint8_t reg, uint8_t value)
{
	uint8_t status;
	NRF24L01_CSN = 0;					  /* 选通器件 */
	status = SPI2_ReadWriteByte(reg);  /* 写寄存器地址 */
	SPI2_ReadWriteByte(value);		  /* 写数据 */
	NRF24L01_CSN = 1;					  /* 禁止该器件 */
  return 	status;
}
/***************************************************************
* 读寄存器
****************************************************************/
uint8_t Read_Reg(uint8_t reg)
{
	uint8_t reg_val;
	NRF24L01_CSN = 0;					  /* 选通器件 */
	SPI2_ReadWriteByte(reg);			  /* 写寄存器地址 */
	reg_val = SPI2_ReadWriteByte(0);	  /* 读取该寄存器返回数据 */
	NRF24L01_CSN = 1;					  /* 禁止该器件 */
   return 	reg_val;
}

扩展至连续读连续写,那么还有下面函数:

/****************************************************************
* 写缓冲区----------------------------------
*****************************************************************/
uint8_t Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars)
{
	uint8_t i;
	uint8_t status;
	NRF24L01_CSN = 0;				        /* 选通器件 */
	status = SPI2_ReadWriteByte(reg);	/* 写寄存器地址 */
	for(i=0; i<uchars; i++)
	{
		SPI2_ReadWriteByte(pBuf[i]);		/* 写数据 */
	}
	NRF24L01_CSN = 1;						/* 禁止该器件 */
  return 	status;	
}
/****************************************************************
* 读缓冲区-------------------------------
****************************************************************/
uint8_t Read_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars)
{
	uint8_t i;
	uint8_t status;
	NRF24L01_CSN = 0;						/* 选通器件 */
	status = SPI2_ReadWriteByte(reg);	/* 写寄存器地址 */
	for(i=0; i<uchars; i++)
	{
		pBuf[i] = SPI2_ReadWriteByte(0); /* 读取返回数据 */ 	
	}
	NRF24L01_CSN = 1;						/* 禁止该器件 */
    return 	status;
}

至此,单片机与NRF芯片的通信手段已经搭好了,可以开始配置NRF24L01(+)了。
首先声明寄存器

#define TX_ADR_WIDTH    5 	 	
#define RX_ADR_WIDTH    5  
#define RX_PLOAD_WIDTH  50  //最大64	
#define TX_PLOAD_WIDTH  50  //最大64	
//***************************************NRF24L01寄存器指令*******************************************************
#define NRF_READ_REG        0x00  	// 读寄存器指令
#define NRF_WRITE_REG       0x20 	// 写寄存器指令
#define R_RX_PL_WID   	0x60    // 读取RX payload宽度指令
#define RD_RX_PLOAD     0x61  	// 读取接收数据指令
#define WR_TX_PLOAD     0xA0  	// 写待发数据指令
#define FLUSH_TX        0xE1 	// 冲洗发送 FIFO指令
#define FLUSH_RX        0xE2  	// 冲洗接收 FIFO指令
#define REUSE_TX_PL     0xE3  	// 定义重复装载数据指令
#define NOP             0xFF  	// 保留
//*************************************(nRF24L01)寄存器映射地址****************************************************
#define CONFIG          0x00  // 配置收发状态,CRC校验模式以及收发状态响应方式
#define EN_AA           0x01  // 自动应答功能设置
#define EN_RXADDR       0x02  // 可用信道设置
#define SETUP_AW        0x03  // 收发地址宽度设置
#define SETUP_RETR      0x04  // 自动重发功能设置
#define RF_CH           0x05  // 工作频率设置
#define RF_SETUP        0x06  // 发射速率、功耗功能设置
#define NRFRegSTATUS    0x07  // 状态寄存器
#define OBSERVE_TX      0x08  // 发送监测功能
#define CD              0x09  // 地址检测           
#define RX_ADDR_P0      0x0A  // 频道0接收数据地址
#define RX_ADDR_P1      0x0B  // 频道1接收数据地址
#define RX_ADDR_P2      0x0C  // 频道2接收数据地址
#define RX_ADDR_P3      0x0D  // 频道3接收数据地址
#define RX_ADDR_P4      0x0E  // 频道4接收数据地址
#define RX_ADDR_P5      0x0F  // 频道5接收数据地址
#define TX_ADDR         0x10  // 发送地址寄存器
#define RX_PW_P0        0x11  // 接收频道0接收数据长度
#define RX_PW_P1        0x12  // 接收频道1接收数据长度
#define RX_PW_P2        0x13  // 接收频道2接收数据长度
#define RX_PW_P3        0x14  // 接收频道3接收数据长度
#define RX_PW_P4        0x15  // 接收频道4接收数据长度
#define RX_PW_P5        0x16  // 接收频道5接收数据长度
#define FIFO_STATUS     0x17  // FIFO栈入栈出状态寄存器设置
#define DYNPD   		0x1C  // 使能动态数据包长度
#define FEATURE 		0x1D  // 主要使能DYNPD

下面就开始写NRF的初始化函数:


void NRF_Init(u8 mode, u8 ch)//初始化,mode=PTX/PRX,ch频宽
{
	MySPI2_Init();
	NRF24L01_CE = 0;
	Write_Reg(NRF_WRITE_REG + SETUP_AW, 0x03);
	Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(uint8_t *)RX_ADDRESS,5);
	Write_Buf(NRF_WRITE_REG+TX_ADDR,(uint8_t *)TX_ADDRESS,5);
	Write_Reg(NRF_WRITE_REG+EN_AA,0x01); 													//使能通道0的自动应答 
	Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01);											//使能通道0的接收地址 
	Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a);											//设置自动重发间隔时间:500us;最大自动重发次数:10次 2M波特率下
	Write_Reg(NRF_WRITE_REG+RF_CH,ch);														//设置RF通道为CHANAL
	Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f); 												//设置TX发射参数,0db增益,2Mbps,低噪声增益开启
	if(mode==1)		//PRX
	{
		Write_Reg(NRF_WRITE_REG + CONFIG, 0x0f);   		 // IRQ收发完成中断开启,16位CRC,主接收
		Write_Reg(FLUSH_TX,0xff);
		Write_Reg(FLUSH_RX,0xff);
		Write_Reg(NRF_WRITE_REG+0x1c,0x01);
		Write_Reg(NRF_WRITE_REG+0x1d,0x06);
	}
	else if(mode==2)							//TX2
	{
		Write_Reg(NRF_WRITE_REG + CONFIG, 0x0e);   		 // IRQ收发完成中断开启,16位CRC,主发送
		Write_Reg(FLUSH_TX,0xff);
		Write_Reg(FLUSH_RX,0xff);
		Write_Reg(NRF_WRITE_REG+0x1c,0x01);
		Write_Reg(NRF_WRITE_REG+0x1d,0x06);
	}
	NRF24L01_CE = 1;
}

数据传输涉及到发射数据包和接收数据包,另外,有必要检测NRF是否初始化正常,还需要检测环节。

//就是试着通过SPI写一串数据,然后再读出来,对比下,没问题返回1
u8 NRF_Check(void)//检查NRF模块是否正常工作
{ 
	u8 buf1[5]; 
	u8 i; 
	/*写入5个字节的地址. */ 
	Write_Buf(NRF_WRITE_REG+TX_ADDR,(uint8_t *)TX_ADDRESS,5); 
	/*读出写入的地址 */ 
	Read_Buf(TX_ADDR,buf1,5); 
	/*比较*/ 
	for(i=0;i<5;i++) 
	{ 
		if(buf1[i]!=TX_ADDRESS[i]) 
			break; 
	} 
	if(i==5)
		return 1; //MCU与NRF成功连接 
	else
		return 0; //MCU与NRF不正常连接 
}
/****************************************************************
*打包发送
*****************************************************************/
void TxPacket(uint8_t * tx_buf, uint8_t len)
{	
	NRF24L01_CE = 0;	
	//可向别的模块(地址)发射数据
	Write_Buf(NRF_WRITE_REG + RX_ADDR_P0, TX_ADDRESS, 5); // 装载接收端地址
	Write_Buf(WR_TX_PLOAD, tx_buf, len); 			 // 装载数据	
	NRF24L01_CE = 1;		 //置高CE,激发数据发送
}
void NRF_Send_Data(u8 *data , u8 length)
{
	TxPacket(data,length);
	//while(NRF24L01_IRQ!=0);
}
void Receive_Data(void)//检查是否有通信事件
{
	u8 sta = Read_Reg(NRF_READ_REG + NRFRegSTATUS);
	//while(NRF24L01_IRQ!=0);//等待发送完成
	
	//接收到数据包后,再对数据进行辨别
	if(sta & (1<<RX_DR))//接收中断
	{
		u8 rx_len = Read_Reg(R_RX_PL_WID);
		Read_Buf(RD_RX_PLOAD,NRF24L01RXDATA,rx_len);
		//Data_Receive_PROGRAM
		printf("...");
		Data_Receive_PRO();
	}
	if(sta & (1<<MAX_RT))
	{
		if(sta & 0x01)	//TX FIFO FULL
		{
			Write_Reg(FLUSH_TX,0xff);
		}
	}
	//很多单片机或者是其他芯片,清除状态为都是往相应位写1
	Write_Reg(NRF_WRITE_REG + NRFRegSTATUS, sta);//写1清除状态寄存器
	sta = Read_Reg(NRF_READ_REG + NRFRegSTATUS);
}
void Data_Receive_PRO(void)
{
	if((NRF24L01RXDATA[0] == 0x01) && (NRF24L01RXDATA[1]==0x02))//帧头
	{
		Rec_ADC_Raw_Val = ((uint16_t)NRF24L01RXDATA[2]<<8) + NRF24L01RXDATA[3];
	}
	else
		return;
}

至此,NRF24L01的驱动程序已经准备好了,接下来主程序调用:
(1)主发送主程序demo:

	#include "led.h"
	#include "sys.h"
	#include "usart.h"	 
	#include "24l01.h" 	
	uint16_t ms_2=0,ms_10=0,ms_500=0;
 	uint8_t Txdata_buffer1[64]={0x01,0x02,0x12,0x34,0x56,0x78,0x9A,0xBC,0xDE};
 	uint16_t Rec_ADC_Raw_Val;
 	static u8  blink_flag=0;
 
	
 	int main(void)
 	{	 	    
		u8 blink_flag2=0;
		//简单点,省去配置通用定时器的麻烦,直接用滴答定时器
		//注意这里开启滴答定时器中断就不要用正点原子的delay.c了
		//一个完美的程序尽量不去用延时,占用CPU资源
		//不精确延时可以自己写
		SysTick_Config(SystemCoreClock / 1000);//开启滴答定时器中断(72000000/1000)/72MHz = 1ms,即定时1ms中断一次
		uart_init(115200);	 	//串口初始化为115200
 		LED_Init();		  		//初始化与LED连接的硬件接口
 		//下面的函数第一个入口参数为PTX,表示设置为主发送
 		//其实,作为两个nRF实现双向通讯,另一方只需要设置为PRX即可。
 		NRF_Init(PTX,80);    	//初始化NRF24L01 
		while(NRF_Check()==0)
			printf("NRF24L01 disconnected!/n"); 
 		while(1)
		{
			if(ms_10>100)//每100ms发射一次数据包,闪一次灯,表明正常执行
			{
				ms_10 = 0;
				/*调试用****/
				if(blink_flag2 == 0)
				{
					PEout(5) = 0;
					blink_flag2 = 1;
				}
				else if(blink_flag2 == 1)
				{
					PEout(5) = 1;
					blink_flag2 = 0;
				}
				/*****只自加这个元素*******/
				Txdata_buffer1[2]++;
				NRF_Send_Data(Txdata_buffer1,7);
		}
		if(ms_2>2)//每2ms检查NRF是否有通信事件
		{
			Receive_Data();
			ms_2 =0;
		}
		if(ms_500>500)//每0.5秒led灯闪一次,串口打印接收到的ADC原始值。
		{
			if(blink_flag == 0){
				PBout(5) = 0;
				blink_flag = 1;
			}
			else{
				PBout(5) = 1;
				blink_flag = 0;
			}
			printf("the Raw ADC val is %d.\n",Rec_ADC_Raw_Val);
			ms_500 = 0;
		}
	}
}

上面的ms_2,ms_10,ms_500在文件stm32f10x_it.c中的滴答定时器中断更新。

void SysTick_Handler(void)
{
	ms_10++;
	ms_500++;
	ms_2++;
}

(2)主接收主程序demo:

//平台用的是潘多拉开发板stm32L475
int main(void)
{
    HAL_Init();
    SystemClock_Config();	//初始化系统时钟为80M
    delay_init(80); 		//初始化延时函数    80M系统时钟
	Usart1_Init(115200);
	ADC1IN3_Init();
    LED_Init();				//初始化LED
	NRF_Init(PRX,80);
	while(NRF_Check()==0);
	LCD_Init();//IIC接口屏
	LCD_Clear(WHITE);
	TIM2_Init(100 - 1, 8000 - 1);//10ms
	//Iwdg_Init();
    while(1)
    {
		LED_Function();//每0.5秒LED闪一次
		Schedule_100ms();//每100ms在显示屏上面,这里只显示了在主发送方那边自加的Txdata_buffer1[2]
		Schedule_Rec_data();//每10ms检查有没有接收到数据
		Schedule_Send_data();//每50ms发送数据包
    }
}
void Schedule_Send_data(void)
{
	uint8_t temp[10];
	if(count_Send_data>5)
	{
		temp[0] = 0x01;
		temp[1] = 0x02;
		temp[2] = adc_test/256;
		temp[3] = adc_test%256;
		NRF_Send_Data(temp, 4);
		count_Send_data = 0;
	}
}
void Schedule_Rec_data(void)
{
	if(count_Rec_data>1)
	{
		Receive_Data();
		count_Rec_data = 0;
	}
}
void Schedule_100ms(void)
{
	if(counter_100ms > 10)
	{
		Get_ADC();
		Get_AHT10_Data();
		LCD_ShowNum(100, 200, Rec_Test_Buf[0] , 5, 16);
		counter_100ms = 0;
	}
}

总结

  • 两个NRF24L01双向通讯,初始化程序除了配置寄存器的最低位不一样之外,其他都一样。(最低位切换PTX和PRX)
  • 一开始没设置收发地址宽度,导致两个模块通讯不上,查找了挺久,最后看了寄存器表试着设置了就OK了。
  • 上面的主接收主程序没写全,不过相信大家应该知道怎么写,跟主发送的差不多。
  • 本程序在两个开发板上已经调试正常运行
  • 5
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值