STM32——定位模块ATGM336H,数据解析,提取经纬度

这篇具有很好参考价值的文章主要介绍了STM32——定位模块ATGM336H,数据解析,提取经纬度。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

模块介绍

ATGM336H定位模块支持GPS系统,BDS(北斗)系统,GLONASS(俄罗斯)系统,伽利略卫星导航系统(欧盟)。这个模块要拿到室外才能接收到信号,且初次初始化或者隔太久时间没有启用会导致获取定位信息的时间很长。
STM32——定位模块ATGM336H,数据解析,提取经纬度
可以使用中科微电子提供的集成软件设置模块,可以设置串口输出的参数,波特率等等参数。
本文介绍用STM32串口接收定位模块的数据,并将数据进行解析,解析后得到经纬度的原始数据,把经纬度原始数据转换成精确的经纬度信息后存放到数组里,可打印或继续串口传输到其他设备,后面会介绍传输到ESP32,并保存到SPIFFS中。
STM32——定位模块ATGM336H,数据解析,提取经纬度上图是模块通过串口向上位机输出的数据帧样例。

通过GNSS工具可以看到模块能够获取的数据帧有如下格式。
STM32——定位模块ATGM336H,数据解析,提取经纬度
STM32——定位模块ATGM336H,数据解析,提取经纬度其中RMC数据是最简定位信息,得到这串数据后,可以将GNRMC开始的前六项数据提取,分别是UTC时间,数据有效标志位,纬度,纬度方向,经度,经度方向。此时的经度和纬度只是原始数据,需要再转换为精确数据。

RMC的数据样例为

$GNRMC,123211.000,A,2295.33602,N,11326.27041,E,3.21,217.19,100722,,,A*7A

程序思路

数据转换:
2322.74250 格式:ddmm.mmmmm
11326.27041 格式:ddmm.mmmmm
转换成北纬:23 + 22.74250 / 60 = 23.37904
转换成东经:113 + 26.27041 / 60 = 113.43784
比较不准。。串口输出的数据就是这样的,估计芯片哪里出了问题。。
读取数据可以先用一个缓存数组接收所有的数据,先判断是不是$GNRMC开头的数据帧,如果是的话则逐个字符接收,因为数据帧是以换行符为结尾的,当读取到’\n’则是数据帧的结尾。
定义几个字符数组,用于分别存放UTC时间,经度和纬度的原始数组,经度和纬度的方向。
以逗号为分隔符切割字符串,这里我们需要用到strstr这个函数。
STM32——定位模块ATGM336H,数据解析,提取经纬度
返回指向str1中第一次出现str2的位置的指针,当str1中没有str2则返回空指针。我们可以定义一个子串指针,用这个函数在数据帧中找逗号,再用memcpy函数把数据分割后存在相应的数组。

#include "ATGM336H.h"	
#include "string.h"

#define GPS_Buffer_Length 80
#define UTCTime_Length 11
#define latitude_Length 11
#define N_S_Length 2
#define longitude_Length 12
#define E_W_Length 2 
char USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
typedef struct SaveData 
{
	char GPS_Buffer[GPS_Buffer_Length];
	
	char isGetData;		//数据获取完成标志位
	char isParseData;	//解析完成标志位
	
	char UTCTime[UTCTime_Length];		//UTC时间
	char latitude[latitude_Length];		//纬度
	char N_S[N_S_Length];		//N/S
	char longitude[longitude_Length];	//经度
	char E_W[E_W_Length];		//E/W
	
	char isUsefull;		//信息有效标志位
}GNRMC;

首先定义一个结构体存放几个数组,并宏定义数组的长度。

void ATGM_StructInit()
{
	GNRMC_Info.isGetData = false;
	GNRMC_Info.isParseData = false;
	GNRMC_Info.isUsefull = false;
	memset(GNRMC_Info.GPS_Buffer, 0, GPS_Buffer_Length);    
	memset(GNRMC_Info.UTCTime, 0, UTCTime_Length);
	memset(GNRMC_Info.latitude, 0, latitude_Length);
	memset(GNRMC_Info.N_S, 0, N_S_Length);
	memset(GNRMC_Info.longitude, 0, longitude_Length);
	memset(GNRMC_Info.E_W, 0, E_W_Length);
}

首先初始化结构体,先把结构体成员内容全部置零。

