LoRa移植到STM32F051

时间:2024-04-06 18:46:00

LoRa移植和驱动开发

 好久没有写博文了,这从今还要捡起来,今天发一个最近移植的LoRa文档,粘贴出来供大家参考。里面也有一些参考别人的过程。

现有的驱动移植到STM32F0(STM32Cubemx)上。官网SX12xxx系列驱动sx12xxdrivers-v2.1.0.zip。解压后:

LoRa移植到STM32F051

图1‑1 解压后库文件

开发之前先了解几个专业术语:

表 1-1专业数据表

FHSS

跳频扩频技术

FIFO

进先出队列,这里代表队列寄存器

PA

功率放大器

LNA

低噪声放大器

SNR

信噪比

SF

扩频因子

PLL

锁相环

CAD

信道活动检测

CR

编码率

BW

带宽

RS

符号速率

Preamble

序头

 

文件的驱动程序接口在src下platform和radio文件夹复制到你自己的工程中,如下图:

LoRa移植到STM32F051

图1‑2 LoRa库文件

在IAR工程中新建两个文件目录radio和sx12xxEiger。

LoRa移植到STM32F051

在两个文件目录中添加LoRa驱动文件,

LoRa移植到STM32F051

图1‑3添加驱动文件

LoRa移植到STM32F051

同理在sx12xEiger中添加文件,因为我们使用的是SPI通信协议,所以选择如下:

 LoRa移植到STM32F051

整体移植如下:

LoRa移植到STM32F051

图1‑4 工程中添加库文件

以上图为基础删除和我们芯片无关的文件(我们是sx1278),即删除sx1232和sx1272的文件。

LoRa移植到STM32F051

图1‑5保留文件

移植完之后,编译,出现很多错误。需要修改里面的一些配置,因为LoRa是基于STM32F10的库做。而我们是把现有的库移植到STM32Cubemx上,可能有些库中结构体名字、头文件和函数名不一致,需要进行修改。这个自己可以慢慢修改,这里就不说了。直到编译没有错误为止。

1.1       库SPI修改

MCU和LoRa模块使用的是SPI总线方式,参考原理图查看使用那几个我们使用STM32Cubemx实现SPI驱动。然后修改LoRa库中的引脚配置(DIO0和片选线)和SPI库函数操作。

LoRa移植到STM32F051

图1‑6 LoRa连接MCU引脚

如上图,在STM32Cubemx配置上图的红色框中的引脚即可,PA7作为片选配置为输出,默认拉高。其它的按SPI的引脚固定引脚配置。

LoRa移植到STM32F051

图1‑7 STM32Cubemx自动生成SPI驱动文件

配置完之后,会在user目录下生成一个spi.c文件。在文件下添加读取数据的函数。

片选引脚:

 COBOL Code 

1
2
3
4
5
6
7

#define SX12xx_NSS_IOPORT                                  GPIOA
#define SX12xx_NSS_PIN                                     GPIO_PIN_7
//片选信号
//NSS = 0
HAL_GPIO_WritePin(SX12xx_NSS_IOPORT, SX12xx_NSS_PIN,GPIO_PIN_RESET);
//NSS = 1
HAL_GPIO_WritePin(SX12xx_NSS_IOPORT, SX12xx_NSS_PIN,GPIO_PIN_SET);

SPI写读操作函数:

 COBOLCode 

1
2
3
4
5
6
7
8
9
10
11

uint8_t SpiInOut( uint8_t outData )
{
      uint8_t pData=
0;
    
     
if( HAL_SPI_TransmitReceive(&hspi1,&outData,&pData,1,0xfff)!= HAL_OK)
     {
      
return ERROR;
     }
      
      
return pData;
}

修改下面函数SX1276WriteBufferSX1276ReadBuffer中的片选线如下面代码(改为STM32Cubemx库中操作):

 COBOLCode 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

void SX1276WriteBuffer( uint8_t addr, uint8_t *buffer, uint8_t size )
{
    uint8_t i;

    
//NSS = 0;
    HAL_GPIO_WritePin(SX12xx_NSS_IOPORT, SX12xx_NSS_PIN,GPIO_PIN_RESET);

    SpiInOut( addr | 0x80 );
    
for( i = 0; i < size; i++ )
    {
        SpiInOut( buffer[i] );
    }

    
//NSS = 1;
    HAL_GPIO_WritePin(SX12xx_NSS_IOPORT, SX12xx_NSS_PIN,GPIO_PIN_SET);
}

