4108|9

606

帖子

20

TA的资源

一粒金砂(高级)

楼主
 

【基于GDF350的无线数字对讲机】2、NRF24L01的SPI通讯 [复制链接]

 
nRF24L01是由NORDIC生产的工作在2.4GHz~2.5GHz的ISM 频段的单片无线收发器芯片。无线收发器包括:频率发生器、增强型“SchockBurst”模式控制器、功率放大器、晶体振荡器、调制器和解调器。
它的优点如下:
(1) 2.4Ghz 全球开放ISM 频段免许可证使用
(2) 最高工作速率2Mbps,高效GFSK调制,抗干扰能力强,特别适合工业控制场合
(3) 126 频道,满足多点通信和跳频通信需要
(4) 内置硬件CRC 检错和点对多点通信地址控制
(5) 低功耗1.9 - 3.6V 工作,待机模式下状态为22uA;掉电模式下为900nA

模块及原理图如下:

今天就来用GD32F350来驱动下这个芯片,NRF24L01使用SPI通讯,GDF350提供了两个SPI接口,我们使用SPI1来连接.用到的引脚PB6,PB6,PB12,PB13,PB14,PB15.复用功能0.
连接图如下:

关于SPI总线四种工作方式科普下,我当年就对这个不清楚。
CPOL=0,串行同步时钟的空闲状态为低电平;CPOL=1,串行同步时钟的空闲状态为高电平。
CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。
NRF24L01数据手册上给的时序图如下:

从中可以看出在配置SPI时钟极性的时候,应该配置为:同步时钟空闲状态为低电平,在同步时钟的第一个跳变沿数据被采样。(SPI_CK_PL_LOW_PH_1EDGE)
  1. /*
  2. * 函数名:spi_config
  3. * 描述  :SPI接口初始化
  4. * 输入  :
  5. * 输出  :
  6. * 调用  :api接口
  7. */
  8. void spi_config(void)
  9. {
  10.     spi_parameter_struct SPI_InitStructure;

  11.     /* 使能 SPI1 相关 GPIO 的时钟 */
  12.     rcu_periph_clock_enable(RCU_GPIOB);
  13.     rcu_periph_clock_enable(RCU_SPI1);

  14.     /* 配置CS pin : Pb.12 */
  15.     /* 配置ce pin : Pb.6 */
  16.     gpio_mode_set(GPIOB,GPIO_MODE_OUTPUT,GPIO_PUPD_PULLUP,PIN_CS|PIN_CE);
  17.     gpio_output_options_set(GPIOB,GPIO_OTYPE_PP,GPIO_OSPEED_2MHZ,PIN_CS|PIN_CE);

  18.     /*配置SPI_NRF_SPI的IRQ引脚,PB7 */
  19.     gpio_mode_set(GPIOB,GPIO_MODE_INPUT,GPIO_PUPD_NONE,PIN_IRQ);

  20.     /* 配置 SCK  MISO MOSI*/
  21.     gpio_af_set(GPIOB,GPIO_AF_0,PIN_MISO|PIN_MOSI|PIN_SCK);
  22.     gpio_mode_set(GPIOB,GPIO_MODE_AF,GPIO_PUPD_NONE,PIN_SCK|PIN_MISO|PIN_MOSI);
  23.     gpio_output_options_set(GPIOB,GPIO_OTYPE_PP,GPIO_OSPEED_10MHZ,PIN_SCK|PIN_MISO|PIN_MOSI);

  24.     /* 这是自定义的宏,用于拉高csn引脚,NRF进入空闲状态 */
  25.     NRF_CSN_HIGH();

  26.     /* SPI1 配置*/
  27.     SPI_InitStructure.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
  28.     SPI_InitStructure.device_mode = SPI_MASTER;
  29.     SPI_InitStructure.frame_size = SPI_FRAMESIZE_8BIT;
  30.     SPI_InitStructure.nss = SPI_NSS_SOFT;
  31.     SPI_InitStructure.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
  32.     SPI_InitStructure.prescale = SPI_PSC_8;
  33.     SPI_InitStructure.endian = SPI_ENDIAN_MSB;
  34.     spi_init(SPI1, &SPI_InitStructure);

  35.     spi_enable(SPI1);
  36. }
复制代码

