基于51单片机的定位系统设计
1、基于51单片机GPS定位系统设计
基于51单片机GPS定位系统设计
GPS(Global Positioning System,全球定位系统),是一个由24颗卫星组成的卫星系统。可以保证在任意时刻,任何地点都同时检测到4颗卫星,以保证卫星可以采集到该观测点的经纬度和高度,从而实现导航、定位、授时等功能。现在在很多领域都有用到GPS技术,比如可以用来引导汽车、飞机、船舶以及个人准确的到达目的地,本文就是以保证个人的安全、准确出行研究的GPS定位系统,设计制作一个单片机系统,单片机系统由电源电路、晶振电路、复位电路、LCD驱动电路等组成,此系统用以读取GPS模块的数据并处理,最后显示在LCD12864上面,用户可以实时通过读取12864显示的内容了解自己的位置以及当前时间,同时由于用到有单片机,以后还可以在上面加很多想要的功能,是一个可扩展、很具有使用价值的系统。
1、 系统方案选择和论证
1.1、引言
GPS 是英文Global Positioning System(全球定位系统)的简称,而其中文简称为“球位系”。GPS起始于1958年美国军方的一个项目,1964年投入使用。是20世纪70年代由美国陆海空三军联合研制的新一代空间卫星导航定位系统 。其主要目的是为陆、海、空三大领域提供实时、 全天候和全球性的导航服务,并用于情报收集、核爆监测和应急通讯等一些军事目的。经过20余年的研究实验,耗资300亿美元,到1994年,全球覆盖率高达98%的24颗GPS卫星星座己布设完成。随着人民生活水平的发展,GPS技术被越来越多的应用在个人PDA、个人车载终端、手机等设备上。人们通过GPS,能准确知道自己所在的位置,从实现到导航、确定旅游路线、获取地理信息等功能。本文介绍的GPS导航系统,以单片机作为主控芯片,配以GPS、12864显示屏,构建了一个GPS信息显示的系统。本系统在一定程度上推动了GPS导航设备研究的发展。
1.2 方案说明
1.2.1、 总体设计方案
个人手持设备要求是功耗要足够低、操作简单、界面美观、方便观看。
为完成相应功能,本设计提出的方案如图1.1所示。系统包括以下几个基本模块:电源模块、主控模块、显示模块、GPS定位模块。GPS模块负责接收卫星信息,单片机模块负责读取GPS模块数据并处理,显示模块主要负责将GPS模块接收到的数据显示出来供用户随时观看。
1.2.2、 系统功能说明
(1).定位功能
GPS通过接收卫星信号,可以准确地定出其所在的位置,位置误差小于10米。利用GPS,在12864上面显示当前位置。
(2).查询时间功能
GPS还可以接收卫星发下来的时间信息,利用单片机控制12864显示出当前时间,用户可以很方便的了解时间。
1.3、 部分模块设计方案
1.3.1、 控制模块设计方案
单片机最小系统由晶振电路、复位电路、电源电路等组成
1.3.2、 显示电路设计方案
本项目选用LCD12864作为显示屏,电路简单,选用并行通信方式
1.3.3、 GPS模块选择方案
方案一:采用串口接口GPS模块。优点:便于驱动,程序简单,价格便宜。缺点:体积较大。
方案二:采用usb接口GPS模块。优点:便于携带。缺点:驱动相对较难、价格相对较贵。
1.4、总体方案确定
1.4.1、系统硬件方案
表1.1系统硬件方案
2、 系统的硬件设计与实现
系统硬件特性如下图:
该系统是用手工做的腐蚀板,成本很低,性能稳定,同时用LCD12864作为显示屏显示位置及时间等信息,显示的容量大数据量小,一个页面可以显示8*4个汉字或16*4个字符。
2.1、 主控芯片电路
主控芯片为AT89S52,该芯片性能稳定、工艺精良。
图2.1为主控芯片电路,AT89S52是一种低功耗、高性能CMOS 8位微控制器,具有8K 在系统可编程Flash 存储器。使用Atmel 公司高密度非易失性存储器技术制造,与工业80C51 产品指令和引脚完全兼容。片上Flash允许程序存储器在系统可编程,亦适于常规编程器。在单芯片上,拥有灵巧的8 位CPU 和在系统可编程Flash,使得AT89S52在众多嵌入式控制应用系统中得到广泛应用,这里采用AT89S52单片机作为控制芯片,实现了成本低,开发简单等特点。
2.2、 复位电路
图2.2为系统复位电路,为确保系统中电路稳定可靠工作,复位电路是必不可少的一部分,复位电路的第一功能是上电复位。一般单片机电路正常工作需要供电电源为5V±5%,即4.75~5.25V。由于单片机电路是时序数字电路,它需要稳定的时钟信号,因此在电源上电时,只有当VCC超过4.75V低于5.25V以及晶体振荡器稳定工作时,复位信号才被撤除,单片机电路开始正常工作。这种复位电路的工作原理是:VCC上电时,C1充电,在10K电阻上出现电压,使得单片机复位;几个毫秒后,C1充满,10K电阻上电流降为0,电压也为0,使得单片机进入工作状态。工作期间,按下S1,C1放电。S1松手,C1又充电,在10K电阻上出现电压,使得单片机复位。几个毫秒后,单片机进入工作状态
2.3、 晶振电路
图2.3为震荡电路,每个单片机系统里都有晶振,全称叫晶体震荡器,在单片机系统里晶振的作用非常大,它结合单片机内部的电路,产生单片机所必须的时钟频率,单片机的一切指令的执行都是建立在这个基础上的,晶振的提供的时钟频率越高,那单片机的运行速度也就越快,如图,在晶振两边添加两个30pF(27--33pF)电容,使震荡更加稳定。震荡电路接在AT89S52单片机的18、19两个引脚上,给单片机提供震荡信号
2.4、 电源电路
图2.3为电源电路,用的是六脚开关控制电源通断,同时控制了电源的正负极,安全方便,同时有一个电源指示灯,上面加有一个1K的限流电阻。该电路实现了对系统的电源控制,同时显示了电路的状态,方便大家操作。
2.5、 显示电路
图2.4为12864显示电路,该12864的数据口用的AT89S52的P0口,由于P0口内部没有加上拉电阻,需外接10K的上拉电阻,才能保证数据的正常传输。12864的3脚为背光调节的引脚,当3脚的电压不同时背光的亮度就不同,这里设计了一个电路来调节3脚的电压,就是利用一个滑动变阻器一个脚接电源电压,一个脚接地,调节滑动变阻器就可以改变中间那个引脚的电压,我们只需将中间那个引脚和12864的3脚相连接就行了。
2.7 电路图的打印
由于采用的是曝光显影法制作电路板,故打印电路图前,要先对电路图进行打印设置。
由于我们是制作单面板,多余的线路可用跳线连接。打印前,先进行页面设置,具体设置参数如图2.6.1所示,在Mechanical层放置填充方块,覆盖整个电路图。然后进行打印设置:
点击文件,打印设置,然后点击高级选项,在高级选项中选择将要打印的层,本系统中需打印的层如图2.6.2所示:
然后点击左下角的特性设置各层的打印颜色。Bottom Layer 设置为白色,Mechanical层设置为黑色,Multi-Layer设置成白色。设置好后,将打印比例设置为1.0即可进行打印,打印时要注意用硫酸纸或菲林纸,我们是将线布在底层,打印可以选择镜像打印,就是将图2.6.2中Mirror这个选项勾上,到时候正面曝光,也可以将就按照图2.6.2设置,曝光时就背面曝光。
3、 系统的软件设计
3.1、系统概述
本系统的所有程序均采用C语言编写,开发工具为keil,开发环境为windows。应用软件开发的模块化思想来开发这个项目,程序定义了许多子程序来分别管理各个模块,通过对各个子程序的操作,来构建整个程序框架。各个子程序即可单独提取出作为独立的一个有机代码,大大增加了项目的健壮性及可移植性。
3.2、软件详细设
3.2.1 头文件
本系统写了很多头文件,里面包含了很多子模块的信息,各个子模块相互分开,加大了项目的可移植性,如在其它的项目中要使用这些子模块,只需要将C文件和H文件复制过去就行了。包含的头文件如下:
#include
#include
#include
#include "GPS.h"
#include "LCD.h"
#include "display.h"
3.2.2、系统IO定义
整个系统用到了很多独立的IO,在控制12864的读写中要定义几个IO,在控制GPS模块的数据传输中都要用到单片机IO,具体定义IO情况如下:
3.2.3、主函数
主函数,项目入口,用于启动其他模块,并在屏幕上显示主界面窗口,主函数具体如下:
/****************************************
主函数
/****************************************/
void main(void)
{
uchar error_num = 0;
Uart_Init(); //初始化串口
Lcd_Init(); //初始化LCD
GPS_Init(); //初始化GPS
rev_stop=0;
REV_NO;
while(1)
{
if (rev_stop) //如果接收完一行
{
TR0 = 1; //开启定时器
REV_YES;
if (change_page % 2 == 1) //换页
{
if (GPS_GGA_Parse(rev_buf, &GPS)) //解析GPGGA
{
GGA_YES;
GPS_DisplayTwo(); //显示第二页信息
error_num = 0;
gps_flag = 0;
rev_stop = 0;
REV_NO;
}
else
{
error_num++;
if (error_num >= 20) //如果数据无效超过20次
{
GGA_NO;
error_num = 20;
GPS_Init(); //返回初始化
}
gps_flag = 0;
rev_stop = 0;
REV_NO;
}
}
else
{
if (GPS_RMC_Parse(rev_buf, &GPS)) //解析GPRMC
{
RMC_YES;
GPS_DisplayOne(); //显示GPS第一页信息
error_num = 0;
gps_flag = 0;
rev_stop = 0;
led1 = 1;
}
else
{
error_num++;
if (error_num >= 20) //如果数据无效超过20次
{
RMC_NO;
error_num = 20;
GPS_Init(); //返回初始化
}
gps_flag = 0;
rev_stop = 0;
REV_NO;
}
}
}
}
}
由于GPS与单片机通信是选用的串口,在程序中首先初始化了串口,在初始化了LCD12864,然后初始化GPS,做好了准备工作后,就一直等待从GPS接收数据。
3.2.4、延时子函数
在整个系统中都要用到延时函数,这个时间的取定决定这整个系统的工作效率,如果延时时间短了就会造成LCD12864显示不正常,还有就是GPS数据传输不成功;但是延时时间过长又会导致系统的效率低下,所以一个准确的延时函数是很重要的,具体的延时函数如下:
void delay(uint z)
{
uint x, y;
for (x = z; x > 0; x--)
for(y = 110; y > 0; y--);
}
void timer0(void) interrupt 1
{
static uchar count = 0;
TH0 = 0x3c;
TL0 = 0xb0;
count++;
if (count == 200) //2*5秒钟
{
count = 0;
change_page++; //换页
if (change_page == 10)
change_page = 0;
}
}
上面是一个延时1ms(晶振为11.0592M)的函数下面是一个中断函数,用于12864显示用的。
3.2.5、12864子模块
12864的显示函数,先要写地址,然后在写数据,在这个过程前还要检测12864是否忙,就是是否在接收上次的数据,具体的函数如下:
#ifndef __LCD_H_
#define __LCD_H_
#include
#include
#define uchar unsigned char
#define uint unsigned int
#define LCD_data P2 //数据口
sbit LCD_RS = P0^7; //寄存器选择输入
sbit LCD_RW = P0^6; //液晶读/写控制
sbit LCD_EN = P0^5; //液晶使能控制
sbit LCD_PSB = P0^4; //串/并方式控制
#define DelayNOP(); {_nop_();_nop_();_nop_();_nop_();};
void clr_screen();
void delay(uint z);
void Lcd_WriteCmd(uchar cmd);
void Lcd_WriteDat(uchar dat);
void Lcd_Init(void);
void Lcd_SetPos(uchar X,uchar Y);
void Lcd_DispLine(uchar line, uchar pos, uchar *str);
#endif //__LCD_H_
#include "LCD.h"
void delay(uint z)
{
uint x, y;
for (x = z; x > 0; x--)
for(y = 110; y > 0; y--);
}
void clr_screen()
{
Lcd_WriteCmd(0x34); //扩充指令操作
delay(5);
Lcd_WriteCmd(0x30); //基本指令操作
delay(5);
Lcd_WriteCmd(0x01); //清屏
delay(5);
}
static bit Lcd_Busy(void)
{
bit result;
LCD_RS = 0;
LCD_RW = 1;
LCD_EN = 1;
DelayNOP();
result = (bit)(P0&0x80);
LCD_EN = 0;
return(result);
}
void Lcd_WriteCmd(uchar cmd)
{
LCD_RS = 0;
LCD_RW = 0;
LCD_EN = 0;
_nop_();
_nop_();
LCD_data = cmd;
DelayNOP();
LCD_EN = 1;
DelayNOP();
LCD_EN = 0;
}
void Lcd_WriteDat(uchar dat)
{
LCD_RS = 1;
LCD_RW = 0;
LCD_EN = 0;
LCD_data = dat;
DelayNOP();
LCD_EN = 1;
DelayNOP();
LCD_EN = 0;
}
void Lcd_Init(void)
{
LCD_PSB = 1; //并口方式
Lcd_WriteCmd(0x34); //扩充指令操作
delay(5);
Lcd_WriteCmd(0x30); //基本指令操作
delay(5);
Lcd_WriteCmd(0x0C); //显示开,关光标
delay(5);
Lcd_WriteCmd(0x01); //清除LCD的显示内容
delay(5);
}
void Lcd_SetPos(uchar X,uchar Y)
{
uchar pos;
if (X==0)
{X=0x80;}
else if (X==1)
{X=0x90;}
else if (X==2)
{X=0x88;}
else if (X==3)
{X=0x98;}
pos = X+Y ;
Lcd_WriteCmd(pos); //显示地址
}
void Lcd_DispLine(uchar line, uchar pos, uchar *str)
{
int i = 0;
Lcd_SetPos(line, pos);
while (str[i] != '