• 【STM32CubeMX】NRF24L01模块实现“1对1“及“1对多“无线通信


      大家好,我是小政。本篇文章我将针对NRF24L01模块实现"1对1"及"1对多"无线通信的STM32CubeMX配置过程进行详细的讲解,让准备学习HAL库的小伙伴能够更好的理解STM32CubeMX如何配置。

    1. 芯片简介

      NRF24L01 是 NORDIC 公司最近生产的一款无线通信通信芯片,采用 FSK 调制,内部集成 NORDIC 自己的 Enhanced Short Burst 协议。可以实现点对点或是 1 对 6 的无线通信。无线通信速度可以达到 2Mbps。NORDIC 公司提供通信模块的 GERBER 文件,可以直接加工生产。嵌入式工程师或是单片机爱好者只需要为单片机系统预留 5 个 GPIO,1 个中断输入引脚,就可以很容易实现无线通信的功能,非常适合用来为 MCU 系统构建无线通信功能。

    (1)NRF24L01功能框图

    NRF24L01功能框图
      NRF24L01的框图上图所示,从单片机控制的角度来看,我们只需要关注图1右面的六个控制和数据信号,分别为CSN、SCK、MISO、MOSI、IRQ、CE。
    NRF24L01引脚图

    • CSN:芯片的片选线,CSN为低电平时,芯片工作
    • SCK:芯片控制的时钟线(SPI时钟)
    • MISO:芯片控制数据线,主控制器输出,从器件数据输入
    • MOSI:芯片控制数据线,主控制器输入,从器件数据输出
    • CE:芯片的模式控制线。在CSN为低电平的情况下,CE协同NRF24L01的CONFIG寄存器共同决定NRF24L01的状态
    • IRQ:中断信号。无线通信过程中MCU主要通过IRQ与NRF24L01进行通信

    在以下三种情况变低:
      a) Tx FIFO 发完并且收到ACK(使能ACK情况下)
      b) Rx FIFO 收到数据
      c) 达到最大重发次数

    (2)NRF24L01状态机

      对于NRF24L01的固件编程工作主要是参照NRF24L01的状态机。主要有以下几个状态:
      Power Down Mode:掉电模式
      Tx Mode:发射模式
      Rx Mode:接收模式
      Standby-1 Mode:待机1模式
      Standby-2 Mode:待机2模式

    (3)NRF24L01工作模式

    NRF24L01工作模式
      工作模式分为四种模式:接收模式,发送模式,待机模式,掉电模式
      收发模式又分为:Enhanced ShockBurstTM收发模式(支持自动ACK和自动重发)、ShockBurstTM收发模式和直接收发模式三种,收发模式由器件配置字决定。
      通常使用Enhanced模式。在Enhanced ShockBurstTM收发模式下, NRF24L01自动处理字头和CRC校验码。在接收数据时,自动把字头和CRC校验码移去。在发送数据时,自动加上字头和CRC校验码,在发送模式下,置CE为高,至少10us,将时发送过程完成后。,该模式要求终端设备在接收到数据后有应答信号,以便发送方检测有无数据丢失,一旦丢失则重发数据。

    (4)TX与RX的配置过程

      这里采用Enhanced ShockBurstTM通信方式的Tx 与Rx的配置及通信过程。

    4.1 TX 模式初始化过程

    TX 模式初始化过程

    4.2 RX 模式初始化过程

    在这里插入图片描述


    2. 硬件准备

    (1)所需硬件

    • 芯片:STM32F103C8T6系统板
    • 供电:USB线
    • 模块:NRF24L01

    (2)硬件连接:

    • PB4 —— NRF24L01的IRQ引脚
    • PB5 —— NRF24L01的CSN引脚
    • PB6 —— NRF24L01的CE引脚
    • PB13 —— NRF24L01的SCK引脚
    • PB14 —— NRF24L01的MISO引脚
    • PB15 —— NRF24L01的MOSI引脚

    3. STM32CubeMX配置过程

    1.1 所用工具:

    • 芯片:STM32F103C8T6
    • IDE:MDK-Keil软件
    • STM32F1xxHAL库

    1.2 知识概括:

    • STM32CubeMX创建LED,KEY,SPI例程
    • Keil软件程序编写

    1.3 工程创建
    1、芯片选择
      芯片:STM32F103C8T6(根据自己的板子来进行选择)

    芯片选择
    2、设置Debug
      选择Serial Wire,这里是当你想要在keil中进行调试时一定要选择该选项

    设置Debug
    3、设置RCC
      设置高速外部时钟HSE 选择外部时钟源

    设置RCC
    4、SPI2配置
      SPI2设置为全双工主模式,NRF24L01要求时钟速率设置为8M以下

    设置SPI参数
    5、USART1配置
      异步收发,波特率默认:115200 Bit/s,打开串口1异步收发中断

    USART1配置
    6、GPIO配置
      NRF24L01引脚:IRQ设置为上拉输入,CSN和CE都设置为推挽输出;
      我使用的板子LED引脚为PC13,初始电平为高电平,目的是通过观察小灯的亮灭判断是否通信成功;按键设置为PA0,外部中断下降沿触发

    在这里插入图片描述
    7、中断优先级配置
      因为需要实时发送数据,所以串口通信的优先级要高于按键优先级

    在这里插入图片描述
    8、配置时钟
      F1系列芯片系统时钟为72MHzs

    在这里插入图片描述

    9、项目创建最后步骤

    • 设置项目名称
    • 选择所用IDE
      在这里插入图片描述

    10、输出文件

    • ①处:复制所用文件的.c和.h
    • ②处:每个功能生产独立的.c和.h文件
      在这里插入图片描述

    11、创建工程文件
      点击GENERATE CODE 创建工程

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

    在这里插入图片描述

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

    在这里插入图片描述

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

    在这里插入图片描述

    #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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94

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

    在这里插入图片描述

    #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
    	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
    	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在位
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219

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

    在这里插入图片描述
    在这里插入图片描述
    17、点击编译
      编译完成后NRF24L01.c文件前会出现"+"号,点击查看NRF24L01.h文件是否导入

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


    4. STM32源代码

    4. 1 发送端

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

    #include 
    #include 
    
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

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

    /* USER CODE BEGIN PV */
    unsigned char flag_nrf24l01 = 0;
    /* USER CODE END PV */
    
    • 1
    • 2
    • 3

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

    /* USER CODE BEGIN 0 */
    unsigned char tmp_buf[32] = {0};
    unsigned char tmp_buf2[32] = {0};
    unsigned char temp;
    /* USER CODE END 0 */
    
    • 1
    • 2
    • 3
    • 4
    • 5

      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 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    【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");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

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

    /* USER CODE BEGIN 1 */
    extern unsigned char flag_nrf24l01;
    /* USER CODE END 1 */
    
    • 1
    • 2
    • 3
    /* 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 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4. 2 接收端

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

    #include 
    #include 
    
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

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

    /* USER CODE BEGIN PV */
    unsigned char flag_nrf24l01 = 0;
    /* USER CODE END PV */
    
    • 1
    • 2
    • 3

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

    /* USER CODE BEGIN 0 */
    unsigned char tmp_buf[32] = {0};
    unsigned char tmp_buf2[32] = {0};
    unsigned char temp;
    /* USER CODE END 0 */
    
    • 1
    • 2
    • 3
    • 4
    • 5

      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 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    【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");
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    /* USER CODE BEGIN 1 */
    extern unsigned char flag_nrf24l01;
    /* USER CODE END 1 */
    
    • 1
    • 2
    • 3
    /* 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 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

      在这里修改从机的频道,从而对不同频道的从机进行控制。
    在这里插入图片描述


    5. 结果展示

    5. 1 上位机数据

    在这里插入图片描述

    5. 2 从机

      通过按PA0转换通道,实现交替控制从机1,从机2,通过LED灯的亮灭验证是否通信成功。
    在这里插入图片描述


    6、下载

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

    (2)串口助手下载地址:https://pan.baidu.com/s/11xBkoLBMVcIv7QNALpOeeg
    提取码:yzx3


    7、总结

      以上就是NRF24L01模块实现“1对1“及“1对多“无线通信的HAL库配置与keil编程,若文章中出现错误或者小伙伴对以上内容有所疑问,欢迎大家在评论区留言,小政看到后会尽快回复大家!我们下期再见!

  • 相关阅读:
    Jmeter发送webService请求并压测
    VsCode集成Python开发环境
    SQL的约束
    C语言 L1-016 查验身份证
    【SQL刷题】Day3----SQL必会的常用函数专项练习
    DS查找——折半查找求平方根
    不明白如何将批量图片格式转换?2招教你们快速搞定
    vue页面缓存解决方案
    传统企业如何转型社交电商,泰山众筹的玩法有哪些?
    【6.824】分布式lab1 mapReduce
  • 原文地址:https://blog.csdn.net/weixin_44270218/article/details/128042919