当前位置:   article > 正文

LoRa SX1278/76驱动原理 附代码_sx1278前导码和地址

sx1278前导码和地址

LoRa 的名字是远距离无线电(Long Range Radio),作为一种线性调频扩频的调制技术,最早由法国几位年轻人创立的一家创业公司 Cycleo 推出,2012 年 Semtech 收购了这家公司,并将这一调制技术封装到芯片中,基于 LoRa 技术开发出一整套LoRa 通信芯片解决方案,包括用于网关和终端上不同款的LoRa 芯片,开启了 LoRa 芯片产品化之路。
LoRa 是低功耗局域网无线标准,它最大特点就是在同样的功耗条件下比其他无线方式传播的距离更远,实现了低功耗和远距离的统一,它在同样的功耗下比传统的无线射频通信距离扩大 3-5 倍。在实验中使用的 LoRa 模块是安信可的 Ra01,该模组用于超长距离扩频通信,其射频芯片 SX1278主要采用 LoRa™远程调制解调器,用于超长距离扩频通信,抗干扰性强,能够最大限度降低电流消耗。借助 SEMTECH 的 LoRa™专利调制技术,SX1278 具有超过-148dBm 的高灵敏度,+20dBm 的功率输出,传输距离远,可靠性高。

在这里插入图片描述

原理解释

SX1278无线串口模块LORA扩频超远距离传输

LoRa 关键参数说明

Lora 数据包由三个部分组成部分:前导码可选报头数据有效负载

在这里插入图片描述

前导码:

前导码用于保持接收机与输入的数据流同步。默认情况下,数据包含有12个符号长度的前导码。前导长度是一个可以通过编程来设置的变量,所以前导码的长度可以扩展。可以将前导码寄存器长度设置在6到65536之间来改变发送前导码长度,实际发送前导码的长度范围为6+4至65535+4个符号。接收机会定期执行前导码检测。接收机的前导码长度应与发射机一致。如果前导码长度为未知或可能会发生变化,应将接收机的前导码长度设置为最大值。

报头:

根据所选择的操作模式,可以选用两种报头。在 RegModemConfig1 寄存器上,通过设定ImplicitHeaderModeOn 位选择报头类型。

显式报头模式:

显式报头模式是默认的操作模式。在这种模式下,报头包含有效负载的相关信息,包括:

  1. 以字节数表示的有效负载长度;
  2. 前向纠错码率;
  3. 是否打开可选的 16 位负载 CRC。

报头按照最大纠错码(4/8)发送。另外,报头还包含自己的 CRC,使接收机可以丢弃无效的报头。

隐式报头模式:

在特定情况下,如果有效负载长度、编码率及 CRC 为固定或已知,则比较有效的做法是通过调用隐式报头模式来缩短发送时间。这种情况下,需要手动设置无线链路两端的有效负载长度、错误编码率及CRC。

注意:如果将扩频因子 SF 设定为 6,则只能使用隐式报头模式,下面将详细解释扩频因子。

LoRa 调制解调:

LoRa 调制解调技术(下面简称 LoRa)采用专有的调制和解调程序,将扩频调制与循环纠错编码技术结合起来,与传统的调制技术(FSK 或 OOK)相比,这种技术扩大了无线通讯链路的覆盖范围,提高了链路的鲁棒性。具有更强的抗干扰性。对同信道GMSK 干扰信号的抑制能力达到 20dB,所以 LoRa 用于频谱使用率较高的频段和混合通讯网络,方便在网络中原有的调制方案失败时扩大覆盖范围。开发人员通过调整扩频因子、调制带宽和编码率这三个关键设计参数对 LoRa 进行优化,可在链路预算、抗干扰性、频谱占用度及标称数据速率之间达到平衡。

扩频因子:

