STM32
直播中

毛头大小子

12年用户 610经验值
私信 关注
[问答]

两个51单片机之间通过nRF24L01模块通信的过程是怎样的

两个51单片机之间通过nRF24L01模块通信的过程是怎样的

回帖(1)

蔡表胤

2021-12-17 13:52:13
写在前面:这一篇先介绍一下两个51单片机之间通过nRF24L01模块通信的过程,下一篇我会写 51单片机STM32F407 单片机通信过程。
关于nRF24L01这个模块,网上可以说是资料非常多了,我参考的是云佳科技的pdf以及官方的datasheet英文数据手册。 另外,关于这个模块的介绍以及能够用来做什么也不过多的废话,数据手册及说明书都有。
51单片机我使用的是买来的开发板,核心是STC89C52,大家不必担心平台不同,都是使用软件模拟spi,引脚怎么定义都可以,也可以选择跟我使用的不一样的引脚,都Ok的。
  一、硬件介绍

  1、 nRF24L01模块接口威廉希尔官方网站 见下图



单片机是作为主机的,即Master nRF24L01作为从机,即Slave;
这样大家应该会很好理解MOSI和MISO了,,在我的另一篇博客也有SPI介绍,STM32F407使用MFRC522射频卡调试及程序移植成功
这个我是在STM32上调试的,可以参考一下里面对spi MOSI和MISO的解释
  
  1 GND ------>> 接地(与单片机共地)
  2 VCC ------>> 1.9~3.6V (推荐3.3V)
  3 CE ------>> RX 或 TX模式选择 高电平>10us则为发送模式 持续高电平为接收模式
设为低电平是待机模式
  4 CSN ------>> SPI片选信号 低电平使能,默认状态应该设置为高,以免发生错误的数据传输
  5 SCK ------>> SPI时钟信号
  6 MOSI ------>> 从SPI数据输入脚 (这里解释一下MOSI对应的单片机引脚输出信号, 即单片机输出数据给nRF24L01)
  7 MISO ------>> 从SPI数据输出脚 (MISO对应单片机引脚设置为输入, 即数据从nRF24L01出来送进单片机 )
  注:对于51单片机,无需设置单片机引脚的输入输出,但是STM32单片机需要设置
  8 IRQ ------>> 可屏蔽中断脚 中断 低电平使能
  ** PS:**
1) VCC电压供电范围要求1.9~3.6V之间,由于51单片机大多是5V,所以自己的开发板上没有无线模块接口的要注意,把VCC另外接到这个范围的电压上,电压过高会烧坏模块。。 推荐 3.3V 其他引脚无电压要求
2) 用普通单片机IO口模拟SPI协议即可控制该模块,我一般都是用模拟SPI,可移植性高
该模块使用的芯片方框图如下





2、 单片机2.4G模块引脚接口





3、 单片机按键引脚图





4、 硬件连接实物图(我用的两个相同的51单片机,所以引脚都一样,只是程序里面接收模式和发送模式略微不同)






  二、软件部分

  对于某个模块写程序是一定要参照datasheet的时序图, 这样才可以保证不出错。
下面是我从 nRF24L01 datasheet上截的SPI 时序图






变量设置及宏定义 接收端和发射端一样
// 宏定义
#define     uchar       unsigned char
#define     uint        unsigned int


#define     TX_ADR_WIDTH    5  // 5字节宽度的发送/接收地址
#define     TX_PLOAD_WIDTH  4  // 数据通道有效数据宽度


// LED灯及按键位定义
***it LED = P1^0;
***it KEY1 = P3^0;
***it KEY2 = P3^1;
***it BEEP = P2^3;


uchar code TX_ADDRESS[TX_ADR_WIDTH] = {0x34,0x43,0x10,0x10,0x01};  // 定义一个静态发送地址


uchar RX_BUF[TX_PLOAD_WIDTH];
uchar TX_BUF[TX_PLOAD_WIDTH];
uchar flag;
uchar DATA = 0x01;
uchar bdata sta;
***it  RX_DR     = sta^6;
***it  TX_DS     = sta^5;
***it  MAX_RT    = sta^4;


