STM32模拟SPI控制NRF24L01发送和接收

STM32模拟SPI控制NRF24L01发送和接收

NRF24L01是一款2.4Ghz ISM频段无线收发芯片。NRF24L01模块可视为无需配对和连接型的WIFI或蓝牙模块。NRF24L01可工作于1发6收工作模式。一个NRF24L01模块工作于发送模式时,每次根据设定的接收端地址发送射频信号和数据;一个NRF24L01模块工作于接收模式时,可以接收来自1~6个发送端发来的射频信号和数据,对应内部的6个接收通道(Pipe)进行接收。一个NRF24L01模块可以根据场景进行发送和接收模式切换,从而分时进行发送和接收,此时对应半双工概念。

这里介绍STM32模拟SPI控制NRF24L01发送和接收的范例。市面上的模块也比较丰富:
在这里插入图片描述

NRF24L01特性

NRF24L01的特性如下所示:
在这里插入图片描述
NRF24L01可以和STM32供应相同的电压,从而管脚可以直接连接。

NRF24L01管脚连接

NRF24L01芯片的引脚图如下所示,红框选中的是芯片/模块和STM32进行信号连接的部分:
在这里插入图片描述

在这里插入图片描述
除了NRF24L01输出给外部MCU的状态中断IRQ(三种典型场景:Tx FIFO 发送完成、Rx FIFO接收到数据、发送达到最大重发次数),NRF24L01比标准SPI还多一个CE信号,在标准SPI的CSN片选信号有效时(低电平),由 CE 和 PWR_UP (CONFIG寄存器第1位)、 PRIM_RX(CONFIG寄存器第0位) 共同决定NRF24L01工作模式:
在这里插入图片描述
简而言之,PWR_UP设置值为1则芯片正常工作,PWR_UP设置值为0则芯片关电不干活。PRIM_RX设置芯片数据方向即发送还是接收。
在发送模式,如果CE信号一直拉高,则所有的待发送FIFO数据会被发送出去,从而缓冲区清空,而当有新的待发送FIFO数据出现时也会马上被发送滚出去。
在发送模式,如果CE信号以脉冲形式出现,则要求每次脉冲出现的时候CE高电平保持超过10us,则每次CE信号脉冲触发待发送FIFO数据发送一次。
在发送模式,如果CE信号以脉冲形式出现,当待发送FIFO数据已为空时,NRF24L01在CE高电平阶段进入待机状态Standby-II模式,在CE低电平阶段进入待机状态Standby-I模式。
在接收模式,在CE高电平阶段进入接收模式,在CE低电平阶段进入待机状态Standby-I模式。

NRF24L01常规工作模式

NRF24L01采用Enhanced ShockBurst常规工作模式,即经过配置,可以自动进行一些包处理功能,如用于时序同步的同步头(preamble),CRC校验码编码/解码,要求发送回包,接收回包,没有接收到回包时重发等。主要涉及到(发送)包格式的理解:
在这里插入图片描述
用于信号时序同步的preamble字节,不能出现多个连续的0或1:
在这里插入图片描述
随地址发送首位是0或者1,而发送不同的同步训练序列。
地址如果出现归零或归一位数多,则出错率上升。如果地址第一个字节和同步训练序列相同也增加出错率。
如果发送为3字节地址,而有两个接收设备,一个地址为3字节,另一个为5字节,而这两个接收设备前3字节地址与发送地址相同,则两个接收设备都会接收到数据,但接收地址为5字节的会取出错误的CRC字节,从而CRC校验失败而丢弃改包。

地址则是3~5个字节,这里解释一下地址,实际上这个地址是接收端的地址,接收端基于收到的这个地址判断是不是发给自己的。那么,包格式里发送端的地址在哪?答案是莫有。因此,当接收端收到发给自己的包,并且发送端要求接收端发送“接收到”的回包时,接收端只能向唯一的这个地址发送回包,当然接收端会错开接收时隙,发送回包时自己不接收,毕竟还是这个地址,不然搞成无限循环了,而发送端通过接收通道0接收回包时,接收通道0的地址也还是这个唯一的地址,这样才能接收到。所以搞来搞去搞复杂后再搞简单,就是作为发送端时:发送地址(接收端地址)和回包接收地址(发送端地址)相同,作为发送端时,回包接收地址应配置在接收通道0。
而作为接收端时,有6个通道可以配置成不同的地址,从而可以接收来自6个不同发送端的数据包。在配置功能方面,通道0可以自由配置地址,而通道1~5高位地址相同,只有尾部1个字节地址不同进行通道区分:
在这里插入图片描述
在这里插入图片描述

