找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 10803|回复: 19
打印 上一主题 下一主题
收起左侧

基于51单片机的GPS定位系统程序设计 LCD12864显示

  [复制链接]
跳转到指定楼层
楼主
新人一枚,由于某些需要制作这一块的东西,就把自己的理解写在这里,欢迎各位大佬交流。
GPS定位的基本工作原理是:导航卫星上搭载了专用的无线电设备,可向地面用户不断的发射固定频段的无线电信号,用户利用导航接收机收到卫星上的导航信号后,通过时间测距或多普勒测速获得自己相对于卫星的距离参数,并根据卫星发播的轨道、时间参数等信息求得卫星的实时位置,进而解算出自身的地理位置坐标和速度矢量。
卫星星文信息的解算我们大可不必深究,市面上有现成的集成模块可以实现卫星信号的解算输出,并且几乎所有的模块都遵循或至少兼容统一的串口通信协议--NMEA 0183。
   

     多数GPS模块的引脚主要为VCC、GND、TXD、RXD、PPS。其中TXD、RXD为串口通信接口,PPS为秒脉冲输出接口,用于同步授时。模块的串口输出如图所示(可用一个USB转TTL模块将GPS模块和电脑相连,再用串口助手或模块自带的配置软件查看GPS模块串口的输出)



     由此可见,GPS模块每秒各输出一次$GPGGA $GPGSA $GPGSV $GPGLL $GPRMC $GPVTG语句,其中最常用的语句为$GPRMC。它的基本格式如下:
$GPRMC,(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)*hh(CR)(LF)
        (1) UTC时间,hhmmss(时分秒)
        (2) 定位状态,A=有效定位,V=无效定位
        (3) 纬度ddmm.mmmmm(度分)
        (4) 纬度半球N(北半球)或S(南半球)
        (5) 经度dddmm.mmmmm(度分)
        (6) 经度半球E(东经)或W(西经)
        (7) 地面速率(000.0~999.9节)
        (8) 地面航向(000.0~359.9度,以真北方为参考基准)
        (9) UTC日期,ddmmyy(日月年)
        (10)磁偏角(000.0~180.0度,前导位数不足则补0)
        (11) 磁偏角方向,E(东)或W(西)
        (12) 模式指示(A=自主定位,D=差分,E=估算,N=数据无效)
      可以通过GPS模块的配置软件让模块只输出这一条语句,也可以通过判断第四位的字符是不是"M"从这几条语句中筛选出$GPRMC语句。编程的流程图如下图所示:

     这样解码的效率较低,但不影响效果。可以在串口接收中断里定义一个5位的命令类型数组,在中断内就判断命令的类型,这样免去了每条命令都完全接收的麻烦。这里卫星解码过后的定位数据、时间数据可以直接用1602和12864显示。由于我的课设需求是要有一个基于LabView的上位机显示,所以还需要考虑串口被占用的问题。由于还有其他数据需要处理,加之用普通IO口模拟串口输出效率较低需要占用较多资源,所以暂定用三态门拓展串口。以后不定期再更新完善,欢迎各位交流指点。1602和12864显示的代码就添加在附件里了。

V1.0第一次编写
V1.1更改一些外部中断的BUG
V1.2加入UTC时间/日期和北京时间/日期的转换,包括闰年、日/月/年的进位计算

