目 录CONTENT

文章目录

基于STM32_HAL库使用NRF24L01+模块进行2.4G无线通信

成培培
2026-02-13 / 0 评论 / 0 点赞 / 8 阅读 / 0 字

之前写过一个51单片机使用NRF24L01进行2.4G无线通信,不过最近在做四轴飞控,51单片机肯定是不够用的,需要用stm32实现无线通信,所以这里基于STM32 HAL库又调了一遍

NRF24L01 简介

nRF24L01 是由 Nordic Semiconductor 推出的一款工作在 2.4GHz ISM 频段的单芯片无线收发器(GFSK 调制),使用SPI 接口通信。

SPI协议介绍

参考这里:https://www.chengpei.top/archives/spi-xie-yi-jian-jie

NRF24L01+模块引脚定义

模块有8个引脚,定义如下:
CSN:芯片的片选线,CSN 为低电平芯片工作。
SCK:芯片控制的时钟线(SPI 时钟)
MISO:芯片控制数据线(Master input slave output)
MOSI:芯片控制数据线(Master output slave input)
IRQ:中断信号。无线通信过程中 MCU 主要是通过 IRQ 与 NRF24L01 进行通信。
CE: 芯片的模式控制线。 在 CSN 为低的情况下,CE 协同 NRF24L01 的 CONFIG 寄存器共同决定 NRF24L01 的状态(参照 NRF24L01 的状态机)
VCC:供电
GND:接地
其中SCK、MISO、MOSI对应SPI外设的3个引脚,CSN、CE、IRQ接普通的GPIO引脚即可

Stm32CubeMX配置

基础的调试、时钟频率什么的就不说了,参考这个:https://www.chengpei.top/archives/clion_openocd_stm32_hal#%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81
另外我这里开启了外设SPI1以及USART1,分别用于SPI通信和串口打印,另外启用了3个普通GPIO口:PC4、PC5、PC6,分别接到IRQ、CE、CSN
这里特别要注意的是IRQ对应引脚的外部中断,必须设置为下降沿触发
因为根据 nRF24L01 数据手册IRQ 默认是 高电平,当这些任意事件:收到数据、发送完成、达到最大重发次数发生时,IRQ 会被 拉低,CubeMX默认配置这里是上升沿触发我没注意,导致这里调了半天,配置如图:
https://www.chengpei.top/upload/cubemx_pc4.png
IRQ外部中断下降沿触发,最好默认上拉,CE、CSN默认输出模式即可

驱动代码

其实代码基本就是用AI写的,手册都懒得看了,就是花了点时间调试

nrf24l01.h

#ifndef __NRF24_H  
#define __NRF24_H  
  
#include "stm32f1xx_hal.h"  
  
/* ===================== 引脚定义 这里可以根据你实际的接线方式修改===================== */  
#define NRF24_CE_PORT       GPIOC  
#define NRF24_CE_PIN        GPIO_PIN_5  
  
#define NRF24_CSN_PORT      GPIOC  
#define NRF24_CSN_PIN       GPIO_PIN_6  
  
#define NRF24_IRQ_PORT      GPIOC  
#define NRF24_IRQ_PIN       GPIO_PIN_4  
  
/* ===================== SPI 句柄 ===================== */  
extern SPI_HandleTypeDef hspi1;  
  
/* ===================== 基本命令 ===================== */  
#define NRF_CMD_R_REGISTER      0x00  
#define NRF_CMD_W_REGISTER      0x20  
#define NRF_CMD_R_RX_PAYLOAD    0x61  
#define NRF_CMD_W_TX_PAYLOAD    0xA0  
#define NRF_CMD_FLUSH_TX        0xE1  
#define NRF_CMD_FLUSH_RX        0xE2  
#define NRF_CMD_NOP             0xFF  
  
/* ===================== 寄存器地址 ===================== */  
#define NRF_REG_CONFIG          0x00  
#define NRF_REG_EN_AA           0x01  
#define NRF_REG_EN_RXADDR       0x02  
#define NRF_REG_SETUP_AW        0x03  
#define NRF_REG_SETUP_RETR      0x04  
#define NRF_REG_RF_CH           0x05  
#define NRF_REG_RF_SETUP        0x06  
#define NRF_REG_STATUS          0x07  
#define NRF_REG_RX_ADDR_P0      0x0A  
#define NRF_REG_TX_ADDR         0x10  
#define NRF_REG_RX_PW_P0        0x11  
#define NRF_REG_FIFO_STATUS     0x17  
  
