NRF24L01双向传输(一对一)

NRF24L01双向传输(一对一)

简介

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

命令名 命令字 #数据字节 操作
R_REGISTER 000A AAAA 1到5,低位在前 读命令/状态寄存器
W_REGISTER 001A AAAA 1到5,低位在前 写命令/状态寄存器
R_RX_PAYLOAD 0110 0001 1到32,低位在前 读RX_Payload(1到32个字节)
W_TX_PAYLOAD 1010 0000 1到32,低位在前 写TX_Payload(1到32个字节)
FLUSH_TX 1110 0001 0 Flush TX FIFO,发送模式使用
FLUSH_RX 1110 0010 0 Flush RX FIFO,接收模式使用
REUSE_TX_PL 1110 0011 0 重用上次发送的载荷(数据包)
R_RX_PL_WID 0110 0000 1 读取RX payload宽度
W_ACK_PAYLOAD 1010 1PPP 1到32,低位在前 写载荷(这些载荷数据同应答包一起从PPP通道发出)
W_TX_PAYLOAD_NO ACK 1011 0000 1到32,低位在前 失能自动应答,用于发送模式
NOP 1111 1111 0 无操作,可用于读取状态寄存器

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

地址 助记符 复位值 类型 描述
00 CONFIG 配置寄存器
Reserved 7 0 R/w 只能为0
MASK_RX_DR 6 0 R/w 接收中断标志。0:使能
MASK_TX_D 5 0 R/w 发送中断标志。0:使能
MASK_MAX_RT 4 0 R/w 最大重发次数中断。0:使能
EN_CRC 3 1 R/w 使能CRC
CRCO 2 0 R/w CRC编码方案选择,1~2bytes
PWR_UP 1 0 R/w 1:上电,0:下电
PRIM_RX 0 0 R/w 收发控制。1:PRX;0:PTX
01 EN_AA 使能自动应答
Reserved 7:6 00 R/w 只能为0
ENAA_P5 5 1 R/w 使能通道5自动应答机制
ENAA_P4 4 1 R/w 使能通道4自动应答机制
ENAA_P3 3 1 R/w 使能通道3自动应答机制
ENAA_P2 2 1 R/w 使能通道2自动应答机制
ENAA_P1 1 1 R/w 使能通道1自动应答机制
ENAA_P0 0 1 R/w 使能通道0自动应答机制
02 EN_RXADDR 使能 RX 地址
Reserved 7:6 00 R/w 只能为0
ERX_P5 5 0 R/w 使能通道5
ERX_P4 4 0 R/w 使能通道4
ERX_P3 3 0 R/w 使能通道3
ERX_P2 2 0 R/w 使能通道2
ERX_P1 1 0 R/w 使能通道1
ERX_P0 0 0 R/w 使能通道0
03 SETUP_AW 设置地址宽度(所有通道的地址)
Reserved 7:2 000000 R/w 只能为0
AW 1:0 11 R/w 收发地址宽度,’11’-5bytes
04 SETUP_RETR 设置自动重发射机制
ARD 7:4 0000 R/w 自动重发延时’0001’-等待500us
ARC 3:0 0011 R/w 自动重发计数
05 RF_CH 设置RF频宽
Reserved 7 0 R/w 只能为0
RF_CH 6:0 0000010 R/w 设置RF的工作频宽
06 RF_SETUP RF设置寄存器
CONT_WAVE 7 0 R/w 为1使能连续传送载波
Reserved 6 0 R/w 只能为0
RF_DR_LOW 5 0 R/w 设置RF数据250kbps
PLL_LOCK 4 0 R/w 仅使用于测试?
RF_DR_HIGH 3 1 R/w ‘00’-1M,’01’-2M,’10’-250kbps
RF_PWR 2:1 11 R/w 设置RF输出增益 ’00’-18dbm,’11’-0dbm
Obsolete 0 无意义
07 STATUS 状态寄存器
Reserved 7 0 R/w 只能为0
RX_DR 6 0 R/w 数据就绪RXFIFO中断,写1清除
TX_DS 5 0 R/w 数据发送RXFIFO中断,写1清除
MAX_RT 4 0 R/w 最大字节重发中断,写1清除
RX_P_NO 3:1 111 R 数据通道号
TX_FULL 0 0 R TX FIFO满标志(满为1)
08 OBSERVE_TX 发射监测寄存器
PLOS_CNT 7:4 0 R 丢包计数
ARC_CNT 3:0 0 R 重发射数据包次数
09 RPD 接收电源检测
Reserved 7:1 000000 R 只能为0
RPD 0 0 R 接收电源检测
0A RX_ADDR_P0 39:0 E7E7E7E7E7 R/W 通道0接收地址(5个字节)
0B RX_ADDR_P1 39:0 C2C2C2C2C2 R/W 通道1接收地址(5个字节)
0C RX_ADDR_P2 7:0 C3 R/W 通道2接收地址(1个字节(低))
0D RX_ADDR_P3 7:0 C4 R/W 通道3接收地址(1个字节(低))
0E RX_ADDR_P4 7:0 C5 R/W 通道4接收地址(1个字节(低))
0F RX_ADDR_P5 7:0 C6 R/W 通道5接收地址(1个字节(低))
10 TX_ADDR 39:0 E7E7E7E7E7 R/W 发射地址,仅适用于PTX
11 RX_PW_P0 在通道0中RX_Payload的字节个数
Reserved 7:6 00 R/W 只能为0
RX_PW_P0 5:0 0 R/W 接收数据通道0字节数
12 RX_PW_P1 在通道1中RX_Payload的字节个数
13 RX_PW_P2 在通道2中RX_Payload的字节个数
14 RX_PW_P3 在通道3中RX_Payload的字节个数
15 RX_PW_P4 在通道4中RX_Payload的字节个数
16 RX_PW_P5 在通道5中RX_Payload的字节个数
17 FIFO_STATUS FIFIO状态寄存器
Reserved 7 0 R/W 只能为0
TX_REUSE 6 0 R 重用TX Payload
TX_FULL 5 0 R TX FIFO满标志
TX_EMPTY 4 1 R TX FIFO空标志
Reserved 3:2 00 R/W 只能为0
RX_FULL 1 0 R RX FIFO满标志
RX_EMPTY 0 1 R RX FIFO空标志
1C DYNPD 使能动态数据包长度
Reserved 7:6 0 R/W 只能为0
DPL_P5 5 0 R/W 使能pipe 5动态数据包长度
DPL_P4 4 0 R/W 使能pipe 4动态数据包长度
DPL_P3 3 0 R/W 使能pipe 3动态数据包长度
DPL_P2 2 0 R/W 使能pipe 2动态数据包长度
DPL_P1 1 0 R/W 使能pipe 1动态数据包长度
DPL_P0 0 0 R/W 使能pipe 0动态数据包长度
1D FEATURE R/W 特征寄存器
Reserved 7:3 0 R/W 只能为0
EN_DPL 2 0 R/W 使能动态数据包长度
EN_ACK_PAY 1 0 R/W 使能数据包应答
EN_DYN_ACK 0 0 R/W 使能写发送数据包非应答命令

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

接下来看看该芯片的时序图:
(1) 写时序
NRF24L01双向传输(一对一)_第3张图片
(2) 读时序
NRF24L01双向传输(一对一)_第4张图片
: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了。
  • 上面的主接收主程序没写全,不过相信大家应该知道怎么写,跟主发送的差不多。
  • 本程序在两个开发板上已经调试正常运行

你可能感兴趣的:(单片机,单片机,嵌入式,stm32)