切换到宽版
  • 5840阅读
  • 7回复

[STM]STM8学习之——nRF24L01 [复制链接]

上一主题 下一主题
离线zeze10000
 

发帖
2858
M币
8559
专家
35
粉丝
112
只看楼主 倒序阅读 我要置顶 楼主  发表于: 2018-09-09
nRF24L01应该算得上应用比较广泛的2.4G无线通讯模块了。
SPI的接口,能达到比较高的通讯速率,模块体积小,待机功耗也不错,一般的单片机教程都会教。
网上的资料也多,方便学习。
这个模块我老早就买了,只是一直没时间玩。
这两天抽点时间学了一下。
无线这东西,看不见摸不着,出问题了也不知道是接收有问题还是发送有问题。

先说说这个模块吧
2.4G无线通讯,可设置频段,有一个发送通道,6个接收通道。
6个接收通道的地址可以设置为3字节、4字节、5字节。
0通道可以独立设置,2~5通道的地址高位字节要与1通道相同,只有最低字节能独立设置。
由于有6个接收通道,所以一次设置最多可以接收6个发送主机的信息。
当然可以分时接收,那就不止6个了。

两个模块要建立通信的条件是1、发射频段相同,2、发送、接收地址相同,3、通信速率、数据包格式相同
对于发射主机,如果要接收应答信号,其接收通道0地址要和发送地址相同。
数据包格式主要指数据包长度,可以设置动态长度。

话不多说,我们看图说话

这是nRF24L01的状态机
有掉电模式、待机I和待机II、工作模式
有些寄存器在掉点或者待机模式下才能修改,所以一般先初始化配置
图上写的很清楚,这里就不多解释了。

几种状态的解释如上图

要和模块通讯,首先要看的就是通讯时序


上面是读写时序
我们能得到的信息就是空闲时钟状态和相位
正确设置SPI才能正常通讯

nRF24L01的数据包格式

引导码,表示数据传送开始
地址码,接收机和自己的地址比较
控制字符,携带数据包长度信息,是否重发包等等
数据字节,需要传送的主体
CRC校验码

一般的流程是主机发送一段数据,
接收机读取,当检测到和自己地址相符的数据包则开始读入,
当数据包校验通过则向主机发送应答信号
主机接收到应答则传输结束。
主机未接收到应答则等待一段时间后重发,
重发次数超过设定后停止发送。
从机也会检测数据包是否为新包,如果是新包则产生接收中断,让单片机读取。

这个模块的好处就是发送和接收自动完成,数据的打包、校验、重传都不需要单片机干预。
单片机只需要启动发送,和收到数据时启动接收就可以了。

发送流程


接收流程


多机通信

这里主要是看各个模块的地址设置,正确设置才能通信成功

关于nRF24L01的具体设置我不想讲太多,网上资料很多,讲的也比我清楚,
我只讲我遇到的问题

我的实验是主机发送数据,从机接收数据,并在应答包中返回数据。
一开始总是接收不到应答包,从机数据接收正常。
后面检查发现数据是接收到了,只是我在主机发送完成后清状态位时把接收完成的状态给清掉了
后面改成只清发送完成就正常了。


后来发现了一个更诡异的问题
接受的字符串乱码
我一般喜欢用在线仿真代替串口打印
因为不用设置,直接就能读取到数据
单独检查字符串,发送前和发送后读取出来都是正常的,从机接收也是正常
但是主机接收的应答包里面的数据是乱码的
从机发送前后读取的数据也是正常的
检查程序,各种修改也不行。
有时候能正常接收,有时候死活不行。

打印到串口也是乱码
郁闷的不行
甚至读取nRF24L01的配置寄存器都是错误的!

然而,停止仿真后,单片机自动复位运行

居然是正常的
what?!
再仿真还是乱码
停止仿真,正常!
我的天,搞了那么久查原因。。。
有时改几句代码就又正常,正常后将代码原封不动改回来也是正常!
结果浪费了很多时间,找不到原因。。。