LoRa 扩采用多个信息码片来代表有效负载信息的每个位。扩频信息的发送速度称为符号速率(Rs),而码片速率与标称符号速率之间的比值即为扩频因子,其表示每个信息位发送的符号数量。负信噪比条件下信号也能正常接收,提高了的灵敏度、链路预算及覆盖范围。但是不同扩频因子之间为正交关系,因此发送端和接收端的扩频因子必须一致。

在这里插入图片描述
由上表可以看出当扩频因子为 12 时在-20dB 还能收到数据包,说明扩频因子越大灵敏度越高,发送速度越慢。

编码率:

LoRa 采用循环纠错编码进行前向错误检测与纠错,但会产生传输开销。每次传输产生的数据开销如下:

在这里插入图片描述
编码率越大前向纠错越强,链路抗干扰性越强,但是传输开销将会加大,进而加大传输时间。

信号带宽

由信号频谱图可以观察到一个信号所包含的频率成分。把一个信号所包含谐波的最高频率与最低频率
之差,即该信号所拥有的频率范围,定义为该信号的带宽。信号的频率变化范围越大,信号的带宽就越宽。

在这里插入图片描述
上表可以看出增加信号带宽,发送标称比特率越大,说明增加信号带宽可以有效提高数据速率以缩短传输时间,但会有弊端将会降低接收灵敏度,缩短传输距离。

代码说明

附LoRa SX1278/76驱动源码

SPI接口引脚的宏定义 初始化

源文件叫做 SX1276_hal.c,这个文件里包括了 LoRa 模块的硬件驱动接口,包括 SPI2 和使用到的其他 IO 口,在移植驱动的时候,只需要对这个文件修改即可。这一部分建议对着 LORA 接口电路图来看。

在这里插入图片描述

在这个文件的最前面,是接口引脚的宏定义,在做驱动移值的时候,这一部分是主要修改的对象:

#include "sx1276_hal.h"
#include "stm32f10x_it.h"
/*
 * LoRa LoRa sx1278/76驱动
 */
#define RESET_IOPORT                                GPIOA
#define RESET_PIN                                   GPIO_Pin_1

/*!
 * SX1276 SPI definitions
 */
#define NSS_IOPORT                                  GPIOB
#define NSS_PIN                                     GPIO_Pin_12

#define SPI_INTERFACE                               SPI2
#define SPI_CLK                                     RCC_APB1Periph_SPI2

#define SPI_PIN_SCK_PORT                            GPIOB
#define SPI_PIN_SCK_PORT_CLK                        RCC_APB2Periph_GPIOB
#define SPI_PIN_SCK                                 GPIO_Pin_13

#define SPI_PIN_MISO_PORT                           GPIOB
#define SPI_PIN_MISO_PORT_CLK                       RCC_APB2Periph_GPIOB
#define SPI_PIN_MISO                                GPIO_Pin_14

#define SPI_PIN_MOSI_PORT                           GPIOB
#define SPI_PIN_MOSI_PORT_CLK                       RCC_APB2Periph_GPIOB
#define SPI_PIN_MOSI                                GPIO_Pin_15

/*!
 * SX1276 DIO pins  I/O definitions
 */
#define DIO0_IOPORT                                 GPIOC
#define DIO0_PIN                                    GPIO_Pin_4

#define DIO1_IOPORT                                 GPIOB
#define DIO1_PIN                                    GPIO_Pin_11

#define DIO2_IOPORT                                 GPIOB
#define DIO2_PIN                                    GPIO_Pin_3


#define DIO3_IOPORT                                 GPIOB
#define DIO3_PIN                                    GPIO_Pin_10

#define DIO4_IOPORT                                 GPIOC
#define DIO4_PIN                                    GPIO_Pin_3

#define DIO5_IOPORT                                 GPIOB
#define DIO5_PIN                                    GPIO_Pin_4


//软件延时函数,ms级别
void Soft_delay_ms(u16 time)
{
   u16 i=0;
   while(time--)
   {
      i=7950;  //自己定义调试时间:20141116
      while(i--) ;
   }
}

