手把手教你一步步从零玩转 STM32 GPS:HAL 库驱动 NEO-6M 获取时间经纬度

手把手教你一步步从零玩转 STM32 GPS:HAL 库驱动 NEO-6M 获取时间经纬度
一模块介绍NEO-6M GPS 模块1. 模块核心特性型号NEO-6M 是嵌入式开发中最常用的民用 GPS 定位模块通信方式UART 串口TTL 电平默认配置9600波特率、8N1输出协议标准NMEA-0183定位协议自动发送多帧定位数据核心数据帧$GPRMC帧包含✅ UTC 时间 / 日期 ✅ 经纬度原始数据 ✅ 定位有效状态 ✅ 速度航向信息硬件接口共 4 个引脚VCC、GND、TX、RX3.3V 供电切记需要到室外该模块室内无法获取信息2. 硬件连接规则STM32 ↔ NEO-6MVCC ↔ 3.3VGND ↔ GNDUSART2_RX(PA3)↔ GPS_TXUSART2_TX(PA2)↔ GPS_RX3. 定位说明模块需要在室外空旷环境才能定位成功室内 / 遮挡环境会提示定位无效。二初始化STM32CUBEMX 配置步骤本工程使用USART2接收 GPS 数据按照以下步骤配置步骤 1基础系统配置选择你的 STM32 芯片型号如 STM32F103C8T6RCC 配置选择外部晶振HSE配置系统时钟为 72MHz选择外部晶振作为系统时钟设置为72Mhz时钟主频 速度更快3.SYS 配置Debug 选择 Serial Wire如果没勾选可能导致下一次无法烧录需要通过拉BOOT。开启SW调试下载模式步骤 2USART2 串口配置左侧Connectivity→ 选择USART2模式选择Asynchronous异步通信参数设置与 GPS 模块一致波特率9600字长8 Bits校验位None停止位1无需修改硬件默认引脚TXPA2RXPA3。选择串口2异步模式设置为9600波特率步骤 3开启串口接收中断左侧System Core→NVIC勾选USART2 global interrupt使能中断优先级默认即可点击 OK。开启串口2中断同理可以开启串口1作为测试接口打印一些结果步骤 4生成工程代码点击GENERATE CODE生成 MDK/STM32CubeIDE 工程打开工程准备添加 GPS 驱动代码。三核心代码gps.h gps.c 完整文件1. GPS 头文件gps.h#ifndef _GPS_H#define _GPS_H#include main.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#define GPSRX_LEN_MAX 255// GPS数据存储结构体typedef struct SaveData{char GPS_Buffer[GPS_Buffer_Length];char isGetData; // 是否获取到完整GPS数据char isParseData; // 是否解析完成char UTCTime[UTCTime_Length]; // UTC时间char latitude[latitude_Length];// 纬度char N_S[N_S_Length]; // 南北纬标识 N/Schar longitude[longitude_Length];// 经度char E_W[E_W_Length]; // 东西经标识 E/Wchar isUsefull; // 定位信息是否有效char UTCDate[15]; // GPS日期DDMMYY} _SaveData;// 函数声明void BSP_GPS_IRQHandler(uint8_t dat);void parseGpsBuffer(void);void printGpsBuffer(void);float gps_str_to_float(char *dm_str, uint8_t is_longitude);void str_time_to_arr(char *utc_str, uint8_t *time);void gps_date_split(char *date_str, uint8_t *day);uint8_t Get_Week(int year, int month, int day);#endif2. GPS 驱动文件gps.c#include gps.h#include stdlib.h#include string.h#include stdio.h// GPS接收全局变量unsigned char GPSRX_BUFF[GPSRX_LEN_MAX];unsigned char GPSRX_LEN 0;_SaveData Save_Data;#define GPRMC_FRAME_MAX_LEN 80/******************************************************************* 函 数 名 称BSP_GPS_IRQHandler* 函 数 说 明GPS串口中断接收函数******************************************************************/void BSP_GPS_IRQHandler(uint8_t dat){// 边界检查防止数组越界if(GPSRX_LEN (GPSRX_LEN_MAX - 1)){GPSRX_LEN 0;memset((void*)GPSRX_BUFF, 0, GPSRX_LEN_MAX);}// 帧头$重置接收缓存if(dat $){GPSRX_LEN 0;memset((void*)GPSRX_BUFF, 0, GPSRX_LEN_MAX);}GPSRX_BUFF[GPSRX_LEN] dat;// 识别$GPRMC帧接收完成if(GPSRX_BUFF[0] $ strncmp((char*)GPSRX_BUFF[1], GPRMC, 4) 0){if(dat \n GPSRX_LEN GPRMC_FRAME_MAX_LEN){memcpy(Save_Data.GPS_Buffer, (char*)GPSRX_BUFF, GPSRX_LEN);Save_Data.GPS_Buffer[GPSRX_LEN] \0;Save_Data.isGetData 1;// 重置缓存GPSRX_LEN 0;memset((void*)GPSRX_BUFF, 0, GPSRX_LEN_MAX);}}}/******************************************************************* 函 数 名 称parseGpsBuffer* 函 数 说 明解析GPRMC数据帧******************************************************************/void parseGpsBuffer(void){char *subString;char *subStringNext;char i 0;if (Save_Data.isGetData){Save_Data.isGetData 0;for (i 0 ; i 9 ; i){if (i 0){if ((subString strstr(Save_Data.GPS_Buffer, ,)) NULL)printf(GPS_1\r\n);}else{subString;if ((subStringNext strstr(subString, ,)) ! NULL){char usefullBuffer[2];switch(i){case 1:memcpy(Save_Data.UTCTime, subString, subStringNext - subString);break;case 2:memcpy(usefullBuffer, subString, subStringNext - subString);break;case 3:memcpy(Save_Data.latitude, subString, subStringNext - subString);break;case 4:memcpy(Save_Data.N_S, subString, subStringNext - subString);break;case 5:memcpy(Save_Data.longitude, subString, subStringNext - subString);break;case 6:memcpy(Save_Data.E_W, subString, subStringNext - subString);break;case 9:memcpy(Save_Data.UTCDate, subString, subStringNext - subString);break;default:break;}subString subStringNext;Save_Data.isParseData 1;if(usefullBuffer[0] A)Save_Data.isUsefull 1;else if(usefullBuffer[0] V)Save_Data.isUsefull 0;}else{printf(GPS_2\r\n);}}}}}/******************************************************************* 函 数 名 称printGpsBuffer* 函 数 说 明串口打印GPS解析数据******************************************************************/void printGpsBuffer(void){if (Save_Data.isParseData){Save_Data.isParseData 0;printf(UTC时间: %s\r\n,Save_Data.UTCTime);if(Save_Data.isUsefull){Save_Data.isUsefull 0;printf(纬度: %s %s\r\n,Save_Data.latitude,Save_Data.N_S);printf(经度: %s %s\r\n,Save_Data.longitude,Save_Data.E_W);}else{printf(GPS 定位无效!\r\n);}}}/******************************************************************* 函 数 名 称gps_str_to_float* 函 数 说 明度分格式转十进制经纬度******************************************************************/float gps_str_to_float(char *dm_str, uint8_t is_longitude){if(dm_str NULL || strlen(dm_str) 0){return 0.0f;}float degree 0.0f;float minute 0.0f;char degree_str[4] {0};char minute_str[15] {0};uint16_t dm_len strlen(dm_str);if(is_longitude){if(dm_len 3) return 0.0f;memcpy(degree_str, dm_str, 3);strncpy(minute_str, dm_str 3, dm_len - 3);}else{if(dm_len 2) return 0.0f;memcpy(degree_str, dm_str, 2);strncpy(minute_str, dm_str 2, dm_len - 2);}degree atof(degree_str);minute atof(minute_str);return (degree minute / 60.0f);}/******************************************************************* 函 数 名 称str_time_to_arr* 函 数 说 明UTC时间转北京时间(UTC8)******************************************************************/void str_time_to_arr(char *utc_str, uint8_t *time){uint8_t utc_hour, utc_min;if(utc_str NULL || time NULL) return;utc_hour (utc_str[0] - 0) * 10 (utc_str[1] - 0);utc_min (utc_str[2] - 0) * 10 (utc_str[3] - 0);time[0] utc_hour 8;if(time[0] 24) time[0] - 24;time[1] utc_min;}/******************************************************************* 函 数 名 称gps_date_split* 函 数 说 明拆分GPS日期******************************************************************/void gps_date_split(char *date_str, uint8_t *day){if(date_str NULL || day NULL) return;day[0] (date_str[0]-0)*10 (date_str[1]-0);day[1] (date_str[2]-0)*10 (date_str[3]-0);day[2] (date_str[4]-0)*10 (date_str[5]-0);}/******************************************************************* 函 数 名 称Get_Week* 函 数 说 明计算星期几******************************************************************/uint8_t Get_Week(int year, int month, int day){if(month 1 || month 2){month 12;year--;}int week (day 2*month 3*(month1)/5 year year/4 - year/100 year/400) % 7;return (uint8_t)week;}四使用实例main.c 中调用 GPS 驱动1. 第一步添加串口中断回调在工程中找到stm32xx_it.c文件添加以下代码USART2 中断处理#include gps.h// 定义GPS串口接收变量uint8_t rx2;// 串口接收完成回调函数void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){if (huart-Instance USART2){// 把接收到的数据传给GPS处理函数BSP_GPS_IRQHandler(rx2);// 重新开启中断接收HAL_UART_Receive_IT(huart2, rx2, 1);}}2. 第二步main.c 完整使用代码#include main.h#include usart.h#include gpio.h#include gps.h#include stdio.h// 全局变量uint8_t beijing_time[2]; // 北京时间[小时,分钟]uint8_t gps_date[3]; // 日期[日,月,年]uint8_t week; // 星期char *week_table[] {周一,周二,周三,周四,周五,周六,周日};// 重定向printfint fputc(int ch ,FILE *f){HAL_UART_Transmit(huart1,(uint8_t *)ch,1,1000);return ch;}int main(void){// 1. 系统初始化HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART1_UART_Init(); // 调试串口MX_USART2_UART_Init(); // GPS串口// 2. 开启GPS串口中断接收HAL_UART_Receive_IT(huart2, rx2, 1);while (1){// 3. 解析GPS数据parseGpsBuffer();// 4. 打印GPS原始数据printGpsBuffer();// 5. 数据格式转换if(Save_Data.isParseData Save_Data.isUsefull){// 转换北京时间str_time_to_arr(Save_Data.UTCTime, beijing_time);// 拆分日期gps_date_split(Save_Data.UTCDate, gps_date);// 计算星期week Get_Week(2000 gps_date[2], gps_date[1], gps_date[0]);// 转换十进制经纬度float lat gps_str_to_float(Save_Data.latitude, 0);float lon gps_str_to_float(Save_Data.longitude, 1);// 打印转换后数据printf(\r\n);printf(北京时间: %02d:%02d\r\n, beijing_time[0], beijing_time[1]);printf(日期: 20%02d-%02d-%02d\r\n, gps_date[2], gps_date[1], gps_date[0]);printf(星期: %s\r\n, week_table[week]);printf(十进制纬度: %.5f\r\n, lat);printf(十进制经度: %.5f\r\n, lon);printf(\r\n\r\n);}HAL_Delay(1000);}}测试帧6,03,40,105,27,14,86,293,36,19,36,290,37*7B$GPGSV,2,2,07,23,,,10,30,24,208,26,50,37,132,31*76$GPGLL,3438.65354,N,11257.61544,E,072530.00,A,A*65$GPRMC,072531.00,A,3438.65335,N,11257.61546,E,0.019,,130226,,,A*74$GPVTG,,T,,M,0.019,N,0.036,K,A*2E$GPGGA,072531.00,3438.65335,N,11257.61546,E,1,05,1.40,276.4,M,-17.0,M,,*78$GPGSA,A,3,01,30,19,03,14,,,,,,,,3.05,1.40,2.71*09$GPGSV,2,1,07,01,44,044,36,03,40,105,28,14,86,293,37,19,36,290,38*7A$GPGSV,2,2,07,23,,,12,30,24,208,26,50,37,132,32*77也可参考下面文章手把手教你一步步使用基于HAL库的STM32的GPS模块解析(NEO-6M-GPS)_stm32 hal库的 给gps 编程-CSDN博客