包控制字段则由如下部分组成:
在这里插入图片描述
Payload length指定发送的数据字节数
在这里插入图片描述
PID则是用于是否是重复接收到的包的识别(由于信号多径效应及发送端临界时序自动重发等)
在这里插入图片描述
当接收端收到数据包时,通过PID进行判断,如果是当前已接收到的包的PID之前的PID,则认为是多径传输的延时无效包从而丢弃。如果是当前已接收到的包的PID之后的PID,则仍为是正常包,CRC校验后接收。如果在异常情况下,丢失了几个包的接收,从而再次收到和已接收的包的PID相同的包,则进行CRC尾值比较,如果相同,则认为接收到的是之前的包,从而丢弃,如果CRC值不同,则认为是新包,从而CRC校验后接收。

通过NO_ACK位设置通知接收端是否发送回复包,而接收端也有设置是否允许发送回复包:
在这里插入图片描述
Payload是信息数据字节段,可以有0~32个字节。

CRC则是可选8位或16位CRC校验,发送端和接收端设置成同样即可:
在这里插入图片描述

NRF24L01兼容工作模式

NRF24L01可以工作于兼容模式,以便NRF24L01和其它芯片如nRF2401A, nRF2402,nRF24E1和nRF24E2通讯时采用。简单介绍如下:
在这里插入图片描述
包格式有不同:
在这里插入图片描述
在这里插入图片描述

NRF24L01收发流程

发送模式初始化:
1) CE置低
2) 写Tx模式节点地址(接收端地址)
3) 写Tx模式接收通道0的地址,用于自动应答过程接收回包
4) 使能Tx模式接收通道0的自动应答
5) 使能Tx模式接收通道0的接收地址
6) 设置自动重发间隔时间和最大自动重发次数
7) 设置RF通道
8) 配置TX射频参数(发射功率、无线速率)
9) 配置基本工作模式的参数(有无中断/CRC类型/POWER-ON/TX模式)
10)CE拉高,进入发送模式

发送操作:
1) CE置低
2) 写发送数据到FIFO
3) CE拉高,发送数据
4) 检查发送标识,处理异常

接收模式初始化:
1) CE置低
2)写Rx模式节点地址(0~5通道可选)
3)使能选定通道x的自动应答
4)使能选定通道x的接收地址
5)设置RF通信频率
6)选择通道x的有效数据宽度
7)设置RX射频参数(无线速率)
8)配置基本工作模式的参数(有无中断/CRC类型/POWER-ON/RX模式)
9)CE拉高,进入接收模式

接收操作:
1) 查询状态寄存器,检查是否接收到数据
2)如果接收到数据,清除状态寄存器数据接收到标识,及读取数据

如果不采用自动应答,就相当于UDP的底层工作模式,把数据包发出去就不管接收端是否收到了。不采用自动应答时,发送和接收端不使能自动应答即可。

NRF24L01操作指令

NRF24L01的操作指令涉及读寄存器,写寄存器,查询状态寄存器等:
在这里插入图片描述

NRF24L01寄存器定义

NRF24L01寄存器定义如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

NRF24L01发送接收范例

这里采用STM32CUBEIDE开发环境,两片STM32F103C6T6各自连接一片NRF24L01,一端发送,另一端接收,通过串口可以观察发送和接收状态。

TX端每秒发送一次数据给RX端,各自的UART1串口打印输出。

代码里通过如下开关选择是否采用自动应答模式:

#define Auto_Ack 1  //1: Enable Auto_Ack; 0: Disable Auto_Ack

发送端和接收端初始化函数支持配置通讯通道号(0~5),可以选择各个通道进行测试。