/* ===================== 位定义 ===================== */  
#define NRF_CONFIG_PWR_UP       1  
#define NRF_CONFIG_PRIM_RX      0  
  
#define NRF_STATUS_RX_DR        6  
#define NRF_STATUS_TX_DS        5  
#define NRF_STATUS_MAX_RT       4  
  
/* ===================== 固定参数 ===================== */  
#define NRF24_ADDR_WIDTH        5  
#define NRF24_PAYLOAD_SIZE      16  
#define NRF24_CHANNEL           40  
  
/* ===================== 函数声明 ===================== */  
void NRF24_Init(void);  
void NRF24_SetTxMode(uint8_t *addr);  
void NRF24_SetRxMode(uint8_t *addr);  
uint8_t NRF24_Send(uint8_t *data);  
uint8_t NRF24_Read(uint8_t *data);  
void NRF24_IRQ_Handler(volatile uint8_t nrf_rx_buffer[NRF24_PAYLOAD_SIZE], volatile uint8_t* nrf_rx_flag);  
  
#endif

nrf24l01.c

#include "nrf24l01.h"  
#include "usart.h"  
  
/* ===================== 基础控制宏 ===================== */  
#define NRF24_CE_HIGH()     HAL_GPIO_WritePin(NRF24_CE_PORT, NRF24_CE_PIN, GPIO_PIN_SET)  
#define NRF24_CE_LOW()      HAL_GPIO_WritePin(NRF24_CE_PORT, NRF24_CE_PIN, GPIO_PIN_RESET)  
  
#define NRF24_CSN_HIGH()    HAL_GPIO_WritePin(NRF24_CSN_PORT, NRF24_CSN_PIN, GPIO_PIN_SET)  
#define NRF24_CSN_LOW()     HAL_GPIO_WritePin(NRF24_CSN_PORT, NRF24_CSN_PIN, GPIO_PIN_RESET)  
  
/* ===================== SPI 底层 ===================== */  
static uint8_t NRF24_SPI_RW(uint8_t data)  
{  
    uint8_t rx;  
    HAL_SPI_TransmitReceive(&hspi1, &data, &rx, 1, 100);  
    return rx;  
}  
  
/* ===================== 寄存器读写 ===================== */  
static uint8_t NRF24_ReadReg(uint8_t reg)  
{  
    uint8_t value;  
    NRF24_CSN_LOW();  
    NRF24_SPI_RW(NRF_CMD_R_REGISTER | reg);  
    value = NRF24_SPI_RW(NRF_CMD_NOP);  
    NRF24_CSN_HIGH();  
    return value;  
}  
  
static void NRF24_WriteReg(uint8_t reg, uint8_t value)  
{  
    NRF24_CSN_LOW();  
    NRF24_SPI_RW(NRF_CMD_W_REGISTER | reg);  
    NRF24_SPI_RW(value);  
    NRF24_CSN_HIGH();  
}  
  
static void NRF24_WriteBuf(uint8_t reg, uint8_t *buf, uint8_t len)  
{  
    NRF24_CSN_LOW();  
    NRF24_SPI_RW(NRF_CMD_W_REGISTER | reg);  
    for(uint8_t i=0;i<len;i++)  
        NRF24_SPI_RW(buf[i]);  
    NRF24_CSN_HIGH();  
}  
  
static void NRF24_ReadBuf(uint8_t cmd, uint8_t *buf, uint8_t len)  
{  
    NRF24_CSN_LOW();  
    NRF24_SPI_RW(cmd);  
    for(uint8_t i=0;i<len;i++)  
        buf[i] = NRF24_SPI_RW(NRF_CMD_NOP);  
    NRF24_CSN_HIGH();  
}  
  
