NRF24L01是一款工作在2.4-2.5GHz世界通用ISM頻段的單片收發(fā)芯片, 使用4線SPI通訊端口,通訊速率最高可達8Mbps,適合與各種MCU連接,編程簡單;輸出功率、頻道選擇和協(xié)議的設(shè)置可以通過SPI接口設(shè)置極低的電流消耗,當工作在發(fā)射模式下發(fā)射功率為6dBm時電流消耗為9.0mA接受模式為12.3mA掉電模式和待機模式下電流消耗模式更低。
一模塊來源
模塊實物展示:
資料鏈接:https://pan.baidu.com/s/1CUQ3SOdnmD8xSXMdR4YopA
資料提取碼:1234
二規(guī)格參數(shù)
工作電壓:1.9~3.6V
供電電流:900 ~ 12.3mA
最大數(shù)據(jù)傳輸率:2000 Kbps
控制方式:SPI
管腳數(shù)量:8 Pin(2.54mm間距排針)
以上信息見廠家資料文件
三移植過程
我們的目標是將例程移植至CW32F030C8T6開發(fā)板上【實現(xiàn)無線的數(shù)據(jù)傳輸?shù)墓δ堋俊J紫纫@取資料,查看數(shù)據(jù)手冊應(yīng)如何實現(xiàn)讀取數(shù)據(jù),再移植至我們的工程。
3.1查看資料
接收方式
NRF24L01的接收端是靠IRQ引腳進行判斷,當IRQ為高電平時,說明接收到了數(shù)據(jù),IRQ為1則是正在等待數(shù)據(jù)。因此可以根據(jù)IRQ引腳來決定接收的方式。這里提供輪詢方式和中斷方式。
輪詢方式接收
采用輪詢方式會阻礙其他任務(wù)的運行,因接收數(shù)據(jù)要時時刻刻判斷IRQ引腳是否為高電平,會一直占用MCU的時間。為了解決因為沒有接收到數(shù)據(jù)就卡死問題以及防止錯過數(shù)據(jù)沒有接收問題,在等待數(shù)據(jù)的過程中,加入了超時判斷,當一定的時間內(nèi)沒有接收到數(shù)據(jù),則結(jié)束等待接收,去運行其他任務(wù)。
中斷方式接收
采用中斷方式接收數(shù)據(jù),是通過將IRQ引腳設(shè)置為外部中斷功能。當檢測到IRQ引腳有變化時,則接收數(shù)據(jù)。根據(jù)24L01的要求,當接收完數(shù)據(jù)后,必須清除接收的FIFO。
3.2引腳選擇
引腳說明
硬件SPI與軟件SPI相比,硬件SPI是靠硬件上面的SPI控制器,所有的時鐘邊緣采樣,時鐘發(fā)生,還有時序控制,都是由硬件完成的。它降低了CPU的使用率,提高了運行速度。軟件SPI就是用代碼控制IO輸出高低電平,模擬SPI的時序,這種方法通信速度較慢,且不可靠。
想要使用硬件SPI驅(qū)動,需要確定使用的引腳是否有SPI外設(shè)功能??梢酝ㄟ^用戶手冊146頁進行查看。
當前使用的是硬件SPI接口,而NRF24L01我們需要與它發(fā)送數(shù)據(jù)也需要接收數(shù)據(jù),故使用的是4線的SPI,使用到了時鐘線SCK、主機輸出從機輸入線MOSI、主機輸入從機輸出線MISO和軟件控制的片選線NSS。所以除了這些引腳需要使用硬件SPI功能的引腳外,其他引腳都可以使用開發(fā)板上其他的GPIO。這里選擇使用PA5/PA6/PA7的SPI復(fù)用功能?。其他對應(yīng)接入的引腳請按照你的需要。這里選擇的引腳見右表。
有SPI功能的引腳
模塊接線圖
3.3移植至工程
移植步驟中的導(dǎo)入.c和.h文件與【CW32模塊使用】DHT11溫濕度傳感器相同,只是將.c和.h文件更改為drv_spi.c與drv_spi.h。這里不再過多講述,移植完成后面修改相關(guān)代碼。
在drv_spi.c中,修改為如下代碼。
/*
* Change Logs:
* Date Author Notes
* 2024-06-21 LCKFB-LP first version
*/
#include "drv_spi.h"
/** 硬件SPI */
#define SPI_WAIT_TIMEOUT ((uint16_t)0xFFFF)
/**
* @brief :SPI初始化(硬件)
* @param :無
* @note :無
* @retval:無
*/
void drv_spi_init( void )
{
GPIO_InitTypeDef GPIO_InitStruct1; // GPIO初始化結(jié)構(gòu)體
GPIO_InitTypeDef GPIO_InitStruct2; // GPIO初始化結(jié)構(gòu)體
SPI_GPIO_RCC(); // 使能GPIO時鐘
RCC_SPI_HARDWARE_ENABLE(); // 使能SPI1時鐘
// GPIO復(fù)用為SPI1
BSP_SPI_AF_SCK();
BSP_SPI_AF_MISO();
BSP_SPI_AF_MOSI();
GPIO_InitStruct1.Pins = SPI_NSS_GPIO_PIN|
SPI_CLK_GPIO_PIN|
SPI_MOSI_GPIO_PIN; // GPIO引腳
GPIO_InitStruct1.Mode = GPIO_MODE_OUTPUT_PP; // 推挽輸出
GPIO_InitStruct1.Speed = GPIO_SPEED_HIGH; // 輸出速度高
GPIO_Init(SPI_GPIO_PORT, &GPIO_InitStruct1); // 初始化
GPIO_InitStruct2.Pins = SPI_MISO_GPIO_PIN; // GPIO引腳
GPIO_InitStruct2.Mode = GPIO_MODE_INPUT_PULLUP; // 上拉輸入
GPIO_Init(SPI_GPIO_PORT, &GPIO_InitStruct2); // 初始化
spi_set_nss_high(); // 片選拉高
SPI_InitTypeDef SPI_InitStructure; // SPI 初始化結(jié)構(gòu)體
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 雙線全雙工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 主機模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 幀數(shù)據(jù)長度為8bit
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // 時鐘空閑電平為低
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // 第1個邊沿采樣
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 片選信號由SSI寄存器控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; // 波特率為PCLK的8分頻
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 最高有效位 MSB 收發(fā)在前
SPI_InitStructure.SPI_Speed = SPI_Speed_Low; // 低速SPI
SPI_Init(PORT_SPI, &SPI_InitStructure); // 初始化
SPI_Cmd(PORT_SPI, ENABLE); // 使能SPI1
}
/**
* @brief :SPI收發(fā)一個字節(jié)
* @param :
* @TxByte: 發(fā)送的數(shù)據(jù)字節(jié)
* @note :非堵塞式,一旦等待超時,函數(shù)會自動退出
* @retval:接收到的字節(jié)
*/
uint16_t drv_spi_read_write_byte( uint8_t TxByte )
{
uint16_t l_Data = 0;
uint16_t l_WaitTime = 0;
while(RESET == SPI_GetFlagStatus(PORT_SPI, SPI_FLAG_TXE))//等待發(fā)送緩沖區(qū)為空
{
if( SPI_WAIT_TIMEOUT == ++l_WaitTime )
{
break; //如果等待超時則退出
}
}
l_WaitTime = SPI_WAIT_TIMEOUT / 2; //重新設(shè)置接收等待時間(因為SPI的速度很快,正常情況下在發(fā)送完成之后會立即收到數(shù)據(jù),等待時間不需要過長)
SPI_SendData(PORT_SPI, TxByte);//發(fā)送數(shù)據(jù)
while(RESET == SPI_GetFlagStatus(PORT_SPI, SPI_FLAG_RXNE))//等待接收緩沖區(qū)非空
{
if( SPI_WAIT_TIMEOUT == ++l_WaitTime )
{
break; //如果等待超時則退出
}
}
l_Data = SPI_ReceiveData(PORT_SPI);//讀取接收數(shù)據(jù)
return l_Data; //返回
}
/**
* @brief :SPI收發(fā)字符串
* @param :
* @ReadBuffer: 接收數(shù)據(jù)緩沖區(qū)地址
* @WriteBuffer:發(fā)送字節(jié)緩沖區(qū)地址
* @Length:字節(jié)長度
* @note :非堵塞式,一旦等待超時,函數(shù)會自動退出
* @retval:無
*/
void drv_spi_read_write_string( uint8_t* ReadBuffer, uint8_t* WriteBuffer, uint16_t Length )
{
spi_set_nss_low( );//拉低片選
while( Length-- )
{
*ReadBuffer = drv_spi_read_write_byte( *WriteBuffer ); //收發(fā)數(shù)據(jù)
ReadBuffer++;
WriteBuffer++; //讀寫地址加1
}
spi_set_nss_high( );//拉高片選
}
在drv_spi.h中,修改為如下代碼。
/*
* Change Logs:
* Date Author Notes
* 2024-06-21 LCKFB-LP first version
*/
#ifndef __DRV_SPI_H__
#define __DRV_SPI_H__
#include "board.h"
//SPI引腳定義
#define SPI_GPIO_RCC() __RCC_GPIOA_CLK_ENABLE() // GPIO時鐘
#define SPI_GPIO_PORT CW_GPIOA
#define SPI_CLK_GPIO_PIN GPIO_PIN_5
#define SPI_MISO_GPIO_PIN GPIO_PIN_6
#define SPI_MOSI_GPIO_PIN GPIO_PIN_7
#define SPI_NSS_GPIO_PIN GPIO_PIN_4
#define spi_set_nss_high( ) GPIO_WritePin(SPI_GPIO_PORT, SPI_NSS_GPIO_PIN, GPIO_Pin_SET) //片選置高
#define spi_set_nss_low( ) GPIO_WritePin(SPI_GPIO_PORT, SPI_NSS_GPIO_PIN, GPIO_Pin_RESET) //片選置低
/******** 硬件SPI修改此次 ********/
#define RCC_SPI_HARDWARE_ENABLE() __RCC_SPI1_CLK_ENABLE()
#define PORT_SPI CW_SPI1
//GPIO AF
#define BSP_SPI_AF_SCK() PA05_AFx_SPI1SCK()
#define BSP_SPI_AF_MISO() PA06_AFx_SPI1MISO()
#define BSP_SPI_AF_MOSI() PA07_AFx_SPI1MOSI()
void drv_spi_init( void );
uint16_t drv_spi_read_write_byte( uint8_t TxByte );
void drv_spi_read_write_string( uint8_t* ReadBuffer, uint8_t* WriteBuffer, uint16_t Length );
#endif
在NRF24L01.c中,修改如下代碼。?
/*
* Change Logs:
* Date Author Notes
* 2024-06-21 LCKFB-LP first version
*/
#include "NRF24L01.h"
#include "stdio.h"
const char *g_ErrorString = "RF24L01 is not find !...";
void drv_delay_500Ms( unsigned int ms)
{
while(ms--)
{
delay_ms(500);
}
}
/**
* @brief :NRF24L01讀寄存器
* @param :
@Addr:寄存器地址
* @note :地址在設(shè)備中有效
* @retval:讀取的數(shù)據(jù)
*/
uint8_t NRF24L01_Read_Reg( uint8_t RegAddr )
{
uint8_t btmp;
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( NRF_READ_REG | RegAddr ); //讀命令 地址
btmp = drv_spi_read_write_byte( 0xFF ); //讀數(shù)據(jù)
RF24L01_SET_CS_HIGH( ); //取消片選
return btmp;
}
/**
* @brief :NRF24L01讀指定長度的數(shù)據(jù)
* @param :
* @reg:地址
* @pBuf:數(shù)據(jù)存放地址
* @len:數(shù)據(jù)長度
* @note :數(shù)據(jù)長度不超過255,地址在設(shè)備中有效
* @retval:讀取狀態(tài)
*/
void NRF24L01_Read_Buf( uint8_t RegAddr, uint8_t *pBuf, uint8_t len )
{
uint8_t btmp;
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( NRF_READ_REG | RegAddr ); //讀命令 地址
for( btmp = 0; btmp < len; btmp ++ )
{
*( pBuf + btmp ) = drv_spi_read_write_byte( 0xFF ); //讀數(shù)據(jù)
}
RF24L01_SET_CS_HIGH( ); //取消片選
}
/**
* @brief :NRF24L01寫寄存器
* @param :無
* @note :地址在設(shè)備中有效
* @retval:讀寫狀態(tài)
*/
void NRF24L01_Write_Reg( uint8_t RegAddr, uint8_t Value )
{
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( NRF_WRITE_REG | RegAddr ); //寫命令 地址
drv_spi_read_write_byte( Value ); //寫數(shù)據(jù)
RF24L01_SET_CS_HIGH( ); //取消片選
}
/**
* @brief :NRF24L01寫指定長度的數(shù)據(jù)
* @param :
* @reg:地址
* @pBuf:寫入的數(shù)據(jù)地址
* @len:數(shù)據(jù)長度
* @note :數(shù)據(jù)長度不超過255,地址在設(shè)備中有效
* @retval:寫狀態(tài)
*/
void NRF24L01_Write_Buf( uint8_t RegAddr, uint8_t *pBuf, uint8_t len )
{
uint8_t i;
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( NRF_WRITE_REG | RegAddr ); //寫命令 地址
for( i = 0; i < len; i ++ )
{
drv_spi_read_write_byte( *( pBuf + i ) ); //寫數(shù)據(jù)
}
RF24L01_SET_CS_HIGH( ); //取消片選
}
/**
* @brief :清空TX緩沖區(qū)
* @param :無
* @note :無
* @retval:無
*/
void NRF24L01_Flush_Tx_Fifo ( void )
{
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( FLUSH_TX ); //清TX FIFO命令
RF24L01_SET_CS_HIGH( ); //取消片選
}
/**
* @brief :清空RX緩沖區(qū)
* @param :無
* @note :無
* @retval:無
*/
void NRF24L01_Flush_Rx_Fifo( void )
{
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( FLUSH_RX ); //清RX FIFO命令
RF24L01_SET_CS_HIGH( ); //取消片選
}
/**
* @brief :重新使用上一包數(shù)據(jù)
* @param :無
* @note :無
* @retval:無
*/
void NRF24L01_Reuse_Tx_Payload( void )
{
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( REUSE_TX_PL ); //重新使用上一包命令
RF24L01_SET_CS_HIGH( ); //取消片選
}
/**
* @brief :NRF24L01空操作
* @param :無
* @note :無
* @retval:無
*/
void NRF24L01_Nop( void )
{
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( NOP ); //空操作命令
RF24L01_SET_CS_HIGH( ); //取消片選
}
/**
* @brief :NRF24L01讀狀態(tài)寄存器
* @param :無
* @note :無
* @retval:RF24L01狀態(tài)
*/
uint8_t NRF24L01_Read_Status_Register( void )
{
uint8_t Status;
RF24L01_SET_CS_LOW( ); //片選
Status = drv_spi_read_write_byte( NRF_READ_REG + STATUS ); //讀狀態(tài)寄存器
RF24L01_SET_CS_HIGH( ); //取消片選
return Status;
}
/**
* @brief :NRF24L01清中斷
* @param :
@IRQ_Source:中斷源
* @note :無
* @retval:清除后狀態(tài)寄存器的值
*/
uint8_t NRF24L01_Clear_IRQ_Flag( uint8_t IRQ_Source )
{
uint8_t btmp = 0;
IRQ_Source &= ( 1 << RX_DR ) | ( 1 << TX_DS ) | ( 1 << MAX_RT ); //中斷標志處理
btmp = NRF24L01_Read_Status_Register( ); //讀狀態(tài)寄存器
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( NRF_WRITE_REG + STATUS ); //寫狀態(tài)寄存器命令
drv_spi_read_write_byte( IRQ_Source | btmp ); //清相應(yīng)中斷標志
RF24L01_SET_CS_HIGH( ); //取消片選
return ( NRF24L01_Read_Status_Register( )); //返回狀態(tài)寄存器狀態(tài)
}
/**
* @brief :讀RF24L01中斷狀態(tài)
* @param :無
* @note :無
* @retval:中斷狀態(tài)
*/
uint8_t RF24L01_Read_IRQ_Status( void )
{
return ( NRF24L01_Read_Status_Register( ) & (( 1 << RX_DR ) | ( 1 << TX_DS ) | ( 1 << MAX_RT ))); //返回中斷狀態(tài)
}
/**
* @brief :讀FIFO中數(shù)據(jù)寬度
* @param :無
* @note :無
* @retval:數(shù)據(jù)寬度
*/
uint8_t NRF24L01_Read_Top_Fifo_Width( void )
{
uint8_t btmp;
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( R_RX_PL_WID ); //讀FIFO中數(shù)據(jù)寬度命令
btmp = drv_spi_read_write_byte( 0xFF ); //讀數(shù)據(jù)
RF24L01_SET_CS_HIGH( ); //取消片選
return btmp;
}
/**
* @brief :讀接收到的數(shù)據(jù)
* @param :無
* @note :無
* @retval:
@pRxBuf:數(shù)據(jù)存放地址首地址
*/
uint8_t NRF24L01_Read_Rx_Payload( uint8_t *pRxBuf )
{
uint8_t Width, PipeNum;
PipeNum = ( NRF24L01_Read_Reg( STATUS ) >> 1 ) & 0x07; //讀接收狀態(tài)
Width = NRF24L01_Read_Top_Fifo_Width( ); //讀接收數(shù)據(jù)個數(shù)
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( RD_RX_PLOAD ); //讀有效數(shù)據(jù)命令
for( PipeNum = 0; PipeNum < Width; PipeNum ++ )
{
*( pRxBuf + PipeNum ) = drv_spi_read_write_byte( 0xFF ); //讀數(shù)據(jù)
}
RF24L01_SET_CS_HIGH( ); //取消片選
NRF24L01_Flush_Rx_Fifo( ); //清空RX FIFO
return Width;
}
/**
* @brief :發(fā)送數(shù)據(jù)(帶應(yīng)答)
* @param :
* @pTxBuf:發(fā)送數(shù)據(jù)地址
* @len:長度
* @note :一次不超過32個字節(jié)
* @retval:無
*/
void NRF24L01_Write_Tx_Payload_Ack( uint8_t *pTxBuf, uint8_t len )
{
uint8_t btmp;
uint8_t length = ( len > 32 ) ? 32 : len; //數(shù)據(jù)長達大約32 則只發(fā)送32個
NRF24L01_Flush_Tx_Fifo( ); //清TX FIFO
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( WR_TX_PLOAD ); //發(fā)送命令
for( btmp = 0; btmp < length; btmp ++ )
{
drv_spi_read_write_byte( *( pTxBuf + btmp ) ); //發(fā)送數(shù)據(jù)
}
RF24L01_SET_CS_HIGH( ); //取消片選
}
/**
* @brief :發(fā)送數(shù)據(jù)(不帶應(yīng)答)
* @param :
* @pTxBuf:發(fā)送數(shù)據(jù)地址
* @len:長度
* @note :一次不超過32個字節(jié)
* @retval:無
*/
void NRF24L01_Write_Tx_Payload_NoAck( uint8_t *pTxBuf, uint8_t len )
{
if( len > 32 || len == 0 )
{
return ; //數(shù)據(jù)長度大于32 或者等于0 不執(zhí)行
}
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( WR_TX_PLOAD_NACK ); //發(fā)送命令
while( len-- )
{
drv_spi_read_write_byte( *pTxBuf ); //發(fā)送數(shù)據(jù)
pTxBuf++;
}
RF24L01_SET_CS_HIGH( ); //取消片選
}
/**
* @brief :在接收模式下向TX FIFO寫數(shù)據(jù)(帶ACK)
* @param :
* @pData:數(shù)據(jù)地址
* @len:長度
* @note :一次不超過32個字節(jié)
* @retval:無
*/
void NRF24L01_Write_Tx_Payload_InAck( uint8_t *pData, uint8_t len )
{
uint8_t btmp;
len = ( len > 32 ) ? 32 : len; //數(shù)據(jù)長度大于32個則只寫32個字節(jié)
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( W_ACK_PLOAD ); //命令
for( btmp = 0; btmp < len; btmp ++ )
{
drv_spi_read_write_byte( *( pData + btmp ) ); //寫數(shù)據(jù)
}
RF24L01_SET_CS_HIGH( ); //取消片選
}
/**
* @brief :設(shè)置發(fā)送地址
* @param :
* @pAddr:地址存放地址
* @len:長度
* @note :無
* @retval:無
*/
void NRF24L01_Set_TxAddr( uint8_t *pAddr, uint8_t len )
{
len = ( len > 5 ) ? 5 : len; //地址不能大于5個字節(jié)
NRF24L01_Write_Buf( TX_ADDR, pAddr, len ); //寫地址
}
/**
* @brief :設(shè)置接收通道地址
* @param :
* @PipeNum:通道
* @pAddr:地址存肥著地址
* @Len:長度
* @note :通道不大于5 地址長度不大于5個字節(jié)
* @retval:無
*/
void NRF24L01_Set_RxAddr( uint8_t PipeNum, uint8_t *pAddr, uint8_t Len )
{
Len = ( Len > 5 ) ? 5 : Len;
PipeNum = ( PipeNum > 5 ) ? 5 : PipeNum; //通道不大于5 地址長度不大于5個字節(jié)
NRF24L01_Write_Buf( RX_ADDR_P0 + PipeNum, pAddr, Len ); //寫入地址
}
/**
* @brief :設(shè)置通信速度
* @param :
* @Speed:速度
* @note :無
* @retval:無
*/
void NRF24L01_Set_Speed( nRf24l01SpeedType Speed )
{
uint8_t btmp = 0;
btmp = NRF24L01_Read_Reg( RF_SETUP );
btmp &= ~( ( 1<<5 ) | ( 1<<3 ) );
if( Speed == SPEED_250K ) //250K
{
btmp |= ( 1<<5 );
}
else if( Speed == SPEED_1M ) //1M
{
btmp &= ~( ( 1<<5 ) | ( 1<<3 ) );
}
else if( Speed == SPEED_2M ) //2M
{
btmp |= ( 1<<3 );
}
NRF24L01_Write_Reg( RF_SETUP, btmp );
}
/**
* @brief :設(shè)置功率
* @param :
* @Speed:速度
* @note :無
* @retval:無
*/
void NRF24L01_Set_Power( nRf24l01PowerType Power )
{
uint8_t btmp;
btmp = NRF24L01_Read_Reg( RF_SETUP ) & ~0x07;
switch( Power )
{
case POWER_F18DBM:
btmp |= PWR_18DB;
break;
case POWER_F12DBM:
btmp |= PWR_12DB;
break;
case POWER_F6DBM:
btmp |= PWR_6DB;
break;
case POWER_0DBM:
btmp |= PWR_0DB;
break;
default:
break;
}
NRF24L01_Write_Reg( RF_SETUP, btmp );
}
/**
* @brief :設(shè)置頻率
* @param :
* @FreqPoint:頻率設(shè)置參數(shù)
* @note :值不大于127
* @retval:無
*/
void RF24LL01_Write_Hopping_Point( uint8_t FreqPoint )
{
NRF24L01_Write_Reg( RF_CH, FreqPoint & 0x7F );
}
/**
* @brief :NRF24L01檢測
* @param :無
* @note :無
* @retval:無
*/
void NRF24L01_check( void )
{
uint8_t i;
uint8_t error = 0;
uint8_t buf[5]={ 0XA5, 0XA5, 0XA5, 0XA5, 0XA5 };
uint8_t read_buf[ 5 ] = { 0 };
while( 1 )
{
NRF24L01_Write_Buf( TX_ADDR, buf, 5 ); //寫入5個字節(jié)的地址
NRF24L01_Read_Buf( TX_ADDR, read_buf, 5 ); //讀出寫入的地址
for( i = 0; i < 5; i++ )
{
if( buf[ i ] != read_buf[ i ] )
{
break;
}
}
if( 5 == i )
{
break;
}
else
{
error++;
if( error >= 3 )
{
break;
}
//測試錯誤
printf("NRF24L01 ERROR FILE:NRF24L01.C LINE = %drn",__LINE__);
}
drv_delay_500Ms( 4 );
}
printf("Successful configurationrn");
}
/**
* @brief :設(shè)置模式
* @param :
* @Mode:模式發(fā)送模式或接收模式
* @note :無
* @retval:無
*/
void RF24L01_Set_Mode( nRf24l01ModeType Mode )
{
uint8_t controlreg = 0;
controlreg = NRF24L01_Read_Reg( CONFIG );
if( Mode == MODE_TX )
{
controlreg &= ~( 1<< PRIM_RX );
}
else
{
if( Mode == MODE_RX )
{
controlreg |= ( 1<< PRIM_RX );
}
}
NRF24L01_Write_Reg( CONFIG, controlreg );
}
/**
* @brief :NRF24L01發(fā)送一次數(shù)據(jù)
* @param :
* @txbuf:待發(fā)送數(shù)據(jù)首地址
* @Length:發(fā)送數(shù)據(jù)長度
* @note :無
* @retval:
* MAX_TX:達到最大重發(fā)次數(shù)
* TX_OK:發(fā)送完成
* 0xFF:其他原因
*/
uint8_t NRF24L01_TxPacket( uint8_t *txbuf, uint8_t Length )
{
uint8_t l_Status = 0;
uint16_t l_MsTimes = 0;
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( FLUSH_TX );
RF24L01_SET_CS_HIGH( );
RF24L01_SET_CE_LOW( );
NRF24L01_Write_Buf( WR_TX_PLOAD, txbuf, Length ); //寫數(shù)據(jù)到TX BUF 32字節(jié) TX_PLOAD_WIDTH
RF24L01_SET_CE_HIGH( ); //啟動發(fā)送
while( 0 != RF24L01_GET_IRQ_STATUS( ))
{
delay_ms( 5 );
// printf("error-1rn");
if( 500 == l_MsTimes++ ) //500ms還沒有發(fā)送成功,重新初始化設(shè)備
{
NRF24L01_Gpio_Init_transmit( );
RF24L01_Init( );
RF24L01_Set_Mode( MODE_TX );
break;
}
}
l_Status = NRF24L01_Read_Reg(STATUS); //讀狀態(tài)寄存器
NRF24L01_Write_Reg( STATUS, l_Status ); //清除TX_DS或MAX_RT中斷標志
if( l_Status & MAX_TX ) //達到最大重發(fā)次數(shù)
{
NRF24L01_Write_Reg( FLUSH_TX,0xff ); //清除TX FIFO寄存器
return MAX_TX;
}
if( l_Status & TX_OK ) //發(fā)送完成
{
return TX_OK;
}
return 0xFF; //其他原因發(fā)送失敗
}
void key_gpio_config(void)
{
RF24L01_CE_GPIO_RCC_ENABLE(); // 使能GPIO時鐘
GPIO_InitTypeDef GPIO_InitStruct; // GPIO初始化結(jié)構(gòu)體
GPIO_InitStruct.Pins = RF24L01_IRQ_GPIO_PIN; // GPIO引腳
GPIO_InitStruct.Mode = GPIO_MODE_INPUT_PULLUP; // 上拉輸入
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; // 速度高
GPIO_InitStruct.IT = GPIO_IT_FALLING; // 下降沿觸發(fā)中斷
GPIO_Init(RF24L01_GPIO_PORT, &GPIO_InitStruct); // 初始化
// 清除PA0中斷標志
GPIOA_INTFLAG_CLR(BSP_KEY_EXTI_PIN);
// 使能NVIC
NVIC_EnableIRQ(BSP_KEY_EXTI_IRQN);
}
/**
* @brief :RF24L01引腳初始化
* @param :無
* @note :無
* @retval:無
*/
void NRF24L01_Gpio_Init_receive( void )
{
RF24L01_CE_GPIO_RCC_ENABLE(); // GPIO時鐘使能
//CE推挽輸出
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pins = RF24L01_CE_GPIO_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_Init(RF24L01_GPIO_PORT, &GPIO_InitStructure);
//// IRQ上拉輸入
// gpio_mode_set(RF24L01_IRQ_GPIO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP,RF24L01_IRQ_GPIO_PIN);
// gpio_output_options_set(RF24L01_IRQ_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,RF24L01_IRQ_GPIO_PIN);
// gpio_bit_set(RF24L01_IRQ_GPIO_PORT,RF24L01_IRQ_GPIO_PIN);
// IRQ外部中斷方式
key_gpio_config();
RF24L01_SET_CE_LOW( );
RF24L01_SET_CS_HIGH( );
}
/************************************************
函數(shù)名稱 : BSP_KEY_EXTI_IRQHandler
功 能 : 中斷處理函數(shù)
參 數(shù) : 無
返 回 值 : 無
作 者 : LCEDA
*************************************************/
extern uint8_t g_RF24L01RxBuffer[30];
extern uint32_t flag;
void Buff_Clear(void)
{
for(int i = 0; i < 30; i++)
{
g_RF24L01RxBuffer[i] = 0;
}
}
void BSP_KEY_EXTI_IRQHANDLER(void)
{
if(RF24L01_GPIO_PORT->ISR_f.BSP_KEY_EXTI_JUDGE) // 中斷標志位為1,按鍵按下
{
if(GPIO_ReadPin(RF24L01_GPIO_PORT, RF24L01_IRQ_GPIO_PIN) == GPIO_Pin_RESET) // IRQ為低電平
{
NRF24L01_RxPacket(g_RF24L01RxBuffer); //接收數(shù)據(jù)
// printf("data = %s",g_RF24L01RxBuffer );//輸出數(shù)據(jù)
RF24L01_SET_CS_LOW( ); //片選
drv_spi_read_write_byte( FLUSH_RX );//清除RX FIFO寄存器
RF24L01_SET_CS_HIGH( );
}
else// IRQ為高電平
{
}
GPIOA_INTFLAG_CLR(BSP_KEY_EXTI_PIN); // 清除標志位
}
}
/**
* @brief :NRF24L01接收數(shù)據(jù)
* @param :
* @rxbuf:接收數(shù)據(jù)存放地址
* @note :無
* @retval:接收的數(shù)據(jù)個數(shù)
*/
uint8_t NRF24L01_RxPacket( uint8_t *rxbuf )
{
uint8_t l_Status = 0, l_RxLength = 0;
l_Status = NRF24L01_Read_Reg( STATUS ); //讀狀態(tài)寄存器
NRF24L01_Write_Reg( STATUS,l_Status ); //清中斷標志
if( l_Status & RX_OK) //接收到數(shù)據(jù)
{
l_RxLength = NRF24L01_Read_Reg( R_RX_PL_WID ); //讀取接收到的數(shù)據(jù)個數(shù)
NRF24L01_Read_Buf( RD_RX_PLOAD,rxbuf,l_RxLength ); //接收到數(shù)據(jù)
NRF24L01_Write_Reg( FLUSH_RX,0xff ); //清除RX FIFO
return l_RxLength;
}
return 0; //沒有收到數(shù)據(jù)
}
/**
* @brief :RF24L01引腳初始化
* @param :無
* @note :無
* @retval:無
*/
void NRF24L01_Gpio_Init_transmit( void )
{
RF24L01_CE_GPIO_RCC_ENABLE(); // GPIO使能時鐘
//CE推挽輸出
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pins = RF24L01_CE_GPIO_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_Init(RF24L01_GPIO_PORT, &GPIO_InitStructure);
// GPIO_InitStructure.Pins = RF24L01_IRQ_GPIO_PIN;
// GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
// GPIO_Init(RF24L01_GPIO_PORT, &GPIO_InitStructure);
//
// GPIO_WritePin( RF24L01_GPIO_PORT, RF24L01_IRQ_GPIO_PIN,GPIO_Pin_SET);
key_gpio_config();
RF24L01_SET_CE_LOW( );
RF24L01_SET_CS_HIGH( );
}
/**
* @brief :RF24L01模塊初始化
* @param :無
* @note :無
* @retval:無
*/
void RF24L01_Init( void )
{
uint8_t addr[5] = {INIT_ADDR};
RF24L01_SET_CE_HIGH( );
NRF24L01_Clear_IRQ_Flag( IRQ_ALL );
#if DYNAMIC_PACKET == 1
NRF24L01_Write_Reg( DYNPD, ( 1 << 0 ) ); //使能通道1動態(tài)數(shù)據(jù)長度
NRF24L01_Write_Reg( FEATRUE, 0x07 );
NRF24L01_Read_Reg( DYNPD );
NRF24L01_Read_Reg( FEATRUE );
#elif DYNAMIC_PACKET == 0
L01_WriteSingleReg( L01REG_RX_PW_P0, FIXED_PACKET_LEN ); //固定數(shù)據(jù)長度
#endif //DYNAMIC_PACKET
NRF24L01_Write_Reg( CONFIG, /*( 1<<MASK_RX_DR ) |*/ //接收中斷*/
( 1 << EN_CRC ) | //使能CRC 1個字節(jié)
( 1 << PWR_UP ) ); //開啟設(shè)備
NRF24L01_Write_Reg( EN_AA, ( 1 << ENAA_P0 ) ); //通道0自動應(yīng)答
NRF24L01_Write_Reg( EN_RXADDR, ( 1 << ERX_P0 ) ); //通道0接收
NRF24L01_Write_Reg( SETUP_AW, AW_5BYTES ); //地址寬度 5個字節(jié)
NRF24L01_Write_Reg( SETUP_RETR, ARD_4000US |
( REPEAT_CNT & 0x0F ) ); //重復(fù)等待時間 250us
NRF24L01_Write_Reg( RF_CH, 00 ); //初始化通道
NRF24L01_Write_Reg( RF_SETUP, 0x26 );
NRF24L01_Set_TxAddr( &addr[0], 5 ); //設(shè)置TX地址
NRF24L01_Set_RxAddr( 0, &addr[0], 5 ); //設(shè)置RX地址
}
四移植驗證
將發(fā)送端代碼燒入開發(fā)板,接收端代碼燒入另一個開發(fā)板。
main.c代碼如下:
/*
* Change Logs:
* Date Author Notes
* 2024-06-21 LCKFB-LP first version
*/
#include "board.h"
#include "stdio.h"
#include "bsp_uart.h"
#include "NRF24L01.h"
#include "drv_spi.h"
#define RECEIVING_MODE 1 // 1:接收模式 0:發(fā)送模式
uint8_t g_RF24L01RxBuffer[30];
int32_t main(void)
{
board_init();
uart1_init(115200);
//SPI初始化
drv_spi_init( );
#if RECEIVING_MODE
NRF24L01_Gpio_Init_receive( );
//檢測nRF24L01
NRF24L01_check( );
//NRF初始化
RF24L01_Init( );
RF24L01_Set_Mode( MODE_RX );//NRF接收模式 .
printf("MODE_RXrn");
while(1)
{
if( 0 != g_RF24L01RxBuffer[0])
{
printf("Data = %srn",g_RF24L01RxBuffer);
Buff_Clear();
}
}
#else
NRF24L01_Gpio_Init_transmit( );
//檢測nRF24L01
NRF24L01_check( );
//NRF初始化
RF24L01_Init( );
RF24L01_Set_Mode( MODE_TX );//NRF發(fā)送模式 .
printf("MODE_TXrn");
while(1)
{
NRF24L01_TxPacket((uint8_t*)"hello LCKFB!rn",13);//NRF發(fā)送數(shù)據(jù)
printf("Sendrn");
delay_ms(200);
}
#endif
}
上電現(xiàn)象:一個開發(fā)板燒錄接收模式的代碼,一個開發(fā)板燒錄發(fā)送模式的代碼。
模塊移植成功案例代碼:
鏈接:https://pan.baidu.com/s/1KReODFKFa4vjTN8qfsaNrQ?pwd=LCKF
提取碼:LCKF