1.项目硬件MCU采用AT89C52、GPS采用的是NE0-6M的GPS模块、显示模块采用LCD1602
2.项目软件代码
1.main.c
#include <reg52.h>
#include <intrins.h>
#include <stdio.h>
#include "delay.h"
#include "1602.h"// 定义串口引脚
sbit NEO_TX = P3^0; // NEO-6M 模块的 TX 引脚连接到 P1.0 引脚
sbit NEO_RX = P3^1; // NEO-6M 模块的 RX 引脚连接到 P1.1 引脚/**The UART1 stage1**/
#define STAGE_SOHE 0x01
#define STAGE_TYPE 0x02
#define STAGE_NONE 0x03
#define STAGE_DATA 0x04
#define BAUDRATE 9600 // 设置波特率为 9600bpsunsigned char Lin0_No[16]="N:000.000000 ";//显示北纬
unsigned char Lin1_Ea[16]="E:000.000000 ";//显示东经
unsigned long xdata time_20ms=0;unsigned char xdata devide_flag; //GPS数据逗号分隔符
unsigned char xdata speed_end; //收速度数据结束标志
unsigned char xdata dir_end; //收方向角数据结束标志
unsigned char xdata sysmode_GPS=0; //gps有效无效标志
unsigned char xdata ew_flag; //东西标志
unsigned char xdata ns_flag; //南北标志unsigned char xdata gps_infor_weijing[17]; //暂存经纬度 格式是以度分秒的是形式
unsigned char xdata gps_infor_speed[4]; //暂存速率
unsigned char xdata gps_infor_time[6]; //时间暂存
unsigned char xdata gps_infor_date[6]; //日期暂存
unsigned char xdata gps_infor_dir[3]; //方向暂存unsigned char xdata recv1_step=STAGE_SOHE; //串口1接收指令步骤
unsigned char xdata uart1_r_buf; //串口的缓存
unsigned char xdata rev1_buf_busy; //串口接收忙碌标志
unsigned char xdata temp1_buf[85]; //串口接收数组
unsigned int xdata record1=0;
unsigned char times = 0; //延时计数
void Init_Timer0(void);//定时器初始化
void UART_Init(void);void main (void)
{
unsigned char num=0;
unsigned long Mid_Du; //中间变量 暂存经纬度的整数部分 即度
unsigned long Mid_Fen; //中间变量 暂存经纬度的小数部分 即分 gps原始数据是度分秒格式
unsigned long Mid_Vale; 中间变量 暂存经纬度 并将其扩大了10000000倍
unsigned char i ;Init_Timer0(); //定时器0初始化
UART_Init();
LCD_Init(); //初始化液晶
DelayMs(20); //延时有助于稳定
LCD_Clear(); //清屏DelayMs(120);
while(1) //主循环
{
if(sysmode_GPS==1) //检测gps是否有效数据
{
sysmode_GPS=0; //清除有效位
times = 0; //防止gps数据未更新就误判断数据无效
Mid_Du=(gps_infor_weijing[0]-0x30)*10000000+(gps_infor_weijing[1]-0x30)*1000000; //处理纬度 暂存整数部分扩大10000000
Mid_Fen=(gps_infor_weijing[2]-0x30)*10000000+(gps_infor_weijing[3]-0x30)*1000000+
(gps_infor_weijing[4]-0x30)*100000+(gps_infor_weijing[5]-0x30)*10000+
(gps_infor_weijing[6]-0x30)*1000+(gps_infor_weijing[7]-0x30)*100; //处理纬度 暂存小数部分扩大10000000
Mid_Fen=Mid_Fen/60; //分秒换算为小数位
Mid_Vale=Mid_Du+Mid_Fen; //最终纬度 格式为000.00000000 非度分秒格式
Lin0_No[0]='N';
Lin0_No[1]=':';
Lin0_No[2]='0';
Lin0_No[3]=Mid_Vale/10000000+0x30; //将处理后的纬度填入字符串 并打印显示
Lin0_No[4]=(Mid_Vale/1000000)%10+0x30;
Lin0_No[5]='.';
Lin0_No[6]=(Mid_Vale/100000)%10+0x30;
Lin0_No[7]=(Mid_Vale/10000)%10+0x30;
Lin0_No[8]=(Mid_Vale/1000)%10+0x30;
Lin0_No[9]=(Mid_Vale/100)%10+0x30;
Lin0_No[10]=(Mid_Vale/10)%10+0x30;
Lin0_No[11]=Mid_Vale%10+0x30;
Mid_Du=(gps_infor_weijing[8]-0x30)*100000000+(gps_infor_weijing[9]-0x30)*10000000+(gps_infor_weijing[10]-0x30)*1000000; //处理经度 暂存整数部分扩大10000000
Mid_Fen=(gps_infor_weijing[11]-0x30)*10000000+(gps_infor_weijing[12]-0x30)*1000000+
(gps_infor_weijing[13]-0x30)*100000+(gps_infor_weijing[14]-0x30)*10000+
(gps_infor_weijing[15]-0x30)*1000+(gps_infor_weijing[16]-0x30)*100; //处理经度 暂存小数部分扩大10000000
Mid_Fen=Mid_Fen/60; //分秒换算为小数位
Mid_Vale=Mid_Du+Mid_Fen; //最终经度 格式为000.00000000 非度分秒格式
Lin1_Ea[0]='E';
Lin1_Ea[1]=':';
Lin1_Ea[2]=Mid_Vale/100000000+0x30; //将处理后的经度填入字符串 并打印显示
Lin1_Ea[3]=(Mid_Vale/10000000)%10+0x30;
Lin1_Ea[4]=(Mid_Vale/1000000)%10+0x30;
Lin1_Ea[5]='.';
Lin1_Ea[6]=(Mid_Vale/100000)%10+0x30;
Lin1_Ea[7]=(Mid_Vale/10000)%10+0x30;
Lin1_Ea[8]=(Mid_Vale/1000)%10+0x30;
Lin1_Ea[9]=(Mid_Vale/100)%10+0x30;
Lin1_Ea[10]=(Mid_Vale/10)%10+0x30;
Lin1_Ea[11]=Mid_Vale%10+0x30;
}
else
{
times++;
if(times>4) //防止gps数据未更新就误判断数据无效
{
times = 0; //清零计数
Lin1_Ea[0]='G'; //无gps信号情况下 打印正在连接
Lin1_Ea[1]='P';
Lin1_Ea[2]='S';
Lin1_Ea[3]=' '; //将处理后的纬度填入字符串 并打印显示
Lin1_Ea[4]='L';
Lin1_Ea[5]='I';
Lin1_Ea[6]='N';
Lin1_Ea[7]='K';
Lin1_Ea[8]='I';
Lin1_Ea[9]='N';
Lin1_Ea[10]='G';
Lin1_Ea[11]='.';
for(i=0;i<12;i++) //数组重新填充
{
Lin0_No[i]=Lin1_Ea[i];
}
}}
LCD_Write_String(0,0,Lin0_No);//显示第一行
LCD_Write_String(0,1,Lin1_Ea);//显示第二行
DelayMs(200); //延时有助于稳定
}}
void Init_Timer0(void)
{
TMOD |= 0x01; //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响
TH0=(65536-20000)/256; //重新赋值 20ms
TL0=(65536-20000)%256;
EA=1; //总中断打开
ET0=1; //定时器中断打开
TR0=1; //定时器开关打开
}void Timer0_isr(void) interrupt 1
{
TH0=(65536-20000)/256; //重新赋值 20ms
TL0=(65536-20000)%256;time_20ms++;
}// 初始化串口
void UART_Init(void) {
TMOD |= 0x20; // 设置为模式 1,8 位自动重装载定时器
TH1 = 256 - (11059200UL / 12 / 32 / BAUDRATE); // 设置波特率
TL1 = TH1; // 初始值与 TH1 相同
TR1 = 1; // 启动定时器 1
SCON = 0x50; // 8 位数据,可变波特率
EA = 1; // 允许全局中断
ES = 1; // 允许串口中断
}void UART_SER (void) interrupt 4 //串行中断服务程序
{
if(RI) //判断是接收中断产生
{
RI=0; //标志位清零
uart1_r_buf=SBUF; //提取buf中的值
rev1_buf_busy=0x00; //判别 放置break问题
switch(recv1_step)
{
case STAGE_SOHE: if(uart1_r_buf == '$') //判断接收到了$ 具体原因参考GPS标准协议NMEA0183
{
rev1_buf_busy=0x01;
if(uart1_r_buf == '$') //再次查看接收的是否是$
{
recv1_step=STAGE_TYPE; //跳转到下一步
record1=0; //计数清零
}
else
{
recv1_step=STAGE_SOHE; //恢复初始化
record1=0;
}
}
break;
case STAGE_TYPE: if(rev1_buf_busy == 0x00)
{rev1_buf_busy=0x01;
temp1_buf[record1]=uart1_r_buf;
record1++;
if(record1 == 0x05)
{ //查看$GPRMC开头的命令行
if((temp1_buf[0] == 'G') && (temp1_buf[1] == 'P') && (temp1_buf[2] == 'R') && (temp1_buf[3] == 'M') && (temp1_buf[4] == 'C'))
{
recv1_step=STAGE_NONE; //跳转到下一步
record1=0;
}
else
{
recv1_step=STAGE_SOHE;//恢复初始化
record1=0;
}
}
}
break;
case STAGE_NONE: if(rev1_buf_busy == 0x00)//接收命令格式:$GPRMC,054347.00,A,3202.04770,N,11846.23632,E,0.000,0.00,221013,,,A*67
{
rev1_buf_busy=0x01;
record1++;
if((record1 > 0x01) && (record1 < 0x08))
{
gps_infor_time[record1-2]=uart1_r_buf; //时间存储
}
if((uart1_r_buf == ',') && (record1 > 0x07) && (record1 < 0x010)) //||((uart1_r_buf == ',') && (record1==0x02))
{
record1=0xcc;
}
if(record1 == 0xcd)
{
record1=0;
devide_flag=2;
speed_end=0x00;
dir_end=0x00;
if(uart1_r_buf == 'A') //gps收到数据 且有效
{
recv1_step=STAGE_DATA; //跳转到下一步
}
else
{
sysmode_GPS=0;
recv1_step=STAGE_SOHE; //无效恢复初始化
record1=0;
}
}
}
break;
case STAGE_DATA: if(rev1_buf_busy == 0x00)
{
rev1_buf_busy=0x01;
record1++;
if(uart1_r_buf == ',') //判断逗号
{
devide_flag++; //逗号次数记录
record1=0;
}
if(devide_flag == 3)
{
if((record1 > 0) && (record1 < 5))
{
gps_infor_weijing[record1-1]=uart1_r_buf; //存储经纬度 此处为纬度
}
if((record1 > 5) && (record1 < 10)) //跳过小数点的存储
{
gps_infor_weijing[record1-2]=uart1_r_buf; //存储经纬度 此处为纬度
}
}
if(devide_flag == 4)
{
if(record1 > 0)
{
ns_flag=uart1_r_buf; //接收纬度NS标志
}
}
if(devide_flag == 5)
{
if((record1 > 0) && (record1 < 6))
{
gps_infor_weijing[record1+7]=uart1_r_buf; //存储经纬度 此处为纬度
}
if((record1 > 6) && (record1 < 11)) //跳过小数点的存储
{
gps_infor_weijing[record1+6]=uart1_r_buf; //存储经纬度 此处为经度
}
}
if(devide_flag == 6)
{
if(record1 > 0)
{
ew_flag=uart1_r_buf; //经度度 EW标志
}
}
if(devide_flag == 7)
{
if(speed_end == 0x00)
{
if((record1 > 0) && (uart1_r_buf != '.'))
{
gps_infor_speed[record1-1]=uart1_r_buf; //接收速率
}
else if(uart1_r_buf == '.')
{
record1--;
speed_end=0xff;
}
}
else if(speed_end == 0xff)
{
speed_end=0xfe;
gps_infor_speed[record1-1]=uart1_r_buf;
gps_infor_speed[3]=gps_infor_speed[record1-1];
gps_infor_speed[2]=gps_infor_speed[record1-2];
if(record1 > 2)
{
gps_infor_speed[1]=gps_infor_speed[record1-3];
}
else
{
gps_infor_speed[1]=0x30;
}
if(record1 > 3)
{
gps_infor_speed[0]=gps_infor_speed[record1-4];
}
else
{
gps_infor_speed[0]=0x30;
}
}
}
if(devide_flag == 8)
{
if(dir_end == 0x00)
{
if((record1 > 0) && (uart1_r_buf != '.'))
{
gps_infor_dir[record1-1]=uart1_r_buf; //存储方向
}
else if(uart1_r_buf == '.')
{
record1--;
dir_end=0xff;
}
}
else if(dir_end == 0xff)
{
dir_end=0xfe;
if(record1 == 2)
{
gps_infor_dir[2]=gps_infor_dir[record1-2];
gps_infor_dir[1]=0x30;
gps_infor_dir[0]=0x30;
}
if(record1 == 3)
{
gps_infor_dir[2]=gps_infor_dir[record1-2];
gps_infor_dir[1]=gps_infor_dir[record1-3];
gps_infor_dir[0]=0x30;
}
}
}
if(devide_flag == 9)
{
if((record1 > 0) && (record1 < 7))
{
gps_infor_date[record1-1]=uart1_r_buf;
}
}
if(uart1_r_buf == 0x0d)
{
recv1_step=STAGE_SOHE; //接收完成 并信号确定
record1=0; //恢复初始化状态 为下一次准备
devide_flag=0;
sysmode_GPS=1; //置位 GPS 信号为正确
}
}
break;
}
}
if(TI) //如果是发送标志位,清零
TI=0;
}
1602.c
#include "1602.h"
#include "delay.h"
#include <intrins.h>#define uchar unsigned char
#define uint unsigned int#define _NOP() _nop_()
sbit RS = P2^6; //定义端口
sbit RW = P2^5;
sbit EN = P2^7;#define DataPort P0
#define DataPIN P0#define CLR_RS (RS=0)
#define SET_RS (RS=1)
#define CLR_RW (RW=0)
#define SET_RW (RW=1)
#define CLR_EN (EN=0)
#define SET_EN (EN=1)/*
LcdReset(); //LCD1602初始化
DelayMs(10);
sprintf(temp,"1111111111111111");//更新显示
DispStr(0,0,(unsigned char *)temp);//打印显示
sprintf(temp,"1111111111111111");//更新显示
DispStr(0,1,(unsigned char *)temp);//打印显示
*/
/***********************************************
函数名称:DispStr
功 能:让液晶从某个位置起连续显示一个字符串
参 数:x--位置的列坐标
y--位置的行坐标
ptr--指向字符串存放位置的指针
返回值 :无
***********************************************/
void DispStr(uchar x,uchar y,uchar *ptr)
{
uchar *temp;
uchar i,n = 0;
temp = ptr;
while(*ptr++ != '\0') n++; //计算字符串有效字符的个数
for (i=0;i<n;i++)
{
Disp1Char(x++,y,temp[i]);
if (x == 0x10)
{
break;
}
}
}/*******************************************
函数名称:DispNchar
功 能:让液晶从某个位置起连续显示N个字符
参 数:x--位置的列坐标
y--位置的行坐标
n--字符个数
ptr--指向字符存放位置的指针
返回值 :无
*******************************************
void DispNChar(uchar x,uchar y, uchar n,uchar *ptr)
{
uchar i;
for (i=0;i<n;i++)
{
Disp1Char(x++,y,ptr[i]);
if (x == 0x10)
{
x = 0;
y ^= 1;
}
}
}
*/
/*******************************************
函数名称:LocateXY
功 能:向液晶输入显示字符位置的坐标信息
参 数:x--位置的列坐标
y--位置的行坐标
返回值 :无
********************************************/
void LocateXY(uchar x,uchar y)
{
uchar temp;temp = x&0x0f;
y &= 0x01;
if(y) temp |= 0x40; //如果在第2行
temp |= 0x80;LcdWriteCommand(temp,1);
}/*******************************************
函数名称:Disp1Char
功 能:在某个位置显示一个字符
参 数:x--位置的列坐标
y--位置的行坐标
data--显示的字符数据
返回值 :无
********************************************/
void Disp1Char(uchar x,uchar y,uchar data1)
{
LocateXY( x, y );
LcdWriteData( data1 );
}/*******************************************
函数名称:LcdReset
功 能:对1602液晶模块进行复位操作
参 数:无
返回值 :无
********************************************/
void LcdReset(void)
{
// DataDir = 0xFF; //数据端口设为输出状态
LcdWriteCommand(0x38, 0); //规定的复位操作
DelayMs(5);
LcdWriteCommand(0x38, 0);
DelayMs(5);
LcdWriteCommand(0x38, 0);
DelayMs(5);LcdWriteCommand(0x38, 1); //显示模式设置
LcdWriteCommand(0x08, 1); //显示关闭
LcdWriteCommand(0x01, 1); //显示清屏
LcdWriteCommand(0x06, 1); //写字符时整体不移动
LcdWriteCommand(0x0c, 1); //显示开,不开游标,不闪烁
}/*------------------------------------------------
清屏函数
------------------------------------------------*/
void LcdClear(void)
{
LcdWriteCommand(0x01,1);
DelayMs(5);
}/*******************************************
函数名称:LcdWriteCommand
功 能:向液晶模块写入命令
参 数:cmd--命令,
chk--是否判忙的标志,1:判忙,0:不判
返回值 :无
********************************************/
void LcdWriteCommand(uchar cmd,uchar chk)
{if (chk) WaitForEnable(); // 检测忙信号?
CLR_RS;
CLR_RW;
_NOP();DataPort = cmd; //将命令字写入数据端口
_NOP();
SET_EN; //产生使能脉冲信号
_NOP();
_NOP();
CLR_EN;
}/*******************************************
函数名称:LcdWriteData
功 能:向液晶显示的当前地址写入显示数据
参 数:data--显示字符数据
返回值 :无
********************************************/
void LcdWriteData( uchar data1 )
{
WaitForEnable(); //等待液晶不忙
SET_RS;
CLR_RW;SET_EN;
_NOP();
DataPort = data1; //将显示数据写入数据端口
_NOP();
//产生使能脉冲信号
_NOP();
_NOP();
CLR_EN;
}/*******************************************
函数名称:WaitForEnable
功 能:等待1602液晶完成内部操作
参 数:无
返回值 :无
********************************************/
void WaitForEnable(void)
{
unsigned int later=0;
DataPort=0xff;
CLR_RS;
SET_RW;
_NOP();
SET_EN;
_NOP();
_NOP();
// while((DataPIN&Busy)!=0);
while(((DataPIN&0x80)!=0)&&(later<1000)) //检测忙标志
{
DelayUs2x(2);
later++;
}
CLR_EN;
// DataDir|=0xFF; //将P4口切换为输出状态
}
delay.c
#include "delay.h"
/*------------------------------------------------
uS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编,大致延时
长度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t)
{
while(--t);
}
/*------------------------------------------------
mS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
delay.h
/*-----------------------------------------------
名称:LCD1602.h
论坛:www.doflye.net
编写:shifang
日期:2009.5
修改:无
内容:
引脚定义如下:1-VSS 2-VDD 3-V0 4-RS 5-R/W 6-E 7-14 DB0-DB7 15-BLA 16-BLK
------------------------------------------------*/
#include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
#include<intrins.h>#ifndef __1602_H__
#define __1602_H__void DispStr(unsigned char x,unsigned char y,unsigned char *ptr);
void DispNChar(unsigned char x,unsigned char y, unsigned char n,unsigned char *ptr);
void LocateXY(unsigned char x,unsigned char y);
void Disp1Char(unsigned char x,unsigned char y,unsigned char data1);
void LcdReset(void);
void LcdWriteCommand(unsigned char cmd,unsigned char chk);
void LcdWriteData( unsigned char data1 );
void WaitForEnable(void);
void LcdClear(void);#define LCD_Init LcdReset
#define LCD_Write_Char Disp1Char
#define LCD_Write_String DispStr
#define LCD_Clear LcdClear
#endif
delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
/*------------------------------------------------
uS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编,大致延时
长度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t);
/*------------------------------------------------
mS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t);#endif
以下是项目压缩包链接
链接:https://pan.baidu.com/s/1eUJ4JuEyaWN2NG1fEKvL5Q?pwd=x68n
提取码:x68n
--来自百度网盘超级会员V4的分享