1. 程式人生 > >STM32驅動NRF24L01

STM32驅動NRF24L01

1. 簡介

NRF24L01是 nordic 的無線通訊晶片,它具有以下特點:

1) 2.4G 全球開放的 ISM 頻段(2.400 - 2.4835GHz),免許可證使用; 2)最高工作速率 2Mbps,高校的 GFSK 調製,抗干擾能力強; 3) 125 個可選的頻道,滿足多點通訊和調頻通訊的需要; 4)內建 CRC 檢錯和點對多點的通訊地址控制; 5)低工作電壓(1.9~3.6V),待機模式下狀態為 26uA;掉電模式下為 900nA; 6)可設定自動應答,確保資料可靠傳輸; 7)工作於EnhancedShockBurst 具有Automatic packet handling,Auto packet transaction handling ,可以實現點對點或是 1 對 6 的無線通訊,速度可以達到 2M(bps),具有可選的內建包應答機制,極大的降低丟包率。 8)通過 SPI 匯流排與微控制器進行互動,最大通訊速率為10Mbps;

1.1 結構框圖

在這裡插入圖片描述

如圖右側為六個控制和資料訊號,分別為 CSN、 SCK、 MISO、 MOSI、 IRQ、 CE。

訊號線 功能
CSN 晶片的片選線, CSN 為低電平晶片工作
SCK 晶片控制的時鐘線(SPI 時鐘)
MISO 晶片控制資料線(Master input slave output)
MOSI 晶片控制資料線(Master output slave input)
IRQ 中斷訊號。無線通訊過程中 MCU 主要是通過 IRQ 與 NRF24L01 進行通訊
CE 晶片的模式控制線。 在 CSN 為低的情況下, CE 協同 NRF24L01 的 CONFIG 暫存器共同決定 NRF24L01 的狀態

1.2 NRF24L01 狀態機

在這裡插入圖片描述

NRF24L01 的狀態機如上圖 所示,對於 NRF24L01 的韌體程式設計工作主要是參照 NRF24L01 的狀態機。主要有以下幾個狀態:

模式 PWR_UP register PRIM_RX register CE FIFO state
RX Mode 1 1 1 -
TX Mode 1 0 1 資料存在TX FIFO暫存器中
TX Mode 1 0 最小 10us高電平 停留在傳送模式,直到資料傳送完
待機模式2 1 0 1 TX FIFO為空
待機模式1 1 - 0 無資料傳輸
掉電模式 0 - - -

注:PWR_UP: 上電;PRIM_RX: 掉電;CE: 晶片使能 (PWR_UP和PRIM_RX 在配置暫存器(CONFIG)中設定位0和位1:)

1.3 硬體設計

原理圖已經在資料手冊給出,我們只需參照其設計即可,主要是在 PCB 上,注意天線部分器件的擺放和天線的淨空處理,使天線能夠達到最佳效果(這個可以參照資料手冊的建議,當然也可以根據需求做更改)。

在這裡插入圖片描述

此處驗證採用的是現成的模組。

1.4 韌體程式設計

1) 置 CSN 為低,使能晶片,配置晶片各個引數。配置引數在 Power Down 狀態中完成。 2) 如果是 Tx 模式,填充 Tx FIFO。 3) 配置完成以後,通過 CE 與 CONFIG 中的 PWR_UP 與 PRIM_RX 引數確定 24L01要切換到的狀態。

Tx Mode: PWR_UP=1; PRIM_RX=0; CE=1 (保持超過 10us 就可以); Rx Mode: PWR_UP=1; PRIM_RX=1; CE=1;

  1. IRQ 引腳會在以下三種情況變低:中斷時變為低電平 Tx FIFO 發完並且收到 ACK(使能 ACK 情況下); Rx FIFO 收到資料; 達到最大重發次數; 將 IRQ 接到外部中斷輸入引腳,通過中斷程式進行處理。nRF24L01 的中斷引腳(IRQ)為低電平觸發,當狀態暫存器中TX_DS(資料傳送完成中斷位)、RX_DR(接收資料中斷位) 或MAX_RT(達到最多次重發中斷位)為高時觸發中斷。當MCU 給中斷源寫‘1’時,中斷引腳被禁止。可遮蔽中斷可以被IRQ 中斷遮蔽。通過設定可遮蔽中斷位為高,則中斷響應被禁止。預設狀態下所有的中斷源是被禁止的。