代码采用的微秒级延时函数实现,参考: STM32 HAL us delay(微秒延时)的指令延时实现方式及优化
UART串口printf打印输出配置,参考: STM32 UART串口printf函数应用及浮点打印代码空间节省 (HAL)

NRF24L01 工程配置

两片STM32F103C6T6的工程配置相同,首先建立基本工程并设置时钟:
在这里插入图片描述
采用内部时钟或外部时钟都可以:
在这里插入图片描述
配置USART1作为打印输出串口:
在这里插入图片描述
选择6个GPIO作为与NRF24L01接口的管脚, 4发送2接收配置:
在这里插入图片描述
保存并生成初始工程代码:
在这里插入图片描述

NRF24L01 工程库代码

建立nrf24l01.h的头文件:

#ifndef INC_NRF24L01_H_
#define INC_NRF24L01_H_

#include "main.h"

//NRF24L01 address and data load operation width
#define TX_ADR_WIDTH    5                               //TX address width byte number
#define RX_ADR_WIDTH    5                               //RX address width byte number
#define TX_PLOAD_WIDTH  32                              //TX data load width byte number
#define RX_PLOAD_WIDTH  32                              //RX data load width byte number

//NRF24L01 register operation command
#define SPI_READ_REG    0x00     //Read command and status registers by SPI_READ_REG | 5bits_register_address
#define SPI_WRITE_REG   0x20     //Write command and status registers by SPI_WRITE_REG | 5bits_register_address, Executable in power down or standby modes only.
#define RD_RX_PLOAD     0x61     //Read RX-payload: 1 – 32 bytes. A read operation always starts at byte 0. Payload is deleted from FIFO after it is read. Used in RX mode.
#define WR_TX_PLOAD     0xA0     //Write TX-payload: 1 – 32 bytes. A write operation always starts at byte 0 used in TX payload.
#define FLUSH_TX        0xE1     //Flush TX FIFO, used in TX mode
#define FLUSH_RX        0xE2     //Flush RX FIFO, used in RX mode Should not be executed during transmission of acknowledge, that is, acknowledge package will not be completed.
#define REUSE_TX_PL     0xE3     //Used for a PTX device Reuse last transmitted payload. TX payload reuse is active until W_TX_PAYLOAD or FLUSH TX is executed. TX payload reuse must not be activated or deactivated during package transmission.
#define R_RX_PL_WID     0x60     //Read RX payload width for the top R_RX_PAYLOAD in the RX FIFO. Note: Flush RX FIFO if the read value is larger than 32 bytes.
#define W_ACK_PAYLOAD   0xA8     //Used in RX mode by W_ACK_PAYLOAD | 3bits_PPP. Write Payload to be transmitted together with ACK packet on PIPE PPP. (PPP valid in the range from 000 to 101). Maximum three ACK packet payloads can be pending. Payloads with same PPP are handled using first in - first out principle. Write payload: 1– 32 bytes. A write operation always starts at byte 0.
#define W_TX_PAYLOAD_NOACK 0xB0  //Used in TX mode. Disables AUTOACK on this specific packet.
#define NOP             0xFF     //No Operation. Might be used to read the STATUS register

//NRF24L01 register address
#define NCONFIG          0x00

#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 STATUS          0x07

#define MAX_TX  	    0x10
#define TX_OK       	0x20
#define RX_OK   	    0x40

#define OBSERVE_TX      0x08
#define CD              0x09
#define RX_ADDR_P0      0x0A
#define RX_ADDR_P1      0x0B
#define RX_ADDR_P2      0x0C
#define RX_ADDR_P3      0x0D
#define RX_ADDR_P4      0x0E
#define RX_ADDR_P5      0x0F
#define TX_ADDR         0x10
#define RX_PW_P0        0x11
#define RX_PW_P1        0x12
#define RX_PW_P2        0x13
#define RX_PW_P3        0x14
#define RX_PW_P4        0x15
#define RX_PW_P5        0x16
#define FIFO_STATUS     0x17