还有CE,CS,IRQ这个按手册上配置就是了。没有什么技术难度。关于NRF24L01的驱动代码我也发出来
  1. /*!
  2.     \file  nRF24L01.h
  3.     \brief 2.4GHz High-speed wireless transceiver chip(nRF24L01) driver.
  4. */

  5. /*
  6.     Copyright (C) 2018 sunsjw

  7.     2018-09-09, V1.0.0
  8. */
  9. #include "gd32f3x0.h"                   // Device header

  10. #include "nRF24L01.h"
  11. #include "spi_api.h"
  12. #include "systick.h"


  13. // 定义一个静态发送地址
  14. static uint8_t TX_ADDRESS[TX_ADR_WIDTH] = {0x34,0x43,0x10,0x10,0x01};
  15. static uint8_t RX_ADDRESS[RX_ADR_WIDTH] = {0x34,0x43,0x10,0x10,0x01};
  16. uint8_t cdVal;
  17. uint8_t PackLoss;
  18. NRF_Mode _nRFMode;

  19. /*
  20. * 函数名:nRF24L01_Init
  21. * 描述  :nRF24L01 初始化
  22. * 输入  :
  23. * 输出  :
  24. * 调用  :api接口
  25. */
  26. void nRF24L01_Init(void)
  27. {
  28.     spi_config();
  29.     _nRFMode = Unknow;
  30. }

  31. /*
  32. * 函数名:SPI_NRF_WriteReg
  33. * 描述  :用于向NRF特定的寄存器写入数据
  34. * 输入  :reg:NRF的命令+寄存器地址。
  35.            dat:将要向寄存器写入的数据
  36. * 输出  :NRF的status寄存器的状态
  37. * 调用  :内部调用
  38. */
  39. uint8_t SPI_NRF_WriteReg(uint8_t reg,uint8_t dat)
  40. {
  41.     uint8_t status;
  42.     spi_ce_disable();
  43.     /*置低CSN,使能SPI传输*/
  44.     spi_cs_enable();

  45.     /*发送命令及寄存器号 */
  46.     status = spi_write(reg);

  47.     /*向寄存器写入数据*/
  48.     spi_write(dat);

  49.     /*CSN拉高,完成*/
  50.     spi_cs_disable();

  51.     /*返回状态寄存器的值*/
  52.     return(status);
  53. }


  54. /*
  55. * 函数名:SPI_NRF_ReadReg
  56. * 描述  :用于从NRF特定的寄存器读出数据
  57. * 输入  :reg:NRF的命令+寄存器地址。
  58. * 输出  :寄存器中的数据
  59. * 调用  :内部调用
  60. */
  61. uint8_t SPI_NRF_ReadReg(uint8_t reg)
  62. {
  63.     uint8_t reg_val;

  64.     spi_ce_disable();
  65.     /*置低CSN,使能SPI传输*/
  66.     spi_cs_enable();

  67.     /*发送寄存器号*/
  68.     spi_write(reg);

  69.     /*读取寄存器的值 */
  70.     reg_val = spi_write(NOP);

  71.     /*CSN拉高,完成*/
  72.     spi_cs_disable();

  73.     return reg_val;
  74. }


  75. /*
  76. * 函数名:SPI_NRF_ReadBuf
  77. * 描述  :用于从NRF的寄存器中读出一串数据
  78. * 输入  :reg:NRF的命令+寄存器地址。
  79.            pBuf:用于存储将被读出的寄存器数据的数组,外部定义
  80.            bytes: pBuf的数据长度
  81. * 输出  :NRF的status寄存器的状态
  82. * 调用  :外部调用
  83. */
  84. uint8_t SPI_NRF_ReadBuf(uint8_t reg,uint8_t *pBuf,uint8_t bytes)
  85. {
  86.     uint8_t status, byte_cnt;

  87.     spi_ce_disable();
  88.     /*置低CSN,使能SPI传输*/
  89.     spi_cs_enable();

  90.     /*发送寄存器号*/
  91.     status = spi_write(reg);

  92.     /*读取缓冲区数据*/
  93.     for(byte_cnt=0; byte_cnt<bytes; byte_cnt++)
  94.         pBuf[byte_cnt] = spi_write(NOP); //从NRF24L01读取数据

  95.     /*CSN拉高,完成*/
  96.     spi_cs_disable();

  97.     return status;      //返回寄存器状态值
  98. }



  99. /*
  100. * 函数名:SPI_NRF_WriteBuf
  101. * 描述  :用于向NRF的寄存器中写入一串数据
  102. * 输入  :reg:NRF的命令+寄存器地址。
  103.            pBuf:存储了将要写入写寄存器数据的数组,外部定义
  104.            bytes: pBuf的数据长度
  105. * 输出  :NRF的status寄存器的状态
  106. * 调用  :外部调用
  107. */
  108. uint8_t SPI_NRF_WriteBuf(uint8_t reg,uint8_t *pBuf,uint8_t bytes)
  109. {
  110.     uint8_t status,byte_cnt;
  111.     spi_ce_disable();
  112.     /*置低CSN,使能SPI传输*/
  113.     spi_cs_enable();

  114.     /*发送寄存器号*/
  115.     status = spi_write(reg);

  116.     /*向缓冲区写入数据*/
  117.     for(byte_cnt=0; byte_cnt<bytes; byte_cnt++)
  118.         spi_write(*pBuf++);    //写数据到缓冲区

  119.     /*CSN拉高,完成*/
  120.     spi_cs_disable();

  121.     return (status);    //返回NRF24L01的状态
  122. }

  123. /*
  124. * 函数名:NRF_RX_Mode
  125. * 描述  :配置并进入接收模式
  126. * 输入  :无
  127. * 输出  :无
  128. * 调用  :外部调用
  129. */
  130. void NRF_RX_Mode(void)
  131. {
  132.     _nRFMode = Recv;

  133.     spi_ce_disable();

  134.     SPI_NRF_WriteBuf(W_REGISTER+RX_ADDR_P0,RX_ADDRESS,RX_ADR_WIDTH);//写RX节点地址

  135.     SPI_NRF_WriteReg(W_REGISTER+EN_AA,0x01);    //使能通道0的自动应答

  136.     SPI_NRF_WriteReg(W_REGISTER+EN_RXADDR,0x01);//使能通道0的接收地址

  137.     SPI_NRF_WriteReg(W_REGISTER+RF_CH,CHANAL);      //设置RF通信频率

  138.     SPI_NRF_WriteReg(W_REGISTER+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度

  139.     SPI_NRF_WriteReg(W_REGISTER+RF_SETUP,0x0f); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启

  140.     SPI_NRF_WriteReg(W_REGISTER+CONFIG, 0x0f);  //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式

  141.     /*CE拉高,进入接收模式*/
  142.     spi_ce_enable();
  143. }

  144. /*
  145. * 函数名:NRF_TX_Mode
  146. * 描述  :配置发送模式
  147. * 输入  :无
  148. * 输出  :无
  149. * 调用  :外部调用
  150. */
  151. void NRF_TX_Mode(void)
  152. {
  153.     _nRFMode = Send;

  154.     spi_ce_disable();

  155.     SPI_NRF_WriteBuf(W_REGISTER+TX_ADDR,TX_ADDRESS,TX_ADR_WIDTH);    //写TX节点地址

  156.     SPI_NRF_WriteBuf(W_REGISTER+RX_ADDR_P0,RX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK

  157.     SPI_NRF_WriteReg(W_REGISTER+EN_AA,0x01);     //使能通道0的自动应答

  158.     SPI_NRF_WriteReg(W_REGISTER+EN_RXADDR,0x01); //使能通道0的接收地址

  159.     SPI_NRF_WriteReg(W_REGISTER+SETUP_RETR,0x1a);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次

  160.     SPI_NRF_WriteReg(W_REGISTER+RF_CH,CHANAL);       //设置RF通道为CHANAL

  161.     SPI_NRF_WriteReg(W_REGISTER+RF_SETUP,0x0f);  //设置TX发射参数,0db增益,2Mbps,低噪声增益开启

  162.     SPI_NRF_WriteReg(W_REGISTER+CONFIG,0x0e);    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,发射模式,开启所有中断

  163.     /*CE拉高,进入发送模式*/
  164.     spi_ce_enable();
  165.     delay_10us(1); //CE要拉高一段时间才进入发送模式
  166. }



  167. /*
  168. * 函数名:NRF_Check
  169. * 描述  :主要用于NRF与MCU是否正常连接
  170. * 输入  :无
  171. * 输出  :SUCCESS/ERROR 连接正常/连接失败
  172. * 调用  :外部调用
  173. */
  174. uint8_t NRF_Check(void)
  175. {
  176.     uint8_t buf[5]= {0xC2,0xC2,0xC2,0xC2,0xC2};
  177.     uint8_t buf1[5];
  178.     uint8_t i;

  179.     /*写入5个字节的地址.  */
  180.     SPI_NRF_WriteBuf(W_REGISTER+TX_ADDR,buf,5);

  181.     /*读出写入的地址 */
  182.     SPI_NRF_ReadBuf(TX_ADDR,buf1,5);

  183.     /*比较*/
  184.     for(i=0; i<5; i++)
  185.     {
  186.         if(buf1[i]!=0xC2)
  187.             break;
  188.     }

  189.     if(i==5)
  190.         return SUCCESS ;        //MCU与NRF成功连接
  191.     else
  192.         return ERROR ;        //MCU与NRF不正常连接
  193. }


  194. /*
  195. * 函数名:NRF_Tx_Dat
  196. * 描述  :用于向NRF的发送缓冲区中写入数据
  197. * 输入  :txBuf:存储了将要发送的数据的数组,外部定义
  198. * 输出  :发送结果,成功返回TXDS,失败返回MAXRT或ERROR
  199. * 调用  :外部调用
  200. */
  201. uint8_t NRF_Tx_Dat(uint8_t *txbuf)
  202. {
  203.     uint8_t state;

  204.     /*ce为低,进入待机模式1*/
  205.     spi_ce_disable();

  206.     /*写数据到TX BUF 最大 32个字节*/
  207.     SPI_NRF_WriteBuf(W_TX_PAYLOAD,txbuf,TX_PLOAD_WIDTH);

  208.     /*CE为高,txbuf非空,发送数据包 */
  209.     spi_ce_enable();

  210.     /*等待发送完成中断 */
  211.     while(read_IRQ() != RESET)
  212.     {
  213.         if(_nRFMode == Recv)
  214.             return MODECHANGE;
  215.     }

  216.     /*读取状态寄存器的值 */
  217.     state = SPI_NRF_ReadReg(STATUS);

  218.     /*清除TX_DS或MAX_RT中断标志*/
  219.     SPI_NRF_WriteReg(W_REGISTER+STATUS,state);

  220.     SPI_NRF_WriteReg(FLUSH_TX,NOP);    //清除TX FIFO寄存器

  221.     /*判断中断类型*/
  222.     if(state&MAX_RT)                     //达到最大重发次数
  223.     {
  224.         //uint8_t cdVal = NRF_Carrier_Detect();
  225.                                 //PackLoss = NRF_Check_PackageLoss();
  226.         return MAX_RT;
  227.     }
  228.     else if(state&TX_DS)                  //发送完成
  229.         return TX_DS;
  230.     else
  231.     {
  232.         cdVal = NRF_Carrier_Detect();
  233.         return ERROR;                 //其他原因发送失败
  234.     }
  235. }


  236. /*
  237. * 函数名:NRF_Rx_Dat
  238. * 描述  :用于从NRF的接收缓冲区中读出数据
  239. * 输入  :rxBuf:用于接收该数据的数组,外部定义
  240. * 输出  :接收结果,
  241. * 调用  :外部调用
  242. */
  243. uint8_t NRF_Rx_Dat(uint8_t *pRxbuf)
  244. {
  245.     uint8_t state;

  246.     spi_ce_enable();   //进入接收状态

  247.     /*等待接收中断*/
  248.     while(read_IRQ() != RESET)
  249.     {
  250.         if(_nRFMode == Send)
  251.             return MODECHANGE;
  252.     }

  253.     //进入待机状态
  254.     spi_ce_disable();

  255.     /* 读取status寄存器的值  */
  256.     state=SPI_NRF_ReadReg(STATUS);

  257.     /* 清除中断标志*/
  258.     SPI_NRF_WriteReg(W_REGISTER+STATUS,state);

  259.     /*判断是否接收到数据*/
  260.     if(state& RX_DR)                                 //接收到数据
  261.     {
  262.         SPI_NRF_ReadBuf(R_RX_PAYLOAD,pRxbuf,RX_PLOAD_WIDTH);//读取数据
  263.         SPI_NRF_WriteReg(FLUSH_RX,NOP);          //清除RX FIFO寄存器
  264.         return SUCCESS;
  265.     }
  266.     else
  267.     {
  268.         cdVal = NRF_Carrier_Detect();
  269.                                 PackLoss = NRF_Check_PackageLoss();
  270.         return ERROR;                    //没收到任何数据
  271.     }
  272. }

  273. /*
  274. * 函数名:NRF_Check_PackageLoss
  275. * 描述  :nRF24l01发送检测
  276. * 输入  :
  277. * 输出  :返回值的7-4位表示数据包丢失计数,3-0位表示重发计数
  278. * 调用  :外部调用
  279. */
  280. uint8_t NRF_Check_PackageLoss(void)
  281. {
  282.     return SPI_NRF_ReadReg(OBSERVE_TX);
  283. }

  284. /*
  285. * 函数名:NRF_Carrier_Detect
  286. * 描述  :nRF24l01载波检测
  287. * 输入  :
  288. * 输出  :返回1,通道拥挤,需要更改通信频道
  289. * 调用  :外部调用
  290. */
  291. uint8_t NRF_Carrier_Detect(void)
  292. {
  293.     return SPI_NRF_ReadReg(CD);
  294. }

  295. /*
  296. * 函数名:NRF_GetStatus
  297. * 描述  :获取nRF24l01的状态
  298. * 输入  :
  299. * 输出  :返回nRF24l01的发送和接收状态,
  300. * 调用  :外部调用
  301. */
  302. NRF_Mode NRF_GetStatus(void)
  303. {
  304.     return _nRFMode;
  305. }

  306. /*
  307. * 函数名:NRF_SetStatus
  308. * 描述  :设置nRF24l01的状态
  309. * 输入  :
  310. * 输出  :
  311. * 调用  :中断调用
  312. */
  313. void NRF_SetStatus(NRF_Mode newMode)
  314. {
  315.     _nRFMode = newMode;
  316. }

复制代码

代码还是做了很详细的注释,有疑问的同学可以留言。
此帖出自GD32 MCU论坛

最新回复

声音效果怎么样?是否压缩?静等测试视频  详情 回复 发表于 2018-9-30 14:44
点赞 关注(2)
 

回复
举报

1368

帖子

6

TA的资源

版主

沙发
 
不错,棒棒的……
此帖出自GD32 MCU论坛
 
个人签名专注智能产品的研究与开发,专注于电子电路的生产与制造……QQ:2912615383,电子爱好者群: void
 
 

回复

693

帖子

7

TA的资源

版主

板凳
 
这个对讲机应该不是用来传递声音信号的吧,能不能远距离语音通信呢,看代码像是传送数据的
此帖出自GD32 MCU论坛

点评

是传递的语音信号。是要把语音信号转换数字信号进行传输的。 目前我在我们小区测,有几百米的距离是没有问题的。  详情 回复 发表于 2018-9-29 15:04
 
 
 

回复

935

帖子

1

TA的资源

禁止发言

4
 
此帖出自GD32 MCU论坛
 
个人签名存储芯片/MCU/SRAM/PSRAM/DDR/FLASH/MRAM。web.www.sramsun.com  QQ3161422826 TEL:13751192923
 
 

回复

196

帖子

0

TA的资源

一粒金砂(中级)

5
 
多谢分享!
此帖出自GD32 MCU论坛
 
 
 

回复

606

帖子

20

TA的资源

一粒金砂(高级)

6
 
bqgup 发表于 2018-9-29 09:14
这个对讲机应该不是用来传递声音信号的吧,能不能远距离语音通信呢,看代码像是传送数据的

是传递的语音信号。是要把语音信号转换数字信号进行传输的。
目前我在我们小区测,有几百米的距离是没有问题的。
此帖出自GD32 MCU论坛

点评

这么牛,多大的采样率?8k还是16k  详情 回复 发表于 2018-9-30 09:34
 
 
 

回复

196

帖子

0

TA的资源

一粒金砂(中级)

7
 
ketose 发表于 2018-9-29 15:04
是传递的语音信号。是要把语音信号转换数字信号进行传输的。
目前我在我们小区测,有几百米的距离是没有 ...

这么牛,多大的采样率?8k还是16k
此帖出自GD32 MCU论坛

点评

8K采样  详情 回复 发表于 2018-9-30 12:07
 
 
 

回复

932

帖子

3

TA的资源

纯净的硅(中级)

8
 
也准备通过射频模块通讯,已经购买了SI4463芯片的无线模块,还没有开始使用,正好可以参照学习楼主的经验。
此帖出自GD32 MCU论坛
 
 
 

回复

606

帖子

20

TA的资源

一粒金砂(高级)

9
 
leifengfirst 发表于 2018-9-30 09:34
这么牛,多大的采样率?8k还是16k

8K采样
此帖出自GD32 MCU论坛
 
 
 

回复

196

帖子

0

TA的资源

一粒金砂(中级)

10
 
声音效果怎么样?是否压缩?静等测试视频
此帖出自GD32 MCU论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表