[STM32CubeMX] NRF24L01 module realizes "1 to 1" and "1 to many" wireless communication

  Hello everyone, I am Xiaozheng. In this article, I will explain in detail the STM32CubeMX configuration process for the NRF24L01 module to realize "1-to-1" and "1-to-many" wireless communication, so that friends who are ready to learn the HAL library can better understand how to configure STM32CubeMX.

1. Chip introduction

  NRF24L01 is a wireless communication chip recently produced by NORDIC. It adopts FSK modulation and integrates NORDIC's own Enhanced Short Burst protocol inside. It can realize point-to-point or 1-to-6 wireless communication . The wireless communication speed can reach 2Mbps. NORDIC provides the GERBER file of the communication module, which can be processed and produced directly. Embedded engineers or microcontroller enthusiasts only need to reserve 5 GPIOs and 1 interrupt input pin for the microcontroller system, and they can easily realize the wireless communication function, which is very suitable for building wireless communication functions for the MCU system.

(1) Functional block diagram of NRF24L01

NRF24L01 functional block diagram
  The block diagram of NRF24L01 is shown in the figure above. From the perspective of microcontroller control, we only need to pay attention to the six control and data signals on the right side of Figure 1, which are CSN, SCK, MISO, MOSI, IRQ, and CE.
NRF24L01 pin diagram

  • CSN: chip select line of the chip, when CSN is low level, the chip works
  • SCK: chip-controlled clock line (SPI clock)
  • MISO: chip control data line, master controller output, slave device data input
  • MOSI: chip control data line, master controller input, slave device data output
  • CE: The mode control line of the chip. When CSN is low, CE cooperates with the CONFIG register of NRF24L01 to determine the state of NRF24L01
  • IRQ: interrupt signal. In the process of wireless communication, MCU mainly communicates with NRF24L01 through IRQ

It becomes low in the following three situations:
  a) Tx FIFO has finished sending and received ACK (when ACK is enabled)
  b) Rx FIFO has received data
  c) reached the maximum number of retransmissions

(2) NRF24L01 state machine

  The firmware programming work for NRF24L01 mainly refers to the state machine of NRF24L01. There are mainly the following states:
  Power Down Mode: Power down mode
  Tx Mode: Transmit mode
  Rx Mode: Receive mode
  Standby-1 Mode: Standby 1 mode
  Standby-2 Mode: Standby 2 mode

(3) NRF24L01 working mode

NRF24L01 working mode
  The working mode is divided into four modes: receiving mode, sending mode, standby mode, and power-down mode .
  The transceiver mode is divided into three types: Enhanced ShockBurstTM transceiver mode (supports automatic ACK and automatic retransmission), ShockBurstTM transceiver mode and direct transceiver mode. The transceiver mode is determined by the device configuration word.
  Usually use Enhanced mode. In the Enhanced ShockBurstTM transceiver mode, NRF24L01 automatically processes the header and CRC check code. When receiving data, automatically remove the header and CRC check code. When sending data, the prefix and CRC check code are automatically added. In the sending mode, set CE to be high, at least 10us, after the sending process is completed. , this mode requires the terminal device to have a response signal after receiving the data, so that the sender can detect whether there is data loss, and resend the data once it is lost.

(4) Configuration process of TX and RX

  Here, the Tx and Rx configuration and communication process of Enhanced ShockBurstTM communication mode are adopted.

4.1 TX mode initialization process

TX mode initialization process

4.2 RX mode initialization process

insert image description here


2. Hardware preparation

(1) Required hardware

  • Chip: STM32F103C8T6 system board
  • Power supply: USB cable
  • Module: NRF24L01

(2) Hardware connection:

  • PB4 - IRQ pin of NRF24L01
  • PB5 - CSN pin of NRF24L01
  • PB6 - CE pin of NRF24L01
  • PB13 - SCK pin of NRF24L01
  • PB14 - MISO pin of NRF24L01
  • PB15 - MOSI pin of NRF24L01

3. STM32CubeMX configuration process

1.1 Tools used:

  • Chip: STM32F103C8T6
  • IDE: MDK-Keil software
  • STM32F1xxHAL library

1.2 Knowledge summary:

  • STM32CubeMX creates LED, KEY, SPI routines
  • Keil software programming

