STM32F103C8T6 GPS北斗模块UM220定位数据解析

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&&currentPage>=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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值