标题: NRF24L01无线模块多机通信单片机程序 上位机+下位机 [打印本页]

作者: huyun    时间: 2015-6-4 13:55
标题: NRF24L01无线模块多机通信单片机程序 上位机+下位机
最近在接了一个项目,要求各个设备能够联网(不是互联网)控制。nrf24l01刚好有这个功能。但是之前只做过一对一的通信,还是用例程的那一种。我在想,用两个地址,共用同一个通道应该也可以完成。后来他又要求发过去的数据还要能回传,这下我只好来研究多通道通信了。

多机通信和一对一通信基本上相同,就是要配置其他通道的地址和使能其他通道的有效数据宽度,还有自动应答。

这是接收机的:

​void NRF24L01_Init_RX(void)
{   
CE=0;   
CSN=1;   
SCK=0;   
SPI_Write_Buf(WRITE_REG + TX_ADDR, RX_ADDRESS, TX_ADR_WIDTH);
SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, RX_ADDRESS, RX_ADR_WIDTH);
SPI_Write_Buf(WRITE_REG + RX_ADDR_P1, RX_ADDRESS1, RX_ADR_WIDTH);
SPI_Write_Reg(WRITE_REG + EN_AA, 0x3f);
SPI_Write_Reg(WRITE_REG + EN_RXADDR, 0x3f);
SPI_Write_Reg(WRITE_REG + RF_CH, 40);
SPI_Write_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH);
SPI_Write_Reg(WRITE_REG + RX_PW_P1, RX_PLOAD_WIDTH);
SPI_Write_Reg(WRITE_REG + RF_SETUP, 0x0F);
SPI_Write_Reg(WRITE_REG + CONFIG, 0x0f);
}

​由于我用的是KeilC51和MDK5共存的,所以注释复制过来之后就成了乱码,在此贴图一张:

NRF24L01_Init_Rx(void)
​下面就到了发送机了的配置了:

void NRF24L01_Init_TX(uint8_t *ADDRn)
{   
CE=0;
CSN=1;
SCK=0;
SPI_Write_Buf(WRITE_REG + TX_ADDR, (uint8_t*)ADDRn, TX_ADR_WIDTH);
SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, (uint8_t*)ADDRn, RX_ADR_WIDTH);
SPI_Write_Reg(WRITE_REG + EN_AA, 0x3f);
SPI_Write_Reg(WRITE_REG + EN_RXADDR, 0x3f);
SPI_Write_Reg(WRITE_REG + RF_CH, 40);
SPI_Write_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH);
SPI_Write_Reg(WRITE_REG + RX_PW_P1, RX_PLOAD_WIDTH);
SPI_Write_Reg(WRITE_REG + SETUP_RETR, 0xff);
SPI_Write_Reg(WRITE_REG + RF_SETUP, 0x0f);
SPI_Write_Reg(WRITE_REG + CONFIG, 0x0E);
}

同样:


NRF24L01_Init_TX(uint8_t *ADDRn)
​发送给其他通道,只要改成其他通道的地址就可以了,不用改通道号,现在还不知道为什么。

接下来就是发送函数:

uint8_t nRF24L01_TxPacket(unsigned char * tx_buf)
{
uint8_t Return_Flag=0;
CE=0;
SPI_Write_Buf(WR_TX_PLOAD, tx_buf, TX_PLOAD_WIDTH);
Delayms(2);
Return_Flag = Check_ACK(1);
return Return_Flag;
}

​由于我是通过电脑串口控制上位机,发给某一个下位机,然后再发回到我的上位机中,最后回传到电脑,所以没有数据抢答的问题。

最后再说一句,要配置第N个​的通道,前N个通道也必须被配置,要不然配置不成功。