1.3 Project creation
1. Chip selection
  chip: STM32F103C8T6 (choose according to your own board)

chip select
2. Set Debug
  to select Serial Wire, here is the option you must choose when you want to debug in keil

Set Debug
3. Set RCC
  to set high speed External clock HSE Select external clock source

set RCC
4. SPI2 configuration
  SPI2 is set to full-duplex master mode, NRF24L01 requires the clock rate to be set below 8M

Set SPI parameters
5. USART1 configures
  asynchronous transceiver, default baud rate: 115200 Bit/s, open serial port 1 asynchronous transceiver Interrupt

USART1 configuration
6, GPIO configuration
  NRF24L01 pin: IRQ is set to pull-up input, CSN and CE are both set to push-pull output; the
  LED pin of the board I use is PC13, the initial level is high level, the purpose is to observe the small light If the button is on and off to judge whether the communication is successful; the button is set to PA0, and the external interrupt is triggered by

insert image description here
the
  falling

insert image description here
edge
  . Clock is 72MHzs

insert image description here

9. The final step of project creation

  • set project name
  • Choose your IDE
    insert image description here

10. Output file

  • ① place: copy the .c and .h of the file used
  • ② place: each function produces independent .c and .h files
    insert image description here

11. Create a project file
  and click GENERATE CODE to create a project

12、设置Use MicroLIB
  Use MicroLIB是KEIL自带的一个简易的库,例如你用printf函数的时候,就会从串口1 输出字符串,直接默认定向到串口1,因为代码后续要用到printf,所以这里需要选上

insert image description here

13、配置下载工具
  这里我们需要勾选上下载后直接运行,然后进行一次编译

insert image description here

14、NRF24L01.h文件
  在Core/inc文件中添加NRF24L01.h文件

insert image description here

#ifndef __NRF24L01_H
#define __NRF24L01_H

//NRF24L01 驱动函数
/****************************************************************************************************/
//NRF24L01寄存器操作命令
#define SPI_READ_REG    0x00  //读配置寄存器,低5位为寄存器地址
#define SPI_WRITE_REG   0x20  //写配置寄存器,低5位为寄存器地址
#define RD_RX_PLOAD     0x61  //读RX有效数据,1~32字节
#define WR_TX_PLOAD     0xA0  //写TX有效数据,1~32字节
#define FLUSH_TX        0xE1  //清除TX FIFO寄存器.发射模式下用
#define FLUSH_RX        0xE2  //清除RX FIFO寄存器.接收模式下用
#define REUSE_TX_PL     0xE3  //重新使用上一包数据,CE为高,数据包被不断发送.
#define NOP             0xFF  //空操作,可以用来读状态寄存器	 
//SPI(NRF24L01)寄存器地址
#define CONFIG          0x00  //配置寄存器地址;bit0:1接收模式,0发射模式;bit1:电选择;bit2:CRC模式;bit3:CRC使能;
//bit4:中断MAX_RT(达到最大重发次数中断)使能;bit5:中断TX_DS使能;bit6:中断RX_DR使能
#define EN_AA           0x01  //使能自动应答功能  bit0~5,对应通道0~5
#define EN_RXADDR       0x02  //接收地址允许,bit0~5,对应通道0~5
#define SETUP_AW        0x03  //设置地址宽度(所有数据通道):bit1,0:00,3字节;01,4字节;02,5字节;
#define SETUP_RETR      0x04  //建立自动重发;bit3:0,自动重发计数器;bit7:4,自动重发延时 250*x+86us
#define RF_CH           0x05  //RF通道,bit6:0,工作通道频率;
#define RF_SETUP        0x06  //RF寄存器;bit3:传输速率(0:1Mbps,1:2Mbps);bit2:1,发射功率;bit0:低噪声放大器增益
#define STATUS          0x07  //状态寄存器;bit0:TX FIFO满标志;bit3:1,接收数据通道号(最大:6);bit4,达到最多次重发
//bit5:数据发送完成中断;bit6:接收数据中断;
#define MAX_TX  	    0x10  //达到最大发送次数中断
#define TX_OK       	0x20  //TX发送完成中断
#define RX_OK   	    0x40  //接收到数据中断