void SX1276ReadBuffer( uint8_t addr, uint8_t 
*buffer, uint8_t size )
{
    uint8_t i;

    
//NSS = 0;
    HAL_GPIO_WritePin(SX12xx_NSS_IOPORT, SX12xx_NSS_PIN,GPIO_PIN_RESET);

    SpiInOut( addr & 0x7F );

    
for( i = 0; i < size; i++ )
    {
        buffer[i] = SpiInOut( 
0 );
    }

    
//NSS = 1;
    HAL_GPIO_WritePin(SX12xx_NSS_IOPORT, SX12xx_NSS_PIN,GPIO_PIN_SET);
}

1.2       修改LoRa库参考时间

为什么需要修改这个时间,是因为其库操作中是基于状态机操作,有超时检测的事件,参照TickCounter计数。

 COBOLCode 

1

#define GET_TICK_COUNT( )                           HAL_GetTick() 

1.3       LED灯

由于LoRa库中的LED和我们的模块的引脚不一致,需要我们根据原理图进行修改。

LoRa移植到STM32F051

图1‑8 LED引脚

上面三个引脚连接到底板上发送,接收和网络3个指示灯上,在led.hled.c

注销LED相关的初始化,只需更改LED相关端口和引脚的配置。如下配置。

 COBOLCode 

1
2
3
4
5
6
7
8
9
10
11
12
13
14

#define LED_NB                           4

// RED
#define LED1_PIN                         GPIO_PIN_1
#define LED1_GPIO_PORT                   GPIOB
// GREEN
#define LED2_PIN                         GPIO_PIN_2
#define LED2_GPIO_PORT                   GPIOB
// DBG1
#define LED3_PIN                         GPIO_PIN_1
#define LED3_GPIO_PORT                   GPIOB
// DBG2
#define LED4_PIN                         GPIO_PIN_0
#define LED4_GPIO_PORT                   GPIOB

还需更改LED的操作函数LedOn和LedOff。

 COBOLCode 

1
2
3
4
5
6
7
8
9
10

//LED是共阳极电路,引脚置地,灯开。
void LedOn( tLed led )
{
    HAL_GPIO_WritePin(LedPort[led], LedPin[led],GPIO_PIN_RESET );
}

void LedOff( tLed led )
{
    HAL_GPIO_WritePin(LedPort[led], LedPin[led], GPIO_PIN_SET);
}

1.4       添加printf

STM32Cubemx生产的工程本身不可以直接使用printf,需要在工程中添加下面代码即可。使用串口1打印信息。

 COBOLCode 

1
2
3
4
5
6

int fputc(int chFILE *f)
{     
  while((USART1->ISR & 0X40)==
0);
  USART1->TDR = (uint8_t)
ch;      
  
return ch;
}

1.5       修改loRa配置参数

次处根据LoRa芯片操作,我们使用的是sx1278频段范围在410~525MHz,所以我们配置为434000000。具体配置如下:

 COBOLCode 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

#define LoRa_FREQENCY    434000000

// Default settings
tLoRaSettings LoRaSettings =
{
    LoRa_FREQENCY,    
//870000000,        // RFFrequency
    20,               // Power
    9,                // SignalBw [0: 7.8kHz, 1: 10.4 kHz, 2: 15.6 kHz, 3: 20.8 kHz, 4: 31.2 kHz,
                      // 5: 41.6 kHz, 6: 62.5 kHz, 7: 125 kHz, 8: 250 kHz, 9: 500 kHz, other: Reserved]
    7,                // SpreadingFactor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096  chips]
    2,                // ErrorCoding [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
    true,             // CrcOn [0: OFF, 1: ON]
    false,            // ImplicitHeaderOn [0: OFF, 1: ON]
    1,                // RxSingleOn [0: Continuous, 1 Single]
    0,                // FreqHopOn [0: OFF, 1: ON]
    4,                // HopPeriod Hops every frequency hopping period symbols
    100,              // TxPacketTimeout
    100,              // RxPacketTimeout
   128,              // PayloadLength (used for implicit header mode)
};

 

1.6       程序调试

我们使用的SX12xxDrivers-V2.1.0的LoRa库,库资源如下:

LoRa移植到STM32F051

图1‑9 LoRa官方库

下面是官方库工程目录说明:

表 1-2工程目录结构

目录

说明

doc

官方文档

lst

