STM32
直播中

张娜

7年用户 930经验值
私信 关注
[问答]

如何解决nRF24L01模块设计和调试遇到的问题?

nRF24L01的最多互通节点数量是多少呢?

如何解决nRF24L01模块设计和调试遇到的问题?

回帖(1)

于扬

2021-12-17 09:36:52
成就更好的自己

课设用到了nRF24L01模块,设计和调试前后出现一些问题,开一篇博客说一下理论方面和使用中的问题


元件介绍:

宽电压工作范围,1.9V~3.6V,输入引脚可承受5V电压输入
工作温度范围,-40℃~+80℃
调制方式:GFSK/FSK
工作频率范围,2.400GHz~2.525GHz
发射功率可选择为0dBm、-6dBm、-12dBm和-18dBm
数据传输速率支持1Mbps、2Mbps [1]
极少外围器件,降低系统应用成本
低功耗设计,接收时工作电流12.3mA,0dBm功率发射时11.3mA,掉电模式时仅为900nA
126个通讯通道,6个数据通道,满足多点通讯和调频需要
增强型“ShockBurst”工作模式,硬件的CRC校验和点对多点的地址控制
数据包每次可传输1~32Byte的数据
4线SPI通讯端口,通讯速率最高可达8Mbps,适合与各种MCU连接,编程简单可通过软件设置工作频率、通讯地址、传输速率和数据包长度
MCU可通过IRQ引脚块判断是否完成数据接收和数据发送
芯片引脚(QFN20 4x4mm封装):





典型应用威廉希尔官方网站 :







一般用到的也都是制作好的成品模块,如下:





对应的引脚定义及功能如下:
CE:模块控制线,CSN为低时,CE协同CONFIG寄存器共同决定NRF24L01状态(发送或接收模式)
CSN:SPI片选线,控制是否激活配置模式
SCK: SPI时钟线
MOSI:SPI数据线(主机输出从机输入)
MISO:SPI数据线(主机输入从机输出)
IRQ:中断信号线。中断时变为低电平,在以下三种情况变低:Tx FIFO发完并且收到ACK(使能ACK情况下)、Rx FIFO收到数据、达到最大重发次数


工作模式及理论知识:

按照博主的理解,言简意赅的说一下
Nrf24L01一般使用只在这3个模式下:
待机模式
接收模式
发送模式(细分为准备发送和正在发送两种模式)





且在上面三种模式下,只有待机模式和准备发送模式下才能对nRF内部各寄存器进行修改

与MCU通信方式:SPI通信

所采用的通信协议如下:
每次通讯由两部分内容组成:指令格式+数据
而且指令格式也由两部分组成:命令+寄存器地址(有时候寄存器地址为空,只有命令,是因为有的命令默认对指定的寄存器操作,理解不了的看下图)
综上所示,每次通信为命令+寄存器地址+数据;
下图为指令格式(命令+寄存器地址)





如果是写寄存器的话,指令格式后的数据是由主机发出的
如果是读寄存器的话,指令格式后的数据由nRF24L01发出
nFR寄存器太多,就不一一介绍了,我会在代码段介绍几个常用的寄存器

nRF24L01之间的无线通信

上面讲的都是主机与nRF24L01的总线通信,是对Nrf24L01的设置和数据传输,下面讲解nRF24L01之间的无线通信和数据收发
nrf发送数据是以包来发送





前导码和CRC不用管,具体看中间两部分:
地址:地址也就是接收到通道的地址,,1个nRF有6个通道,每个通道有有自己的一个地址





如果是能了自动应答,那么我们得将发送地址(TX_ADDR) 和接受应答信号的通道地址(RX_ADDR_P0)设置为一样的。










数据:接收至少1字节,最多32字节,作为一次通信的数据包,这个是需要设置的,这个接收的数据有效宽度需要与发射端的发射数据宽度一致,否则会出现各种问题;





一次完整的发送数据流程如下:
发送流程:发送方根据发送地址(TX_ADDR)发送到接收方(RX_ADDR_P0)接收方收到数据后以接收通道的地址(RX_ADDR_P0)为发送地址发送应答信号给发送方(这个应答信号为自动发送不需要人为发送)。
例如:发送方TX_ADDR=0x10=接收方接收通道地址(以通道0为例:RX_ADDR_P0)=发送端接收通道地址(以通道0为例:RX_ADDR_P0),  总的来说就是接受方发送地址和接受应答信号的通道地址和接受方地址要一致。
多机组网只需要使能接收方全部通道并且分配好地址就ok了。
对于地址的分配要注意:
通道0和通道1的地址为5字节可随意给值。
其他2~5高4字节与通道1高四字节相同(通用高4字节)我们只能修改他的左后一个字节地址