不管了,总之是没找到具体原因

接收端一直是正常的,不过接收和发送端单片机不同

除了硬件仿真有问题,其它的都正常了,就这样吧!

最后说一个prinf的问题。
开始时使用的全功能的printf
结果占用了差不多7K的空间。
换成tiny的printf,几百个字节,但是不能打印变量。
最后设定成自动选择,大概1k多大小,显示也正常,就这样吧!


贴上nRF24L01的相关代码
供大家参考、指正!

nRF24L01.h
  1. #ifndef __24L01_H__
  2. #define __24L01_H__
  3. #include<iostm8s105k4.h>
  4. #include<spi.h>
  5. #include<delay.h>
  6. #define CSN PC_ODR_ODR4         //宏定义
  7. #define CE  PC_ODR_ODR3
  8. #define IRQ PB_IDR_IDR4
  9. #define TX_MODE         //模式定义,注释掉此行则为接收模式
  10. extern unsigned char const ADDR0[3];    //地址数组
  11. extern unsigned char const ADDR1[3];
  12. extern unsigned char const ADDR2[3];
  13. extern unsigned char const ADDR3[3];
  14. extern unsigned char const ADDR4[3];
  15. extern unsigned char const ADDR5[3];
  16. /*寄存器操作命令*/
  17. #define R_REGISTER              0x00    // 读配置寄存器, 低 5 位为寄存器地址
  18. #define W_REGISTER              0x20    // 写配置寄存器, 低 5 位为寄存器地址(待机和停机模式有效)
  19. #define R_RX_PAYLOAD            0x61    // 读取 Rx 有效数据, 1-32 字节
  20. #define W_TX_PAYLOAD            0xa0    // 写 Tx 有效数据, 1-32 字节
  21. #define FLUSH_TX                0xe1    // 清除 Tx FIFO 寄存器,发射模式下用
  22. #define FLUSH_RX                0xe2    // 清除 Rx FIFO 寄存器,接收模式下用
  23. #define REUSE_TX_PL             0xe3    // 重新使用上一包数据
  24. #define ACTIVATE                0x50    // ACTIVATE加0x73激活或停用下方三个寄存器(待机或停机模式下更改)
  25. #define R_RX_PL_WID             0x60    // 读取顶层RX FIFO 数据长度
  26. #define W_ACK_PAYLOAD           0xa8    // 接收模式下,写应答数据包附加数据
  27. #define W_TX_PAYLOAD_NOACK      0xb0    // 发送模式下,停用自动应答
  28. #define NOP                     0xff    // 空指令
  29. /*寄存器地址*/
  30. #define CONFIG          0x00    // 配置寄存器 bit0(PRIM_RX):0 发送,1接收; bit(PWR_UP):0 power down,1 power on;
  31.                                  // bit2(CRCO):0 1byte,1 2bytes; bit3(EN_CRC):0 disable,1 enable;
  32.                                  // bit4(MASK_MAX_RT):0 使能MAX_RT中断,1 屏蔽MAX_RT中断;
  33.                                  // bit5(MASK_TX_DS):0 使能TX_DS中断,1 屏蔽TX_DS中断; bit6(MASK_RX_DS):0 使能RX_DS中断,1 屏蔽RX_DS中断;
  34. #define EN_AA           0x01    // 使能自动应答功能 Bit0 - 5, 对应通道 0 - 5
  35. #define EN_RXADDR       0x02    // 接收地址允许, Bit0 - 5, 对应通道 0 - 5
  36. #define SETUP_AW        0x03    // 设置地址宽度 ( 所有数据通道 ) : Bit0-1: 00 3字节   01 4字节  10 5字节
  37. #define SETUP_RETR      0x04    // 建立自动重发, Bit0-3: 自动重发计数, Bit4-7: 自动重发延时 250 * x + 86us
  38. #define RF_CH           0x05    // RF 通道, Bit0-6: 工作通道频率
  39. #define RF_SETUP        0x06    // RF 寄存器, Bit3: 传输速度 (0: 1 Mbps, 1: 2 Mbps), Bit1-2: 发射功率, Bit0: 低噪声发大器增益
  40. #define STATUS          0x07    // 状态寄存器, Bit0: RX FIFO 满标志, Bit1-3: 接收数据通道号 (最大: 6 ), Bit4: 达到最多次重发, Bit5: 数据发送完成中断, Bit6: 接收数据中断
  41. #define OBSERVE_TX      0x08    // 发送检测寄存器, Bit4-7: 数据包丢失计数器, Bit0-3: 重发计数器
  42. #define CD              0x09    // 载波检测, Bit0: 载波检测
  43. #define RX_ADDR_P0      0x0a    // 接收通道0地址, 最大长度 5 个字节, 低字节在前
  44. #define RX_ADDR_P1      0x0b    // 接收通道1地址, 最大长度 5 个字节, 低字节在前
  45. #define RX_ADDR_P2      0x0c    // 接收通道2地址,最低字节可设置,其它同RX_ADDR_P1
  46. #define RX_ADDR_P3      0x0d    // 接收通道3地址,最低字节可设置,其它同RX_ADDR_P1
  47. #define RX_ADDR_P4      0x0e    // 接收通道4地址,最低字节可设置,其它同RX_ADDR_P1
  48. #define RX_ADDR_P5      0x0f    // 接收通道5地址,最低字节可设置,其它同RX_ADDR_P1
  49. #define TX_ADDR         0x10    // 发送地址(低字节在前), ShockBurstTM 模式下, RX_ADDR_P0 与地址相等
  50. #define RX_PW_P0        0x11    // 接收数据通道 0 有效数据宽度 (1-32字节), 设置为 0 则非法
  51. #define RX_PW_P1        0x12    // 接收数据通道 1 有效数据宽度 (1-32字节), 设置为 0 则非法
  52. #define RX_PW_P2        0x13    // 接收数据通道 2 有效数据宽度 (1-32字节), 设置为 0 则非法
  53. #define RX_PW_P3        0x14    // 接收数据通道 3 有效数据宽度 (1-32字节), 设置为 0 则非法
  54. #define RX_PW_P4        0x15    // 接收数据通道 4 有效数据宽度 (1-32字节), 设置为 0 则非法
  55. #define RX_PW_P5        0x16    // 接收数据通道 5 有效数据宽度 (1-32字节), 设置为 0 则非法
  56. #define FIFO_STATUS     0x17    // FIFO 状态寄存器, Bit0: RX FIFO 寄存器空标志, Bit1 RX FIFO 满标志, Bit2-3 保留
  57.                                  // Bit4 TX FIFO 空标志, Bit5 TX FIFO 满标志, Bit6 1 循环发送上一次数据包, 0 不循环
  58. #define DYNPD           0x1C    // 启用动态数据长度, Bit6-7: NULL, Bit5: 启用动态数据长度管道 5...
  59. #define FEATRUE         0x1D    // 功能寄存器, Bit3-7: 无用,总是为 0, Bit2: 启用动态数据长度, Bit1: 启用 ACK, Bit0: 启用 W_TX_PAYLOAD_NOACK 命令
  60. void W_CMD(unsigned char CMD);
  61. unsigned char R_REG(unsigned char reg);
  62. void W_REG(unsigned char reg,unsigned char dat);
  63. void R_buf(unsigned char reg,unsigned char *pBuf,unsigned char len);
  64. void W_buf(unsigned char reg,unsigned char *pBuf,unsigned char len);
  65. void RX_mode();
  66. void TX_mode();
  67. unsigned char nRF24L01_send(unsigned char const *addr,unsigned char *pBuf);
  68. void nRF24L01_send_noack(unsigned char const *addr,unsigned char *pBuf);
  69. void nRF24L01_ack_sendback(unsigned char pipe,unsigned char *pBuf);
  70. void nRF24L01_recive(unsigned char *pBuf);
  71. void nRF24L01_init();
  72. #endif


