STM32 + 无线通信模块 NRF24L01 数据收发
NRF24L01的模塊資料,網上已很詳盡了,在這不再重復描述知識點了。
這篇文章的目的,旨在把主要知識點膠接起來,梳理成一套完整的步驟,使器件快速上手匯入工作使用。
將按操作順序,拆分成7個步驟,注解重點、暗坑。以方便自己進行知識管理,亦方便同行兄弟查閱。
代碼方面,盡量做到一行代碼一行解釋。(如有訂正和疑問,可留言交流,將迅速回復。)
目錄
一、思維導圖
二、 引腳連接,解釋
三、SPI初始化、函數封裝
四、NRF24L01參數寫入
五、中斷處理函數
六、發送數據
七、接收數據
八、代碼下載
一、思維導圖
3種主要工作模式、8個命令字、每包32字節有效數據,等等這些,直接思維導圖更有效,不用文字啰嗦。
對主要點進行了組織,包括了工作模式、命令字等圖例,認真過一遍,心里馬上就能有個框框。
點擊一下圖片能變成清晰的大圖。當然,右擊保存就更方便查看了。
二、 引腳連接,解釋
明確一個,NRF24L01和SI24R1的引腳、程序是通用的,無需任何修改,市面上大部份的NRF24L01模塊,用的是SI24R1的芯片。兩者間通信也互通,已測試確認!
下面是一個通用模塊的引腳,以安信可的SI24R1模塊為例:
共8個引腳:
三、SPI初始化、函數封裝
NRF的配置,就是把各種參數(數值),如頻道,速率,目標地址等,用SPI方式寫到指定地址(芯片的寄存器).
?這個寫入配置的動作,拆分開來看,理解為兩部分,是在操作兩種通信,別混亂。
首先主機按NRF datasheet的要求,設置和通過SPI通信,向NRF芯片特定地址(寄存器)寫入參數值;
而這些寫入的數值,就是用于控制NRF與別一個NRF的通信參數。
這兩個通信,理解一下~
1:主機和NRF間的通信:? ? ? ?
- 使用SPI,主機完成對NRF操控,所有操控其實就是4個操作:寫參數、讀參數、寫要發送的數據,讀出收到的數據;?
- 上面的操控,分拆到SPI的操作上,就是常用的6個函數:SPI初始化、字節收發、寫1字節,讀1字節,寫N字節,讀N字節;
- 寫入參數:是NRF與NRF間通信的參數值, 如頻道 ,速率,CRC校檢,自動回答,自動重發,目標地址....
- 讀取參數,主要是STATUS狀態寄存器的值,用于判斷中斷源;
- 寫入發送數據,把待發送的數據寫到TX_FIFO. NRF按包發送數據,包中有效數據最大32字節;要手動做分包處理;
- 讀出收到數據,檢測到RX_DR中斷發生后(IRQ引腳被拉低),用SPI把RX_FIFO緩沖區的值,讀取存放到指自已的緩沖區;
- NRF的SPI通信速度可達10MHz,但為保證數據傳輸的完整不掉包,盡量不要超過8MHz;
- NRF要求SPI通信時,在上升沿采樣數據,要注意時序。
2:NRF和NRF間的收發通信:
- NRF按主機剛才寫到芯片的參數值開始工作(手動),通過電波傳輸到另一個芯片中或接收別一芯片的數據(自動)。
- 這部份通信我們要做的,僅是控制CE引腳的高低電平,配合CONFIG寄存器,使NRF在接收、發送、待機三種狀態切換。
- NRF間的無線通信,基本沒我們什么事。
編程順序:? GPIO配置? >? SPI配置? >? SPI(5個小函數)? >? 寫NRF配置? >? NRF中斷處理(3種情況)? >? NRF收、發(2個主函數)
GPIO初始化代碼:
/*** SPI通信引腳, CS, SCK, MOSI, MISO ***/ GPIOSet (NRF24L01_SPI_CSN_GPIO, NRF24L01_SPI_CSN_PIN, G_MODE_OUT , G_OTYPE_PP, G_OSPEED_25M, G_PUPD_UP , 0); // cs GPIOSet (NRF24L01_SPI_CLK_GPIO , NRF24L01_SPI_CLK_PIN , G_MODE_AF , G_OTYPE_PP , G_OSPEED_25M , G_PUPD_DOWN , G_AF_SPI2 ); // sck GPIOSet (NRF24L01_SPI_MOSI_GPIO ,NRF24L01_SPI_MOSI_PIN , G_MODE_AF , G_OTYPE_PP , G_OSPEED_25M , G_PUPD_DOWN , G_AF_SPI2 ); // mosi GPIOSet (NRF24L01_SPI_MISO_GPIO ,NRF24L01_SPI_MISO_PIN , G_MODE_AF , G_OTYPE_PP , G_OSPEED_25M , G_PUPD_DOWN , G_AF_SPI2 ); // miso /*** NRF控制引腳, CE, IRQ ***/ GPIOSet (NRF24L01_CE_GPIO , NRF24L01_CE_PIN , G_MODE_OUT , G_OTYPE_PP , G_OSPEED_25M , G_PUPD_DOWN , 0); // CE GPIOSet (NRF24L01_IRQ_GPIO , NRF24L01_IRQ_PIN , G_MODE_IN , G_OTYPE_PP , G_OSPEED_25M , G_PUPD_UP , 0); // IRQ NRF24L01芯片的IRQ引腳下拉能力很弱,注意外部上拉電阻大小,及MCU的上下拉GPIOSet是個自己封裝的初始化函數,不用管,重點看其中的參數就好,注意各上下拉。
SPI 初始化代碼:
/*** SPI通信部分 ***/ CSN_HIGH; //失能NRF NRF24L01_SPI_EN_CLOCK ; // 時鐘 NRF24L01_SPIX->CR1 = 0; // 清0 NRF24L01_SPIX->CR1 |= 0<<0; // 采樣沿數, NRF要求上升沿采樣 CPHA:時鐘相位,0x1=在第2個時鐘邊沿進行數據采樣, NRF24L01_SPIX->CR1 |= 0<<1; // 時鐘線閑時極性, CPOL:時鐘極性,0x1=空閑狀態時,SCK保持高電平 NRF24L01_SPIX->CR1 |= 1<<2; // 主從模式, 0=從,1=主 NRF24L01_SPIX->CR1 |= 2<<3; // 波特率控制[5:3], 0=fPCLK/2, 1=/4倍 2=/8 3/16 NRF24L01_SPIX->CR1 |= 0<<7; // LSB先行, 0=MSB, 1=LSB NRF24L01_SPIX->CR1 |= 1<<8; // 內部從器件選擇,根據9位設置(失能內部NSS) NRF24L01_SPIX->CR1 |= 1<<9; // 軟件從器件管理 : 0=禁止軟件管理從設備, 1=使能軟件從器件管理(軟件NSS) NRF24L01_SPIX->CR1 |= 0<<11; // 數據幀格式, 0=8位, 1=16位 NRF24L01_SPIX->CR1 |= 1<<6; // 使能SPI 初始化重點:
- NRF要求在上升沿時采集數值。這就要閑時電平和采樣沿要配合好。
- NRF的SPI可達10MHz, 但實際使用時,不要超過8MHz。這個在SPI的波特率中可控制,因為SPI通信速率受限低速一方。
- 重要:如果多個設備共用同一SPI,把各自的SPI配置封裝成函數,每次使用SPI通信前各自調用一次,以使用自己的配置。
插個話題,為什么使用寄存器操作編程?因為:使用寄存器=簡單+清晰,你看,不是嗎?
SPI收發函數
/***************************************************************************** *函 數: u8 SPI_RW(u8 Data) *功 能: SPI寫入一個字節,并返回一個字節 *參 數: 要寫入的一字節 *返回值: 返回一字節數據 *****************************************************************************/ u8 SPI_SendByte(u8 Data) {u8 retry =0;while((NRF24L01_SPIX ->SR & 2) == 0){ // 理解方式,應該把前式的結果理解為一個寄存器位值,如果這個位值是等號后面的值,就等待retry++;if(retry>200) return 0;}NRF24L01_SPIX ->DR = Data;retry=0;while((NRF24L01_SPIX->SR & 1) == 0 ){retry++;if(retry>200) return 0;}return NRF24L01_SPIX->DR ; }/***************************************************************************** *函 數:u8 Nrf24l01_WriteReg(u8 reg,u8 value) *功 能:向指定寄存器地址,寫一個字節數據 *參 數:reg: 寄存器地址 * val: 要寫入的值 *返回值:status *****************************************************************************/ u8 Nrf24l01_WriteReg(u8 reg,u8 value) {u8 status; CSN_LOW;status = SPI_SendByte(reg) ;SPI_SendByte(value);CSN_HIGH; return status; }/***************************************************************************** *函 數:u8 NRF24l01_read_reg(u8 reg) *功 能:向指定寄存器地址,讀出一字節數據 *參 數:reg: 寄存器地址 *返回值:reg_val(第二個讀取到的字節) *****************************************************************************/ u8 Nrf24l01_ReadReg(u8 reg) {u8 reg_val; CSN_LOW;SPI_SendByte(reg);reg_val = SPI_SendByte(0xFF);CSN_HIGH; return reg_val; }/***************************************************************************** *函 數:u8 Nrf24l01_WriteBuf(u8 reg, u8 *pBuf, u8 len) *功 能:寫一組數據到寄存器 *參 數:reg: 寄存器地址 * pBuf: 要寫入數據的地址 * len: 要寫入的數據長度 *返回值:status *備 注:NRF2401代碼移植只需把SPI驅動修改成自己的即可 *****************************************************************************/ u8 Nrf24l01_WriteBuf(u8 reg, u8 *pBuf, u8 len) {u8 status; CSN_LOW;status = SPI_SendByte(reg);for(u8 i=0; i<len; i++) {SPI_SendByte(pBuf[i]); }CSN_HIGH; return status; }/***************************************************************************** *函 數: u8 vNrf24l01_ReadBuf(u8 reg, u8 *pBuf, u8 len) *功 能: 向指定寄存器地址,讀出指定長度的數據 *參 數: reg : 寄存器地址 * pBuf : 數據存放緩沖區 * len : 讀取的字節數量 *返回值: status : 設備狀態字 *****************************************************************************/ u8 Nrf24l01_ReadBuf(u8 reg, u8 *pBuf, u8 len) {u8 status; CSN_LOW;status = SPI_SendByte(reg);for(u8 i = 0; i<len ;i++){pBuf[i] = SPI_SendByte(0xFF); }CSN_HIGH; return status; }注意:在進入下一步配置NRF24L01前,必須先測試一下SPI通信是否成功 。一般是往NRF的發送地址寄存器寫入五個字節,再讀出來,把讀出的數據和原數據對比,?就能知道SPI是否配置正確、 NRF24L01是否連接成功。
代碼程序中,有個連接測試函數,可以作模塊的連接,沒有貼上來,留郵箱吧。
四、NRF24L01參數寫入
使用上面初始化的SPI,和剛封裝好的幾個函數,就可以把需要的參數,寫到NRF特定的地址(寄存器), 完成對其配置。
NRF24L01 參數配置代碼:
/*** NRF24L01通信配置 30,2M,***/ CE_LOW; // 熱待機模式, 只有在ce置低時,才能配置寄存器 //delayUs(2000); // PowerDown 切換為 PowerUp需要1.5ms Nrf24l01_WriteReg(W_REGISTER + RF_CH, 30); // 射頻通道,即頻率(0-125) Nrf24l01_WriteReg(W_REGISTER + RF_SETUP, 0x0F); // 設置TX發射參數,0db增益,2Mbps,低噪聲增益關閉 (注意:低噪聲增益關閉/開啟直接影響通信,要開啟都開啟,要關閉都關閉0x0f)0x07 Nrf24l01_WriteReg(W_REGISTER + SETUP_AW, 0x03); // 地址長度,默認值時0x03,即5字節 Nrf24l01_WriteBuf(W_REGISTER + TX_ADDR, (u8*)TX_ADDRESS, 5); // 寫TX節點地址, 地址寬度:5字節,40位 Nrf24l01_WriteBuf(W_REGISTER + RX_ADDR_P0, (u8*)TX_ADDRESS, 5); // 設置TX節點地址,主要為了使能ACK,, 地址寬度:5字節,40位 Nrf24l01_WriteReg(W_REGISTER + SETUP_RETR, 0x0A); // 設置自動重發間隔時間:500us + 86us;最大自動重發次數:10次 0x1A Nrf24l01_WriteReg(W_REGISTER + EN_RXADDR, 0x01); // 使能通道0的接收地址 Nrf24l01_WriteReg(W_REGISTER + EN_AA, 0x01); // 使能通道0自動應答 Nrf24l01_WriteReg(W_REGISTER + RX_PW_P0, 32); // 選擇通道0的有效數據寬度 Nrf24l01_WriteReg(FLUSH_TX, 0xff); // 清除TX_FIFO Nrf24l01_WriteReg(W_REGISTER+STATUS, 0X7E); // 清除所有中斷,防止一進去接收模式就觸發中斷 Nrf24l01_WriteReg(W_REGISTER+CONFIG, 0x0F); // 配置為接收模式 CE_HIGH; // CE置高,進入狀態重點:
- CE引腳置低,方可配置NRF寄存器!? 各教程都這教的,但測試過不置低也可正常配置。
- RF_CH,:頻道,2.4G為基址,1M為間隔值,如上面的30,代表使用2.430G的頻率進行芯片間通信??稍O值0~125。
- RF_SETUP: 發射功率可配置,說白了就是的耗電程度。接收狀態的功率是不能設置的,所以接收才是耗電的大頭。數據傳輸率,常說的空中速率,一般設置2M
- 地址長度,5字節,這個很獨特
- TX_ADDR:數據要發送到的目標地址
- RX_ADDR_P0: 接收通道0的接收地址,接收這個地址設備發來的數據,這里設為和TX_ADDR一致,是為了自動應答。
- NRF共有6個接收通道,p0~p5, 可同時監聽6個不同地址的信號,p0通道也用于作自動應答作用。
- TX_FIFO, 發送數據緩沖區,96字節,32字節為一組,共3組,可理解為緩沖區可存放3組發送數據。
- RX_FIFO, 同上,兩個FIFO相互獨立。 雖然是FIFO,雖然有3組,但最好還是一包一包收發,狀態切換時間難把握。
- 倒數第二行,配置為接收模式,注意,這個時候還沒開始工作的,還處于PownDown狀態,狀態和模式是兩回事。
- 最后一行,CE置高10us后,才進入工作狀態,因之前配置的是接收模式,所以將進入接收狀態
五、中斷處理函數
為什么要先說中斷?感覺先了解了中斷,那么發送、接收就更好理解。
其實不應該叫中斷的,但這樣好理解,還是遵從約定俗成吧。
中斷時, IRQ電平被拉低,是由NRF控制產生的,三種情況可觸發:發送成功、達到重發最大次數、接收到數據。
發送成功:
- 1:PTX發送數據后,馬上開始計時,130us后切換到接收模式,此時計時還在繼續
- 2:PRX在收到數據后,經CRC校檢,數據完整后發回ACK信號
- 3:PTX在規定時間內收到ACK信號,則置位TX_DS標志,IRQ引腳被拉低
達到重發最大次數:
- 1:PTX發送數據后,馬上開始計時,130us后切換到接收模式,此時計時還在繼續
- 2:PTX在規定時間內,沒收到ACK信號,原因很多:如PRX沒收到數據,CRC校驗錯誤....
- 3:PTX再次發送一次數據,重復步驟1
- 4:SETUP_RETR寄存器可設置重發的次數,達到最大次數后,MAX_RT位被置位,IRQ引腳被拉低。
接收到數據:
- PRX收到數據,經CRC校檢,數據完整,有效數據存放到RX_FIFO,RX_DR位置高,IRQ引腳被拉低。
說說清理中斷,要清理的中斷有兩處:
- NRF的中斷位,上面的三種標志中斷位,都在寄存器STATUS中,各位置1可清0,IRQ引腳即回到高電平。
- 單片機mcu的中斷位,清理外部中斷線標志位,否則會在中斷函數里死循環。
中斷處理函數代碼:
void NRF24L01_IRQ_IRQHANDLER(void) { u8 status=0 ;CE_LOW; // 拉低CE,以便讀取NRF中STATUS中的數據 status = Nrf24l01_ReadReg(R_REGISTER + STATUS); // 讀取STATUS中的數據,以便判斷是由什么中斷源觸發的IRQ中斷/*** 發送完成中斷 ***/if(status & STATUS_TX){ Nrf24l01_WriteReg(W_REGISTER+STATUS, STATUS_TX); // 清NRF中斷:發送完成Nrf24l01_WriteReg(FLUSH_TX, 0xff); // 清發送緩沖區:TX_FIFOprintf("\r\n發送成功!!!!\r\n");vNrf24l01_RxMode (); // 切換為接收狀態}/*** 接收完成中斷 ***/if(status & STATUS_RX){ memset (NRF_RX_DATA , 0, 32); Nrf24l01_ReadBuf(R_RX_PAYLOAD, NRF_RX_DATA , RX_PAYLO_WIDTH); // 讀數據Nrf24l01_WriteReg(W_REGISTER+STATUS, STATUS_RX); // 清NRF中斷:收到數據Nrf24l01_WriteReg(FLUSH_RX,0xff); // 清除RX FIFO(注意:這句話很必要) printf("\r\n接收到數據: %s\r\n", NRF_RX_DATA); vNrf24l01_RxMode (); // 切換為接收狀態}/*** 最大重發次數中斷 ***/if(status & STATUS_MAX){ Nrf24l01_WriteReg(W_REGISTER+STATUS, 0x70); // 清NRF中斷:三個Nrf24l01_WriteReg(FLUSH_TX, 0xff); // 清除TX_FIFO printf("\r\n發送失敗,達到最大重發次數!!!\r\n"); vNrf24l01_RxMode(); // 切換為接收狀態}EXTI->PR |= NRF24L01_IRQ_PIN ; // 清理外部中斷線標志位 }六、發送數據
NRF的數據收發,是一包一包進行的,一包(幀)數據:包括了前導碼、目標地址、包控制域、有效數據、CRC, 但我們只管有效數據,其它的不用我們負責,NRF發送時自動打包,接收到數據時自動拆包。
每一包的有效數據最大為32個字節。當然,也可以只發一個字節的數據。
要發送的數據大于32字節,就要分包進行,自行手動分包處理。
因為在配置部分時,已配置好了頻道,速率,重發次數等各種參數,在需要發送數據時,只要往芯片寫入要發送的數據和地址,然后切換為發送狀態,芯片就會自動發送。
發送成功(收到ack),會產生TX_DS中斷。
發送失敗了(達到最大重發次數), 也會產生MAR_RT中斷。
在中斷函數里,根據情況作處理就好。
發送數據代碼:
void vNrf24l01_TxPacket(u8 *txbuf) {CE_LOW; Nrf24l01_WriteBuf(W_TX_PAYLOAD, txbuf, 32); // 寫數據到TX_BUFF Nrf24l01_WriteBuf(W_REGISTER+TX_ADDR, (u8*)TX_ADDRESS, 5); // 寫入要發送的目標地址 Nrf24l01_WriteBuf(W_REGISTER+RX_ADDR_P0, (u8*)TX_ADDRESS, 5); // 通道0的地址設為和目標地址一致,以接收自動回復auto_ack信號 Nrf24l01_WriteReg(W_REGISTER+CONFIG, 0x0E); // 設置為發送模式,開啟所有中斷 CE_HIGH; }發送就這幾句!
重點:RX_ADDR_P0的地址和TX_ARRD一樣,目的是自動應答。有個前提,在配置中已使能自動應答。
把操作封裝成一個函數,要發什么,就往函數里掉數據就好,每次不要大于32字節。
七、接收數據
當系統或程序運行時,大部分時間是運行在接收狀態下的。如:
- 第四個部分配置步驟完成后,程序已處在接收狀態。
- 第五個部分,在中斷處理函數中,三種中斷處理后,也切換為接收狀態,當然,也可以切換為更省電的待機狀態。
NRF有6個接收通道,指在可同時監聽接收同一個頻道,同一速率的6個不同設備的數據。
常用的只是通道P0, 如果只使能了通道P0,那就只能接收到P0中地址設備發來的數據。
可以使能全部6個通道,設置6個不同設備地址,就可以監聽接收6個設備發來的數據(同一時間,只能接收其中1個設備的數據);
注意,接收數據,指在接收中斷發生后,我們從RX_FIFO中把數據讀存到主機。而中斷發生前的監聽接收,NRF自動完成。
接收數據的代碼:
接收本來就是最簡單的,沒啥特別代碼,下面的代碼,只是在中斷處理函數里,再貼出來而已。
Nrf24l01_ReadBuf(R_RX_PAYLOAD, NRF_RX_DATA , RX_PAYLO_WIDTH); // 讀數據到數組 Nrf24l01_WriteReg(W_REGISTER+STATUS, STATUS_RX); // 清NRF中斷:收到數據 Nrf24l01_WriteReg(FLUSH_RX,0xff); // 清除RX FIFO(注意:這句話很必要)把接收到的數據,先存放NRF_RX_DATA數組,再進行處理,不要在中斷函數中處理。
讀完后,記得要清理RX_FIFO,不然它一直占用NRF的緩沖區。RX_FIFO共96字節,分成32字節3組,NRF每次接收到數據就存放到最后面的一組中,當存滿了3組,后面再接收到的數據,就會被NRF掉棄。
八、代碼下載
兩種方式,
1:代碼已打包并上傳成CSDN下載資源:
無線通信_NRF24L01.zip_nrf24l01發送前導碼-嵌入式文檔類資源-CSDN下載
2:由于咨詢代碼的人多,已把代碼打包上傳到Q群,群文件夾里找吧:262901124.
最后,如果你有更完善的代碼,或修改后的代碼,請回贈我一份,謝謝~~~
寫了三四個小時,累~~~
總結
以上是生活随笔為你收集整理的STM32 + 无线通信模块 NRF24L01 数据收发的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 橡皮擦是黑色的
- 下一篇: 无线通信模块定点传输-点对多点的具体传输