// NRF24L01 模块引脚位定义
***it CE  =  P1^2;
***it CSN =  P1^3;
***it SCK =  P1^7;
***it MOSI= P1^5;
***it MISO= P1^6;
***it IRQ = P1^4;


寄存器设置
/*  
    SPI(nRF24L01) 指令设置
    指令格式
    <命令字  : 由高位到低位(每字节)>
    <数据字节: 低字节到高字节,每一字节高位在前>
*/
#define READ_REG    0x00  // Define read command to register
#define WRITE_REG   0x20  // Define write command to register
#define RD_RX_PLOAD 0x61  // Define RX payload register address
#define WR_TX_PLOAD 0xA0  // Define TX payload register address
#define FLUSH_TX    0xE1  // 清除 TX FIFO寄存器  应用于发射模式下
#define FLUSH_RX    0xE2  // 清除 RX FIFO寄存器  应用于接收模式下。
#define REUSE_TX_PL 0xE3  // 重新使用上一包有效数据。 当CE=1,数据包被不断重新发射 发射过程中必须禁止数据包重利用功能
#define NOP         0xFF  // 空操作。可以用来读状态寄存器


/*  
    SPI(nRF24L01) registers(addresses)
*/
#define CONFIG      0x00  // 'Config' register address
#define EN_AA       0x01  // 'Enable Auto Acknowledgment' register address
#define EN_RXADDR   0x02  // 'Enabled RX addresses' register address
#define SETUP_AW    0x03  // 'Setup address width' register address
#define SETUP_RETR  0x04  // 'Setup Auto. Retrans' register address
#define RF_CH       0x05  // 'RF channel' register address
#define RF_SETUP    0x06  // 'RF setup' register address
#define STATUS      0x07  // 'Status' register address
#define OBSERVE_TX  0x08  // 'Observe TX' register address
#define CD          0x09  // 'Carrier Detect' register address
#define RX_ADDR_P0  0x0A  // 'RX address pipe0' register address
#define RX_ADDR_P1  0x0B  // 'RX address pipe1' register address
#define RX_ADDR_P2  0x0C  // 'RX address pipe2' register address
#define RX_ADDR_P3  0x0D  // 'RX address pipe3' register address
#define RX_ADDR_P4  0x0E  // 'RX address pipe4' register address
#define RX_ADDR_P5  0x0F  // 'RX address pipe5' register address
#define TX_ADDR     0x10  // 'TX address' register address
#define RX_PW_P0    0x11  // 'RX payload width, pipe0' register address
#define RX_PW_P1    0x12  // 'RX payload width, pipe1' register address
#define RX_PW_P2    0x13  // 'RX payload width, pipe2' register address
#define RX_PW_P3    0x14  // 'RX payload width, pipe3' register address
#define RX_PW_P4    0x15  // 'RX payload width, pipe4' register address
#define RX_PW_P5    0x16  // 'RX payload width, pipe5' register address
#define FIFO_STATUS 0x17  // 'FIFO Status Register' register address


1 简介一下 Enhanced ShockBurstTM发射流程
A. 把接收机的地址和要发送的数据按时序送入NRF24L01;


B. 配置CONFIG寄存器,使之进入发送模式。


C. 微控制器把CE置高(至少10us),激发NRF24L01进行Enhanced ShockBurstTM发射;


D. N24L01的Enhanced ShockBurstTM发射


(1)  给射频前端供电;


(2) 射频数据打包(加字头、CRC校验码);


(3) 高速发射数据包;


(4) 发射完成,NRF24L01进入空闲状态。


********** ** 发射端代码 ** *********
1) 首先初始化IO口


// 初始化IO
void init_io(void)
{
    CE  = 0;        // 待机
    CSN = 1;        // SPI禁止
    SCK = 0;        // SPI时钟置低
    IRQ = 1;        // 中断复位
    LED = 1;        // 关闭指示灯
}


2) 通过SPI对24L01进行读写的函数 返回读取的字节


