之前写过一个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默认配置这里是上升沿触发我没注意,导致这里调了半天,配置如图:

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; // 清空缓冲
}
}
}
评论区