1.4.1 Tx 模式初始化
初始化步驟 配置NRF24L01暫存器
1)寫 Tx 節點的地址 TX_ADDR
2)寫 Rx 節點的地址(使能 Auto Ack) RX_ADDR_P0
3)使能 AUTO ACK EN_AA
4)使能 PIPE 0 EN_RXADDR
5)配置自動重發次數 SETUP_RETR
6)選擇通訊頻率 RF_CH
7)配置發射引數(低噪放大器增益、發射功率、無線速率) RF_SETUP
8 ) 選擇通道 0 有效資料寬度 Rx_Pw_P0
9)配置 24L01 的基本引數以及切換工作模式 CONFIG

按照如上思路即可配置 TX 模式:

/*****************************************************************************
* 函  數:void NRF24L01_TX_Mode(void)
* 功  能:NRF24L01傳送模式配置
* 參  數:無
* 返回值:無
* 備  注:無
*****************************************************************************/
void NRF24L01_TX_Mode(void)
{
	NRF_CE2_LOW;
	NRF24L01_Write_Buf2(W_REGISTER+TX_ADDR,TX_ADR_WIDTH,(uint8_t *)TX_ADDRESS_X);//寫TX節點地址
	NRF24L01_Write_Buf2(W_REGISTER+RX_ADDR_P0,RX_ADR_WIDTH, (uint8_t *) RX_ADDRESS_X);//寫RX節點地址,為了自動使能ACK
	NRF24L01_Write_Reg2(W_REGISTER+EN_AA, 0x01);//使能通道0自動應答
	NRF24L01_Write_Reg2(W_REGISTER+EN_RXADDR, 0x01);//使能通道0接收地址
	NRF24L01_Write_Reg2(W_REGISTER+SETUP_PETR, 0x1a);//設定自動重發間隔時間:500us+86us,最大重大次數:10次
	NRF24L01_Write_Reg2(W_REGISTER+RF_CH, 40);//設定通道為40
	NRF24L01_Write_Reg2(W_REGISTER+RF_SETUP, 0x0f);//設定發射引數:0dB增益;2Mnps;低噪聲增益開啟
	NRF24L01_Write_Reg2(W_REGISTER+NRF24L01_CONFIG,0x0e);//基本引數:PWR_UP;EN_CRC;16BIT_CRC;傳送模式;開啟所有中斷
	NRF_CE2_HIGH;//NRF_CE為高,10us後啟動傳送資料
}
1.4.2 Rx 模式初始化
初始化步驟 配置NRF24L01暫存器
1)寫 Rx 節點的地址 RX_ADDR_P0
2)使能 AUTO ACK EN_AA
3)使能 PIPE 0 EN_RXADDR
4)選擇通訊頻率 RF_CH
5 ) 選擇通道 0 有效資料寬度 Rx_Pw_P0
6)配置發射引數(低噪放大器增益、發射功率、無線速率) RF_SETUP
7)配置 24L01 的基本引數以及切換工作模式 CONFIG
/*****************************************************************************
* 函  數:void NRF24L01_TX_Mode(void)
* 功  能:NRF24L01傳送模式配置
* 參  數:無
* 返回值:無
* 備  注:無
*****************************************************************************/
void NRF24L01_RX_Mode(void)
{
	NRF_CE2_LOW;
	NRF24L01_Write_Buf2(W_REGISTER+RX_ADDR_P0, RX_ADR_WIDTH, (uint8_t *) RX_ADDRESS_X);//寫RX地址節點
	NRF24L01_Write_Reg2(W_REGISTER+EN_AA,0x01);//使能通道0自動應答
	NRF24L01_Write_Reg2(W_REGISTER+EN_RXADDR,0x01);//使能通道0接收地址
	NRF24L01_Write_Reg2(W_REGISTER+RF_CH, 40);//設定RF通訊頻率
	NRF24L01_Write_Reg2(W_REGISTER+RX_PW_P0,RX_PLOAD_WIDTH);//選擇通道0有效資料寬度
	NRF24L01_Write_Reg2(W_REGISTER+RF_SETUP, 0x0f);//設定TX發射引數:0db增益,2Mbps,低噪聲增益開啟
	NRF24L01_Write_Reg2(W_REGISTER+NRF24L01_CONFIG, 0x0f);//基本引數:PWR_UP;EN_CRC;16BIT_CRC;接收模式;開啟所有中斷
	NRF_CE2_HIGH;//CE為高,進入接收模式
}