#define OBSERVE_TX      0x08  //发送检测寄存器,bit7:4,数据包丢失计数器;bit3:0,重发计数器
#define CD              0x09  //载波检测寄存器,bit0,载波检测;
#define RX_ADDR_P0      0x0A  //数据通道0接收地址,最大长度5个字节,低字节在前
#define RX_ADDR_P1      0x0B  //数据通道1接收地址,最大长度5个字节,低字节在前
#define RX_ADDR_P2      0x0C  //数据通道2接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P3      0x0D  //数据通道3接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P4      0x0E  //数据通道4接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P5      0x0F  //数据通道5接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define TX_ADDR         0x10  //发送地址(低字节在前),ShockBurstTM模式下,RX_ADDR_P0与此地址相等
#define RX_PW_P0        0x11  //接收数据通道0有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P1        0x12  //接收数据通道1有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P2        0x13  //接收数据通道2有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P3        0x14  //接收数据通道3有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P4        0x15  //接收数据通道4有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P5        0x16  //接收数据通道5有效数据宽度(1~32字节),设置为0则非法
#define FIFO_STATUS     0x17  //FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RX FIFO满标志;bit2,3,保留
//bit4,TX FIFO空标志;bit5,TX FIFO满标志;bit6,1,循环发送上一数据包.0,不循环;
/**********************************************************************************************************/
//NRF24L01控制操作
/*
#define NRF24L01_CE      GPIO_Pin_7
#define GPIO_NRF24L01_CE  GPIOE
#define RCC_NRF24L01_CE  RCC_AHB1Periph_GPIOE

//NRF24L01 SPI接口CS信号
#define NRF24L01_CSN      GPIO_Pin_8
#define GPIO_NRF24L01_CSN  GPIOE
#define RCC_NRF24L01_CSN  RCC_AHB1Periph_GPIOE

#define NRF24L01_IRQ      GPIO_Pin_9
#define GPIO_NRF24L01_IRQ  GPIOE
#define RCC_NRF24L01_IRQ  RCC_AHB1Periph_GPIOE
*/

//NRF2401片选信号
#define Clr_NRF24L01_CE      HAL_GPIO_WritePin(CE_24L01_GPIO_Port, CE_24L01_Pin, GPIO_PIN_RESET)
#define Set_NRF24L01_CE      HAL_GPIO_WritePin(CE_24L01_GPIO_Port, CE_24L01_Pin, GPIO_PIN_SET)

//SPI片选信号
#define Clr_NRF24L01_CSN     HAL_GPIO_WritePin(CSN_24L01_GPIO_Port, CSN_24L01_Pin, GPIO_PIN_RESET)
#define Set_NRF24L01_CSN     HAL_GPIO_WritePin(CSN_24L01_GPIO_Port, CSN_24L01_Pin, GPIO_PIN_SET)

//NRF2401_IRQ数据输入
#define READ_NRF24L01_IRQ   HAL_GPIO_ReadPin(IRQ_24L01_GPIO_Port, IRQ_24L01_Pin)

//NRF24L01发送接收数据宽度定义
#define TX_ADR_WIDTH    5                               //5字节的地址宽度
#define RX_ADR_WIDTH    5                               //5字节的地址宽度
#define TX_PLOAD_WIDTH  32                              //32字节的用户数据宽度
#define RX_PLOAD_WIDTH  32                              //32字节的用户数据宽度
#define MAX_TIME_INTO_IDEL		10						//进入IDEL模式,最大时间,单位:S
extern 	unsigned char idel_mode_flag;
extern 	unsigned char mode_time_counter;

void NRF24L01_Init(void);                                //NRF24l01初始化
void RX_Mode(void);                                      //配置为接收模式
void TX_Mode(void);                                      //配置为发送模式
unsigned char NRF24L01_Write_Buf(unsigned char regaddr, unsigned char *pBuf, unsigned char datalen); //写数据区
unsigned char NRF24L01_Read_Buf(unsigned char regaddr, unsigned char *pBuf, unsigned char datalen);  //读数据区
unsigned char NRF24L01_Read_Reg(unsigned char regaddr);		                 //读寄存器
unsigned char NRF24L01_Write_Reg(unsigned char regaddr, unsigned char data);              //写寄存器
unsigned char NRF24L01_Check(void);                                 //检查NRF24L01是否在位
unsigned char NRF24L01_TxPacket(unsigned char *txbuf);                         //发送一个包的数据
unsigned char NRF24L01_RxPacket(unsigned char *rxbuf);                         //接收一个包的数据
#endif

