51单片机 NRF24L01 接收程序

一、硬件连接(STC89C52)

引脚连接表

NRF24L01 引脚 51单片机引脚 备注
VCC 3.3V 必须3.3V!5V会烧坏
GND GND
CE P1.2 使能
CSN P1.3 片选
SCK P1.4 SPI时钟
MOSI P1.5 主出从入
MISO P1.6 主入从出
IRQ P1.7 中断(可不用)

注意:NRF24L01 是 3.3V 器件,如果 51 单片机是 5V,需要电平转换或串联 1K 电阻。


二、完整程序(Keil C51)

1、头文件定义

c 复制代码
// nrf24l01.h
#ifndef __NRF24L01_H
#define __NRF24L01_H

#include <reg52.h>
#include <intrins.h>

// 引脚定义
sbit CE  = P1^2;   // 使能
sbit CSN = P1^3;   // 片选
sbit SCK = P1^4;   // 时钟
sbit MOSI = P1^5;  // 主出从入
sbit MISO = P1^6;  // 主入从出
sbit IRQ = P1^7;   // 中断(接收数据时变低)

// 寄存器地址
#define CONFIG      0x00  // 配置寄存器
#define EN_AA       0x01  // 自动应答
#define EN_RXADDR   0x02  // 接收地址使能
#define SETUP_AW    0x03  // 地址宽度
#define SETUP_RETR  0x04  // 自动重发
#define RF_CH       0x05  // RF通道
#define RF_SETUP    0x06  // RF设置
#define STATUS      0x07  // 状态寄存器
#define OBSERVE_TX  0x08  // 发送状态
#define CD          0x09  // 载波检测
#define RX_ADDR_P0  0x0A  // 接收地址0
#define RX_ADDR_P1  0x0B  // 接收地址1
#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  // 接收数据宽度0
#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  // FIFO状态
#define DYNPD       0x1C  // 动态长度
#define FEATURE     0x1D  // 功能

// 命令
#define R_REGISTER    0x00  // 读寄存器
#define W_REGISTER    0x20  // 写寄存器
#define R_RX_PAYLOAD  0x61  // 读接收数据
#define W_TX_PAYLOAD  0xA0  // 写发送数据
#define FLUSH_TX      0xE1  // 清空TX FIFO
#define FLUSH_RX      0xE2  // 清空RX FIFO
#define REUSE_TX_PL   0xE3  // 重用TX有效数据
#define R_RX_PL_WID   0x60  // 读取RX有效数据宽度
#define W_ACK_PAYLOAD 0xA8  // 写ACK数据
#define W_TX_PAYLOAD_NOACK 0xB0
#define NOP           0xFF  // 空操作

// 函数声明
void SPI_Init(void);
unsigned char SPI_RW(unsigned char dat);
unsigned char SPI_Read(unsigned char reg);
void SPI_Write(unsigned char reg, unsigned char value);
void NRF24L01_Init(void);
void NRF24L01_RX_Mode(void);
unsigned char NRF24L01_RxPacket(unsigned char *rx_buf);
void NRF24L01_Config(void);

#endif

2、SPI 底层驱动

c 复制代码
// spi.c
#include "nrf24l01.h"

// SPI初始化
void SPI_Init(void)
{
    CSN = 1;   // 片选高(不选中)
    SCK = 0;   // 时钟低
    CE = 0;    // 不使能
}

// SPI读写一个字节
unsigned char SPI_RW(unsigned char dat)
{
    unsigned char i;
    
    for (i = 0; i < 8; i++) 
    {
        MOSI = (dat & 0x80) ? 1 : 0;  // 输出高位
        dat <<= 1;  // 左移
        SCK = 1;    // 时钟上升沿
        
        _nop_(); _nop_();  // 延时,等待数据稳定
        
        if (MISO)   // 读取MISO
            dat |= 0x01;
        
        SCK = 0;    // 时钟下降沿
        _nop_(); _nop_();
    }
    
    return dat;
}

// 读寄存器
unsigned char SPI_Read(unsigned char reg)
{
    unsigned char value;
    
    CSN = 0;           // 片选低
    SPI_RW(reg);       // 发送寄存器地址
    value = SPI_RW(0); // 读取值
    CSN = 1;           // 片选高
    
    return value;
}