uchar SPI_RW(uchar byte)
{
    uchar   bit_ctr;
    // output 8-bits
    for (bit_ctr = 0; bit_ctr < 8; bit_ctr++)
    {
        MOSI = (byte & 0x80);   // output ‘byte’  MSB to MOSI
        byte = (byte << 1);     // shift next bit into MSB..
        SCK = 1;                // Set SCK high.. 24L01 read 1-bit from MOSI and output 1-bit to MISO
        byte |= MISO;           // capture current MISO bit
        SCK = 0;                // ..then set SCK low again
    }


    return (byte);  // return read byte
}


3)


通过SPI协议向寄存器reg 写入数据value


uchar SPI_RW_Reg(uchar reg, uchar value)
{
    uchar status;
    CSN = 0;                // CSN low, init SPI transaction, start transmitting data
    status = SPI_RW(reg);   // select register and return status byte
    SPI_RW(value);          // ..and write value to it..
    CSN = 1;                // CSN high again, transmission end
    return(status);         // return nRF24L01 status byte
}


4) 从寄存器reg中读数据 返回读取的数据


uchar SPI_Read(uchar reg)
{
        uchar reg_val;
    CSN = 0;                    // CSN置低,开始传输数据
        SPI_RW(reg);                // 选择寄存器
        reg_val = SPI_RW(0);        // 然后从该寄存器读数据
        CSN = 1;                    // CSN拉高,结束数据传输
        return(reg_val);            // 返回寄存器数据


}


5) 从reg寄存器读 bytes 个字节


uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar bytes)
{
    uchar status,byte_ctr;
    CSN = 0;                    // Set CSN low, init SPI tranaction
    status = SPI_RW(reg);       // Select register to read & return status byte
   
    for (byte_ctr = 0; byte_ctr < bytes; byte_ctr++)
        pBuf[byte_ctr] = SPI_RW(0); //逐个字节从nRF24L01读出
    CSN = 1;                    // set CSN high, stop transaction
    return(status);             // return nRF24L01 status byte
}


6) 往reg寄存器写入 bytes 个字节


uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar bytes)
{
    uchar status,byte_ctr;
    CSN = 0;                // Set CSN low, init SPI tranaction
    status = SPI_RW(reg);   // Select register to write to & return status byte
   
    for (byte_ctr = 0; byte_ctr < bytes; byte_ctr++)
        SPI_RW(*pBuf++);    // 逐个字节写入nRF24L01
    CSN = 1;                // Set CSN high again 结束数据传输
    return(status);         // 返回状态寄存器
}
7) 设置nRF24L01为接收模式的函数,等待接收发送设备的数据包


void RX_Mode(void)
{
    CE = 0;
    SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH);  // 接收设备接收通道0使用和发送设备相同的发送地址
    SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);               // 使能接收通道0自动应答
    SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);           // 使能接收通道0
    SPI_RW_Reg(WRITE_REG + RF_CH, 40);                 // 选择射频通道0x40
    SPI_RW_Reg(WRITE_REG + RX_PW_P0, TX_PLOAD_WIDTH);  // 接收通道0选择和发送通道相同有效数据宽度
    SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);            // 数据传输率1Mbps,发射功率0dBm,低噪声放大器增益
    SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f);              // CRC使能,16位CRC校验,上电,接收模式
    delay_ms(150);
    CE = 1;                                            // 拉高CE启动接收设备
}


8) 设置nRF24L01为发送模式


void TX_Mode(uchar *BUF)
{
    CE = 0;
    SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);     // 写入发送地址
    SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH);  // 为了应答接收设备,接收通道0地址和发送地址相同
    SPI_Write_Buf(WR_TX_PLOAD, BUF, TX_PLOAD_WIDTH);                  // 写数据包到TX FIFO
    SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);       // 使能接收通道0自动应答
    SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);   // 使能接收通道0
    SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x0a);  // 自动重发延时等待250us+86us,自动重发10次
    SPI_RW_Reg(WRITE_REG + RF_CH, 40);         // 选择射频通道0x40
    SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);    // 数据传输率1Mbps,发射功率0dBm,低噪声放大器增益
    SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e);      // CRC使能,16位CRC校验,上电
    delay_ms(150);
    CE = 1;
}


9) 检查接收设备有无接收到数据包


