1. 程式人生 > >STM32驅動NRF24L01一對多的通訊---可變資料包寬度

STM32驅動NRF24L01一對多的通訊---可變資料包寬度

既然是一對多可變payload寬度的通訊,肯定是包含兩個方面:
(1)能進行一對多通訊(同個頻道下一般最多是一對六)
(2)傳送的資料包寬度是可變的
配置NRF24L01進行一對多通訊,前提是一對一通訊機制必須要清楚。
我個人的理解是這樣的

PTX端需要配置的地址TX_ADDR和RX_ADDR
PRX端需要配置的地址RX_ADDR
至於以上地址在這個機制中是怎麼用的,為什麼會相同,下面會解釋。
進入正題
ShockBurst™下資料包格式:
這裡寫圖片描述
PTX端傳送資料前,會先對資料進行打包。在這個資料包紅色框中的就是PTX發射端的地址TX ADDR。當PRX接收到一包有效的資料時,它會解析這個資料包中的Address地址是否跟它自身RX_ADDR相同,如果是相同的,那它就認為這是個發給我的包,如果不相同呢,那肯定是發給別人的包,就會被丟棄。好的,傳送和接收都搞明白了,還有應答訊號(前提是使能了自動應答)。前面說了PRX會對比Address地址是否跟自身RX_ADDR相同,一旦對比成功,PRX會自動轉換到傳送模式並以這個地址作為傳送地址傳送應答訊號。那麼PTX是怎麼接收這個應答訊號的呢(前提是使能了自動應答),PTX在傳送了一包資料後會自動轉換為接收模式,等待接收PRX發過來的應答訊號。那PTX是怎麼知道需要接收哪個從機發送過來的應答訊號呢,PTX端的RX_ADDR就起作用了,PTX也會檢測應答包中的地址是不是跟自身的RX_ADDR地址相同,相同就表示這是個正確的應答包,TX_DS中斷被觸發,MCU通過識別中斷號就能知道傳送成功。
明白了一對一通訊的機制,也就明白了為什麼PTX端的TX_ADDR,RX_ADDR,和PRX端的RX_ADDR是相同的。其實這三個地址說白了就是一個地址,就是PRX某個PIPEx的地址

,這就是一對多通訊的基礎

一般來說一對多說的是,一個接收N個傳送
MultiCeiver(多方通訊):
MultiCeiver 是接收模式下的一個功能,包含了一組共六個並行的資料通道,每個通道都擁有獨一無二的地址。一個RF通道對應一個邏輯資料通道。在NRF24L01+中,每個資料通道都有其自身的實體地址。
這裡寫圖片描述
NRF24L01+配置成PRX(接收者)後,在同一通訊頻道中,可以接收6個不同資料通道的資料。6個NRF24L01+被配置為PTX(傳送者),他們能與同一個PRX通訊。PRX能同時搜尋所有的資料通道。但是同一時刻只有一個數據通道能接收資料包。所有的資料通道都能工作在Enhanced ShockBurst™下。
以下配置所有的資料通道都一樣:

CRC使能/失能(CRC在Enhanced ShockBurst™下一般都是被使能的)
CRC編碼
RX地址寬度
通訊頻道
傳輸速率
LNA增益

資料通道的使能在EN_RXADDR暫存器。預設情況下只有資料通道0和1被使能。每個資料通道的地址在RX_ADDR_Px暫存器中設定。
NOTE:確保每個資料通道的地址都是不同的。否則GG

每個通道有5個位元組的可配置地址。資料通道0有獨一無二的5個位元組的地址。資料管道1~5共享4地址位元組。但是6個通道的最低位元組必須是不同的。如圖13的例子所示:
這裡寫圖片描述
PRX使用MultiCeiver™ 和 Enhanced ShockBurst™與多個PTX通訊。為了確保從PRX傳送的應答包能被正確的PTX接收,PRX會獲取當前接收到的資料包中的通道地址,然後以這個地址作為傳送地址傳送應答資料包。圖14中的例子解釋了PRX和PTX的地址配置詳情。在PRX中RX_ADDR_Pn是管道地址,必須是獨一無二的。在PTX中TX_ADDR是指定通道的通道地址,必須跟RX_ADDR_P0(沒錯就是RX_ADDR_P0不是RX_ADDR_Pn)中設定的地址相同。也就是說假如有6個PTX跟1個PRX建立通訊,這6個PTX的TX ADDR1~TX ADDR6就必須要一一對應PRX端通道0~通道5的地址,而每個PTX端的接收通道0(RX_ADDR_P0)必須跟其對應的TX ADDR相同。
這裡寫圖片描述


只有當一個數據通道接收一個正確的資料包時,其他的通道才能開始接收資料。
配置可變寬度的資料包
Enhanced ShockBurst™有兩種可供選擇的payload長度:
(1)static length,通過設定接收端的RX_PW_Px寄存實現,接收端TX FIFO中的資料長度必須跟接收端設定的想等。
(2)Dynamic payload length(DPL):DPL允許PTX端傳送可變長度payload給接收端,而接收端不需要使用RX_PW_Px暫存器,MCU使用R_RX_PL_WID命令能讀出payload的長度。
使用DPL模式,必須使能FREATURE暫存器的EN_DPL位,在接收模式下,DYNPD暫存器的DPL_Px必須要設定。
NOTE:MCU最好能每次都檢查一下接收到的payload的長度是不否大於32位元組,如果大於32位元組可能是接收到看錯誤的資訊,需要丟棄。丟棄一幀資料包使用Flush_RX命令。
注意:PTX和PRX同時需要設定FREATURE暫存器的EN_DPL位和DYNPD暫存器的DPL_Px位。
舉個一對六的配置
接收節點