// 写寄存器
void SPI_Write(unsigned char reg, unsigned char value)
{
    CSN = 0;              // 片选低
    SPI_RW(reg | 0x20);   // 写命令
    SPI_RW(value);        // 写值
    CSN = 1;              // 片选高
}

3、NRF24L01 配置

c 复制代码
// nrf24l01.c
#include "nrf24l01.h"

// 初始化NRF24L01
void NRF24L01_Init(void)
{
    SPI_Init();
    CE = 0;   // 不使能
    CSN = 1;  // 不选中
    
    // 延时确保上电稳定
    Delay_ms(100);
}

// 配置为接收模式
void NRF24L01_RX_Mode(void)
{
    CE = 0;  // 不使能
    
    // 写接收地址(必须与发送端一致)
    CSN = 0;
    SPI_RW(W_REGISTER + RX_ADDR_P0);
    SPI_RW(0x34);  // 地址字节0
    SPI_RW(0x43);  // 地址字节1
    SPI_RW(0x10);  // 地址字节2
    SPI_RW(0x10);  // 地址字节3
    SPI_RW(0x01);  // 地址字节4
    CSN = 1;
    
    // 接收数据宽度(32字节)
    SPI_Write(RX_PW_P0, 32);
    
    // 通道频率(2.4GHz + RF_CH * 1MHz)
    SPI_Write(RF_CH, 40);  // 2.440GHz
    
    // 自动应答
    SPI_Write(EN_AA, 0x01);  // 只有通道0自动应答
    
    // 使能接收地址
    SPI_Write(EN_RXADDR, 0x01);  // 只有通道0
    
    // RF设置
    // 0x0E = 250kbps, 0dBm输出
    // 0x0F = 1Mbps, 0dBm输出
    SPI_Write(RF_SETUP, 0x0F);
    
    // 配置寄存器
    // 0x0B = 接收模式,上电,16位CRC,CRC使能
    SPI_Write(CONFIG, 0x0B);
    
    // 清空状态寄存器
    SPI_Write(STATUS, 0x7E);
    
    // 清空FIFO
    SPI_RW(FLUSH_RX);
    
    // 使能接收
    CE = 1;
    
    // 延时,进入接收模式
    Delay_ms(5);
}

// 接收数据包
unsigned char NRF24L01_RxPacket(unsigned char *rx_buf)
{
    unsigned char i, status, rx_len = 0;
    
    status = SPI_Read(STATUS);  // 读取状态
    
    // 判断是否接收到数据
    if (status & 0x40)  // RX_DR位
    {
        CE = 0;  // 关闭使能
        
        CSN = 0;  // 片选低
        SPI_RW(R_RX_PAYLOAD);  // 读数据命令
        
        // 读取32字节数据
        for (i = 0; i < 32; i++) 
        {
            rx_buf[i] = SPI_RW(0);
        }
        rx_len = 32;
        CSN = 1;  // 片选高
        
        // 清除RX_DR标志
        SPI_Write(STATUS, 0x40);
        
        CE = 1;  // 重新使能接收
    }
    
    return rx_len;  // 返回接收到的字节数
}

4、串口通信(用于调试)

c 复制代码
// uart.c
#include "uart.h"

// 串口初始化,9600波特率,11.0592MHz晶振
void UART_Init(void)
{
    SCON = 0x50;  // 串口模式1,允许接收
    TMOD |= 0x20; // 定时器1,模式2
    TH1 = 0xFD;   // 9600波特率
    TL1 = 0xFD;
    TR1 = 1;      // 启动定时器1
    ES = 1;       // 允许串口中断
    EA = 1;       // 开启总中断
}

// 发送一个字节
void UART_SendByte(unsigned char dat)
{
    SBUF = dat;
    while (!TI);  // 等待发送完成
    TI = 0;       // 清除发送完成标志
}

// 发送字符串
void UART_SendString(unsigned char *str)
{
    while (*str != '\0')
    {
        UART_SendByte(*str);
        str++;
    }
}