1.5 NRF24L01的收發模式

收發模式有Enhanced ShockBurstTM收發模式、ShockBurstTM 收發模式和直接收發模式三種。

ShockBurstTM模式:

ShockBurst模式下,nRF24L01 可以與成本較低的低速 MCU 相連,高速訊號處理是由晶片內部的射頻協議處理的。nRF24L01 提供 SPI 介面資料率取決於微控制器本身介面速度。ShockBurst 模式通過允許與微控制器低速通訊而無線部分高速通訊減小了通訊的平均消耗電流。

在 ShockBurstTM 接收模式下,當接收到有效的地址和資料時 IRQ 通知 MCU ,隨後MCU可將接收到的資料從RX FIFO暫存器中讀出。

在 ShockBurstTM 傳送模式下,nRF24L01 自動生成前導碼及 CRC 校驗,資料傳送完畢後 IRQ 通知 MCU ,減少了 MCU 的查詢時間,也就意味著減少了MCU 的工作量同時減少了軟體的開發時間。nRF24L01 內部有三個不同的RX FIFO暫存器 6 個通道共享此暫存器和三個不同的TX FIFO暫存器在掉電模式下待機模式下和資料傳輸的過程中 MCU 可以隨時訪問FIFO暫存器。這就允許 SPI 介面可以以低速進行資料傳送並且可以應用於MCU硬體上沒有SPI介面的情況下。

增強型的ShockBurstTM模式:

增強型ShockBurstTM模式可以使得雙向連結協議執行起來更為容易有效,典型的雙向連結為傳送方要求終端裝置在接收到資料後有應答訊號以便於傳送方檢測有無資料丟失失。一旦資料丟失,則通過重新發送功能將丟失的資料恢復。增強型的ShockBurstTM模式可以同時控制應答及重發功能(資料重發設定暫存器(SETUP_RETR))而無需增加MCU工作量。

nRF24L01 在接收模式下可以接收6 路不同通道的資料,每一個數據通道使用不同的地址,但是共用相同的頻道。也就是說6 個不同的 nRF24L01 設定為傳送模式後可以與同一個設定為接收模式的 nRF24L01 進行通訊,而設定為接收模式的nRF24L01 可以對這6 個發射端進行識別。

在這裡插入圖片描述

資料通道0 是唯一 的一個可以配置為40 位自身地址的資料通道。1~5 資料通道都為8 位自身地址和32 位公用地址。所有的 資料通道都可以設定為增強型ShockBurst 模式。NRF24L01 在確認收到資料後記錄地址,並以此地址為目標地址傳送應答訊號,在傳送端,資料通道0被用作接收應答訊號,因此屬通道0 的接收地址要與傳送地址端地址相等,以確保接收到正確的應答訊號

nRF24l01 配置為增強型的ShockBurstTM模式下,只要 MCU 有資料傳送,就會啟動增強型的ShockBurstTM模式來發送資料。傳送結束後NRF24L01 轉到接收模式且等待終端應答訊號,若為收到應答,NRF24L01 將啟動重發資料,直至收到 ACK 訊號或者超出最大重發次數為止,超過重發次數,將產生 MAX_RT 中斷。收到確認訊號,NRF24L01 就認為最後一包資料已經發送成功,將把 TX_FIFO 中的資料清除且產生 TX_DS 中斷(IRQ訊號置高)。