const u8 PIPE0_RX_ADDRESS[RX_ADR_WIDTH]={0x78,0x78,0x78,0x78,0x78};//接收通道0地址
const u8 PIPE1_RX_ADDRESS[RX_ADR_WIDTH]={0xF1,0xB3,0xB4,0xB5,0xB6};//接收通道0地址
const u8 PIPE2_RX_ADDRESS[1]={0xCD};//接收通道0地址,PIPE2~5共用PIPE1的4位元組地址,低位元組地址在前,下同
const u8 PIPE3_RX_ADDRESS[1]={0xA3};//接收通道0地址
const u8 PIPE4_RX_ADDRESS[1]={0x0F};//接收通道0地址
const u8 PIPE5_RX_ADDRESS[1]={0x05};//接收通道0地址
void NRF24L01_RX_Mode(void)
{
    NRF24L01_CE_L();      
    NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)PIPE0_RX_ADDRESS,RX_ADR_WIDTH);//寫通道0地址
    NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P1,(u8*)PIPE1_RX_ADDRESS,RX_ADR_WIDTH);//寫通道1地址
    NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P2,(u8*)PIPE2_RX_ADDRESS,1);//寫通道2地址
    NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P3,(u8*)PIPE3_RX_ADDRESS,1);//寫通道3地址
    NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P4,(u8*)PIPE4_RX_ADDRESS,1);//寫通道4地址
    NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P5,(u8*)PIPE5_RX_ADDRESS,1);//寫通道5地址
    NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x3F);    //使能所有通道自動應答   
    NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x3F);//使能所有接收地址  
    NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,0x40);        //設定傳送頻道24.064Ghz 
//  NRF24L01_Write_Reg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//通道0資料寬度,DPL不需要設定接收資料寬度所以註釋掉,下同
//  NRF24L01_Write_Reg(NRF_WRITE_REG+RX_PW_P1,RX_PLOAD_WIDTH);//通道1資料寬度
    NRF24L01_Write_Reg(NRF_WRITE_REG+FEATURE,0x04|0x02);    //配合DPL功能使用
    NRF24L01_Write_Reg(NRF_WRITE_REG+DYNPD,0x3f);    //使能所有通道DPL功能
    NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x07); 
    NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG, 0x0f);//主接收
    NRF24L01_CE_H(); //啟動接收
}       

傳送節點0的程式碼,1~5只要更換本地地址和接收地址就行啦,6個不同的地址下面都列出來了

u8 const PIPE0_TX_ADDRESS[TX_ADR_WIDTH]= {0x78,0x78,0x78,0x78,0x78};    //PTX0本地地址
u8 const PIPE0_RX_ADDRESS[RX_ADR_WIDTH]= {0x78,0x78,0x78,0x78,0x78};    //PIPE0接收地址

u8 const PIPE1_TX_ADDRESS[TX_ADR_WIDTH]= {0xF1,0xB3,0xB4,0xB5,0xB6,};   //PTX1本地地址
u8 const PIPE1_RX_ADDRESS[RX_ADR_WIDTH]= {0xF1,0xB3,0xB4,0xB5,0xB6,};   //PIPE1接收地址

u8 const PIPE2_TX_ADDRESS[TX_ADR_WIDTH]= {0xCD,0xB3,0xB4,0xB5,0xB6};        //PTX2本地地址
u8 const PIPE2_RX_ADDRESS[RX_ADR_WIDTH]= {0xCD,0xB3,0xB4,0xB5,0xB6};        //PIPE2接收地址

u8 const PIPE3_TX_ADDRESS[TX_ADR_WIDTH]= {0xA3,0xB3,0xB4,0xB5,0xB6};        //PTX3本地地址
u8 const PIPE3_RX_ADDRESS[RX_ADR_WIDTH]= {0xA3,0xB3,0xB4,0xB5,0xB6};        //PIPE3接收地址

u8 const PIPE4_TX_ADDRESS[TX_ADR_WIDTH]= {0x0F,0xB3,0xB4,0xB5,0xB6};        //PTX4本地地址
u8 const PIPE4_RX_ADDRESS[RX_ADR_WIDTH]= {0x0F,0xB3,0xB4,0xB5,0xB6};        //PIPE4接收地址

u8 const PIPE5_TX_ADDRESS[TX_ADR_WIDTH]= {0x05,0xB3,0xB4,0xB5,0xB6};        //PTX5本地地址
u8 const PIPE5_RX_ADDRESS[RX_ADR_WIDTH]= {0x05,0xB3,0xB4,0xB5,0xB6};        //PIPE5接收地址

void NRF24L01_TX_Mode(void)
{                                                        
    NRF24L01_CE_L();    
    NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,(u8*)PIPE0_TX_ADDRESS,TX_ADR_WIDTH);//寫傳送端的地址
    NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)PIPE0_RX_ADDRESS,RX_ADR_WIDTH);//寫接收端PIPE0的地址  

    NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01);     //使能PIPE0自動應答
    NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能PIPE0接收地址
    NRF24L01_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a);
    NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,0x40);
    NRF24L01_Write_Reg(NRF_WRITE_REG+FEATURE,0x04|0x02);    //配合DPL功能使用
    NRF24L01_Write_Reg(NRF_WRITE_REG+DYNPD,0x3f);    //使能所有通道DPL功能
    NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x07); 
    NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG,0x0e);
    NRF24L01_CE_H();
}

製作信標燈
2017/3/18