STM32使用HAL库获取GPS模块HT1818Z3G5L信息(方法1)

1、写在最前

先了解一下GPRMC的格式

格 式:

GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,A*50

说 明:

字段 0:$GPRMC,语句ID,表明该语句为Recommended Minimum Specific GPS/TRANSIT Data(RMC)推荐最小定位信息

字段 1:UTC时间,hhmmss.sss格式

字段 2:状态,A=定位,V=未定位

字段 3:纬度ddmm.mmmm,度分格式(前导位数不足则补0)

字段 4:纬度N(北纬)或S(南纬)

字段 5:经度dddmm.mmmm,度分格式(前导位数不足则补0)

字段 6:经度E(东经)或W(西经)

字段 7:速度,节,Knots(一节也是1.852千米/小时)

字段 8:方位角,度(二维方向指向,相当于二维罗盘)

字段 9:UTC日期,DDMMYY格式

字段10:磁偏角,(000 - 180)度(前导位数不足则补0)

字段11:磁偏角方向,E=东,W=西

字段12:模式,A=自动,D=差分,E=估测,N=数据无效(3.0协议内容)

字段13:校验值

2、配置

使用STM32F103C8T6的串口1,连接模块的串口。

此模块4个引脚标注为"V G T R"

功能:

V = 3.3V

G = GND

T = TX

R = RX

STM32CUBE配置如下:

记得开启串口中断。

3、程序编写

1、先使用单字节接收方式,因为每个帧的结构都是$开头,0x0d,0x0a结尾。如果使用空闲接收的话,这个模块一次发送的数据比较大,需要大几百字节的缓存。

首先在main函数中,先定义需要的全局变量

c 复制代码
uint8_t g_ucRxByte;
uint8_t g_ucRxCnt;
uint8_t g_ucaRx[GPS_BUFF_SIZE];
rtcType_T g_tRtcCN;
float g_fSpeed;

2、进入主循环之前,开启串口接收

c 复制代码
  /* USER CODE BEGIN 2 */
	HAL_UART_Receive_IT(&huart1,&g_ucRxByte,1);
  /* USER CODE END 2 */

3、编写GPS.h头文件

c 复制代码
#ifndef __GPS_H
#define __GPS_H
#include "main.h"

#define GPS_BUFF_SIZE	100

typedef struct
{
	uint16_t usYear;
	uint8_t ucMonth;
	uint8_t ucDay;
	uint8_t ucHour;
	uint8_t ucMinute;
	uint8_t ucSecond;
}rtcType_T;

extern uint8_t g_ucaGNRMC[GPS_BUFF_SIZE];
extern uint8_t g_ucGNRMCRxSta;

uint8_t GPS_GetTime(char *cpRmc,rtcType_T *tpRTC);/*从RMC(推荐最小定位信息)中获取GPS的时间信息,东8区时间*/
rtcType_T GPS_TimeToArea(rtcType_T tTime, uint8_t ucArea);/*转换GPS时间到指定区的时间*/
float GPS_GetSpeed(char *cpRmc);

#endif

4、编写GPS.c文件

c 复制代码
#include "gps.h"
#include "string.h"
#include "stdlib.h"

uint8_t g_ucaGNRMC[GPS_BUFF_SIZE];
uint8_t g_ucGNRMCRxSta;


uint8_t GPS_GetTime(char *cpRmc,rtcType_T *tpRTC)
{
	char *cpStart;
	uint8_t i;

	cpStart = strchr(cpRmc,',');
	
	tpRTC->ucHour = (cpStart[1]-0x30)*10 + (cpStart[2]-0x30) ;/**/

	tpRTC->ucMinute = (cpStart[3]-0x30)*10 + (cpStart[4]-0x30);
	tpRTC->ucSecond = (cpStart[5]-0x30)*10 + (cpStart[6]-0x30);
	
	cpStart = cpRmc;
	for(i = 0;i < 9;i++)
	{
		cpStart = strchr(cpStart,',');
		cpStart++;
	}
	tpRTC->ucDay = (cpStart[0]-0x30)*10 + (cpStart[1]-0x30);
	tpRTC->ucMonth = (cpStart[2]-0x30)*10 + (cpStart[3]-0x30);
	tpRTC->usYear = (cpStart[4]-0x30)*10 + (cpStart[5]-0x30) + 2000;
	
	return 1;

}


rtcType_T GPS_TimeToArea(rtcType_T tTime, uint8_t ucArea)
{
	rtcType_T tConvertTime;
	tConvertTime = tTime;
	tConvertTime.ucHour += ucArea;
	if(1==tConvertTime.ucMonth||3==tConvertTime.ucMonth||5==tConvertTime.ucMonth||7==tConvertTime.ucMonth||8==tConvertTime.ucMonth||10==tConvertTime.ucMonth||12==tConvertTime.ucMonth)//1,3,5,7,8,9,12月每月为31天
	{
		if(24 <= tConvertTime.ucHour)
		{
			tConvertTime.ucHour -= 24;
			tConvertTime.ucDay += 1;//如果超过24小时,减去24小时,后再加上一天
			if(tConvertTime.ucDay > 31)
			{
				tConvertTime.ucDay -= 31;
				tConvertTime.ucMonth += 1;
			}//如果超过31一天,减去31天,后加上一个月
		}
	}
	else if(4==tConvertTime.ucMonth||6==tConvertTime.ucMonth||9==tConvertTime.ucMonth||11==tConvertTime.ucMonth)//4,6,9,11月每月为30天
	{
		if(24 <= tConvertTime.ucHour)
		{
			tConvertTime.ucHour -= 24;
			tConvertTime.ucDay += 1;//如果超过24小时,减去24小时,后再加上一天
			if(30 < tConvertTime.ucDay)
			{
				tConvertTime.ucDay -= 30;
				tConvertTime.ucMonth += 1;
			}//如果超过30一天,减去30天,后加上一个月
		}
	}
	else//剩下为2月,闰年为29天,平年为28天
	{
		if(24 <= tConvertTime.ucHour)
		{
			tConvertTime.ucHour -= 24;
			tConvertTime.ucDay += 1;
			if((0 == tConvertTime.usYear%400)||(0 == tConvertTime.usYear%4 && 0 != tConvertTime.usYear%100))//判断是否为闰年,年号能被400整除或年号能被4整除,而不能被100整除为闰年
			{
				if(29 < tConvertTime.ucDay)
				{
					tConvertTime.ucDay -= 29;
					tConvertTime.ucMonth += 1;
				}
			}//为闰年
			else
			{
				if(28 < tConvertTime.ucDay)
				{
					tConvertTime.ucDay -= 28;
					tConvertTime.ucMonth += 1;
				}
			}//为平年
		}
 
	}
	if(12 < tConvertTime.ucMonth)
	{
		tConvertTime.ucMonth-=12;
		tConvertTime.usYear+=1;
	}
	return tConvertTime;
}

/*GNRMC的第7个字段为速度,节,Knots(一节也是1.852千米/小时)*/
float GPS_GetSpeed(char *cpRmc)
{
	float fSpeed;
	char *cpStart,*cpEnd,*ptr;
	uint8_t i,ucLenth;
	char caTmp[10];
	
	memset(caTmp,0,10);
	
	cpStart = cpRmc;
	for(i = 0;i < 7;i++)
	{
		cpStart = strchr(cpStart,',');
		cpStart++;
	}
	cpEnd = strchr(cpStart,',');
	ucLenth = cpEnd - cpStart;
	memcpy(caTmp,cpStart,ucLenth);
	caTmp[ucLenth] = '\0';
	fSpeed = strtof(caTmp,&ptr);
	fSpeed = fSpeed * 1.852;
	return fSpeed;
}

文件中使用了字符串转浮点数函数,如果不需要速度,可以取消,同时无需包含#include "stdlib.h"

5、返回主函数,进行中断回调函数的编写。

c 复制代码
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(g_ucRxByte == '$')
	{
		g_ucRxCnt = 0;
	}
	else if(g_ucRxByte == 0x0a)/*一帧结束*/
	{
		if(g_ucaRx[0]=='$' && g_ucaRx[4]=='M' && g_ucaRx[5]=='C')/*只提取GNRMC*/
		{
			memcpy(g_ucaGNRMC,g_ucaRx,g_ucRxCnt);
			g_ucGNRMCRxSta = g_ucRxCnt | 0x80;
		}
	}
	
	g_ucaRx[g_ucRxCnt++] = g_ucRxByte;
	
	if(g_ucRxCnt >= GPS_BUFF_SIZE)	
	{
		g_ucRxCnt = 0;
	}
	
	HAL_UART_Receive_IT(&huart1,&g_ucRxByte,1);
}
/* USER CODE END 4 */

6、使用模块

在主循环中,解析获取的GNRMC数据。

c 复制代码
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  if(g_ucGNRMCRxSta & 0x80)
	  {
		  g_ucGNRMCRxSta = 0;
		  GPS_GetTime((char *)g_ucaGNRMC,&tRTCGps);
		  g_tRtcCN = GPS_TimeToArea(tRTCGps,8);
		  g_fSpeed = GPS_GetSpeed((char *)g_ucaGNRMC);
	  }
  }
  /* USER CODE END 3 */

7、结果

4、写在最后

这个模块一般般吧,我把模块放到靠近窗户,定位时有时无,定位成功后,静止状态的速度也会跳动。如果需要获取有效的速度信号,可能需要其他辅助,例如运动检测等判断是否在静止状态。

相关推荐
lucy153027510798 分钟前
【青牛科技】GC5931:工业风扇驱动芯片的卓越替代者
人工智能·科技·单片机·嵌入式硬件·算法·机器学习
scan113 小时前
单片机串口接收状态机STM32
stm32·单片机·串口·51·串口接收
Qingniu0113 小时前
【青牛科技】应用方案 | RTC实时时钟芯片D8563和D1302
科技·单片机·嵌入式硬件·实时音视频·安防·工控·储能
Mortal_hhh14 小时前
VScode的C/C++点击转到定义,不是跳转定义而是跳转声明怎么办?(内附详细做法)
ide·vscode·stm32·编辑器
深圳市青牛科技实业有限公司15 小时前
【青牛科技】应用方案|D2587A高压大电流DC-DC
人工智能·科技·单片机·嵌入式硬件·机器人·安防监控
Mr.谢尔比16 小时前
电赛入门之软件stm32keil+cubemx
stm32·单片机·嵌入式硬件·mcu·信息与通信·信号处理
LightningJie16 小时前
STM32中ARR(自动重装寄存器)为什么要减1
stm32·单片机·嵌入式硬件
鹿屿二向箔16 小时前
STM32外设之SPI的介绍
stm32
西瓜籽@16 小时前
STM32——毕设基于单片机的多功能节能窗控制系统
stm32·单片机·课程设计
远翔调光芯片^1382879887219 小时前
远翔升压恒流芯片FP7209X与FP7209M什么区别?做以下应用市场摄影补光灯、便携灯、智能家居(调光)市场、太阳能、车灯、洗墙灯、舞台灯必看!
科技·单片机·智能家居·能源