Enhanced ShockBurstTM發射流程:

A. 把接收機的地址和要傳送的資料按時序送入NRF24L01; B. 配置CONFIG暫存器,使之進入傳送模式。 C. 微控制器把CE置高(至少10us),激發NRF24L01進行Enhanced ShockBurstTM發射; D.N24L01的Enhanced ShockBurstTM發射:(1) 給射頻前端供電; (2)射頻資料打包(加字頭、CRC校驗碼); (3) 高速發射資料包; (4)發射完成,NRF24L01進入空閒狀態。

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可以進入四種主要的模式之一。

1.5 NRF24L01的資料通道

nRF24L01 配置為接收模式時可以接收 6 路不同地址相同頻率的資料。每個資料通道擁有自己的地址並且可以通過暫存器來進行分別配置。資料通道是通過暫存器EN_RXADDR來設定的,預設狀態下只有資料通道0和資料通道1是開啟狀態的。每一個數據通道的地址是通過暫存器RX_ADDR_Px來配置的。通常情況下不允許不同的資料通道設 置完全相同的地址。 資料通道0有40 位可配置地址。資料通道1~5的地址為:32位共用地址+各自的地址(最低位元組)。如下圖所示:

在這裡插入圖片描述

1.6 NRF24L01的SPI通訊模式

在這裡插入圖片描述

從圖中可以看出, SCK 空閒的時候是低電平的,而資料在 SCK 的上升沿被讀寫。所以,我們需要設定 SPI 的 CPOL 和 CPHA均為 0(SPI模式0),來滿足 NRF24L01 對 SPI 操作的要求。

/*****************************************************************************
* 函  數:void SPI_GPIO_Init(void)
* 功  能:SPI1相關GPIO初始化配置
* 參  數:無
* 返回值:無
* 備  注:無
*****************************************************************************/
void SPI_GPIO_Init(void)
{
   GPIO_InitTypeDef GPIO_InitStructure;
   
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
   
   GPIO_InitStructure.GPIO_Pin = SPI1_CLK|SPI1_MISO|SPI1_MOSI;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   
   GPIO_Init(GPIOA,&GPIO_InitStructure);
   GPIO_SetBits(GPIOA,SPI1_CLK|SPI1_MISO|SPI1_MOSI);
}

/*****************************************************************************
* 函  數:void SPI1_Init(void)
* 功  能:SPI1初始化配置
* 參  數:無
* 返回值:無
* 備  注:無
*****************************************************************************/
void SPI1_Init(void)
{
   SPI_InitTypeDef SPI1_InitStructure;
   
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);//spi1在APB2總線上
   
   SPI1_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//設定SPI單向或者雙向的資料模式:SPI設定為雙線雙向全雙工
   SPI1_InitStructure.SPI_Mode = SPI_Mode_Master;//設定SPI工作模式:設定為主SPI
   SPI1_InitStructure.SPI_DataSize = SPI_DataSize_8b;//設定SPI的資料大小:SPI傳送接收8位幀結構
   SPI1_InitStructure.SPI_CPOL = SPI_CPOL_Low;	//選擇了序列時鐘的穩態:時鐘懸空低電平
   SPI1_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//資料捕獲於第一個時鐘沿
   SPI1_InitStructure.SPI_NSS = SPI_NSS_Soft;//NSS訊號由硬體(NSS管腳)還是軟體(使用SSI位)管理:內部NSS訊號有SSI位控制
   SPI1_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;//定義波特率預分頻的值:波特率預分頻值為16:72/16=4.5MHz
   SPI1_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//指定資料傳輸從MSB位還是LSB位開始:資料傳輸從MSB位開始
   SPI1_InitStructure.SPI_CRCPolynomial = 7;//CRC值計算的多項式
   SPI_Init(SPI1,&SPI1_InitStructure);
   SPI_Cmd(SPI1,ENABLE);

   SPI1_ReadWrite_Byte(0xFF);//啟動傳輸
}

