查看: 2252|回复: 0

[技术交流] 射频模组应用之LoRa SPI调试

[复制链接]

67

主题

100

帖子

570

积分

利尔达员工

Rank: 9Rank: 9Rank: 9

积分
570
发表于 2018-12-14 20:56:37 | 显示全部楼层 |阅读模式
      经常有遇到客户在应用LoRa模组时,SPI调试不通或者有些关于SPI的疑问,下面整理了些关于SPI的相关知识及对LoRa SPI模组的SPI调试进行了简单说明,希望可以帮到一些对LoRa应用不是很熟悉,又需要快速开发的用户。
1.SPI简介
      SPI 是英语 Serial Peripheral interface 的缩写,顾名思义就是串行外围设备接口。是 Motorola首先在其 MC68HCXX 系列处理器上定义的。 SPI 接口主要应用在 EEPROM FLASH,实时时钟, AD 转换器,还有数字信号处理器和数字信号解码器之间。 SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为 PCB 的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,LoRa模组采用的 SPI 接口。 下面我们看看 SPI 的内部简明图 :



图一 SPI简
SPI 接口一般使用 4 条线通信:
MISO 主设备数据输入,从设备数据输出。
MOSI 主设备数据输出,从设备数据输入。
SCLK 时钟信号,由主设备产生。
CS 从设备片选信号,由主设备控制。
      其中,CS是从芯片是否被主芯片选中的控制信号,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),主芯片对此从芯片的操作才有效。这就使在同一条总线上连接多个SPI设备成为可能。通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCLK时钟线存在的原因,由SCLK提供时钟脉冲,SDISDO则基于此脉冲完成数据传输。数据输出通过 SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。因此,至少需要8次时钟信号的改变(上沿和下沿为一次),才能完成8位数据的传输。
      SPI 总线四种工作方式 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果CPOL=0,串行同步时钟的空闲状态为低电平;如果 CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果 CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI 主模块和与之通信的外设备时钟相位和极性应该一致。 SPI时钟相位(CPHA)时钟极性(CPOL)分别可以为01,对应组合构成了SPI4种模式:
Mode 0:CPOL=0, CPHA=0   空闲时低电平,第一个跳变沿数据被采样
Mode 1:CPOL=0, CPHA=1  空闲时低电平,第二个跳变沿数据被采样
Mode 2:CPOL=1, CPHA=0   空闲时高电平,第一个跳变沿数据被采样
Mode 3:CPOL=1, CPHA=1  空闲时高电平,第二个跳变沿数据被采样
2.LoRa模块的SPI
      LoRa模块SPI接口通过与Motorola/Freescale术语中的CPOL=0CPHA=0相对应的同步全双工协议来访问配置寄存器。只实现从机端。
LoRa模块提供了三种对寄存器的访问模式:
单次(SINGLE)访问:一个地址字节后面跟着一个数据字节被发送用于写访问,而一个地址字节被发送并且一个读字节被接收用于读访问。NSS引脚在帧开始时变低,在数据字节之后变高。
突发(BURST)访问:地址字节后面跟着几个数据字节。地址在内部自动递增在每个数据字节之间。此模式可用于读和写访问。NSS引脚在帧的开始,并在每个字节之间保持低位。只有在最后一次字节传输之后,它才会变高。
FIFO访问:如果地址字节对应于FIFO的地址,那么后续的数据字节将处理FIFO。地址不会自动递增,而是被存储,并且不需要在每个数据字节之间发送。NSS管脚在帧开始时变低,在每个字节之间保持低位。只有在最后一次字节传输之后,它才会变高
下图显示了LoRa模块对寄存器的典型SPI单次访问:

图二 LoRa模块对寄存器的典型SPI单次访问

      MOSISCK的下降沿的主电路产生,并由SCK的上升沿的从电路(即SPI接口)采样。MISO是由SCK下降沿的从机生成的。输总是由NSS管脚变低开始。当NSS较高时,MISO是高阻抗的。
第一个字节是地址字节。它包括:
      一个wnr位,用于写访问为1,用于读访问为0
      然后7位地址,MSB优先;
      第二个字节是数据字节,在写访问的情况下由主机通过MOSI发送,在读访问的情况下由主机通过MISO接收。数据字节首先由MSB发送。
      处理字节可以在MOSI(用于写访问)上发送,或者在MISO(用于读访问)上接收,而无需上升的NSS边缘并重新发送地址。
      在FIFO模式下,如果地址是FIFO地址,那么字节将在FIFO地址被写入/读取。在Burst模式下,如果地址不是FIFO地址,那么对于接收到的每个新字节,它自动递增。
      当NSS变高时,帧结束。下一帧必须以地址字节开始。因此,单次访问模式是FIFO/BURST模式的特殊情况,只传输了一个数据字节。

