利用nRF24L01组件组件小型控制网络

最新心血来潮,看着手里的一块STM32开发板和几个AT89C4051和nRF24L01无线模块,准备利用它们做成一套无线控制系统。由于nRF24L01无线模块的特点,打算利用不同的物理地址,通过程序控制,实现组网控制。有了想法,开始着手设计。

    首先是主控制系统,使用早期入手的智嵌STM32开发板。为啥要用这块板子呢?因为这个开发板上已经设置nRF24L01接口,不用单独设计了,拿来直接使用就行,很方便。

各个被控制的分机,使用AT89C4051。其实更想用STC家的产品,因为有看门狗,在无人值守的时候,更适合对应程序跑飞这种情况。奈何手里的这几片AT89C4051实在是闲置太久,本着不想浪费的原则,就只好将就一下了。为了防止程序跑飞,只能再考虑在各个分机的公用电源控制上,加一个单独的有看门狗的电源控制模块。

    主控制板的图片如下:

网口下面的那个8口双排插座的位置就是给nRF24L01用的。可以直接插上nRF24L01使用。而且卖家还提供了例程,例程里就有nRF24L01的,拿来改改就能用。话说“天下**一大抄,抄来抄去有提高”,其实程序也是这样,哈哈哈,题外话了。作为测试,修改目标是,按下开发板上四个按键中的某一个时,向地址匹配的分机发送信号。主机加载nRF24L01的样子如下:


分机的样子如下:

1对应的是接收信号用的nRF24L01模块。

2对应的外部测量信号输入端,配合AT89C4051的比较器使用。如果使用可以互换的STC芯片,这个地方可以用AD输入作为动作条件。

3对应的驱动外部继电器模块。提供5V、GND、驱动信号。驱动信号低电平有效。

4对应的是串口输入输出。主要是开发阶段测试用。实际应用时,可以不用串口。空出P3.0和P3,1给U3(24C64)作为控制信号用。

5对应的是拨码开关。目的是设置分机的地址。这个地址是和主控板的K1-K4一一对应的。

挂载了nRF24L01模块、继电器模块、连接串口的分机,如下图所示:

主控板主处理代码如下:

int main(void) {

         unsignedchar i=0, kv=0, tmp_buf[TX_PLOAD_WIDTH], addr1=0, addr2=0, errorFlag=0;

   /* 串口初始化 */

   Printf_Init();

   SysTick_Configuration();           //系统初始化     

       Key_Configuration();            // 初始化按键用到的GPIO口

         LED_Configuration();            // 初始化LED用到的GPIO口

         NVIC_Configuration();

   NRF24L01_Init();                            // 初始化处理器与NRF24L01连接的GPIO口

   

   while(NRF24L01_Check())        {                 //24L01在线检测

       printf("Didn't find NRF24L01,Please check whether NRF24L01 is online!\n\r");

                   delay_ms(2000);

    }

   printf("NRF24L01 OK\n\r");

   /*****熄灭四个led灯******/

         LED1_OFF;

         LED2_OFF;

         LED3_OFF;

         LED4_OFF;

   

         RX_Mode(kv);                                     //默认接收模式      

   // 打印本机地址

   NRF24L01_Read_Buf(RX_ADDR_P0, tmp_buf, RX_ADR_WIDTH);

   // 输出到串口

   printf("Receive address of channel 0:");

   for (i=0;i<5;i++) {

       printf("%c", tmp_buf);

    }

   printf("\n\r");

           

         while(1){                                                            

                   kv= Key_Value();                      //读取按键值

                   if(kv)          {                                             //如果有键按下,则进入到发送模式,并将键值发送出去

                            //设置发送数据

           tmp_buf[0]=0xC0;   // 指令码

           tmp_buf[1]=0x00;   // 通讯数据1

           tmp_buf[2]=0x14;   // 通讯数据2

           tmp_buf[3]=0x05;   // 通讯数据3

           tmp_buf[4]=getVerCode(tmp_buf, 0, 3);

           // 根据按键,设置目标分机地址

           addr1=TX_ADDRESS[3] + kv/10;

           addr2=TX_ADDRESS[4] + kv%10;

           

           // 转为发射模式

           TX_Mode(kv);

                            if(NRF24L01_TxPacket(tmp_buf)== TX_OK)   //如果发送成功

                            {

               printf("Sendingdata...\n\r");

                            }else {                                                                                               

   

                printf("Transfer failed.\n\r");

                       }

                            RX_Mode(kv);                                                //发送完成后进入到接收模式

                   }

      

                   //如果接收成功,则点亮相应的LED

       if(NRF24L01_RxPacket(tmp_buf) == 0)   {

           // 对方发射的目标机是本机?指令码是否正确?

           if (tmp_buf[0]==addr1 && tmp_buf[1]==addr2 &&tmp_buf[2]==0xC1) {

                // 判断校验码

                if (tmp_buf[3]!=8) {

                    printf("Wrong datalength.\n\r");

                    errorFlag=1;

                } else {

                    if(tmp_buf[8]!=getVerCode(tmp_buf, 0, 7)) {

                        printf("Errorcheck code.\n\r");

                        errorFlag=1;

                    } else {

                        // 根据对方分机的编号,点亮对应的LED

                        i=(addr1-0x30)*10 +(addr2-0x30);

                        if (i>0 && i< 5) {

                           

                            One_LED_ON(i);                        //点亮LED

                        } else {

                            printf("Wrongaddress of sub-device.\n\r");

                            errorFlag=1;

                        }                    

                    }

                }

           } else {

                errorFlag=1;

           }

           

           if (errorFlag == 1) {

                // 全亮,表示出错:可能数据错误,可能分机编号不对

                One_LED_ON(1);

                One_LED_ON(2);

                One_LED_ON(3);

                One_LED_ON(4);

                // 输出到串口

                printf("Receiveddata:\r\n");

                for(i=0;i<TX_PLOAD_WIDTH;i++) {

                    printf("%2x ",tmp_buf);

                }

                printf("\n\r");                  

           }

                   }

         }

}