uchar Check_ACK(bit clear)
{
    delay_ms(200);
    while(IRQ);             // 等待数据接收完成
    sta = SPI_RW(NOP);      // 返回状态寄存器
    if(TX_DS)
    {
        LED0 = ~LED0;
        delay_ms(200);
        LED0 = ~LED0;
        delay_ms(200);
        LED0 = ~LED0;
        delay_ms(200);
    }
    if(MAX_RT)
        if(clear)                         // 是否清除TX FIFO,没有清除在复位MAX_RT中断标志后重发
            SPI_RW(FLUSH_TX);
    SPI_RW_Reg(WRITE_REG + STATUS, sta);  // 清除TX_DS或MAX_RT中断标志
    IRQ = 1;
    if(TX_DS)
        return(0x00);
    else
        return(0xff);
}


10) 按键扫描


// 按键扫描
void CheckButtons()
{
    if(KEY1 == 0)
    {
        delay_ms(10);
        if(KEY1 == 0)
        {
            while(!KEY1);
            TX_BUF[0] = 1;          // 数据送到缓存
            TX_Mode(TX_BUF);        // 把nRF24L01设置为发送模式并发送数据
            Check_ACK(0);           // 等待发送完毕,清除TX FIFO
            delay_ms(250);
            delay_ms(250);
        }
    }


    if(KEY2 == 0)
    {
        delay_ms(10);
        if(KEY2 == 0)
        {
            while(!KEY2);
            TX_BUF[0] = 2;                  // 数据送到缓存
            TX_Mode(TX_BUF);            // 把nRF24L01设置为发送模式并发送数据
            Check_ACK(0);               // 等待发送完毕,清除TX FIFO
            delay_ms(250);
            delay_ms(250);
        }
    }
}


11) 主函数


void main(void)
{
    init_io();                      // 初始化IO
    while(1)
    {
        CheckButtons();           // 按键扫描
    }
}


2 Enhanced ShockBurstTM接收流程
A. 配置本机地址和要接收的数据包大小;


B. 配置CONFIG寄存器,使之进入接收模式,把CE置高。


C. 130us后,NRF24L01进入监视状态,等待数据包的到来;


D. 当接收到正确的数据包(正确的地址和CRC校验码),NRF2401自动把字


头、地址和CRC校验位移去;


E. NRF24L01通过把STATUS寄存器的RX_DR置位( STATUS一般引起微


控制器中断 )通知微控制器;


F. 微控制器把数据从 NewMsg_RF2401 读出;


G. 所有数据读取完毕后,可以清除STATUS寄存器。NRF2401可以进入


四种主要的模式之一。


接收端代码和发射端是一样的


注意发射端地址和接收端地址一致即可


接收端主函数


void main(void)
{
    init_io();                      // 初始化IO
        RX_Mode();                                                // 设置为接收模式


    while(1)
    {
        sta = SPI_Read(STATUS);      // 读状态寄存器
        //delay_ms(200);
        if(RX_DR)                    // 判断是否接受到数据
        {
            SPI_Read_Buf(RD_RX_PLOAD, RX_BUF, TX_PLOAD_WIDTH);  // 从RX FIFO读出数据
            flag = 1;
        }
        SPI_RW_Reg(WRITE_REG + STATUS, sta);  // 清除RX_DS中断标志


        if(flag)                   // 接受完成
        {
            if(RX_BUF[0] == 1)// KEY1按下  则蜂鸣器 响1下
            {
                BEEP = 0;
                delay_ms(500);
                BEEP = 1;
                delay_ms(500);
            }


            if(RX_BUF[0] == 2)        // KEY2按下   蜂鸣器响3下
            {
                BEEP = 0;
                delay_ms(500);
                BEEP = 1;
                delay_ms(500);
                BEEP = 0;
                delay_ms(500);
                BEEP = 1;
                delay_ms(500);
                BEEP = 0;
                delay_ms(500);
                BEEP = 1;
                delay_ms(500);
            }
            flag = 0;               // 清标志
            delay_ms(250);
            delay_ms(250);
            LED = 1;                // 关闭LED
        }
    }
}
举报

更多回帖

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