nRF24L01.c
  1. #include<24l01.h>
  2. #include<string.h>
  3. unsigned char const ADDR0[3]={0xa0,0xa0,0xa0};  
  4. unsigned char const ADDR1[3]={0xa1,0xaa,0xaa};
  5. unsigned char const ADDR2[3]={0xa2,0xaa,0xaa};
  6. unsigned char const ADDR3[3]={0xa3,0xaa,0xaa};
  7. unsigned char const ADDR4[3]={0xa4,0xaa,0xaa};
  8. unsigned char const ADDR5[3]={0xa5,0xaa,0xaa};
  9. /*
  10. **写单字节命令
  11. **如:FLUSH_RX,FLUSH_TX
  12. **CMD:命令        
  13. */
  14. void W_CMD(unsigned char CMD)
  15. {
  16.   CSN=0;
  17.   SPI_RW(CMD);
  18.   CSN=1;
  19. }
  20. /*
  21. **读寄存器值
  22. **如:config,FIFO_status
  23. **reg:寄存器名
  24. **返回寄存器值
  25. */
  26. unsigned char R_REG(unsigned char reg)
  27. {
  28.   unsigned char dat;
  29.   
  30.   CSN=0;
  31.   SPI_RW(reg);
  32.   dat=SPI_RW(NOP);
  33.   CSN=1;
  34.   
  35.   return dat;
  36. }
  37. /*
  38. **写寄存器值
  39. **如:config
  40. **reg:寄存器名;dat:寄存器值
  41. */
  42. void W_REG(unsigned char reg,unsigned char dat)
  43. {
  44.   CSN=0;
  45.   SPI_RW(W_REGISTER|reg);
  46.   SPI_RW(dat);
  47.   CSN=1;
  48. }
  49. /*
  50. **读buf
  51. **如:读RD_RX_PLOAD
  52. **reg:寄存器名,*pBuf:接收数组,len:读取长度
  53. */
  54. void R_buf(unsigned char reg,unsigned char *pBuf,unsigned char len)
  55. {
  56.   CSN=0;
  57.   SPI_RW(reg);
  58.   if(len>32) len=32;
  59.   while(len--)
  60.     *pBuf++=SPI_RW(NOP);
  61.   
  62.   CSN=1;
  63. }
  64. /*
  65. **写buf
  66. **如:写TX_ADDR、RX_ADDR_P0
  67. **reg:寄存器名,*pBuf:写入的数据,len:写入的长度
  68. */
  69. void W_buf(unsigned char reg,unsigned char *pBuf,unsigned char len)
  70. {  
  71.   CSN=0;
  72.   SPI_RW(reg);
  73.   if(len>32) len=32;
  74.   while(len--)
  75.     SPI_RW(*pBuf++);
  76.   
  77.   CSN=1;
  78. }
  79. /*
  80. **接收模式初始化
  81. */
  82. void RX_mode()
  83. {
  84.   CE=0;
  85.   W_REG(SETUP_AW,0x01);        //addr width 3bytes
  86.   W_REG(RF_CH,0x40);           //RF CH
  87.   W_REG(RF_SETUP,0x07);        //1M,0DB,LNA
  88.   W_REG(DYNPD,0x3f);           //enable pipe0~pipd5 dyn
  89.   W_REG(FEATRUE,0x07);         //enable dyn,enable payload with ACK,enable NACK payload
  90.   W_buf(RX_ADDR_P0,(unsigned char  *)ADDR0,3);    //pipe0 addr
  91.   W_buf(RX_ADDR_P1,(unsigned char  *)ADDR1,3);    //pipe1 addr
  92.   W_buf(RX_ADDR_P2,(unsigned char  *)ADDR2,3);    //pipe2 addr
  93.   W_buf(RX_ADDR_P3,(unsigned char  *)ADDR3,3);    //pipe3 addr
  94.   W_buf(RX_ADDR_P4,(unsigned char  *)ADDR4,3);    //pipe4 addr
  95.   W_buf(RX_ADDR_P5,(unsigned char  *)ADDR5,3);    //pipe5 addr
  96.   W_REG(EN_AA,0x3f);           //enable ACK
  97.   W_REG(EN_RXADDR,0x3f);       //enable pipe0~5
  98.   W_CMD(FLUSH_TX);             //flush TX fifo
  99.   W_CMD(FLUSH_RX);             //flush RX fifo
  100.   W_REG(STATUS,0xf0);          //clear status
  101.   W_REG(CONFIG,0x3b);          //PRX mode,power up,1byte CRC,enable CRC,enable RX_DS interrupt
  102.   CE=1;                        //enable RX mode
  103.   
  104. }
  105. /*
  106. **发送模式初始化
  107. */
  108. void TX_mode()
  109. {
  110.   CE=0;
  111.   W_REG(SETUP_AW,0x01);        //addr width 3bytes
  112.   W_REG(SETUP_RETR,0x2f);      //waite 750us,retransmit 15times
  113.   W_REG(RF_CH,0x40);           //RF CH
  114.   W_REG(RF_SETUP,0x07);        //1M,0DB,LNA
  115.   W_REG(DYNPD,0x01);           //enable pipe0 dyn
  116.   W_REG(FEATRUE,0x07);         //enable dyn,enable ACK,enable NACK payload
  117.   W_buf(RX_ADDR_P0,(unsigned char  *)ADDR0,3);    //pipe0 addr
  118.   W_buf(TX_ADDR,(unsigned char  *)ADDR0,3);       //TX addr
  119.   W_REG(EN_AA,0x01);           //enable ACK
  120.   W_REG(EN_RXADDR,0x01);       //enable pipe0
  121.   W_CMD(FLUSH_TX);             //flush TX fifo
  122.   W_CMD(FLUSH_RX);             //flush RX fifo
  123.   W_REG(STATUS,0xf0);          //clear status
  124.   W_REG(CONFIG,0x3a);          //PTX mode,power up,1byte CRC,enable CRC,enable,enable RX_DS interrupt
  125. }
  126. /*
  127. **发送数据
  128. **向接收机发送数据,数据长度在32字节以内
  129. ** *addr:接收机地址,*pBuf:发送的数据
  130. **发送成功返回1,发送超时返回0
  131. */
  132. unsigned char nRF24L01_send(unsigned char const *addr,unsigned char *pBuf)
  133. {
  134.   W_buf(RX_ADDR_P0,(unsigned char  *)addr,3);    //设置通道0地址,与发送地址相同,用来接收应答信息
  135.   W_buf(TX_ADDR,(unsigned char  *)addr,3);       //发送地址,与接收机的接收通道地址相同
  136.   
  137.   W_CMD(FLUSH_TX);                                //清理发送缓存
  138.   W_buf(W_TX_PAYLOAD,pBuf,strlen((char const *)pBuf));  //发送数据写入缓存
  139.   
  140.   CE=1;
  141.   delay10us(2);         //触发发送,时间大于10us
  142.   CE=0;
  143.   
  144.   while(!(R_REG(STATUS)&0x20))  //判断发送完成
  145.   {
  146.     if(R_REG(STATUS)&0x10)      //发送超时
  147.     {
  148.       W_REG(STATUS,0x10);       //清超时标志
  149.       return 0;                 //超时退出
  150.     }
  151.   }
  152.   W_REG(STATUS,0x20);           //清发送完成标志
  153.   return 1;                     //发送完成退出
  154. }
  155. /*
  156. **发送无需应答的数据包
  157. ***addr:发送地址,*pBuf:发送的数据
  158. */
  159. void nRF24L01_send_noack(unsigned char const *addr,unsigned char *pBuf)
  160. {
  161.   W_buf(TX_ADDR,(unsigned char  *)addr,3);       //设置发送地址,与接收机接收通道地址相同
  162.   
  163.   W_CMD(FLUSH_TX);                                 //清发送缓存
  164.   W_buf(W_TX_PAYLOAD_NOACK,pBuf,strlen((char const *)pBuf));    //写发送数据
  165.   CE=1;
  166.   delay10us(2);         //激活发送
  167.   CE=0;
  168. }
  169. /*
  170. **写应答数据包,接收机应答时发送给主机的数据,需在接收前写入
  171. **pipe:接收机接收通道,*pBuf:需要发送的数据
  172. */
  173. void nRF24L01_ack_sendback(unsigned char pipe,unsigned char *pBuf)
  174. {
  175.   if(pipe<6)
  176.   {
  177.     W_CMD(FLUSH_TX);            //清发送寄存器
  178.     W_buf(W_ACK_PAYLOAD|pipe,pBuf,strlen((char const *)pBuf));  //写如数据
  179.   }
  180. }
  181. /*
  182. **读取接收数据
  183. ***pBuf:数据存放地址
  184. */
  185. void nRF24L01_recive(unsigned char *pBuf)
  186. {
  187.   memset(pBuf,0,32);            //清空接收数据缓存
  188.   if(R_REG(STATUS)&0x40)        //判断有数据接收
  189.   {
  190.     R_buf(R_RX_PAYLOAD,pBuf,R_REG(R_RX_PL_WID));        //读取接收到的数据
  191.   }
  192.   W_REG(STATUS,0x40);           //清接收标志位
  193. }
  194. /*
  195. **nRF24L01初始化
  196. */
  197. void nRF24L01_init()
  198. {
  199.   CSN=1;
  200.   CE=0;
  201.   
  202.   PC_DDR_DDR4=1;        //端口配置
  203.   PC_CR1_C14=1;
  204.   PC_CR2_C24=1;
  205.   
  206.   PC_DDR_DDR3=1;
  207.   PC_CR1_C13=1;
  208.   PC_CR2_C23=1;
  209.   
  210.   PB_DDR_DDR4=0;
  211.   PB_CR1_C14=1;
  212.   PB_CR2_C24=0;
  213. #ifdef TX_MODE          //模式选择,在头文件中定义
  214.   TX_mode();
  215. #else
  216.   RX_mode();
  217. #endif
  218.   
  219. }


