一、测试篇
刚拿到ATK-NEO-6M这个型号的GPS模块,有点不大相信,近100块的东西居然只有3cm那么大一点。之前在网上下载了相关的资料,第一次快速测试肯定是借助电脑,正好msp430开发板上有max232模块,直接将GPS模块的TX接max232的TX,RX同样。PC端安装u-center,u-center 是由ublox 公司提供的GPS 评估软件,功能十分强大,可以对我们的ATK-NEO-6M GPS 模块进行全面的测试。安装好后,点击连接/断开按钮,选择你的串口号,一般测试都是选择自动配置按钮,也就是那个魔法样式的按钮,单击后会自己配置波特率,如果正常通讯的话,在最右下角的状态栏会显示黄色,当GPS模块已经定位成功的话,会在界面上显示当前的基本信息,如经度,纬度等。想查看接收到的原本信息,按F8键即可显示。我测试后工作正常,在屋里基本能搜到9颗卫星信号。
二、开发篇
刚拿到GPS模块,感觉要是开发起来会很麻烦,后来经过实验,其实很简单,因为卖家提供的资料已经足够开发。句段的分析函数都已提供,我们只需将接口写好即可。接下来先看看我的硬件环境。
硬件环境:MPS430开发板,FYD12864LCD显示屏,USB转串口线,ATK-NEO-6M GPS模块
软件环境:IAR集成开发环境,串口调试工具,Secure CRT
实现目标:MSP430通过串口2接收到GPS信息,显示在LCD上,同时通过串口1发送接收到的数据到PC。
1. 先把msp430的句段分析部分调通。
思路:将厂商提过的GPS语句分析部分代码移植过来,串口手动发送GPS数据,分析完后在LCD上显示。
将厂商提供的GPS语句分析代码贴出,(在此仅作为参考学习只用)
#ifndef __GPS_H
#define __GPS_H
#include <math.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include "../inc/uart.h"
//GPS NMEA-0183协议重要参数结构体定义
//卫星信息
typedef struct
{
uchar num; //卫星编号
uchar eledeg; //卫星仰角
uint azideg; //卫星方位角
uchar sn; //信噪比
}nmea_slmsg;
//UTC时间信息
typedef struct
{
uint year; //年份
uchar month; //月份
uchar date; //日期
uchar hour; //小时
uchar min; //分钟
uchar sec; //秒钟
}nmea_utc_time;
//NMEA 0183 协议解析后数据存放结构体
typedef struct
{
uchar svnum; //可见卫星数
nmea_slmsg slmsg[12]; //最多12颗卫星
nmea_utc_time utc; //UTC时间
int latitude; //纬度 分扩大100000倍,实际要除以100000
uchar nshemi; //北纬/南纬,N:北纬;S:南纬
int longitude; //经度 分扩大100000倍,实际要除以100000
uchar ewhemi; //东经/西经,E:东经;W:西经
uchar gpssta; //GPS状态:0,未定位;1,非差分定位;2,差分定位;6,正在估算.
uchar posslnum; //用于定位的卫星数,0~12.
uchar possl[12]; //用于定位的卫星编号
uchar fixmode; //定位类型:1,没有定位;2,2D定位;3,3D定位
uint pdop; //位置精度因子 0~500,对应实际值0~50.0
uint hdop; //水平精度因子 0~500,对应实际值0~50.0
uint vdop; //垂直精度因子 0~500,对应实际值0~50.0
int altitude; //海拔高度,放大了10倍,实际除以10.单位:0.1m
uint speed; //地面速率,放大了1000倍,实际除以10.单位:0.001公里/小时
}nmea_msg;
////////////////////////////////////////////////////////////////////////////////////////////////////
//UBLOX NEO-6M 时钟脉冲配置结构体
typedef struct
{
uint header; //cfg header,固定为0X62B5(小端模式)
uint id; //CFG TP ID:0X0706 (小端模式)
uint dlength; //数据长度
int interval; //时钟脉冲间隔,单位为us
int length; //脉冲宽度,单位为us
signed char status; //时钟脉冲配置:1,高电平有效;0,关闭;-1,低电平有效.
uchar timeref; //参考时间:0,UTC时间;1,GPS时间;2,当地时间.
uchar flags; //时间脉冲设置标志
uchar reserved; //保留
signed short antdelay; //天线延时
signed short rfdelay; //RF延时
signed int userdelay; //用户延时
uchar cka; //校验CK_A
uchar ckb; //校验CK_B
}_ublox_cfg_tp;
//UBLOX NEO-6M 刷新速率配置结构体
typedef struct
{
uint header; //cfg header,固定为0X62B5(小端模式)
uint id; //CFG RATE ID:0X0806 (小端模式)
uint dlength; //数据长度
uint measrate; //测量时间间隔,单位为ms,最少不能小于200ms(5Hz)
uint navrate; //导航速率(周期),固定为1
uint timeref; //参考时间:0=UTC Time;1=GPS Time;
uchar cka; //校验CK_A
uchar ckb; //校验CK_B
}_ublox_cfg_rate;
int NMEA_Str2num(uchar *buf,uchar*dx);
void GPS_Analysis(nmea_msg *gpsx,uchar *buf);
void NMEA_GPGSV_Analysis(nmea_msg *gpsx,uchar *buf);
void NMEA_GPGGA_Analysis(nmea_msg *gpsx,uchar *buf);
void NMEA_GPGSA_Analysis(nmea_msg *gpsx,uchar *buf);
void NMEA_GPGSA_Analysis(nmea_msg *gpsx,uchar *buf);
void NMEA_GPRMC_Analysis(nmea_msg *gpsx,uchar *buf);
void NMEA_GPVTG_Analysis(nmea_msg *gpsx,uchar *buf);
void Ublox_Cfg_Tp(int interval,int length,signed char status);
void Ublox_Cfg_Rate(uint measrate,uchar reftime);
#endif /* __GPS_H */
gps.c
以上为GPS相关代码,下面为MSP430相关代码
uart..h
uart.c
该串口操作为配置串口1和串口2波特率都为9600(之前看了GPS模块的应用手册,发现默认的波特率为38400
,但我将430的波特率设为38400发现接收到的数据为乱码,于是干脆直接将GPS的波特率设为9600,我直接将模块上的R5电阻取了,终于接收到正常的数据.)
#include <msp430f149.h>
#include "inc/lcd_fyd12864.h"
#include "inc/uart.h"
#include "inc/gps.h"
uchar Welcom1[] = "欢迎来到嵌入式";
uchar Welcom2[] = "*************";
uchar Welcom3[] = "版主:******";
uchar Welcom4[] = "DIY 实验室";
uchar latitude[] = "Lat: ";
uchar longitude[] = "Long: ";
uchar elevation[] = "Ele: ";
uchar currTime[] = "Time: ";
uchar RxBuf[300], TxBuf[300];
uchar RxLen = 0;
uchar RxTempLen = 0;
uchar rev_flag = 0;
char dtbuf[50];
nmea_msg gpsx;
void main()
{
volatile uint i;
WDTCTL = WDTPW + WDTHOLD; //关闭看门狗
BCSCTL1 &= ~XT2OFF; // XT2= HF XTAL
do
{
IFG1 &= ~OFIFG; // Clear OSCFault flag
for (i = 0xFF; i > 0; i--); // Time for flag to set
}
while ((IFG1 & OFIFG)); // OSCFault flag still set?
//MCLK=8M,SCLK=1M
BCSCTL2 |= SELM_2 + SELS + DIVS_3; // MCLK= XT2 (safe)
UartInit();
Delay_ms(500);
LCDReset();
DisplayString(1, 0, Welcom1);
DisplayString(1, 1, Welcom2);
DisplayString(1, 2, Welcom3);
DisplayString(1, 3, Welcom4);
Delay_ms(2000);
LCDClear();
Delay_ms(1000);
/*
DisplayString(0, 0, latitude);
DisplayString(0, 1, longitude);
DisplayString(0, 2, elevation);
DisplayString(0, 3, currTime);
*/
SendString("**************\n");
_EINT();
while(1){
//SendString("UART0 test!\n");
if(rev_flag == 1){
int i,len = 0;
len = RxLen;
for(i = 0; i < len; i++)
TxBuf = RxBuf;
RxLen = 0;
TxBuf = 0;
GPS_Analysis(&gpsx,TxBuf);
Show_GPS_Info(gpsx);
SendString(TxBuf);
}
//Delay_ms(1000);
}
}
void Show_GPS_Info(nmea_msg gpsx)
{
float tp;
//得到经度字符串
tp=gpsx.longitude;
sprintf((char *)dtbuf,"Long:%.5f %1c",tp/=100000,gpsx.ewhemi);
DisplayString(0, 0, dtbuf);
//得到纬度字符串
tp=gpsx.latitude;
sprintf((char *)dtbuf,"Lat:%.5f %1c",tp/=100000,gpsx.nshemi);
DisplayString(0, 1, dtbuf);
//得到高度字符串
tp=gpsx.altitude;
sprintf((char *)dtbuf,"Alt:%.1fm",tp/=10);
DisplayString(0, 2, dtbuf);
//显示UTC时间
sprintf((char *)dtbuf,"UTC:%02d:%02d:%02d",gpsx.utc.hour,gpsx.utc.min,gpsx.utc.sec);
DisplayString(0, 3, dtbuf);
}
#pragma vector = UART0RX_VECTOR
__interrupt void UART0RxISR(void)
{
//接收来自串口的数据
while(!(IFG1 & UTXIFG0));
RxBuf[RxTempLen++] = RXBUF0;
while(RxBuf[RxTempLen-1] == '\n'){
RxLen = RxTempLen;
RxTempLen = 0;
rev_flag = 1;
}
}
#pragma vector = UART1RX_VECTOR
__interrupt void UART1RxISR(void)
{
//接收来自串口的数据
while(!(IFG2 & UTXIFG1));
P2OUT = RXBUF1;
}
其中,有关LCD显示部分在其他博文中会详细说到.
将程序烧到430后,上电,打开串口调试工具,发送文本如下:
$GPGGA,023543.00,2308.28715,N,11322.09875,E,1,06,1.49,41.6,M,-5.3,M,,*7D
就会收到发回来的数据跟发送的一样,同时LCD上显示海拔为41.6m.
说明能对数据接受并进行正确处理了。
后面要做的便是将串口0的接收中断代码复制到串口1接收中断代码即可。
同样上电,这时LCD可显示信息,但显示的数据都为0,说明GPS还没有定位成功。等2~3分钟左右,GPS模块上的指示灯开始闪烁时,这时从LCD上可看到当前的经度、纬度、高度、UTC时间,同时用ScuetCRT连接,可看到430发回来接受到的原始数据。
下图为实验的结果,时间显示稍微有点问题,因为没有转换还是其他。不过忙了几天终于将GPS弄好了,下一步将编写linux驱动。。。下面晒一下成果^-^.
补充:
之前忙于工作,现在回头看看,发现对收到的数据进行分析显示后结果不对,很有可能是数据溢出导致,通过串口1发送到PC的数据可以看到,收到GPS模块的数据是正确的。唯一可能的是在数据进行分析时出错。果然,追踪到gps.c中可以看到,对数据处理的数据超大,而msp430的int只为16位,最大值为65536,所以在对数据进行装载时肯定会溢出。这也是今天上班偶尔看看才发现的。回去后将所用的int类型替换为long型,应该就正确了,期待回去后验证。。。。
昨天回去修改一下代码,将以前的uint类型替换为ulong,烧写后结果显示正确。同时在以前的基础上修改显示部分,按按键后显示另外的信息。
修改后的main.cpp如下:
#include <msp430f149.h>
#include "inc/lcd_fyd12864.h"
#include "inc/uart.h"
#include "inc/gps.h"
uchar Welcom1[] = "欢迎来到嵌入式";
uchar Welcom2[] = "************";
uchar Welcom3[] = "版主:*******";
uchar Welcom4[] = "DIY 实验室";
uchar latitude[] = "Lat: ";
uchar longitude[] = "Long: ";
uchar elevation[] = "Ele: ";
uchar currTime[] = "Time: ";
const uchar *mode[4] = {"Fail", "Fail", "2D", "3D"};
uchar RxBuf[300], TxBuf[300],RxData[9];
uchar RxLen = 0;
uchar RxTempLen = 0;
uchar rev_flag = 0;
char dtbuf[50];
nmea_msg gpsx;
uchar swt = 1;
void Show_GPS_Info1(nmea_msg gpsx)
{
float tp;
//得到经度字符串
tp=gpsx.longitude;
sprintf((char *)dtbuf,"Lon: %.5f %1c",tp/=100000,gpsx.ewhemi);
DisplayString(0, 0, dtbuf);
//得到纬度字符串
tp=gpsx.latitude;
sprintf((char *)dtbuf,"Lat: %.5f %1c",tp/=100000,gpsx.nshemi);
DisplayString(0, 1, dtbuf);
//得到高度字符串
tp=gpsx.altitude;
sprintf((char *)dtbuf,"Alt: %.1fm",tp/=10);
DisplayString(0, 2, dtbuf);
//得到速度字符串
tp=gpsx.speed;
sprintf((char *)dtbuf,"Speed:%.3fkm/h",tp/=1000);
DisplayString(0, 3, dtbuf);
}
void Show_GPS_Info2(nmea_msg gpsx)
{
//定位状态
if(gpsx.fixmode<=3){
sprintf((char *)dtbuf,"Fix Mode: %s",mode[gpsx.fixmode]);
DisplayString(0, 0, dtbuf);
}
//用于定位的卫星数
sprintf((char *)dtbuf,"Val sat: %02d",gpsx.posslnum);
DisplayString(0, 1, dtbuf);
//可见卫星数
sprintf((char *)dtbuf,"Vis sat: %02d",gpsx.svnum%100);
DisplayString(0, 2, dtbuf);
}
void Show_GPS_Info3(nmea_msg gpsx)
{
//显示UTC日期
sprintf((char *)dtbuf,"Date:%04d/%02d/%02d",gpsx.utc.year,gpsx.utc.month,gpsx.utc.date);
DisplayString(0, 0, dtbuf);
//显示UTC时间
sprintf((char *)dtbuf,"Time:%02d:%02d:%02d",gpsx.utc.hour,gpsx.utc.min,gpsx.utc.sec);
DisplayString(0, 1, dtbuf);
}
void main()
{
volatile uint i;
WDTCTL = WDTPW + WDTHOLD; //关闭看门狗
BCSCTL1 &= ~XT2OFF; // XT2= HF XTAL
do
{
IFG1 &= ~OFIFG; // Clear OSCFault flag
for (i = 0xFF; i > 0; i--); // Time for flag to set
}
while ((IFG1 & OFIFG)); // OSCFault flag still set?
//MCLK=8M,SCLK=1M
BCSCTL2 |= SELM_2 + SELS + DIVS_3; // MCLK= XT2 (safe)
Delay_ms(500);
UartInit();
Delay_ms(500);
P3DIR &= (~BIT2 + ~BIT3);
LCDReset();
DisplayString(1, 0, Welcom1);
DisplayString(1, 1, Welcom2);
DisplayString(1, 2, Welcom3);
DisplayString(1, 3, Welcom4);
Delay_ms(2000);
LCDClear();
Delay_ms(1000);
/*
DisplayString(0, 0, latitude);
DisplayString(0, 1, longitude);
DisplayString(0, 2, elevation);
DisplayString(0, 3, currTime);
*/
SendString("Lixiaoming\n");
_EINT();
while(1){
//SendString("UART0 test!\n");
if(!(P3IN & BIT2)){
swt++;
if(swt == 4)
swt = 1;
}
if(rev_flag == 1){
int i,len = 0;
len = RxLen;
for(i = 0; i < len; i++)
TxBuf = RxBuf;
RxLen = 0;
TxBuf = 0;
GPS_Analysis(&gpsx,TxBuf);
switch(swt){
case 1: LCDClear(); Show_GPS_Info1(gpsx); break;
case 2: LCDClear(); Show_GPS_Info2(gpsx); break;
default: LCDClear(); Show_GPS_Info3(gpsx); break;
}
SendString(TxBuf);
rev_flag = 0;
}
//Delay_ms(1000);
}
}
#pragma vector = UART0RX_VECTOR
__interrupt void UART0RxISR(void)
{
//接收来自串口的数据
while(!(IFG1 & UTXIFG0));
RxBuf[RxTempLen++] = RXBUF0;
while(RxBuf[RxTempLen-1] == '\n' && RxBuf[RxTempLen-2] == '\r' ){
RxLen = RxTempLen;
RxTempLen = 0;
rev_flag = 1;
}
}
#pragma vector = UART1RX_VECTOR
__interrupt void UART1RxISR(void)
{
//接收来自串口的数据
while(!(IFG2 & UTXIFG1));
RxBuf[RxTempLen++] = RXBUF1;
while(RxBuf[RxTempLen-1] == '\n'){
RxLen = RxTempLen;
RxTempLen = 0;
rev_flag = 1;
}
}
显示的结果如下:(因为是在市内的窗口,信号不是很好)
PC串口接收到的数据如下:
$GPRMC,134856.00,A,2232.50576,N,11354.64802,E,0.084,,251113,,,A*7E
$GPGSV,3,2,09,22,75,191,27,25,58,074,22,29,08,122,16,31,48,276,19*76
gpsx->utc temp: 134857
gpsx->latitude temp: 223250578
gpsx->longitude temp: 1135464798
gpsx->utc.date temp: 251113
$GPRMC,134857.00,A,2232.50578,N,11354.64798,E,0.173,,251113,,,A*74
$GPGSV,3,2,09,22,75,191,27,25,58,074,23,29,08,122,15,31,48,276,20*7E
gpsx->utc temp: 134858
gpsx->latitude temp: 223250575
gpsx->longitude temp: 1135464785
gpsx->utc.date temp: 251113
$GPRMC,134858.00,A,2232.50575,N,11354.64785,E,0.140,,251113,,,A*7A
$GPGSV,3,2,09,22,75,191,27,25,58,074,23,29,08,122,15,31,48,276,19*74
gpsx->utc temp: 134859
gpsx->latitude temp: 223250575
gpsx->longitude temp: 1135464782
gpsx->utc.date temp: 251113
$GPRMC,134859.00,A,2232.50575,N,11354.64782,E,0.130,,251113,,,A*7B
$GPGSV,3,2,09,22,75,191,27,25,58,074,23,29,08,122,15,31,48,276,20*7E
gpsx->utc temp: 134900
gpsx->latitude temp: 223250570
gpsx->longitude temp: 1135464784
gpsx->utc.date temp: 251113
$GPRMC,134900.00,A,2232.50570,N,11354.64784,E,0.112,,251113,,,A*75