//spi初始化
static void SpiInit( void )
{
    SPI_InitTypeDef SPI_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    //


    /* Enable peripheral clocks --------------------------------------------------*/
    /* Enable SPIy clock and GPIO clock for SPIy */
    RCC_APB2PeriphClockCmd( SPI_PIN_MISO_PORT_CLK | SPI_PIN_MOSI_PORT_CLK |
                            SPI_PIN_SCK_PORT_CLK, ENABLE );
    RCC_APB1PeriphClockCmd( SPI_CLK, ENABLE );

    /* GPIO configuration ------------------------------------------------------*/
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_InitStructure.GPIO_Pin = SPI_PIN_SCK;
    GPIO_Init( SPI_PIN_SCK_PORT, &GPIO_InitStructure );

    GPIO_InitStructure.GPIO_Pin = SPI_PIN_MOSI;
    GPIO_Init( SPI_PIN_MOSI_PORT, &GPIO_InitStructure );

    GPIO_InitStructure.GPIO_Pin = SPI_PIN_MISO;
    GPIO_Init( SPI_PIN_MISO_PORT, &GPIO_InitStructure );

    //禁用JTAG
		RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO, ENABLE );
    GPIO_PinRemapConfig( GPIO_Remap_SWJ_JTAGDisable, ENABLE );

    /* SPI_INTERFACE Config -------------------------------------------------------------*/
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; // 72/8 MHz
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init( SPI_INTERFACE, &SPI_InitStructure );
    SPI_Cmd( SPI_INTERFACE, ENABLE );
}


//SX127x相关初始化希要设置如下内容
//SPI片选设置为输出,并初始化SPI口
//复位角初始化为输出高电平
//DI00~5为输入(暂时只用到了DIo0和DIo1,如果不用硬件超时DIo1也可以不接)
void SX1276HALInit( void )
{
    GPIO_InitTypeDef GPIO_InitStructure;




    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
                            RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE );

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    // Configure SPI-->NSS as output
    GPIO_InitStructure.GPIO_Pin = NSS_PIN;
    GPIO_Init( NSS_IOPORT, &GPIO_InitStructure );
		GPIO_WriteBit( NSS_IOPORT, NSS_PIN, Bit_SET );
		SpiInit();

		//配置复位引脚
		GPIO_InitStructure.GPIO_Pin = RESET_PIN;
    GPIO_Init( RESET_IOPORT, &GPIO_InitStructure );
		GPIO_WriteBit( RESET_IOPORT, RESET_PIN, Bit_SET );

    // Configure radio DIO as inputs
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

    // Configure DIO0
    GPIO_InitStructure.GPIO_Pin =  DIO0_PIN;
    GPIO_Init( DIO0_IOPORT, &GPIO_InitStructure );

    // Configure DIO1
    GPIO_InitStructure.GPIO_Pin =  DIO1_PIN;
    GPIO_Init( DIO1_IOPORT, &GPIO_InitStructure );

    // Configure DIO2
    GPIO_InitStructure.GPIO_Pin =  DIO2_PIN;
    GPIO_Init( DIO2_IOPORT, &GPIO_InitStructure );

		// Configure DIO3
    GPIO_InitStructure.GPIO_Pin =  DIO3_PIN;
    GPIO_Init( DIO3_IOPORT, &GPIO_InitStructure );

		// Configure DIO4
    GPIO_InitStructure.GPIO_Pin =  DIO4_PIN;
    GPIO_Init( DIO4_IOPORT, &GPIO_InitStructure );

    // Configure DIO5
    GPIO_InitStructure.GPIO_Pin =  DIO5_PIN;
    GPIO_Init( DIO5_IOPORT, &GPIO_InitStructure );

}

//spi发送和接收函数
uint8_t SpiInOut( uint8_t outData )
{
    /* Send SPIy data */
    SPI_I2S_SendData( SPI_INTERFACE, outData );
    while( SPI_I2S_GetFlagStatus( SPI_INTERFACE, SPI_I2S_FLAG_RXNE ) == RESET );
    return SPI_I2S_ReceiveData( SPI_INTERFACE );
}

