请问如何用单片机+北斗模块实现定位功能?

Miltonliu201051单片机 2024-01-05 16:45:03 5895阅读
请问如何STC12C5A32S单片机和卫星接收模块UM220-III设计的北斗导航系统接收机实现定位功能?

提示:如果此问题没有解决您的需求,您可以点击 “我也要问” 在线咨询。 我也要问

若此问题存在违规行为,您可以点击 “举报”

1条回答

  • silly蒙奇奇
    2024-01-05 16:47:01 已采纳
    导航是继移动通信之后发展最快的信息产业之一,只要是设计到位置、速度、时间信息的领域斗鱼卫星导航技术相关。中国北斗卫星导航系统是我国自主研制的全球导航系统,是继美国定位系统(GPS)、俄罗斯格洛纳斯卫星导航系统之后的第三个成熟的卫星导航系统。随着北斗导航系统的不断完善,基于北斗导航系统的定位也应用的越来越广泛。小编采用的STC12C5A32S2单片机结合卫星接收模块UM220-III设计的北斗导航系统接收机,UM220-III是双模接收模块,能够同时接受GPS、北斗的信号。这次设计主要完成了导航信息接收机的基本设计,实现了获取实时的位置经纬度、标准时间等相关信息的显示功能。

    1 原理介绍
    采用以单片机为核心,读取北斗导航系统模块的标准数据,并在 LCD 屏幕上显示当前的经纬度信息。具体的系统方案图如下:

    1.1北斗 UM220-III 模块简介
    (1)接口电路
    北斗模块芯片电路由北斗模块UM220-III N 和其附加电子器件组成。其中北斗模块外接4组排针,在芯片与电源之间串联电感的作用是起差模滤波作用,防止电流突变对芯片产生损坏,并联电感的作用是提高芯片运行的稳定性,防止产生噪声。
    模块输入端口(UM220-III N 包括:RXD、GPIO、SDA ,SCL 等 如下图)为防止输入端不定态对模块造成影响,模块内置上拉电阻至VCC,因此在模块未加电时,如果上述端口有数据输入,会在模块VCC上形成串电,又可能造成模块上电失败。
    case1:设计中使用 nRESET功能
    在模块上电后,将nRESET拉低5 ms以上,即可确保模块正常启动。
    case2:设计中未使用nRESET
    在模块上电之前,保证模块已连接输入端口为高阻态或低电平,以避免串电。使用串口1的典型用户,需要吧RXD1设置为高阻态或低电平,未使用的其他PIN悬空。程序流程图如下:
    (2)LCD 液晶显示器
    LCD液晶显示器数据由单片机p0口进行控制,p2口进行指令控制操作。LCD1602是一种专门用来显示字母,数字,符号等的点阵型液晶模块。1602:显示的内容主要是16*2,即可以显示两行,每行16个字符液晶显示模块。相应的管教功能,百度上都是可以查阅的,所以小编这里就不赘述了。
    (3)UM220-III 通信协议简介

    在Unicore 协议中,输入和输出的语句被称为消息。每条消息均为ASCII 字符组成的字符串。
    消息的基本格式为:KaTeX parse error: Can't use function '\r' in math mode at position 32: …2,data3,……[*cc]\̲r̲\n 所有的消息都以 '’(0x24)开始,后面紧跟着的就是消息名。之后的跟的就是不定数目的参数和数据。消息名与数据之间均以逗号隔开(0x2c)进行分割。最后一个参数是可选的校验和,以 ‘*’(0x2A)与前面的数据分割最后,输入的消息以 ’ \r\n’ 结束。每条消息的总长度不超过256个字节,消息名和参数,校验和中的字母不区分大小写。
    某些输入命令的某些参数可以省略(在命令描述中被标记为可选)。这些参数可以为空,即在两个逗号之间没有任何字符。

    2 调试方法
    由1.1的原理简介可知,此次课题实现主要由 5 部分组成:系统初始化、设定显示模式、读取预显示内容、送扫描脉冲、送显示数据。
    这里对单片机与模块的连接做简要说明。
    UM220/um220-3-n 上带有两组 TTL 电平(2.85V),一组标准电平 RS232电平。当单片机的RS232电平接口接到UM220模块上的RS232上,正常通信。UM220模块上的RS232接口是DB9 母头,可以使用公头的连接线与RS230的接口相连,注意的是通信线需要交叉连接,就像TTL电平中的TXD - - RXD,RXD–TXD 一样,RS232电平通信中也是有2根通信电缆,一个是发送端(PCXD),一个是接收端(PCRXD)。若板子上的RS232 的接口是DB9 母头,那么 2 3针就是 PCRXD 和 PCTXD .第五针就是GND,若没有串口线来连接UM220模块的话,可以考虑在DB9下面的2 3 5 针 焊接出3跟线跟单片机的RS232连接。
    若单片机是3.3V时,可以将单片机的TTL 接口连接到模块上的TTL接口。连接好后就可以编写程序了。
    注意,我们使用的 52 单片机的驱动程序,使用的是11.05926 的晶振,波特率为9600.
    #3 程序编写
    程序主要是由5部分组成:系统初始化,设定显示模式。读取预显示内容,送扫描脉冲,送显示数据。小编只放主程序部分好了:
    定义端口及变量
    #include <REG52.H>
    #include <stdio.h>
    #include <intrins.h>
    #include <lcd1602.h>
    #include <uart.h>
    #include <delay.h>
    #include "string.h"
    #include <stdlib.h>
    
    unsigned char  flag_rec=0;    //接收数据标志
    unsigned char num_rec=0;      // 计数标志      
    //char code TIME_AREA= 8;		//时区,我们不需要它
    unsigned char flag_data;	//date flag
    //only displaty cmd $GPGGA information
    unsigned char JD[16];		//longitude
    unsigned char JD_a;		//经度方向
    unsigned char WD[15];		//latitude
    unsigned char WD_a;		// 纬度方向
    unsigned char date[6];		//date
    unsigned char time[6];		//date
    unsigned char time1[6];		//date
    unsigned char speed[5]={'0','0','0','0','0'};		// 速度
    unsigned char high[6];		// 高度
    unsigned char angle[5];		//方位角
    unsigned char use_sat[2];	// 卫星计数器
    unsigned char total_sat[2];	//卫星总数
    unsigned char lock;			//位置状态
    
    //date handing variable
    unsigned char seg_count;	// 逗号计数器
    unsigned char dot_count;	//小数点计数器
    unsigned char byte_count;	// 位计数器
    unsigned char cmd_number;	// 命令模式
    unsigned char mode;			//
    unsigned char buf_full;		
    unsigned char cmd[5];		// 存储命令模式
    
    //serial disconnect timer
    unsigned  long int tt=0;
    

    主函数 系统初始化

    //main
    void main () 
    {	
      int jd_second,wd_second;  // 中间变量
    	init_uart();	     //初始化序列号
      lcd_init() ;       // 初始化 lcd1602
      delay(200);
      LCD_Write_String(0,0,"Please Waiting...");   //    "Please Waiting" when it is boot up
      delay(200);
      delay(200);
      delay(200);
      delay(200);
      delay(200);
      delay(200);
      delay(200);
      delay(200);
      delay(200);
      delay(200);                             // 延迟显示
      write_com(0x01);			// 清屏
    
    


    设置延时函数,以形成视觉暂留

    while(1)
    {
    tt++;
    if(tt>10000)
      {
        tt=10000;
        write_com(0x01);
        LCD_Write_String(3,0,"No Data!");
        LCD_Write_String(3,1,"No Data!");
        delay(200);
        delay(200);
        delay(200);
        delay(200);
        delay(200);
       }
    

    ···

    读取预显示内容,设置显示格式,转16进制为10进制**


        if(flag_rec==1)             // 获取gps数据
          {
            flag_rec=0;               // 清除标志符
            if (lock==1)              //  获取位置信息
    		  	{  
              //
              LCD_Write_String(0,0,"JD  :");    // 显示经度
              LCD_Write_String(6,0,JD);           // 显示数据
              LCD_Write_String(9,0,".");           // 进制转换
              LCD_Write_String(10,0,JD+3);           
    
              jd_second=60*atof((char *)(JD+5));     
              LCD_Write_Char(13,0,jd_second/10+'0');  // 将上一步转换得到浮点数据打印在lcd
              LCD_Write_Char(14,0,jd_second%10+'0');  // 将上面得到的数据分为两部分,分别打印在LCD 上
              LCD_Write_Char(15,0,' ');                //填充空间
    
              delay(200);                         // 保护lcd
              LCD_Write_String(0,1,"WD  :");     // 显示下一行
              LCD_Write_String(6,1,WD);
              LCD_Write_String(8,1,"."); 
              LCD_Write_String(9,1,WD+2);           // 小数点
              wd_second=60*atof((char *)(WD+4)); 
    // 将字符串转换成浮点数
              LCD_Write_Char(12,1,wd_second/10+'0');
              LCD_Write_Char(13,1,wd_second%10+'0');
              LCD_Write_String(14,1,"  ");
              delay(200);
           }
          }
         }
    		}
    


    串口中断函数及模式判断
    判断的主要依据就是接收端接受消息与预结果匹配,通过设置数组[i,j]和if函数进行判断匹配(发送报文的消息内容见1.3的UM220的通信协议详解)

    //serial interruupt service function
    void ser_int (void) interrupt 4
    {
     	
     	unsigned char tmp;
    	if(RI)
    	{
        tt=0;
    		RI=0;
    		tmp=SBUF;            // 从缓冲区接收数据
    		switch(tmp)   //if $GPGGA,$GNGSW,$GNRMC,get data then processing it
    		{
          //date start with $
    			case '$':
    				cmd_number=0;		// 清除命令模式
    				mode=1;				// 选项命令接收模式
    				byte_count=0;		//清除位计数器
    				flag_data=1;     // 设置数据标志
    				flag_rec=1;		// 设置数据接收标志
    			break;
    
    			case ',':         //Eg:$GNRMC,134645.000,A,2603.964436,N,11912.410232,E,0.000,15.744,030718,,E,A*0B
    				seg_count++;		// 计数器增加
    				byte_count=0;
    				break;
    
    			case '*':
    				switch(cmd_number)
    				{
    					case 1:
    						buf_full|=0x01;   //00000001
    						break;
    					case 2:
    						buf_full|=0x02;  //00000010
    						break;
    					case 3:
    						buf_full|=0x04;  //00000100
    						break;
    				}
    
    				mode=0;         //clear mode
    				break;
    			default:
    // receive date cmd
    				if(mode==1)	
    				{
    					cmd[byte_count]=tmp;	// 获取数据和存储缓冲区					if(byte_count>=4)          //overlook cmd which less 4 bit
    					{			
    						if(cmd[0]=='G')           // 第一个字符
    						{
    							if(cmd[1]=='N')
    							{
    								if(cmd[2]=='G')
    								{
    									if(cmd[3]=='G')
    									{
    										if(cmd[4]=='A')//判断$GNGGA
    										{
    											cmd_number=1;      //数据类型
    											mode=2;            //接收日期
    											seg_count=0;       //comma counter clear
    											byte_count=0;      //位计数器清除
    										}
    									}
    									else if(cmd[3]=='S')       //命令模式$GNGSV
    									{
    										if(cmd[4]=='V')
    										{
    											cmd_number=2;
    											mode=2;                //获取数据
    											seg_count=0;
    											byte_count=0;
    										}
    									}
    								}
    								else if(cmd[2]=='R')   //命令模式 $GNRMC
    								{
    									if(cmd[3]=='M')
    									{
    										if(cmd[4]=='C')
    										{
    											cmd_number=3;
    											mode=2;         //存储数据
    											seg_count=0;
    											byte_count=0;
    										}
    									}
    								}
    							}
    						}
    					}
    				}
    //日期处理
    


    			else if(mode==2)
    			{
    				
    				switch (cmd_number)  //if receive data
    				{
    					case 1:				//get and store data,$GPGGA,[],[],[],[],[],[],[],[],[].....
    						switch(seg_count)   //  comma 计数器
    						{
    							case 2:		// 2rd逗号后的纬度
    								if(byte_count<9)
    								{
    									WD[byte_count]=tmp;   //获取纬度
    								}
    								break;
    							case 3:		//纬度方向
    								if(byte_count<1)
    								{
    									WD_a=tmp;
    								}
    								break;
    							case 4:		//经度
    								if(byte_count<10)
    								{
    									JD[byte_count]=tmp; //存储
    								}
    								break;
    							case 5:		//经度方向
    								if(byte_count<1)
    								{
    									JD_a=tmp;
    								}
    								break;
    							case 6:		//location
    								if(byte_count<1)
    								{
    									lock=tmp;
    								}
    								break;
    							case 7:		
    								if(byte_count<2)
    								{
    									use_sat[byte_count]=tmp;
    								}
    								break;
    							case 9:		// 高度
    								if(byte_count<6)
    								{
    									high[byte_count]=tmp;
    								}
    								break;
    						}
    						break;
    
    					case 2:	//命令模式  $GPGSV
    						switch(seg_count)
    						{
    							case 3:		// 卫星总数
    								if(byte_count<2)
    								{
    									total_sat[byte_count]=tmp;
    								}
    								break;
    						}
    						break;
    


    //命令模式3:无SUE
    						case 3:				//$GPRMC
    							switch(seg_count)
    							{
    								case 1:		//time
    									if(byte_count<6)
    									{				
    										time[byte_count]=tmp;	
    									}
    									break;
    								case 2:		// 位置			
    									if(byte_count<1)
    									{
    									  if (tmp=='V') {lock=0;}
    									  else
    									  {
    									    lock=1;
    									   }
    									}
    									break;
    								case 3:		//lititude			
    //									if(byte_count<9)
    //									{
    //										WD[byte_count]=tmp;//我们只需要一次
    //									}
    									break;
    								case 4:		//					
    									if(byte_count<1)
    									{
    										WD_a=tmp;
    									}
    									break;
    								case 5:		//			
    //									if(byte_count<10)
    //									{
    //										JD[byte_count]=tmp;  //do not get again
    //									}
    									break;
    								case 6:		// 直线方向	
    									if(byte_count<1)
    									{
    										JD_a=tmp;
    									}
    									break;
    								case 7:		// 速度处理		
    									if(byte_count<5)
    									{
    										speed[byte_count]=tmp;
    									}
    									break;
    								case 8:		// 方向角				
    									if(byte_count<5)
    									{
    										angle[byte_count]=tmp;
    									}
    									break;
    								case 9:		//other			
    									if(byte_count<6)
    									{
    										date[byte_count]=tmp;
    									}
    									break;
    
    							}
    							break;
    					}
    				}
    				byte_count++;		// 位计数器++
    				break;
    		}
    	}
    


    4 结果显示








    256 举报
103电阻是多少
STM32 38732人阅读
Linux内核printfk打印信息怎么打开
Linux驱动 8813人阅读
ECxx旋转编码器如何消除抖动和杂波
传感器设计与应用 5470人阅读

快速提问,在线解答

1

描述需求

填写需求概要标题,补充详细需求

2

耐心等

等待网友或网站工作人员在线解答

3

巧咨询

还有疑问?及时追问回复

立即咨询