照例打包上传


THE END!

本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖最近打赏记录:共9条打赏M币+113
rallyezhang M币 +20 原創內容 2018-09-18
luly8818 M币 +1 優秀文章 2018-09-10
数码家园 M币 +13 謝謝分享 2018-09-10
xueyuking M币 +8 優秀文章謝謝分享 2018-09-10
i6950 M币 +3 優秀文章 2018-09-10
jsycwnw M币 +13 謝謝分享 2018-09-09
彩虹研究所 M币 +15 謝謝分享 2018-09-09
2545889167 M币 +20 不错的教材 支持 2018-09-09
cxw0102 M币 +20 優秀文章+1 2018-09-09
离线liposlt

发帖
2362
M币
6536
专家
9
粉丝
265
只看该作者 1楼 发表于: 2018-09-09
请登录后查看
离线cxw0102

发帖
1704
M币
6510
专家
15
粉丝
108
只看该作者 2楼 发表于: 2018-09-09
请登录后查看
离线2545889167

发帖
13268
M币
20754
专家
302
粉丝
4860
只看该作者 3楼 发表于: 2018-09-09
请登录后查看
离线lyy-cy

发帖
326
M币
4
专家
9
粉丝
26
只看该作者 4楼 发表于: 2018-09-09
请登录后查看
离线xueyuking

发帖
1439
M币
2607
专家
0
粉丝
22
只看该作者 5楼 发表于: 2018-09-10
请登录后查看
离线dxa572862121
发帖
52
M币
-492
专家
0
粉丝
4
只看该作者 6楼 发表于: 2018-09-14
离线big_zhuang
发帖
2
M币
-500
专家
0
粉丝
0
只看该作者 7楼 发表于: 2019-10-05
请登录后查看
快速回复
限80 字节
温馨提示:所有技术区严禁灌水,“沙发”“顶”字样;禁止广告贴;以免被删除
 
上一个 下一个