/*****************************************************************************
* 函  數:uint8_t SPI1_ReadWrite_Byte(uint8_t tx_dat)
* 功  能:SPI1收發資料
* 參  數:tx_dat:傳送的資料
* 返回值:接收的資料
* 備  注:無
*****************************************************************************/

uint8_t SPI1_ReadWrite_Byte(uint8_t tx_dat)
{
   while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET);
   	SPI_I2S_SendData(SPI1,tx_dat);
   
   while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)==RESET);
   	return SPI_I2S_ReceiveData(SPI1);
}

2. 軟體實現

這裡就貼一下 NRF24L01 涉及的程式:

/*****************************************************************************
* 函  數:void NRF24L01_GPIO_Init(void)
* 功  能:NRF24L01相關GPIO初始化
* 參  數:void
* 返回值:無
* 備  注:無
*****************************************************************************/
void NRF24L01_GPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC,ENABLE);

	//SPI1上還掛了flash和sd卡,一直片選置高,排除干擾
	GPIO_InitStructure.GPIO_Pin = FLASH_CS|SD_CARD_CS;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = NRF_CE;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = NRF_IRQ;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//中斷上拉輸入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = NRF_CS;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC,&GPIO_InitStructure);
	
	GPIO_SetBits(GPIOA,NRF_IRQ|NRF_CE);
	GPIO_SetBits(GPIOC,NRF_CS);

//第二模組相關的GPIO	
	GPIO_InitStructure.GPIO_Pin = NRF_CE2;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = NRF_IRQ2;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = NRF_CS2;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC,&GPIO_InitStructure);
	
	GPIO_SetBits(GPIOC,NRF_IRQ2|NRF_CE2);
	GPIO_SetBits(GPIOC,NRF_CS2);
}

/*****************************************************************************
* 函  數:uint8_t NRF24L01_Write_Reg(uint8_t res,uint8_t value)
* 功  能:從暫存器寫入一位元組資料
* 參  數:res:暫存器地址;value:寫入的值
* 返回值:status:讀取的暫存器狀態值
* 備  注:無
*****************************************************************************/
uint8_t NRF24L01_Write_Reg(uint8_t res,uint8_t value)
{
	uint8_t status;
	
	NRF_CS_LOW;
	status = SPI1_ReadWrite_Byte(res);
	SPI1_ReadWrite_Byte(value);
	NRF_CS_HIGH;

	return status;	//返回暫存器狀態值
}

/*****************************************************************************
* 函  數:uint8_t NRF24L01_Read_Reg(uint8_t res)
* 功  能:從暫存器讀取一位元組資料
* 參  數:res:暫存器地址
* 返回值:ret:讀取的值
* 備  注:無
*****************************************************************************/
uint8_t NRF24L01_Read_Reg(uint8_t res)
{
	uint8_t ret;
	
	NRF_CS_LOW;
	SPI1_ReadWrite_Byte(res);
	ret = SPI1_ReadWrite_Byte(0xFF);	//在讀取資料前穩定MISO的電平,防止觸發slave裝置
	NRF_CS_HIGH;

	return ret;
}

/*****************************************************************************
* 函  數:uint8_t NRF24L01_Read_Buf(uint8_t res,uint8_t len,uint8_t *pBuf)
* 功  能:在指定位置讀取一定長度的資料
* 參  數:res:指定位置;len:資料長度;*pBuf:指定資料的首地址
* 返回值:status:暫存器的狀態
* 備  注:無
*****************************************************************************/
uint8_t NRF24L01_Read_Buf(uint8_t res,uint8_t len,uint8_t *pBuf)
{
	uint8_t status,i;

	NRF_CS_LOW;
	status = SPI1_ReadWrite_Byte(res);
	for(i=0;i<len;i++)
		{
			pBuf[i] = SPI1_ReadWrite_Byte(0xFF);
	}
	NRF_CS_HIGH;

	return status;
}