细枝末节知识点:

IRQ引脚一般接到普通IO口上,有时候为了确保实时性和对接收的数据尽快处理,会把IRQ接到外部中断上,在中断服务子函数处理数据;
IRQ产生低电平的条件是1.发送数据成功(开应答的必须要收到应答后才算完成)2.接收数据成功3.发送数据达到最大重发次数,且可以设置这三个条件发生后是否产生中断;





博主在开始的时候一直以为发送方的数据只要在32字节内,接收方都会接受,而且全都放入RX_FIFO寄存器(临时存放已经接受的数据的寄存器),只不过读RX_FIFO寄存器时读的是有效长度的数据,但是实际情况是只会让有效长度的数据进入RX_FIFO寄存器;所以一定要保证发送接收双方的数据长度一致,否则会影响数据帧中CRC部分的数据进而导致通信失败;
写入寄存器的时候记得拉低CE引脚;
nRF24L01的最多互通节点数量是多少呢?答案是无数个;

一对一通信源码加注释:

平台:STM32F103C8T6
头文件:

#include "stm32f10x.h"
#include "sys.h"

typedef unsigned char uint8;
typedef unsigned int uint16;

/**********************
引脚别名定义
***********************/       
#define NRF_CE GPIO_Pin_3         //使能控制
#define NRF_CS GPIO_Pin_4         //模拟SPI片选
#define SPI_CLK GPIO_Pin_5         //模拟SPI时钟
#define SPI_MOSI GPIO_Pin_7     //模拟SPI主出从入
#define SPI_MISO GPIO_Pin_6     //模拟SPI主入从出
#define NRF_IRQ GPIO_Pin_2             //中断
#define NRF_IRQ_STATUS GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2)
#define CE_LOW PAout(3)=0        //带位定义,使用会报错,修改为原始库函数GPIO_ResetBits即可
#define CE_HIGH PAout(3)=1        //同上
#define CS_LOW PAout(4)=0        //同上
#define CS_HIGH PAout(4)=1        //同上

//无线收发地址宽度(字节数)
#define TX_ADDR_WIDTH 5
#define RX_ADDR_WIDTH 5

//无线收发数据长度(字节数)
#define TX_PLOAD_WIDTH 1
#define RX_PLOAD_WIDTH 1

//无线收发中断标志
#define MAX_TX              0x10    //达到最大发送次数中断
#define TX_OK               0x20    //TX发送完成中断
#define RX_OK               0x40    //接收到数据中断

/****************************************************************************************************/
//NRF24L01寄存器操作命令(共11个)
#define SPI_READ_REG    0x00  //读配置寄存器,低5位为寄存器地址
#define SPI_WRITE_REG   0x20  //写配置寄存器,低5位为寄存器地址
#define RD_RX_PLOAD     0x61  //读RX有效数据(低字节先出),1~32字节
#define WR_TX_PLOAD     0xA0  //写TX有效数据,1~32字节
#define FLUSH_TX        0xE1  //清除TX FIFO寄存器.发射模式下用
#define FLUSH_RX        0xE2  //清除RX FIFO寄存器.接收模式下用
#define REUSE_TX_PL     0xE3  //重新使用上一包数据,CE为高,数据包被不断发送
#define RD_RX_PL_WID    0x60  //读RX有效数据(高字节先出),1~32字节
#define W_ACK_PLOAD     0xA0  //发送寄存器,写入数据可发送出去
#define W_TX_PLOAD_NACK 0xB0  //发送寄存器,写入数据可发送出去,但不应答
#define RF_NOP          0xFF  //空操作,可以用来读状态寄存器         
//NRF24L01寄存器地址(共24个)
#define CONFIG          0x00  //配置寄存器地址;bit0:1接收模式,0发射模式;bit1:电选择;bit2:CRC模式;bit3:CRC使能;
                              //bit4:中断MAX_RT(达到最大重发次数中断)使能;bit5:中断TX_DS使能;bit6:中断RX_DR使能
#define EN_AA           0x01  //使能自动应答功能  bit0~5,对应通道0~5
#define EN_RXADDR       0x02  //接收地址允许,bit0~5,对应通道0~5
#define SETUP_AW        0x03  //设置地址宽度(所有数据通道):bit1,0:00,3字节;01,4字节;02,5字节;
#define SETUP_RETR      0x04  //建立自动重发;bit3:0,自动重发计数器;bit7:4,自动重发延时 250*x+86us
#define RF_CH           0x05  //RF通道,bit6:0,工作通道频率;
#define RF_SETUP        0x06  //RF寄存器;bit5,bit3:传输速率(00:1Mbps,01:2Mbps,10:250Kbps);bit2:1,发射功率;bit0:低噪声放大器增益
#define STATUS          0x07  //状态寄存器;bit0:TX FIFO满标志;bit3:1,接收数据通道号(最大:6);bit4,达到最多次重发
                              //bit5:数据发送完成中断;bit6:接收数据中断;
