AN1409 ATK-NEO-6M GPS 模块使用 
本应用文档(AN1409,对应战舰 STM32 开发板扩展实验 3/MiniSTM32 开发板扩展实验
12)将教大家如何在 ALIENTEK STM32 开发板上使用 ATK-NEO-6M GPS 模块(注意,本文档同
时适用 ALIENTEK 战舰和 MiniSTM32 两款开发板),并实现 GPS 定位。 
本文档分为如下几部分: 
1, ATK-NEO-6M GPS 模块简介 
2, 硬件连接 
3, 软件实现 
4, 验证 
1、ATK-NEO-6M GPS 模块简介 
ATK-NEO-6M-V23 模块,是 ALIENTEK 生产的一款高性能 GPS 模块,模块核心采用 UBLOX 
公司的 NEO-6M 模组,具有 50 个通道,追踪灵敏度高达-161dBm,测量输出频率最高可达
5Hz。ATK-NEO-6M-V23 模块具有以下特点: 
1, 模块采用 U-BLOX NEO-6M 模组,体积小巧,性能优异。 
2, 模块自带陶瓷天线及 MAXIM 公司 20.5dB 高增益 LNA 芯片,搜星能力强。 
3, 模块可通过串口进行各种参数设置,并可保存在 EEPROM,使用方便。 
4, 模块自带 IPX 接口,可以连接各种有源天线,适应能力强。 
5, 模块兼容 3.3V/5V 电平,方便连接各种单片机系统。 
6, 模块自带可充电后备电池,可以掉电保持星历数据 1。 
注 1:在主电源断开后,后备电池可以维持半小时左右的 GPS 星历数据的保存,以支持温启动或热启
动,从而实现快速定位。     
ATK-NEO-6M 模块非常小巧(25.5mm*31mm),模块通过 5 个 2.54mm 间距的排针与外
部连接,模块外观如图 1.1 所示: 
图 1.1 ATK-NEO-6M 模块外观图   
 
www.alientek.comALIENTEK STM32开发板AN1409www.openedv.com开源电子网2014-03-30
图 1.1 中,从右到左,依次为模块引出的 PIN1~PIN5 脚,各引脚的详细描述如表 1.1 所
示:   
序号 
1 
2 
3 
4 
5 
名称 
PPS 
RXD 
TXD 
GND 
VCC 
时钟脉冲输出脚 
说明 
模块串口接收脚(TTL 电平,不能直接接 RS232 电平!),可接单片机的 TXD 
模块串口发送脚(TTL 电平,不能直接接 RS232 电平!),可接单片机的 RXD 
地 
电源(3.3V~5.0V) 
表 1.1 ATK-NEO-6M 模块各引脚功能描述 
  其中,PPS 引脚同时连接到了模块自带了的状态指示灯:PPS,该引脚连接在 UBLOX 
NEO-6M 模组的 TIMEPULSE 端口,该端口的输出特性可以通过程序设置。PPS 指示灯(即 PPS
引脚),在默认条件下(没经过程序设置),有 2 个状态: 
1, 常亮,表示模块已开始工作,但还未实现定位。 
2, 闪烁(100ms 灭,900ms 亮),表示模块已经定位成功。 
这样,通过 PPS 指示灯,我们就可以很方便的判断模块的当前状态,方便大家使用。   
另外,图 1.1 中,左上角的 IPX 接口,可以用来外接一个有源天线,从而进一步提高模
块的接收能力,通过外接有源天线,我们就可以把模块放到室内,天线放到室外,实现室内
定位。       
ATK-NEO-6M 模块默认采用 NMEA-0183 协议输出 GPS 定位数据,并可以通过 UBX 协议
 
对模块进行配置,NMEA-0183 协议详细介绍请参考《ATK-NEO-6M 用户手册.pdf》,UBX 配置
协议,请参考《u-blox6_ReceiverDescriptionProtocolSpec_GPS.G6-SW-10018-C.pdf》。   
通过 ATK-NEO-6M GPS 模块,任何单片机(3.3V/5V 电源)都可以很方便的实现 GPS 定位,
当然他也可以连接电脑,利用电脑软件实现定位。ATK-NEO-6M-V12  GPS 模块的原理图如图
1.2 所示: 
图 1.2 ATK-NEO-6M GPS 模块原理图 
 
2、硬件连接 
本实验功能简介:通过串口 2 连接 ATK-NEO-6M GPS 模块,然后通过液晶显示 GPS 信息,
www.alientek.comALIENTEK STM32开发板AN1409www.openedv.com开源电子网2014-03-30
包括精度、纬度、高度、速度、用于定位的卫星数、可见卫星数、UTC 时间等信息。同时,
可以通过 USMART 工具,设置 GPS 模块的刷新速率(最大支持 5Hz 刷新)和时钟脉冲的配
置。另外,通过 KEY0 按键,可以开启或关闭 NMEA 数据的上传(即输出到串口 1,方便开
发调试)。 
所要用到的硬件资源如下: 
1, 指示灯 DS0   
2, KEY0 按键 
3, 串口 1、串口 2 
4, TFTLCD 模块   
5, ATK-NEO-6M GPS 模块   
接下来,我们看看 ATK-NEO-6M GPS 模块同 ALIENTEK STM32 开发板的连接,前面我们介
绍了 ATK-NEO-6M 模块的接口,我们通过杜邦线连接模块和开发板的相应端口,连接关系如
表 2.1 所示: 
ATK-NEO-6M GPS 模块与开发板连接关系 
TXD 
GND 
3.3V/5V  GND 
PA3 
ATK-NEO-6M GPS 模块 
ALIENTEK STM32 开发板 
VCC 
RXD 
PA2 
表 2.1 ATK-NEO-6M 模块同 ALIENTEK STM32 开发板连接关系表 
表中 ATK-NEO-6M  GPS 模块的 VCC,因为我们的模块是可以 3.3V 或 5V 供电的,所以可
以接开发板的 3.3V 电源,也可以接开发板的 5V 电源,这个随便大家自己选择。另外,这里
我们没有用到模块的 PPS 引脚,所以没有和单片机进行连接。 
模块与开发板的连接是很简单,不过这里特别提醒大家: 
1, 请把 GPS 模块放到窗户边/阳台,否则可能收不到 GPS 信号。 
2, 如果想在室内开发,可以自备有源天线,将天线放外面,模块放室内,亦可实现定
位。也可以考虑使用 ALIENTEK 提供的蓝牙串口模块(ATK-HC05)一对,这样,我
们可以将 GPS 放到户外/窗口,而仍然在室内进行程序的调试开发。   
3, 如果使用的是战舰板,请把战舰 STM32 开发板 P9 端口的 PA2、PA3 与 48T、48R 的
跳线帽拔了!!否则开发板可能会检测不到 ATK-HC05 模块。 
3、软件实现 
本实验(注:这里仅以战舰板代码为例进行介绍,MiniSTM32 开发板对应代码几乎一
模一样,详见 MiniSTM32 开发板扩展实验 12),我们在扩展例程 1:ATK-HC05 蓝牙串口
模块实验的基础上修改,本例程用不到蓝牙模块,所以先删掉 hc05.c。 
然后,在 HARDWARE 文件夹里面新建一个 GPS 文件夹,并新建 gps.c,gps.h 两个文
件。然后在工程 HARDWARE 组里面添加 gps.c,并在工程添加 gps.h 的头文件包含路径。 
在 gps.c 里面,我们输入如下代码: 
#include "gps.h"   
#include "led.h"   
#include "delay.h"    
#include "usart2.h"   
 
 
 
 
 
 
 
 
 
 
 
 
       
 
       
#include "stdio.h" 
   
#include "stdarg.h"    
#include "string.h"     
#include "math.h" 
//从 buf 里面得到第 cx 个逗号所在的位置 
www.alientek.comALIENTEK STM32开发板AN1409www.openedv.com开源电子网2014-03-30
//返回值:0~0XFE,代表逗号所在位置的偏移. 
//              0XFF,代表不存在第 cx 个逗号 
 