最后贴上上位机程序:
  1. #include <stc12c5a.H>
  2. #include <intrins.h>
  3. #include "nrf24l01.h"
  4. #include "delay.h"
  5. #define uint32_t unsigned int
  6. #define uint8_t unsigned char

  7. uint8_t const RX_ADDRESS[TX_ADR_WIDTH]= {0x01,0x13,0x5C,0x0C,0x03};        //本地地址
  8. uint8_t const RX_ADDRESS1[TX_ADR_WIDTH]={0x02,0x13,0x5C,0x0C,0x03}; //本地地址1


  9. extern uint8_t Rx_Buf[32];
  10. extern uint8_t Tx_Buf[32];

  11. /**********************************RNF24L01状态标志位************************************************/
  12. uint8_t         bdata sta;   
  13. sbit        RX_DR        =sta^6;
  14. sbit        TX_DS        =sta^5;
  15. sbit        MAX_RT        =sta^4;

  16. /**********************************NRF24L01管脚与单片机接口定义**************************************/


  17. sbit  MISO =P3^4;
  18. sbit  MOSI =P3^6;
  19. sbit SCK =P3^3;
  20. sbit  CE =P3^2;
  21. sbit CSN =P3^5;
  22. sbit IRQ =P3^7;



  23. /***************************************************************************
  24. 函数名称:uchar SPI_RW(uint8_t dat)
  25. 函数功能:NRF24L01的SPI写时序
  26. 函数备注:
  27. ***************************************************************************/
  28. uint8_t SPI_RW(uint8_t dat)
  29. {
  30.         uint8_t i;
  31.            for(i=0;i<8;i++)
  32.            {
  33.                 MOSI = (dat & 0x80);         
  34.                 dat = (dat << 1);           
  35.                 SCK = 1;                     
  36.                 dat |= MISO;                         
  37.                 SCK = 0;                              
  38.            }
  39.     return(dat);                             
  40. }

  41. /***************************************************************************
  42. 函数名称:uint8_t SPI_Read(uint8_t cmd_reg)
  43. 函数功能:NRF24L01的SPI读时序
  44. 函数备注:
  45. ***************************************************************************/
  46. uint8_t SPI_Read(uint8_t cmd_reg)
  47. {
  48.         uint8_t value;
  49.        
  50.         CSN = 0;               
  51.         SPI_RW(cmd_reg);            
  52.         value = SPI_RW(0);   
  53.         CSN = 1;               
  54.        
  55.         return(value);        
  56. }

  57. /***************************************************************************
  58. 函数名称:void SPI_Write_Reg(uint8_t cmd_reg, uint8_t value)
  59. 函数功能:写入NRF24L01寄寄存器和从NRF24L01寄存器中读出
  60. 函数备注:
  61. ***************************************************************************/
  62. void SPI_Write_Reg(uint8_t cmd_reg, uint8_t value)
  63. {       
  64.         CSN = 0;                  
  65.         SPI_RW(cmd_reg);     
  66.         SPI_RW(value);            
  67.         CSN = 1;                  
  68. }

  69. /***************************************************************************
  70. 函数名称:uint8_t SPI_Read_Buf(uint8_t cmd_reg, uint8_t *pBuf, uint8_t num)
  71. 函数功能:从NRF24L01寄存器中读出数据
  72. 函数备注:reg:为寄存器地址,pBuf:为待读出数据地址,uint8_t:读出数据的个数
  73. ***************************************************************************/
  74. uint8_t SPI_Read_Buf(uint8_t cmd_reg, uint8_t *pBuf, uint8_t num)
  75. {
  76.         uint8_t status,i;
  77.        
  78.         CSN = 0;                                   
  79.         status = SPI_RW(cmd_reg);                      
  80.         for(i=0;i<num;i++)   pBuf[i] = SPI_RW(0);           
  81.         CSN = 1;                           
  82.        
  83.         return(status);                    
  84. }

  85. /***************************************************************************
  86. 函数名称:void SPI_Write_Buf(uint8_t cmd_reg, uint8_t *pBuf, uint8_t num)
  87. 函数功能:在NRF24L01寄存器中写入数据
  88. 函数备注:reg:为寄存器地址,pBuf:为待写入数据地址,uint8_t:写入数据的个数
  89. ***************************************************************************/
  90. void SPI_Write_Buf(uint8_t cmd_reg, uint8_t *pBuf, uint8_t num)
  91. {
  92.         uint8_t i;       
  93.         CSN = 0;            //SPI使能      
  94.         SPI_RW(cmd_reg);   
  95.         for(i=0; i<num; i++)   SPI_RW(*pBuf++);
  96.         CSN = 1;           //关闭SPI
  97. }

  98. /***************************************************************************
  99. 函数名称:uint8_t nRF24L01_RxPacket(uint8_t* rx_buf)
  100. 函数功能:数据读取后放入rx_buf接收缓冲区中
  101. 函数备注:
  102. ***************************************************************************/
  103. uint8_t nRF24L01_RxPacket(uint8_t* rx_buf)
  104. {
  105.    uint8_t flag;
  106.         CE = 1;                          //很重要!启动接收!
  107.         Delayms(1);
  108.         sta=SPI_Read(READ_REG+STATUS);        // 读取状态寄存其来判断数据接收状况           //寄存器前面要加是读还是写
  109.         if(RX_DR)                                // 判断是否接收到数据  如果置1则说明接到数据并且放置在接收缓存器
  110.         {
  111.            CE = 0;                
  112.                 SPI_Read_Buf(RD_RX_PLOAD,rx_buf,RX_PLOAD_WIDTH); //这本来就是指令,不用加,意思说去缓存器里读
  113.                 flag =1;                        //读取数据完成标志
  114.         }
  115.         SPI_Write_Reg(WRITE_REG+STATUS,sta);   //接收到数据后RX_DR,TX_DS,MAX_PT都置高为1,通过写1来清楚中断标志
  116.         return flag;
  117. }




  118. /**************************************************
  119. 函数:Check_ACK(bit clear)
  120. 描述:检查接收设备有无接收到数据包,设定没有收到应答信
  121.             号是否重发
  122. /**************************************************/
  123. uint8_t Check_ACK(bit clear)
  124. {
  125.         while(IRQ);
  126.         sta = SPI_RW(NOP);                    // 返回状态寄存器
  127.         if(MAX_RT)
  128.                 if(clear)                         // 是否清除TX FIFO,没有清除在复位MAX_RT中断标志后重发
  129.                         SPI_RW(FLUSH_TX);   //清空寄存器,很重要!!!
  130.         SPI_Write_Reg(WRITE_REG + STATUS, sta);  // 清除TX_DS或MAX_RT中断标志
  131.         IRQ = 1;
  132.         if(TX_DS)
  133.                 return(0x01);
  134.         else
  135.                 return(0x00);
  136. }


  137. /***************************************************************************
  138. 函数名称:void nRF24L01_TxPacket(unsigned char * tx_buf)
  139. 函数功能:发送 tx_buf中数据
  140. 函数备注:
  141. ***************************************************************************/
  142. uint8_t nRF24L01_TxPacket(unsigned char * tx_buf)
  143. {
  144.         uint8_t Return_Flag=0;
  145.         CE=0;                        //StandBy I模式       
  146.         SPI_Write_Buf(WR_TX_PLOAD, tx_buf, TX_PLOAD_WIDTH);                          // 装载数据  WR_TX_PLOAD本来就是指令  不用加   将数据放入发送缓存器       
  147.         CE=1;                 //置高CE,激发数据发送
  148.         Delayms(2);
  149.         Return_Flag = Check_ACK(1);
  150.         return Return_Flag;
  151. }


  152. /***************************************************************************
  153. 函数名称:void NRF24L01_Init_TX(void)
  154. 函数功能:初始化NRF24L01
  155. 函数备注:在调用NRF24L01作为发射时,都要先调用该函数对NRF24L01进行初始化
  156. ***************************************************************************/
  157. void NRF24L01_Init_TX(uint8_t *ADDRn)
  158. {   
  159.         /*待机模式*/
  160.         CE=0;
  161.         CSN=1;
  162.         SCK=0;
  163.         /*装载数据*/
  164.         SPI_Write_Buf(WRITE_REG + TX_ADDR, (uint8_t*)ADDRn, TX_ADR_WIDTH);//写TX节点地址
  165.         SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, (uint8_t*)ADDRn, RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK          
  166.         /*配置寄存器*/
  167.         SPI_Write_Reg(WRITE_REG + EN_AA, 0x3f); //使能通道所有的自动应答   
  168.         SPI_Write_Reg(WRITE_REG + EN_RXADDR, 0x3f);//使能通道所有的接收地址  
  169.         SPI_Write_Reg(WRITE_REG + RF_CH, 40);//设置RF通道为40   收发必须一致!
  170.         SPI_Write_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH); //选择通道0的有效数据宽度           
  171.         SPI_Write_Reg(WRITE_REG + RX_PW_P1, RX_PLOAD_WIDTH); //选择通道1的有效数据宽度           
  172.         SPI_Write_Reg(WRITE_REG + SETUP_RETR, 0xff);//自动重发  4000+86us    重发15次
  173.         SPI_Write_Reg(WRITE_REG + RF_SETUP, 0x0f); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启  收发必须一致!                 
  174.         SPI_Write_Reg(WRITE_REG + CONFIG, 0x0E);  //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断                         
  175. }


  176. /***************************************************************************
  177. 函数名称:void NRF24L01_Init_RX(void)
  178. 函数功能:初始化NRF24L01
  179. 函数备注:在调用NRF24L01作为接收时,都要先调用该函数对NRF24L01进行初始化
  180. ***************************************************************************/
  181. void NRF24L01_Init_RX(void)
  182. {   
  183.         /*待机模式*/
  184.         CE=0;   
  185.         CSN=1;   
  186.         SCK=0;   
  187.         /*装载数据*/
  188.         SPI_Write_Buf(WRITE_REG + TX_ADDR, RX_ADDRESS, TX_ADR_WIDTH);//写TX节点地址            
  189.         SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, RX_ADDRESS, RX_ADR_WIDTH);  //设置TX节点地址,主要为了使能ACK
  190.         SPI_Write_Buf(WRITE_REG + RX_ADDR_P1, RX_ADDRESS1, RX_ADR_WIDTH);  //设置TX节点地址,主要为了使能ACK
  191.         /*配置寄存器*/
  192.         SPI_Write_Reg(WRITE_REG + EN_AA, 0x3f);//使能通道0的自动应答
  193.         SPI_Write_Reg(WRITE_REG + EN_RXADDR, 0x3f); //使能通道0的接收地址   
  194.         SPI_Write_Reg(WRITE_REG + RF_CH, 40); //设置RF通道为40   收发必须一致!
  195.         SPI_Write_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH); //选择通道0的有效数据宽度
  196.         SPI_Write_Reg(WRITE_REG + RX_PW_P1, RX_PLOAD_WIDTH); //选择通道1的有效数据宽度
  197.         SPI_Write_Reg(WRITE_REG + RF_SETUP, 0x0F); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启  收发必须一致!
  198.         SPI_Write_Reg(WRITE_REG + CONFIG, 0x0f); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断
  199. }