//spi片选,status=0使能(NSs拉低)status=1失能(NSs拉高)
void SpiNSSEnable( uint8_t status )
{
    GPIO_WriteBit( NSS_IOPORT, NSS_PIN, status );
}

void SX1276Write( uint8_t addr, uint8_t data )
{
    SX1276WriteBuffer( addr, &data, 1 );
}

void SX1276Read( uint8_t addr, uint8_t *data )
{
    SX1276ReadBuffer( addr, data, 1 );
}


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

    //NSS = 0;
		SpiNSSEnable(0);	//片选spi1

    SpiInOut(addr & 0x7F );

    for( i = 0; i < size; i++ )
    {
        buffer[i] = SpiInOut(0x00);//读取数据
    }

    //NSS = 1;
		SpiNSSEnable(1);
}

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

    //NSS = 0;
		SpiNSSEnable(0);

    SpiInOut(addr | 0x80 );
    for( i = 0; i < size; i++ )
    {
			SpiInOut(buffer[i]);//写入数据
    }

    //NSS = 1;
		SpiNSSEnable(1);
}

void SX1276WriteFifo( uint8_t *buffer, uint8_t size )
{
    SX1276WriteBuffer( 0, buffer, size );
}

void SX1276ReadFifo( uint8_t *buffer, uint8_t size )
{
    SX1276ReadBuffer( 0, buffer, size );
}


void SX1276SetReset( uint8_t state )
{
    GPIO_InitTypeDef GPIO_InitStructure;

    if( state == RADIO_RESET_ON )
    {
        // Configure RESET as output
		GPIO_InitStructure.GPIO_Pin = RESET_PIN;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
        GPIO_Init( RESET_IOPORT, &GPIO_InitStructure );

		// Set RESET pin to 0
        GPIO_WriteBit( RESET_IOPORT, RESET_PIN, Bit_RESET );
    }
    else
    {
		GPIO_InitStructure.GPIO_Pin =  RESET_PIN;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_Init( RESET_IOPORT, &GPIO_InitStructure );

		// Set RESET pin to 1
        GPIO_WriteBit( RESET_IOPORT, RESET_PIN, Bit_SET );

    }
}

//读取DTo0电平,返回值0低电平,1高电平
uint8_t SX1276ReadDio0(void){
	return GPIO_ReadInputDataBit( DIO0_IOPORT, DIO0_PIN );
}

//读取DTo1电平,返回值0低电平,1高电平
uint8_t SX1276ReadDio1(void){
	return GPIO_ReadInputDataBit( DIO1_IOPORT, DIO1_PIN );
}

uint8_t SX1276ReadDio2(void){
	return 0;//GPIO_ReadInputDataBit( DIO2_IOPORT, DIO2_PIN );
}

uint8_t SX1276ReadDio3(void){
	return GPIO_ReadInputDataBit( DIO3_IOPORT, DIO3_PIN );
}

uint8_t SX1276ReadDio4(void){
	return GPIO_ReadInputDataBit( DIO4_IOPORT, DIO4_PIN );
}

uint8_t SX1276ReadDio5(void){
	return ;//GPIO_ReadInputDataBit( DIO5_IOPORT, DIO5_PIN );
}

  • 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
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292

在头文件sx1276-LoRa.h里,定义有这么一个结构体tLoRaSettings,这个结构就是第三部介绍的LoRa的参数缩影,定义如下:

在这里插入图片描述

通过这个结构体定义的变量我们就可以初始化LoRa模块,在源文件sx1276-LoRa.c里,是这样初始化的:

在这里插入图片描述

也就是模块工作在 450MHz,功率是 20dB,带宽是 500kHz,扩频因子是 128,前导码长度是 10 个字节,CRC 校验是打开的等,在两个通信模块之间,这些参考必须一致,才能保证通信。