u8 NMEA_Comma_Pos(u8 *buf,u8 cx) 
{ 
   
 
         
 
 
 
 
 
     
 
 
 
 
 
 
 
 
u8 *p=buf; 
while(cx) 
   
 
if(*buf=='*'||*buf<' '||*buf>'z')return 0XFF;//遇到非法字符,则不存在第 cx 个逗号 
if(*buf==',')cx--; 
buf++; 
{ 
 
 
 
} 
return buf-p; 
   
} 
//m^n 函数 
//返回值:m^n 次方. 
u32 NMEA_Pow(u8 m,u8 n) 
{ 
 
 
 
u32 result=1;     
while(n--)result*=m;         
return result; 
} 
//str 转换为数字,以','或者'*'结束 
//buf:数字存储区 
//dx:小数点位数,返回给调用函数 
//返回值:转换后的数值 
int NMEA_Str2num(u8 *buf,u8*dx) 
{ 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
u8 *p=buf; 
u32 ires=0,fres=0; 
u8 ilen=0,flen=0,i; 
u8 mask=0; 
int res; 
while(1) //得到整数和小数的长度 
{ 
 
 
 
 
 
 
 
} 
if(*p=='-'){mask|=0X02;p++;}//是负数 
if(*p==','||(*p=='*'))break;//遇到结束了 
if(*p=='.'){mask|=0X01;p++;}//遇到小数点了 
else if(*p>'9'||(*p<'0')) {ilen=0; flen=0; break;}//有非法字符 
if(mask&0X01)flen++; 
else ilen++; 
p++; 
if(mask&0X02)buf++; 
for(i=0;i
 
 
 
 
 
 
if(flen>5)flen=5; 
//最多取 5 位小数 
//小数点位数 
*dx=flen;    
for(i=0;isvnum=NMEA_Str2num(p1+posx,&dx); 
for(i=0;islmsg[slx].num=NMEA_Str2num(p1+posx,&dx);   
else break;   
posx=NMEA_Comma_Pos(p1,5+j*4);   
//得到卫星仰角 
if(posx!=0XFF)gpsx->slmsg[slx].eledeg=NMEA_Str2num(p1+posx,&dx);   
else break; 
posx=NMEA_Comma_Pos(p1,6+j*4);   
//得到卫星方位角 
if(posx!=0XFF)gpsx->slmsg[slx].azideg=NMEA_Str2num(p1+posx,&dx);   
else break;   
posx=NMEA_Comma_Pos(p1,7+j*4);   
//得到卫星信噪比 
if(posx!=0XFF)gpsx->slmsg[slx].sn=NMEA_Str2num(p1+posx,&dx); 
 
else break; 
slx++; 
       
}       
p=p1+1;//切换到下一个 GPGSV 信息 
www.alientek.comALIENTEK STM32开发板AN1409www.openedv.com开源电子网2014-03-30
 
}       
} 
//分析 GPGGA 信息 
//gpsx:nmea 信息结构体 
//buf:接收到的 GPS 数据缓冲区首地址 
void NMEA_GPGGA_Analysis(nmea_msg *gpsx,u8 *buf) 
{ 
 
 
 
 
 
 
 
 
 
u8 *p1,dx; 
 
 
   
u8 posx;         
p1=(u8*)strstr((const char *)buf,"$GPGGA"); 
posx=NMEA_Comma_Pos(p1,6);  
        //得到 GPS 状态 
if(posx!=0XFF)gpsx->gpssta=NMEA_Str2num(p1+posx,&dx);   
posx=NMEA_Comma_Pos(p1,7);  
 
//得到用于定位的卫星数 
if(posx!=0XFF)gpsx->posslnum=NMEA_Str2num(p1+posx,&dx);   
posx=NMEA_Comma_Pos(p1,9);  
 
//得到海拔高度 
if(posx!=0XFF)gpsx->altitude=NMEA_Str2num(p1+posx,&dx);     
} 
//分析 GPGSA 信息 
//gpsx:nmea 信息结构体 
//buf:接收到的 GPS 数据缓冲区首地址 
void NMEA_GPGSA_Analysis(nmea_msg *gpsx,u8 *buf) 
{ 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
u8 *p1,dx; 
 
 
   
u8 posx;   
u8 i;       
p1=(u8*)strstr((const char *)buf,"$GPGSA"); 
posx=NMEA_Comma_Pos(p1,2);  
 
//得到定位类型 
if(posx!=0XFF)gpsx->fixmode=NMEA_Str2num(p1+posx,&dx); 
//得到定位卫星编号 
for(i=0;i<12;i++) 
 
 
 
 
 
 
{ 
 
 
 
posx=NMEA_Comma_Pos(p1,3+i); 
 
 
 
 
   
if(posx!=0XFF)gpsx->possl[i]=NMEA_Str2num(p1+posx,&dx); 
else break;   
 
 
} 
posx=NMEA_Comma_Pos(p1,15);                  //得到 PDOP 位置精度因子 
     
 
if(posx!=0XFF)gpsx->pdop=NMEA_Str2num(p1+posx,&dx);     
posx=NMEA_Comma_Pos(p1,16); 
 
  //得到 HDOP 位置精度因子 
if(posx!=0XFF)gpsx->hdop=NMEA_Str2num(p1+posx,&dx);     
posx=NMEA_Comma_Pos(p1,17); 
 
  //得到 VDOP 位置精度因子 
if(posx!=0XFF)gpsx->vdop=NMEA_Str2num(p1+posx,&dx);     
} 
//分析 GPRMC 信息 
//gpsx:nmea 信息结构体 
//buf:接收到的 GPS 数据缓冲区首地址 
www.alientek.comALIENTEK STM32开发板AN1409www.openedv.com开源电子网2014-03-30
void NMEA_GPRMC_Analysis(nmea_msg *gpsx,u8 *buf) 
u8 *p1,dx; 
 
 
   
u8 posx;           
u32 temp; 
       
float rs;     
p1=(u8*)strstr((const char *)buf,"$GPRMC"); 
posx=NMEA_Comma_Pos(p1,1);  
 
 
 
 
//得到 UTC 时间 
if(posx!=0XFF) 
{ 
 
 
 
 
temp=NMEA_Str2num(p1+posx,&dx)/NMEA_Pow(10,dx);//得到 UTC 时间 
gpsx->utc.hour=temp/10000; 
gpsx->utc.min=(temp/100)%100; 
gpsx->utc.sec=temp%100; 
   
   
} 
 
 
     
 
 
     
posx=NMEA_Comma_Pos(p1,3);  
 
 
 
 
//得到纬度 
if(posx!=0XFF) 
{ 
 
 
 
} 
temp=NMEA_Str2num(p1+posx,&dx);  
   
gpsx->latitude=temp/NMEA_Pow(10,dx+2); 
rs=temp%NMEA_Pow(10,dx+2);   
 
 
   
//得到° 
//得到' 
     
 
gpsx->latitude=gpsx->latitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;   
    gpsx->longitude=gpsx->longitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;   
} 
posx=NMEA_Comma_Pos(p1,6);  
 
 
if(posx!=0XFF)gpsx->ewhemi=*(p1+posx);  
posx=NMEA_Comma_Pos(p1,9);  
 
 
 
   
 
