一、基于freertos系统上关于ATGM336H定位模块的定位测试验证
·
一、硬件连接

模块引脚 连接目标 说明
TX 串口助手接收端(RX) 交叉连接,用于模块发送数据到上位机
RX 不接 测试阶段无需发送指令,可悬空
VCC 5V/3.3V 根据模块版本选择:多数ATGM336H型号需5V供电(具体以规格书为准)
GND GND 必须与上位机共地,否则会导致数据乱码或通信失败
注意:
1、模块上的led灯闪烁时,相当于提示定位信息获取到了,常量表示定位失效。
2、断电重启模块,更新信息状态。
3、定位时,天线上的陶瓷片向上(陶瓷片上有金属小圆点一面朝向天空),朝向空旷的天空,高楼会遮挡信号导致定位不到。
4、模块的通信默认用9600波特率通信的。
二、代码部分
定义部分
#define DMA_BUF_SIZE 256 // DMA接收缓冲区大小
#define NMEA_QUEUE_LEN 8 // NMEA帧队列长度
#define GNGGA_HEAD "$GNGGA"
#define LAT_LON_SCALE 60.0f // 度分转度的系数
#define GNGGA_MIN_FIELD_CNT 6
typedef struct {
uint8_t valid; // 定位有效标志(1有效,0无效)
char time[7]; // UTC时间(HHMMSS)
float latitude; // 纬度(度)
char lat_dir; // 纬度方向(N/S)
float longitude; // 经度(度)
char lon_dir; // 经度方向(E/W)
uint8_t sat_num; // 锁定卫星数(新增,适配GNGGA)
uint8_t parse_ok; // 解析成功标志
} GnssInfo_t;
extern GnssInfo_t gnss_info ;
float nmea_degree_convert(char *str); // 度分转度
extern uint8_t dma_rx_buf[DMA_BUF_SIZE];
extern volatile uint16_t dma_rx_len;
// 开启USART2空闲中断
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
// 启动DMA接收(循环模式)
HAL_UART_Receive_DMA(&huart2, dma_rx_buf, DMA_BUF_SIZE);
void USART2_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart2);
USART2_IDLE_IRQHandler(); // 处理空闲中断
}
// 打印定位信息
void USART2_IDLE_IRQHandler(void)
{
if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET)
{
__HAL_UART_CLEAR_IDLEFLAG(&huart2); // 清除空闲中断标志
HAL_UART_DMAStop(&huart2); // 停止DMA接收
// 计算接收长度
dma_rx_len = DMA_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
}
}
float nmea_degree_convert(char *str)
{
if (str == NULL || strlen(str) < 4 || strchr(str, '.') == NULL)
{
return 0.0f;
}
float degree = 0.0f;
float minute = 0.0f;
char temp[16] = {0};
int dot_pos = strchr(str, '.') - str;
// 纬度:DDMM.mmmm → 前2位度
if (strlen(str) >= 4 && dot_pos >= 2)
{
strncpy(temp, str, 2);
degree = atof(temp);
minute = atof(str + 2);
}
// 经度:DDDMM.mmmm → 前3位度
else if (strlen(str) >= 5 && dot_pos >= 3)
{
strncpy(temp, str, 3);
degree = atof(temp);
minute = atof(str + 3);
}
else
{
return 0.0f;
}
// 防止转换后为0(有效经纬度不可能为0)
float res = degree + (minute / LAT_LON_SCALE);
return (res > 0.0f) ? res : 0.0f;
}
任务调用部分
//获取信息
void StartTask01(void *argument)
{
uint8_t nmea_buf[DMA_BUF_SIZE] = {0};
for (;;)
{
if (dma_rx_len > 0)
{
// 拷贝DMA接收的数据到临时缓冲区
memcpy (nmea_buf, dma_rx_buf, dma_rx_len);
nmea_buf[dma_rx_len] = '\0'; // 字符串结束符
//输出串口接收信息
HAL_UART_Transmit (&huart1, (uint8_t*) &dma_rx_buf, dma_rx_len, 8000);
printf ("\r\n");
// 发送到NMEA解析队列
osMessageQueuePut (myQueue01Handle, nmea_buf, 0, 100);
// 重置DMA接收
dma_rx_len = 0;
memset (dma_rx_buf, 0, DMA_BUF_SIZE);
HAL_UART_Receive_DMA (&huart2, dma_rx_buf, DMA_BUF_SIZE);
}
osDelay (5);
}
}
//解析部分
void StartTask02 (void *argument)
{
uint8_t nmea_buf[DMA_BUF_SIZE] ={ 0 };
for (;;)
{
// 解析任务核心逻辑
if (osMessageQueueGet(myQueue01Handle, nmea_buf, NULL, 100) == osOK)
{
gnss_info.parse_ok = 0;
char *gngga_frame = strstr((char*)nmea_buf, GNGGA_HEAD);
if (gngga_frame != NULL && strlen(gngga_frame) > 20)
{
char *token = NULL;
char *save_ptr = NULL;
int field_idx = 0;
int field_cnt = 0;
// 临时变量:先存储经纬度字符串,等定位质量解析后再转换
char lat_str[16] = {0};
char lon_str[16] = {0};
token = strtok_r(gngga_frame, ",", &save_ptr);
while (token != NULL)
{
field_cnt++;
if (strlen(token) == 0)
{
token = strtok_r(NULL, ",", &save_ptr);
field_idx++;
continue;
}
switch (field_idx)
{
case 0: // $GNGGA:帧头校验
if (strcmp(token, GNGGA_HEAD) != 0)
{
field_idx = -1;
break;
}
break;
case 1: // UTC时间(HHMMSS.ss)
if (strlen(token) >= 6)
{
strncpy(gnss_info.time, token, 6);
gnss_info.time[6] = '\0';
}
else
{
memset(gnss_info.time, 0, sizeof(gnss_info.time));
}
break;
// 先存储经纬度字符串(不立即转换)
case 2: // 纬度字符串(DDMM.mmmm)
strncpy(lat_str, token, sizeof(lat_str)-1);
break;
case 3: // 纬度方向(N/S)
if (token[0] == 'N' || token[0] == 'S')
{
gnss_info.lat_dir = token[0];
}
else
{
gnss_info.lat_dir = '\0';
}
break;
case 4: // 经度字符串(DDDMM.mmmm)
strncpy(lon_str, token, sizeof(lon_str)-1);
break;
case 5: // 经度方向(E/W)
if (token[0] == 'E' || token[0] == 'W')
{
gnss_info.lon_dir = token[0];
}
else
{
gnss_info.lon_dir = '\0';
}
break;
case 6: // 定位质量(核心:先解析!)
gnss_info.valid = (atoi(token) == 1) ? 1 : 0;
// 定位有效时,再转换经纬度(关键修复!)
if (gnss_info.valid)
{
if (strlen(lat_str) >= 4)
gnss_info.latitude = nmea_degree_convert(lat_str);
if (strlen(lon_str) >= 5)
gnss_info.longitude = nmea_degree_convert(lon_str);
}
break;
case 7: // 卫星数
gnss_info.sat_num = atoi(token);
break;
default:
if (field_idx >= 8) // 解析到卫星数后停止
{
token = NULL;
break;
}
break;
}
if (field_idx == -1)
{
token = NULL;
break;
}
token = strtok_r(NULL, ",", &save_ptr);
field_idx++;
}
// 放宽最终校验条件(适配实际场景)
if (field_cnt >= 7 // 至少7个字段(核心字段)
&& gnss_info.valid == 1 // 定位质量=1
&& gnss_info.lat_dir != '\0' // 纬度方向有效
&& gnss_info.lon_dir != '\0' // 经度方向有效
&& gnss_info.latitude > 0.0f // 纬度>0(有效范围)
&& gnss_info.longitude > 0.0f // 经度>0(有效范围)
&& gnss_info.sat_num >= 1) // 卫星数≥1(放宽)
{
gnss_info.parse_ok = 1;
// 【调试打印】解析成功提示
printf("解析成功!纬度:%.6f%c,经度:%.6f%c,utc时间:%s,定位质量=%d,卫星数=%d\r\n",
gnss_info.latitude, gnss_info.lat_dir,
gnss_info.longitude, gnss_info.lon_dir,gnss_info.time,gnss_info.valid, gnss_info.sat_num);
}
else
{
memset(&gnss_info, 0, sizeof(GnssInfo_t));
printf("解析失败:字段数=%d,定位质量=%d,卫星数=%d\r\n",
field_cnt, gnss_info.valid, gnss_info.sat_num);
}
}
}
osDelay (10);
}
}
测试效果示意图:

源码:https://download.csdn.net/download/weixin_44386927/92460926?spm=1001.2014.3001.5501
更多推荐
所有评论(0)