uint8_t SPI_RW(uint8_t txd);
uint8_t NRF24L01_Write_Reg(uint8_t regaddr,uint8_t data);
uint8_t NRF24L01_Write_Buf(uint8_t regaddr, uint8_t *pBuf, uint8_t datalen);
uint8_t NRF24L01_Read_Reg(uint8_t regaddr);
uint8_t NRF24L01_Read_Buf(uint8_t regaddr,uint8_t *pBuf,uint8_t datalen);
uint8_t NRF24L01_Check(void);
void TX_Mode(uint8_t pipe_num);
void RX_Mode(uint8_t pipe_num);
uint8_t NRF24L01_TxPacket(uint8_t *txbuf);
uint8_t NRF24L01_RxPacket(uint8_t *rxbuf);

#endif /* INC_NRF24L01_H_ */

建立nrf24l01.c的源文件:

#include "nrf24l01.h"
#include "string.h"
extern void PY_Delay_us_t(uint32_t Delay);

#define  NRF24L01_SCK_H  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET)
#define  NRF24L01_SCK_L  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET)
#define  NRF24L01_MOSI_H  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET)
#define  NRF24L01_MOSI_L  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET)
#define  NRF24L01_MISO  HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14)

#define Set_NRF24L01_CSN  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET)
#define Clr_NRF24L01_CSN  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET)
#define Set_NRF24L01_CE  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET)
#define Clr_NRF24L01_CE  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET)
#define READ_NRF24L01_IRQ  HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)

#define SPI_SPEED_DELAY 1
uint8_t SPI_RW(uint8_t txd)
{
		uint8_t rxd=0x00;

		for (uint8_t i = 0; i < 8; i++)
		{

				NRF24L01_SCK_L;
				if (txd&0x80)
				{
						NRF24L01_MOSI_H;
				}
				else
				{
						NRF24L01_MOSI_L;
				}
				txd <<= 1;
				rxd<<=1;
				PY_Delay_us_t(SPI_SPEED_DELAY);
				NRF24L01_SCK_H;
				if(NRF24L01_MISO)
				{
					rxd++;
				}
				PY_Delay_us_t(SPI_SPEED_DELAY);
				NRF24L01_SCK_L;
		}

		return rxd;
}

#define Auto_Ack 1  //1: Enable Auto_Ack; 0: Disable Auto_Ack

uint8_t TX_ADDRESS[]= {0xCC,0xCC,0xCC,0xCC,0XC1};
uint8_t RX_ADDRESS[]= {0xCC,0xCC,0xCC,0xCC,0XC1};


//Write NRF24L01 register
uint8_t NRF24L01_Write_Reg(uint8_t regaddr,uint8_t data)
{
	uint8_t status;
    Clr_NRF24L01_CSN;                    //Enable SPI transceiver
  	status =SPI_RW(regaddr);             //Send register address
  	SPI_RW(data);                        //Send register data
  	Set_NRF24L01_CSN;                    //Disable SPI transceiver
  	return(status);       		         //Return status
}

//Write NRF24L01 register for specific length from buffer
uint8_t NRF24L01_Write_Buf(uint8_t regaddr, uint8_t *pBuf, uint8_t datalen)
{
	uint8_t status,uint8_t_ctr;
	Clr_NRF24L01_CSN;                                                       //Enable SPI transceiver
  	status = SPI_RW(regaddr);                                               //Send register address
  	for(uint8_t_ctr=0; uint8_t_ctr<datalen; uint8_t_ctr++) SPI_RW(*pBuf++); //Send register data
  	Set_NRF24L01_CSN;                                                       //Disable SPI transceiver
  	return status;                                                          //Return status
}

//Read NRF24L01 register
uint8_t NRF24L01_Read_Reg(uint8_t regaddr)
{
	uint8_t reg_val;
	Clr_NRF24L01_CSN;                //Enable SPI transceiver
  	SPI_RW(regaddr);                 //Send register address
  	reg_val=SPI_RW(0XFF);            //Read register data
  	Set_NRF24L01_CSN;                //Disable SPI transceiver
  	return(reg_val);                 //Return status
}

