基于stm32的正点原子Lora模块教程-程序员宅基地

技术标签: 串口通信  物联网  嵌入式  单片机  

基于stm32的LoRa模块调试教程

初识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					//0,不接收;1,接收.

extern u8  USART3_RX_BUF[USART3_MAX_RECV_LEN]; 		//接收缓冲,最大USART3_MAX_RECV_LEN字节
extern u8  USART3_TX_BUF[USART3_MAX_SEND_LEN]; 		//发送缓冲,最大USART3_MAX_SEND_LEN字节
extern vu16 USART3_RX_STA;   						//接收数据状态

void usart3_init(u32 bound);		//串口2初始化 
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]; 			//接收缓冲,最大USART3_MAX_RECV_LEN个字节.
u8 USART3_TX_BUF[USART3_MAX_SEND_LEN]; 			//发送缓冲,最大USART3_MAX_SEND_LEN字节
u8 Temp;
//通过判断接收连续2个字符之间的时间差不大于10ms来决定是不是一次连续的数据.
//如果2个字符接收间隔超过timer,则认为不是1次连续数据.也就是超过timer没有接收到
//任何数据,则表示此次接收完毕.
//接收到的数据状态
//[15]:0,没有接收到数据;1,接收到了一批数据.
//[14:0]:接收到的数据长度
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) 				//使能定时器7的中断 
					{
    
						TIM_Cmd(TIM7,ENABLE);           //使能定时器7
					}
				}
				USART3_RX_BUF[USART3_RX_STA++]=res;	//记录接收到的值	 
			}else 
			{
    
				USART3_RX_STA|=1<<15;				//强制标记接收完成
			} 
		}
	} 


}   

USART_InitTypeDef USART_InitStructure;
//初始化IO 串口3
//pclk1:PCLK1时钟频率(Mhz)
//bound:波特率	  
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_IN_FLOATING;           //浮空输入
	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);//开启中断   
	
	//设置中断优先级
	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寄存器
	
	TIM7_Int_Init(99,7199);	//10ms中断
	USART3_RX_STA=0;		//清零
	TIM_Cmd(TIM7,DISABLE);	//关闭定时器7
}

//串口3,printf 函数 发送端LORA模块发送数据
//确保一次发送数据不超过USART3_MAX_SEND_LEN字节
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]); 
	} 
}


 
//串口接收使能控制
//enable:0,关闭 1,打开
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); //初始化串口3
   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;//0:配置模式 

extern u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;


//LORA模块发送数据
void LoRa_SendData(void)
{
        
    	u8 temp[256] = "Hello Lora !!!";

			u3_printf("%s\r\n",temp);
			
}


//Lora模块接收数据
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);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	delay_init();	    	 //延时函数初始化	  
	uart_init(115200);	 //串口初始化为115200
	usart3_init(115200);  //串口3初始化为115200 
	usart3_rx(1);//开启串口3接收
	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并使用!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_51426754/article/details/119321806

智能推荐

深度学习——优化算法_优化算法为什么要考察收敛-程序员宅基地

文章浏览阅读1.7w次,点赞5次,收藏40次。为什么需要优化算法优化算法可以加快收敛速度(未加入优化的神经网络训练时间比加入优化后时间更短),甚至得到一个更好更小的损失函数值。优化算法能帮你快速高效地训练模型。有哪些优化算法Mini-Batch 梯度下降Momentum 动量梯度下降法RMSpropAdam 提升算法其中Adam提升算法是Momentum和RMSprop两种相结合的算法,接下来我们会依次介绍这四种算法。Mini-Bat_优化算法为什么要考察收敛

highcharts柱状图+折线图_highcharts3d折线图-程序员宅基地

