基于stm32标准库的正点原子lora模块通信(超详细)
基于stm32标准库的正点原子lora模块通信(超详细,有代码)
lora通信正点原子有参考代码【正点原子产品资料】LORA模块ATK-LORA-02资料下载和技术讨论链接-OpenEdv-开源电子网
下载好后解压,进入程序源码,随便解压一个标注库的压缩包,里面要用的一些文件我后面会提到
先讲接收吧,我是用stm32f103c8t6作为发送的,但板子没啥影响的。创建工程我就不讲了,这个接收首先要,要将串口3初始化,后面再lora初始化,当然这里面有的延时,我推荐直接将正点原子的延时文件直接引入进来。
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"//这个直接用正点原子的
#include "usart3.h"//这个根据情况改
#include "lora_app.h"//这个我把正点的简化了
#include "string.h"
#include "stdio.h"
#include "usart1.h"//这个也可以直接用正点原子的
int main(){
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init();
uart_init(115200);//串口1对应于电脑的串口助手
usart3_init(115200);//串口3对应于lora通信的串口
LoRa_Init();//lora模块的初始化
while(1){
LoRa_ReceData();
delay_ms(100);
}
}
usart3.c其实也没啥要改的,只不过stm32f103c8t6没有定时器7,所以直接选择用定时器3了,在usart3_init中
TIM3_Int_Init(99,7199); //10ms中断
USART3_RX_STA=0; //清零
TIM_Cmd(TIM3,DISABLE); //关闭定时器3
改了后同时注意将timer.c中的TIM7全部改成TIM3.
lora_app.c
这里感觉就是重点了,我的注释会用//*表示,大家可以注意看下,便于理解。这里面的printf就是串口1的打印,其实lora通信就是串口通信,这要波特率与校验位相等,两个lora其实就可以收到数据,但lora内部应该有一个比对机制,你在lora初始化是会用到lora_send_set,这个就是将自己的一些对比值通过串口3存入到自己的内部中去,然后当初始化后,lora可以接收到波特率与校验位相等另一个lora发来的数据,但是如果与set设置的值不一样,他就不会真正读入内部中去。就比如定向传输不能与透明传输通信,地址不同不能通信,信道不同也不能通信。但我在实验时发现有个小问题,一个定向传输的,另一个透明传输的,其它一样的条件下,定向可以向透明单向传输。所以知道这个原理就可以很好理解接收只要从串口3接收的数组里面读取出字符就行了,但如果你想通过接收到发来的某条语句进行点灯或者其它操作时,我建议使用strstr函数,对比是否有这条指令,因为他进入一次lora_rcedate要么是收满0x8000个数据,要么就是10ms,因为定时器3中断里面有个USART3_RX_STA|=1<<15; //标记接收完成,而10ms进入的时候有可能你发送很快,接受两次(但好像不太可能),还有就是初始化后Usart3_RX里面有内容,所以其接收数据内容不一定与你发的内容一模一样。
同时我们也可以很好理解透明传输其实与定向传输的接收方式一样的,改变他们的接收模式在接收中只要在void LoRa_Init(void)中 LoRa_CFG.mode_sta =LORA_STA_Tran;//LORA_STA_Dire;换它就行。
#include "lora_app.h"
#include "sys.h"//*这个直接用正点原子的
#include "Delay.h"
#include "usart3.h"
#include "string.h"
#include "stdio.h"
//设备参数初始化(具体设备参数见lora_cfg.h定义)
_LoRa_CFG LoRa_CFG=
{
.addr = LORA_ADDR, //设备地址
.power = LORA_POWER, //发射功率
.chn = LORA_CHN, //信道
.wlrate = LORA_RATE, //空中速率
.wltime = LORA_WLTIME, //睡眠时间
.mode = LORA_MODE, //工作模式
.mode_sta = LORA_STA, //发送状态
.bps = LORA_TTLBPS , //波特率设置
.parity = LORA_TTLPAR //校验位设置
};
//全局参数
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//设备工作模式(用于记录设备状态)
u8 Lora_mode=0;//0:配置模式 1:接收模式 2:发送模式
//记录中断状态
static u8 Int_mode=0;//0:关闭 1:上升沿 2:下降沿
//usmart支持部分
//将收到的AT指令应答数据返回给电脑串口
//mode:0,不清零USART3_RX_STA;
// 1,清零USART3_RX_STA;
void lora_at_response(u8 mode)
{
if(USART3_RX_STA&0X8000) //接收到一次数据了
{
USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0;//添加结束符
printf("%s",USART3_RX_BUF); //发送到串口
if(mode)USART3_RX_STA=0;
}
}
//lora发送命令后,检测接收到的应答
//str:期待的应答结果
//返回值:0,没有得到期待的应答结果
//其他,期待应答结果的位置(str的位置)
u8* lora_check_cmd(u8 *str)
{
char *strx=0;
if(USART3_RX_STA&0X8000) //接收到一次数据了
{
USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0;//添加结束符
strx=strstr((const char*)USART3_RX_BUF,(const char*)str);
}
return (u8*)strx;
}
//lora发送命令
//cmd:发送的命令字符串(不需要添加回车了),当cmd<0XFF的时候,发送数字(比如发送0X1A),大于的时候发送字符串.
//ack:期待的应答结果,如果为空,则表示不需要等待应答
//waittime:等待时间(单位:10ms)
//返回值:0,发送成功(得到了期待的应答结果)
// 1,发送失败
u8 lora_send_cmd(u8 *cmd,u8 *ack,u16 waittime)
{
u8 res=0;
USART3_RX_STA=0;
if((u32)cmd<=0XFF)
{
while((USART3->SR&0X40)==0);//等待上一次数据发送完成
USART3->DR=(u32)cmd;
}else {
u3_printf("%s\r\n",cmd);
}//发送命令
if(ack&&waittime) //需要等待应答
{
while(--waittime) //等待倒计时
{
delay_ms(10);
if(USART3_RX_STA&0X8000)//接收到期待的应答结果
{
if(lora_check_cmd(ack))break;//得到有效数据
USART3_RX_STA=0;
}
}
if(waittime==0)res=1;
}
return res;
}
//AUX中断设置
//mode:配置的模式 0:关闭 1:上升沿 2:下降沿
void Aux_Int(u8 mode)
{
if(!mode)
{
EXTI_InitStructure.EXTI_LineCmd = DISABLE;//关闭中断
NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
}else
{
if(mode==1)
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿
else if(mode==2)
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
}
Int_mode = mode;//记录中断模式
EXTI_Init(&EXTI_InitStructure);
NVIC_Init(&NVIC_InitStructure);
}
//LoRa模块初始化
//返回值: 0,检测成功
// 1,检测失败
u8 LoRa_Configure(void)
{
u8 retry=0;
u8 temp=1;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用功能时钟
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//禁止JTAG,从而PA15可以做普通IO使用,否则PA15不能做普通IO!!!
//*我的PA15引脚有问题,所以我直接换成PB4了,但注意换的话,要去lora_app.h中去改引脚
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; //LORA_MD0
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
// GPIO_Init(GPIOA, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //LORA_AUX
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA.4
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource4);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PA端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //LORA_MD0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
//*这个对应的是一个上升沿触发的中断,对应于PA4引脚
EXTI_InitStructure.EXTI_Line=EXTI_Line4;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发
EXTI_InitStructure.EXTI_LineCmd = DISABLE; //中断线关闭(先关闭后面再打开)
EXTI_Init(&EXTI_InitStructure);//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //LORA_AUX
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; //关闭外部中断通道(后面再打开)
NVIC_Init(&NVIC_InitStructure);
LORA_MD0=0;
LORA_AUX=0;
while(LORA_AUX)//确保LORA模块在空闲状态下(LORA_AUX=0)
{
delay_ms(500);
}
usart3_init(115200);//初始化串口3
LORA_MD0=1;//进入AT模式
delay_ms(40);
retry=3;
while(retry--)
{
if(!lora_send_cmd("AT","OK",70))
{
temp=0;//检测成功
break;
}
}
if(retry==0) temp=1;//检测失败
return temp;
}
void LoRa_Init(void)
{
delay_ms(6000);
while(LoRa_Configure())//初始化ATK-LORA-01模块
{
printf("未检测到模块!!!\n\r");
delay_ms(500);
}
printf("检测到模块!!!\n\r");
delay_ms(500);
//1.进入配置模式
LORA_MD0=1; //进入配置模式
delay_ms(40);
usart3_rx(0);//关闭串口接收
Aux_Int(0);//关闭中断
LoRa_CFG.addr = 0x10; // 0x5410
LoRa_CFG.chn = 0x10; //0x20
LoRa_CFG.power = LORA_PW_20Bbm;
LoRa_CFG.wlrate = LORA_RATE_19K2;
LoRa_CFG.wltime = LORA_WLTIME_1S;
LoRa_CFG.mode = LORA_MODE_GEN;
//*注意不同传输方式的选择
LoRa_CFG.mode_sta =LORA_STA_Tran;//LORA_STA_Dire;
LoRa_CFG.bps = LORA_TTLBPS_115200;
LoRa_CFG.parity = LORA_TTLPAR_8N1;
LoRa_Set();
}
//Lora模块参数配置
void LoRa_Set(void)
{
u8 sendbuf[20];
u8 lora_addrh,lora_addrl=0;
usart3_set(LORA_TTLBPS_115200,LORA_TTLPAR_8N1);//进入配置模式前设置通信波特率和校验位(115200 8位数据 1位停止 无数据校验)
usart3_rx(1);//开启串口3接收
while(LORA_AUX);//等待模块空闲
LORA_MD0=1; //进入配置模式
delay_ms(40);
Lora_mode=0;//标记"配置模式"
lora_addrh = (LoRa_CFG.addr>>8)&0xff;
lora_addrl = LoRa_CFG.addr&0xff;
sprintf((char*)sendbuf,"AT+ADDR=%02x,%02x",lora_addrh,lora_addrl);//设置设备地址
lora_send_cmd(sendbuf,"OK",50);
sprintf((char*)sendbuf,"AT+WLRATE=%d,%d",LoRa_CFG.chn,LoRa_CFG.wlrate);//设置信道和空中速率
lora_send_cmd(sendbuf,"OK",50);
sprintf((char*)sendbuf,"AT+TPOWER=%d",LoRa_CFG.power);//设置发射功率
lora_send_cmd(sendbuf,"OK",50);
sprintf((char*)sendbuf,"AT+CWMODE=%d",LoRa_CFG.mode);//设置工作模式x
lora_send_cmd(sendbuf,"OK",50);
sprintf((char*)sendbuf,"AT+TMODE=%d",LoRa_CFG.mode_sta);//设置发送状态
lora_send_cmd(sendbuf,"OK",50);
sprintf((char*)sendbuf,"AT+WLTIME=%d",LoRa_CFG.wltime);//设置睡眠时间
lora_send_cmd(sendbuf,"OK",50);
sprintf((char*)sendbuf,"AT+UART=%d,%d",LoRa_CFG.bps,LoRa_CFG.parity);//设置串口波特率、数据校验位
lora_send_cmd(sendbuf,"OK",50);
LORA_MD0=0;//退出配置,进入通信
delay_ms(40);
while(LORA_AUX);//判断是否空闲(模块会重新配置参数)
USART3_RX_STA=0;
Lora_mode=1;//标记"接收模式"
usart3_set(LoRa_CFG.bps,LoRa_CFG.parity);//返回通信,更新通信串口配置(波特率、数据校验位)
Aux_Int(1);//设置LORA_AUX上升沿中断
}
u8 Dire_Date[]={0x11,0x22,0x33,0x44,0x55};//定向传输数据
u8 date[30]={0};//定向数组
u8 Tran_Data[200]={0};//透传数组
//*这里是定向传输发送地址与目标信道的选择
u32 obj_addr = 0x0014;//记录用户输入目标地址
u8 obj_chn = 0x05;//记录用户输入目标信道
u8 wlcd_buff[10]={0}; //LCD显示字符串缓冲区
//Lora模块发送数据
void LoRa_SendData(char* message)
{
u16 addr;
u8 chn;
u16 i=0;
if(LoRa_CFG.mode_sta == LORA_STA_Tran)//透明传输0
{
sprintf((char*)Tran_Data,"%s",message);
u3_printf("%s\r\n",Tran_Data);
printf("Send123:%s\r\n",Tran_Data);//显示发送的数据
}else if(LoRa_CFG.mode_sta == LORA_STA_Dire)//定向传输1
{
addr = (u16)obj_addr;//目标地址
chn = obj_chn;//目标信道
date[i++] =(addr>>8)&0xff;//高位地址
date[i++] = addr&0xff;//低位地址
date[i] = chn;//无线信道
//*发送对应的地址与信道
for(i=0;i<Dire_DateLen;i++)//数据写到发送BUFF
{
date[3+i] = Dire_Date[i];
}
for(i=0;i<(Dire_DateLen+3);i++)//*发送真正的数据内容
{
while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET);//循环发送,直到发送完毕
USART_SendData(USART3,date[i]);
}
//将十六进制的数据转化为字符串打印在lcd_buff数组
sprintf((char*)wlcd_buff,"%x %x %x %x %x %x %x %x",
date[0],date[1],date[2],date[3],date[4],date[5],date[6],date[7]);
printf("send:%s",wlcd_buff);
Dire_Date[4]++;//Dire_Date[4]数据更新
}
}
u8 rlcd_buff[10]={0}; //LCD显示字符串缓冲区
//Lora模块接收数据
void LoRa_ReceData(void)
{
u16 len=0;
char msg[1024];
//有数据来了
if(USART3_RX_STA&0x8000)
{
printf("已经接收到数据了\r\n");
while(USART3_RX_BUF[len]!='\0')
{
msg[len]=USART3_RX_BUF[len];//*直接将接收到的数据存入
len++;
}
msg[len]='\0';
printf("%s",msg);
USART3_RX_STA=0;
memset((char*)USART3_RX_BUF,0x00,len);//串口接收缓冲区清0
}
}
//LORA_AUX中断服务函数
void EXTI4_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line4))
{
if(Int_mode==1)//上升沿(发送:开始发送数据 接收:数据开始输出)
{
if(Lora_mode==1)//接收模式
{
USART3_RX_STA=0;//数据计数清0
}
Int_mode=2;//设置下降沿触发
}
else if(Int_mode==2)//下降沿(发送:数据已发送完 接收:数据输出结束)
{
if(Lora_mode==1)//接收模式
{
USART3_RX_STA|=1<<15;//数据计数标记完成
}else if(Lora_mode==2)//发送模式(串口数据发送完毕)
{
Lora_mode=1;//进入接收模式
}
Int_mode=1;//设置上升沿触发
}
Aux_Int(Int_mode);//重新设置中断边沿
EXTI_ClearITPendingBit(EXTI_Line4); //清除LINE4上的中断标志位
}
}
lora_app.h
#ifndef __LORA_APP_H
#define __LORA_APP_H
#include "sys.h"
//设备参数定义
typedef struct
{
u16 addr;//设备地址
u8 chn;//信道
u8 power;//发射功率
u8 wlrate;//空中速率
u8 wltime;//休眠时间
u8 mode;//工作模式
u8 mode_sta;//发送状态
u8 bps;//串口波特率
u8 parity;//校验位
}_LoRa_CFG;
#define LORA_AUX PAin(4) //LORA模块状态引脚
#define LORA_MD0 PBout(4) //LORA模块控制引脚
//空中速率(单位:Kbps)
#define LORA_RATE_0K3 0 //0.3
#define LORA_RATE_1K2 1 //1.2
#define LORA_RATE_2K4 2 //2.4
#define LORA_RATE_4K8 3 //4.8
#define LORA_RATE_9K6 4 //9.6
#define LORA_RATE_19K2 5 //19.2
//休眠时间(单位:秒)
#define LORA_WLTIME_1S 0 //1秒
#define LORA_WLTIME_2S 1 //2秒
//工作模式
#define LORA_MODE_GEN 0 //一般模式
#define LORA_MODE_WK 1 //唤醒模式
#define LORA_MODE_SLEEP 2 //省电模式
//发射功率
#define LORA_PW_11dBm 0 //11dBm
#define LORA_PW_14Bbm 1 //14dBm
#define LORA_PW_17Bbm 2 //17dBm
#define LORA_PW_20Bbm 3 //20dBm
//发送状态
#define LORA_STA_Tran 0 //透明传输
#define LORA_STA_Dire 1 //定向传输
//串口波特率(单位:bps)
#define LORA_TTLBPS_1200 0 //1200
#define LORA_TTLBPS_2400 1 //2400
#define LORA_TTLBPS_4800 2 //4800
#define LORA_TTLBPS_9600 3 //9600
#define LORA_TTLBPS_19200 4 //19200
#define LORA_TTLBPS_38400 5 //38400
#define LORA_TTLBPS_57600 6 //57600
#define LORA_TTLBPS_115200 7 //115200
//串口数据校验
#define LORA_TTLPAR_8N1 0 //8位数据
#define LORA_TTLPAR_8E1 1 //8位数据+1位偶校验
#define LORA_TTLPAR_8O1 2 //8位数据+1位奇校验
//设备默认参数
//***//
#define LORA_ADDR 0 //设备地址
#define LORA_CHN 23 //通信信道
#define LORA_POWER LORA_PW_11dBm //发射功率
#define LORA_RATE LORA_RATE_9K6 //空中速率
#define LORA_WLTIME LORA_WLTIME_1S //休眠时间
#define LORA_MODE LORA_MODE_GEN //工作模式
#define LORA_STA LORA_STA_Tran //发送状态
#define LORA_TTLBPS LORA_TTLBPS_9600 //波特率
#define LORA_TTLPAR LORA_TTLPAR_8N1 //校验位
//*改引脚注意改掉这里
#define LORA_AUX PAin(4) //LORA模块状态引脚
#define LORA_MD0 PBout(4) //LORA模块控制引脚
#define Dire_DateLen sizeof(Dire_Date)/sizeof(Dire_Date[0])
extern _LoRa_CFG LoRa_CFG;
extern u8 Lora_mode;
void lora_at_response(u8 mode);
u8* lora_check_cmd(u8 *str);
u8 lora_send_cmd(u8 *cmd,u8 *ack,u16 waittime);
u8 LoRa_Configure(void);
void LoRa_Init(void);
void Aux_Int(u8 mode);
void LoRa_Set(void);
void LoRa_SendData(char* message);
void LoRa_ReceData(void);
#endif
说完接收,我们开始说下发送,发送我用的板子时stm32f103zet6.
main.c
就是你如果想用电脑直接发送,那就直接去usart1里面改下串口中断1,应为电脑的串口连接的是串口1,在全局变量中用下extern (extern char str123[40];extern uint8_t len_usart1;),同时在串口1中断函数里(void USART1_IRQHandler(void)),Res =USART_ReceiveData(USART1); //读取接收到的数据这句话后面加上str123[len_usart1++]=Res;if(Res=='\n')str123[len_usart1]='\0';就行。
#include "stm32f10x.h" // Device header
#include "usart3.h"
#include "lora_app.h"
#include "string.h"
#include "stdio.h"
#include "Delay.h"
#include "usart1.h"
//char str123[40];
//uint8_t len_usart1=0;
int main(){
char msg[40]="001616hello\r\n";
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init();
uart_init(115200);
usart3_init(115200);
LoRa_Init();
while(1){
/* if(str123[len_usart1]=='\0'&&len_usart1!=0){//这个是用通过电脑串口1直接发送的
sprintf(msg,"%s",str123);
len_usart1=0;
LoRa_SendData((char*)msg);
}
*/
LoRa_SendData((char*)msg);
delay_ms(1000);
}
}
发送的其实其他文件都一样的,lora_app.c也差不多一样的,唯一要区别的就是需要注意定向传输发送数据的格式,
u32 obj_addr = 0x0020;//记录用户输入目标地址
u8 obj_chn = 0x10;//记录用户输入目标信道
这两句对应的数值需要更改成与你另一个定向接受的地址一样
(lora_sendDated()函数前面)同时使用定向传输时注意发送状态的改变,内容放在(u8 Dire_Date[]={0x11,0x22,0x33,0x44,0x55};//定向传输数据)就行。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)