// 发送16进制数
void UART_SendHex(unsigned char hex)
{
    unsigned char temp;
    
    temp = (hex >> 4) & 0x0F;
    if (temp < 10)
        UART_SendByte(temp + '0');
    else
        UART_SendByte(temp - 10 + 'A');
    
    temp = hex & 0x0F;
    if (temp < 10)
        UART_SendByte(temp + '0');
    else
        UART_SendByte(temp - 10 + 'A');
}

5、主程序

c 复制代码
// main.c
#include <reg52.h>
#include "nrf24l01.h"
#include "uart.h"

// 定义LED指示灯
sbit LED1 = P2^0;
sbit LED2 = P2^1;
sbit LED3 = P2^2;
sbit LED4 = P2^3;

// 延时函数
void Delay_ms(unsigned int ms)
{
    unsigned int i, j;
    for (i = ms; i > 0; i--)
        for (j = 110; j > 0; j--);
}

// 主函数
void main(void)
{
    unsigned char rx_buffer[32] = {0};  // 接收缓冲区
    unsigned char i, rx_len = 0;
    
    UART_Init();               // 串口初始化
    NRF24L01_Init();           // NRF24L01初始化
    NRF24L01_RX_Mode();        // 设置为接收模式
    
    UART_SendString("NRF24L01 Receiver Start!\r\n");
    UART_SendString("Waiting for data...\r\n");
    
    LED1 = 0;  // 点亮LED1表示准备就绪
    
    while (1)
    {
        // 尝试接收数据
        rx_len = NRF24L01_RxPacket(rx_buffer);
        
        if (rx_len > 0)  // 如果接收到数据
        {
            LED2 = ~LED2;  // LED2闪烁表示接收到数据
            
            UART_SendString("Received: ");
            
            // 打印接收到的数据
            for (i = 0; i < rx_len; i++)
            {
                UART_SendHex(rx_buffer[i]);
                UART_SendByte(' ');
            }
            UART_SendString("\r\n");
            
            // 解析命令
            if (rx_buffer[0] == 0x01)  // 开灯命令
            {
                LED3 = 1;
                LED4 = 0;
                UART_SendString("Command: Turn ON\r\n");
            }
            else if (rx_buffer[0] == 0x02)  // 关灯命令
            {
                LED3 = 0;
                LED4 = 1;
                UART_SendString("Command: Turn OFF\r\n");
            }
            else if (rx_buffer[0] == 0x03)  // 状态查询
            {
                UART_SendString("Command: Query Status\r\n");
                UART_SendString("Status: OK\r\n");
            }
        }
        
        // 防止接收太快
        Delay_ms(100);
    }
}

三、发送端参考程序(STM32)

c 复制代码
// 发送端核心代码(STM32)
void NRF24L01_TxPacket(unsigned char *tx_buf)
{
    CE = 0;  // 禁止发射
    
    // 写入发送地址
    CSN = 0;
    SPI_RW(W_REGISTER + TX_ADDR);
    SPI_RW(0x34);
    SPI_RW(0x43);
    SPI_RW(0x10);
    SPI_RW(0x10);
    SPI_RW(0x01);
    CSN = 1;
    
    // 写入数据
    CSN = 0;
    SPI_RW(W_TX_PAYLOAD);
    for (i = 0; i < 32; i++)
    {
        SPI_RW(tx_buf[i]);
    }
    CSN = 1;
    
    // 启动发射
    CE = 1;
    Delay_us(20);  // 至少维持10us
    CE = 0;
}

四、测试步骤

1、硬件检查

  1. 电压确认是 3.3V
  2. 检查所有引脚连接
  3. 晶振是否起振(11.0592MHz)

2、软件设置

c 复制代码
// 发送端发送的数据格式
unsigned char tx_data[32] = {
    0x01,  // 命令:开灯
    0xAA, 0xBB, 0xCC, 0xDD,  // 数据
    // ... 其他数据
};

3、串口调试

  1. 打开串口调试助手

  2. 设置波特率 9600

  3. 接收端上电后显示:

    复制代码
    NRF24L01 Receiver Start!
    Waiting for data...
  4. 发送端发送数据后,接收端显示:

    复制代码
    Received: 01 AA BB CC DD ...
    Command: Turn ON