单片机源程序如下:
  1. #include "main.h"
  2. #include "LCD12864.h"
  3. #include "GPS.h"

  4. //定义变量
  5. unsigned char KEY_NUM = 0;
  6. bit Page = 0;
  7. unsigned char xdata Display_GPGGA_Buffer[68];
  8. unsigned char xdata Display_GPRMC_Buffer[68];
  9. bit Flag_OV = 0;
  10. bit Flag_Calc_GPGGA_OK = 0;
  11. bit Flag_Calc_GPRMC_OK = 0;

  12. //****************************************************
  13. //主函数
  14. //****************************************************
  15. void main()
  16. {
  17.         unsigned char i = 0;
  18.         LCD12864_Reset();                                                                //初始化液晶
  19.         LCD12864_HAIZI_SET();                                                        //设置为普通模式
  20.         
  21.         Delay_ms(100);
  22.         

  23.         LCD12864_NoWaitIdle_COM_Write(0x80);                                                //指针设置
  24.         LCD12864_write_word("※※※※※※※※");
  25.         LCD12864_NoWaitIdle_COM_Write(0x90);                                                //指针设置
  26.         LCD12864_write_word("    欢迎使用    ");                        
  27.         LCD12864_NoWaitIdle_COM_Write(0x88);                                                //指针设置
  28.         LCD12864_write_word("正在初始化GPS...");
  29.         LCD12864_NoWaitIdle_COM_Write(0x98);                                                //指针设置
  30.         LCD12864_write_word("※※※※※※※※");

  31.         Uart_Init();

  32.         while(1)
  33.         {
  34.                 Scan_Key();
  35.                 if(Flag_GPS_OK == 1 && RX_Buffer[4] == 'G' && RX_Buffer[6] == ',' && RX_Buffer[13] == '.')                        //确定是否收到"GPGGA"这一帧数据
  36.                 {
  37.                         for( i = 0; i < 68 ; i++)
  38.                         {
  39.                                 Display_GPGGA_Buffer[i] = RX_Buffer[i];        
  40.                         }
  41.                         Hour = (Display_GPGGA_Buffer[7]-0x30)*10+(Display_GPGGA_Buffer[8]-0x30)+8;                        //UTC时间转换到北京时间UTC+8   
  42.                                                                                                                                                                                                 //0X30为ASCII码转换为数字
  43.                                 if( Hour >= 24)                                //溢出
  44.                         {
  45.                                 Hour %= 24;                                //获取当前Hour
  46.                                 Flag_OV = 1;                        //日期进位
  47.                         }
  48.                         else
  49.                         {
  50.                                 Flag_OV = 0;
  51.                         }

  52.                         Min_High = Display_GPGGA_Buffer[9];
  53.                         Min_Low = Display_GPGGA_Buffer[10];
  54.         
  55.                         Sec_High = Display_GPGGA_Buffer[11];
  56.                         Sec_Low = Display_GPGGA_Buffer[12];

  57.                         Flag_Calc_GPGGA_OK = 1;
  58.                 }

  59.                 if(Page == 0 && Flag_Calc_GPGGA_OK == 1)
  60.                 {
  61.                         LED1 = ~LED1;
  62.                         Flag_Calc_GPGGA_OK = 0;
  63.                
  64.                         LCD12864_NoWaitIdle_COM_Write(0x80);                        //设置指针
  65.                         LCD12864_write_word("★");                                //显示内容
  66.                         LCD12864_Data_Write(Hour/10+0x30);
  67.                         LCD12864_Data_Write(Hour%10+0x30);
  68.                         LCD12864_write_word("时");
  69.         
  70.                         LCD12864_Data_Write(Min_High);
  71.                         LCD12864_Data_Write(Min_Low);
  72.                         LCD12864_write_word("分");
  73.         
  74.                         LCD12864_Data_Write(Sec_High);
  75.                         LCD12864_Data_Write(Sec_Low);
  76.                         LCD12864_write_word("秒");
  77.                         LCD12864_write_word("★");
  78.         
  79.         
  80.                         LCD12864_NoWaitIdle_COM_Write(0x90);                        //设置指针
  81.                         LCD12864_write_word("纬度:");                                //显示内容
  82.                         
  83.                         LCD12864_Data_Write(Display_GPGGA_Buffer[28]);                        //N 或者 S
  84.                         LCD12864_Data_Write(' ');

  85.                         LCD12864_Data_Write(Display_GPGGA_Buffer[17]);                        //纬度
  86.                         LCD12864_Data_Write(Display_GPGGA_Buffer[18]);                        //纬度
  87.                         LCD12864_write_word("°");
  88.                         LCD12864_Data_Write(Display_GPGGA_Buffer[19]);                        //纬度
  89.                         LCD12864_Data_Write(Display_GPGGA_Buffer[20]);                        //纬度
  90.                         LCD12864_write_word("' ");


  91.                         LCD12864_NoWaitIdle_COM_Write(0x88);                        //设置指针
  92.                         LCD12864_write_word("经度:");                                //显示内容
  93.                         
  94.                         LCD12864_Data_Write(Display_GPGGA_Buffer[42]);                        //E 或者 W

  95.                         LCD12864_Data_Write(Display_GPGGA_Buffer[30]);                        //经度
  96.                         LCD12864_Data_Write(Display_GPGGA_Buffer[31]);        
  97.                         LCD12864_Data_Write(Display_GPGGA_Buffer[32]);        
  98.                         LCD12864_write_word("°");
  99.                         LCD12864_Data_Write(Display_GPGGA_Buffer[33]);                        
  100.                         LCD12864_Data_Write(Display_GPGGA_Buffer[34]);               
  101.                         LCD12864_write_word("' ");

  102.                         LCD12864_NoWaitIdle_COM_Write(0x98);                        //设置指针
  103.                         LCD12864_write_word("海拔:    ");                                //显示内容
  104.                         
  105.                         LCD12864_Data_Write(Display_GPGGA_Buffer[54]);        
  106.                         LCD12864_Data_Write(Display_GPGGA_Buffer[55]);        
  107.                         LCD12864_Data_Write(Display_GPGGA_Buffer[56]);        
  108.                         LCD12864_Data_Write(Display_GPGGA_Buffer[57]);
  109.                         LCD12864_write_word("米");                        
  110.                 }
  111.                
  112.                 if(Flag_GPS_OK == 1 && RX_Buffer[4] == 'M' && RX_Buffer[52] == ',' && RX_Buffer[59] == ',')                        //确定是否收到"GPRMC"这一帧数据
  113.                 {
  114.                         for( i = 0; i < 68 ; i++)
  115.                         {
  116.                                 Display_GPRMC_Buffer[i] = RX_Buffer[i];        
  117.                         }

  118.                         Year_High = Display_GPRMC_Buffer[57];
  119.                         Year_Low = Display_GPRMC_Buffer[58];

  120.                         Month_High = Display_GPRMC_Buffer[55];
  121.                         Month_Low = Display_GPRMC_Buffer[56];

  122.                         Day_High = Display_GPRMC_Buffer[53];
  123.                         Day_Low = Display_GPRMC_Buffer[54];


  124.                         if(Flag_OV == 1)                        //有进位
  125.                         {
  126.                                 UTCDate2LocalDate();                        //UTC日期转换为北京时间               
  127.                         }

  128.                         Flag_Calc_GPRMC_OK = 1;
  129.                 }

  130.                 if(Page == 1 && Flag_Calc_GPRMC_OK == 1)
  131.                 {
  132.                         LED1 = ~LED1;
  133.                         Flag_Calc_GPRMC_OK = 0;
  134.                         
  135.                         LCD12864_NoWaitIdle_COM_Write(0x80);                        //设置指针
  136.                         LCD12864_write_word("20");
  137.                         LCD12864_Data_Write(Year_High);
  138.                         LCD12864_Data_Write(Year_Low);
  139.                         LCD12864_write_word("年");
  140.         
  141.                         LCD12864_Data_Write(Month_High);
  142.                         LCD12864_Data_Write(Month_Low);
  143.                         LCD12864_write_word("月");
  144.         
  145.                         LCD12864_Data_Write(Day_High);
  146.                         LCD12864_Data_Write(Day_Low);
  147.                         LCD12864_write_word("日");
  148.                         
  149.                         
  150.                         LCD12864_NoWaitIdle_COM_Write(0x90);                        //设置指针
  151.                         LCD12864_write_word("速度:  ");                                //显示内容

  152.                         LCD12864_Data_Write(' ');
  153.                         LCD12864_Data_Write(Display_GPRMC_Buffer[46]);               
  154.                         LCD12864_Data_Write(Display_GPRMC_Buffer[47]);               
  155.                         LCD12864_Data_Write(Display_GPRMC_Buffer[48]);                        
  156.                         LCD12864_Data_Write(Display_GPRMC_Buffer[49]);        
  157.                         LCD12864_Data_Write(Display_GPRMC_Buffer[50]);
  158.                         
  159.                         LCD12864_write_word("米");
  160.                                                 
  161.                 }               
  162.         }
  163. }

  164. //****************************************************
  165. //UTC日期与当地日期转换
  166. //****************************************************
  167. void UTCDate2LocalDate(void)
  168. {
  169.         Day = (Day_High - 0x30) * 10 + (Day_Low-0x30) + 1;                //日  加一
  170.         Month = (Month_High - 0x30) * 10 + (Month_Low - 0x30);
  171.         Year = 2000 + (Year_High - 0x30) * 10 + (Year_Low - 0x30);
  172.         
  173.         MaxDay = GetMaxDay(Month,Year);                                //获取当月 天数 最大值
  174.         if(Day > MaxDay)                //溢出
  175.         {
  176.                 Day = 1;
  177.                 Month += 1;
  178.                 if(Month > 12)
  179.                 {
  180.                         Year+=1;
  181.                 }
  182.         }

  183.         Day_High = Day/10 + 0x30;                                //转换日期值为ASCII
  184.         Day_Low = Day%10 + 0x30;

  185.         Month_High = Month/10 + 0x30;                        //转换月份值为ASCII
  186.         Month_Low = Month%10 + 0x30;

  187.         Year_High = Year%100/10 + 0x30;                        //转换年份值为ASCII
  188.         Year_Low = Year%10 + 0x30;                        
  189. }

  190. //****************************************************
  191. //获取当月日期最大值
  192. //****************************************************
  193. unsigned char GetMaxDay(unsigned char Month_Value,unsigned int Year_Value)
  194. {
  195.         unsigned char iDays;
  196.         switch(Month_Value)
  197.         {
  198.                 case 1:
  199.                 case 3:
  200.                 case 5:
  201.                 case 7:
  202.                 case 8:
  203.                 case 10:
  204.                 case 12:
  205.                         {
  206.                                 iDays = 31;
  207.                         }
  208.                         break;
  209.                 case 2:
  210.                         {
  211.                                 //2月份比较特殊,需要根据是不是闰年来判断当月是28天还29天
  212.                                 iDays = IsLeapYear(Year_Value)?29:28;
  213.                         }
  214.                         break;
  215.                 case 4:
  216.                 case 6:
  217.                 case 9:
  218.                 case 11:
  219.                         {
  220.                                 iDays = 30;
  221.                         }
  222.                         break;
  223.                 default : break;
  224.         }
  225.         return(iDays);                                                
  226. }

  227. //****************************************************
  228. //闰年检测
  229. //****************************************************
  230. ……………………

  231. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码


所有资料51hei提供下载:
51GPS信息获取例程.zip (372.2 KB, 下载次数: 272)


评分

参与人数 1黑币 +100 收起 理由
admin + 100 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏12 分享淘帖 顶1 踩
回复

使用道具 举报

沙发
ID:603958 发表于 2019-10-16 13:34 | 只看该作者
学习一下,
回复

使用道具 举报

板凳
ID:484435 发表于 2019-10-16 14:10 | 只看该作者
谢谢分享,谢谢分享
回复

使用道具 举报

地板
ID:37851 发表于 2019-10-16 16:20 | 只看该作者
不错  前来学习
回复

使用道具 举报

5#
ID:37851 发表于 2019-10-16 16:21 | 只看该作者
谢谢分享,谢谢分享
回复

使用道具 举报

6#
ID:499701 发表于 2019-10-17 13:37 | 只看该作者
学习一下,谢谢分享
回复

使用道具 举报

7#
ID:613618 发表于 2019-10-17 16:00 来自手机 | 只看该作者
写得不错
回复

使用道具 举报

8#
ID:679011 发表于 2020-3-16 22:04 | 只看该作者
请问PPS是要接地吗?
回复

使用道具 举报

9#
ID:720122 发表于 2020-4-1 12:40 | 只看该作者
学习一下
回复

