基于51单片机的GPS定位系统

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  // 设置波特率为 9600bps

unsigned 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的分享 

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值