主机nRF24L01模块的部分处理代码:

//启动NRF24L01发送一次数据

//txbuf:待发送数据首地址

//返回值:发送完成状况

u8 NRF24L01_TxPacket(u8 *txbuf) {

         u8state, i;   

   // 输出到串口

   printf("Sent data:");

   for (i=0;i<TX_PLOAD_WIDTH;i++) {

       printf("%2x ", txbuf);

    }

   printf("\n\r");

   

         Clr_NRF24L01_CE;

       NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF 32个字节

        Set_NRF24L01_CE;                                     //启动发送        

         while(READ_NRF24L01_IRQ!=0);                         //等待发送完成

         state=NRF24L01_Read_Reg(STATUS);                     //读取状态寄存器的值      

         NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS,state);      //清除TX_DS或MAX_RT中断标志

         if(state&MAX_TX)                                     //达到最大重发次数

         {

                   NRF24L01_Write_Reg(FLUSH_TX,0xff);               //清除TX FIFO寄存器

                   returnMAX_TX;

         }

         if(state&TX_OK)                                      //发送完成

         {

       NRF24L01_Write_Reg(FLUSH_TX,0xff);               //清除TX FIFO寄存器

                   returnTX_OK;

         }

         return0xff;                                        //其他原因发送失败

}

//启动NRF24L01接收一次数据

//rxbuf:待接收数据首地址

//返回值:0,接收完成;其他,错误代码

u8 NRF24L01_RxPacket(u8 *rxbuf) {

         u8state, i;                    

   

         state=NRF24L01_Read_Reg(STATUS);                //读取状态寄存器的值           

         NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS,state);//清除TX_DS或MAX_RT中断标志

         if(state&RX_OK)                                 //接收到数据

         {

                   NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据

                   NRF24L01_Write_Reg(FLUSH_RX,0xff);          //清除RX FIFO寄存器

        

       // 输出到串口

       printf("Received data::");

       for (i=0;i<TX_PLOAD_WIDTH;i++) {

           printf("%2x ", rxbuf);

       }

       printf("\n\r");

      

      

                   return0;

         }         

         return1;                                      //没收到任何数据

}

//该函数初始化NRF24L01到RX模式

//设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR

//当CE变高后,即进入RX模式,并可以接收数据了                 

void RX_Mode(u8 offset) {

   u8  addr[RX_ADR_WIDTH],i;

   for (i=0;i<RX_ADR_WIDTH;i++) {

       addr=RX_ADDRESS;

    }

   // 根据预设子机地址,重新设定主机无线模块地址

   addr[RX_ADR_WIDTH-2]=offset/10 + addr[RX_ADR_WIDTH-2];

   addr[RX_ADR_WIDTH-1]=offset%10 + addr[RX_ADR_WIDTH-1];

   

         Clr_NRF24L01_CE;     

   //写RX节点地址

       NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0,(u8*)addr,RX_ADR_WIDTH);

   // 自动应答

       NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA,0x01);

    //使能通道0的接收地址           

       NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01);

   

   //设置RF通信频率                    

       NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,40);

   

   //选择通道0的有效数据宽度        

       NRF24L01_Write_Reg(SPI_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);

   

   //设置TX发射参数,0db增益,2Mbps,低噪声增益开启   

       NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x0f);

   

   //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,PRIM_RX接收模式

       NRF24L01_Write_Reg(SPI_WRITE_REG+CONFIG,0x0f);

   

   //CE为高,进入接收模式

       Set_NRF24L01_CE;                                

}                           

//该函数初始化NRF24L01到TX模式

//设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,

//选择RF频道,波特率和LNA HCURR PWR_UP,CRC使能

//当CE变高后,即进入RX模式,并可以接收数据了                 

//CE为高大于10us,则启动发送.   

