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,0x00); 
    //使能通道0的接收地址                                         
    NRF24L01_Write_Reg(NRF24L01_WRITE_REG+EN_RXADDR,0x01); 
    //失能自动重发功能                                   
    NRF24L01_Write_Reg(NRF24L01_WRITE_REG+SETUP_RETR,0x00);  
    //设置RF通道为CHANNEL
    NRF24L01_Write_Reg(NRF24L01_WRITE_REG+RF_CH,CHANNEL);    
    //设置TX发射参数,0db增益,250kbps,低噪声增益开启                                            
    NRF24L01_Write_Reg(NRF24L01_WRITE_REG+RF_SETUP,0x27);     
    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,关闭发送和重发中断                       
    NRF24L01_Write_Reg(NRF24L01_WRITE_REG+CONFIG,0x3e);  
    //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,0x00);    
    //使能通道0的接收地址                                      
    NRF24L01_Write_Reg(NRF24L01_WRITE_REG+EN_RXADDR,0x01);  
    //设置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,0x27);   
    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,关闭发送和重发中断                                    
    NRF24L01_Write_Reg(NRF24L01_WRITE_REG+CONFIG, 0x3f);  
    //CE为高,进入接收模式                                      
    Set_NRF24L01_CE;                                                                            
}
//发送一次数据
unsigned char NRF24L01_TxPacket(unsigned char *txbuf)
{
    unsigned char sta;
    uint32_t count = 0;

    //拉低CE脚才能进行写数据
    Clr_NRF24L01_CE;
    //写数据到TXBUF
    NRF24L01_Write_Buf(NRF24L01_WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH); 
    Set_NRF24L01_CE;
    //根据主频而定,足够的延时,等待发送完成
    while(count<300) 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,0xff);
    //达到最大重发次数,要不要都无所谓
    if(sta&MAX_TX)
    {
        return MAX_TX; 
    }
    else if(sta&TX_OK)
    {
        NRF24L01_SEND_COUNT++;    //单次发送计数,用于发送计数统计
        return TX_OK;
    }
    else
    {
        return 0xff;    //其他原因发送失败
    }    
}
//接收一次数据
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, 0xff);  
        return RX_OK;
    } else {
        return 0;
    } 
}
//接收中断处理数据
void interrupt INTERRUPT_InterruptManager(void) 
{
    if (PIE1bits.TMR2IE == 1 && PIR1bits.TMR2IF == 1)
    {
        TMR2_ISR(); 
    }
    else if((IOCIF==1)&&(IOCIE==1))
    {        
        //关闭电平变化中断
        IOCIE=0; 
        //清除PB4电平变化标志                       
        IOCBF4=0;                 
        //对接收数据的返回值进行判断,是否是接收到数据了      
        if(NRF24L01_RxPacket(NRF_RX_BUF) == RX_OK) {
            LED2_SetLow(); //DS2灯
            __delay_ms(5);
            LED2_SetHigh();
            NRF_RX_FLAG = 1; //接收标志位
        }
        IOCIE = 1;
    }
    else if (PIE1bits.RCIE == 1 && PIR1bits.RCIF == 1) 
    {
        EUSART_Receive_ISR();
    } 
    else if (PIE1bits.TXIE == 1 && PIR1bits.TXIF == 1) 
    {
        EUSART_Transmit_ISR();
    } 
}

主从机的发送和接收的代码差不多的,但是从机在处理接收数据时需要与软件分配的地址进行对比:

void interrupt INTERRUPT_InterruptManager(void) 
{
    if((IOCIF==1)&&(IOCIE==1))
    {
        //关闭电平变化中断
        IOCIE=0;
        //清除PB4电平变化标志
        IOCBF4=0;
        //把上次收到的数据进行清零
        for (int i = 0; i < 10; i++) {             
            RX_BUF[i] = 0;
        }
        //接受无线数据进行处理    
        if(NRF24L01_RxPacket(RX_BUF)==RX_OK)
        {
            BCC_BACK_VALUE = BCC(RX_BUF, 9); //CRC校验
            //比对地址信息,0x00为主机地址,0x01~0xFE为从机的地址
            if (RX_BUF[0] == TUREDATA.Data.Addr || RX_BUF[0] == 0x00)
            {
                for (int i = 0; i < 10; i++) {
                    TX_BUF[i] = 0;
                }
                if (BCC_BACK_VALUE != RX_BUF[9]) //校验码错误直接返回数据
                {
                    if (RX_BUF[0] != 0x00) {
                        TX_BUF[0] = RX_BUF[0];
                        TX_BUF[1] = RX_BUF[1];
                        TX_BUF[2] = 0x84;
                        TX_BUF[9] = BCC(TX_BUF, 9);
                        NRF24L01_SendMSG(TX_BUF);
                    }
                    IOCIE = 1; //打开电平外部中断
                    return;
                } else {                                //校验正确
                    TUREDATA.Data.CMD = RX_BUF[1];
                    switch (TUREDATA.Data.CMD) {
                        case 0xB1:
                            NRF24L01_SendMSG((uint8_t *) TUREDATA.DataBuffer);
                            break;
                        default:
                            break;
                    }
                }
            }
        }
        IOCIE=1;//打开电平外部中断
    }
}

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

你可能感兴趣的:(无线通讯)