初识LoRa
简单来讲,LoRa就是一种低功耗远程无线通信技术。它是基于Semtech公司SX1276/1278芯片开发的无线数传模块,这种芯片集成规模小、效率高,从而让LoRa模块拥有高接收灵敏度。那么它相比于我们常用的蓝牙和WiFi有什么优势呢?总结而言,就是低功耗、远距离、抗干扰。相同条件下,LoRa模块比WIFI模块传输距离更远。多见的WIFI、蓝牙等近距离无线通信技术,通信距离一般也就只有几十米左右。如果要覆盖某个地区一个城市的网络,部署的成本会很高,不划算。而作为低功耗广域网的LoRa技术,无线通信距离可以达到几公里,甚至十几公里,相对WIFI模块而言,距离要远得多。而这些优势,使得LoRa在现在的物联网中应用广泛,得到了很快的发展。
上手LoRa
本次教程使用的LoRa模块是正点原子的ATK-LORA-01,实物图就长这样:
刚开始拿到手一看,不就是个无线串口嘛,写一下串口的数据收发就完事了,应该很快就能调好了。可是最后前前后后调通大概花了我一天时间,这当然得归功于正点那个又臭又长的例程,和讲不明白重点的用户手册。我觉得大家用这些模块肯定是想直接就可以拿来用的,程序应该是很方便移植的那种,可是正点偏不,非要在程序里面加各种各样的显示屏、外设模块,然后写一些复复杂杂的看着就头大的程序。于是我又上网参考了一下别人的程序,结合自己的调试经验,又重新写了LoRa模块的程序,移植十分方便。
拿到一个模块,在编程之前肯定是要看的用户手册和数据手册,先要知道它要怎么用。我把正点给的资料中一些重要的地方(和编程使用模块相关的地方)贴在这里,读者如果还有其他需求可以自行查阅手册。
首先便是引脚功能描述:除了串口常见的那四个引脚外,还多了两个引脚。参考它的说明我们可以得出这两个引脚是用于配置模块通信的引脚,因为是无线串口,肯定两个模块得有相同的配置才能通信嘛。
接着就是这两个配置引脚的描述了,它关系到我们如何让模块处于不同的工作状态下:显然,当AUX和MD0引脚都为低电平时,才是模块的通信功能(即两个LoRa模块互相收发数据)。而我们在刚开始给它配对的时候,需要进入配置功能,这时候需要MD0引脚为高电平。然后我们从手册中得知,MD0、 AUX 引脚悬空下为低电平 。
这也就是说,当我们已经配对好两个模块后,我们是可以不用接MD0、AUX这两个引脚的线的,让它们悬空处于低电平两个模块就可以通信了,这样基本就和串口没什么区别了,程序也会相应地简化很多了。
那么如何配对两个模块呢?我个人的建议就是接一个USB转TTL连到电脑上,然后用正点提供的上位机去设置。这样可以不去关心那些AT指令的写法及意义,达到最快速的上手使用LoRa模块。这里连接好后修改模块基本参数配置就好,工作模式配置和发送状态先保持默认。模块参数配置里面两个模块必须都保持一致,我个人建议把通信信道、模块地址可以修改一下,这样可以减少干扰(以防万一嘛)。
一旦我们的模块配对好后,程序的编写逻辑就很简单了,就只是串口的接收和发送了。当然,我们完全可以把模块的配置之类的操作写在程序里,不过试想我们需要再连两个引脚的线,而且多写很多的逻辑控制,为什么不先把它配对好后当个串口用呢?
程序编写
这里我的目的是使stm32和电脑通过两个LoRa模块实现无线通信,并都能显示接收到和发送的数据。MCU端让LoRa使用串口3,然后将串口3接收端的数据通过串口1在电脑上打印出来。
usart3.h的编写:
#ifndef __USART3_H
#define __USART3_H
#include "sys.h"
#define USART3_MAX_RECV_LEN 1024
#define USART3_MAX_SEND_LEN 600
#define USART3_RX_EN 1
extern u8 USART3_RX_BUF[USART3_MAX_RECV_LEN];
extern u8 USART3_TX_BUF[USART3_MAX_SEND_LEN];
extern vu16 USART3_RX_STA;
void usart3_init(u32 bound);
void usart3_set(u8 bps,u8 parity);
void usart3_rx(u8 enable);
void u3_printf(char* fmt,...);
#endif
usart3.c的编写:
#include "delay.h"
#include "usart3.h"
#include "stdarg.h"
#include "stdio.h"
#include "string.h"
#include "timer.h"
extern u8 Lora_mode;
u8 USART3_RX_BUF[USART3_MAX_RECV_LEN];
u8 USART3_TX_BUF[USART3_MAX_SEND_LEN];
u8 Temp;
vu16 USART3_RX_STA=0;
void USART3_IRQHandler(void)
{
u8 res;
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
{
res =USART_ReceiveData(USART3);
if((USART3_RX_STA&(1<<15))==0)
{
if(USART3_RX_STA<USART3_MAX_RECV_LEN)
{
if(!Lora_mode)
{
TIM_SetCounter(TIM7,0);
if(USART3_RX_STA==0)
{
TIM_Cmd(TIM7,ENABLE);
}
}
USART3_RX_BUF[USART3_RX_STA++]=res;
}else
{
USART3_RX_STA|=1<<15;
}
}
}
}
USART_InitTypeDef USART_InitStructure;
void usart3_init(u32 bound)
{
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
USART_DeInit(USART3);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = bound;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART3, &USART_InitStructure);
USART_Cmd(USART3, ENABLE);
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM7_Int_Init(99,7199);
USART3_RX_STA=0;
TIM_Cmd(TIM7,DISABLE);
}
void u3_printf(char* fmt,...)
{
u16 i,j;
va_list ap;
va_start(ap,fmt);
vsprintf((char*)USART3_TX_BUF,fmt,ap);
va_end(ap);
i=strlen((const char*)USART3_TX_BUF);
for(j=0;j<i;j++)
{
while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET);
USART_SendData(USART3,USART3_TX_BUF[j]);
}
}
void usart3_rx(u8 enable)
{
USART_Cmd(USART3, DISABLE);
if(enable)
{
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
}else
{
USART_InitStructure.USART_Mode = USART_Mode_Tx;
}
USART_Init(USART3, &USART_InitStructure);
USART_Cmd(USART3, ENABLE);
}
lora.h的编写:
#ifndef __LORA_H
#define __LORA_H
#include "sys.h"
void LoRa_Process(void);
void LoRa_SendData(void);
void LoRa_ReceData(void);
void Lora_Test(void);
#endif
lora.c的编写:
#include "lora.h"
#include "sys.h"
#include "delay.h"
#include "usart3.h"
#include "string.h"
#include "stdio.h"
#include "usart.h"
#include "led.h"
#include "key.h"
u8 Lora_mode=0;
extern u8 USART_RX_BUF[USART_REC_LEN];
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
void LoRa_SendData(void)
{
u8 temp[256] = "Hello Lora !!!";
u3_printf("%s\r\n",temp);
}
void LoRa_ReceData(void)
{
u16 len=0;
if(USART3_RX_STA&0x8000)
{
len = USART3_RX_STA&0X7FFF;
USART3_RX_BUF[len]=0;
USART3_RX_STA=0;
printf("接收到的数据为");
printf("%s\r\n",USART3_RX_BUF);
}
}
void LoRa_Process(void)
{
u8 key=0;
u8 t=0;
static u8 n = 1;
while(1)
{
if(n==1)
{
printf("按下KEY0发送数据\r\n");
n++;
}
key = KEY_Scan(0);
if(key==KEY0_PRES)
{
if(n==2)
{
printf("KEY0已被按下\r\n");
LoRa_SendData();
printf("数据已被发送\r\n");
}
}
LoRa_ReceData();
t++;
if(t==20)
{
t=0;
LED1=~LED1;
}
delay_ms(10);
}
}
void Lora_Test(void)
{
u8 t=0;
u8 key=0;
while(1)
{
printf("按下KEY_UP进入数据测试\r\n");
key = KEY_Scan(0);
if(key==WKUP_PRES)
{
printf("进入数据测试\r\n");
LoRa_Process();
}
t++;
if(t==30)
{
t=0;
LED1=~LED1;
}
delay_ms(10);
}
}
main.c的编写:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "key.h"
#include "led.h"
#include "lora.h"
#include "timer.h"
#include "usart3.h"
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init();
uart_init(115200);
usart3_init(115200);
usart3_rx(1);
LED_Init();
KEY_Init();
printf("LORA模块测试程序开始\r\n");
Lora_Test();
}
在移植程序时,只需要将usart.h、usart.c、lora.h、lora.c包含进你的工程里即可。也可以只移植lora.c和lora.h然后将串口3修改为你使用的串口即可。(相比于正点那个复杂庞大的工程,我觉得这些模块还是这样好用)
结果演示
数据发送效果:
数据接收效果:
这里开两个串口助手就可以,一个用于看和MCU相连的LoRa模块的数据,一个用于看和电脑相连的LoRa模块的数据,还是很好理解的。
再见LoRa
需要完整工程代码的以及加LoRa配置代码的私聊我获取即可。助大家都能很快上手LoRa并使用!
|