天天看點

NRF24L01+自組網(1對多)實作NRF24L01+自組網(1對多)實作

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=;//打開電平外部中斷
    }
}
           

基本上就是以上的思路了,還請大家多多指教!

繼續閱讀