复制代码




1发2发1.zip

309.64 KB, 下载次数: 553, 下载积分: 黑币 -5


作者: 南天白云    时间: 2015-6-4 18:38
谢谢!资料下载,我不知道与别人的有什么不一样?
作者: 上帝武装    时间: 2015-6-18 11:28
感谢楼主,我最近也在这个无线模块,我的数据刷新有问题,参考下楼主的
作者: 上帝武装    时间: 2015-6-18 11:28
不知道楼主这个数据刷新是如何处理的
作者: zajzaj111    时间: 2015-7-3 16:07
很好的东西
作者: flybaby    时间: 2015-9-6 13:59
非常感谢楼主无私奉献
作者: ludaijin    时间: 2015-10-19 13:16
太强了,顶起
作者: jiakuo25    时间: 2015-10-19 16:58
本帖最后由 jiakuo25 于 2015-10-19 17:00 编辑

顶一下


作者: 腾飞的龙    时间: 2015-10-19 23:21
上位机的源码不全啊、
作者: jie100    时间: 2016-1-1 00:10
非常感谢
作者: 风中有你    时间: 2016-1-15 18:28
还是不太懂啊,比如一对十,各个接收单元如何知道主机是对它发数据?
作者: xuanyifeng    时间: 2016-4-6 14:23
如何可以获得黑币呀,新手不懂
作者: xuanyifeng    时间: 2016-4-6 17:35
xuanyifeng 发表于 2016-4-6 14:23
如何可以获得黑币呀,新手不懂

