現(xiàn)在無線在我們的生活中無處不在。而我們開發(fā)的物聯(lián)網(wǎng)產(chǎn)品也大量使用無線通訊。在這一篇文章中,我們將討論nRF24L01無線通訊模塊驅動程序的開發(fā)與實現(xiàn)。
1 、功能概述
nRF24L01是一款工作在2.4~2.5GHz世界通用ISM 頻段的單片無線收發(fā)器芯片無線收發(fā)器包括:頻率發(fā)生器、增強型SchockBurst模式控制器、功率放大器、晶體振蕩器、調(diào)制器、解調(diào)器。輸出功率、頻道選擇和協(xié)議的設置可以通過SPI 接口進行設置。其封裝及引腳定義如下:
1.1 、工作模式
nRF24L01無線通訊模塊可以設置為多種不同的工作模式:待機模式、掉電模式、數(shù)據(jù)包處理方式。各模式的功能及操作如下:
1.1.1 、待機模式
待機模式I在保證快速啟動的同時減少系統(tǒng)平均消耗電流。在待機模式I下,晶振正常工作。在待機模式II下部分時鐘緩沖器處在工作模式。當發(fā)送端TX FIFO寄存器為空并且CE為高電平時進入待機模式II。在待機模式期間,寄存器配置字內(nèi)容保持不變。
1.1.2 、掉電模式
在掉電模式下,nRF24L01各功能關閉,保持電流消耗最小。進入掉電模式后,nRF24L01停止工作,但寄存器內(nèi)容保持不變。掉電模式由寄存器中PWR_UP位來控制。
1.1.3 、數(shù)據(jù)包處理方式
nRF24L01數(shù)據(jù)包處理方式包括ShockBurst模式和增強型ShockBurst模式。
ShockBurst模式下nRF24L01可以與成本較低的低速MCU相連。高速信號處理是由芯片內(nèi)部的射頻協(xié)議處理的,nRF24L01提供SPI接口,數(shù)據(jù)率取決于單片機本身接口速度。ShockBurst模式通過允許與單片機低速通信而無線部分高速通信,減小了通信的平均消耗電流。
增強型ShockBurst模式可以使得雙向鏈接協(xié)議執(zhí)行起來更為容易、有效。典型的雙向鏈接為:發(fā)送方要求終端設備在接收到數(shù)據(jù)后有應答信號,以便于發(fā)送方檢測有無數(shù)據(jù)丟失。一旦數(shù)據(jù)丟失,則通過重新發(fā)送功能將丟失的數(shù)據(jù)恢復。增強型的ShockBurstTM模式可以同時控制應答及重發(fā)功能而無需增加MCU工作量。
1.2 、數(shù)據(jù)通訊
1.2.1 、通訊指令及數(shù)據(jù)包
nRF24L01所有配置都在配置寄存器中。所有寄存器都是通過SPI口進行配置的。SPI接口采用標準的SPI接口,其最大的數(shù)據(jù)傳輸率為10Mbps。指令格式采用命令字加數(shù)據(jù)字節(jié)的格式。其中命令字由高位到低位(每字節(jié));數(shù)據(jù)字節(jié)從低字節(jié)到高字節(jié),每一字節(jié)高位在前。nRF24L01支持的指令如下:
R_REGISTER和W_REGISTER寄存器可能操作單字節(jié)或多字節(jié)寄存器。當訪問多字節(jié)寄存器時首先要讀/寫的是最低字節(jié)的高位。在所有多字節(jié)寄存器被寫完之前可以結束寫SPI操作,在這種情況下沒有寫完的高字節(jié)保持原有內(nèi)容不變。例如RX_ADDR_P0寄存器的最低字節(jié)可以通過寫一個字節(jié)給寄存器RX_ADDR_P0來改變。在CSN狀態(tài)由高變低后可以通過 MISO 來讀取狀態(tài)寄存器的內(nèi)容。
nRF24L01在增強型ShockBurst模式下和ShockBurst模式下的數(shù)據(jù)包格式略有不同。
增強型ShockBurst模式下的數(shù)據(jù)包形式如下:
ShockBurst模式下的數(shù)據(jù)包形式如下:
在數(shù)據(jù)包中,前導碼用來檢測0和1。芯片在接收模式下去除前導碼,在發(fā)送模式下加入前導碼。地址內(nèi)容為接收機地址。地址寬度可以是3、4或5字節(jié)寬度。地址可以對接收通道及發(fā)送通道分別進行配置。從接收的數(shù)據(jù)包中自動去除地址。標志位就是PID數(shù)據(jù)包識別號,后兩位會在每次接收到新的數(shù)據(jù)包后加,前7位保留。CRC校驗是可選的,0-2字節(jié)寬度的CRC校驗。若采用8位CRC校驗,則其特征多項式是:X8+X2+X+1;若采用16位CRC校驗,則其特征多項式是:X16+X12+X5+1。
1.2.2 、數(shù)據(jù)通道
nRF24L01配置為接收模式時可以接收6路不同地址相同頻率的數(shù)據(jù)。每個數(shù)據(jù)通道擁有自己的地址并且可以通過寄存器來進行分別配置。數(shù)據(jù)通道是通過寄存器EN_RXADDR來設置的,默認狀態(tài)下只有數(shù)據(jù)通道0和數(shù)據(jù)通道1是開啟狀態(tài)的。每一個數(shù)據(jù)通道的地址是通過寄存器RX_ADDR_Px來配置的。通常情況下不允許不同的數(shù)據(jù)通道設置完全相同的地址。數(shù)據(jù)通道0有40位可配置地址。數(shù)據(jù)通道1~5的地址為32位共用地址+各自的地址(最低字節(jié))。如下所示:
2 、驅動設計與實現(xiàn)
我們已經(jīng)了解了nRF24L01無線通訊模塊的功能及操作方式,接下來我們將設計并實現(xiàn)nRF24L01無線通訊模塊的驅動程序。
2.1 、對象定義
在使用一個對象之前我們需要獲得一個對象。同樣的我們想要nRF24L01無線通訊模塊就需要先定義nRF24L01無線通訊模塊的對象。
2.1.1 、對象的抽象
我們要得到nRF24L01無線通訊模塊對象,需要先分析其基本特性。一般來說,一個對象至少包含兩方面的特性:屬性與操作。接下來我們就來從這兩個方面思考一下nRF24L01無線通訊模塊的對象。
先來考慮屬性,作為屬性肯定是用于標識或記錄對象特征的東西。我們來考慮nRF24L01無線通訊模塊對象屬性。nRF24L01有一些寄存器用于配置工作狀態(tài),所以我們將這些寄存器狀態(tài)作為對象的屬性。
接著我們還需要考慮nRF24L01無線通訊模塊對象的操作問題。我們通過nRF24L01來收發(fā)數(shù)據(jù)就需要讀寫SPI接口,而這與特定的硬件平臺相關,所以我們將其作為對象的操作。而片選信號和使能信號以及中斷輸入信號也都與具體的操作平臺有關,所以我們也將其作為對象的操作。在進行相關操作時,我們需要控制時序,則需要使用延時操作,但延時處理總是依賴于具體的軟硬件平臺,所以我們將延時處理作為對象的操作。
根據(jù)上述我們對nRF24L01無線通訊模塊的分析,我們可以定義nRF24L01無線通訊模塊的對象類型如下:
/* 定義NRF24L01對象類型 */
typedef structNRF24L01Object {
uint8_t reg[8];//記錄前8個配置寄存器
uint8_t (*ReadWriteByte)(uint8_tTxData);//聲明向nRF24L01讀寫一個字節(jié)的函數(shù)
void (*ChipSelect)(NRF24L01CSType cs);//聲明片選操作函數(shù)
void (*ChipEnable)(NRF24L01CEType en);//聲明使能及模式操作函數(shù)
uint8_t (*GetIRQ)(void);//聲明中斷獲取函數(shù)
void (*Delayms)(volatile uint32_t nTime); //毫秒延時操作指針
}NRF24L01ObjectType;
2.1.2 、對象初始化
我們知道,一個對象僅作聲明是不能使用的,我們需要先對其進行初始化,所以這里我們來考慮nRF24L01無線通訊模塊對象的初始化函數(shù)。一般來說,初始化函數(shù)需要處理幾個方面的問題。一是檢查輸入?yún)?shù)是否合理;二是為對象的屬性賦初值;三是對對象作必要的初始化配置。據(jù)此我們設計nRF24L01無線通訊模塊對象的初始化函數(shù)如下:
/*nRF24L01對象初始化函數(shù)*/
NRF24L01ErrorTypeNRF24L01Initialization(NRF24L01ObjectType *nrf, //nRF24L01對象
NRF24L01ReadWriteByte spiReadWrite, //SPI讀寫函數(shù)指針
NRF24L01ChipSelect cs,//片選信號操作函數(shù)指針
NRF24L01ChipEnable ce, //使能信號操作函數(shù)指針
NRF24L01GetIRQ irq, //中斷信號獲取函數(shù)指針
NRF24L01Delayms delayms //毫秒延時
)
{
int retry=0;
if((nrf==NULL)||(spiReadWrite==NULL)||(ce==NULL)||(irq==NULL)||(delayms==NULL))
{
return NRF24L01_InitError;
}
nrf->ReadWriteByte=spiReadWrite;
nrf->ChipEnable=ce;
nrf->GetIRQ=irq;
nrf->Delayms=delayms;
if(cs!=NULL)
{
nrf->ChipSelect=cs;
}
else
{
nrf->ChipSelect=NRF24L01CSDefault;
}
while(NRF24L01Check(nrf)&&(retry<5))
{
nrf->Delayms(300);
retry++;
}
if(retry>=5)
{
return NRF24L01_Absent;
}
for(int i=0;i<8;i++)
{
nrf->reg[i]=0;
}
SetNRF24L01Mode(nrf,NRF24L01RxMode);
return NRF24L01_NoError;
}
2.2 、對象操作
我們已經(jīng)完成了nRF24L01無線通訊模塊對象類型的定義和對象初始化函數(shù)的設計。但我們的主要目標是獲取對象的信息,接下來我們還要實現(xiàn)面向nRF24L01無線通訊模塊的各類操作。
2.2.1 、讀操作
nRF24L01無線通訊模塊有很多的寄存器,所謂讀操作就是對這些寄存器的讀取過程。這個過程就是使用前面我們介紹的命令去獲取不同寄存器的數(shù)值。具體的時序過程如下所示:
根據(jù)上述時序圖以及各寄存器的定義,我們將讀nRF24L01無線通訊模塊寄存器的方式分為兩類:一類是讀普通的單字節(jié)寄存器,這些寄存器主要與配置和狀態(tài)有關;另一類是讀多字節(jié)寄存器,這些寄存器與數(shù)據(jù)通訊相關。具體的實現(xiàn)如下:
/*讀取寄存器值*/
static uint8_tNRF24L01ReadRegigster(NRF24L01ObjectType *nrf,uint8_t reg)
{
uint8_t reg_val;
nrf->ChipSelect(NRF24L01CS_Enable); //使能SPI傳輸
nrf->ReadWriteByte(reg); //發(fā)送寄存器號
reg_val=nrf->ReadWriteByte(0XFF); //讀取寄存器內(nèi)容
nrf->ChipSelect(NRF24L01CS_Disable); //禁止SPI傳輸
return(reg_val); //返回狀態(tài)值
}
/*在指定位置讀出指定長度的數(shù)據(jù)*/
static uint8_tNRF24L01ReadBuffer(NRF24L01ObjectType *nrf,uint8_t reg,uint8_t *pBuf,uint8_tlen)
{
uint8_t status;
nrf->ChipSelect(NRF24L01CS_Enable); //使能SPI傳輸
status=nrf->ReadWriteByte(reg); //發(fā)送寄存器值(位置),并讀取狀態(tài)值
for(int i=0;iReadWriteByte(0XFF);//讀出數(shù)據(jù)
}
nrf->ChipSelect(NRF24L01CS_Disable); //關閉SPI傳輸
return status; //返回讀到的狀態(tài)值
}
2.2.2 、寫操作
nRF24L01無線通訊模塊有很多的寄存器,所謂寫操作就是向這些寄存器寫值的過程。在寫寄存器之前一定要進入待機模式或掉電模式。雖然寄存器的位數(shù)等存在差異,但其操作過程基本是一樣的。具體的時序過程如下所示:
同樣的,根據(jù)上述時序圖以及各寄存器的定義,我們將寫nRF24L01無線通訊模塊寄存器的方式分為兩類:一類是寫普通的單字節(jié)寄存器,這些寄存器主要與配置和狀態(tài)有關;另一類是寫多字節(jié)寄存器,這些寄存器與數(shù)據(jù)通訊相關。具體的實現(xiàn)如下:
/*寫寄存器*/
static uint8_t NRF24L01WriteRegister(NRF24L01ObjectType*nrf,uint8_t reg,uint8_t value)
{
uint8_t status;
nrf->ChipSelect(NRF24L01CS_Enable); //使能SPI傳輸
status =nrf->ReadWriteByte(reg); //發(fā)送寄存器號
nrf->ReadWriteByte(value); //寫入寄存器的值
nrf->ChipSelect(NRF24L01CS_Disable); //禁止SPI傳輸
return(status); //返回狀態(tài)值
}
/*在指定位置寫指定長度的數(shù)據(jù)*/
static uint8_tNRF24L01WriteBuffer(NRF24L01ObjectType *nrf,uint8_t reg, uint8_t *pBuf, uint8_tlen)
{
uint8_t status;
nrf->ChipSelect(NRF24L01CS_Enable); //使能SPI傳輸
status = nrf->ReadWriteByte(reg); //發(fā)送寄存器值(位置),并讀取狀態(tài)值
for(int i=0; iReadWriteByte(pBuf[i]); //寫入數(shù)據(jù)
}
nrf->ChipSelect(NRF24L01CS_Disable); //關閉SPI傳輸
return status; //返回讀到的狀態(tài)值
}
3 、驅動的使用
前面我們已經(jīng)設計并實現(xiàn)了nRF24L01無線通訊模塊的驅動程序,我們還需要驗證這一驅動程序的設計是否符合要求,所以在這一節(jié)中我們將基于nRF24L01無線通訊模塊的驅動程序設計一驗證應用。
3.1 、聲明并初始化對象
使用基于對象的操作我們需要先得到這個對象,所以我們先要使用前面定義的nRF24L01無線通訊模塊類型聲明一個nRF24L01無線通訊模塊對象變量,具體操作格式如下:
NRF24L01ObjectTypenrf;
聲明了這個對象變量并不能立即使用,我們還需要使用驅動中定義的初始化函數(shù)對這個變量進行初始化。這個初始化函數(shù)所需要的輸入?yún)?shù)如下:
NRF24L01ObjectType*nrf,nRF24L01對象
NRF24L01ReadWriteBytespiReadWrite,SPI讀寫函數(shù)指針
NRF24L01ChipSelectcs,片選信號操作函數(shù)指針
NRF24L01ChipEnablece,使能信號操作函數(shù)指針
NRF24L01GetIRQirq,中斷信號獲取函數(shù)指針
NRF24L01Delaymsdelayms,毫秒延時
對于這些參數(shù),nRF24L01對象變量我們已經(jīng)定義了。余下的參數(shù)是一些函數(shù)指針,這是我們需要定義的,并將函數(shù)指針作為參數(shù)。這幾個函數(shù)的類型如下:
//聲明向nRF24L01讀寫一個字節(jié)的函數(shù)
typedef uint8_t (*NRF24L01ReadWriteByte)(uint8_tTxData);
//聲明片選操作函數(shù)
typedef void(*NRF24L01ChipSelect)(NRF24L01CSType cs);
//聲明使能及模式操作函數(shù)
typedef void(*NRF24L01ChipEnable)(NRF24L01CEType en);
//聲明中斷獲取函數(shù)
typedef uint8_t(*NRF24L01GetIRQ)(void);
//毫秒延時操作指針
typedef void(*NRF24L01Delayms)(volatile uint32_t nTime);
對于這幾個函數(shù)我們根據(jù)樣式定義就可以了,具體的操作可能與使用的硬件平臺有關系。片選操作函數(shù)用于多設備需要軟件操作時,如采用硬件片選可以傳入NULL即可。具體函數(shù)定義如下:
/* 基于HAL庫的SPI讀寫字節(jié)函數(shù) */
static uint8_tNRF24L01ReadWrite(uint8_t txData)
{
uint8_t rxData=0;
HAL_SPI_TransmitReceive(&nrf24l01hspi,&txData,&rxData,1,1000);
return rxData;
}
/*實現(xiàn)片選*/
static voidNRF24L01ChipSelectf(NRF24L01CSType cs)
{
if(NRF24L01CS_Enable==cs)
{
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_4, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_4, GPIO_PIN_SET);
}
}
/*實現(xiàn)使能*/
static voidNRF24L01ChipEnablef(NRF24L01CEType en)
{
if(NRF24L01CE_Enable==en)
{
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_5, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_5, GPIO_PIN_SET);
}
}
/*實現(xiàn)Ready狀態(tài)監(jiān)視*/
static uint8_tNRF24L01GetIRQf(void)
{
returnHAL_GPIO_ReadPin(GPIOC,GPIO_PIN_0);
}
對于延時函數(shù)我們可以采用各種方法實現(xiàn)。我們采用的STM32平臺和HAL庫則可以直接使用HAL_Delay()函數(shù)。于是我們可以調(diào)用初始化函數(shù)如下:
NRF24L01Initialization(&nrf,NRF24L01ReadWrite,NRF24L01ChipSelectf,NRF24L01ChipEnablef,NRF24L01GetIRQf,HAL_Delay);
3.2 、基于對象進行操作
我們定義了對象變量并使用初始化函數(shù)給其作了初始化。接著我們就來考慮操作這一對象獲取我們想要的數(shù)據(jù)。我們在驅動中已經(jīng)將獲取數(shù)據(jù)并轉換為轉換值的比例值,接下來我們使用這一驅動開發(fā)我們的應用實例。
/*NRF24L01數(shù)據(jù)通訊*/
void NRF24L01DataExchange(void)
{
uint8_t txDatas[32]={0xAA};
uint8_t rxDatas[32]={0x00};
NRF24L01TransmitPacket(&nrf,txDatas);
HAL_Delay(1);
NRF24L01ReceivePacket(&nrf,rxDatas);
}
4 、應用總結
我們已經(jīng)設計并實現(xiàn)了nRF24L01無線通訊模塊的驅動程序,并且在次驅動程序的基礎上開發(fā)了簡單的測試應用。經(jīng)測試,這一驅動的設計基本上是正確的。
在使用驅動時需注意,采用SPI接口的器件需要考慮片選操作的問題。如果片選信號是通過硬件電路來實現(xiàn)的,我們在初始化時給其傳遞NULL值。如果是軟件操作片選則傳遞我們編寫的片選操作函數(shù)。
在使用驅動時,驅動中修改接收和發(fā)送模式時采用的是直接寫入數(shù)值。其他的寄存器配置也基本都是直接寫入數(shù)值,如果需要修改則需要在源碼中修改。事實上,需要經(jīng)常修改的可能性并不大,這也是我們寫固定值的原因。另外,驅動中配置的是CRC-16校驗,如果需要修改也是在源碼中修改數(shù)值。
-
nRF24L01
+關注
關注
17文章
331瀏覽量
70349 -
無線通訊
+關注
關注
5文章
602瀏覽量
40621 -
驅動設計
+關注
關注
1文章
111瀏覽量
15437
發(fā)布評論請先 登錄
相關推薦
[Micropython]TPYBoard v10x NRF24L01無線通訊模塊使用教程
如何設計并實現(xiàn)nRF24L01無線通訊模塊的驅動程序呢?
基于nRF24L01的數(shù)據(jù)無線傳輸系統(tǒng)的設計與實現(xiàn)
nrf24l01無線模塊傳輸距離分析
nRF24L01無線模塊使用教程之經(jīng)典使用程序詳細資料合集免費下載
微雪電子NRF24L01 RF Board 2.4G無線模塊簡介

nrf24l01是什么模塊_nrf24l01工作原理
【Arduino】NRF24L01無線模塊6通道通信

使用NRF24L01模塊無線控制的自動窗簾

基于nRF24L01的通用無線通信模塊設計案例

評論