3.实战应用
1MCU资源确认,正确连接电路
      结合MCU资源,确认下来是使用硬件上的SPI还是需要通过软件模拟来实现。电路连接参考如下:


图三 LoRa模组连接参考电路图
2)确定LoRaSPI工作模式
      LoRa作为一个从机,其时钟相位和极性分别为CPOL=0, CPHA=0即空闲时为低电平,第一个跳变沿数据被采样。
3)下面分别介绍LoRaSPI单次访问寄存器的软件模拟实现方式和硬件实现方式
LoRaSPI单次访问寄存器的软件实现:
      1、通过软件模拟,我们需先分配好各IO的方向及开始电平;因为CPOL=0, CPHA=0 ,即时钟空闲时为低电平,第一个跳变沿数据被采样(即在这里就为上升沿采样),且传输是由NSS管脚变低开始,当NSS较高时,MISO是高阻抗的。所以配置应该为:MOSI需要配置成输出高、MISO需要配置成输入、SCK需要配置成输出低、NSS需要配置成输出高;如下代码实现:
  1. void SX1276SPISetup(void)
  2. {
  3.     LSD_SPI_SIMO_PORT_SEL=0 ;
  4.     LSD_SPI_SIMO_PORT_DIR=1 ;
  5.     LSD_SPI_SIMO_PORT_OUT=1;
  6.    
  7.     LSD_SPI_SCK_PORT_SEL=0;  
  8.     LSD_SPI_SCK_PORT_DIR=1;  
  9.     LSD_SPI_SCK_PORT_OUT=0;  
  10.    
  11.     LSD_SPI_SOMI_PORT_SEL=0;
  12.     LSD_SPI_SOMI_PORT_DIR=0;
  13.     LSD_SPI_SOMI_PORT_OUT=0;
  14.    
  15.     LSD_SPI_NSS_PORT_SEL=0 ;
  16.     LSD_SPI_NSS_PORT_DIR=1;  
  17.     LSD_SPI_NSS_PORT_OUT=1;  
  18.     return;
  19. }
复制代码
2、接下来是根据时钟相位和极性来模拟SPI数据存取;
实现代码如下:
  1. static unsigned char LSD_RF_SpiInOut (unsigned char mosi)
  2. {
  3.     unsigned char i;
  4.     unsigned char onebyte;
  5.     onebyte = mosi;
  6.     for(i = 8; i > 0; i--)
  7.     {
  8.        __no_operation();
  9.         if(onebyte & 0x80)
  10.         {
  11.             LSD_SPI_SIMO_OUT(1);
  12.         }
  13.         else
  14.         {
  15.             LSD_SPI_SIMO_OUT(0);
  16.         }

  17.         __no_operation();
  18.         LSD_SPI_SCK_OUT(1);
  19.         onebyte <<= 1;      // next bit
  20.         if(LSD_SPI_SOMI_IN())
  21.         {
  22.             onebyte++;      // 1 found on MISO
  23.         }
  24.         __no_operation();
  25.         LSD_SPI_SCK_OUT(0);
  26.     }
  27.     return onebyte;         // mosi now becomes the value of miso
  28. }
复制代码
      LoRaSPI单次访问寄存器的硬件实现:
1)先配置好SPI相关IO方向及电平,主要是MISO、MOSI、SCK、NSS引脚
实现代码如下(STM32):
  1. <p>void SX127X_SPIGPIO_Init()
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStruct;
  4.     /* Configure the SX126X_NSS pin */
  5.     GPIO_InitStruct.Pin = GPIO_PIN_15;
  6.     GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  7.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  8.     GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
  9.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  10.     /* SPI SCK GPIO pin configuration  */
  11.     GPIO_InitStruct.Pin       = GPIO_PIN_10;
  12.     GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
  13.     GPIO_InitStruct.Pull      = GPIO_PULLUP;
  14.     GPIO_InitStruct.Speed     = GPIO_SPEED_FAST;
  15.     GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
  16.     HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  17.     /* SPI MISO GPIO pin configuration  */
  18.     GPIO_InitStruct.Pin = GPIO_PIN_11;
  19.     GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
  20.     HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  21.     /* SPI MoSi GPIO pin configuration  */
  22.     GPIO_InitStruct.Pin       = GPIO_PIN_12;
  23.     GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
  24.     GPIO_InitStruct.Pull      = GPIO_PULLUP;
  25.     GPIO_InitStruct.Speed     = GPIO_SPEED_FAST;
  26.     GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
  27.     HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  28. }</p>