#define OBSERVE_TX      0x08  //发送检测寄存器,bit7:4,数据包丢失计数器;bit3:0,重发计数器
#define CD              0x09  //载波检测寄存器,bit0,载波检测;
#define RX_ADDR_P0      0x0A  //数据通道0接收地址,最大长度5个字节,低字节在前
#define RX_ADDR_P1      0x0B  //数据通道1接收地址,最大长度5个字节,低字节在前
#define RX_ADDR_P2      0x0C  //数据通道2接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P3      0x0D  //数据通道3接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P4      0x0E  //数据通道4接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P5      0x0F  //数据通道5接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define TX_ADDR         0x10  //发送地址(低字节在前),ShockBurstTM模式下,RX_ADDR_P0与此地址相等
#define RX_PW_P0        0x11  //接收数据通道0有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P1        0x12  //接收数据通道1有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P2        0x13  //接收数据通道2有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P3        0x14  //接收数据通道3有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P4        0x15  //接收数据通道4有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P5        0x16  //接收数据通道5有效数据宽度(1~32字节),设置为0则非法
#define FIFO_STATUS     0x17  //FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RX FIFO满标志;bit2,3,保留
                              //bit4,TX FIFO空标志;bit5,TX FIFO满标志;bit6,1,循环发送上一数据包.0,不循环;
/****************************************************************************************************/                                                                                                                       

extern void NRF24L01_INIT(void);                                                                                                                       
extern void Set_TXMode(void);               
extern void Set_RXMode(void);               
extern uint8 NRF24L01_TXPacket(uint8 *txbuf);       
extern uint8 NRF24L01_RXPacket(uint8 *rxbuf);


发射模式配置:

1.写入发送地址和接受应答通道地址
2.使能应答通道
3.使能接收地址
4.设置自动重发时间和重发次数
5.设置通信频道
6.设置发射参数(功率,增益等)
7.设置模式(发送还是接受)
注意在配置发送或者接受时候要先让其工作再待机模式即CE要先置0.

/*****************************************************************************
* 函  数:void NRF24L01_TX_Mode(void)
* 功  能:NRF24L01发送模式配置
* 参  数:无
* 返回值:无
* 备  注:无
*****************************************************************************/
void Set_TXMode(void)
{
        CE_LOW;            
        //写TX节点地址
        NRF24L01_Write_Buf(SPI_WRITE_REG+TX_ADDR,(uint8*)TX_ADDRESS,TX_ADDR_WIDTH);   
        //设置RX节点地址,使能ACK(应答)                  
        NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0,(uint8*)RX_ADDRESS,RX_ADDR_WIDTH);
        //使能通道0的自动应答   
        NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA,0x01);     
        //使能通道0的接收地址  
        NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01);
        //设置自动重发间隔时间:750us+86us ;最大自动重发次数:5次
        NRF24L01_Write_Reg(SPI_WRITE_REG+SETUP_RETR,0x25);
        //设置RF通道工作频率       
        NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,40);      
        //设置TX发射参数,0db增益,250Kbps,低噪声增益开启   
        NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x26);  
        //设置为发送模式PTX,使能PWR_UP、EN_CRC,使能CRC为2字节        ,开启RX_DR、TX_DS、MAX_RT中断引脚
        NRF24L01_Write_Reg(SPI_WRITE_REG+CONFIG,0x0e);
        CE_HIGH;       
        delay_ms(12);
}

接收模式配置:

1.接收通道地址
2.接收通道有效数据宽度
3.使能通道自动应答
4.使能接受通道地址
5.设置通信频道
6.设置参数
7.配置工作模式

/*****************************************************************************
* 函  数:void NRF24L01_RX_Mode(void)
* 功  能:NRF24L01接收模式配置
* 参  数:无
* 返回值:无
* 备  注:无
*****************************************************************************/
void Set_RXMode(void)
{
        CE_LOW;          
        //写RX节点地址
        NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0,(uint8 *)RX_ADDRESS,RX_ADDR_WIDTH);
        //使能通道0的自动应答   
        NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA,0x01);   
        //使能通道0的接收地址           
        NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01);
        //设置RF通道工作频率                  
        NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,40);             
        //选择通道0的有效数据宽度             
        NRF24L01_Write_Reg(SPI_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);
        //设置TX发射参数,0db增益,250Kbps,低噪声增益开启  
        NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x26);
        //设置为接收模式PRX,使能PWR_UP、EN_CRC,使能CRC为2字节,开启RX_DR、TX_DS、MAX_RT中断引脚
        NRF24L01_Write_Reg(SPI_WRITE_REG+CONFIG, 0x0f);
        //CE为高,进入接收模式
        CE_HIGH;
}