void ATGM336H_Init()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	
	ATGM_StructInit();
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	
     //USART1_TX   PA.9
	
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	
    GPIO_Init(GPIOA, &GPIO_InitStructure);
   
    //USART1_RX	  PA.10
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);  

	//配置中断通道
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ;//优先级最低
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			
	NVIC_Init(&NVIC_InitStructure);	
  
	USART_InitStructure.USART_BaudRate = 9600;
	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(USART1, &USART_InitStructure); 
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    USART_Cmd(USART1, ENABLE);                 

	ClrBuf();	//初始化
}

之后配置引脚,配置串口1中断,配置串口。引脚TX我选择PA9,RX选择PA10。TX要选择复用推挽输出,RX选择浮空输入。之后开通NVIC中断通道,配置串口波特率为9600,8位数据位,1位停止位,吴娇艳,无硬件流控制,模式为收发模式,因为我既要接收定位数据,也要打印或发送数据。最后别忘记使能中断和串口。

unsigned int DataIndex = 0;
void USART1_IRQHandler()                	
{
	unsigned char receive;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) == 1) 
	{
		receive = USART_ReceiveData(USART1);		//读取接收到的数据
	
		if(receive == '$')
			DataIndex = 0;	
			
		USART_RX_BUF[DataIndex++] = receive;

		//GNRMC\GPRMC
		if(USART_RX_BUF[0] == '$' && USART_RX_BUF[4] == 'M' && USART_RX_BUF[5] == 'C')	
		{
			if(receive == '\n')									   
			{
				memset(GNRMC_Info.GPS_Buffer, 0, GPS_Buffer_Length);      //清空
				memcpy(GNRMC_Info.GPS_Buffer, USART_RX_BUF, DataIndex); 	//保存数据
				GNRMC_Info.isGetData = true;
				DataIndex = 0;
				memset(USART_RX_BUF, 0, USART_REC_LEN);      //清空				
			}					
		}	 
   } 
}

先是定义一个DataIndex作为数组的下标,当读到了$标志表示数据帧开始,将下标置零,之后判断是不是我们要读取的这种格式,如果是则读到’\n’,即把数据转而存储到GPS_Buffer中,并把数据读取标志位置为true。

void ParseGps()
{
	char *subString;
	char *subStringNext;
	char i = 0;
	if (GNRMC_Info.isGetData)
	{
		GNRMC_Info.isGetData = false;
//		printf("\r\n");
//		printf(GNRMC_Info.GPS_Buffer);

		//截取数据帧前六部分    							 |对地航速 对地航向  日期
		//$GNRMC,112536.000,A,2322.75023,N,11326.28605,E,|  0.00,   0.00,  100722,,,A*78
		for (i = 0 ; i <= 6 ; i++)
		{
			if (i == 0)
			{
				if ((subString = strstr(GNRMC_Info.GPS_Buffer, ",")) == NULL)//如果没有找到逗号
				{
					return;
					//ERROR
				}
				
			}
			else
			{
				subString++;
				if ((subStringNext = strstr(subString, ",")) != NULL)
				{
					char usefulBuffer[2]; 
					switch(i)
					{
						case 1:memcpy(GNRMC_Info.UTCTime, subString, subStringNext - subString);break;	
						case 2:
						{
							memcpy(usefulBuffer, subString, subStringNext - subString);//有效标志位
							if(usefulBuffer[0] == 'A')
								GNRMC_Info.isUsefull = true;
							else if(usefulBuffer[0] == 'V')
								GNRMC_Info.isUsefull = false;	
							break;
						}	
						case 3:memcpy(GNRMC_Info.latitude, subString, subStringNext - subString);break;	
						case 4:memcpy(GNRMC_Info.N_S, subString, subStringNext - subString);break;	
						case 5:memcpy(GNRMC_Info.longitude, subString, subStringNext - subString);break;	
						case 6:memcpy(GNRMC_Info.E_W, subString, subStringNext - subString);break;	
						default:break;
					}
					subString = subStringNext;					
				}
			}
		}
		GNRMC_Info.isParseData = true;	
	}
}

这里进行六次循环,把数据帧分成六部分,用strstr函数找到从子串next指针开始的下一个逗号的位置,并把这个这个位置到子串指针之间的数据分别存到各自的数组中去。六次循环之后便结束,因为后面的数据是对地航速,对地航向和日期,我没有这个需求,有需要的同学可以自己修改。