15、NRF24L01.c文件
  在Core/src文件中添加NRF24L01.c文件

insert image description here

#include "NRF24L01.h"
#include "main.h"
#include "spi.h"
#include "stdio.h"

//NRF24L01 驱动函数

unsigned char idel_mode_flag = 0;
unsigned char mode_time_counter = 0;

const unsigned char INIT_ADDR0[5]= {
    
    0x02,0x3A,0xB1,0xB1,0x01};
const unsigned char INIT_ADDR1[5]= {
    
    0x02,0x3A,0x01,0x01,0x01};
const unsigned char INIT_ADDR2[5]= {
    
    0x03,0x3A,0x01,0x01,0x01};
const unsigned char INIT_ADDR3[5]= {
    
    0x04,0x3A,0x01,0x01,0x01};
const unsigned char INIT_ADDR4[5]= {
    
    0x05,0x3A,0x01,0x01,0x01};
const unsigned char INIT_ADDR5[5]= {
    
    0x06,0x3A,0x01,0x01,0x01};

#define CH_Num	120
#define debug_out(fmt,args...)  printf(fmt,##args)
//#define debug_out(fmt,args...) 

void delay_us(uint32_t n)
{
    
    
	unsigned char i;

	while(n--)
	{
    
    
		i = 8;
		while(i--);
	}
}
//初始化24L01的IO口
void NRF24L01_Init(void)
{
    
    
	//spi init
	//gpio init
	Clr_NRF24L01_CE;    // chip enable
	Set_NRF24L01_CSN;   // Spi disable
	delay_us(100);
}

//封装spi读写函数
unsigned char nRF24_SPI_Send_Byte(unsigned char txdata)
{
    
    
	unsigned char rxdata;
	HAL_SPI_TransmitReceive(&hspi2, &txdata, &rxdata, 1, 0x10);
	return(rxdata);							// return read unsigned char
}


//通过SPI写寄存器
unsigned char NRF24L01_Write_Reg(unsigned char regaddr,unsigned char data)
{
    
    
	unsigned char status;
	Clr_NRF24L01_CSN;                    //使能SPI传输
	status =nRF24_SPI_Send_Byte(regaddr); //发送寄存器号
	nRF24_SPI_Send_Byte(data);            //写入寄存器的值
	Set_NRF24L01_CSN;                    //禁止SPI传输
	return(status);       		         //返回状态值
}
//读取SPI寄存器值 ,regaddr:要读的寄存器
unsigned char NRF24L01_Read_Reg(unsigned char regaddr)
{
    
    
	unsigned char reg_val;
	Clr_NRF24L01_CSN;                //使能SPI传输
	nRF24_SPI_Send_Byte(regaddr);     //发送寄存器号
	reg_val=nRF24_SPI_Send_Byte(0XFF);//读取寄存器内容
	Set_NRF24L01_CSN;                //禁止SPI传输
	return(reg_val);                 //返回状态值
}
//在指定位置读出指定长度的数据
//*pBuf:数据指针
//返回值,此次读到的状态寄存器值
unsigned char NRF24L01_Read_Buf(unsigned char regaddr,unsigned char *pBuf,unsigned char datalen)
{
    
    
	unsigned char status,u8_ctr;
	Clr_NRF24L01_CSN;                     //使能SPI传输
	status=nRF24_SPI_Send_Byte(regaddr);   //发送寄存器值(位置),并读取状态值
	//for(u8_ctr=0;u8_ctr<datalen;u8_ctr++)pBuf[u8_ctr]=SPI_ReadWriteByte(0XFF);//读出数据
	HAL_SPI_Receive(&hspi2, pBuf, datalen, 0x10);
	Set_NRF24L01_CSN;                     //关闭SPI传输
	return status;                        //返回读到的状态值
}
//在指定位置写指定长度的数据
//*pBuf:数据指针
//返回值,此次读到的状态寄存器值
unsigned char NRF24L01_Write_Buf(unsigned char regaddr, unsigned char *pBuf, unsigned char datalen)
{
    
    
	unsigned char status,u8_ctr;
	Clr_NRF24L01_CSN;                                    //使能SPI传输
	status = nRF24_SPI_Send_Byte(regaddr);                //发送寄存器值(位置),并读取状态值
	//for(u8_ctr=0; u8_ctr<datalen; u8_ctr++)SPI_ReadWriteByte(*pBuf++); //写入数据
	HAL_SPI_Transmit(&hspi2, pBuf, datalen, 0x10);
	Set_NRF24L01_CSN;                                    //关闭SPI传输
	return status;                                       //返回读到的状态值
}
//启动NRF24L01发送一次数据
//txbuf:待发送数据首地址
//返回值:发送完成状况
unsigned char NRF24L01_TxPacket(unsigned char *txbuf)
{
    
    
	unsigned char state;
	Clr_NRF24L01_CE;
	NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF  32个字节
	Set_NRF24L01_CE;                                     //启动发送
	while(READ_NRF24L01_IRQ!=0);                         //等待发送完成
	state=NRF24L01_Read_Reg(STATUS);                     //读取状态寄存器的值
	NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS,state);      //清除TX_DS或MAX_RT中断标志
	if(state&MAX_TX)                                     //达到最大重发次数
	{
    
    
		NRF24L01_Write_Reg(FLUSH_TX,0xff);               //清除TX FIFO寄存器
		debug_out("TX MAX_TX error!\r\n");
		return MAX_TX;
	}
	if(state&TX_OK)                                      //发送完成
	{
    
    
		debug_out("TX OK!\r\n");
		return TX_OK;
	}
	debug_out("TX other error!\r\n");
	return 0xff;                                         //其他原因发送失败
}

