【嵌入式实战分享】模块篇 UBLOX-6M GPS

目录

  • 项目背景
  • 模块介绍
  • 使用说明
  • 引脚说明
  • 使用介绍
    • 1.调试过程
    • 1.驱动代码
    • 2.应用编程
  • 使用总结
    • 1.常见问题
    • 2.使用心得

项目背景

主机通过GPS模块获取当地卫星信息——经纬度和时间信息甚至海拔,通过无线通讯将这些信息发送到从机。

  • 在这里我用的标准版*

模块介绍

【嵌入式实战分享】模块篇 UBLOX-6M GPS_第1张图片

1, 模块采用U-BLOX NEO-6M模组,体积小巧,性能优异。
2, 模块增加放大电路,有利于无源陶瓷天线快速搜星。(MINI版本没有板载天线
3, 模块可通过串口进行各种参数设置,并可保存在EEPROM,使用方便。
4, 模块自带SMA接口,可以连接各种有源天线,适应能力强。另外Mini版本带有IPEX接口,可以接本店小尺寸的有源陶瓷天线。
5, 模块兼容3.3V/5V电平,方便连接各种单片机系统。
6, 模块自带可充电后备电池,可以掉电保持星历数据
7.本模块默认波特率为 9600

使用说明

本模块为室外GPS模块,需要在室外进行测试 !
【嵌入式实战分享】模块篇 UBLOX-6M GPS_第2张图片

第一次测试的时候,由于是第一次初始化,建议加上外置的天线,这样连接速率更快,在以后的连接中,因为GPS有备用电池可以掉电保存,有了第一次的数据,第二次的初始化会相对较快一些。

当然在室内也是可以的,只不过需要一根较长的天线,连接好GPS模块后,将天线至于窗户外面空旷的位置

看我的巨型天线——路灯!哈哈哈【嵌入式实战分享】模块篇 UBLOX-6M GPS_第3张图片

引脚说明

序号 名称 说明
1 VCC 电源(3.3V~5.0V)
2 GND
3 TXD 模块串口发送脚(TTL电平,不能直接接RS232电平!),可接单片机的RXD
4 RXD 模块串口接收脚(TTL电平,不能直接接RS232电平!),可接单片机的TXD
5 PPS 时钟脉冲输出脚

其中,PPS引脚同时连接到了模块自带了的状态指示灯:PPS,该引脚连接在UBLOX NEO-6M
模组的TIMEPULSE端口,该端口的输出特性可以通过程序设置。PPS指示灯(即PPS引脚),
在默认条件下(没经过程序设置),有2个状态:
1, 常亮,表示模块已开始工作,但还未实现定位。
2, 闪烁(100ms灭,900ms亮),表示模块已经定位成功。
这样,通过 PPS 指示灯,我们就可以很方便的判断模块的当前状态,方便大家使用。

我们只需要学会怎么用即可,具体内部原理,美国佬也进行了封装,我们无从知晓

使用介绍

1.调试过程

调试方式有两种USB调试法,和UART调试法,

其中,如果要使用USB调试,需要先下载驱动【嵌入式实战分享】模块篇 UBLOX-6M GPS_第4张图片
下载好驱动后打开官方驱动工具
【嵌入式实战分享】模块篇 UBLOX-6M GPS_第5张图片

在上方工具栏的接收器中的端口处选择我们GPS模块的端口,其中 右侧的小窗口可以在边界调整。【嵌入式实战分享】模块篇 UBLOX-6M GPS_第6张图片
当然也可以在receiver中调节波特率,在这里我设置的是9600【嵌入式实战分享】模块篇 UBLOX-6M GPS_第7张图片
然后等待启动初始化结束,主界面小窗口会显示获取到的卫星数据
【嵌入式实战分享】模块篇 UBLOX-6M GPS_第8张图片
然后从VIEW选项卡中,通过xxxxx_Console选项卡调用控制平台和配置其他参数(在这里我们没有用到),通过xxxxxx_View观察数据【嵌入式实战分享】模块篇 UBLOX-6M GPS_第9张图片

不过,一般我们不用调节它的一些参数,最多也就是用它调节一下GPS发送速率,(在这里我们没有调节)

1.驱动代码

其实我们就是通过MCU读取GPS模块获取到的已经打包好的数据,根据我们的需求对其进行拆分。

GPS发送的数据为字符型,所以我们采用字符型数组存储接收到的数据,在这里为了方便后期调用,我们把这些数据根据信息的种类进行拆分,借用结构体进行调用(这段code可以放到H文件里)

#include 
#include 
#include 
//定义数组长度
#define GPS_Buffer_Length 80
#define UTCTime_Length 11
#define latitude_Length 11
#define N_S_Length 2
#define longitude_Length 12
#define E_W_Length 2 
#define gpsRxBufferLength  76

typedef struct SaveData 
{
	char GPS_Buffer[GPS_Buffer_Length];
	char isGetData;		//是否获取到GPS数据
	char isParseData;	//是否解析完成
	char UTCTime[UTCTime_Length];		//UTC时间
	char latitude[latitude_Length];		//纬度
	char N_S[N_S_Length];		//N/S
	char longitude[longitude_Length];		//经度
	char E_W[E_W_Length];		//E/W
	char isUsefull;		//定位信息是否有效
} xdata _SaveData;

由于这些数据内存对于52单片机的存储空间较大,我们将这些从串口接收到的数据存储到RAM中,

//uart.c
#include "uart.h"

char idata gpsRxBuffer[gpsRxBufferLength];
unsigned char RX_Count = 0;
_SaveData Save_Data;

void Uart_Init()					  			   
{
	SCON = 0X50;  //UART方式1;8位UART
	REN  = 1;     //允许串行口接收数据
	PCON = 0x00;  //SMOD=0;波特率不加倍
	TMOD = 0x20;  //T1方式2,用于产生波特率
	TH1  = 0xFD;  //装初值
	TL1  = 0xFD;
	TR1  = 1;     //启动定时器1
	EA   = 1;     //打开全局中断控制
	ES   = 1;     //打开串行口中断	
}

void UartPrintf(unsigned char *p)				//发送字符串
{	
 	while(*p)
 	{
	 	SBUF=*(p++);
		while(TI==0);
		TI=0;
	}   
}

void UartPrintASCII(unsigned char c)				//发送一个字符
{ 
    TI=0;   
    SBUF=c;   
    while(TI==0);   
    TI=0;   
}


void RECEIVE_DATA(void) interrupt 4 using 3	  		
{ 
	unsigned char temp = 0;
	char i = 0;
	ES=0;
	temp = SBUF;
	RI = 0;
	
	if(temp == '$')
	{
		RX_Count = 0;	
	}
		
	if(RX_Count <= 5)
	{
	   gpsRxBuffer[RX_Count++] = temp;
	}
	else if(gpsRxBuffer[0] == '$' &gpsRxBuffer[4] == 'M' && gpsRxBuffer[5] == 'C')			//确定是否收到"GPRMC/GNRMC"这一帧数据
	{
		gpsRxBuffer[RX_Count++] = temp;
		if(temp == '\n')									   
		{
			memset(Save_Data.GPS_Buffer, 0, GPS_Buffer_Length);      //清空
			memcpy(Save_Data.GPS_Buffer, gpsRxBuffer, RX_Count); 	//保存数据
			Save_Data.isGetData = true;
			RX_Count = 0;
			memset(gpsRxBuffer, 0, gpsRxBufferLength);      //清空				
		}
		
		if(RX_Count >= 75)
		{
			RX_Count = 75;
			gpsRxBuffer[RX_Count] = '\0';//添加结束符
		}			
	}
	ES=1; 
}

void clrStruct()
{
	Save_Data.isGetData = false;
	Save_Data.isParseData = false;
	Save_Data.isUsefull = false;
	memset(Save_Data.GPS_Buffer, 0, GPS_Buffer_Length);      //清空
	memset(Save_Data.UTCTime, 0, UTCTime_Length);
	memset(Save_Data.latitude, 0, latitude_Length);
	memset(Save_Data.N_S, 0, N_S_Length);
	memset(Save_Data.longitude, 0, longitude_Length);
	memset(Save_Data.E_W, 0, E_W_Length);
	
}

然后我们可能在主函数中可能会调用接收到的这些数据,所以再进行外部声明

//uart.h
#ifndef __GPS_H__
#define __GPS_H__

#include 
#include "main.h"

#define false 0
#define true 1

//函数或者变量声明
extern void Uart_Init();
extern void UartPrintf(unsigned char *p);
extern void UartPrintASCII(unsigned char c);
extern void clrStruct();

extern char idata gpsRxBuffer[gpsRxBufferLength];
extern unsigned char RX_Count;
extern _SaveData Save_Data;

根据在串口调试助手或者官方调试助手中获取到的GPS读取到的数据我们可以得知有效数据(对于我们而言)

【嵌入式实战分享】模块篇 UBLOX-6M GPS_第10张图片

在本项目中,对于经纬度的经度没有较高的要求,在这里我是直接提取的字符进而进行显示,代码如下:

void Print_to_OLED()
{
	Hour = (Save_Data.GPS_Buffer[7]-0x30)*10+(Save_Data.GPS_Buffer[8]-0x30)+8;			
	Minute=(Save_Data.GPS_Buffer[9]-0x30)*10+(Save_Data.GPS_Buffer[10]-0x30);
	Second=(Save_Data.GPS_Buffer[11]-0x30)*10+(Save_Data.GPS_Buffer[12]-0x30);
	//UTC时间转换到北京时间		UTC+8		//0x30为ASCII转换为数字
	Latitude=(Save_Data.GPS_Buffer[19]-0x30)*10+(Save_Data.GPS_Buffer[20]-0x30);
	Longitude=(Save_Data.GPS_Buffer[32]-0x30)*100+(Save_Data.GPS_Buffer[33]-0x30)*10+(Save_Data.GPS_Buffer[34]-0x30);
}

2.应用编程

main.c部分代码如下(示例):


void errorLog(int num)
{
	while (1)
	{
        UartPrintf("ERROR");
		UartPrintASCII(num+0x30);
		UartPrintf("\r\n");
        //测试用
//		OLED_Clear();
//		OLED_ShowString(0,0,"ERROR",12);	 
//		OLED_ShowNum(50,2,num,1,12);
	}
}

errorLog函数就是打印数据错误的函数,就是一个循环,相当于我们平时访问网页时偶尔显示的“错误代码:404”。

void parseGpsBuffer()
{
	char *subString;
	char *subStringNext;
	char i = 0;
	if (Save_Data.isGetData)
	{	
		LED_GPS=LED_ON;// 得到数据,指示灯亮;闪烁代表正常
		Save_Data.isGetData = false;
        OLED_ShowString(60,1,"GPSok",8);	//测试用
		
		for (i = 0 ; i <= 6 ; i++)
		{
			if (i == 0)
			{
				if ((subString = strstr(Save_Data.GPS_Buffer, ",")) == NULL)
					errorLog(1);	//解析错误
			}
			else
			{
				subString++;
				if ((subStringNext = strstr(subString, ",")) != NULL)
				{
					char usefullBuffer[2]; 
					switch(i)
					{
						case 1:memcpy(Save_Data.UTCTime, subString, subStringNext - subString);break;	//获取UTC时间
						case 2:memcpy(usefullBuffer, subString, subStringNext - subString);break;	//获取UTC时间
						case 3:memcpy(Save_Data.latitude, subString, subStringNext - subString);break;	//获取纬度信息
						case 4:memcpy(Save_Data.N_S, subString, subStringNext - subString);break;	//获取N/S
						case 5:memcpy(Save_Data.longitude, subString, subStringNext - subString);break;	//获取经度信息
						case 6:memcpy(Save_Data.E_W, subString, subStringNext - subString);break;	//获取E/W

						default:break;
					}

					subString = subStringNext;
					Save_Data.isParseData = true;
					if(usefullBuffer[0] == 'A')
						Save_Data.isUsefull = true;
					else if(usefullBuffer[0] == 'V')
						Save_Data.isUsefull = false;

				}
				else
				{
					errorLog(2);	//解析错误
				}
			}
		}
	}
	else    //长灭,没有数据
    {
        LED_GPS=LED_OFF;
        OLED_ShowString(60,1,"noGPS",8);	//测试用
    }
}

parseGpsBuffer()函数时解析函数用来判断GPS是否获取到数据,在这里我引入了LED_GPS端口作为指示灯驱动端口和OLED函数用来调试,方便我根据现象进行开发。

同时这里面有一个函数memcpy(),需要引用stdio.h和string.h。memcpy()是一个在C语言中常用的内存拷贝函数,用于将一块内存区域的内容复制到另一块内存区域,在这里我们通过它将我们读取到的数据存放到RAM区中,用于存储GPS数据。

然后就是提取其中有用的信息,并进行格式转换,判断标准是根据它发送的packet数据来进行筛选
【packet图】


void printGpsBuffer()
{
	if (Save_Data.isParseData)
	{
		Save_Data.isParseData = false;
		UartPrintf(Save_Data.GPS_Buffer);
		Print_to_OLED();//格式转换
		OLED_ShowString(0,2,"Time=",8);	
		OLED_ShowNum(40,2,Hour,2,8);OLED_ShowString(56,2,":",8);
		OLED_ShowNum(64,2,Minute,2,8);OLED_ShowString(80,2,":",8);OLED_ShowNum(88,2,Second,2,8);
        
		if(Save_Data.isUsefull)
		{
			Save_Data.isUsefull = false;
			
			OLED_ShowString(0,3,"latitude=",8);	OLED_ShowNum(72,3,Latitude,2,8);//纬度
			OLED_ShowString(88,3,"'",8);OLED_ShowString(96,3,Save_Data.N_S,8);
			OLED_ShowString(0,4,"longitude=",8);	OLED_ShowNum(80,4,Longitude,3,8);	//经度
			OLED_ShowString(104,4,"'",8);OLED_ShowString(112,4,Save_Data.E_W,8);
		}
		else
		{
            UartPrintf("GPS DATA is not useful!\r\n");
			OLED_Clear();
            OLED_ShowString(0,0,"GPS not useful!",8);	 
		}
	}
}

打印数据的话,就因人而异了,上面是我用到的数据,在Oled上进行了打印,仅供参考。


使用总结

1.常见问题

在室内有时候连不上,即使连接了天线,最好还是到室外进行测试(但是很不方便,不过也没办法)

2.使用心得

  • 先从商家那里要一套例程,看看有没有问题,没有问题的话再进行开发
  • 一定要阅读官方手册(如果英文的话,请斟酌)
  • GPS有时候会读取不到数据,可能是信号问题,请再室外空旷位置进行调试

你可能感兴趣的:(模块介绍,经验分享,嵌入式硬件)