//Read NRF24L01 register for specific length to buffer
uint8_t NRF24L01_Read_Buf(uint8_t regaddr,uint8_t *pBuf,uint8_t datalen)
{
	uint8_t status,uint8_t_ctr;
  	Clr_NRF24L01_CSN;                                                                        //Enable SPI transceiver
  	status=SPI_RW(regaddr);                                                                  //Send register address
	for( uint8_t_ctr=0; uint8_t_ctr<datalen; uint8_t_ctr++ ) pBuf[uint8_t_ctr]=SPI_RW(0XFF);  //Read register data
  	Set_NRF24L01_CSN;                                                                        //Disable SPI transceiver
  	return status;                                                                           //Return status
}


//Check NRF24L01 existing, 0: existing; 1: not existing
uint8_t NRF24L01_Check(void)
{
	uint8_t buf[5]={ 0x5A, 0x5A, 0x5A, 0x5A, 0x5A };

	NRF24L01_Write_Buf(SPI_WRITE_REG+TX_ADDR, buf, 5);      //Write 5-byte address

	memset(buf, 0 , 5);
	NRF24L01_Read_Buf(SPI_READ_REG+TX_ADDR, buf, 5);        //Read back 5-byte address

	for(uint8_t i=0;i<5;i++)
	{
		if(buf[i]!=0x5A) return 1;                          //NRF24L01 not existing
	}

	return 0;		                                        //NRF24L01 existing
}

//NRF24L01 TX mode initialization
//pipe_num: 0 ~ 5 for RX side
void TX_Mode(uint8_t pipe_num)
{
	Clr_NRF24L01_CE;
    //Write TX mode node address
	TX_ADDRESS[4] = 0xC1 + pipe_num ;
  	NRF24L01_Write_Buf(SPI_WRITE_REG+TX_ADDR, (uint8_t*)TX_ADDRESS, TX_ADR_WIDTH);
  	//Write TX mode RX Pipe 0 address for Auto-ACK
  	RX_ADDRESS[4] = 0xC1 + pipe_num ;
  	NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0, (uint8_t*)RX_ADDRESS, RX_ADR_WIDTH);

    //Enable or disable Auto-Ack for TX mode RX Pipe 0
  	if(Auto_Ack) NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA,0x01);
  	else NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA,0x00);
    //Enable TX mode RX Pipe 0 address
  	NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01);
  	//Config auto-repeat interval: 86us+250us*(1+1); maximum auto-repeat times: 10
  	NRF24L01_Write_Reg(SPI_WRITE_REG+SETUP_RETR,0x1a);
    //Config RF channel: 40
  	NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,40);
  	//Config TX RF parameter: 0dbm output power, 1Mbps RF data rate
  	NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x07);  //0x27 : 250Kbps;  0x07 : 1Mbps; 0x0F : 2Mbps
  	//Config basic parameter: no interrupt, CRC enable, 16-bit CRC, power-on and TX mode
  	NRF24L01_Write_Reg(SPI_WRITE_REG+NCONFIG,0x0e);
    //Set CE high, 10us delay to start sending
	Set_NRF24L01_CE;
}

//NRF24L01 RX mode initialization
//pipe_num: 0 ~ 5
void RX_Mode(uint8_t pipe_num)
{
	Clr_NRF24L01_CE;
    //Write RX mode node address
	RX_ADDRESS[4] = 0xC1 + pipe_num ;
  	NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0+pipe_num, (uint8_t*)RX_ADDRESS, RX_ADR_WIDTH);

  	//Enable or disable Auto-Ack for RX node
  	if(Auto_Ack) NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA, 0x01<<pipe_num);
  	else NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA, 0x00);
  	//Enable RX node Pipe x address
  	NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR, 0x01<<pipe_num);
    //Config RF channel: 40
  	NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH, 40);
    //Config RX node Pipe x data width: 32
  	NRF24L01_Write_Reg(SPI_WRITE_REG+RX_PW_P0+pipe_num, RX_PLOAD_WIDTH);
  	//Config RX RF parameter: 1Mbps RF data rate
  	NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP, 0x07);
  	//Config basic parameter: no interrupt, CRC enable, 16-bit CRC, power-on and RX mode
  	NRF24L01_Write_Reg(SPI_WRITE_REG+NCONFIG, 0x0f);
  	//Set CE high, 10us delay to start receiving
  	Set_NRF24L01_CE;
}