//启动NRF24L01发送一次数据
//txbuf:待发送数据首地址
//返回值:0,接收完成;其他,错误代码
unsigned char NRF24L01_RxPacket(unsigned char *rxbuf)
{
    
    
	unsigned char state;
	state=NRF24L01_Read_Reg(STATUS);                //读取状态寄存器的值
	NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS,state); //清除TX_DS或MAX_RT中断标志
	if(state&TX_OK)
	{
    
    
		debug_out("RX send ack!\r\n"); //成功发送ACK
	}
	if(state&RX_OK)                                 //接收到数据
	{
    
    
		NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据
		NRF24L01_Write_Reg(FLUSH_RX,0xff);          //清除RX FIFO寄存器
		debug_out("RX read data!\r\n");
		return 0;
	}
	return 1;                                      //没收到任何数据
}

//该函数初始化NRF24L01到RX模式
//设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR
//当CE变高后,即进入RX模式,并可以接收数据了
void RX_Mode(void)
{
    
    
	Clr_NRF24L01_CE;
	//写RX节点地址
	NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0,(unsigned char*)INIT_ADDR0,RX_ADR_WIDTH);

	//使能通道0的自动应答
	NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA,0x01);
	//使能通道0的接收地址
	NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01);
	//设置RF通信频率
	NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,CH_Num);
	//选择通道0的有效数据宽度
	NRF24L01_Write_Reg(SPI_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);
	//设置TX发射参数,0db增益,2Mbps,低噪声增益开启
	NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x0f);
	//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,PRIM_RX接收模式
	NRF24L01_Write_Reg(SPI_WRITE_REG+CONFIG, 0x0f);
	//CE为高,进入接收模式
	Set_NRF24L01_CE;
}

//该函数初始化NRF24L01到TX模式
//设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,
//选择RF频道,波特率和LNA HCURR PWR_UP,CRC使能
//当CE变高后,即进入RX模式,并可以接收数据了
//CE为高大于10us,则启动发送.
void TX_Mode(void)
{
    
    
  //启动发送模式
	Clr_NRF24L01_CE;
	//写TX节点地址
	NRF24L01_Write_Buf(SPI_WRITE_REG+TX_ADDR,(unsigned char*)INIT_ADDR0,TX_ADR_WIDTH);
	//设置TX节点地址,主要为了使能ACK
	NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0,(unsigned char*)INIT_ADDR0,RX_ADR_WIDTH);

	//使能通道0的自动应答
	NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA,0x01);
	//使能通道0的接收地址
	NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01);
	//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次
	NRF24L01_Write_Reg(SPI_WRITE_REG+SETUP_RETR,0x1a);
	//设置RF通道为40
	NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,CH_Num);
	//设置TX发射参数,0db增益,2Mbps,低噪声增益开启
	NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x0f);
	//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,PRIM_RX发送模式,开启所有中断
	NRF24L01_Write_Reg(SPI_WRITE_REG+CONFIG,0x0e);
	// CE为高,10us后启动发送
	Set_NRF24L01_CE;
}