LoRa 数据发送流程

在这里插入图片描述

从上图可以看出,LoRa 发送前一直处于待机状态,在初始化 Tx 模块后,将待发送数据(Payload)写
入 FIFO,然后切换到发送状态将数据通过 LoRa 调制成信号发送出去,等到发送完成后,会产生 TxDone 中断,同时再次切换为待机状态,完成一个发送流程。

当发送完成后,根据设计要求,可以是发送状态,也可以是新的接收状态。

LoRa 数据接收流程

LoRa 接收有两种模式:单一接收连续接收
在这里插入图片描述

单一接收模式:

在这种模式下,调制解调器在给定的时间窗口内搜索前导码。如果在该时间窗口结束时还未找到前导码,则芯片会产生RxTimeout中断信号并切换回待机模式。时间窗口长度(以符号计)由RegSymbTimeout寄存器定义,必须为4(调制解调器获取前导码锁的最短时间)到1023个符号。缺省值为5。如果在时间窗口内未发现前导码,则会产生RxTimeout中断信号,同时芯片切换回待机模式
在有效负载结束时,如果负载CRC无效,则会产生RxDone中断信号及PayloadCrcError中断信号。然而,即使CRC无效,仍然可以在FIFO数据缓存中写入数据,以便后续进行处理。
RxDone中断产生后,芯片切换回待机模式。当RxDoneRxTimeout中断信号产生时,调制解调器也会自动回到待机模式。 因此,只有在数据包到达时间窗口为已知的情况下才会使用RX单一接收模式 。而在其他情况下,应使用X连续模式。

连接接收模式:

在连续接收模式下,调制解调器会持续扫描信道,以搜索前导码。每当检测到前导码时,调制解调器都会在收到数据包前对该前导码进行检测及跟踪,然后继续等待检测下一前导码。如果前导码长度超过寄存器RegPreambleMsbRegPreambleLsb设定的预计值(按照符号周期测量),则前导码会被丢弃,并重新开始前导码搜索。但在这种场景不会产生中断标志。与单一 Rx 模式相反,在连续 Rx 模式下,当产生超时中断时,设备不会进入待机模式。这时,用户必须在设备继续等待有效前导码的同时直接清除中断信号。
在实验里,基本上都是使用连接接收模式。

信道活动检测(CAD)

LoRa 使用信道检测器来检测其他 LoRa 信号,流程如下图:
在这里插入图片描述
信道活动检测模式在以尽可能高的功耗效率检测无线信道上的 LoRa 前导码,当检测到前导码的存在,
就进入接收模块。

IO 引脚中断映射

在前面的介绍中 CAD,TX,RX 都有操作完成中断,例如 CadDone 中断,TxDone 中断,RxDone 中断这些中断的配置与 RegDioMapping1RegDioMapping2 这两个寄存器有关,DIO0 到 DIO3 引脚映射RegDioMapping1,DIO4 到 DIO5 引脚映射 RegDioMapping2,具体见下表所示:

在这里插入图片描述

LoRa 状态机和接口

在源文件 sx1276-LoRa.h里,有一个处理函数 uint32_t SX1276LoRaProcess( void),在这个函数里根
据当前 LoRa 的状态(发送、接收或者 CAD 检测)决定进行不同的处理。
LoRa 的状态有以下几种:
在这里插入图片描述
在这里插入图片描述

LoRa 对外接口 API 定义,操作 LoRa 的时候,只需要使用这些 API 即可。定义在 radio.h文件里:
在这里插入图片描述
在 radio.c 里,定义了 tRadioDriver RadioDriver 这样的一个变量,应用层就是对这个变量的操作,以下是对它赋值代码
在这里插入图片描述

主函数 main()分析

首先看初始化部分,以下是它的代码:
在这里插入图片描述

第 49 行:首先取出操作 LoRa 的函数指针保存在 radio 里。
第 50 行:对 LoRa 初始化。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号