文章先从LoRa的物联网通信技术前辈们讲起,慢慢引出了这种功耗又低,距离又远的无线通信技术,然后又似庖丁解牛一般,从物理层到链路层,详细的介绍了LoRa这种技术的组成,最后以一种实际的原理与嵌入式软件实现,让读者近距离接触到基于LoRa这种无线通信技术产品的开发过程。总而言之,博主在这一篇文章中集中的介绍了物联网无线通信技术-LoRa的前世今生,帮助各位对这门“新”的无线通信技术有一个全面且直观的了解。
文章目录
LoRa技术前序
LoRa技术简介
LoRa应用
LoRa系统架构
LoRaWAN
LoRa通信物理层
LoRa调制与解调
LoRa 编码与解码
STM32+SX1268实现LoRa
实现原理
嵌入式程序
参考文献
LoRa之前的主要无线通信协议分为一下三种:
上面三类技术大都要求较高的信噪比,并且对障碍的穿透性较小,无法在复杂环境中实现远距离低功耗传输。低功耗广域网有效的弥补了现有物联网连接方法的不足,成为支持物联网连接的重要基础,得到了国内外的广泛关注,并成为了国内外的研究和应用前沿。
LPWAN (Low Power Wide Area Network)指的是低功耗广域网,其特点在于极低功耗,长距离以及海量连接,适用于物联网万物互联的场景。LPWAN不只是一种技术,而是代表了一族有着各种形式的低功耗广域网技术,如下图所示。其中LoRa使用的是一种扩频技术,NB-IoT使用的是窄带技术,这是两种有代表性的低功耗广域网技术。
LoRa 是 Long Range Communication的简称,我们可以从三个不同的角度来理解LoRa这门技术。从而获得对LoRa这么技术完整的理解。
LoRa作为目前广泛使用的低功耗广域网技术(LPWAN),为低功耗物联网设备提供了可靠的连接方案。 如下图所示,相比于Wi-Fi、蓝牙、ZigBee等传统无线局域网,LoRa可以实现更远距离的通信,有效扩展了网络的覆盖范围; 而相比于移动蜂窝网络,LoRa具有更低的硬件部署成本和更长的节点使用寿命,单个LoRa节点可以在电池供电的情况下连续工作数年。 LoRa具有低数据率、远距离和低功耗的性质,因此非常适合与室外的传感器及其他物联网设备进行通信或数据交互。
考虑到LoRa在覆盖距离、部署成本等方面的巨大优势,近年来LoRa在全球范围内进行了大量的应用部署,在智能仪表(如智能水表、智能电表)、智慧城市、智能交通数据采集、野生动物监控等众多物联网场景中都可以看到LoRa的应用。例如LoRa通信模块与传统的水质传感器进行连接,从而使用户可以数十公里外远程监控饮用水在输送过程中的水质变化情况。而在荷兰的KPN项目中,工程人员通过广泛部署LoRa网关,实现LoRa网络全覆盖,为智慧运输、智能农业、智慧路灯等具体应用提供了通信支持。
现在常用的LoRa架构由节点、网关及服务器所组成,各部分的关系如下图所示。 LoRa节点与网关之间采用单跳直接连接,这一阶段的物理层使用线性扩频调制(Chirp Spreading Spectrum, CSS),MAC层(Media Access Control,媒体访问控制)通常使用LoRaWAN协议。
网关收到数据包后,对数据包信号进行解码,并将解码结果通过蜂窝或有线网络传输给网络服务器,这一阶段使用传统的TCP/IP进行传输,同时网络服务器与网关之间的交互仍然遵守LoRaWAN协议。 网络服务器汇总多个网关的数据,过滤重复的数据包,执行安全检查,并根据内容将数据发送至不同的应用服务器,供用户读取和使用,这一阶段也使用TCP/IP和SSL进行传输和加密。
在LoRa网络中,会有很多LoRa节点向同一网关发送数据,这就需要MAC协议来协调不同节点间的数据传输,在LoRa中比较典型的MAC协议就是开源的LoRaWAN。 LoRaWAN是由LoRa联盟在LoRa物理层编码技术的基础上提出的MAC层协议,由LoRa联盟负责维护。LoRaWAN规范1.0版本于2015年6月发布。LoRaWAN协议主要规定了节点与网关、网关与服务器之间的连接规范,确定了LoRa网络的星型拓扑结构。受LoRa节点成本和能耗的限制,现有的LoRaWAN协议基本采用纯ALOHA机制,即节点在发送数据前不进行载波侦听,也就是没有使用CSMA/CA,而是随机选择时间进行发送。
LoRaWAN定义了网络的通信协议和系统架构,还负责管理所有设备的通信频率,数据速率和功率。 在LoRaWAN的控制下,网络中的所有设备可以是异步的,并在只有可用数据时进行传输。 针对不同的应用场景,LoRaWAN定义了三种节点运行模式,分别是Class A(ALL)、Class B(Beacon)、Class C(Continuously Listening):
三种网络模式中,Class A是所有LoRa网络都必须支持的模式,也是最常用的网络模式。这三个模式设计并不复杂,其实就是在网络灵活性、可用性和节能之间的一个平衡。Class A最节能,但是灵活性相对较低,例如下行数据只能依赖于上行数据的时间。Class C最耗电,但是也是上行和下行数据发送最灵活的。
我们来介绍LoRa通信的基本原理,包括调制、解调、编码和解码,着重于物理层协议的分析,关于上层协议(如LoRaWAN),有很多其他的资料和开源实现供读者学习[1][2],这里就不详述了。
需要说明的是,LoRa物理层是一个商用的私有协议,并没有完整公开的协议说明,因而已有的一些LoRa实现[3][4][5][6][7]都是依照Semtech公司的相关专利和文件猜出来的。[6]MATLAB版本用于原型验证和离线操作,[7]基于GNURadio平台的C++版本则是一个实时的高性能LoRa实现。很多对LoRa的说法只是基于大家的观察和理解,同时很多LoRa代码实现的性能是很差的,包括不少研究论文中使用的LoRa代码,实际性能也存在着很大的问题。
相关论文的介绍见[8]: Zhenqiang Xu, Pengjin Xie, Jiliang Wang. "Pyramid: Real-Time LoRa Collision Decoding withPeak Tracking", IEEE INFOCOM 2021. [PDF]
在这节我们介绍LoRa的调制与解调,也即如何在物理波形和比特数据之间进行转换。
LoRa 使用 CSS (Chirp Spread Spectrum)线性扩频调制,频率线性扫过整个带宽,因此抗干扰极强,对多径和多普勒效应的抵抗也很强。LoRa的基本通信单元是linear chirp,也即频率随时间线性增加(或减小)的信号。我们将频率随着时间线性增加的chirp符号叫做upchirp,将频率随着时间线性减小的chirp符号叫做downchirp。如下图分别从时域波形和时频域展示了一个upchirp的图像:
一个chirp怎么编码数据呢?LoRa的做法是通过在频域循环平移chirp进行数据的编码,不同的起始频率代表不同的数据。如下图所示,在带宽B内四等分标定四个起始频率,我们可以得到4种类型的符号,分别表示00,01,10,11。所以在接收端,只需要将这个起始频率计算出来,就可以计算出每一个chirp对应的比特数据。我们将下图(a)所示从最低频率扫频到最高频率的chirp符号称为basic upchirp。
LoRa规定了一个参数SF(Spreading Factor,扩频因子),如上图所示,在带宽一定的情况下,扩频因子的增加意味着采样时长的增加(扫频速度减低)。可以看出,提高SF,虽然在相同时间减少了可以传输的实际数据,但是这样扩频后传输可以降低误码率,灵敏度越高,可以得到更远的传输距离。
LoRa解调过程,实质就是求出chirp符号的起始频率,其做法通常是这样的:首先将收到的基带upchirp信号与downchirp点乘,化为单频信号,这一操作叫做dechirp(解扩频)。
Dechirp之后,对得到的信号进一步做FFT(快速傅里叶变换),即可在频域获得一个峰值,这个峰值位置对应的频率即是起始频率,我们因此得到对应的SF个比特,然后根据所有SF个比特,来判断实际接收Bit数据为1还是0。如下图所示。
一个完整的LoRa数据包结构包含三个部分:前导码(Preamble)、SFD(Start Frame Delimiter)和数据部分(Data)。 前导码包含6~65535个basic upchirp和两个标识网络号的其他chirp符号。接着是2.25个basic downchirp,作为SFD标识数据段的开始。后面的数据段则包含着若干编码了数据的data chirp。
当我们使用软件无线电设备(Software-defined radio, SDR)接收一段LoRa设备发出的信号,并用inspectrum这个软件(其他可画时频图的软件或代码也可以)把信号的时频图画出来,那么它大概会是如下样子:
LoRa物理层编码过程如下:
LoRa采用循环纠错编码(CRC)进行前向错误检测与纠错,但会产生传输开销。编码率越大前向纠错越强,链路抗干扰性越强,但是传输开销将会加大,进而加大传输时间。
LoRa物理层解码过程与编码相反,如下:
补充说明一下:在仅仅增加带宽的情况下,能有效的提高传输速率。下图是带宽与SF对接收数据dbm底线以及传输速率的对应数据。
MCU选择STM81L101F3P6S实现超低功耗,其与LoRa射频芯片SX1268之间采用SPI通信接口。
下面的嵌入式程序为main函数,主要实现了三个部分功能的调用实现。
oid main(void)
{
u8 ExtiDelay = 0;
u8 Txbuffer[5] = {0x11,0x22,0x33,0x44,0x99};
CLK_MasterPrescalerConfig(CLK_MasterPrescaler_HSIDiv1); //1分频,16MHz
//串口初始化
USART1_Init();
//SX126x初始化
Sx1276M_GpioInt();
CS_LOW;
delayxms(5);
CS_HIGH;
RESET_HIGH;
reset_sx1262();//reset RF
sx1262_Config();//频率431.5M
Rx_Init();//接收模式
//按键初始化
GPIO_Init(GPIOB, GPIO_Pin_1, GPIO_Mode_In_FL_No_IT);//PB1按键
while(1)
{
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1))
{
ExtiDelay++;
delayxms(1);
if(ExtiDelay > 10)
{//消抖
ExtiDelay = 0;
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1))
{
KeyTrigger_Flag = 0;
LORA_TxData(Txbuffer, 5);//LORA发送数据
delayxms(30);//等待一会再接收,发射和接收不能同时进行
sx1262_Config();
Rx_Init();//切回接收模式
}
}
}
hal_Sx1268_RxHandle();//等待接收
}
}
void USART1_Init(void)
{
GPIO_Init(GPIOC, GPIO_Pin_3, GPIO_Mode_Out_PP_High_Fast);
GPIO_Init(GPIOC, GPIO_Pin_2, GPIO_Mode_In_PU_No_IT);
CLK_PeripheralClockConfig(CLK_Peripheral_USART,ENABLE);//使能串口外设时钟
USART_Init(115200, //波特率115200
USART_WordLength_8D, //数据位8
USART_StopBits_1, //停止位1
USART_Parity_No, //无奇偶校验
USART_Mode_Tx|USART_Mode_Rx);//USART初始化
USART_ITConfig(USART_IT_RXNE,ENABLE);//使能接收中断
USART_Cmd(ENABLE);//使能USART
//USART1_SendStr("usart_tx is ok");
}
void hal_Sx1268_RxHandle(void)
{
if(IRQ_DATABIT)//Wait for the IRQ RxDone or Timeout
{
Irq_Status = GetIrqStatus();//0x02
if((Irq_Status&0x02) == RxDone_IRQ)
{
GetRxBufferStatus(&packet_size, &buf_offset); //read rx packet_size
if(packet_size>0)
{//接收长度大于0
ReadBuffer(buf_offset, &rxbuf[0], packet_size); //read rx data
USART1_SendString(&rxbuf[0], packet_size);//串口打印接收数据
ClearIrqStatus(RxDone_IRQ);//清除中断
}
else
{
ClearIrqStatus(RxDone_IRQ);
}
Rx_Init();
}
}
}
下面是SX1628芯片的驱动代码,包括初始化,发送模式接收配置,数据发送等具体的接口实现。
/*
* THE FOLLOWING FIRMWARE IS PROVIDED: (1) "AS IS" WITH NO WARRANTY; AND
* (2)TO ENABLE ACCESS TO CODING INFORMATION TO GUIDE AND FACILITATE CUSTOMER.
* CONSEQUENTLY, SEMTECH SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT OR
* CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT
* OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION
* CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* Copyright (C) SEMTECH S.A.
*/
#include "stm8l10x.h"
#include "sx1268-LoRa.h"
#include "string.h"
void Sx1276M_GpioInt();
u8 gb_SF;
u8 gb_BW;
u8 CR; //LR_RegModemConfig1
#define CRC 0x01 //CRC Enable
#define DATA_LEN (13)
/**********************************************************
**Parameter table define
**********************************************************/
//__root const u16 SX1276FreqTbl[3] = {0x066C, 0x0780, 0x0800}; //434MHz
__root const u16 SX1276FreqTbl[3] = {0x06D9, 0x0700, 0x0800}; //868MHz @ 26m
__root const u16 SX1276PowerTbl[4] =
{
0x09FF, //20dbm
0x09FC, //17dbm
0x09F9, //14dbm
0x09F6, //11dbm
};
__root const u8 SX1276LoRaBwTbl[10] =
{// 0 1 2 3 4 5 6 7 8 9
//7.8KHz,10.4KHz,15.6KHz,20.8KHz,31.2KHz,41.7KHz,62.5KHz,125KHz,250KHz,500KHz
0,1,2,3,4,5,6,7,8,9
};
__root const u8 SX1276SpreadFactorTbl[7] =
{
6,7,8,9,10,11,12
};
u8 SX1276Data[11];
u8 gb_RxData[256]; //Receive data buffer
#if 1 //sx1268 Aloma
#define payload_length 13
#define payload_length1 11
#if 0
u8 txbuf[payload_length] = {'s','e','n','d','_','t','e','s','t'};
#else
u8 txbuf[payload_length];
#endif
u8 txbuf_1[payload_length1] = {'r','e','t','u','r','n','_','t','e','s','t'};
u8 rxbuf[200];
u8 packet_size;
u8 buf_offset;
u8 Irq_Status;
u8 tx_buff_flag;
extern u16 Lux_Data;
extern u8 STM8S_ID[12];
extern u8 FrameID;
#endif
void delayms(unsigned int t)
{
unsigned int i;
unsigned char j;
for(i=0;i5) //超时120ms复位
{
busy_timecnt=0;
//SetStandby(0); //0:STDBY_RC; 1:STDBY_XOSC
reset_sx1262(); //reset RF
//sx1262_Config();
//Rx_Init();
break;
}
}
}
void SetSleep(void)
{
u8 Opcode,sleepConfig;
check_busy();
Opcode = SET_SLEEP; //0x84
sleepConfig = 0x00; //bit2: 1:warm start; bit0: 0: RTC timeout disable
CS_LOW;
spi_rw(Opcode);
spi_rw(sleepConfig);
CS_HIGH;
}
//0:STDBY_RC; 1:STDBY_XOSC
void SetStandby(u8 StdbyConfig)
{
u8 Opcode;
check_busy();
Opcode = SET_STANDBY; //0x80
CS_LOW;
spi_rw(Opcode);
spi_rw(StdbyConfig);
CS_HIGH;
}
void SetTx(u32 timeout)
{
u8 Opcode;
u8 time_out[3];
check_busy();
Opcode = SET_TX; //0x83
time_out[0] = (timeout>>16)&0xFF;//MSB
time_out[1] = (timeout>>8)&0xFF;
time_out[2] = timeout&0xFF;//LSB
CS_LOW;
spi_rw(Opcode);
spi_rw(time_out[0]);
spi_rw(time_out[1]);
spi_rw(time_out[2]);
CS_HIGH;
}
void SetRx(u32 timeout)
{
u8 Opcode;
u8 time_out[3];
check_busy();
Opcode = SET_RX; //0x82
time_out[0] = (timeout>>16)&0xFF;//MSB
time_out[1] = (timeout>>8)&0xFF;
time_out[2] = timeout&0xFF;//LSB
CS_LOW;
spi_rw(Opcode);
spi_rw(time_out[0]);
spi_rw(time_out[1]);
spi_rw(time_out[2]);
CS_HIGH;
}
//0:GFSK; 1:LORA
void SetPacketType(u8 PacketType)
{
u8 Opcode;
check_busy();
Opcode = SET_PACKET_TYPE; //0x8A
CS_LOW;
spi_rw(Opcode);
spi_rw(PacketType);
CS_HIGH;
}
u8 GetPacketType(void)
{
u8 Opcode;
u8 Status;
u8 packetType;
check_busy();
Opcode = 0x11;
CS_LOW;
spi_rw(Opcode);
Status = spi_rw(0xFF);
packetType = spi_rw(0xFF);
CS_HIGH;
return packetType;
}
//RF_Freq = freq_value * 32M / (2^25) -----> freq_value = (RF_Freq * (2^25)) / 32M
//431.5M : freq_value = (431.5M * (2^25)) / 32M = 452460544 = 0x1AF80000
//868M : freq_value = (868M * (2^25)) / 32M = 910163968 = 0x36400000
void SetRfFrequency(void)
{
u8 Opcode;
u8 Rf_Freq[4];
u32 RfFreq = 0;
//RfFreq = 0x1B200096;//434M
RfFreq = 0x1AF80000;//431.5M : freq_value = (431.5M * (2^25)) / 32M = 452460544 = 0x1AF80000
check_busy();
Opcode = SET_RF_FREQUENCY; //0x86
Rf_Freq[0] = (RfFreq>>24)&0xFF;//MSB
Rf_Freq[1] = (RfFreq>>16)&0xFF;
Rf_Freq[2] = (RfFreq>>8)&0xFF;
Rf_Freq[3] = RfFreq&0xFF;//LSB
CS_LOW;
spi_rw(Opcode);
spi_rw(Rf_Freq[0]);
spi_rw(Rf_Freq[1]);
spi_rw(Rf_Freq[2]);
spi_rw(Rf_Freq[3]);
CS_HIGH;
}
void SetPaConfig(void)
{
u8 Opcode;
check_busy();
Opcode = 0x95;
CS_LOW;
spi_rw(Opcode);
spi_rw(0x04); //paDutyCycle//22dbm
spi_rw(0x07); //hpMax:0x00~0x07; 7:22dbm
spi_rw(0x00); //deviceSel: 0: SX1262; 1: SX1261
spi_rw(0x01);
CS_HIGH;
}
void SetRegulatorMode(void)
{
u8 Opcode;
check_busy();
Opcode = 0x96;
CS_LOW;
spi_rw(Opcode);
spi_rw(0x01);//regModeParam
CS_HIGH;
}
void WriteRegister(u16 address, u8 *data, u8 length)
{
u8 Opcode;
u8 addr_l,addr_h;
u8 i;
if(length<1)
return;
check_busy();
addr_h = address>>8;
addr_l = address&0xff;
Opcode = 0x0D;
CS_LOW;
spi_rw(Opcode);
spi_rw(addr_h);//MSB
spi_rw(addr_l);//LSB
for(i=0;i>8;
Opcode = 0x1D;
CS_LOW;
spi_rw(Opcode);
spi_rw(addr_h);//MSB
spi_rw(addr_l);//LSB
spi_rw(0x00); //20190809 fix
for(i=0;i>8;
prea_len_l = prea_len&0xFF;
invertIQ = LORA_IQ_NORMAL;
CS_LOW;
spi_rw(Opcode);
spi_rw(prea_len_h);//PreambleLength MSB
spi_rw(prea_len_l);//PreambleLength LSB
spi_rw(0x00);//HeaderType 0:Variable,explicit 1:Fixed,implicit
//spi_rw(0x01);
spi_rw(payload_len);//PayloadLength: 0x00 to 0xFF
spi_rw(0X01);//CRCType 0:OFF 1:ON
spi_rw(invertIQ);//InvertIQ 0:Standard 1:Inverted
spi_rw(0XFF);//
spi_rw(0XFF);//
spi_rw(0XFF);//
CS_HIGH;
// WORKAROUND - Optimizing the Inverted IQ Operation, see DS_SX1261-2_V1.2 datasheet chapter 15.4
if( invertIQ == LORA_IQ_INVERTED )
{
// RegIqPolaritySetup = @address 0x0736
ReadRegister(0x0736,data_buf,1);
data_buf[0] = data_buf[0] & ~( 1 << 2 );
WriteRegister( 0x0736,data_buf,1);
}
else
{
// RegIqPolaritySetup @address 0x0736
ReadRegister(0x0736,data_buf,1);//0x0D
data_buf[0] = data_buf[0] | ( 1 << 2 );
WriteRegister(0x0736,data_buf,1);
}
// WORKAROUND END
}
void SetDioIrqParams(u16 irq)
{
u8 Opcode;
u16 Irq_Mask;
u8 Irq_Mask_h,Irq_Mask_l;
u16 DIO1Mask;
u8 DIO1Mask_h,DIO1Mask_l;
u16 DIO2Mask;
u8 DIO2Mask_h,DIO2Mask_l;
u16 DIO3Mask;
u8 DIO3Mask_h,DIO3Mask_l;
Irq_Mask = irq;
DIO1Mask = irq;
DIO2Mask = 0;
DIO3Mask = 0;
Irq_Mask_h = Irq_Mask>>8;
Irq_Mask_l = Irq_Mask&0xFF;
DIO1Mask_h = DIO1Mask>>8;
DIO1Mask_l = DIO1Mask&0xFF;
DIO2Mask_h = DIO2Mask>>8;
DIO2Mask_l = DIO2Mask&0xFF;
DIO3Mask_h = DIO3Mask>>8;
DIO3Mask_l = DIO3Mask&0xFF;
Opcode = 0x08;
check_busy();
CS_LOW;
spi_rw(Opcode);
spi_rw(Irq_Mask_h);//Irq_Mask MSB
spi_rw(Irq_Mask_l);//Irq_Mask LSB
spi_rw(DIO1Mask_h);//
spi_rw(DIO1Mask_l);//
spi_rw(DIO2Mask_h);//
spi_rw(DIO2Mask_l);//
spi_rw(DIO3Mask_h);//
spi_rw(DIO3Mask_l);//
CS_HIGH;
}
u16 GetIrqStatus(void)
{
u8 Opcode;
u8 Status;
u16 IrqStatus;
u8 temp;
check_busy();
Opcode = 0x12;
CS_LOW;
spi_rw(Opcode);
Status = spi_rw(0xFF);
temp = spi_rw(0xFF);
IrqStatus = temp;
IrqStatus = IrqStatus<<8;
temp = spi_rw(0xFF);
IrqStatus = IrqStatus|temp;
CS_HIGH;
return IrqStatus;
}
void ClearIrqStatus(u16 irq)
{
u8 Opcode;
u16 irq_h,irq_l;
check_busy();
irq_h = irq>>8;
irq_l = irq&0xFF;
Opcode = 0x02;
CS_LOW;
spi_rw(Opcode);
spi_rw(irq_h);
spi_rw(irq_l);
CS_HIGH;
}
void SetDIO2AsRfSwitchCtrl(void)
{
u8 Opcode;
check_busy();
Opcode = 0x9D;
CS_LOW;
spi_rw(Opcode);
spi_rw(0x01); //DIO2 is selected to be used to control an RF switch; DIO2 = 1 in TX mode
CS_HIGH;
}
#define DIO3_1_6V 0x00
#define DIO3_1_7V 0x01
#define DIO3_1_8V 0x02
#define DIO3_2_2V 0x03
#define DIO3_2_4V 0x04
#define DIO3_2_7V 0x05
#define DIO3_3_0V 0x06
#define DIO3_3_3V 0x07
void SetDIO3AsTCXOCtrl(uint8_t tcxoVoltage)
{
u8 Opcode;
check_busy();
Opcode = 0x97;
CS_LOW;
spi_rw(Opcode);
spi_rw(tcxoVoltage); //
spi_rw(0x00); //Timeout MSB ; Timeout duration = Timeout *15.625 μs
spi_rw(0x00);
spi_rw(0x64); //Timeout LSB
CS_HIGH;
}
void ClearDeviceErrors(void)
{
u8 Opcode;
check_busy();
Opcode = 0x07;
CS_LOW;
spi_rw(Opcode);
spi_rw(0x00);
spi_rw(0x00);
CS_HIGH;
}
void sx1262_Config(void)
{
u8 bw_temp;
u8 data_buf[2];
SetStandby(0);//0:STDBY_RC; 1:STDBY_XOSC
SetRegulatorMode();
SetPaConfig();
/*************************************
** Uncomment below two lines if you **
** used the SX1262 module of nicerf,**
** keep comment if SX1268 used. **
**************************************/
/*SetDIO3AsTCXOCtrl(DIO3_1_8V);*/
/*ClearDeviceErrors();*/
SetDIO2AsRfSwitchCtrl();
SetPacketType(1); //0:GFSK; 1:LORA
SetRfFrequency(); //设置频率
SetTxParams(22,SET_RAMP_10U); //set power and ramp_time
bw_temp = LORA_BW_500;//9501
//bw_temp = LORA_BW_125;//4338: SF7, LORA_BW_125 9501: SF8, LORA_BW_500
SetModulationParams(SF8, bw_temp, LORA_CR_4_5, LDRO_ON);//9501速率
// WORKAROUND - Modulation Quality with 500 kHz LoRa mode Bandwidth, see DS_SX1261-2_V1.2 datasheet chapter 15.1
if(bw_temp == LORA_BW_500)
{
// RegTxModulation = @address 0x0889
ReadRegister(0x0889,data_buf,1);//0x04
data_buf[0] = data_buf[0] & ~( 1 << 2 );
WriteRegister(0x0889,data_buf,1);
}
else
{
// RegTxModulation = @address 0x0889
ReadRegister(0x0889,data_buf,1);
data_buf[0] = data_buf[0] | ( 1 << 2 );
WriteRegister(0x0889,data_buf,1);
}
// WORKAROUND END
SetPacketParams(payload_length);//PreambleLength;HeaderType;PayloadLength;CRCType;InvertIQ
}
void LORA_TxData(unsigned char *data, unsigned char len)
{
//unsigned char busy_timecnt;
//u8 i;
u8 busy_timecnt = 0;
SetStandby(0);//0:STDBY_RC; 1:STDBY_XOSC
//SetBufferBaseAddress(0,0);//(TX_base_addr,RX_base_addr)
//if(tx_buff_flag==0)
WriteBuffer(0,data,len);//(offset,*data,length)
SetPacketParams(len);//PreambleLength;HeaderType;PayloadLength;CRCType;InvertIQ
//else
//{
//WriteBuffer(0,txbuf_1,payload_length1);//(offset,*data,length)
//SetPacketParams(payload_length1);//PreambleLength;HeaderType;PayloadLength;CRCType;InvertIQ
//}
SetDioIrqParams(TxDone_IRQ);//TxDone IRQ
//Define Sync Word value
SetTx(0);//timeout = 0
//Wait for the IRQ TxDone or Timeout
#if 1
while(!IRQ_DATABIT)
{
delayxms(1);//if time out, reset the module//超时120ms复位//复位时间与发送数据的长度有关
busy_timecnt++;
if(busy_timecnt>60)
{
busy_timecnt=0;
ClearIrqStatus(TxDone_IRQ); //Clear the IRQ TxDone flag
SetStandby(0); //0:STDBY_RC; 1:STDBY_XOSC
reset_sx1262(); //reset RF
sx1262_Config();
break;
}
}
busy_timecnt=0;
ClearIrqStatus(TxDone_IRQ); //Clear the IRQ TxDone flag
SetStandby(0); //0:STDBY_RC; 1:STDBY_XOSC
reset_sx1262(); //reset RF
sx1262_Config();
#else
delayxms(30);//发送数据的长度越长,延时时间越长。发送速率越低,延时时间越长。
if(!IRQ_DATABIT)
{
ClearIrqStatus(TxDone_IRQ); //Clear the IRQ TxDone flag
SetStandby(0); //0:STDBY_RC; 1:STDBY_XOSC
reset_sx1262(); //reset RF
sx1262_Config();
}
#endif
Irq_Status = GetIrqStatus();//0x8C
ClearIrqStatus(TxDone_IRQ);//Clear the IRQ TxDone flag
//UART1_printf("Irq_Status=%d\r\n",Irq_Status);
//Irq_Status = GetIrqStatus();
}
void Rx_Init(void)
{
//SetBufferBaseAddress(0,0); //(TX_base_addr,RX_base_addr)
//SetPacketParams(payload_length); //PreambleLength;HeaderType;PayloadLength;CRCType;InvertIQ
SetDioIrqParams(RxDone_IRQ); //RxDone IRQ
SetRx(0);//timeout = 0
}
#endif
十六宿舍 原创作品,转载必须标注原文链接。
©2023 Yang Li. All rights reserved.
欢迎关注 『十六宿舍』,大家喜欢的话,给个,更多关于嵌入式相关技术的内容持续更新中。