//上电检测NRF24L01是否在位
//写5个数据然后再读回来进行比较,
//相同时返回值:0,表示在位;否则返回1,表示不在位
unsigned char NRF24L01_Check(void)
{
    
    
	unsigned char buf[5]= {
    
    0XA5,0XA5,0XA5,0XA5,0XA5};
	unsigned char buf1[5];
	unsigned char i;
	NRF24L01_Write_Buf(SPI_WRITE_REG+TX_ADDR,buf,5);//写入5个字节的地址.
	NRF24L01_Read_Buf(TX_ADDR,buf1,5);              //读出写入的地址
	for(i=0; i<5; i++)if(buf1[i]!=0XA5)break;
	if(i!=5)
	{
    
    
		debug_out(("nRF24L01 TEST FAIL\r\n"));
		return 1;                               //NRF24L01不在位
	}
	debug_out(("nRF24L01 TEST OK\r\n"));
	return 0;		                           //NRF24L01在位
}

16、添加NRF24L01.c文件
  双击下方,添加.c文件

insert image description here
insert image description here
17、点击编译
  编译完成后NRF24L01.c文件前会出现"+"号,点击查看NRF24L01.h文件是否导入

insert image description here
18、保存文件重启Keil
  这里重启Keil的原因是因为在CubeMX里添加其他c文件时,如果再修改CubeMX内的参数重新生成代码时,该.c文件会被吞噬掉,保存重启后修改CubeMX内的参数就不会被吞噬。


4. STM32源代码

4. 1 发送端

(1)在usart.c函数下面的USER CODE BEGIN 1(90行处)处添加下列代码:
  这里的代码主要是让printf可以在主函数中正常使用。

#include <stdarg.h>
#include <stdio.h>

int fputc(int ch, FILE *f)
{
    
    
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0x10); 
  return ch;
}

unsigned char printf_temp[64]; 
void Uart1_printf(const char *format,...)
{
    
    
	unsigned short len;
	
	va_list args;	
	va_start(args, format);
	len = vsnprintf((char*)printf_temp, sizeof(printf_temp)+1, (char*)format,args);
	va_end(args);
	
	HAL_UART_Transmit_IT(&huart1, printf_temp, len);
}

(2)在main.c函数添加下列代码:
  这里是通信通道转换的标志位,1是通道50,0是通道120(默认通道120)

/* USER CODE BEGIN PV */
unsigned char flag_nrf24l01 = 0;
/* USER CODE END PV */

  定义通信时发送和接收的数据信息

/* USER CODE BEGIN 0 */
unsigned char tmp_buf[32] = {
    
    0};
unsigned char tmp_buf2[32] = {
    
    0};
unsigned char temp;
/* USER CODE END 0 */

  NRF24L01初始化,并上电检测NRF24L01是否在位,然后设置为发送模式

  /* USER CODE BEGIN 2 */
  	NRF24L01_Init();
	printf("\r\ninit OK!\r\n");
	temp = 4;
	while(NRF24L01_Check()&&(temp--));

	TX_Mode();	//发送模式
  /* USER CODE END 2 */

【while循环内加入】
  按下PA0,转换通信通道。在主循环中每隔1s发送一帧数据。每1s,tmp_buf[0]计数标志加1以查看数据变化;每次收到ack回复,则tmp_buf[1]加1。

/* USER CODE BEGIN 3 */
    if(flag_nrf24l01 == 1)//按下KEY
    {
    
    
      Clr_NRF24L01_CE;	//拉低CE引脚
      NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,50);//修改为从机1的频道
      Set_NRF24L01_CE;	//拉高CE引脚
      HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
    }
       
    if(flag_nrf24l01 == 0)//按下KEY
    {
    
    
      Clr_NRF24L01_CE;	//拉低CE引脚
      NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,120);//修改为从机2的频道
      Set_NRF24L01_CE;	//拉高CE引脚
      HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
    }
    
    HAL_Delay(1000);
    tmp_buf[1]++;
    if(NRF24L01_TxPacket(tmp_buf) == 0x20)	
    {
    
    
      printf("send data OK!\r\n");
      tmp_buf[0]++;
    }
    else
    {
    
    
      printf("send error!\r\n");
    }