谢谢你,太给力了,能先奖励点么,嘿嘿嘿
作者: xuanyifeng    时间: 2016-4-6 20:42
好的,十分感谢
作者: xuanyifeng    时间: 2016-4-6 20:49
xuanyifeng 发表于 2016-4-6 17:35
谢谢你,太给力了,能先奖励点么,嘿嘿嘿

别人下载资源,自己可以奖励黑币么?如何设置呢
作者: admin    时间: 2016-4-6 21:17
xuanyifeng 发表于 2016-4-6 20:49
别人下载资源,自己可以奖励黑币么?如何设置呢

设置出售附件即可,不过要工程师级别才可以。
作者: 文家燕    时间: 2016-4-12 22:30
hao  zi liao
作者: cheff    时间: 2016-10-10 10:29
不错的学习资料,谢谢楼主
作者: 滴~电子小白    时间: 2016-10-19 14:25
感谢楼主的无私分享
作者: sywangjunqing    时间: 2017-3-21 11:58
正想研究一下 谢谢!
作者: ~IOT夙愿    时间: 2017-4-8 22:15
感谢楼主分享
作者: MRluo    时间: 2017-4-24 21:34
感谢楼主
作者: 流行双杰    时间: 2017-4-26 17:49
谢谢分享,正在学习
作者: 仰望星空753521    时间: 2017-4-26 19:25
主要协议得写对,还得稳定,不卡死
作者: 13118302007    时间: 2017-4-27 09:52
1-1,1-2都简单,有没有1-3到1-6的我参考一下
作者: 独孤侠    时间: 2017-10-1 21:36