发送一帧数据(1~32字节):

1.向fifo中写入数据,等待IRQ发送完成中断
2.读取状态寄存器中的值
3.将读取到的状态寄存器中的值写入状态寄存器
4,.判断之前读取到的状态寄存器中得值判断是发送完成还是发送失败
5.清空fifo寄存器

/*****************************************************************************
* 函  数:uint8_t NRF24L01_TX_Packet(uint8_t *txbuf)
* 功  能:NRF24L01发送一次数据
* 参  数:*txbuf:等待发送数据的首地址
* 返回值:MAX_TX:最大重发次数;TX_OK:发送完成;0xFF:发送失败
* 备  注:无
*****************************************************************************/
uint8 NRF24L01_TXPacket(uint8_t *buf)
{
        uint8 state;

        CE_LOW;
        NRF24L01_Write_Buf(WR_TX_PLOAD, buf, TX_PLOAD_WIDTH);//写数据到txbuf,32字节
        CE_HIGH;//启动发送
       
       
       
        while(NRF_IRQ_STATUS!=0);//等待发送完成       
        state = NRF24L01_Read_Reg(STATUS);//读取状态寄存器的值
        NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS, state);//清除TX_DS or MAX_RT的中断标志
        if(state&MAX_TX)//达到最大重发次数
                {
                        NRF24L01_Write_Reg(FLUSH_TX, 0XFF);//清除TX FIFO        寄存器
                        return MAX_TX;
        }
        if(state&TX_OK)        //发送完成
                {
                        return TX_OK;
        }
        return 0xFF;//发送失败
       
}

接收一帧数据(1~32字节):

1.读取状态寄存器中的值
2.将读取到的状态寄存器中的值写入状态寄存器
4.判断状态寄存器中得值,是否接受成功(成功就读取fifo中的数据,并且清空fifo)

/*****************************************************************************
* 函  数:uint8_t NRF24L01_RX_Packet(uint8_t *rxbuf)
* 功  能:NRF24L01接收一次数据
* 参  数:*rxbuf:等待接收数据的首地址
* 返回值:0:接收成功;1:接收数据失败
* 备  注:无
*****************************************************************************/
uint8 NRF24L01_RXPacket(uint8_t *buf)
{
        uint8 state;


        CE_HIGH;
        while(NRF_IRQ_STATUS!=0);
        CE_LOW;

        state = NRF24L01_Read_Reg(STATUS);//读取状态寄存器的值
        NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS, state);//清除TX_DS or MAX_RT的中断标志
        if(state&RX_OK)//接收到数据
        {
                        NRF24L01_Read_Buf(RD_RX_PLOAD,buf,RX_PLOAD_WIDTH);//读取数据
                        NRF24L01_Write_Reg(FLUSH_RX,0xFF);//清除RX FIFO寄存器
                        return RX_OK;
        }
        return 0;//没有接收到数据
}
调试时的问题

博主目前只做过实际的1对3通信,在通信过程中要保证每次使用MOSI或MISO线时要至少留出几十微秒,否则会干扰下一次通信;
SPI比特率不要超过8MHz;
地址设置和有效数据宽度一致是大问题,重中之重;
板载天线模块那种的通信距离实测最多有十米(空旷),不管多近只要隔个房间就不行了,有严格需求了推荐用PA+LNA那种的(某宝20一个),实测至少300m开外(户外无人机);
这玩意的传输速率太慢了,想做图传的还是直接考虑5GHz的吧,2.4G太慢了;

题外话

博主还买了一个nRF24L01转USB的模块,因为某宝上的商家和网上的资料太少,我一直认为是USB转串口芯片加串口转SPI芯片组成的,过后才发现原理是USB转串口然后用STC8F2K单片机控制nRF24L01,早知道这个模块用单片机我就不买他了,因为他长得实在实在太像普通IC了(SOP16封装),然后我扒了扒目前某宝上这种模块都是单片机为中继的,淦。
当时买这个模块是想建立一个以nRF24L01为平台的稳固的上位机调试系统,但是现在个人认为去他娘的nRF,又不用大量数据通信,还是HC-08蓝牙模块真香





还有一种芯片是国产的2.4G芯片SI24R1,我身边很多人也有用这个的,这个兼容nRF24L系列的通信,据说好像便宜还是怎么着的;
网上有各种问题的解决办法,但是关于多点通信的文章很少,博主有机会可能会做一期关于多点通信的文章;

举报

更多回帖

发帖
×
20
完善资料,
赚取积分