(3)在gpio.c函数添加下列代码:
  这里主要是外部中断的设置并加入了软件消抖。
注:CubeMX生成的代码外部中断中不能加HAL_Delay函数,会涉及优先级问题

/* USER CODE BEGIN 1 */
extern unsigned char flag_nrf24l01;
/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    
    
  if(GPIO_Pin==KEY_Pin)
    if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin)==0)
    {
    
    
      if(flag_nrf24l01==0)
        flag_nrf24l01 = 1;
      else
        flag_nrf24l01 = 0;
    }
}
/* USER CODE END 2 */

4. 2 接收端

(1)在usart.c函数下面的USER CODE BEGIN 1(90行处)处添加下列代码:
  这里的代码主要是让printf可以在主函数中正常使用。

#include <stdarg.h>
#include <stdio.h>

int fputc(int ch, FILE *f)
{
    
    
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0x10); 
  return ch;
}

unsigned char printf_temp[64]; 
void Uart1_printf(const char *format,...)
{
    
    
	unsigned short len;
	
	va_list args;	
	va_start(args, format);
	len = vsnprintf((char*)printf_temp, sizeof(printf_temp)+1, (char*)format,args);
	va_end(args);
	
	HAL_UART_Transmit_IT(&huart1, printf_temp, len);
}

(2)在main.c函数添加下列代码:
  这里是通信通道转换的标志位,1是通道50,0是通道120(默认通道120)

/* USER CODE BEGIN PV */
unsigned char flag_nrf24l01 = 0;
/* USER CODE END PV */

  定义通信时发送和接收的数据信息

/* USER CODE BEGIN 0 */
unsigned char tmp_buf[32] = {
    
    0};
unsigned char tmp_buf2[32] = {
    
    0};
unsigned char temp;
/* USER CODE END 0 */

  NRF24L01初始化,并上电检测NRF24L01是否在位,然后设置为发送模式

  /* USER CODE BEGIN 2 */
  	NRF24L01_Init();
	printf("\r\ninit OK!\r\n");
	temp = 4;
	while(NRF24L01_Check()&&(temp--));

	RX_Mode();	//接收模式
  /* USER CODE END 2 */

【while循环内加入】
  不断查询是否接收到数据,如果接收到,则打印接收到的数据,同时LED灯亮起:

/* USER CODE BEGIN 3 */
	if(NRF24L01_RxPacket(tmp_buf) == 0)	
	{
    
    
		unsigned char i;
		for(i=0;i<32;i++)
		   {
    
    
			 printf(" %x",tmp_buf[i]);
		     HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET); 
		   }
		printf("\r\n");
	}

(3)在gpio.c函数添加下列代码:
  这里主要是外部中断的设置并加入了软件消抖。
注:CubeMX生成的代码外部中断中不能加HAL_Delay函数,会涉及优先级问题

/* USER CODE BEGIN 1 */
extern unsigned char flag_nrf24l01;
/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    
    
  if(GPIO_Pin==KEY_Pin)
    if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin)==0)
    {
    
    
      if(flag_nrf24l01==0)
        flag_nrf24l01 = 1;
      else
        flag_nrf24l01 = 0;
    }
}
/* USER CODE END 2 */

4. 3 修改从机频道实现1对多通信

  在这里修改从机的频道,从而对不同频道的从机进行控制。
insert image description here


5. 结果展示

5. 1 上位机数据

insert image description here

5. 2 从机

  通过按PA0转换通道,实现交替控制从机1,从机2,通过LED灯的亮灭验证是否通信成功。
insert image description here


6、下载

(1)程序下载地址:https://pan.baidu.com/s/1rRKbPSop4r_r77Q9hhXH_A
提取码:1234

(2) Serial port assistant download address: https://pan.baidu.com/s/11xBkoLBMVcIv7QNALpOeeg
Extraction code: yzx3


7. Summary

  The above is the HAL library configuration and keil programming for the NRF24L01 module to realize "1-to-1" and "1-to-many" wireless communication. If there is an error in the article or you have any questions about the above content, please leave a message in the comment area, Xiaozheng I will reply you as soon as I see it! See you next time!

Guess you like

Origin blog.csdn.net/weixin_44270218/article/details/128042919