不错的学习资料,谢谢楼主
作者: yeyan16900    时间: 2017-10-9 14:22
不错谢谢分享
作者: xiong731    时间: 2017-11-28 21:08
感谢楼主的无私分享
作者: luokuipeng    时间: 2017-12-19 14:59

感谢楼主的无私分享
作者: WANG1995    时间: 2018-3-8 00:25
还是有用的
作者: bhjyqjs    时间: 2018-3-11 16:58
感谢楼主的无私奉献
作者: Demon丶    时间: 2018-6-5 13:18
谢谢楼主
作者: HPC005    时间: 2018-6-5 15:33

谢谢分享! 51黑有你更精彩!!!
作者: guolh    时间: 2018-7-20 16:04
支持!!!!!!!!!!!!!
作者: redtxd    时间: 2018-8-15 07:04
谢谢楼主分享,正在学习该项目。
作者: redtxd    时间: 2018-8-27 16:56
谢谢老师分享,我也在做电脑发送地址的方式与多个nrf24l01终端通信,但是一直没有成功。
作者: mm1113512    时间: 2019-1-19 20:42
谢谢分享
作者: _晨昏线    时间: 2019-3-17 19:25
楼主,想知道NRF24L01多对一的的发送,对于接受端是如何判断是由哪个发送端发送的数据,又要如何处理?
作者: odiwuano    时间: 2019-9-7 13:43
我也下载来膜拜一下
作者: chunzz    时间: 2019-9-8 02:27
也来看看
作者: jnshuhe    时间: 2019-9-8 07:37
非常感谢,非常好
作者: frank123098    时间: 2019-10-9 10:08
请问楼主有一对一的资料,借鉴参考一下吗?最近在学习这个模块
作者: big_zhuang    时间: 2019-11-24 22:29
谢谢!资料下载,我不知道与别人的有什么不一样?
作者: 莫诗书    时间: 2020-5-3 10:12
多谢楼主了
作者: raindrop1976    时间: 2020-5-24 11:47
学习了,谢谢说明
作者: 12334345    时间: 2021-1-30 10:55
这是一对多吗
作者: bugall    时间: 2021-1-30 21:58
有没有实测距离啊,这个模块到底能穿多元
作者: rs2020    时间: 2022-1-20 14:03

也有一个WS2812 时钟程序,60灯驱动. 参考学习一下.
作者: 小new    时间: 2023-5-10 11:00
我在想。这个上位机地址要配置,是通过窗口调试工具吗?就是AT+RAX=那个,但是它只能改一个本地地址和发送地址,这设置成有两个本地地址,这怎么操作的呀





欢迎光临 (http://www.51hei.com/bbs/) Powered by Discuz! X3.1