extern float Lat;
extern float Lon;
extern char dest[23];
void printGpsBuffer()
{
	//$GNRMC,123211.000,A,2322.74250,N,11326.27041,E,3.21,217.19,100722,,,A*7A
	if (GNRMC_Info.isParseData)
	{
		int i = 0;
		GNRMC_Info.isParseData = false;		
		if(GNRMC_Info.isUsefull)
		{
			float tmp = 0;		int j = 0;	
			GNRMC_Info.isUsefull = false;
			for (i = 0; GNRMC_Info.latitude[i] != '\0'; i++)
			{

				if (GNRMC_Info.latitude[i] == '.')
				{
					continue;
				}
				if (i <= 1)
				{
					Lat = (GNRMC_Info.latitude[0] - 48) * 10 + (GNRMC_Info.latitude[1] - 48);
					//取出个位和十位
				}
				else
				{
					tmp += (GNRMC_Info.latitude[i] - 48);
					tmp *= 10;
				}
			}
			for (j = 0; j <= 5; j++)
			{
				tmp /= 10;
			}
			Lat += tmp / 60;
			//23 22.74250
			//23.xxxxx
			int iLat = 0;			
			iLat = (int)Lat;
			GNRMC_Info.latitude[0] = iLat / 10 + '0';
			GNRMC_Info.latitude[1] = iLat % 10 + '0';
			GNRMC_Info.latitude[2] = '.';
			Lat -= iLat;
			for (j = 3; j < 10; j++)
			{
				Lat *= 10;
				iLat = (int)Lat;
				GNRMC_Info.latitude[j] = iLat + '0';
				Lat -= iLat;
			}							
			tmp = 0;
			//113.27041
			for (i = 0; GNRMC_Info.longitude[i] != '\0'; i++)
			{

				if (GNRMC_Info.longitude[i] == '.')
				{
					continue;
				}
				if (i <= 2)
				{
					Lon = (((GNRMC_Info.longitude[0] - 48) * 10 + (GNRMC_Info.longitude[1] - 48)) * 10) + (GNRMC_Info.longitude[2] - 48);
					//取出个位和十位和百位
				}
				else
				{
					tmp += (GNRMC_Info.longitude[i] - 48);
					tmp *= 10;
				}
			}
			for (j = 0; j <= 5; j++)
			{
				tmp /= 10;
			}
			int iLon = 0;
			//113.43784
			Lon += tmp / 60;
			iLon = (int)Lon;
			GNRMC_Info.longitude[0] = iLon / 100 + '0';
			GNRMC_Info.longitude[1] = (iLon % 100) / 10 + '0';
			GNRMC_Info.longitude[2] = iLon % 10 + '0';
			GNRMC_Info.longitude[3] = '.';
			Lon -= iLon;
			for (j = 4; j < 11; j++)
			{
				Lon *= 10;
				iLon = (int)Lon;
				GNRMC_Info.longitude[j] = iLon + '0';
				Lon -= iLon;
			}
			
			dest[8] = dest[10] = dest[20] = ',';
			dest[9] = 'N'; dest[21] = 'E'; dest[22] = '\0';
			for(i = 0; i < 22; i++)
			{
				if(i <= 7)
					dest[i] = GNRMC_Info.latitude[i];
				if(i >= 11 && i <= 19)
					dest[i] = GNRMC_Info.longitude[i - 11];
			}
			
			//printf("\r\ndest = ");
			printf(dest);
			//printf("\r\n");
		}
		else
		{
			printf("GPS DATA Is Not Useful!");
		}	
	}
}

最后就是数据的转换,要把经纬度数组中的字符提取出来组合成数字。对于纬度,先提取数组前两位的数据作为个位和十位,从第三位开始后面的数据提取出来组成一个浮点数,之后把这个浮点数除以60,最后加上个位和十位的整数,便得到了转换后的纬度。经度同理,区别在于经度有百位。
这里只是简单的没有啥依据的不合理的提取。
提取得到两个浮点数后,把经纬度方向一起存储在dest数组中,组合成完整的包括经纬度方向的数据。

结语

数据解析的步骤很繁琐,且模块输出的原始数据不是很准确。后面再试试。文章来源地址https://www.toymoban.com/news/detail-410196.html