void TX_Mode(u8 offset) {      

   u8  addr[TX_ADR_WIDTH],i;

   // 根据预设子机地址,重新设定主机无线模块地址

   for (i=0;i<TX_ADR_WIDTH;i++) {

       addr=TX_ADDRESS;

    }

   addr[TX_ADR_WIDTH-2]=offset/10 + addr[TX_ADR_WIDTH-2];

   addr[TX_ADR_WIDTH-1]=offset%10 + addr[TX_ADR_WIDTH-1];

   

   // 输出到串口

   printf("Target sub-device address:");

   for (i=0;i<TX_ADR_WIDTH;i++) {

       printf("%2x ", addr);

    }

   printf("\n\r");

         Clr_NRF24L01_CE;      

   //写TX节点地址

       NRF24L01_Write_Buf(SPI_WRITE_REG+TX_ADDR,(u8*)addr,TX_ADR_WIDTH);

   

   //设置TX节点地址,主要为了使能ACK           

       NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0,(u8*)addr,RX_ADR_WIDTH);

   //使能通道0的自动应答   

       NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA,0x01);  

   //NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA,0x00);   

   

   //使能通道0的接收地址  

       NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01);

   

   //设置自动重发间隔时间:‘1111’-等待 4000+86us;最大自动重发次数:3次

    NRF24L01_Write_Reg(SPI_WRITE_REG+SETUP_RETR,0xf3);

   

   //设置RF通道为40

       NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,40);      

   

   //设置TX发射参数,0db增益,2Mbps,低噪声增益开启   

       NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x0f);  

   

   //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,PRIM_RX发送模式,开启所有中断

       NRF24L01_Write_Reg(SPI_WRITE_REG+CONFIG,0x0e);   

   

   // CE为高,10us后启动发送

         Set_NRF24L01_CE;                                 

}                  

分机主处理代码:

void main (void) {

   bit et0Status=ET0;

   u8 i=0, state=0;

   

   // 初始化

   system_init();

   transstr("main start...\r\n");

   

   // 初始化nRF24L01关联口线

   nRF24L01P_Init();

   

   if(NRF24L01_Check()==1)         {                 //24L01在线检测

      transstr("Didn'tfind NRF24L01,Please check whether NRF24L01 is online!\r\n");

       delay_ms(300);

    }

   transstr("slaver-NRF24L01 OK\r\n");   

   

   // 接收模式

   nRF24L01P_RX_Mode();

   

   // 主循环

   while (1) {

        // 等待nRF2*4L01的中断信号,表明收到了来自主机的指令

       if (NRF24L01_IRQ==0) {

           transstr("main_NRF24L01_interrupt:\r\n");   

           

           // 保存当前定时器0中断状态,

           et0Status=ET0;

           // 禁止Timer0的中断

           ET0=0;

           TR0=0;

           // 检查在中断类型

           state=nRF24L01P_Read_Reg(REG_STATUS);

           if(state & RX_DR) {

                // 收到来自nRF24L01的中断请求,开始接收nRF24L01的数据

                if(!nRF24L01P_RxPacket(rx_buf)){

                    // 检查数据是什么指令,并执行对应的动作

                    AnalysisCommand();   

                } else {

                    // 没有收到数据?

                    

                }               

           }

           // 达到最大重发次数

           if(state&MAX_RT) {

                // 清除TX_DS或MAX_RT中断标志

                nRF24L01P_Write_Reg(WRITE_REG +REG_STATUS, state);

                // 清除nRF24L01的发送缓冲区

               nRF24L01P_Write_Reg(FLUSH_TX,0xff);               

           }

           // 发送完成

           if(state&TX_DS)        {

                // 清除TX_DS或MAX_RT中断标志

               nRF24L01P_Write_Reg(WRITE_REG +REG_STATUS, state);        

                // 清除nRF24L01的发送缓冲区

               nRF24L01P_Write_Reg(FLUSH_TX,0xff);

           }                        

               

           if (et0Status) {

                ET0=1;

                TR0=1;

           }

       }

    }

}

到这里,简单地使用STM32和51单片机,以nRF24L01实现组网通讯的实验,就完成了。后续准备在STM32开发板上增加系统设置、液晶显示等功能,实现动态地控制子设备的控制。比如用子设备控制什么时候开关灯、开关多久灯。本套系统中,因为使用了4位拨码开关来控制分机地址,所以是可以组建1个主机、16个子机的小型无线网络,当然需要修改下主机程序,以适应16个子机的选址。

实际上,以nRF24L01组网,除了改变物理地址方式实现,还可以使用其它方式,比如开放其它通道;比如使用相同的物理地址,采用禁止应答,在通讯数据中增设逻辑地址检查。逻辑地址一致的才应答。灵活使用nRF24L01,可以很方便的制作小型家用电子网络应用。
---------------------
作者:suncat0504
链接:https://bbs.21ic.com/icview-3191322-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值