参考代码 51单片机NRF24L01接收程序 www.youwenfan.com/contentcsv/102502.html

五、常见问题解决

问题 可能原因 解决方案
接收不到数据 1. 地址不匹配 2. 通道不同 3. 速率不同 1. 检查收发地址 2. 检查RF_CH寄存器 3. 检查RF_SETUP寄存器
数据错误 1. 电源不稳 2. 时钟干扰 1. 加100uF和0.1uF电容 2. 缩短连线
距离短 1. 天线问题 2. 速率太高 1. 检查天线焊接 2. 降低速率到250kbps
通信不稳定 1. 同频干扰 2. 距离过远 1. 更换RF_CH通道 2. 增加增强型PA LNA模块

六、增强功能

1、自动应答模式

c 复制代码
// 启用自动应答
void Enable_Auto_Ack(void)
{
    SPI_Write(EN_AA, 0x01);     // 通道0自动应答
    SPI_Write(SETUP_RETR, 0x2F); // 自动重发5次,间隔750us
}

2、多通道接收

c 复制代码
// 配置多个接收通道
void MultiChannel_RX(void)
{
    // 通道1地址
    SPI_Write(RX_ADDR_P1, 0xC2);
    SPI_Write(RX_ADDR_P1+1, 0xC2);
    SPI_Write(RX_ADDR_P1+2, 0xC2);
    SPI_Write(RX_ADDR_P1+3, 0xC2);
    SPI_Write(RX_ADDR_P1+4, 0xC2);
    SPI_Write(EN_RXADDR, 0x03);  // 使能通道0和1
}

3、低功耗模式

c 复制代码
// 进入掉电模式
void Power_Down(void)
{
    unsigned char config = SPI_Read(CONFIG);
    SPI_Write(CONFIG, config & 0xFE);  // 清除PWR_UP位
    CE = 0;
}

// 唤醒
void Power_Up(void)
{
    unsigned char config = SPI_Read(CONFIG);
    SPI_Write(CONFIG, config | 0x02);  // 设置PWR_UP位
    Delay_ms(5);  // 等待稳定
}

七、性能测试

测试项目 结果
最远距离(空旷) 50-100米
最高速率 2Mbps
平均功耗 12.3mA(接收)
响应时间 < 5ms
数据包大小 1-32字节

八、完整工程结构

复制代码
NRF24L01_RX_51/
├── Source/
│   ├── main.c          # 主程序
│   ├── nrf24l01.c      # NRF24L01驱动
│   ├── spi.c           # SPI底层驱动
│   └── uart.c          # 串口通信
├── Include/
│   ├── nrf24l01.h
│   ├── spi.h
│   └── uart.h
├── Project/
│   └── NRF24L01.uvproj  # Keil工程
└── README.txt
相关推荐
agathakuan1 小时前
從零開始在家開發 IoT: VM編譯到Windows燒錄與逐sector抹除原因
嵌入式硬件·iot·virtualmachine
XINVRY-FPGA2 小时前
XC7Z035-2FFG900I Xilinx/AMD Zynq-7000 SoC FPGA
人工智能·嵌入式硬件·计算机视觉·fpga开发·硬件工程·dsp开发·fpga
fffzd2 小时前
STM32:串口--DMA
stm32·单片机·嵌入式硬件·串口·dma·回调函数
踏着七彩祥云的小丑2 小时前
嵌入式测试学习第 28 天:网络调试助手使用、TCP服务端客户端实操
单片机·嵌入式硬件·学习
不脱发的程序猿15 小时前
AI Coding时上下文不够用咋办?
单片机·嵌入式硬件·嵌入式
leoFY12316 小时前
SGM3209(圣邦微 高压负压电荷泵)(与TP7660可只修改4脚,7脚即可替换)
单片机·嵌入式硬件
zlinear数据采集卡17 小时前
基准电压电路深度解析:从理论参数到ZLinear采集卡的精准参考实战
c语言·单片机·嵌入式硬件·fpga开发·自动化
下午写HelloWorld17 小时前
GD32F4系列微控制器上电启动流程
单片机·嵌入式硬件
daad77717 小时前
记录一次ardupilot_sitl调试longitude的输入数据流
单片机·嵌入式硬件