复制代码
2)根据MCU及LoRa的SPI工作模式配置好SPI功能
实现代码如下(STM32):
  1. void SX127X_SPI_Init()
  2. {
  3.     __HAL_RCC_GPIOA_CLK_ENABLE();//PORTAʱÖÓʹÄÜ
  4.     __HAL_RCC_GPIOC_CLK_ENABLE();//PORTCʱÖÓʹÄÜ
  5.     __HAL_RCC_SPI3_CLK_ENABLE();//SPI2ʱÖÓʹÄÜ
  6.     SX127X_SPIGPIO_Init();

  7.     SPI3_InitStruct.Instance = SPI3;
  8.     SPI3_InitStruct.Init.Mode = SPI_MODE_MASTER;//SPI模式:主机模式
  9. SPI3_InitStruct.Init.Direction = SPI_DIRECTION_2LINES;//两线全双工
  10. SPI3_InitStruct.Init.DataSize = SPI_DATASIZE_8BIT;//数据宽度:8位
  11. SPI3_InitStruct.Init.CLKPolarity = SPI_POLARITY_LOW; //´®ÐÐͬ²½×ÖʱÖÓ¿ØÖÆΪµÍËÙʱÖÓ
  12.     SPI3_InitStruct.Init.CLKPhase = SPI_PHASE_1EDGE;      //CPOL=0;CPHA=0 模式
  13. SPI3_InitStruct.Init.NSS = SPI_NSS_SOFT;//NSS由软件控制
  14. SPI3_InitStruct.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
  15. SPI3_InitStruct.Init.FirstBit = SPI_FIRSTBIT_MSB;//数据从MSB开始
  16. SPI3_InitStruct.Init.TIMode = SPI_TIMODE_DISABLE;//SPI Motorola mode
  17.     SPI3_InitStruct.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  18. SPI3_InitStruct.Init.CRCPolynomial = 7;
  19. SPI3_InitStruct.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  20.     SPI3_InitStruct.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  21. if (HAL_SPI_Init(&SPI3_InitStruct) != HAL_OK)
  22.   {
  23.           while(1);
  24.   }
  25.     __HAL_SPI_ENABLE(&SPI3_InitStruct);
  26. }
复制代码
4LoRa 寄存器访问     
      如下向寄存器中写和读操作的函数代码,下面代码是从寄存器中读取数据的函数代码,从中可以看出,在操作之前,都将使能端NSS拉低了,并且需要主要写写地址时,最高位为1,读时为0,即SX127X_ReadWriteByte(addr | 0x80); 和 SX127X_ReadWriteByte(addr & 0x7F);
  1. void SX127X_WriteBuffer( uint8_t addr, uint8_t *buffer, uint8_t size )
  2. {
  3.     uint8_t i;
  4.     SX127X_NSS_OUTPUT(GPIO_PIN_RESET);
  5.     SX127X_ReadWriteByte(addr | 0x80);
  6.     for( i = 0; i < size; i++ )
  7.     {
  8.         SX127X_ReadWriteByte(buffer[i]);
  9.     }
  10.     SX127X_NSS_OUTPUT(GPIO_PIN_SET);
  11. }
复制代码
  1. void SX127X_ReadBuffer( uint8_t addr, uint8_t *buffer, uint8_t size )
  2. {
  3.     uint8_t i;
  4.     SX127X_NSS_OUTPUT(GPIO_PIN_RESET);
  5.     SX127X_ReadWriteByte(addr & 0x7F);
  6.     for( i = 0; i < size; i++ )
  7.     {
  8.         buffer[i] = SX127X_ReadWriteByte(0x00);
  9.     }

  10.     SX127X_NSS_OUTPUT(GPIO_PIN_SET);
  11. }
复制代码
      为了验证SPI是否通过或者,避免MCU异常不工作,代码中会去选用LoRa的一个在应用中没用到的寄存器去验证SPI,如下,选用0x24寄存器去读写0x91进行验证;
  1. //SPI 验证
  2.     uint8_t test = 0;
  3.     SX127X_Write( REG_LR_HOPPERIOD, 0x91 );
  4.     SX127X_Read( REG_LR_HOPPERIOD, &test);
  5.     if(test != 0x91)
  6.         return SPI_READCHECK_WRONG;
复制代码
四和图五是用逻辑分析仪抓取出来的实验结果。图四是向地址为0x24的寄存器中写入数据0x91的过程,可以看到,写入地址为0x80|0x24,MOSI输出数据0xX91;图五是从地址为0x24的寄存器中读取数据的过程,读取数据也是0x91,读取数据与写入数据相同,实验成功。
图四



图五
最后附上LoRa模组开发资料及例程代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

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

本版积分规则

快速回复 返回顶部 返回列表