nRF24L01--2.4G無線通信模塊(1)(51單片機和51單片機通信)


作者:李剴

出處:https://www.cnblogs.com/kevin-nancy/

或者
https://blog.csdn.net/Kevin_8_Lee/article/details/95667604

歡迎轉載,但也請保留上面這段聲明。謝謝!(上面兩個都是我的博客,只是在不同平台,大家可以點擊鏈接看一下我的博客哦~~~)

寫在前面:這一篇先介紹一下兩個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燈及按鍵位定義
sbit LED = P1^0;
sbit KEY1 = P3^0;
sbit KEY2 = P3^1;
sbit 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;
sbit  RX_DR     = sta^6;
sbit  TX_DS     = sta^5;
sbit  MAX_RT    = sta^4;

// NRF24L01 模塊引腳位定義
sbit CE  =  P1^2;
sbit CSN =  P1^3;
sbit SCK =  P1^7;
sbit MOSI= P1^5;
sbit MISO= P1^6;
sbit 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
        }
    }
}

上面的代碼很詳盡了,想要完整工程的可以下載----->>>完整代碼工程文件(接收端+發射端)

凡事不要說“我不會”或“不可能”, 因為你根本還沒有去做! 加油吧


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM