(一)模块介绍:NEO-6M GPS 模块

1. 模块核心特性

  • 型号:NEO-6M 是嵌入式开发中最常用的民用 GPS 定位模块;
  • 通信方式UART 串口(TTL 电平),默认配置:9600波特率、8N1
  • 输出协议:标准 NMEA-0183 定位协议,自动发送多帧定位数据;
  • 核心数据帧$GPRMC 帧,包含:✅ UTC 时间 / 日期 ✅ 经纬度原始数据 ✅ 定位有效状态 ✅ 速度航向信息
  • 硬件接口:共 4 个引脚(VCC、GND、TX、RX),3.3V 供电,切记需要到室外,该模块室内无法获取信息!

2. 硬件连接规则

STM32 ↔ NEO-6M

  • VCC ↔ 3.3V
  • GND ↔ GND
  • USART2_RX(PA3) ↔ GPS_TX
  • USART2_TX(PA2) ↔ GPS_RX

3. 定位说明

模块需要在室外空旷环境才能定位成功,室内 / 遮挡环境会提示定位无效。

(二)初始化:STM32CUBEMX 配置步骤

本工程使用 USART2 接收 GPS 数据,按照以下步骤配置:

步骤 1:基础系统配置

  1. 选择你的 STM32 芯片型号(如 STM32F103C8T6);
  2. RCC 配置:选择外部晶振(HSE),配置系统时钟为 72MHz;

选择外部晶振作为系统时钟

设置为72Mhz时钟主频 速度更快

3.SYS 配置:Debug 选择 Serial Wire(如果没勾选可能导致下一次无法烧录需要通过拉BOOT)。

    开启SW调试下载模式

    步骤 2:USART2 串口配置

    1. 左侧 Connectivity → 选择 USART2
    2. 模式选择:Asynchronous(异步通信);
    3. 参数设置(与 GPS 模块一致):
      • 波特率:9600
      • 字长:8 Bits
      • 校验位:None
      • 停止位:1
    4. 无需修改硬件默认引脚(TX=PA2,RX=PA3)。

    选择串口2异步模式,设置为9600波特率

    步骤 3:开启串口接收中断

    1. 左侧 System CoreNVIC
    2. 勾选 USART2 global interrupt 使能中断;
    3. 优先级默认即可,点击 OK。

    开启串口2中断

    同理可以开启串口1作为测试接口,打印一些结果

      步骤 4:生成工程代码

      1. 点击 GENERATE CODE 生成 MDK/STM32CubeIDE 工程;
      2. 打开工程,准备添加 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/S

          char longitude[longitude_Length];// 经度

          char E_W[E_W_Length];          // 东西经标识 E/W

          char 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);

      #endif

      2. 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时间转北京时间(UTC+8)

      ******************************************************************/

      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*(month+1)/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[] = {"周一","周二","周三","周四","周五","周六","周日"};

      // 重定向printf

      int 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博客

      Logo

      AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

      更多推荐