一、硬件连接方案
1.1 nRF24L01模块引脚定义
nRF24L01引脚 → STC15W4K32S4引脚
1 (GND) → GND
2 (VCC) → 3.3V(必须使用3.3V供电)
3 (CE) → P2.5(可自定义)
4 (CSN) → P2.4(可自定义)
5 (SCK) → P1.5(SPI时钟)
6 (MOSI) → P1.3(主机输出从机输入)
7 (MISO) → P1.4(主机输入从机输出)
8 (IRQ) → P3.2(外部中断0,可选)
1.2 电源注意事项
- 必须使用3.3V供电:nRF24L01工作电压为1.9-3.6V,推荐3.3V
- 电源滤波:VCC引脚需并联10μF和0.1μF电容
- 天线匹配:确保天线阻抗匹配为50Ω
二、STC15W4K32S4 SPI配置
2.1 硬件SPI配置(推荐)
STC15W4K32S4内置硬件SPI模块,支持主从模式,最高时钟频率为Fosc/4。
c
#include "stc15w4k32s4.h"
#include <intrins.h>
// SPI引脚定义(第二组SPI)
sbit CE = P2^5; // 芯片使能
sbit CSN = P2^4; // 片选
sbit IRQ = P3^2; // 中断引脚(可选)
// SPI初始化函数
void SPI_Init(void) {
// 重要:STC15W4K32S4上电后PWM相关IO口为高阻态
// 需设置为准双向口或强推挽模式
P0M1 = 0; P0M0 = 0; // 设置P0.0~P0.7为准双向口
P2M1 = 0; P2M0 = 0; // 设置P2.0~P2.7为准双向口
P3M1 = 0; P3M0 = 0; // 设置P3.0~P3.7为准双向口
P4M1 = 0; P4M0 = 0; // 设置P4.0~P4.7为准双向口
P5M1 = 0; P5M0 = 0; // 设置P5.0~P5.7为准双向口
// 配置SPI为第二组引脚(P1.5/SCLK, P1.3/MOSI, P1.4/MISO)
P_SW1 &= 0x3F; // 清除SPI切换位
P_SW1 |= 0x40; // SPI切换到第二组(SPI_S1=1, SPI_S0=0)
// SPI控制寄存器配置
SPSTAT = 0xC0; // 清除SPIF和WCOL标志位
SPCTL = 0xD0; // 使能SPI,主机模式,时钟最快
// SSIG=1(忽略SS引脚),SPEN=1(使能SPI)
// DORD=0(MSB先发送),MSTR=1(主机模式)
// CPOL=0(时钟空闲低电平),CPHA=0(数据在第一个时钟沿采样)
// 初始化nRF24L01控制引脚
CE = 0; // 芯片使能低
CSN = 1; // 片选高(不选中)
// 如果使用中断
// IT0 = 1; // 设置INT0为下降沿触发
// EX0 = 1; // 使能INT0中断
// EA = 1; // 开启总中断
}
// SPI数据交换函数
unsigned char SPI_SwapByte(unsigned char dat) {
SPDAT = dat; // 写入数据,启动传输
while (!(SPSTAT & 0x80)); // 等待传输完成(SPIF=1)
SPSTAT = 0xC0; // 清除SPIF和WCOL标志
return SPDAT; // 返回接收到的数据
}
2.2 软件模拟SPI(备用方案)
如果硬件SPI被占用,可使用GPIO模拟SPI时序:
c
// 软件SPI引脚定义
sbit CE = P2^5;
sbit CSN = P2^4;
sbit SCK = P1^5;
sbit MOSI = P1^3;
sbit MISO = P1^4;
sbit IRQ = P3^2;
// 软件SPI字节读写
unsigned char SPI_RW(unsigned char byte) {
unsigned char i;
for(i = 0; i < 8; i++) {
MOSI = (byte & 0x80); // 输出最高位
byte <<= 1; // 左移一位
SCK = 1; // 时钟上升沿
byte |= MISO; // 读取MISO数据
SCK = 0; // 时钟下降沿
}
return byte;
}
三、nRF24L01驱动代码
3.1 nRF24L01寄存器定义
c
// nRF24L01寄存器地址
#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 // 接收地址通道2
#define RX_ADDR_P3 0x0D // 接收地址通道3
#define RX_ADDR_P4 0x0E // 接收地址通道4
#define RX_ADDR_P5 0x0F // 接收地址通道5
#define TX_ADDR 0x10 // 发送地址
#define RX_PW_P0 0x11 // 接收数据宽度通道0
#define RX_PW_P1 0x12 // 接收数据宽度通道1
#define RX_PW_P2 0x13 // 接收数据宽度通道2
#define RX_PW_P3 0x14 // 接收数据宽度通道3
#define RX_PW_P4 0x15 // 接收数据宽度通道4
#define RX_PW_P5 0x16 // 接收数据宽度通道5
#define FIFO_STATUS 0x17 // FIFO状态寄存器
#define DYNPD 0x1C // 动态载荷长度
#define FEATURE 0x1D // 特性寄存器
// SPI命令
#define READ_REG 0x00 // 读寄存器
#define WRITE_REG 0x20 // 写寄存器
#define RD_RX_PLOAD 0x61 // 读RX有效数据
#define WR_TX_PLOAD 0xA0 // 写TX有效数据
#define FLUSH_TX 0xE1 // 清空TX FIFO
#define FLUSH_RX 0xE2 // 清空RX FIFO
#define REUSE_TX_PL 0xE3 // 重用TX有效数据
#define NOP 0xFF // 空操作
// 状态寄存器位定义
#define RX_DR 6 // 数据就绪接收中断
#define TX_DS 5 // 数据发送完成中断
#define MAX_RT 4 // 达到最大重发次数中断
3.2 nRF24L01基本操作函数
c
// 写寄存器
unsigned char NRF24L01_Write_Reg(unsigned char reg, unsigned char value) {
unsigned char status;
CSN = 0; // 使能SPI传输
status = SPI_SwapByte(WRITE_REG | reg); // 发送寄存器地址
SPI_SwapByte(value); // 写入值
CSN = 1; // 禁止SPI传输
return status; // 返回状态寄存器值
}
// 读寄存器
unsigned char NRF24L01_Read_Reg(unsigned char reg) {
unsigned char value;
CSN = 0;
SPI_SwapByte(READ_REG | reg); // 发送读命令
value = SPI_SwapByte(NOP); // 读取寄存器值
CSN = 1;
return value;
}
// 读多个字节
void NRF24L01_Read_Buf(unsigned char reg, unsigned char *pBuf, unsigned char len) {
unsigned char i;
CSN = 0;
SPI_SwapByte(READ_REG | reg); // 发送读命令
for(i = 0; i < len; i++) {
pBuf[i] = SPI_SwapByte(NOP); // 连续读取
}
CSN = 1;
}
// 写多个字节
void NRF24L01_Write_Buf(unsigned char reg, unsigned char *pBuf, unsigned char len) {
unsigned char i;
CSN = 0;
SPI_SwapByte(WRITE_REG | reg); // 发送写命令
for(i = 0; i < len; i++) {
SPI_SwapByte(pBuf[i]); // 连续写入
}
CSN = 1;
}
// 读取接收数据
void NRF24L01_Read_RX_Buf(unsigned char *pBuf, unsigned char len) {
CSN = 0;
SPI_SwapByte(RD_RX_PLOAD); // 发送读数据命令
for(unsigned char i = 0; i < len; i++) {
pBuf[i] = SPI_SwapByte(NOP); // 读取数据
}
CSN = 1;
// 清除RX_DR中断标志
NRF24L01_Write_Reg(STATUS, 0x40);
}
// 清空RX FIFO
void NRF24L01_Flush_RX(void) {
CSN = 0;
SPI_SwapByte(FLUSH_RX);
CSN = 1;
}
// 清空TX FIFO
void NRF24L01_Flush_TX(void) {
CSN = 0;
SPI_SwapByte(FLUSH_TX);
CSN = 1;
}
四、nRF24L01接收端配置
4.1 初始化函数
c
// 接收地址(5字节)
unsigned char RX_ADDRESS[5] = {0x34, 0x43, 0x10, 0x10, 0x01};
// nRF24L01初始化
void NRF24L01_Init(void) {
CE = 0; // 芯片使能低
CSN = 1; // 片选高
// 延时等待模块稳定
Delay_ms(5);
// 配置为接收模式
Set_RxMode();
// 检查模块是否正常
if(NRF24L01_Check()) {
// 初始化成功
printf("nRF24L01 Init OK!\r\n");
} else {
// 初始化失败
printf("nRF24L01 Init Failed!\r\n");
}
}
// 设置接收模式
void Set_RxMode(void) {
CE = 0; // 先拉低CE
// 写入接收地址(通道0)
NRF24L01_Write_Buf(RX_ADDR_P0, RX_ADDRESS, 5);
// 设置接收数据宽度(32字节)
NRF24L01_Write_Reg(RX_PW_P0, 32);
// 使能通道0自动应答
NRF24L01_Write_Reg(EN_AA, 0x01);
// 使能通道0接收地址
NRF24L01_Write_Reg(EN_RXADDR, 0x01);
// 设置地址宽度为5字节
NRF24L01_Write_Reg(SETUP_AW, 0x03);
// 设置RF频道(2.4GHz + RF_CH值)
NRF24L01_Write_Reg(RF_CH, 40); // 2.440GHz
// 设置RF参数:2Mbps速率,0dBm发射功率
NRF24L01_Write_Reg(RF_SETUP, 0x0F);
// 配置基本工作模式:PWR_UP, EN_CRC, 16BIT_CRC, PRIM_RX接收模式
NRF24L01_Write_Reg(CONFIG, 0x0F);
// 清空RX FIFO
NRF24L01_Flush_RX();
// 清空状态寄存器
NRF24L01_Write_Reg(STATUS, 0x70);
CE = 1; // 使能接收模式
Delay_us(130); // 等待稳定
}
// 检查nRF24L01是否正常
unsigned char NRF24L01_Check(void) {
unsigned char buf[5] = {0};
unsigned char i;
// 写入测试地址
unsigned char test_addr[5] = {0x11, 0x22, 0x33, 0x44, 0x55};
NRF24L01_Write_Buf(TX_ADDR, test_addr, 5);
// 读取地址进行比较
NRF24L01_Read_Buf(TX_ADDR, buf, 5);
// 比较地址
for(i = 0; i < 5; i++) {
if(buf[i] != test_addr[i]) {
return 0; // 检查失败
}
}
return 1; // 检查成功
}
4.2 数据接收处理
c
// 接收缓冲区
unsigned char RX_Buffer[32];
unsigned char RX_Flag = 0;
// 检查接收状态
unsigned char NRF24L01_RxPacket(void) {
unsigned char status;
status = NRF24L01_Read_Reg(STATUS); // 读取状态寄存器
if(status & 0x40) { // 检查RX_DR位
// 读取接收数据
NRF24L01_Read_RX_Buf(RX_Buffer, 32);
// 清除中断标志
NRF24L01_Write_Reg(STATUS, 0x40);
return 1; // 接收成功
}
return 0; // 没有数据
}
// 中断方式接收(使用IRQ引脚)
void INT0_ISR(void) interrupt 0 {
unsigned char status;
status = NRF24L01_Read_Reg(STATUS);
if(status & 0x40) { // RX_DR中断
// 读取数据
NRF24L01_Read_RX_Buf(RX_Buffer, 32);
RX_Flag = 1; // 设置接收标志
// 清除中断标志
NRF24L01_Write_Reg(STATUS, 0x40);
}
if(status & 0x20) { // TX_DS中断
// 发送完成(如果也用于发送)
NRF24L01_Write_Reg(STATUS, 0x20);
}
if(status & 0x10) { // MAX_RT中断
// 达到最大重发次数
NRF24L01_Flush_TX(); // 清空TX FIFO
NRF24L01_Write_Reg(STATUS, 0x10);
}
}
五、主程序示例
5.1 轮询方式接收
c
#include "stc15w4k32s4.h"
#include "nrf24l01.h"
#include <stdio.h>
void main(void) {
unsigned char i;
// 系统初始化
System_Init(); // 系统时钟初始化
UART1_Init(); // 串口初始化,用于调试
SPI_Init(); // SPI初始化
NRF24L01_Init(); // nRF24L01初始化
printf("STC15W4K32S4 nRF24L01 Receiver Started!\r\n");
while(1) {
// 检查是否有数据
if(NRF24L01_RxPacket()) {
printf("Received Data: ");
// 打印接收到的数据
for(i = 0; i < 32; i++) {
if(RX_Buffer[i] != 0) {
printf("%02X ", RX_Buffer[i]);
}
}
printf("\r\n");
// 处理数据
Process_Data(RX_Buffer);
}
// 其他任务
Delay_ms(10);
}
}
// 系统初始化
void System_Init(void) {
// 设置系统时钟(内部22.1184MHz)
CLK_DIV = 0x00;
// IO口模式设置
P0M1 = 0; P0M0 = 0;
P1M1 = 0; P1M0 = 0;
P2M1 = 0; P2M0 = 0;
P3M1 = 0; P3M0 = 0;
P4M1 = 0; P4M0 = 0;
P5M1 = 0; P5M0 = 0;
}
// 串口1初始化(用于调试)
void UART1_Init(void) {
SCON = 0x50; // 8位数据,可变波特率
AUXR |= 0x40; // 定时器1时钟为Fosc
AUXR &= 0xFE; // 定时器1为12T模式
TMOD &= 0x0F; // 清除定时器1模式位
TMOD |= 0x20; // 定时器1为8位自动重装
TH1 = 0xFA; // 波特率9600@22.1184MHz
TL1 = 0xFA;
TR1 = 1; // 启动定时器1
ES = 1; // 使能串口中断(可选)
EA = 1; // 开启总中断
}
5.2 中断方式接收
c
// 中断方式主程序
void main(void) {
// 系统初始化
System_Init();
UART1_Init();
SPI_Init();
NRF24L01_Init();
// 配置外部中断0(IRQ引脚)
IT0 = 1; // 下降沿触发
EX0 = 1; // 使能INT0中断
EA = 1; // 开启总中断
printf("nRF24L01 Receiver with Interrupt Mode\r\n");
while(1) {
// 主循环处理其他任务
if(RX_Flag) {
RX_Flag = 0; // 清除标志
printf("Interrupt Received: ");
for(unsigned char i = 0; i < 32; i++) {
if(RX_Buffer[i] != 0) {
printf("%c", RX_Buffer[i]);
}
}
printf("\r\n");
}
// 低功耗模式或延时
Delay_ms(100);
}
}
参考代码 STC15W4K32S4系列单片机2.4G无线接收nRF24L01 www.youwenfan.com/contentcsv/71229.html
六、调试与故障排除
6.1 常见问题及解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无法通信 | 电源电压不正确 | 确保使用3.3V供电,检查电源滤波电容 |
| 通信距离短 | 天线匹配问题 | 检查天线阻抗是否为50Ω,确保天线完好 |
| 数据错误 | SPI时序问题 | 降低SPI时钟频率,检查接线是否可靠 |
| 无法进入接收模式 | 寄存器配置错误 | 检查CONFIG寄存器PRIM_RX位是否设置为1 |
| 频繁丢包 | RF频道干扰 | 更换RF_CH值,避开WiFi频道(1,6,11) |
6.2 调试建议
- 使用逻辑分析仪:检查SPI时序是否正确
- 串口调试:通过串口打印状态寄存器值
- 电源监测:使用示波器检查3.3V电源是否稳定
- 信号测试:使用频谱分析仪检查2.4GHz信号质量
七、性能优化建议
7.1 提高通信可靠性
c
// 启用自动重发
void Enable_Auto_Retransmit(void) {
// 设置自动重发延迟250us,重发15次
NRF24L01_Write_Reg(SETUP_RETR, 0x5F);
}
// 启用动态载荷长度
void Enable_Dynamic_Payload(void) {
NRF24L01_Write_Reg(FEATURE, 0x06); // 使能动态载荷和ACK载荷
NRF24L01_Write_Reg(DYNPD, 0x01); // 通道0启用动态载荷
}
// 设置发射功率和速率
void Set_RF_Power_Rate(unsigned char power, unsigned char rate) {
unsigned char rf_setup = NRF24L01_Read_Reg(RF_SETUP);
// 清除功率和速率位
rf_setup &= 0xF0;
// 设置功率(00:-18dBm, 01:-12dBm, 10:-6dBm, 11:0dBm)
rf_setup |= (power << 1);
// 设置速率(0:1Mbps, 1:2Mbps)
if(rate == 1) {
rf_setup |= 0x08; // 2Mbps
}
NRF24L01_Write_Reg(RF_SETUP, rf_setup);
}
7.2 低功耗优化
c
// 进入低功耗模式
void Enter_Power_Down_Mode(void) {
unsigned char config = NRF24L01_Read_Reg(CONFIG);
config &= ~0x02; // 清除PWR_UP位
NRF24L01_Write_Reg(CONFIG, config);
CE = 0; // 确保CE为低
}
// 唤醒从低功耗模式
void Wake_Up_From_Power_Down(void) {
unsigned char config = NRF24L01_Read_Reg(CONFIG);
config |= 0x02; // 设置PWR_UP位
NRF24L01_Write_Reg(CONFIG, config);
Delay_ms(5); // 等待稳定
}
八、完整工程文件结构
nRF24L01_Receiver/
├── main.c # 主程序
├── nrf24l01.c # nRF24L01驱动
├── nrf24l01.h # nRF24L01头文件
├── spi.c # SPI驱动
├── spi.h # SPI头文件
├── uart.c # 串口驱动
├── uart.h # 串口头文件
├── delay.c # 延时函数
├── delay.h # 延时头文件
└── STC15W4K32S4.H # 单片机寄存器定义