基于51单片机的GPS测速仪

1.项目硬件MCU采用AT89C52、GPS采用的是NE0-6M的GPS模块、显示模块采用LCD1602

2.项目软件代码

1.main.c


#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 char speed[8]="S:0.000";
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();           //初始化液晶
    nms_delay(100);        //延时有助于稳定

    while(1)         //主循环
    {
        if(sysmode_GPS==1)        //检测gps是否有效数据
            {
                sysmode_GPS=0;            //清除有效位
                times = 0;    //防止gps数据未更新就误判断数据无效

                
                Mid_Du=(gps_infor_speed[0]-0x30)*1000; //处理经度 暂存整数部分扩大10000000
                
                Mid_Fen=(gps_infor_speed[1]-0x30)*100+(gps_infor_speed[2]-0x30)*10+(gps_infor_speed[3]-0x30);

                Mid_Vale=Mid_Du+Mid_Fen;

                speed[0]='S';                  
                speed[1]=':';                                 
                speed[2]=Mid_Vale/1000+0x30;;                  //将处理后的纬度填入字符串 并打印显示
                speed[3]='.';
                speed[4]=(Mid_Vale/100)%10+0x30;
                speed[5]=(Mid_Vale/10)%10+0x30;
                speed[6]=(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,speed);//显示第一行
//            LCD_write_string(0,1,Lin1_Ea);//显示第二行
            nms_delay(1000);          //延时有助于稳定
        }

    }


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] == 'V') && (temp1_buf[3] == 'T') && (temp1_buf[4] == 'G'))
          {
            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++;

          record1=0;
          devide_flag=2;
          speed_end=0x00;
          dir_end=0x00;
          
          if(uart1_r_buf == 'T')  //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 == 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)
          {
            if((record1 > 1) && (record1 < 5))  
            gps_infor_speed[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;
}

 delay.c

#include<delay.h>

/****************************************************
函数名称:nus_delay
功能描述:微秒级延时子程序


*****************************************************/
void nus_delay(uint nus)
{

    while(nus--)
    {

    }
}
/****************************************************
函数名称:nms_delay
功能描述:毫秒级延时子程序
           
*****************************************************/
void nms_delay(uint nms)
{
    while(nms--)
    nus_delay(100);
}

 1602.c

#include "1602.h"
#include "delay.h"


/****************************************************
函数名称:lcd_init
功能描述:LCD初始化

说明:注意初始化过程并加上适当延时
     后面LCD的显示方式可按实际需要修改
*****************************************************/
void lcd_init(void)
{
    nms_delay(15);
    LCD_write_onechar(0x38,0);
    nms_delay(1);
    LCD_write_onechar(0x38,0);
    nms_delay(1);
    LCD_write_onechar(0x38,0);
    nms_delay(1);
    LCD_write_onechar(0x38,0);
    LCD_write_onechar(0x08,0);    /*显示关闭*/
    LCD_write_onechar(0x01,0);    /*显示清屏*/
      LCD_write_onechar(0x80,0);    /*显示清屏*/
    LCD_write_onechar(0x06,0);    /*显示光标移动设置*/
    nms_delay(1);;  
    LCD_write_onechar(0x0C,0);    /*显示开及光标设置*/
}
/*函数名称:LCD_en_write
功能描述:EN端产生一个脉冲,写LCD

*****************************************************/
void LCD_en_write(void)        
{
    LCD_EN=1;
    nus_delay(150);  //E脉冲周期最小150ns
    LCD_EN=0;
}
/****************************************************
函数名称:wait_LCD_Ready
功能描述:判断LCD忙标志

*****************************************************/
void wait_LCD_Ready(void)
{
//    uchar temp1;
    LCD_DATA=0XFF;
                                   //判断LCD忙标志 
                                   //1为忙  0为空闲
    LCD_RS=0;
    LCD_RW=1;                      //读
    LCD_EN=1;
    nus_delay(2);
/*    do
    {
    temp1=LCD_DATA;
    temp1&=0x80;
    }while(temp1);   */
    LCD_EN=0;
    LCD_RS=1;
}

/****************************************************
函数名称:LCD_write_onechar
功能描述:写一个字节到LCD
输 入: COMM(指令)   DAT(显示数据) 

*****************************************************/
void LCD_write_onechar(uchar COMM,uchar DAT)

     wait_LCD_Ready();         //等待LCD空闲
     LCD_RW=0;                 //写
     
     //写数据
     if(COMM==0)
     {
        LCD_RS=1;  //RS高电平向LCD写数据
        LCD_DATA=DAT;
     } 
     //写命令
     else
     {
        LCD_RS=0;
        LCD_DATA=COMM;
     }
     LCD_en_write(); 
     nus_delay(2);
     LCD_RW=1;
     LCD_RS=1;
}

/****************************************************
函数名称:LCD_write_string
功能描述:LCD字符串显示函数
输 入: x/y(X与Y坐标)   *string(字符串首地址)

*****************************************************/
void LCD_write_string(uchar X,uchar Y,uchar *string)
{
    set_LCD_xy( X, Y );                //设置LCD显示坐标
    while (*string) 
    {
      LCD_write_onechar(0,*string );
      string ++;                       //指向下一显示字符地址
    }
}

/****************************************************
函数名称:set_LCD_xy
功能描述:设置显示坐标
输 入: x/y(X与Y坐标)
输 出: 无

说明:  LMB1602第一行首地址为0x80
              第二行为0xC0

*****************************************************/
void set_LCD_xy(uchar x, uchar y )
{
    uchar DIS_address;
    if (y == 0) 
       DIS_address = 0x80 + x;            //第一行X列
    else 
       DIS_address = 0xc0 + x;
    LCD_write_onechar( DIS_address, 0 );  //第二行X列
}
 

 delay.h

#ifndef __DELAY_H_
#define __DELAY_H_

//包含头文件
/**********************************/

//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint 
#define uint unsigned int
#endif
/**********************************/

void nus_delay(uint nus);
void nms_delay(uint nms);
#endif

 1602.h

#ifndef __LCD_H_
#define __LCD_H_
/**********************************
当使用的是4位数据传输的时候定义,
使用8位取消这个定义
**********************************/
//#define LCD1602_4PINS

/**********************************
包含头文件
**********************************/
#include <REGX52.H>
#

//---重定义关键词---//

#define uchar unsigned char


 
#define uint unsigned int


#define  LCD_DATA         P0        //定义LCD的D0-D7所在端口
#define  LCD_EN           P2_7      
#define  LCD_RW           P2_5      //LCD读写控制引脚
#define  LCD_RS           P2_6      //LCD指令或数据选择引脚


/**********************************/
//函数声明
void lcd_init(void);
void LCD_en_write(void);
void wait_LCD_Ready(void);
void set_LCD_xy(uchar x, uchar y );
void LCD_write_onechar(uchar COMM,uchar DAT);
void LCD_write_string(uchar X,uchar Y,uchar *string);            

#endif
 

 以下是项目压缩包链接 

链接:https://pan.baidu.com/s/12bTkK376gDewHu3UXWY8Uw?pwd=ntqs 
提取码:ntqs 
--来自百度网盘超级会员V4的分享

  • 15
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值