/* ===================== 初始化 ===================== */  
void NRF24_Init(void)  
{  
    NRF24_CE_LOW();  
    HAL_Delay(5);  
  
    /* CONFIG  
 bit1 PWR_UP=1 上电  
 bit0 PRIM_RX=0 发送模式  
 */  NRF24_WriteReg(NRF_REG_CONFIG, 0x0E);  
  
    /* EN_AA  
 bit0=1 开启PIPE0自动应答  
 */  NRF24_WriteReg(NRF_REG_EN_AA, 0x01);  
  
    /* EN_RXADDR  
 bit0=1 使能PIPE0  
 */  NRF24_WriteReg(NRF_REG_EN_RXADDR, 0x01);  
  
    /* SETUP_AW  
 0x03 = 5字节地址  
 */  NRF24_WriteReg(NRF_REG_SETUP_AW, 0x03);  
  
    /* SETUP_RETR  
 0x1F ARD=0001 500us ARC=1111 重发15次  
 */  NRF24_WriteReg(NRF_REG_SETUP_RETR, 0x1F);  
  
    /* RF_CH  
  设置频道40  
 */  NRF24_WriteReg(NRF_REG_RF_CH, NRF24_CHANNEL);  
  
    /* RF_SETUP  
 0x06 1Mbps 0dBm */  NRF24_WriteReg(NRF_REG_RF_SETUP, 0x06);  
  
    /* 固定payload 16字节 */  NRF24_WriteReg(NRF_REG_RX_PW_P0, NRF24_PAYLOAD_SIZE);  
  
    /* 清除中断标志 */  NRF24_WriteReg(NRF_REG_STATUS, 0x70);  
  
    /* 清空FIFO */  
  NRF24_CSN_LOW();  
    NRF24_SPI_RW(NRF_CMD_FLUSH_TX);  
    NRF24_CSN_HIGH();  
  
    NRF24_CSN_LOW();  
    NRF24_SPI_RW(NRF_CMD_FLUSH_RX);  
    NRF24_CSN_HIGH();  
}  
  
/* ===================== 发送模式 ===================== */  
void NRF24_SetTxMode(uint8_t *addr)  
{  
    NRF24_CE_LOW();  
  
    NRF24_WriteBuf(NRF_REG_TX_ADDR, addr, NRF24_ADDR_WIDTH);  
    NRF24_WriteBuf(NRF_REG_RX_ADDR_P0, addr, NRF24_ADDR_WIDTH);  
  
    /* CONFIG:  
 PWR_UP=1 PRIM_RX=0 */  NRF24_WriteReg(NRF_REG_CONFIG, 0x0E);  
  
    HAL_Delay(2);  
}  
  
/* ===================== 接收模式 ===================== */  
void NRF24_SetRxMode(uint8_t *addr)  
{  
    NRF24_CE_LOW();  
  
    NRF24_WriteBuf(NRF_REG_RX_ADDR_P0, addr, NRF24_ADDR_WIDTH);  
  
    /* CONFIG:  
 PWR_UP=1 PRIM_RX=1 */  NRF24_WriteReg(NRF_REG_CONFIG, 0x0F);  
  
    HAL_Delay(2);  
  
    NRF24_CE_HIGH();  
}  
  
/* ===================== 发送数据 ===================== */  
uint8_t NRF24_Send(uint8_t *data)  
{  
    NRF24_CE_LOW();  
  
    NRF24_CSN_LOW();  
    NRF24_SPI_RW(NRF_CMD_W_TX_PAYLOAD);  
    for(uint8_t i=0;i<NRF24_PAYLOAD_SIZE;i++)  
        NRF24_SPI_RW(data[i]);  
    NRF24_CSN_HIGH();  
  
    NRF24_CE_HIGH();  
    HAL_Delay(1);  
    NRF24_CE_LOW();  
  
    uint8_t status = NRF24_ReadReg(NRF_REG_STATUS);  
  
    if(status & (1<<NRF_STATUS_TX_DS))  
    {  
        NRF24_WriteReg(NRF_REG_STATUS, 1<<NRF_STATUS_TX_DS);  
        return 1;  
    }  
  
    if(status & (1<<NRF_STATUS_MAX_RT))  
    {  
        NRF24_WriteReg(NRF_REG_STATUS, 1<<NRF_STATUS_MAX_RT);  
        return 0;  
    }  
  
    return 0;  
}  
  
