一、硬件连接(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、硬件检查
- 电压确认是 3.3V
- 检查所有引脚连接
- 晶振是否起振(11.0592MHz)
2、软件设置
c
// 发送端发送的数据格式
unsigned char tx_data[32] = {
0x01, // 命令:开灯
0xAA, 0xBB, 0xCC, 0xDD, // 数据
// ... 其他数据
};
3、串口调试
-
打开串口调试助手
-
设置波特率 9600
-
接收端上电后显示:
NRF24L01 Receiver Start! Waiting for data... -
发送端发送数据后,接收端显示:
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