资源描述
基于51单片机的简易GPS时钟制作(2009-04)
明睿洁 李甜 潘琴 局播送中心播出部
GPS(Global Positioning System),又被称为全球卫星定位系统,是时下非常流行的一种校时定位技术。而基于这一技术的校时定位系统更是被广泛的应用在我们生产生活的诸多领域。
在正文开始前,我们有必要对GPS系统的系统组成和基本原理做一个简要的说明。一个完整的GPS系统通常包括三个组成部分,即太空部分,监控部分和用户部分。太空部分包括24颗工作卫星,它们以55°倾角分布在地球上空20~200Km的六个轨道面上,其运行周期为12个恒星时。这样的卫星分布使的地球上任一位置都可以接收到至少六颗卫星的定位定时广播信号。
监控部分由分布在全球的若干个跟踪站所组成的监控网络所构成,以站点的作用分工为标准的话,这些基站又可以被分为主控站,监控站和注入站。主控站在同一时间段内只有一个,其主要作用是根据卫星的GPS观测数据,计算卫星的星历和卫星钟的修正参数,并将生成的修正指令发送给注入站,随后注入站会实时将修正指令发送给卫星。除上述功能外主控站还对卫星进行控制。监控站的作用是接收卫星信号,监测卫星的工作状态。注入站主要实现地面站与卫星的数据通信。
用户部分由GPS天线及相应的周边软硬件模块共同组成。其作用是接收GPS发送的定时定位信号,从中提取我们所需的信息并将这些信息以所需的方式反馈给用户。
GPS的定位定时原理若从工程角度去分析是相当复杂的,这里只做一个简要的说明:GPS卫星会以固定的频率向所覆盖区域广播其位置和时间数据信息,GPS接收设备会测量所接收到的卫星信号的时间延迟并最终计算出接收设备到不同卫星的距离。只要同时接收到4颗卫星的数据就可以计算出三维坐标,速度和时间。而就其定位方式来分类的话,还可将定位方式细分为单点定位和差分定位方式。详细的计算公式较复杂,这里不做说明。
通过上文的介绍我想大家对GPS系统已经有了一个初步的认识,后文中我会一步一步详细的说明一个基于51单片机的GPS时钟的制作,希望能通过这一系统的的开发流程使大家对GPS系统有一个更加全面更加深刻的认识。
在实际使用GPS的过程中我们无须关心GPS的数据计算问题,这一功能是由DPS天线及其周边模块完成的,目前已有许多成熟的OEM产品。因此设计GPS时钟系统的核心问题在于GPS天线模块与单片机系统的数据通信和数据转换显示方面。要处理GPS天线模块数据,首先我们必须对所使用的天线模块输出数据进行特征描述和分析。
考虑到产品定位问题,在示例中我们采用GARMINGPS25LP作为GPS天线模块,GARMINGPS25LP是典型的单点定位GPS器件,其定位精度为15米左右,价位在千元左右,很适合作为家用GPS器件的天线模块。
GARMINGPS25LP具备两种输出数据格式,其详细定义如下:
1. GPS标准数据(GGA)
$GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>,*hh<CR><LF>
<1>:UTC 当地时间,时分秒格式。
<2>:纬度,ddmm.mmmm格式。
<3>:纬度半球,N或者S。
<4>:经度,ddmm.mmmm格式。
<5>:经度半球,E或者W。
<6>:GPS质量参数。
<7>:可用卫星数,00~12。
<8>:水平精度,0.5~99.9。
<9>:天线上下平均海拔,-999.9~99999.9米。
<10>:海拔高度,-999.9~9999.9米。
<11>:差分GPS数据传输间隔时间,单位秒,在非差分模式下为空。
<12>:差分基准站ID,非差分为空。
2. 推荐最小GPS/TRANSIT数据(RMC)
$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh<CR><LF>
<1>:UTC 当地时间,时分秒格式。
<2>:工作状态,A=可用;V=接收器警报,不可用。
<3>:纬度,ddmm.mmmm格式。
<4>:纬度半球,N或者S。
<5>:经度,ddmm.mmmm格式。
<6>:经度半球,E或者W。
<7>:对地速度,000.0~999.9节。
<8>:对地航向,000.0°~359.9°
<9>:UTC当地时间,ddmmyy格式。
<10>:磁变。
<11>:磁变方向。
<12>:工作模式,A=自主,D=差分,E=评估,N=数据无效。
3.3D速度信息(PGRMV)
$PGRMV,<1>,<2>,<3>*hh<CR><LF>
<1>:真实东向速度。
<2>:真实北向速度。
<3>:垂直速度。
4. 出语句激活/屏蔽语句(PGRMO)
$PGRMO<1>,<2>*hh<CR><LF>
<1>:目标语句名。
<2>:目标语句状态参数。
5. GPS板设置语句(PGRMC)
$PGRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>,<13>,<14>*hh<CR><LF>
<1>:工作模式。
<2>:海拔高度。
<3>~<8>:地球数据索引。
<9>:差分模式参数。
<10>:NMEA波特率设置。
<11>:速度过滤参数。
<12>:秒脉冲模式。
<13>:秒脉冲脉宽设置。
<14>:默认值。
在实际使用中,我们主要通过串口来实现单片机与GARMINGPS25LP间的通信。因这里只实现GPS时钟功能,故我们只需通过串口向GPS输出$PGRMO,,2、$PGRMO,GPRMC,1
这样GPS就会连续输出$GPRMC数据。(利用超级终端可以简单的完成这一功能,当然也可以利用单片机进行初始化设置)
在对GARMINGPS25LP做了必要的初始化设定后,我们可以将设计重点转移到单片机部分来。这里我们要面对的问题有三个:
1. 单片机与DPS的硬件接口。
2. 单片机与DPS的软件接口。
3. DPS数据的显示。
对于第一个问题,因GARMINGPS25LP输出的是标准串行数据,故我们可以将其串行数据口与单片机直接对接(注意是交叉对接)。软件接口我会在下问做详细的解释,这里不做赘述。至于数据显示方面,这里我们使用图形液晶显示模块完成,图形液晶显示模块包括两个基本部分,即液晶面板和专用液晶面板控制IC。以图形液晶模块完成显示的优点在于它可以以更灵活友好的方式显示数据内容,这一点是八段数码管和段状液晶面板做不到的。
首先让我们来看一下GARMINGPS25LP的通信接口定义,概图如下:
图1:GARMINGPS25LP管脚图
1. Pin1:TXD2,第二异步串行输出口,输出相位信号。
2. Pin2:RXD2,第二异步串行输入口,输入串行差分GPS信号。
3. Pin3:PPS,秒脉冲输出,上升沿和GPS开始同步。
4. Pin4:TXD1,第一异步串行输出口。
5. Pin5:RXD1,第一异步串行输入口。
6. Pin6:PWR_ON,外部断电输入,输入大于2.7V时激活。
7. Pin7:VAUX,内部辅助电池充电输入。
8. Pin8:GROUND,电源信号地。
9. Pin9:VIN,与Pin10对接。
10. Pin10:VIN,电源输入。
11. NC:无定义。
12. NMEA:电学规范兼容串行输出。
依据管脚定义我们可以很容易的设计出GARMINGPS25LP与51单片机的硬件接口,
排除掉对本系统无作用的管脚,实际上我们只需要用到GARMINGPS25LP的Pin4和Pin5,具体连接概图如下:
图2:单片机与GARMINGPS25LP硬件接口
再让我们回过头来看看显示的硬件部分设计,在开始设计前有必要对图形液晶模块的工作方式做一个简要的说明。一个字符要在液晶模块上显示出来必须经过三个基本的控制操作:
1. 向液晶控制芯片写指令代码。
2. 象液晶控制芯片写显示数据。
3. 读显示数据。
而且在这一过程中,液晶控制器必须处于空闲状态,否则只可以对其进行读状态操作。
这里我们使用内置液晶控制器的图形液晶模块MGLS12032作为显示模块。这里我们不讨论其周边供电电路的设计,原则上LCD模块的供电电路是需要专门设计的,因其需要+5V的工作电压输入和-10V的对比调节电压输入,而-10V的调节电压必须通过一定的电气转换来获得。MGLS12032外部管脚如下图:
图3:MGLS12032管脚定义
管脚定义如下:
1. GND,电源信号地。
2. VCC,电源输入。
3. A8,LCD数据/指令通道选择。
4. A9,LCD读写信号选择。
5. D0~D7,LCD数据总线。
同样,知道LCD的外部管脚定义后我们也可以很容易的完成其与单片机的对接。
图4:单片机与MGLS12032简要接口方式
完成了GPS模块与LCD模块与单片机系统的硬件接口后,接下来我们要做的就是设计软件接口部分。
因GPS与单片机模块的通信为一标准的串口通信模式,故将其放在后面讨论,我们先来处理液晶驱动程序。
在使用液晶模块前必须对其进行初始化操作,相应代码在液晶模块的说明书中会有,MGLS12032的初始化函数有INITIAL(),CLEAR()两个,它们被整合在inclr.a51中,其详细代码如下:
EXTRN CODE(CWE1,DWE1,CWE2,DWE2)
EXTRN DATA(COM,DAT)
PUBLIC INITIAL,CLEAR ;定义INTIAL和CLEAR为全局函数。
INITP SEGMENTCODE
INIROM SEGMENT CODE
RSEG INIROM
TABLE: DB 0E2H,0A4H,0A9H,0A0H,0C0H,0AFH
RSEG INITP
INITIAL: PUSH DPH ;初始化E1,E2。
PUSH DPL
MOV R4,#0
INIT01: MOV DPTR,#TABLE
MOV A,R4
MOVC A,@A+DPTR
MOV COM,A
LCALL CWE1
LCALL CWE2
INC R4
CJNE R4,#6H,INIT01
POP DPL
POP DPH
RET
CLEAR: MOV R4,#00H ;清屏。
CLEAR1: MOV A,R4
ORL A,#0B8H
MOV COM,A
LCALL CWE1
LCALL CWE2
MOV COM,#00H
LCALL CWE1
LCALL CWE2
MOV R3,#3CH
CLEAR2: MOV DAT,#00H
LCALL DWE1
LCALL DWE2
DJNZ R3,CLEAR2
INC R4
CJNE R4,#04H,CLEAR1
RET
END
程序的全局定义Lcd.c的内容为:
#define uchar unsigned char
uchar LINE,CLMN; /*PG页变量,CLMN列变量。
bit AREA; /*AREA=0时,由E1控制。AREA=1时由E2控制。
extern uchar COM,DAT,NDIG;
extern char xdata BUFFER[];
uchar code ALPHA[0x61][0x08]=
{
这里为西文字符库定义。液晶模块说明文件中一般会有。
};
void CWE1(void);
void CWE2(void);
void DWE1(void);
void DWE2(void);
这里CWE1的内容为:
CWE1: PUSH DPL
PUSH DPH
MOV DPTR,#CRADD1
CWE11: MOVX A,@DPTR
JB ACC.7,CWE11
MOV DPTR,#CWADD1
MOV A,COM
MOVX @DPTR,A
POP DPH
POP DPL
RET
这里涉及到汇编与C的混合编程问题,CWE1的作用是传递参数CWADD1的内容到E1,CWE2的作用相同,只是目标为E2。
DWE1的内容为:
DWE1: PUSH DPL
PUSH DPH
MOV DPTR,#CRADD1
DWE11: MOVX A,@DPTR
JB ACC.7,DWE11
MOV DPTR,#DWADD1
MOV A,DAT
MOVX @DPTR,A
POP DPH
POP DPL
RET
其作用是传递CRADD1参数给E1,DWE2作用相同,同样改动只是目标为E2。
因为是图形液晶模块,故在显示数据的时候需要对字符的显示位置进行设置。这里还有个要考虑的因素是MGLS12032内置两个控制器,这样的设计带来的问题就是存在页面区域转换问题,以下代码段就是字符的位置设置函数。
void locate(uchar page,column)
{
page=page+column/120;
LINE=page%4;
COM=LINE|0xb8;
CWE1();
CWE2();
Column=column%120;
CLMN=column;
If(column>=60)
{
AREA=1;
Column=column-60;
}
else AREA=0;
COM=column;
If(AREA)CWE2();
Else CWE1();
}
在完成位置设置后,接下来要做的就是显示字符/字符串了,根据自底向上的设计思想,我们可以先写出显示单个字符的代码:
onechar(char c)
{
uchar i;
for(I=0;I<6;I++)
{
DAT=ALPHA[c-0x20][i];
If(AREA)DWE2();
Else DWE1();
Locate(LINE,CLMN+1);
}
}
有了单个字符的显示代码,字符串的显示代码可以通过简单的循环来实现:
void show(void)
{
uchar i;
for(I=0,I<NDIG;I++)
{
onechar(BUFFER[i];
}
}
有了这些代码我们就可以很简单的完成字符的液晶显示了,接下来是设计的最后部分,即GARMINGPS25LP与单片机的软件接口。
这一部分的流程如下图:
图5:主程序流程
在处理主程序前,有必要先将串口中断服务程序先给出,具体代码如下:
serial() interrupt 4 using 1
{
RI=0;
If(SBUF==0x24) /*判断是否有GPS数据到来。 0x24为西文字符$的ASCII码。
{
record=1;
i=0;
igps=0;
Numbercoma=0;
}
if(record==1)
{
if(SBUF==0x2c){numbercoma++;}
if(numbercoma==1) /*因UTC紧跟在第一个逗号后,故记录到第一个逗号后就开始记录时间数据。
{
stringgps[igps]=SBUF;
igps++;
}
if(SBUF==’*’) /*判断GPRMC数据是否结束。
{
stringgps[igps]=’\0’; /*为字符串添加结束标志。
j=0;
len2=spintf(stringgps,”%s”,stringgps+1);
NDIG=sprintf(BUFFER,”k1:%d”,k1);
Show();
If(len2!=0)
{
uint numgps=atoi(stringgps);
}
numgps=0;
numbercoma=0;
record=0;
igps=0;
}
}
}
这里我们可以看出,单片机仅会记录第一个逗号后的数据(数据被存在数据缓冲区BUFFER中,即UTC本地时间数据,因此在主程序中我们需要考虑的问题就是触发串口中断,并将收到的数据送液晶显示。
主程序的代码如下:
#include <stdio.h>
#include <absacc.h>
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
void INITIAL(void); /*显示初始化。
void CLEAR(void); /*清屏。
void locate(uchar page,uchar column); /*定义初始显示内容。
void show(void);
char data BUFFER[15];
uchar data NDIG;
int xdata sumup=0;
uchar idata stringgps[8];
uint k,i,j,k1;
uint record,number;
uint igps;
uint numbercoma;
uint len1,len2,len3;
uint numgps;
void main(void)
{
TMOD=0x20; /*串口初始化。
TL1=0xfd;
TH1=0xfd;
SCON=0x50;
PCON=0x00;
IE=0x90;
TR1=1;
P1_0=0;
INITIAL(); /*显示初始化。
CLEAR();
Locate(3,4);
NDIG=sprintf(BUFFER,”sumup=rdy”);
Show();
Sumup=12345;
Locate(2,4);
NDIG=sprintf(BUFFER,”sumup=%d”,sumup);
Show();
}
for(;;)
{
locate(0,2);
NDIG=sprintf(BUFFER,”k1=%d”,k1);
Show();
}
}
从主程序代码不太容易看出代码的执行结果,但通过前面的流程图我们就很好理解这一代码了。主程序在开始执行后会对液晶模块和串口中断进行初始化。随后单片机会进入死循环直到GARMINGPS25LP的数据触发串口中断。串口中断服务程序会对接收到的数据进行必要的转换处理并最终送液晶模块显示。实际使用中GARMINGPS25LP是以固定的频率不断发送数据的,故实际的液晶显示内容为不断刷新的时间内容。这样就达到了我们的设计目的。如果数据刷新过快导致时间显示不稳定,可以考虑加入延时丢弃部分数据包。
这篇文章主要讨论的是GPS时钟的设计,故对于LCD和单片机的周边电路并没有进行详细的讨论。本设计的程序规模并不大,完全可以采用内置ROM的单片机实现,故系统在管脚资源上是相当宽裕的,必要的话还可以通过外部扩展引入键盘。
本设计给出的代码灵活度相当大,它不仅可以用做时间显示,GPS送入的其他数据也是可以显示的,而要实现相应功能也很容易,只需要改动串口中断服务程序的部分代码就能达到目的。
不得不承认的是对于高精度定时来说,这一系统是存在缺陷的。代码的执行过程本身是需要消耗时间的,我们的设计并没有考虑到误差修正的问题,当然我们可以通过补入误差修正参数来减小显示误差。
希望通过这篇文章能让大家对GPS系统有一个更全面深刻的认识。
展开阅读全文