/*****************************************************************************
* 函  數:uint8_t NRF24L01_Write_Buf(uint8_t res,uint8_t len,uint8_t *pBuf)
* 功  能:在指定位置寫入一定長度的資料
* 參  數:res:指定位置;len:資料長度;*pBuf:指定資料的首地址
* 返回值:status:暫存器的狀態
* 備  注:無
*****************************************************************************/
uint8_t NRF24L01_Write_Buf(uint8_t res,uint8_t len,uint8_t *pBuf)
{
	uint8_t status,i;

	NRF_CS_LOW;
	status = SPI1_ReadWrite_Byte(res);
	for (i = 0; i < len; i++)
		{
			SPI1_ReadWrite_Byte(*pBuf++);
			//SPI1_ReadWrite_Byte(pBuf[i]);
		}
	NRF_CS_HIGH;
	
	return status;
}

/*****************************************************************************
* 函  數:uint8_t NRF24L01_Check(void)
* 功  能:檢測NRF24L01是否存在
* 參  數:無
* 返回值:0:成功;1:失敗
* 備  注:往NRF24L01的傳送地址暫存器寫入5位元組資料再讀出來,判斷24L01是否已正常工作
*****************************************************************************/
uint8_t NRF24L01_Check(void)
{
	uint8_t buf[5]={0xa5,0xa5,0xa5,0xa5,0xa5};
	uint8_t i;

	NRF24L01_Write_Buf(W_REGISTER+TX_ADDR,5,buf);//寫入5位元組的地址
	NRF24L01_Read_Buf(TX_ADDR,5,buf);
	for(i=0;i<5;i++)
		{
			if(buf[i]!=0xa5)
				break;
	}
	if(i!=5)
		return 1;
	return 0;
}

/*****************************************************************************
* 函  數:void NRF24L01_TX_Mode(void)
* 功  能:NRF24L01傳送模式配置
* 參  數:無
* 返回值:無
* 備  注:無
*****************************************************************************/
void NRF24L01_TX_Mode(void)
{
	NRF_CE_LOW;
	NRF24L01_Write_Buf(W_REGISTER+TX_ADDR,TX_ADR_WIDTH,(uint8_t *)TX_ADDRESS_X);//寫TX節點地址
	NRF24L01_Write_Buf(W_REGISTER+RX_ADDR_P0,RX_ADR_WIDTH, (uint8_t *) RX_ADDRESS_X);//寫RX節點地址,為了自動使能ACK
	NRF24L01_Write_Reg(W_REGISTER+EN_AA, 0x01);//使能通道0自動應答
	NRF24L01_Write_Reg(W_REGISTER+EN_RXADDR, 0x01);//使能通道0接收地址
	NRF24L01_Write_Reg(W_REGISTER+SETUP_PETR, 0x1a);//設定自動重發間隔時間:500us+86us,最大重大次數:10次
	NRF24L01_Write_Reg(W_REGISTER+RF_CH, 40);//設定通道為40
	NRF24L01_Write_Reg(W_REGISTER+RF_SETUP, 0x0f);//設定發射引數:0dB增益;2Mnps;低噪聲增益開啟
	NRF24L01_Write_Reg(W_REGISTER+NRF24L01_CONFIG,0x0e);//基本引數:PWR_UP;EN_CRC;16BIT_CRC;傳送模式;開啟所有中斷
	NRF_CE_HIGH;//NRF_CE為高,10us後啟動傳送資料
	delay_us(12);
}

/*****************************************************************************
* 函  數:void NRF24L01_TX_Mode(void)
* 功  能:NRF24L01傳送模式配置
* 參  數:無
* 返回值:無
* 備  注:無
*****************************************************************************/
void NRF24L01_RX_Mode(void)
{
	NRF_CE_LOW;
	NRF24L01_Write_Buf(W_REGISTER+RX_ADDR_P0, RX_ADR_WIDTH, (uint8_t *) RX_ADDRESS_X);//寫RX地址節點
	NRF24L01_Write_Reg(W_REGISTER+EN_AA,0x01);//使能通道0自動應答
	NRF24L01_Write_Reg(W_REGISTER+EN_RXADDR,0x01);//使能通道0接收地址
	NRF24L01_Write_Reg(W_REGISTER+RF_CH, 40);//設定RF通訊頻率
	NRF24L01_Write_Reg(W_REGISTER+RX_PW_P0,RX_PLOAD_WIDTH);//選擇通道0有效資料寬度
	NRF24L01_Write_Reg(W_REGISTER+RF_SETUP, 0x0f);//設定TX發射引數:0db增益,2Mbps,低噪聲增益開啟
	NRF24L01_Write_Reg(W_REGISTER+NRF24L01_CONFIG, 0x0f);//基本引數:PWR_UP;EN_CRC;16BIT_CRC;接收模式;開啟所有中斷
	NRF_CE_HIGH;//CE為高,進入接收模式
}