//NRF24L01 TX
uint8_t NRF24L01_TxPacket(uint8_t *txbuf)
{
	uint8_t state;
	Clr_NRF24L01_CE;
	NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//Write data to TX Buffer with 32-byte
	Set_NRF24L01_CE;                                     //Set CE high, 10us delay to start sendin
	while(READ_NRF24L01_IRQ!=0);                         //Waiting for completion of TX process
	state=NRF24L01_Read_Reg(STATUS);                     //Read status register
	NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS,state);      //Clear TX_DS and MAX_RT interrupt flags
	if(state&MAX_TX)                                     //Maximum repeat times
	{
		NRF24L01_Write_Reg(FLUSH_TX,0xff);               //Clear TX FIFO register
		return MAX_TX;
	}
	if(state&TX_OK)                                      //TX OK
	{
		return TX_OK;
	}
	return 0xff;                                         //Unknown failure
}


//NRF24L01 RX
uint8_t NRF24L01_RxPacket(uint8_t *rxbuf)
{
	uint8_t state;
	state=NRF24L01_Read_Reg(STATUS);                           //Read status register
	NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS,state);            //Clear Data Ready interrupt flags
	if(state&RX_OK)                                            //RX OK
	{
		NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);   //Read data
		NRF24L01_Write_Reg(FLUSH_RX,0xff);                     //Clear RX FIFO register
		return 0;
	}
	return 1;                                                  //RX Got no data
}

NRF24L01 TX工程代码

在TX工程main.c文件里实现发送测试功能代码:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include "usart.h"
#include "nrf24l01.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
__IO float usDelayBase;
void PY_usDelayTest(void)
{
  __IO uint32_t firstms, secondms;
  __IO uint32_t counter = 0;

  firstms = HAL_GetTick()+1;
  secondms = firstms+1;

  while(uwTick!=firstms) ;

  while(uwTick!=secondms) counter++;

  usDelayBase = ((float)counter)/1000;
}

void PY_Delay_us_t(uint32_t Delay)
{
  __IO uint32_t delayReg;
  __IO uint32_t usNum = (uint32_t)(Delay*usDelayBase);

  delayReg = 0;
  while(delayReg!=usNum) delayReg++;
}
void PY_usDelayOptimize(void)
{
  __IO uint32_t firstms, secondms;
  __IO float coe = 1.0;

  firstms = HAL_GetTick();
  PY_Delay_us_t(1000000) ;
  secondms = HAL_GetTick();

  coe = ((float)1000)/(secondms-firstms);
  usDelayBase = coe*usDelayBase;
}
void PY_Delay_us(uint32_t Delay)
{
  __IO uint32_t delayReg;

  __IO uint32_t msNum = Delay/1000;
  __IO uint32_t usNum = (uint32_t)((Delay%1000)*usDelayBase);

  if(msNum>0) HAL_Delay(msNum);

  delayReg = 0;
  while(delayReg!=usNum) delayReg++;
}
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */


/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;

/* USER CODE BEGIN PV */
uint8_t uart_rxd[256];
uint8_t uart_txd[256];

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t NRF24L01_Status = 0xff;

uint8_t nrf24l01_rxd[32];
uint8_t nrf24l01_txd[32];

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  PY_usDelayTest();
  PY_usDelayOptimize();


  NRF24L01_Status = NRF24L01_Check();
  if(NRF24L01_Status==0)
  {
	  PY_Delay_us_t(1000000);
	  printf("\r\nNRF24L01 exists.\r\n");
  }
  else
  {
	  while(1)
	  {
		printf("\r\nNRF24L01 does't exist.\r\n");
		PY_Delay_us_t(1000000);
	  }
  }

  TX_Mode(0);

  for(uint32_t i=0; i<32; i++)
  {
	  nrf24l01_txd[i]=i;
  }
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		  NRF24L01_Status = NRF24L01_TxPacket(nrf24l01_txd);

		  if(NRF24L01_Status==TX_OK) printf("\r\nData was sent OK.\r\n");
		  else if(NRF24L01_Status==MAX_TX) printf("\r\nData was sent Overtime.\r\n");
		  else printf("\r\nData was sent Error.\r\n");

		  PY_Delay_us_t(1000000);

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13|GPIO_PIN_15, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);

  /*Configure GPIO pins : PB12 PB13 PB15 */
  GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pin : PB14 */
  GPIO_InitStruct.Pin = GPIO_PIN_14;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pin : PA8 */
  GPIO_InitStruct.Pin = GPIO_PIN_8;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pin : PA11 */
  GPIO_InitStruct.Pin = GPIO_PIN_11;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */


