1 深入了解LoRa技术原理
1.1 LoRa扩频通信原理
1.1.1 模拟无线通信:
模拟无线通信是一种使用模拟信号传输数据的通信方式。这种通信方式已经被数字无线通信所取代,因为数字通信具有更高的效率和可靠性。
-
天线:从空中接收到的无线电波转换成微弱的电信号,这些信号随后将被传送到收音机的电路中进行处理。
-
输入调谐:输入调谐电路的作用是选择要接收的频率,并将该频率的信号传送到后续电路中。通常,输入调谐电路由可调谐电感和电容构成,通过调整电感或电容的值来选择不同的频率。
-
变频(混频):变频器或混频器将输入信号与由振荡器产生的本地振荡信号混合,产生中频信号。这个步骤将原始的无线电信号转换为更易于处理的中频信号,以减少信号在处理过程中的干扰。
-
中频放大:中频放大器用于放大中频信号,以增强信号的强度和质量。放大后的信号将被传送到后续的检波器。
-
检波:检波器将中频信号转换为音频信号。常见的检波方式包括二极管检波和晶体管检波。检波过程中,中频信号的振幅调制将被解调,得到原始的音频信号。
-
前置放大:前置放大器用于进一步放大音频信号,以增加其强度,以便后续的处理。
-
功率放大:功率放大器用于将放大后的音频信号的功率增大,以便驱动扬声器产生清晰的声音输出。
1.1.2 数字无线通信:
数字无线通信的一般原理和主要技术:
-
调制与解调(调制解调器): 数字信号需要通过调制技术转换为模拟信号以便在无线介质中传输,接收端则需要通过解调技术将模拟信号转换为数字信号。常见的调制技术包括调幅(AM)、调频(FM)和调相(PM),而解调则是这些过程的逆操作。
-
多址技术: 在数字通信中,多址技术允许多个用户在同一频率范围内进行通信。常见的多址技术包括时分多址(TDMA)、频分多址(FDMA)、码分多址(CDMA)等。
-
信道编码与纠错码: 为了提高数据传输的可靠性,数字无线通信中常常采用信道编码和纠错码技术。信道编码将数据添加冗余信息,纠错码则在接收端用来检测并纠正传输过程中发生的错误。
-
自适应调制: 自适应调制技术允许根据信道条件的变化动态地调整调制方式和参数,以最大程度地提高数据传输速率和可靠性。
-
天线技术: 天线在数字无线通信中起着至关重要的作用,它们可以影响信号的传输范围、方向性以及抗干扰能力等。常见的天线技术包括定向天线、增益天线和多天线系统等。
-
安全与加密: 数字无线通信中的数据传输往往需要保证安全性,因此常常采用加密技术对数据进行保护,防止被未经授权的用户窃取或篡改。
-
网络协议: 数字无线通信通常基于各种网络协议进行数据交换,例如Wi-Fi、蓝牙、LTE等。这些协议规定了数据的格式、传输方式以及通信过程中的控制机制等。
1.1.3 无线传播方式
1.1.4 无线通信传播路径:
1.1.5 无线通信噪声:
无线通信噪声是指在无线通信过程中引入的干扰信号。这些干扰信号可能来自于自然环境、其他无线设备或通信信道本身的噪声。无线通信系统需要采取一些技术手段来抑制噪声,以保证通信质量。
1.1.6 扩频通信的由来:
扩频通信是一种利用宽带信号来传输窄带数据的通信技术。它的由来可以追溯到二战期间的军事通信系统,用于抵抗干扰和窃听。扩频通信通过将窄带信号展开到较大的频带宽度,使得信号在频域上分散,从而提高了通信系统的抗干扰性能。
1.1.7 扩频通信算法:
C是信道容量,单位为比特每秒(bps),它是在理论上可接受的误码率(BER)下允许的最大数据速率,表示通信信道所允许的信息量,也表示了所希望得到的性能。
W单位是Hz,是要求的信道带宽,是付出的代价,因为频率是一种有限的资源。
S/N是信号噪声功率比,表示周围的环境或者物理特性(例如障碍、阻塞和干扰等)。
信噪比指传播的信号的功率与传播信号的过程中参入的噪声的功率之比,也就是说,信噪比越大,抑制噪声的能力越强,声音越清晰一般这个比值用分贝(dB)表示
在信道中对于给定的信噪比要无差错发射信息,我们只需要执行基本的信号扩频操作:提高发射带宽。
1.1.8 扩频通信原理:
把用户数据和扩频数据异或,从而得到发送数据。(发送数据增加了带宽,但是提高了发射质量)
干扰信号和噪声频谱被扩展后,其谱密度降低,这就大大降低了进入信号通带内的干扰功率,使解调器的输入信噪比提高,从而提高系统的抗干扰能力。
1.2 LoRa关键技术参数
1.2.1 信号带宽(BW)7.8-500khz
增加BW,可以提高有效数据速率以缩短传输时间,但是 以牺牲部分接受灵敏度为代价。对于LoRa芯片SX127x,LoRa带宽为双边带宽(全信道带宽),而FSK调制方式的BW是指单边带宽。
1.2.2 扩频因子(SF)6-12
LoRa采用多个信息码片来代表有效负载信息的每个位,扩频信息的发送速度称为符号速率(Rs),而码片速率与标称的Rs比值即为扩频因子(SF,SpreadingFactor),表示了每个信息位发送的符号数量。
1.2.3 编码率(CR)1-4
编码率(或信息率)是数据流中有用部分(非冗余)的比例。也就是说,如果编码率是k/n,则对每k位有用信息,编码器总共产生n位的数据,其中n-k是多余的。 LoRa采用循环纠错编码进行前向错误检测与纠错。。使用该方式会产生传输开销。
LoRa关键技术参数
LoRa符号速率Rs计算: Rs=BW/(2^SF)
LoRa数据速率DR计算: DR= SF*( BW/2^SF)*CR
LoRaWAN主要使用了125kHz信号带宽设置,但其他专用协议可以利用其他的信号带宽(BW)设置。改变BW、SF和CR也就改变了链路预算和传输时间,需要在电池寿命和距离上做个权衡。
LoRa参数设置
1.3 LoRa数据收发任务
1.3.1 LoRa数据发送序列
判断是否有新的发送,没有就返回标准模式
1.3.2 LoRa数据接收序列
等待接收中断
1.3.3 Radio事件任务
2 LoRa PingPong系统设计
2.1 PingPong系统设计需求
2.2 PingPong系统通信机制
发送完要等到发送完成才能进行接收。
无线通信机制都是通过这几个状态实现的 LoRa.h
获取数据包:给到数据和大小 buffer内存拷贝
把发送缓冲器赋值到射频的里面,同时设置无线任务状态为发送初始化,就可以发送
2.3 PingPong系统业务流程
Master:判断无线模块工作状态,是否接收到pang数据,发送ping数据
Slave:和主机一样,是否接收到ping数据,发送pang数据。
2.4 LoRa参数设置
3 LoRa PingPong系统功能开发
3.1 IAR工程配置
添加主机和从机的定义
目的:3个工程公用一个代码
3.2 搭建框架
3.2.1 建立功能函数
main.c
//******************
// 函数: MLCD_SHOW
//
// 参数: 无
//
// 返回值: 无
//
// 函数描述:主机显示任务
//
//******************void MLCD_SHOW(void)
{//LCDgpio重新初始化LCD_GPIO_Init();}//******************
// 函数: SLCD_SHOW
//
// 参数: 无
//
// 返回值: 无
//
// 函数描述:从机显示任务
//
//******************void SLCD_SHOW(void)
{//LCDgpio重新初始化LCD_GPIO_Init();}//******************
// 函数: Master_Task
//
// 参数: 无
//
// 返回值: 无
//
// 函数描述:主机无线任务
//
//******************void Master_Task(void)
{}//******************
// 函数: Slave_Task
//
// 参数: 无
//
// 返回值: 无
//
// 函数描述:从机无线任务
//
//******************void Slave_Task(void)
{}
3.2.2 建立数据结构
#define BUFFERSIZE 4//ping pang数据 字符串
uint8_t PingMsg[] = "PING";
uint8_t PongMsg[] = "PONG";// 缓存区空间大小 ping pang 4个字节,
uint16_t BufferSize = BUFFERSIZE;
uint8_t Buffer[BUFFERSIZE];//设备类型判断
#ifdef MASTER
uint8_t EnbleMaster = true;
#else
uint8_t EnbleMaster = false;
#endif//数据记录
uint32_t Master_TxNumber = 0;
uint32_t Master_RxNumber = 0;uint32_t Slave_TxNumber = 0;
uint32_t Slave_RxNumber = 0;//无线收发的数据结构,来自radio.c的tRadioDriver* RadioDriverInit();
tRadioDriver *Radio = NULL;
3.3 编码
3.3.1 功能函数编码
//无线收发的数据结构,来自radio.c的tRadioDriver* RadioDriverInit();
tRadioDriver *Radio = NULL;//******************
// 函数: MLCD_SHOW
//
// 参数: 无
//
// 返回值: 无
//
// 函数描述:主机显示任务
//
//******************void MLCD_Show(void)
{uint8_t str[20] = {0}; //申请一个可变的字符串20个字节//LCDgpio重新初始化LCD_GPIO_Init(); // lcd和lora公用spi接口,spi特殊要操作miso,操作lcd要重新初始化sprintf((char*)str,"%d",Master_RxNumber); //获取到Tx NumberGui_DrawFont_GBK16(64,48,BLACK,YELLOW,str);memset((char*)str,0,strlen((const char*)str));//清除strsprintf((char*)str,"%d",Master_TxNumber);Gui_DrawFont_GBK16(64,64,BLACK,YELLOW,str);//操作完SPI重新初始化HAL_SPI_DeInit(&hspi1);MX_SPI1_Init();}//******************
// 函数: SLCD_SHOW
//
// 参数: 无
//
// 返回值: 无
//
// 函数描述:从机显示任务
//
//******************void SLCD_Show(void)
{uint8_t str[20] = {0};//LCDgpio重新初始化LCD_GPIO_Init();sprintf((char*)str,"%d",Slave_RxNumber);Gui_DrawFont_GBK16(64,48,BLACK,YELLOW,str);memset((char*)str,0,strlen((const char*)str));sprintf((char*)str,"%d",Slave_TxNumber);Gui_DrawFont_GBK16(64,64,BLACK,YELLOW,str);//SPI重新初始化HAL_SPI_DeInit(&hspi1);MX_SPI1_Init();}//******************
// 函数: Master_Task
//
// 参数: 无
//
// 返回值: 无
//
// 函数描述:主机无线任务
//
//******************void Master_Task(void)
{switch(Radio->Process()) //用swtich判断业务状态(发送完成、接收完成){case RF_RX_DONE:Radio->GetRxPacket(Buffer,&BufferSize); //接收函数获取数据printf("Master_Task:RX_____%s\n",Buffer);if(strncmp((const char*)Buffer,(const char*)PongMsg,strlen((const char*)PongMsg)) == 0) //判断是否为pong数据{LedToggle(LED_RX);Master_RxNumber++;Radio->SetTxPacket(PingMsg,strlen((const char*)PingMsg));HAL_Delay(200);}break;case RF_TX_DONE:LedToggle(LED_TX); //调用led的函数Master_TxNumber++; //发送完成计数+1Radio->StartRx(); //开启接收模式break;default :break;}
}//******************
// 函数: Slave_Task
//
// 参数: 无
//
// 返回值: 无
//
// 函数描述:从机无线任务
//
//******************void Slave_Task(void)
{switch(Radio->Process()){case RF_RX_DONE:Radio->GetRxPacket(Buffer,&BufferSize);printf("Slave_Task:RX_____%s\n",Buffer);if(strncmp((const char*)Buffer,(const char*)PingMsg,strlen((const char*)PingMsg)) == 0){LedToggle(LED_RX);Slave_RxNumber++;Radio->SetTxPacket(PongMsg,strlen((const char*)PongMsg));HAL_Delay(200);}break;case RF_TX_DONE:LedToggle(LED_TX);Slave_TxNumber++;Radio->StartRx();break;default :break;}
3.3.2 Main函数编码
```
int main(void)
{/* USER CODE BEGIN 1 */uint8_t RegVersion = 0;/* USER CODE END 1 *//* MCU Configuration----------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_ADC_Init();MX_USART1_UART_Init();MX_SPI1_Init();/* USER CODE BEGIN 2 */Lcd_Init();//Lcd_Clear(YELLOW);//Gui_DrawFont_GBK16(10,10,GREEN,RED,"hello world"); //只支持英文showimage(gImage_logo);HAL_Delay(500);Lcd_Clear(YELLOW);Gui_DrawFont_GBK16(0,0,RED,GREEN," LoRa Topology ");#ifdef MASTERGui_DrawFont_GBK16(0,16,RED,GREEN," Master ");
#elseGui_DrawFont_GBK16(0,16,RED,GREEN," Slave ");
#endifGui_DrawFont_GBK16(0,32,BLACK,YELLOW,"SSID:");Gui_DrawFont_GBK16(64,32,BLACK,YELLOW,"30");Gui_DrawFont_GBK16(0,48,BLACK,YELLOW,"RX:");//Gui_DrawFont_GBK16(64,48,BLACK,YELLOW,"255"); //剪切到MLCD和SLCD中Gui_DrawFont_GBK16(0,64,BLACK,YELLOW,"TX:");//Gui_DrawFont_GBK16(64,64,BLACK,YELLOW,"255");//剪切到MLCD和SLCD中//SPI重新初始化HAL_SPI_DeInit(&hspi1);MX_SPI1_Init();SX1276Read( REG_LR_VERSION, &RegVersion );if(RegVersion != 0x12){printf("LoRa read Error!\r\n");printf("LoRa RegVersion = %d!\r\n",RegVersion);}else{printf("LoRa read OK!\r\n");printf("LoRa RegVersion = %d!\r\n",RegVersion);}//无线任务初始化Radio = RadioDriverInit(); Radio->Init();/* USER CODE END 2 *///判断是主机,发送数据,若是从机接收数据
#ifdef MASTERRadio->SetTxPacket(PingMsg,strlen((const char*)PingMsg));printf("I am Master!\n");
#elseRadio->StartRx();printf("I am Slave!\n");
#endif/* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE */if(EnbleMaster == true){MLCD_Show();Master_Task();}else{SLCD_Show();Slave_Task();}/* USER CODE BEGIN 3 */}/* USER CODE END 3 */}
3.3.3 LoRa驱动源码修改
增加Fsk的两个代码
注释掉dio2
tLoRaSettings LoRaSettings =
{
470000000, // 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]
0, // RxSingleOn [0: Continuous, 1 Single]
0, // FreqHopOn [0: OFF, 1: ON]
4, // HopPeriod Hops every frequency hopping period symbols
1000, // TxPacketTimeout
1000, // RxPacketTimeout
4, // PayloadLength (used for implicit header mode)
};