文章浏览阅读1.4k次。&lt;div id="container" style="min-width: 280px; height: 233px; margin: 0 auto"&gt;&lt;/div&gt;var Ri1 = parseFloat("33.3");var Ri2 = parseFloat("25.0");var Ri3 = parseFloat("44.4_highcharts3d折线图

ResultSet结果集映射给实体类集合_t t = clazz.getconstructor().newinstance(); 属性赋值-程序员宅基地

文章浏览阅读2.6k次。public static List<Object> handler(ResultSet rs, Class<?> clazz) { List<Object> list = new ArrayList<>(); Object obj = null; try { while (rs..._t t = clazz.getconstructor().newinstance(); 属性赋值

FX3/CX3 DMA Overview_adma discriptor-程序员宅基地

文章浏览阅读1k次。. DMA Socket - A DMA socket is a FX3 device construct that maps to one end of a data path into or out of the device . DMA Buffer - A DMA buffer is a memory buffer in the FX3 system memory that is assi_adma discriptor

命令行抓log-程序员宅基地

文章浏览阅读1.9k次。命令行抓log、Log.isLoggable的使用

Android WorkManager,看这一篇就够了-程序员宅基地

文章浏览阅读1.1w次,点赞4次,收藏11次。1.简介Android上有许多可延期的后台工作选项。此代码实验室涵盖WorkManager,这是一个可延迟的后台工作的兼容,灵活且简单的库。WorkManager是Android上推荐的任务调度框架,用于可延缓的工作,并且可以执行。什么是WorkManagerWorkManager是Android Jetpack的一部分,是用于后台工作的架构组件,需要兼顾机会和有保证的执行。机会..._android workmanager

随便推点

MySQL Cluster7.3 的安装与测试_mysql cluster7.3.33部署测试-程序员宅基地

文章浏览阅读132次。目录部署环境1.安装MySQL Cluster7.32.修改集群配置文件3.修改数据库配置文件4.启动管理节点5.启动数据节点6.启动管理节点7.测试部署环境centos7最小化安装,每台1CPU2G内存MySQL Cluster版本为 7.3.32(当前最新版)下载地址:https://dev.mysql.com/downloads/cluster/节点IP地址功能node1192.168.184.11管理节点、数据节点、SQL节点node2192.168.18_mysql cluster7.3.33部署测试

Swift- 嵌套类型_swift4 嵌套对象-程序员宅基地

文章浏览阅读359次。struct BlackjackCard { // nested Suit enumeration enum Suit: Character { case spades = "", hearts = "♡", diamonds = "♢", clubs = "" } //..._swift4 嵌套对象

JavaFX Pane_fxml pane-程序员宅基地

文章浏览阅读1.6k次。Pane面板类主要有如下几种:栈面板类StackPane、边界面板类BorderPane、流式面板类FlowPane、网格面板类GridePane、单行面板类HBox和单列面板类VBox等几种。ps:一个节点只能添加到一个面板中JavaFX CSSsPane.setStyle("-fx-border-color: yellow;-fx-background-color: green");bt1.setStyle("-fx-border-color: red");StackPaneStackP_fxml pane

数据结构实验六之图的邻接矩阵存储实现_实验邻接矩阵的实现-程序员宅基地

文章浏览阅读657次。源代码:#ifndef MGraph_H#define MGraph_Hconst int MaxSize=10;template&lt;class DataType&gt;class MGraph{ public: MGraph(DataType a[],int n,int e); ~MGraph(){} void DFSTraverse(int v);//深度优先遍历 void BFS..._实验邻接矩阵的实现

Java 保护Excel 工作簿和工作表_free spire.xls for java怎么引入依赖-程序员宅基地

文章浏览阅读647次。出于安全原因,你可能需要保护整个工作簿或工作表。 有时,你甚至可能还需要保护某个工作表,但却保留指定的单元格进行编辑。 本文将介绍如何使用Free Spire.XLS for Java来实现这些操作。将Spire.Xls.jar 添加为依赖项方法1:下载Free Spire.XLS for Java包并解压缩,然后从lib文件夹下,将Spire.Xls.jar包作为依赖项添加到你的Java应用..._free spire.xls for java怎么引入依赖

R语言对完全随机分组实验、拉丁方实验及正交实验进行方差分析(例题,过程+代码)_r语言拉丁方实验-程序员宅基地

文章浏览阅读5.9k次,点赞13次,收藏57次。第一题题目研究5种不同的配料(A、B、C、D、E)对某一化学过程反应时间的效应。每批新材料仅够进行5次试验。每次试验大约需要1.5小时,所以一天只能做5次试验。实验者决定用拉丁方来进行实验,使用日期和批次效应可以系统地控制。分析不同配料对反应时间的效应有无显著差异。解答读取数据并进行预处理mydata = read.csv("data.csv")head(mydata)time <- factor(time)batch <- factor(batch)ingredient_r语言拉丁方实验

推荐文章

热门文章

相关标签