C51_ATGM336H_GPS

文章目录

  • 一、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);
	        }
	        }
	
	        }
	}
相关推荐
v先v关v住v获v取8 小时前
塔式立体车库5张cad+设计说明书+三维图
科技·单片机·51单片机
v先v关v住v获v取10 小时前
杨柳絮收集装置设计cad1张总图+三维图+设计说明书
科技·单片机·51单片机
v先v关v住v获v取11 小时前
草莓采摘机器人结构设计2张cad+设计说明书+三维图
科技·单片机·51单片机
恶魔泡泡糖13 小时前
51单片机蜂鸣器应用
单片机·嵌入式硬件·51单片机
梁下轻语的秋缘1 天前
I2S与I2C
运维·stm32·单片机·51单片机
Q_21932764551 天前
基于单片机的RFID门禁系统设计
单片机·嵌入式硬件·51单片机
就是蠢啊1 天前
51单片机——LCD1602液晶显示
单片机·嵌入式硬件·51单片机
v先v关v住v获v取1 天前
3D打印机的定量铺粉器设计13张 +三维图+设计说明书
科技·单片机·51单片机
v先v关v住v获v取1 天前
汽车后桥壳体工艺及夹具设计“11张cad+设计说明书+
科技·单片机·51单片机