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
相关推荐
女神下凡2 小时前
芯参谋(43):DDR4_电路设计指南
arm开发·单片机·嵌入式硬件·设计规范
电子元器件选型哥3 小时前
MOS管驱动电阻怎么选?阻值大小到底影响什么?
单片机·嵌入式硬件·硬件工程师·半导体·电子元器件
白搞电子4 小时前
ESP32-S31 上做可在线安装的 WASM 应用平台:0.4.9 Beta 发布
单片机·嵌入式硬件·物联网
水果里面有苹果4 小时前
48-AS5600旋转磁编码器
嵌入式硬件
iCxhust4 小时前
8088的nmi中断能关闭吗?
嵌入式硬件·8088单板机
黑妹天下第一乖8 小时前
第 08 讲:阿加犀 AidGenSE 端侧大模型服务化与 OpenAI 兼容接口
人工智能·嵌入式硬件·深度学习·机器人·iot
辰哥单片机设计9 小时前
漏水传感器(STM32)
stm32·单片机·嵌入式硬件
恶魔泡泡糖9 小时前
stm32F103C8T6标准库串口接收之多串口基础应用5
stm32·单片机·嵌入式硬件
木子单片机9 小时前
基于51单片机计步器—无线蓝牙APP上传
单片机·嵌入式硬件·51单片机·keil
恒锐丰科技林技术员10 小时前
EG2123A 三相独立半桥驱动芯片解析与工程应用
经验分享·嵌入式硬件·硬件工程