到了这里,关于STM32——定位模块ATGM336H,数据解析,提取经纬度的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • GNSS定位模块串口配置说明,亲测(使用ATGM332D模块,通用)

    CAS00-设置保存配置指令 拓展指令只有当前上电有效,重启后恢复默认。如果想要配置一次永久生效,可使用该指令。 例:$PCAS00*01 0x24,0x50,0x43,0x41,0x53,0x30,0x30,0x2A,0x30,0x31, 0x0D,0x0A CAS01-设置串口波特率 两种方案: 1、先用9600波特率配置波特率,然后重新初始化串口即可; 2、生

    2023年04月11日
    浏览(56)
  • 【物联网】BDS/GNSS 全星座定位导航模块——ATGM332D-5N

    随着科技的不断进步,导航系统已经成为我们日常生活中不可或缺的一部分。传统的导航系统往往只提供基本的地图和路线规划,对于一些特殊需求或个性化定位并不够满足。 全星座定位导航模块 的出现,为我们带来了全新的导航体验。通过结合星座学说和个人特质,这一

    2024年02月05日
    浏览(40)
  • stm32 m5311上传gps数据上onenet云平台实现地图定位

    新手,第一次写博客,如有不足请大家见谅并指出。下面开始正文。 本文是帮一个朋友做一个NB_Iot的小东西这个过程的学习记录吧。 NB模块是M5311,然后还有一个stm32f103的最小系统开发板,后面开始了漫漫学习NB模块上云的道路。M5311我搜到的例程都是用onenet平台,自然而然地

    2023年04月08日
    浏览(86)
  • STM32采集电流互感器(电流互感器模块)数据

    1. 电流互感器简介 在发电、变电、输电、配电和用电的线路中电流大小悬殊,从几安到几万安都有。为便于测量、保护和控制需要转换为比较统一的电流,另外线路上的电压一般都比较高如直接测量是非常危险的。电流互感器就起到电流变换和电气隔离作用。 2. 电流互感器

    2024年02月12日
    浏览(39)
  • 使用stm32读取UbloxGPS模块的UBX协议数据

    使用模块为图中所示的GPS模块,内部集成UBLOX-M10芯片,可以采用NMEA和UBX协议,使用UBX协议可以输出东北天三个方位速度值,符合本项目要求,所以使用UBX协议输出数据。 虽然模块商家提供了输出UBX协议的C语言例程,但是比较繁琐,我们可以使用UBLOX官方上位机软件进行模块

    2024年02月08日
    浏览(31)
  • STM32F407 CAN模块发送接收数据异常

    现象: 1、CAN模块初始化成CAN_MODE_LOOPBACK模式 CAN模块通过HAL_CAN_AddTxMessage发送数据时,TX管脚能够发出数据,示波器也可以测量到数据波形,CAN使用中断接收HAL_CAN_ActivateNotification或者查询接收HAL_CAN_GetRxMessage都可以接收到正确的数据(和发送的数据一致),但是CAN接口却没有波

    2024年02月12日
    浏览(46)
  • STM32+Zigbee模块实现串口通信获取传感器数据

    1.两个Zigbee透传模块 2.两块32板(我手上的是VET6和RCT6的两块板子),其它的板子也可以,不过注意一下启动文件对应 3.颗粒物传感器 DL-22无线串口模块 1.工作模式分为点对点和广播模式 点对点模式,要设置一个发送端,一个接收端,根据它说明书进行设置就好,还需要设置相

    2023年04月08日
    浏览(50)
  • STM32教学——JQ8900语音模块+光照传感器+4G模块数据上传阿里云物联网

    原理图 PCB 实物图 选用EC200U模块,集成4g和GPS以及蓝牙功能 通过串口2与4G模块串口连接,串口传输数据指令。 这个时候4g模块已经成功启动,下一步注册进阿里云物联网平台 然后就是上发数据给阿里云平台 上发了之后阿里云就可以收到这些数据 根据厂家提供的手册资料,可

    2024年03月14日
    浏览(47)
  • stm32串口发送数据包进行解析,实现人机交互

    串口收发解析数据包 学过stm32的同学都知道,利用串口与32进行通讯非常的方便,在正点原子的官方历程中我们就可以看到,在串口中断服务函数里面,对接受的数据用一个十六位的数据来判断是否接受完成(即是否在数据包的末尾接收到0x0D,0x0A,他们分别对应的是r n),

    2024年02月14日
    浏览(40)
  • 基于STM32CUBEMX驱动TMOS模块STHS34PF80(6)----获取状态数据

    STHS34PF80传感器项目种修改 Arduino 脚本,重新移植到STM32的MCU中。 该项目基于STHS34PF80 IR温度传感器,能够检测环境和物体温度,并且在最大4米范围内检测存在和运动。有一个Arduino脚本,显示如何为基本环境和物体温度测量配置传感器,并如何配置嵌入式功能算法,并使用它们

    2024年02月11日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包