使用道具 举报

10#
ID:716703 发表于 2020-4-1 14:47 | 只看该作者
不错,谢谢分享
回复

使用道具 举报

11#
ID:725775 发表于 2020-4-11 11:51 | 只看该作者
这个电路图是怎样连的?求解
回复

使用道具 举报

12#
ID:258336 发表于 2020-4-27 13:49 | 只看该作者
叶瑞凤 发表于 2020-3-16 22:04
请问PPS是要接地吗?

pps是gps的秒脉冲输出接口,用来校正本地晶振的误差的,如果授时精度要求不是特别高的话这个接口就用不上。
回复

使用道具 举报

13#
ID:258336 发表于 2020-4-27 13:54 | 只看该作者
噗哈哈 发表于 2020-4-11 11:51
这个电路图是怎样连的?求解

gps模块的TXD接单片机的RXD,gps模块的RXD接单片机的TXD。GPS模块的供电可以接在单片机的开发板上也可以单独供电。主要就这四根线。
回复

使用道具 举报

14#
ID:79544 发表于 2020-5-5 09:50 | 只看该作者
感谢楼主分享,学习啦!!!!
回复

使用道具 举报

15#
ID:745627 发表于 2020-5-6 22:50 来自手机 | 只看该作者
师傅 几个问题问一下:1、如果不需要显示的话,程序删除那些?2、经纬度信息(其他不要)最后储存在哪里?怎样调用?先谢谢师傅了
回复

使用道具 举报

16#
ID:751709 发表于 2020-5-14 15:34 | 只看该作者
我在做这个lcd显示时总是出错。。就连光标都出不来。特意找资料来看看
回复

使用道具 举报

17#
ID:258336 发表于 2020-5-19 15:04 来自手机 | 只看该作者
1057544247 发表于 2020-5-6 22:50
师傅 几个问题问一下:1、如果不需要显示的话,程序删除那些?2、经纬度信息(其他不要)最后储存在哪里? ...

1、需要删除lcd的初始化,读,写等子程序。2、这个程序是实时的,经纬度的数据,每秒都会刷新。
回复

使用道具 举报

18#
ID:258336 发表于 2020-5-19 15:06 来自手机 | 只看该作者
嗨过头了有点累 发表于 2020-5-14 15:34
我在做这个lcd显示时总是出错。。就连光标都出不来。特意找资料来看看

这个你要好好的查一查硬件电路有没有接错,lcd的各个端口有没有和程序里相对应
回复

使用道具 举报

19#
ID:926023 发表于 2021-5-23 21:58 | 只看该作者
感谢楼主答疑解惑
回复

使用道具 举报

20#
ID:1011393 发表于 2022-4-11 21:48 | 只看该作者
谢谢楼主分享!
我在STC15单片机上使用了你的程序,刚开始一直没有反应,我以为程序还是有些小问题,后来过了几分钟突然发现它能够准确显示数据了!我便重新启动,然后也还是过了很长一段时间才显示,请问这个是什么原因呀?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表