//东经还是西经 
//得到 UTC 日期 
if(posx!=0XFF) 
{ 
 
 
 
 
}   
temp=NMEA_Str2num(p1+posx,&dx);  
        //得到 UTC 日期 
gpsx->utc.date=temp/10000; 
gpsx->utc.month=(temp/100)%100; 
gpsx->utc.year=2000+temp%100;    
   
{ 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
} 
posx=NMEA_Comma_Pos(p1,4);  
 
 
if(posx!=0XFF)gpsx->nshemi=*(p1+posx);   
   
posx=NMEA_Comma_Pos(p1,5);  
if(posx!=0XFF) 
{ 
 
 
 
 
 
 
 
 
 
 
temp=NMEA_Str2num(p1+posx,&dx);  
   
gpsx->longitude=temp/NMEA_Pow(10,dx+2); 
rs=temp%NMEA_Pow(10,dx+2);   
 
 
 
 
 
 
 
 
 
 
//南纬还是北纬   
 
 
   
//得到经度 
 
     
   
//得到° 
//得到' 
 
www.alientek.comALIENTEK STM32开发板AN1409www.openedv.com开源电子网2014-03-30
//分析 GPVTG 信息 
//gpsx:nmea 信息结构体 
//buf:接收到的 GPS 数据缓冲区首地址 
void NMEA_GPVTG_Analysis(nmea_msg *gpsx,u8 *buf) 
{ 
 
 
 
 
 
 
 
 
 
u8 *p1,dx; 
 
 
   
u8 posx;         
p1=(u8*)strstr((const char *)buf,"$GPVTG"); 
posx=NMEA_Comma_Pos(p1,7);  
 
 
 
 
if(posx!=0XFF) 
 
 
 
 
//得到地面速率 
 
   
{ 
 
 
} 
gpsx->speed=NMEA_Str2num(p1+posx,&dx); 
if(dx<3)gpsx->speed*=NMEA_Pow(10,3-dx);      //确保扩大 1000 倍 
}     
//提取 NMEA-0183 信息 
//gpsx:nmea 信息结构体 
//buf:接收到的 GPS 数据缓冲区首地址 
void GPS_Analysis(nmea_msg *gpsx,u8 *buf) 
{ 
 
 
 
 
 
NMEA_GPGSV_Analysis(gpsx,buf); 
NMEA_GPGGA_Analysis(gpsx,buf);  //GPGGA 解析   
//GPGSV 解析 
 
//GPGSA 解析 
NMEA_GPGSA_Analysis(gpsx,buf); 
NMEA_GPRMC_Analysis(gpsx,buf);  //GPRMC 解析 
NMEA_GPVTG_Analysis(gpsx,buf);  //GPVTG 解析 
} 
//GPS 校验和计算 
//buf:数据缓存区首地址 
//len:数据长度 
//cka,ckb:两个校验结果. 
void Ublox_CheckSum(u8 *buf,u16 len,u8* cka,u8*ckb) 
{ 
 
 
 
 
 
 
 
u16 i; 
*cka=0;*ckb=0; 
for(i=0;i