/* ===================== 读取数据 ===================== */  
uint8_t NRF24_Read(uint8_t *data)  
{  
    uint8_t status = NRF24_ReadReg(NRF_REG_STATUS);  
  
    if(status & (1<<NRF_STATUS_RX_DR))  
    {  
        NRF24_ReadBuf(NRF_CMD_R_RX_PAYLOAD, data, NRF24_PAYLOAD_SIZE);  
  
        NRF24_WriteReg(NRF_REG_STATUS, 1<<NRF_STATUS_RX_DR);  
  
        return 1;  
    }  
  
    return 0;  
}  
  
/* ===================== IRQ 中断处理 ===================== */void NRF24_IRQ_Handler(volatile uint8_t nrf_rx_buffer[NRF24_PAYLOAD_SIZE], volatile uint8_t* nrf_rx_flag)  
{  
    uint8_t status = NRF24_ReadReg(NRF_REG_STATUS);  
  
    while(status & (1<<NRF_STATUS_RX_DR))  
    {  
        NRF24_ReadBuf(NRF_CMD_R_RX_PAYLOAD,  
                      (uint8_t*)nrf_rx_buffer,  
                      NRF24_PAYLOAD_SIZE);  
  
        *nrf_rx_flag = 1;  
  
        NRF24_WriteReg(NRF_REG_STATUS, (1<<NRF_STATUS_RX_DR));  
  
        status = NRF24_ReadReg(NRF_REG_STATUS);  
    }  
  
    /* 清除其他中断源 */  NRF24_WriteReg(NRF_REG_STATUS, status & 0x70);  
}

main函数调用

这里需要声明一个缓冲区用于接收模块发来的数据:

// 接收地址
uint8_t addr[5] = {0x31, 0x32, 0x33, 0x34, 0x35};  
volatile uint8_t nrf_rx_buffer[NRF24_PAYLOAD_SIZE];  
volatile uint8_t nrf_rx_flag = 0;

初始化模块:

NRF24_Init();  
NRF24_SetRxMode(addr);

中断回调中接收数据:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)  
{  
  if(GPIO_Pin == GPIO_PIN_4)  
  {  
    NRF24_IRQ_Handler(nrf_rx_buffer, &nrf_rx_flag);  
  }  
}

主循环中打印到串口:

if(nrf_rx_flag)  
{  
  nrf_rx_flag = 0;  
  HAL_UART_Transmit(&huart1, (uint8_t*) nrf_rx_buffer, NRF24_PAYLOAD_SIZE, 100);  
}

发送模式也很简单,切换到发送模式发送即可:

NRF24_SetTxMode(addr);  
NRF24_Send("hello world");

完整代码

https://github.com/chengpei/nrf24l01_stm32_hal_demo

最后附上我的发送端代码,我的发送端用的Arduino Nano,通过串口输入数据发送出去:

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(9, 10);  // CE, CSN
uint8_t addr[5] = {0x31, 0x32, 0x33, 0x34, 0x35};

const uint8_t PAYLOAD_SIZE = 16;

char serialBuf[64];
uint8_t serialIndex = 0;

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println("NRF24 Serial TX Mode");

  if (!radio.begin()) {
    Serial.println("Radio hardware not responding!");
    while (1);
  }

  radio.setChannel(40);
  radio.setDataRate(RF24_1MBPS);
  radio.setPALevel(RF24_PA_LOW);
  radio.setRetries(1, 15);
  radio.setAutoAck(true);
  radio.setPayloadSize(PAYLOAD_SIZE);
  radio.disableDynamicPayloads();

  radio.openWritingPipe(addr);
  radio.stopListening();   // 永远处于发送模式
}

void loop() {

  while (Serial.available()) {

    char c = Serial.read();

    if (serialIndex < sizeof(serialBuf) - 1) {
      serialBuf[serialIndex++] = c;
    }

    // 遇到换行符就发送
    if (c == '\n') {

      uint8_t data[PAYLOAD_SIZE] = {0};

      // 拷贝到固定16字节payload
      for (uint8_t i = 0; i < PAYLOAD_SIZE && i < serialIndex; i++) {
        data[i] = serialBuf[i];
      }

      bool ok = radio.write(data, PAYLOAD_SIZE);

      if (ok)
        Serial.println("TX OK");
      else
        Serial.println("TX FAILED");

      serialIndex = 0;  // 清空缓冲
    }
  }
}
0

评论区