/*****************************************************************************
* 函  數:uint8_t NRF24L01_TX_Packet(uint8_t *txbuf)
* 功  能:NRF24L01傳送一次資料
* 參  數:*txbuf:等待發送資料的首地址
* 返回值:MAX_TX:最大重發次數;TX_OK:傳送完成;0xFF:傳送失敗
* 備  注:無
*****************************************************************************/
uint8_t NRF24L01_TX_Packet(uint8_t *txbuf)
{
	uint8_t ret;

	NRF_CE_LOW;
	NRF24L01_Write_Buf(W_TX_PAYLOAD, TX_PLOAD_WIDTH, txbuf);//寫資料到txbuf,32位元組
	NRF_CE_HIGH;//啟動傳送
	while (NRF_IRQ_STATUS);//等待發送完成
	ret = NRF24L01_Read_Reg(NRF24L01_STATUS);//讀取狀態暫存器的值
	NRF24L01_Write_Reg(W_REGISTER+NRF24L01_STATUS, ret);//清除TX_DS or MAX_RT的中斷標誌
	if(ret&MAX_TX)//達到最大重發次數
		{
			NRF24L01_Write_Reg(FLUSH_TX, 0XFF);//清除TX FIFO	暫存器
			return MAX_TX;
	}
	if(ret&TX_OK)	//傳送完成
		{
			return TX_OK;
	}
	return 0xFF;//傳送失敗
	
}

/*****************************************************************************
* 函  數:uint8_t NRF24L01_RX_Packet(uint8_t *rxbuf)
* 功  能:NRF24L01接收一次資料
* 參  數:*rxbuf:等待接收資料的首地址
* 返回值:0:接收成功;1:接收資料失敗
* 備  注:無
*****************************************************************************/
uint8_t NRF24L01_RX_Packet(uint8_t *rxbuf)
{
	uint8_t ret;


	NRF_CE_HIGH;
	while(NRF_IRQ_STATUS);
	NRF_CE_LOW;

	ret = NRF24L01_Read_Reg(NRF24L01_STATUS);//讀取狀態暫存器的值
	NRF24L01_Write_Reg(W_REGISTER+NRF24L01_STATUS, ret);//清除TX_DS or MAX_RT的中斷標誌
	if(ret&RX_OK)//接收到資料
		{
			NRF24L01_Read_Buf(R_RX_PAYLOAD,RX_PLOAD_WIDTH,rxbuf);//讀取資料
			NRF24L01_Write_Reg(FLUSH_RX,0xFF);//清除RX FIFO暫存器
			return 0;
	}
	return 1;//沒有接收到資料
}

/*****************************************************************************
* 函  數:void NRF24L01_Init(void)
* 功  能:NRF24L01初始化
* 參  數:無
* 返回值:無
* 備  注:對應GPIO設定
*****************************************************************************/
void NRF24L01_Init(void)
{
	NRF24L01_GPIO_Init();
	
	FLASH_CS_HIGH;
	SD_CARD_CS_HIGH;
	
	NRF_CS_HIGH;
	NRF_CE_LOW;
}

如有興趣,可在此處:NRF24L01的STM32測試程式 進行下載驗證。測試比較簡單,就是一收一發,往復迴圈。

現象:

在這裡插入圖片描述

當然也可以都掛載在一個 SPI 上,只要 CS 訊號不衝突,也可以正常實現。

文中的暫存器等介紹,可在NORDIC官網進行下載:nRF24L01資料手冊下載

參考: