1.传感器用途:
GPS(GlobalPositioningSystem,全球定位系统)是一种由美国研发并维护的全球卫星导航系统,旨在为全球任意地点、任意时间的用户提供高精度的位置、速度和时间信息。以下从核心构成、工作原理、主要功能及应用场景等方面详细介绍:
一、核心构成
GPS系统由三大子系统组成,缺一不可:
(1)空间卫星星座
由24颗工作卫星和若干备用卫星组成,分布6个倾角为]55°]的轨道平面上,每个轨道有4颗卫星,确保全球任何地点在任何时间都能观测到至少4颗卫星(实现定位的基本条件)。
卫星持续向地面发送包含自身位置、时间戳等信息的无线电信号。
(2)地面监控系统
包括1个主控站(位于美国科罗拉多州)、3个注入站和5个监测站,负责跟踪卫星运行状态、修正卫星轨道和时钟误差,并将修正数据注入卫星,确保卫星信号的准确性。
(3)用户接收设备(GPS接收机)
即用户使用的终端(如手机、车载导航、专业定位设备等),通过天线接收卫星信号,计算自身位置、速度和时间。
二、工作原理:三角定位法
GPS的定位基于“时间差计算距离”和“三角测量”原理:
(1)信号传播时间计算距离
卫星与接收机之间的距离=光速×信号从卫星到接收机的传播时间(需消除卫星与接收机的时钟误差)。
(2)多星定位
接收机同时接收至少4颗卫星的信号,分别计算与每颗卫星的距离,形成4个球面方程,联立求解即可得到接收机的三维坐标(经度、纬度、海拔)和时间。
三、主要功能
(1)定位
提供经纬度、海拔高度等地理坐标,民用定位精度通常为1-10米(受环境、设备性能影响),军用精度可达厘米级。
(2)测速
通过连续定位计算移动速度(如车辆行驶速度、航空器飞行速度)。
(3)授时
提供高精度时间信息(与卫星原子钟同步),广泛用于通信、电力等需要精确时间同步的领域。
(4)导航
结合电子地图,为移动目标(行人、车辆、船舶等)规划路径并实时指引。
四、应用场景
(1)民用领域:
车载导航(如高德、百度地图)、手机地图定位;
物流运输(车辆跟踪、路径优化);
户外运动(徒步、骑行的轨迹记录);
农业(精准播种、施肥的定位引导);
应急救援(快速定位受困人员位置)。
(2)专业/军用领域:
航空航天(飞行器导航、航天器轨道控制);
海洋航运(船舶定位与航线规划);
测绘与地理信息(地形图绘制、土地确权);
军事行动(武器制导、部队调度)。
五、补充说明
(1)类似系统:除GPS外,全球还有其他卫星导航系统,如中国的北斗(BDS)、俄罗斯的格洛纳斯(GLONASS)、欧盟的伽利略(Galileo),它们共同构成全球卫星导航系统(GNSS),用户设备可兼容多系统以提高定位稳定性。
(2)局限性:GPS信号易受遮挡(如高楼、隧道、茂密森林)或电磁干扰影响,此时可能需要结合基站定位、惯性导航等辅助手段提升精度。
(3)GPS中常用的协议报文是NMEA-0183协议下的语句,以下是一些常见的报文及其含义:
GPGGA(定位信息):包含了UTC时间、纬度、经度、GPS状态、使用的卫星数量、水平精度因子、海拔高度等信息。例如:$GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,M,19.7,M,,,0000*1F。
GPGSA(当前卫星信息):提供定位模式、定位类型以及正在使用的卫星的伪随机噪声码(PRN码),还有综合位置精度因子、水平精度因子和垂直精度因子等信息。如:$GPGSA,A,3,01,20,19,13,,,,,,,,,40.4,24.4,32.2*0A。
GPGSV(可见卫星信息):显示当前可见卫星的总数,以及每颗卫星的PRN码、仰角、方位角和信噪比等信息。例如:$GPGSV,3,1,10,20,78,331,45,01,59,235,47,22,41,069,,13,32,252,45*70。
GPRMC(推荐定位信息数据格式):包含了UTC时间、定位状态、纬度、经度、地面速度、磁偏角等信息,对于一般的GPS动态定位应用,该语句提供的信息较为关键。如:$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50。
GPVTG(地面速度信息):主要用于表示地面速度和行驶方向等信息。
GPGLL(地理定位信息):提供纬度、经度、定位状态等地理定位相关信息。
2.传感器的介绍:
UM220-IV模块是一款高性能的定位模块。它支持BDSB1、GPSL1频段,具备出色的定位能力,定位精度可达平面:2.0m高程:3.5m。其数据刷新频率为1Hz,能以较为稳定的速率更新定位数据。该模块基于和芯星通完全自主知识产权的芯片设计,集成度高、功耗低,并且拥有抗干扰设计,即便在复杂的电磁环境下,也能稳定工作,输出可靠的定位信息。
3.单片机连接硬件图:
实物图:
驱动思路:
(1)串口通信配置:首先对STM32的串口进行初始化配置,使能GPIOA和USART1时钟,将PA2(TX)配置为复用推挽输出,PA3(RX)配置为浮空输入。设置串口参数,包括波特率为115200(与UM220默认波特率匹配)、数据位8位、停止位1位、无校验位以及无硬件流控制,并使能串口接收中断,以确保能实时接收UM220模块发送的数据。
(2)数据接收处理:利用环形缓冲区来存储串口接收的数据,避免数据溢出。在中断服务程序中,每当接收到一个字节的数据,就将其存入环形缓冲区。在主程序中,不断从环形缓冲区读取数据进行后续处理,例如检测NMEA协议数据帧的帧头和帧尾,以完整获取一帧定位数据。
(3)NMEA协议解析:UM220模块输出的定位数据遵循NMEA-0183协议。通过检测接收缓冲区中的数据,定位到$GNGGA等关键语句帧头,当检测到完整的一帧数据后,使用逗号作为分隔符,将语句拆分为数组,再对各个字段进行数据转换,如将经纬度的格式从ddmm.mmmm转换为十进制度数,同时进行校验和计算,验证数据的完整性,从而提取出准确的定位信息。
4.单片机程序代码:
main.c
#include "stm32f10x.h"
#include "string.h"
#include "stdio.h"
#include "delay.h"
#include "bsp_usart.h"
#include "oled.h"
#include "BeidouGPS.h"
void displayPage1(void);
void displayPage2(void);
GPS_Data gps_data;
char nmea_buffer[512]; // 假设缓冲区足够大以容纳多条NMEA语句
int main(void)
{
NVIC_PriorityGroupConfig (NVIC_PriorityGroup_2);
SysTick_Init(72); //系统时钟初始化
usart1_init(115200);//串口1初始化
printf("USART1 OK!\r\n");
usart2_init(9600);//串口1初始化
usart3_init(115200);//串口3初始化
OLED_Init();
uint8_t currentPage = 0;
while(1)
{
currentPage++;
if(currentPage<=100)
{
displayPage1();
}
else if(currentPage<=200&¤tPage>=101)
{
if(currentPage==101)
{
OLED_Clear();
}
displayPage2();
}
else
{
currentPage=0;
}
// 初始化GPS数据结构
if(buf_uart2.rx_flag==1)
{
delay_ms(10);
USART1_SendStr(buf_uart2.buf);
printf("长度:%d\r\n",buf_uart2.index);
memset(&gps_data, 0, sizeof(GPS_Data));
if (parseNMEA(buf_uart2.buf, &gps_data))
{
printf("GPS数据解析成功:\n");
printf("时间: %02d:%02d:%02d\n", gps_data.hour, gps_data.minute, gps_data.second);
printf("日期: %02d-%02d-%02d\n", gps_data.year, gps_data.month, gps_data.day);
printf("纬度: %.6f°\n", gps_data.latitude);
printf("经度: %.6f°\n", gps_data.longitude);
printf("速度: %.3f 节(%.3f km/h)\n", gps_data.speed, gps_data.speed_kmh);
printf("海拔: %.1f 米\n", gps_data.altitude);
//printf("定位状态: %s\n", gps_data.valid ? "有效" : "无效");
}
else
{
printf("GPS数据解析失败\n");
}
Clear_Buffer_UART2();
}
}
}
char OLEDBUff[512];
void displayPage1(void)
{
OLED_ShowString(0,0,"时间");
memset(OLEDBUff, 0, sizeof(OLEDBUff));
sprintf(OLEDBUff,":%02d:%02d:%02d", gps_data.hour, gps_data.minute, gps_data.second);
OLED_ShowString(36,0,OLEDBUff);
OLED_ShowString(0,2,"日期");
sprintf(OLEDBUff,":%02d-%02d-%02d", gps_data.year, gps_data.month, gps_data.day);
OLED_ShowString(36,2,OLEDBUff);
OLED_ShowString(0,4,"经度");
sprintf(OLEDBUff,":%.4f", gps_data.latitude);
OLED_ShowString(36,4,OLEDBUff);
OLED_ShowString(0,6,"纬度");
sprintf(OLEDBUff,":%.4f",gps_data.longitude);
OLED_ShowString(36,6,OLEDBUff);
}
void displayPage2(void)
{
OLED_ShowString(0,0,"速度");
memset(OLEDBUff, 0, sizeof(OLEDBUff));
sprintf(OLEDBUff,":%.3f",gps_data.speed);
OLED_ShowString(36,0,OLEDBUff);
OLED_ShowString(0,2,"海拔");
sprintf(OLEDBUff,":%.1f ",gps_data.altitude);
OLED_ShowString(36,2,OLEDBUff);
}
Uart.c
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "stdio.h"
#include "string.h"
#include "stm32f10x_tim.h"
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
int _sys_exit(int x)
{
x = x;
return 0;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
#if EN_USART1
UART_BUF buf_uart1; //CH340
//初始化IO 串口1
//bound:波特率
void usart1_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
USART_DeInit(USART1); //复位串口1
//USART1_TX PA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9
//USART1_RX PA.10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA10
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//一般设置为115200;
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(USART1, &USART_InitStructure); //初始化串口
USART_Cmd(USART1, ENABLE); //使能串口
#if EN_USART1_RX
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启相关中断
USART_ClearFlag(USART1, USART_FLAG_TC);
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
#endif
}
/*********************************串口1的服务函数*************************************************/
void USART1_Send_byte(char data)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, data);
}
/*-------------------------------------------------*/
/*函数名:串口1 发送数组 */
/*参 数:bound:波特率 */
/*返回值:无 */
/*-------------------------------------------------*/
void USART1_Send(char *Data,uint16_t Len)
{
uint16_t i;
for(i=0; i<Len; i++)
{
USART1_Send_byte(Data[i]);
}
}
void USART1_SendStr(char*SendBuf)//串口1打印数据
{
while(*SendBuf)
{
while((USART1->SR&0X40)==0);//等待发送完成
USART1->DR = (u8) *SendBuf;
SendBuf++;
}
}
/*****************************************************
清空电脑反馈的缓冲数据 串口1
*****************************************************/
void Clear_Buffer_UART1(void)//清空缓存
{
buf_uart1.index=0;
buf_uart1.rx_flag=0;
memset(buf_uart1.buf,0,BUFLEN);
}
void UART1_receive_process_event(char ch ) //串口2给4g用
{
if(buf_uart1.index >= BUFLEN)
{
buf_uart1.index = 0 ;
}
else
{
buf_uart1.buf[buf_uart1.index++] = ch;
}
}
//串口1的接收中断程序
void USART1_IRQHandler(void) //串口1中断服务程序
{
uint8_t Res;
Res=Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断,可以扩展来控制
{
Res=USART_ReceiveData(USART1);//接收模块的数据;
UART1_receive_process_event(Res);//接收模块的数据
}
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //模块空闲
{
Res=USART_ReceiveData(USART1);//接收模块的数据;
buf_uart1.rx_flag=1;
}
}
#endif
#if EN_USART2
UART_BUF buf_uart2; //EC200T
//初始化IO 串口2
//pclk1:PCLK1时钟频率(Mhz)
//bound:波特率
void usart2_init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能,GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//USART2
USART_DeInit(USART2); //复位串口2
//USART2_TX PA.2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA2
//USART2_RX PA.3
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA3
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//115200
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(USART2, &USART_InitStructure); //初始化串口
USART_Cmd(USART2, ENABLE); //使能串口
#if EN_USART2_RX
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启相关中断
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//开启相关中断
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;//串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =1; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
#endif
}
void Clear_Buffer_UART2(void)//清空缓存
{
buf_uart2.index=0;
buf_uart2.rx_flag=0;
memset(buf_uart2.buf,0,BUFLEN);
}
/*********************************串口2的服务函数*************************************************/
void USART2_Send_byte(char data)
{
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, data);
}
/*-------------------------------------------------*/
/*函数名:串口2 发送数组 */
/*参 数:bound:波特率 */
/*返回值:无 */
/*-------------------------------------------------*/
void USART2_Send(char *Data,uint16_t Len)
{
uint16_t i;
for(i=0; i<Len; i++)
{
USART2_Send_byte(Data[i]);
}
}
void USART2_SendStr(char*SendBuf)//串口1打印数据
{
while(*SendBuf)
{
while((USART2->SR&0X40)==0);//等待发送完成
USART2->DR = (u8) *SendBuf;
SendBuf++;
}
}
void usart2_receive_process_event(unsigned char ch ) //串口2给4g用
{
if(buf_uart2.index >= BUFLEN)
{
buf_uart2.index = 0 ;
}
else
{
buf_uart2.buf[buf_uart2.index++] = ch;
}
}
void USART2_IRQHandler(void) //串口2接收函数
{
char Res;
Res=Res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断,可以扩展来控制
{
Res=USART_ReceiveData(USART2);//接收模块的数据;
usart2_receive_process_event(Res);//接收模块的数据
}
if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) //模块空闲
{
Res=USART_ReceiveData(USART2);//接收模块的数据;
buf_uart2.rx_flag=1;
}
}
#endif
#if EN_USART3
UART_BUF buf_uart3; //TTL
void usart3_init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能,GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//USART3
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); //初始化PA2
//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 初始化设置
USART_InitStructure.USART_BaudRate = bound;//115200
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); //初始化串口
USART_Cmd(USART3, ENABLE); //使能串口
#if EN_USART3_RX
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启相关中断
USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);//开启相关中断
USART_ClearFlag(USART3, USART_FLAG_TC);
//Usart3 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;//串口3中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
#endif
}
void USART3_Send_byte(char data)
{
while(USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET);
USART_SendData(USART3, data);
}
/*-------------------------------------------------*/
/*函数名:串口2 发送数组 */
/*参 数:bound:波特率 */
/*返回值:无 */
/*-------------------------------------------------*/
void USART3_Send(char *Data,uint16_t Len)
{
uint16_t i;
for(i=0; i<Len; i++)
{
USART3_Send_byte(Data[i]);
}
}
void USART3_SendStr(char*SendBuf)//串口3打印数据
{
while(*SendBuf)
{
while((USART3->SR&0X40)==0);//等待发送完成
USART3->DR = (u8) *SendBuf;
SendBuf++;
}
}
/*****************************************************
清空电脑反馈的缓冲数据 串口1
*****************************************************/
void Clear_Buffer_UART3(void)//清空缓存
{
buf_uart3.index=0;
buf_uart3.rx_flag=0;
memset(buf_uart3.buf,0,BUFLEN);
}
void USART3_receive_process_event(char ch ) //串口2给4g用
{
if(buf_uart3.index >= BUFLEN)
{
buf_uart3.index = 0 ;
}
else
{
buf_uart3.buf[buf_uart3.index++] = ch;
}
}
void USART3_IRQHandler(void) //串口3中断服务程序
{
char Res;
Res=Res;
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) //接收中断,可以扩展来控制
{
Res=USART_ReceiveData(USART3);//接收模块的数据;
USART3_receive_process_event(Res);
}
if(USART_GetITStatus(USART3, USART_IT_IDLE) != RESET) //模块空闲
{
Res=USART_ReceiveData(USART3);//接收模块的数据;
buf_uart3.rx_flag=1;
}
}
#endif
beidouGps.c
#include "BeidouGPS.h"
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
// 时区偏移量(小时)- 根据实际时区调整
#define TIMEZONE_OFFSET 8
uint8_t parseNMEA(char *nmea_buffer, GPS_Data *gps_data) {
char *sentence;
uint8_t gnrmc_parsed = 0;
uint8_t gngga_parsed = 0;
// 提取每一条NMEA语句
sentence = strtok(nmea_buffer, "\r\n");
while (sentence != NULL) {
// 检查语句类型并解析
if (strncmp(sentence, "$GNRMC", 6) == 0) {
if (parseGNRMC(sentence, gps_data)) {
gnrmc_parsed = 1;
}
} else if (strncmp(sentence, "$GNGGA", 6) == 0) {
if (parseGNGGA(sentence, gps_data)) {
gngga_parsed = 1;
}
}
// 获取下一条语句
sentence = strtok(NULL, "\r\n");
}
// 如果GNRMC或GNGGA任意一个解析成功,则整体解析成功
return (gnrmc_parsed || gngga_parsed);
}
// 改进的GNRMC解析函数
uint8_t parseGNRMC(char *nmea_sentence, GPS_Data *gps_data)
{
char *token;
char *saveptr;
uint8_t checksum;
uint8_t received_checksum;
// 验证校验和
char *checksum_ptr = strchr(nmea_sentence, '*');
if (!checksum_ptr) {
printf("Error: No checksum found in sentence\n");
return 0; // 没有找到校验和
}
// 计算校验和
checksum = calculateChecksum(nmea_sentence);
sscanf(checksum_ptr + 1, "%2X", &received_checksum);
if (checksum != received_checksum) {
printf("Error: Checksum mismatch\n");
return 0; // 校验和不匹配
}
// 开始解析字段
token = strtok_r(nmea_sentence, ",", &saveptr); // $GNRMC
// 解析时间(UTC)
token = strtok_r(NULL, ",", &saveptr); // UTC时间
if (token && strlen(token) >= 6) {
uint8_t hour, minute, second;
sscanf(token, "%2hhu%2hhu%2hhu", &hour, &minute, &second);
// 转换为本地时间(考虑时区偏移)
gps_data->hour = (hour + TIMEZONE_OFFSET) % 24;
gps_data->minute = minute;
gps_data->second = second;
}
// 解析定位状态
token = strtok_r(NULL, ",", &saveptr); // 状态(A/V)
gps_data->valid = (token && *token == 'A') ? 1 : 0;
// 解析纬度
token = strtok_r(NULL, ",", &saveptr); // 纬度(DMS格式)
char *lat_hemisphere = strtok_r(NULL, ",", &saveptr); // 南北半球
if (token && lat_hemisphere && strlen(token) > 0 && strlen(lat_hemisphere) > 0) {
gps_data->latitude = convertDMStoDD(token, *lat_hemisphere);
//printf("Parsed latitude: %s %c -> %.6f\n", token, *lat_hemisphere, gps_data->latitude);
} else {
printf("Warning: Incomplete latitude data\n");
}
// 解析经度
token = strtok_r(NULL, ",", &saveptr); // 经度(DMS格式)
char *lon_hemisphere = strtok_r(NULL, ",", &saveptr); // 东西半球
if (token && lon_hemisphere && strlen(token) > 0 && strlen(lon_hemisphere) > 0) {
gps_data->longitude = convertDMStoDD(token, *lon_hemisphere);
//printf("Parsed longitude: %s %c -> %.6f\n", token, *lon_hemisphere, gps_data->longitude);
} else {
printf("Warning: Incomplete longitude data\n");
}
// 解析速度(节)
token = strtok_r(NULL, ",", &saveptr); // 地面速度(节)
if (token && strlen(token) > 0) {
gps_data->speed = atof(token);
// 将速度从节转换为km/h (1节 = 1.852 km/h)
gps_data->speed_kmh = gps_data->speed * 1.852f;
}
// 解析日期
token = strtok_r(NULL, ",", &saveptr); // UTC日期
if (token && strlen(token) >= 6) {
sscanf(token, "%2hhu%2hhu%2hu", &gps_data->day, &gps_data->month, &gps_data->year);
gps_data->year += 2000; // 转换为4位数年份
}
return 1; // 解析成功
}
//GNGGA解析函数
uint8_t parseGNGGA(char *nmea_sentence, GPS_Data *gps_data) {
char *token;
char *saveptr;
uint8_t checksum;
uint8_t received_checksum;
int field_index = 0;
// 验证校验和
char *checksum_ptr = strchr(nmea_sentence, '*');
if (!checksum_ptr)
{
printf("GNGGA: Checksum not found\n");
return 0;
}
// 计算校验和
checksum = calculateChecksum(nmea_sentence);
sscanf(checksum_ptr + 1, "%2X", &received_checksum);
if (checksum != received_checksum) {
//printf("GNGGA: Checksum mismatch (calc=0x%02X, recv=0x%02X)\n",checksum, received_checksum);
return 0;
}
// 开始解析字段
token = strtok_r(nmea_sentence, ",", &saveptr); // $GNGGA
field_index++;
// 解析时间(如果GNRMC中没有解析到)
token = strtok_r(NULL, ",", &saveptr); // UTC时间
field_index++;
if (token && strlen(token) >= 6 && gps_data->hour == 0)
{
uint8_t hour, minute, second;
sscanf(token, "%2hhu%2hhu%2hhu", &hour, &minute, &second);
// 转换为本地时间(考虑时区偏移)
gps_data->hour = (hour + TIMEZONE_OFFSET) % 24;
gps_data->minute = minute;
gps_data->second = second;
//printf("GNGGA: Time parsed: %02d:%02d:%02d\n",gps_data->hour, gps_data->minute, gps_data->second);
}
// 解析纬度
token = strtok_r(NULL, ",", &saveptr); // 纬度(DMS格式)
field_index++;
char *lat_hemisphere = strtok_r(NULL, ",", &saveptr); // 南北半球
field_index++;
if (token && lat_hemisphere && gps_data->latitude == 0.0) {
gps_data->latitude = convertDMStoDD(token, *lat_hemisphere);
// printf("GNGGA: Latitude parsed: %s %c -> %.6f\n",token, *lat_hemisphere, gps_data->latitude);
}
// 解析经度
token = strtok_r(NULL, ",", &saveptr); // 经度(DMS格式)
field_index++;
char *lon_hemisphere = strtok_r(NULL, ",", &saveptr); // 东西半球
field_index++;
if (token && lon_hemisphere && gps_data->longitude == 0.0) {
gps_data->longitude = convertDMStoDD(token, *lon_hemisphere);
//printf("GNGGA: Longitude parsed: %s %c -> %.6f\n",token, *lon_hemisphere, gps_data->longitude);
}
// 解析定位质量指示
token = strtok_r(NULL, ",", &saveptr);
field_index++;
if (token) {
int fix_quality = atoi(token);
gps_data->valid = (fix_quality >= 1) ? 1 : 0;
//printf("GNGGA: Fix quality: %d\n", fix_quality);
}
// 解析卫星数量
token = strtok_r(NULL, ",", &saveptr);
field_index++;
if (token) {
int satellites = atoi(token);
//printf("GNGGA: Satellites in use: %d\n", satellites);
}
// 解析HDOP
token = strtok_r(NULL, ",", &saveptr);
field_index++;
// 解析海拔高度(第9个字段)
token = strtok_r(NULL, ",", &saveptr); // 海拔高度
field_index++;
if (token) {
gps_data->altitude = atof(token);
//printf("GNGGA: Altitude parsed: %s -> %.1f meters\n",token, gps_data->altitude);
} else {
printf("GNGGA: Altitude field not found (field_index=%d)\n", field_index);
}
// 跳过单位(M)
token = strtok_r(NULL, ",", &saveptr);
field_index++;
// 解析大地水准面高度
token = strtok_r(NULL, ",", &saveptr);
field_index++;
// 跳过单位(M)
token = strtok_r(NULL, ",", &saveptr);
field_index++;
// 解析差分GPS信息
token = strtok_r(NULL, ",", &saveptr);
field_index++;
// 解析差分参考站ID
token = strtok_r(NULL, ",", &saveptr);
field_index++;
//printf("GNGGA: Parsed %d fields\n", field_index);
return 1; // 解析成功
}
// 修复的DMS到十进制转换函数
float convertDMStoDD(char *dms_str, char hemisphere) {
// 检查输入字符串是否有效
if (!dms_str || strlen(dms_str) == 0) {
printf("Error: Invalid DMS string\n");
return 0.0;
}
// 转换DMS字符串为浮点数
float dms_value = atof(dms_str);
// printf("DMS value: %s -> float value: %.6f\n", dms_str, dms_value);
// 检查是否转换成功
if ((dms_value == 0.0 && strcmp(dms_str, "0") != 0) || dms_value > 18000.0) {
//printf("Warning: Failed to convert DMS string to float or invalid value: %s\n", dms_str);
return 0.0;
}
// 分离度和分 (dms格式为 ddmm.mmmm)
int degrees = (int)(dms_value / 100.0f);
float minutes = dms_value - (degrees * 100.0f);
// 检查数值是否合理
if ((hemisphere == 'N' || hemisphere == 'S') && (degrees < 0 || degrees > 90)) {
//printf("Warning: Invalid latitude degrees value: %d\n", degrees);
return 0.0;
}
if ((hemisphere == 'E' || hemisphere == 'W') && (degrees < 0 || degrees > 180)) {
//printf("Warning: Invalid longitude degrees value: %d\n", degrees);
return 0.0;
}
if (minutes < 0 || minutes >= 60) {
//printf("Warning: Invalid minutes value: %.6f\n", minutes);
return 0.0;
}
// 计算十进制度数
float decimal_degrees = degrees + (minutes / 60);
// printf("Conversion breakdown: degrees=%d, minutes=%.6f -> decimal=%.6f\n",degrees, minutes, decimal_degrees);
// 根据半球调整符号
if ((hemisphere == 'S') || (hemisphere == 'W')) {
decimal_degrees = -decimal_degrees;
// printf("Adjusted for hemisphere %c: %.6f\n", hemisphere, decimal_degrees);
}
return decimal_degrees;
}
uint8_t calculateChecksum(char *nmea_sentence) {
uint8_t checksum = 0;
char *ptr = nmea_sentence + 1; // 跳过$符号
while (*ptr && *ptr != '*') {
checksum ^= (uint8_t)*ptr;
ptr++;
}
return checksum;
}
5.实现效果图片
代码链接:STM32F103C8T6+北斗GPS 链接: https://pan.baidu.com/s/1W0IYoXmGOflJpKA1rX1ELw 提取码: 57uj