文章目录
- 一、ATGM336H
-
- [ 1、简介](# 1、简介)
- [ 2、引脚说明](# 2、引脚说明)
- [ 3、数据帧](# 3、数据帧)
- 二、实例代码
一、ATGM336H
1、简介
ATGM336H 是一款由中科微电子设计生产的中高精度、高性能 GNSS 定位模块。

支持的卫星系统:
支持 BDS、GPS、GALILEO、QZSS、IRNSS 等卫星导航系统,可接收 B1I、B1C、B2a、L1、L5、E1、E5a 等多频点信号,还支持 SBAS 星基增强系统。
性能指标:
水平定位精度(CEP50)可达 1.0m;冷启动时间为 20s,热启动时间为 1s,重捕获时间为 1s;跟踪灵敏度可达 - 162dBm,冷启动捕获灵敏度为 - 148dBm。
功耗与供电:
功耗为 31mA@3.3V,主电源供电电压为 2.7V 至 3.6V。
数据输出:
输出频率最高为 10Hz,默认波特率为 115200,数据格式为 NMEA0183。
尺寸与封装:
封装尺寸为 10.1mm×9.7mm×2.1mm。
特点:
模块提供标准的 UART 串行接口,也可选择支持 I2C 接口,内置 1PPS 秒脉冲输出,可提供高精度的时间同步信号。此外,模块还支持 AGNSS 功能,可利用网络辅助数据加速定位。
应用领域:
适用于共享单车、电单车、宠物跟踪器等多种场景,还可用于车载导航、便携式手持设备、无人机、物联网设备等。
2、引脚说明

如果单纯做定位,只需要前4个引脚即可,也即PPS引脚用不到,所以我们PPS这个引脚可以不接,空着就行。
3、数据帧
GPS模块通过串口将数据发给主控芯片,ATGM336H会一次性返回多条信息,其中信息头的第一个是消息ID,标示着通过什么定位系统的采集的数据。

本次学习只关注"GNRMC"信息。
例:$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
字段8:方位角,度
字段9:UTC日期,DDMMYY格式
字段10:磁偏角,(000 - 180)度(前导位数不足则补0)
字段11:磁偏角方向,E=东W=西
字段16:校验值
二、实例代码
在LCD1602上显示经纬度。
c
#include <REGX52.H>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "LCD1602.h"
// 定义串口缓冲区大小(根据实际GPS波特率/数据长度调整)
#define GPRMC_BUF_SIZE 80
// 经纬度放大倍数(用整数代替浮点:如48.1173° → 481173,避免浮点运算)
#define LAT_LON_SCALE 10000
// 串口接收缓冲区相关
#define UART_BUF_SIZE 100 // 串口接收缓冲区大小(可容纳完整GPRMC语句)
// 全局变量:串口接收缓冲区(中断中使用,需全局)
unsigned char uart_buf[UART_BUF_SIZE]; // 存储接收到的串口数据
unsigned int uart_buf_idx = 0; // 缓冲区索引
bit gprmc_ready = 0; // GPRMC语句接收完成标志
// GPRMC解析结果结构体
typedef struct {
unsigned char valid; // 定位有效性:1=有效(A),0=无效(V)
unsigned char utc_time[7]; // UTC时间 hhmmss(6位+结束符)
unsigned char utc_date[7]; // UTC日期 ddmmyy(6位+结束符)
long latitude; // 纬度(十进制*10000,如48.1173°→481173)
long longitude; // 经度(十进制*10000,如11.5167°→115167)
unsigned char lat_dir; // 纬度方向:'N'/'S'
unsigned char lon_dir; // 经度方向:'E'/'W'
unsigned int speed_knots; // 速度(节,放大10倍:22.4→224)
unsigned int course; // 航向(度,放大10倍:84.4→844)
} GPRMC_Data;
// 全局/局部解析结果存储(根据实际需求调整存储位置)
GPRMC_Data gprmc_data;
// ------------------- 经纬度转字符串函数 -------------------
/**
* @brief 将纬度转换为字符串(如 481173 → "48.1173°N")
* @param lat: 解析后的纬度整数(放大10000倍)
* @param lat_dir: 纬度方向('N'/'S')
* @param str_buf: 输出字符串缓冲区(至少12字节:如"-12.3456°S")
* @retval 0:失败,1:成功
*/
unsigned char Lat2Str(long lat, unsigned char lat_dir, unsigned char *str_buf)
{
long abs_lat = 0;
unsigned int int_part = 0;
unsigned int dec_part = 0;
if(str_buf == NULL) return 0;
memset(str_buf, 0, 12); // 初始化缓冲区
abs_lat = lat > 0 ? lat : -lat; // 取绝对值(处理负数)
int_part = abs_lat / LAT_LON_SCALE; // 整数部分(481173→48)
dec_part = abs_lat % LAT_LON_SCALE; // 小数部分(481173→1173)
// 拼接字符串:符号(可选)+ 整数.小数 + ° + 方向
if(lat < 0) {
sprintf((char*)str_buf, "-%d.%04d %c", int_part, dec_part, lat_dir);
} else {
sprintf((char*)str_buf, "%d.%04d %c", int_part, dec_part, lat_dir);
}
return 1;
}
/**
* @brief 将经度转换为字符串(如 115167 → "11.5167°E")
* @param lon: 解析后的经度整数(放大10000倍)
* @param lon_dir: 经度方向('E'/'W')
* @param str_buf: 输出字符串缓冲区(至少12字节)
* @retval 0:失败,1:成功
*/
unsigned char Lon2Str(long lon, unsigned char lon_dir, unsigned char *str_buf)
{
long abs_lon = 0;
unsigned int int_part = 0;
unsigned int dec_part = 0;
if(str_buf == NULL) return 0;
memset(str_buf, 0, 12);
abs_lon = lon > 0 ? lon : -lon;
int_part = abs_lon / LAT_LON_SCALE;
dec_part = abs_lon % LAT_LON_SCALE;
if(lon < 0) {
sprintf((char*)str_buf, "-%d.%04d %c", int_part, dec_part, lon_dir);
} else {
sprintf((char*)str_buf, "%d.%04d %c", int_part, dec_part, lon_dir);
}
return 1;
}
unsigned char ParseGPRMC(unsigned char *gprmc_buf, GPRMC_Data *p_data)
{
unsigned char *ptr = NULL;
unsigned char field_idx = 0; // 字段索引(对应GPRMC各字段)
unsigned char field_buf[20]; // 单个字段缓冲区
unsigned char buf_idx = 0;
// 1. 基础校验:非空、以$GPRMC开头
if(gprmc_buf == NULL || p_data == NULL) return 0;
if(strstr((char*)gprmc_buf, "$GPRMC") == NULL) return 0;
// 2. 初始化解析结果结构体
memset(p_data, 0, sizeof(GPRMC_Data));
// 3. 按逗号分割字段(核心解析逻辑)
ptr = gprmc_buf;
while(*ptr != '\0' && field_idx <= 12) // GPRMC共13个核心字段(0-12)
{
if(*ptr == ',' || *ptr == '*') // 字段分隔符或校验和起始符
{
field_buf[buf_idx] = '\0'; // 字段结束符
buf_idx = 0; // 重置字段缓冲区索引
// 4. 按字段索引解析对应数据
switch(field_idx)
{
case 1: // UTC时间(hhmmss)
if(strlen((char*)field_buf) == 6) {
strcpy((char*)p_data->utc_time, (char*)field_buf);
}
break;
case 2: // 状态(A=有效,V=无效)
if(field_buf[0] == 'A') {
p_data->valid = 1;
} else if(field_buf[0] == 'V') {
p_data->valid = 0;
} else {
return 0; // 状态非法,解析失败
}
break;
case 3: // 纬度(ddmm.mmmm)
if(strlen((char*)field_buf) >= 5) {
// 提取度(前2位)和分(后几位),转换为十进制整数
unsigned int deg = atoi((char*)field_buf);
unsigned int min = atoi((char*)field_buf + 2);
float lat_float = (deg / 100) + (min % 100 + (atof((char*)field_buf+4))/10000) / 60.0;
p_data->latitude = (long)(lat_float * LAT_LON_SCALE);
}
break;
case 4: // 纬度方向(N/S)
p_data->lat_dir = field_buf[0];
if(p_data->lat_dir == 'S') {
p_data->latitude = -p_data->latitude; // 南半球取负
}
break;
case 5: // 经度(dddmm.mmmm)
if(strlen((char*)field_buf) >= 6) {
unsigned int deg = atoi((char*)field_buf);
unsigned int min = atoi((char*)field_buf + 3);
float lon_float = (deg / 100) + (min % 100 + (atof((char*)field_buf+5))/10000) / 60.0;
p_data->longitude = (long)(lon_float * LAT_LON_SCALE);
}
break;
case 6: // 经度方向(E/W)
p_data->lon_dir = field_buf[0];
if(p_data->lon_dir == 'W') {
p_data->longitude = -p_data->longitude; // 西半球取负
}
break;
case 7: // 速度(节),放大10倍存储(22.4→224)
p_data->speed_knots = (unsigned int)(atof((char*)field_buf) * 10);
break;
case 8: // 航向(度),放大10倍存储(84.4→844)
p_data->course = (unsigned int)(atof((char*)field_buf) * 10);
break;
case 9: // UTC日期(ddmmyy)
if(strlen((char*)field_buf) == 6) {
strcpy((char*)p_data->utc_date, (char*)field_buf);
}
break;
default:
break;
}
field_idx++; // 下一个字段
if(*ptr == '*') break; // 校验和之后无有效字段,退出循环
}
else
{
// 存储当前字符到字段缓冲区(避免缓冲区溢出)
if(buf_idx < sizeof(field_buf)-1) {
field_buf[buf_idx++] = *ptr;
}
}
ptr++; // 下一个字符
}
// 5. 最终校验:必须包含有效状态和经纬度
if(p_data->valid == 0 || p_data->latitude == 0 || p_data->longitude == 0) {
return 0;
}
return 1; // 解析成功
}
// ------------------- 串口初始化函数(必须先初始化) -------------------
void UART_Init(void)
{
TMOD |= 0x20; // 定时器1工作在模式2(8位自动重装)
TH1 = 0xFD; // 波特率9600(晶振11.0592MHz)
TL1 = 0xFD;
TR1 = 1; // 启动定时器1
SCON = 0x50; // 串口工作在模式1,允许接收(REN=1)
EA = 1; // 开启总中断
ES = 1; // 开启串口中断
}
// ------------------- 串口接收中断服务函数(核心) -------------------
void RECEIVE_DATA(void) interrupt 4 using 3
{
unsigned char recv_char; // 接收的单个字符
// 1. 清除中断标志(必须!否则会重复进入中断)
if(RI) // RI=1表示串口接收到一个字符
{
RI = 0; // 清除接收中断标志
recv_char = SBUF; // 读取接收寄存器中的字符
// 2. 数据缓存:存储字符到缓冲区,避免溢出
if(uart_buf_idx < UART_BUF_SIZE - 1)
{
// 关键:GPRMC语句以换行符(\n或\r)结束,以此判断接收完成
if(recv_char == '\n' || recv_char == '\r')
{
uart_buf[uart_buf_idx] = '\0'; // 字符串结束符
// 判断是否是GPRMC语句(以$GPRMC开头)
if(strstr((char*)uart_buf, "$GPRMC") != NULL)
{
gprmc_ready = 1; // 标记GPRMC语句接收完成
}
uart_buf_idx = 0; // 重置缓冲区,准备接收下一条语句
}
else
{
uart_buf[uart_buf_idx++] = recv_char; // 存储字符
}
}
else
{
// 缓冲区溢出,重置(避免数据错乱)
uart_buf_idx = 0;
memset(uart_buf, 0, UART_BUF_SIZE);
}
}
}
void main() {
// 模拟GPS串口接收的GPRMC数据
// unsigned char gprmc_test[] = "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A";
unsigned char lat_str[12]; // 纬度字符串缓冲区
unsigned char lon_str[12]; // 经度字符串缓冲区
LCD_Init();
UART_Init(); // 初始化串口(必须先执行)
while (1) {
// 检测GPRMC语句是否接收完成
if(gprmc_ready)
{
gprmc_ready = 0; // 清除标志
// 解析GPRMC数据
if(ParseGPRMC(uart_buf, &gprmc_data))
{
// 经纬度转字符串
Lat2Str(gprmc_data.latitude, gprmc_data.lat_dir, lat_str);
Lon2Str(gprmc_data.longitude, gprmc_data.lon_dir, lon_str);
LCD_ShowString(1,0,lat_str);
LCD_ShowString(2,0,lon_str);
}
}
}
}