工程编译时的临时文件目录

obj

工程编译时的临时文件目录

src

驱动以及可用硬件平台源码

sx12xxDrivers.rapp

Ride7 集成开发环境工程文件 (兼容 keil ARM)

sx12xxDrivers.rprj

Ride7 集成开发环境工程文件 (兼容 keil ARM)

我们只关注src源码目录和doc官方文档。

Platform:硬件平台相关源码

Radio:sx1232/sx1272/sx1276 驱动源码及无线驱动简易框架

main.c:数据 PING-PONG 收发示例代码

READEME.txt:库工程说明,这个很重要

1.6.1        工程示例说明

本示例使用2台设备进行数据PING-PONG收发,一台作为主(master),一台做从机(slave),注意库中主从关系是自适配,不需要特意指定谁主谁从。

测试时若一台设备线上电运行,则自动成为主机,定时发送“PING”消息,另一台上电的设备一开始也是主机模式,当其收到“PING”消息后自动变为从机,并回应主机“PONG”数据,主机和从机之间不停的PING-PONG收发。

1.6.2        测试效果

模块底板上的LED发送指示灯和接收指示灯互相闪烁。当接收到数据并解析正确,则接收指示灯闪烁,发送数据完成,则发送指示灯闪烁

下面从串口中显示测试效果:

当接收到数据包,会在串口上打印接收到的数据包。

 LoRa移植到STM32F051

图1‑10 打印接收到的信息

OnMater做主机运行代码,OnSlave做从机运行代码。下面红色代码打印接收信息。

 COBOLCode 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

void OnMaster( void )
{
  uint8_t i;
  
  switch( Radio->
Process( ) )
  {
  case RF_RX_TIMEOUT:
    
// Send the next PING frame
    Buffer[0] = 'P';
    Buffer[
1] = 'I';
    Buffer[
2] = 'N';
    Buffer[
3] = 'G';
    
    
for( i = 4; i < BufferSize; i++ )
    {
      Buffer[i] = i - 
4;
    }
    Radio->SetTxPacket( Buffer, BufferSize );
    break;
  case RF_RX_DONE:
    
    Radio->GetRxPacket( Buffer, ( uint16_t
* )&BufferSize );
    
   
 printf("master Rx_________%s\n",Buffer);
    
    
if( BufferSize > 0 )
    {
      
if( strncmp( ( const char* )Buffer, ( const char* )PongMsg, 4 ) == 0 )
      {
        
// Indicates on a LED that the received frame is a PONG
        LedToggle( LED_GREEN );
        
        
// Send the next PING frame            
        Buffer[0] = 'P';
        Buffer[
1] = 'I';
        Buffer[
2] = 'N';
        Buffer[
3] = 'G';
        
// We fill the buffer with numbers for the payload 
        for( i = 4; i < BufferSize; i++ )
        {
          Buffer[i] = i - 
4;
        }
        Radio->SetTxPacket( Buffer, BufferSize );
      }
      
else if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 )
      { // A master already exists then become a slave
        EnableMaster = false;
        LedOff( LED_RED );
      }
    }            
    break;
  case RF_TX_DONE:
    
// Indicates on a LED that we have sent a PING
    LedToggle( LED_RED );
    Radio->StartRx( );
    break;
  
default:
    break;
  }
}

/*
* Manages the slave operation
*/
void OnSlave( void )
{
  uint8_t i;
  
  switch( Radio->
Process( ) )
  {
  case RF_RX_DONE:
    
    Radio->GetRxPacket( Buffer, ( uint16_t
* )&BufferSize );
    
    printf("slave Rx_________%s\n",Buffer);
    
    
if( BufferSize > 0 )
    {
      
if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 )
      {
        
// Indicates on a LED that the received frame is a PING
        LedToggle( LED_GREEN );
        
        
// Send the reply to the PONG string
        Buffer[0] = 'P';
        Buffer[
1] = 'O';
        Buffer[
2] = 'N';
        Buffer[
3] = 'G';
        
// We fill the buffer with numbers for the payload 
        for( i = 4; i < BufferSize; i++ )
        {
          Buffer[i] = i - 
4;
        }
        
        Radio->SetTxPacket( Buffer, BufferSize );
      }
    }
    break;
  case RF_TX_DONE:
    
// Indicates on a LED that we have sent a PONG
    LedToggle( LED_RED );
    Radio->StartRx( );
    break;
  
default:
    break;
  }
}