NRF24L01+自組網(1對多)實作
本算法基于軟體配置設定和判斷從機位址的方式實作,子產品不使用自動應答模式和自動重發模式,并且關閉發送中斷和最大重發次數中斷。故将NRF24L01+子產品的代碼部分發出來分享一下,算法算不上完美,大神輕噴:
算法注意點:
- 主機和從機共同使用同一個通道,所有從機的位址相同,是以主機發送指令時,要帶上軟體配置設定的從機位址
- 關閉發送中斷和最大重發次數中斷,防止發送完成或者超過重發次數引起中斷,但不會影響讀取标志位的值
- 從機采用中斷接收模式,主機采用定時器定時查詢NRF24L01+子產品的STATU寄存器進行判斷,也可用中斷方式進行接收,但是要考慮到中斷是下降沿觸發,但是要考慮到單片機有沒有可能在處理别的中斷的時候,由于關閉了全局中斷而此時有觸發了接收中斷而不能及時處理,進而錯過了下降沿觸發的時間,需根據具體情況具體分析。
- 由于屏蔽了發送中斷,在發送時需要對CE管腳的高電平時間進行足夠的延時,不然會引起發送亂碼或者發送失敗
- 由于采用的是非自動應答模式,是以需要切換發送和接收模式
- 為了保證通訊的品質,需要對發送的資料進行CRC校驗
代碼塊
使用了PIC16F1876系列,發送和接收設定代碼:
//發送模式配置字
void TX_Mode(void)
{
Clr_NRF24L01_CE;
//寫TX節點位址
NRF24L01_Write_Buf(NRF24L01_WRITE_REG+TX_ADDR,(unsigned char*)TX_ADDRESS,TX_ADR_WIDTH);
//設定TX節點位址,主要為了使能ACK
NRF24L01_Write_Buf(NRF24L01_WRITE_REG+RX_ADDR_P0,(unsigned char*)RX_ADDRESS,RX_ADR_WIDTH);
//失能自動應答功能(廣播模式)
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+EN_AA,);
//使能通道0的接收位址
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+EN_RXADDR,);
//失能自動重發功能
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+SETUP_RETR,);
//設定RF通道為CHANNEL
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+RF_CH,CHANNEL);
//設定TX發射參數,0db增益,250kbps,低噪聲增益開啟
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+RF_SETUP,);
//配置基本工作模式的參數;PWR_UP,EN_CRC,16BIT_CRC,接收模式,關閉發送和重發中斷
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+CONFIG,);
//CE為高,10us後啟動發送
Set_NRF24L01_CE;
}
//接收模式配置字
void RX_Mode(void)
{
Clr_NRF24L01_CE;
//寫RX節點位址
NRF24L01_Write_Buf(NRF24L01_WRITE_REG+RX_ADDR_P0,(unsigned char*)RX_ADDRESS,RX_ADR_WIDTH);
//失能自動應答功能(廣播模式)
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+EN_AA,);
//使能通道0的接收位址
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+EN_RXADDR,);
//設定RF通信頻率
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+RF_CH,CHANNEL);
//選擇通道0的有效資料寬度
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);
//設定TX發射參數,0db增益,250kbps,低噪聲增益開啟
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+RF_SETUP,);
//配置基本工作模式的參數;PWR_UP,EN_CRC,16BIT_CRC,接收模式,關閉發送和重發中斷
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+CONFIG, );
//CE為高,進入接收模式
Set_NRF24L01_CE;
}
//發送一次資料
unsigned char NRF24L01_TxPacket(unsigned char *txbuf)
{
unsigned char sta;
uint32_t count = ;
//拉低CE腳才能進行寫資料
Clr_NRF24L01_CE;
//寫資料到TXBUF
NRF24L01_Write_Buf(NRF24L01_WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);
Set_NRF24L01_CE;
//根據主頻而定,足夠的延時,等待發送完成
while(count<) count++;
sta=NRF24L01_Read_Reg(STATUSET);
//清除TX_DS或MAX_RT中斷标志
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+STATUSET,sta);
//清除TX FIFO寄存器
NRF24L01_Write_Reg(NRF24L01_FLUSH_TX,);
//達到最大重發次數,要不要都無所謂
if(sta&MAX_TX)
{
return MAX_TX;
}
else if(sta&TX_OK)
{
NRF24L01_SEND_COUNT++; //單次發送計數,用于發送計數統計
return TX_OK;
}
else
{
return ; //其他原因發送失敗
}
}
//接收一次資料
unsigned char NRF24L01_RxPacket(unsigned char *rxbuf)
{
unsigned char sta;
Set_NRF24L01_CE;
sta = NRF24L01_Read_Reg(STATUSET);
//清除TX_DS或MAX_RT中斷标志
NRF24L01_Write_Reg(NRF24L01_WRITE_REG + STATUSET, sta);
//接收到資料
if (sta & RX_OK)
{
//讀取資料
NRF24L01_Read_Buf(NRF24L01_RD_RX_PLOAD, rxbuf, RX_PLOAD_WIDTH);
//清除RX FIFO寄存器
NRF24L01_Write_Reg(NRF24L01_FLUSH_RX, );
return RX_OK;
} else {
return ;
}
}
//接收中斷處理資料
void interrupt INTERRUPT_InterruptManager(void)
{
if (PIE1bits.TMR2IE == && PIR1bits.TMR2IF == )
{
TMR2_ISR();
}
else if((IOCIF==)&&(IOCIE==))
{
//關閉電平變化中斷
IOCIE=;
//清除PB4電平變化标志
IOCBF4=;
//對接收資料的傳回值進行判斷,是否是接收到資料了
if(NRF24L01_RxPacket(NRF_RX_BUF) == RX_OK) {
LED2_SetLow(); //DS2燈
__delay_ms();
LED2_SetHigh();
NRF_RX_FLAG = ; //接收标志位
}
IOCIE = ;
}
else if (PIE1bits.RCIE == && PIR1bits.RCIF == )
{
EUSART_Receive_ISR();
}
else if (PIE1bits.TXIE == && PIR1bits.TXIF == )
{
EUSART_Transmit_ISR();
}
}
主從機的發送和接收的代碼差不多的,但是從機在處理接收資料時需要與軟體配置設定的位址進行對比:
void interrupt INTERRUPT_InterruptManager(void)
{
if((IOCIF==)&&(IOCIE==))
{
//關閉電平變化中斷
IOCIE=;
//清除PB4電平變化标志
IOCBF4=;
//把上次收到的資料進行清零
for (int i = ; i < ; i++) {
RX_BUF[i] = ;
}
//接受無線資料進行處理
if(NRF24L01_RxPacket(RX_BUF)==RX_OK)
{
BCC_BACK_VALUE = BCC(RX_BUF, ); //CRC校驗
//比對位址資訊,0x00為主機位址,0x01~0xFE為從機的位址
if (RX_BUF[] == TUREDATA.Data.Addr || RX_BUF[] == )
{
for (int i = ; i < ; i++) {
TX_BUF[i] = ;
}
if (BCC_BACK_VALUE != RX_BUF[]) //校驗碼錯誤直接傳回資料
{
if (RX_BUF[] != ) {
TX_BUF[] = RX_BUF[];
TX_BUF[] = RX_BUF[];
TX_BUF[] = ;
TX_BUF[] = BCC(TX_BUF, );
NRF24L01_SendMSG(TX_BUF);
}
IOCIE = ; //打開電平外部中斷
return;
} else { //校驗正确
TUREDATA.Data.CMD = RX_BUF[];
switch (TUREDATA.Data.CMD) {
case :
NRF24L01_SendMSG((uint8_t *) TUREDATA.DataBuffer);
break;
default:
break;
}
}
}
}
IOCIE=;//打開電平外部中斷
}
}
基本上就是以上的思路了,還請大家多多指教!