LoRa移植和驱动开发
好久没有写博文了,这从今还要捡起来,今天发一个最近移植的LoRa文档,粘贴出来供大家参考。里面也有一些参考别人的过程。
现有的驱动移植到STM32F0(STM32Cubemx)上。官网SX12xxx系列驱动sx12xxdrivers-v2.1.0.zip。解压后:
图1‑1 解压后库文件
开发之前先了解几个专业术语:
表 1-1专业数据表
FHSS
|
跳频扩频技术
|
FIFO
|
先进先出队列,这里代表队列寄存器
|
PA
|
功率放大器
|
LNA
|
低噪声放大器
|
SNR
|
信噪比
|
SF
|
扩频因子
|
PLL
|
锁相环
|
CAD
|
信道活动检测
|
CR
|
编码率
|
BW
|
带宽
|
RS
|
符号速率
|
Preamble
|
序头
|
文件的驱动程序接口在src下platform和radio文件夹复制到你自己的工程中,如下图:
图1‑2 LoRa库文件
在IAR工程中新建两个文件目录radio和sx12xxEiger。
在两个文件目录中添加LoRa驱动文件,
图1‑3添加驱动文件
同理在sx12xEiger中添加文件,因为我们使用的是SPI通信协议,所以选择如下:
整体移植如下:
图1‑4 工程中添加库文件
以上图为基础删除和我们芯片无关的文件(我们是sx1278),即删除sx1232和sx1272的文件。
图1‑5保留文件
移植完之后,编译,出现很多错误。需要修改里面的一些配置,因为LoRa是基于STM32F10的库做。而我们是把现有的库移植到STM32Cubemx上,可能有些库中结构体名字、头文件和函数名不一致,需要进行修改。这个自己可以慢慢修改,这里就不说了。直到编译没有错误为止。
1.1 库SPI修改
MCU和LoRa模块使用的是SPI总线方式,参考原理图查看使用那几个我们使用STM32Cubemx实现SPI驱动。然后修改LoRa库中的引脚配置(DIO0和片选线)和SPI库函数操作。
图1‑6 LoRa连接MCU引脚
如上图,在STM32Cubemx配置上图的红色框中的引脚即可,PA7作为片选配置为输出,默认拉高。其它的按SPI的引脚固定引脚配置。
图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;
}
|
修改下面函数SX1276WriteBuffer和SX1276ReadBuffer中的片选线如下面代码(改为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.3 LED灯
由于LoRa库中的LED和我们的模块的引脚不一致,需要我们根据原理图进行修改。
图1‑8 LED引脚
上面三个引脚连接到底板上发送,接收和网络3个指示灯上,在led.h和led.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 ch, FILE *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库,库资源如下:
图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发送指示灯和接收指示灯互相闪烁。当接收到数据并解析正确,则接收指示灯闪烁,发送数据完成,则发送指示灯闪烁。
下面从串口中显示测试效果:
当接收到数据包,会在串口上打印接收到的数据包。
图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;
}
}
|