概述
前言
最近手里有两块白嫖的ATK-Lora-01模块放着也是放着,大概长这样,决定拿出来研究一下,因为网上很少有相关的内容资料可供参考,因此爬了不少资料和代码,在这里记录一下学习过程。
ATK-Lora-01模块的配置和调试
这个模块到手之后,为了调试方便采用了正点原子的上位机软件。资料和软件均下载于正点原子的资料下载中心。
链接:LORA模块ATK-LORA-01 — 正点原子资料下载中心 1.0.0 文档 (openedv.com)
首先要确保模块的功能正常,因为懒得写代码,我直接用了一块USB转TTL的转接器把这个模块接在了笔记本上。接在开发板上也是可以的,但是需要把设置的串口数据拷贝到开发板和电脑通信的串口上(注意波特率),这里不再详细说了。直接看接线
USB转TTL | ATK-Lora-01 |
5V/3.3V均可 | VCC |
GND | GND |
TXD | RX |
RXD | TX |
无需接线 | AUX |
IO | MD0 |
这里需要注意的是这个模块有三种功能分别是配置功能,固件升级功能和通信功能,主要是依赖AUX和MD0引脚的高低电平配合来实现的。在进行固件升级的时候需要在模块上电前将AUX和MD0保持1S的高电平会进入固件升级模式,这里不多赘述。因为USB转TTL的转接头没有IO的原因,我将MD0接到了开发板上随便一个高电平的引脚(注意保持共地!即转接头的GND要连接开发板的GND)。
配置功能 | AUX=0 MD0=1 |
固件升级功能 | AUX=1 MD0=1 |
通信功能 | AUX=0 MD0=0 |
在接线完毕之后,我们只需要保持串口的设置为如图所示的样子,即模块默认的串口波特率然后点击查询配置
如果看到接收区出现了下图所示的内容,则证明模块是正常工作的,然后我们配置基本上就完成了。
快速了解ATK-Lora-01
- 模块的传输方式
- 模块在通信模式下的四种功能
具体内容请参考资料里面的介绍手册,这里不多赘述。
AUX详解:AUX在上电阶段起到和MD0搭配决定模块模式的作用,在上电之后则具有三个功能,分别是
(1)串口输出数据指示
在模块正常工作在通信模式下时
模块输出数据给MCU,AUX引脚会有上升沿电平,提示数据开始输出,这个提示一般会提早一段时间,目的是用于唤醒MCU,当AUX引脚下降沿电平,表示数据输出完毕。
MCU发送数据给模块,AUX引脚上升沿电平表示数据开始发送,当AUX下降沿电平,则表示MCU发送的数据已发送完毕。
(2)无线发射指示
模块有一个512 字节的缓冲区,MCU通过串口输出的数据都被写入到无线芯片(自动分包)
当 AUX=0 时缓冲区空,用户连续发起小于 512 字节数据,不会溢出也可以理解为 AUX=0 代表模块全部串口数据通过无线发射完毕。
当 AUX=1 时缓冲区不为空,内部 512 字节缓存区的数据,尚未全部写入到无线芯片并 开启发射,此时模块有可能在等待用户数据结束超时,或正在进行无线分包发射。
代码解析
由于原本的代码内部包含了很多用于调试的信息,因此我将其进行了简要的整合加入了一些自己的东西,并且使之更贴合我的习惯,所以只放上一些必要的代码解析。
(1)Lora引脚初始化
EXTI_InitTypeDef AUX_EXTI_InitStructure;
NVIC_InitTypeDef AUX_NVIC_InitStructure;
u8 LoRa_Init(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!!!
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);
AUX_EXTI_InitStructure.EXTI_Line=EXTI_Line4;
AUX_EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
AUX_EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发
AUX_EXTI_InitStructure.EXTI_LineCmd = DISABLE; //中断线关闭(先关闭后面再打开)
EXTI_Init(&AUX_EXTI_InitStructure);//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
AUX_NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //LORA_AUX
AUX_NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级2,
AUX_NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子优先级3
AUX_NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; //关闭外部中断通道(后面再打开)
NVIC_Init(&AUX_NVIC_InitStructure);
LORA_MD0=0;
LORA_AUX=0;
while(LORA_AUX)//确保LORA模块在空闲状态下(LORA_AUX=0)
{
printf("Lora Busy!Please Wait!rn");
Delay_ms(200);
}
Usart3_Init(115200);//初始化串口3
LORA_MD0=1;//进入AT模式
Delay_ms(40);
retry=3;
while(retry--)
{
if(!Lora_Send_Cmd("AT","OK",5))
{
temp=0;//检测成功
break;
}
Delay_ms(200);
}
if(retry==0)
temp=1;//检测失败
return temp;
}
请注意,这里我调用的printf是将printf重定向到了串口1输出,方便调试,其中调用的两个函数作用分别是USART3的初始化和发送AT命令并检查是否得到了期望的回复
USART_InitTypeDef USART_InitStructure;
void Usart3_Init(u32 bound)
{
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); //串口3时钟使能
USART_DeInit(USART3); //复位串口3
//USART3_TX PB10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB10
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB10
//USART3_RX PB11
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB11
USART_InitStructure.USART_BaudRate = bound; //波特率一般设置为9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
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); //初始化串口3
USART_Cmd(USART3, ENABLE); //使能串口
//使能接收中断
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
//使能空闲总线中断
//USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);
//设置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
}
u8 Lora_Send_Cmd(u8 *Cmd,u8 *Ack,u16 Timeout)
{
u8 res=0;
if((u32)Cmd<=0XFF)
{
while((USART3->SR&0X40)==0);//等待上一次数据发送完成
USART3->DR=(u32)Cmd;
}
else Lora_printf("%srn",Cmd);//发送命令
if(Ack&&Timeout) //需要等待应答
{
while(--Timeout) //等待倒计时
{
res = Lora_Check_Cmd(Ack);
Delay_ms(120);
}
if(Timeout==0)
res=1;
}
return res;
}
void Lora_printf(char* fmt,...)
{
u16 i,j;
va_list ap;
va_start(ap,fmt);
vsprintf((char*)Lora_Data.TxBuff,fmt,ap);
va_end(ap);
i=strlen((const char*)Lora_Data.TxBuff); //此次发送数据的长度
for(j=0;j<i;j++) //循环发送数据
{
while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET); //循环发送,直到发送完毕
USART_SendData(USART3,Lora_Data.TxBuff[j]);
}
memset((void*)Lora_Data.TxBuff,0,LORA_DATA_TX_SIZE);//清理发送缓存
}
请注意在初始化的时候最好将串口配置结构体 USART_InitTypeDef 以及EXTI_InitTypeDef和NVIC_InitTypeDef定义的变量声明为全局变量,因为在配置模式下,串口工作在固定的默认参数下,在通信模式下串口的参数则由用户指定,因此存在串口配置的切换,在通信过程中,用户也需要指定AUX的功能和状态,也存在外部中断配置的切换。关于代码中可变参数的部分不详细解释了,有兴趣的可以自行了解,大致原理是将用户输入的内容格式化输出到用户指定的接收区缓存中,然后再由串口将其发送给模块。而其中检查是否得到了期望的指令回复则是通过串口中断处理函数实现串口接收内容的缓存,然后对缓存内容调用strstr()函数实现的,也不详细贴代码了。
(2)AUX部分代码
//设备工作模式(用于记录设备状态)
u8 Lora_mode=0;//0:配置模式 1:接收模式 2:发送模式
//记录中断状态
static u8 Int_mode=0;//0:关闭 1:上升沿 2:下降沿
//AUX中断设置
//mode:配置的模式 0:关闭 1:上升沿 2:下降沿
void Aux_Int(u8 mode)
{
switch(mode)
{
case 0:
AUX_EXTI_InitStructure.EXTI_LineCmd = DISABLE;//关闭中断
AUX_NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
break;
case 1:
AUX_EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿
break;
case 2:
AUX_EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿
break;
}
if(mode)
{
AUX_EXTI_InitStructure.EXTI_LineCmd = ENABLE;
AUX_NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
}
Int_mode = mode;//记录中断模式
EXTI_Init(&AUX_EXTI_InitStructure);
NVIC_Init(&AUX_NVIC_InitStructure);
}
//LORA_AUX中断服务函数
void EXTI4_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line4))
{
if(Int_mode==1)//上升沿(发送模式:开始发送数据 接收模式:数据开始输出?接收)
{
if(Lora_mode==1)//接收模式
{
printf("准备接收rn");
//memset(Lora_Data.RxBuff,0,LORA_DATA_RX_SIZE);//清理接收缓存
//Lora_Data.Count=0;//数据计数清0
}
else if(Lora_mode==2)
printf("数据开始发送rn");
Int_mode=2;//设置下降沿触发
}
else if(Int_mode==2)//下降沿(发送:数据已发送完 接收:数据输出结束)
{
if(Lora_mode==1)//接收模式
{
printf("接收完毕rn");
Lora_Data.Transfer_over = 1;
}
else if(Lora_mode==2)//发送模式(串口数据发送完毕)
{
printf("数据发送完毕rn");
Lora_mode=1;//进入接收模式
}
Int_mode=1;//设置上升沿触发
}
Aux_Int(Int_mode);//重新设置中断边沿
EXTI_ClearITPendingBit(EXTI_Line4); //清除LINE4上的中断标志位
}
}
这里的工作内容即是通信的整个流程,首先由用户指定模块处于发送或者是接收模式,然后设定中断为上升沿触发,然后中断处理函数根据是发送和接收模式来执行上升沿触发对应的操作,随后将设置中断边沿,等待下降沿触发后再执行对应的操作。
举个例子,让我们想要发送一段数据,我们首先指定模块为发送模式,然后将其设置为上升沿触发,再往串口中写入数据,这里中断函数便会根据我们指定的模式提示我们数据开始发送,随后将中断边沿设置为下降沿触发,在数据发送完毕后,AUX引脚触发下降沿中断进入处理函数,函数就会提示我们数据发送完毕,然后再将中断边沿设置为上升沿,等待下次执行。接收数据也是同理。如果还不能理解,那么多看看上面关于AUX功能的解释,就容易理解了。这里使用printf只是为了方便理解,大家在实际应用中还是最好不要把这类东西放入中断处理函数中,毕竟中断处理的准则是快入快出...
(3)剩余流程
剩下的就是根据想要实现的功能来用上面解释的工具来随意搭配了,也没有什么难度,对于传输方式这种的,多看看资料就能很轻松的写出来了,我也不多赘述了。
最后
以上就是淡定外套为你收集整理的STM32单片机驱动ATK-Lora-01和配置学习前言ATK-Lora-01模块的配置和调试 快速了解ATK-Lora-01代码解析的全部内容,希望文章能够帮你解决STM32单片机驱动ATK-Lora-01和配置学习前言ATK-Lora-01模块的配置和调试 快速了解ATK-Lora-01代码解析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复