NRF24L01 RX工程代码

在RX工程main.c文件里实现接收测试功能代码:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include "usart.h"
#include "nrf24l01.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
__IO float usDelayBase;
void PY_usDelayTest(void)
{
  __IO uint32_t firstms, secondms;
  __IO uint32_t counter = 0;

  firstms = HAL_GetTick()+1;
  secondms = firstms+1;

  while(uwTick!=firstms) ;

  while(uwTick!=secondms) counter++;

  usDelayBase = ((float)counter)/1000;
}

void PY_Delay_us_t(uint32_t Delay)
{
  __IO uint32_t delayReg;
  __IO uint32_t usNum = (uint32_t)(Delay*usDelayBase);

  delayReg = 0;
  while(delayReg!=usNum) delayReg++;
}
void PY_usDelayOptimize(void)
{
  __IO uint32_t firstms, secondms;
  __IO float coe = 1.0;

  firstms = HAL_GetTick();
  PY_Delay_us_t(1000000) ;
  secondms = HAL_GetTick();

  coe = ((float)1000)/(secondms-firstms);
  usDelayBase = coe*usDelayBase;
}
void PY_Delay_us(uint32_t Delay)
{
  __IO uint32_t delayReg;

  __IO uint32_t msNum = Delay/1000;
  __IO uint32_t usNum = (uint32_t)((Delay%1000)*usDelayBase);

  if(msNum>0) HAL_Delay(msNum);

  delayReg = 0;
  while(delayReg!=usNum) delayReg++;
}
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;

/* USER CODE BEGIN PV */
uint8_t uart_rxd[256];
uint8_t uart_txd[256];

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t NRF24L01_Status = 0xff;

uint8_t nrf24l01_rxd[32];
uint8_t nrf24l01_txd[32];

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  PY_usDelayTest();
  PY_usDelayOptimize();

  NRF24L01_Status = NRF24L01_Check();
  if(NRF24L01_Status==0)
  {
	  PY_Delay_us_t(1000000);
	  printf("\r\nNRF24L01 exists.\r\n");
  }
  else
  {
	  while(1)
	  {
		printf("\r\nNRF24L01 does't exist.\r\n");
		PY_Delay_us_t(1000000);
	  }
  }

  RX_Mode(0);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
        NRF24L01_Status = NRF24L01_RxPacket(nrf24l01_rxd);
	    if(NRF24L01_Status==0)
	    {
	    	printf("\r\nget data:");
	    	for(uint32_t i=0; i<RX_PLOAD_WIDTH; i++)
	    	{
	    		printf(" %d", nrf24l01_rxd[i]);
	    	}
	    	printf("\r\n");
	    }

	    PY_Delay_us_t(10);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13|GPIO_PIN_15, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);

  /*Configure GPIO pins : PB12 PB13 PB15 */
  GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pin : PB14 */
  GPIO_InitStruct.Pin = GPIO_PIN_14;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pin : PA8 */
  GPIO_InitStruct.Pin = GPIO_PIN_8;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pin : PA11 */
  GPIO_InitStruct.Pin = GPIO_PIN_11;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

NRF24L01代码测试效果

发送端每次发送32个数据(从0~31),接收端收到后串口的打印输出为:
在这里插入图片描述

NRF24L01范例下载

STM32F103C6T6控制NRF24L01发收例程下载

–End–

  • 9
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

PegasusYu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值