stm32+neo6m GPS模块定位系统

开发平台:主处理器是意法半导体的stm32f103系列,这款芯片应用广泛,并且资料很多。

主要定位模块:ublox的一款芯片

开发环境:MDK5.0

主要代码还是参考原子的历程,在它的基础上实现了只提取经纬度,海拔等操作

硬件连接:3.3V电源给GPS模块供电,stm32串口2接GPS。

软件实现主要流程:stm32串口初始化,模块初始化,设置更新速率,保存配置,串口2接收消息,对接收到的消息进行字符串处理,提取有用的信息加以利用。

以下给出主要程序:

串口部分:

串口2初始化代码:

    void USART2_Init_JAVE( u32 bound )
    {
        
        GPIO_InitTypeDef  GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
        NVIC_InitTypeDef  NVIC_InitStructure;
        
        USART_DeInit(USART2);
        
        //使能串口3的时钟 和对应GPIO时钟
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
        
        
        //配置TX引脚GPIO
        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;
        GPIO_InitStructure.GPIO_Speed=GPIO_Speed_2MHz;
        GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
        GPIO_Init(GPIOA,&GPIO_InitStructure);
        
        //配置RX引脚GPIO
        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;
        GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
        GPIO_Init(GPIOA,&GPIO_InitStructure);
        
        //配置串口2
        USART_InitStructure.USART_BaudRate = bound;
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;
        USART_InitStructure.USART_StopBits = USART_StopBits_1;
        USART_InitStructure.USART_Parity = USART_Parity_No;
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
        USART_Init(USART2, &USART_InitStructure);
        
        //使能串口2
        USART_Cmd(USART2,ENABLE);
        
        USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);

        //配置串口2接收中断

        NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
        
        USART_ClearFlag(USART2, USART_FLAG_RXNE);
        
        USART_GetFlagStatus(USART2,USART_FLAG_TC);     /* 先读SR,再写DR */
        
    }


串口2中断函数:

    void RX2_Handler( void )
    {
        char temp = 0;
			#ifdef OS_TICKS_PER_SEC	 	//如果时钟节拍数定义了,说明要使用ucosII了.
				OSIntEnter();    
			#endif		
        
        if(USART_GetITStatus(USART2, USART_IT_RXNE) == SET)
        {

            temp = USART_ReceiveData( USART2 );     /* 读取USART2数据,自动清零标志位 RXNE */
        
            if( RX2_Point <= 1999)
            {
                
                RX2_Temp[RX2_Point] = temp;
                
                RX2_Point++;
                
            }
						else	RX2_Point=0;
            
        }
				
			#ifdef OS_TICKS_PER_SEC	 	//如果时钟节拍数定义了,说明要使用ucosII了.
			OSIntExit();  											 
			#endif  
        
    }


串口2接收扫描:

unsigned short USART2_Scan(u16 *len)
{
		unsigned short int ftemp = 0;
		ftemp = RX2_Point;
		*len=0;
		if( ftemp != 0 )
		{
			delay_ms(100);
			while ( ftemp != RX2_Point )
			{
				 ftemp = RX2_Point;
				 delay_ms(100);
			}
			RX2_Point = 0;      /* 重置指针 */														
			*len= ftemp;
			return 1;   /* 扫描到数据,返回1 */				
		}
		return 0;
}


GPS驱动部分:

设置更新速率代码:

//配置UBLOX NEO-6的更新速率	    
//measrate:测量时间间隔,单位为ms,最少不能小于200ms(5Hz)
//reftime:参考时间,0=UTC Time;1=GPS Time(一般设置为1)
//返回值:0,发送成功;其他,发送失败.
unsigned char Ublox_Cfg_Rate(unsigned short measrate,unsigned char reftime)
{
	_ublox_cfg_rate *cfg_rate=(_ublox_cfg_rate *)USART2_TX_BUF;
 	if(measrate<200)return 1;	//小于200ms,直接退出
 	cfg_rate->header=0X62B5;	//cfg header
	cfg_rate->id=0X0806;	 	//cfg rate id
	cfg_rate->dlength=6;	 	//数据区长度为6个字节.
	cfg_rate->measrate=measrate;//脉冲间隔,ms
	cfg_rate->navrate=1;		//导航速率(周期),固定为1
	cfg_rate->timeref=reftime; 	//参考时间为GPS时间
	Ublox_CheckSum((unsigned char*)(&cfg_rate->id),sizeof(_ublox_cfg_rate)-4,&cfg_rate->cka,&cfg_rate->ckb);
//	while(DMA1_Channel7->CNDTR!=0);	//等待通道7传输完成   
//	UART_DMA_Enable(DMA1_Channel7,sizeof(_ublox_cfg_rate));//通过dma发送出去
	Ublox_Send_Date((unsigned char*)cfg_rate,sizeof(_ublox_cfg_rate));//发送数据给NEO-6M 
	return Ublox_Cfg_Ack_Check();
}



GPS初始化:

unsigned char GPS_Init(void)
{
	unsigned char key = 0xFF,cnt=0;	//保存配置成功的标志,成功时返回的值是0;
	MyPrintf("GPS 初始化\r\n");
	Ublox_Cfg_Prt(38400);			//重新设置模块的波特率为38400	 		

	while((Ublox_Cfg_Rate(2000,1)!=0)&&key)	//持续判断,直到可以检查到NEO-6M,且数据保存成功
	{	MyPrintf("2");
		USART2_Init_JAVE( 38400 );			//初始化串口2波特率为9600(EEPROM没有保存数据的时候,波特率为9600.)
		Ublox_Cfg_Prt(38400);			//重新设置模块的波特率为38400	 				
		if(++cnt>=5) return 1;		//错误
		key=Ublox_Cfg_Cfg_Save();		//保存配置,配置成功,返回0  
		delay_ms(50);
	}	  			

	 return 0;
}


算法处理部分:(字符串处理)

对于接收到的消息进行提取处理:

//分析GPGGA信息
//gpsx:nmea信息结构体
//buf:接收到的GPS数据缓冲区首地址
void NMEA_GPGGA_Analysis(GPS_PacketTypeDef *GPS_Packet,u8 *buf)
{	
	unsigned char *p1,dx,posx;
	unsigned int temp;	
	float rs;
	p1=(unsigned char *)strstr((const char *)buf,"$GPGGA");  //此处可改为"$GPGGA"
	
	posx=NMEA_Comma_Pos(p1,9);								//得到海拔高度
	if(posx!=0XFF){
		GPS_Packet->altitude=NMEA_Str2num(p1+posx,&dx);  
		MyPrintf("altitude=%d\r\n",GPS_Packet->altitude);
	}
	posx=NMEA_Comma_Pos(p1,2);								//得到纬度
	if(posx!=0XFF)
	{
		temp=NMEA_Str2num(p1+posx,&dx);		 	 
		GPS_Packet->latitude=temp/NMEA_Pow(10,dx+2);	//得到°
		rs=temp%NMEA_Pow(10,dx+2);				//得到'		 
		GPS_Packet->latitude=GPS_Packet->latitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为° 
		
		MyPrintf("latitude=%d\r\n",GPS_Packet->latitude);
	}
			 
 	posx=NMEA_Comma_Pos(p1,4);								//得到经度
	if(posx!=0XFF)
	{												  
		temp=NMEA_Str2num(p1+posx,&dx);		 	 
		GPS_Packet->longitude=temp/NMEA_Pow(10,dx+2);	//得到°
		rs=temp%NMEA_Pow(10,dx+2);				//得到'		 
		GPS_Packet->longitude=GPS_Packet->longitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为° 
		
		MyPrintf("longitude=%d\r\n",GPS_Packet->longitude);
	}
	
	posx=NMEA_Comma_Pos(p1,6);								//定位状态
	if(posx!=0XFF){
		GPS_Packet->status=*(p1+posx)-'0';
		MyPrintf("status=%d\r\n",GPS_Packet->status);
	}
}


GPS测试主程序:

 unsigned char GPS_Run(void)
 {
	 unsigned char flag=0;									//定位信息成功标志
	 unsigned short rxlen,i;							//rxlen:数据长度
	 unsigned char cnt=0;
	 
	 while(1)
	 { 		
		 if(cnt>2) {sign_run=1;return 0;}
		 cnt++;		
		 rxlen=0;											
		 USART2_Scan(&rxlen);
		 if(rxlen !=0)
		 {
				for(i=0;i读取数据存入MSG数据包并退出
				{		
					MyPrintf("GPS success\r\n");
					GPS_Show( );
					Task_LED( );
					return 1;
				}
		 }
		 delay_ms(50);
	 }
 }



由于位置信息还会受到卫星信号强弱的影响,因此从GPS模块里面接收到的信息不一定就是有效的位置信息。如果定位失败,模块会返回类似这样的信息

$GPRMC,,V,,,,,,,,,,N*53
$GPVTG,,,,,,,,,N*30
$GPGGA,,,,,,0,00,99.99,,,,,,*48
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
$GPGSV,1,1,01,19,,,35*76
$GPGLL,,,,,,V,N*64
可以简单地认为,只要¥GPGLL这一行数据里含有“V”字符,那么这一次数据就是无效的,应当舍弃,等待下一次正确的信号输出。

如果卫星信号足够强,定位数据有效,那么模块会返回类似这样的信息

$GPRMC,031851.00,A,2309.86780,N,11325.74067,E,0.683,,060916,,,A*74
$GPVTG,,T,,M,0.683,N,1.265,K,A*2E
$GPGGA,031851.00,2309.86780,N,11325.74067,E,1,04,5.43,19.1,M,-5.1,M,,*7B
$GPGSA,A,3,28,17,19,01,,,,,,,,,6.77,5.43,4.04*03
$GPGSV,3,1,09,01,24,037,20,02,01,233,,03,44,084,29,06,35,242,*72
$GPGSV,3,2,09,08,01,087,,11,14,051,,17,40,326,30,19,26,302,29*76
$GPGSV,3,3,09,28,75,000,31*4A
$GPGLL,2309.86798,N,11325.74064,E,031852.00,A,A*61


下面给出模块驱动部分的全部源码:

C文件:

//GPS校验和计算
//buf:数据缓存区首地址
//len:数据长度
//cka,ckb:两个校验结果.
void Ublox_CheckSum(unsigned char *buf,unsigned short len,unsigned char* cka,unsigned char*ckb)
{
	unsigned short i;
	*cka=0;*ckb=0;
	for(i=0;iheader=0X62B5;		//cfg header
	cfg_prt->id=0X0006;			//cfg prt id
	cfg_prt->dlength=20;		//数据区长度为20个字节.	
	cfg_prt->portid=1;			//操作串口1
	cfg_prt->reserved=0;	 	//保留字节,设置为0
	cfg_prt->txready=0;	 		//TX Ready设置为0
	cfg_prt->mode=0X08D0; 		//8位,1个停止位,无校验位
	cfg_prt->baudrate=baudrate; //波特率设置
	cfg_prt->inprotomask=0X0007;//0+1+2
	cfg_prt->outprotomask=0X0007;//0+1+2
 	cfg_prt->reserved4=0; 		//保留字节,设置为0
 	cfg_prt->reserved5=0; 		//保留字节,设置为0 
	Ublox_CheckSum((unsigned char*)(&cfg_prt->id),sizeof(_ublox_cfg_prt)-4,&cfg_prt->cka,&cfg_prt->ckb);
//	while(DMA1_Channel7->CNDTR!=0);	//等待通道7传输完成   
//	UART_DMA_Enable(DMA1_Channel7,sizeof(_ublox_cfg_prt));	//通过dma发送出去
	Ublox_Send_Date((unsigned char*)cfg_prt,sizeof(_ublox_cfg_prt));//发送数据给NEO-6M   
	delay_ms(200);				//等待发送完成 
	USART2_Init_JAVE(baudrate);	//重新初始化串口2   
	return Ublox_Cfg_Ack_Check();//这里不会反回0,因为UBLOX发回来的应答在串口重新初始化的时候已经被丢弃了.
} 

//配置保存
//将当前配置保存在外部EEPROM里面
//返回值:0,执行成功;1,执行失败.
unsigned char Ublox_Cfg_Cfg_Save(void)
{
	unsigned char i;
	_ublox_cfg_cfg *cfg_cfg=(_ublox_cfg_cfg *)USART2_TX_BUF;
	cfg_cfg->header=0X62B5;		//cfg header
	cfg_cfg->id=0X0906;			//cfg cfg id
	cfg_cfg->dlength=13;		//数据区长度为13个字节.		 
	cfg_cfg->clearmask=0;		//清除掩码为0
	cfg_cfg->savemask=0XFFFF; 	//保存掩码为0XFFFF
	cfg_cfg->loadmask=0; 		//加载掩码为0 
	cfg_cfg->devicemask=4; 		//保存在EEPROM里面		 
	Ublox_CheckSum((unsigned char*)(&cfg_cfg->id),sizeof(_ublox_cfg_cfg)-4,&cfg_cfg->cka,&cfg_cfg->ckb);
//	while(DMA1_Channel7->CNDTR!=0);	//等待通道7传输完成   
//	UART_DMA_Enable(DMA1_Channel7,sizeof(_ublox_cfg_cfg));	//通过dma发送出去
	Ublox_Send_Date((unsigned char*)cfg_cfg,sizeof(_ublox_cfg_cfg));//发送数据给NEO-6M 
	for(i=0;i<6;i++)if(Ublox_Cfg_Ack_Check()==0)break;		//EEPROM写入需要比较久时间,所以连续判断多次
	return i==6?1:0;
}

//配置UBLOX NEO-6的更新速率	    
//measrate:测量时间间隔,单位为ms,最少不能小于200ms(5Hz)
//reftime:参考时间,0=UTC Time;1=GPS Time(一般设置为1)
//返回值:0,发送成功;其他,发送失败.
unsigned char Ublox_Cfg_Rate(unsigned short measrate,unsigned char reftime)
{
	_ublox_cfg_rate *cfg_rate=(_ublox_cfg_rate *)USART2_TX_BUF;
 	if(measrate<200)return 1;	//小于200ms,直接退出
 	cfg_rate->header=0X62B5;	//cfg header
	cfg_rate->id=0X0806;	 	//cfg rate id
	cfg_rate->dlength=6;	 	//数据区长度为6个字节.
	cfg_rate->measrate=measrate;//脉冲间隔,ms
	cfg_rate->navrate=1;		//导航速率(周期),固定为1
	cfg_rate->timeref=reftime; 	//参考时间为GPS时间
	Ublox_CheckSum((unsigned char*)(&cfg_rate->id),sizeof(_ublox_cfg_rate)-4,&cfg_rate->cka,&cfg_rate->ckb);
//	while(DMA1_Channel7->CNDTR!=0);	//等待通道7传输完成   
//	UART_DMA_Enable(DMA1_Channel7,sizeof(_ublox_cfg_rate));//通过dma发送出去
	Ublox_Send_Date((unsigned char*)cfg_rate,sizeof(_ublox_cfg_rate));//发送数据给NEO-6M 
	return Ublox_Cfg_Ack_Check();
}



//从buf里面得到第cx个逗号所在的位置
//返回值:0~0XFE,代表逗号所在位置的偏移.
//       0XFF,代表不存在第cx个逗号	
unsigned char NMEA_Comma_Pos(unsigned char *buf,unsigned char cx)
{	 		    
	unsigned char *p=buf;
	while(cx)
	{		 
		if(*buf=='*'||*buf<' '||*buf>'z')return 0XFF;//遇到'*'或者非法字符,则不存在第cx个逗号
		if(*buf==',')cx--;
		buf++;
	}
	return buf-p;	 
}

//m^n函数
//返回值:m^n次方.
unsigned int NMEA_Pow(unsigned char m,unsigned char n)
{
	unsigned int result=1;	 
	while(n--)
		result*=m;    
	return result;
}

//str转换为数字,以','或者'*'结束
//buf:数字存储区
//dx:小数点位数,返回给调用函数
//返回值:转换后的数值
int NMEA_Str2num(unsigned char *buf,unsigned char*dx)
{
	unsigned char *p=buf;
	unsigned int ires=0,fres=0;
	unsigned char ilen=0,flen=0,i;
	unsigned char mask=0;
	int res;
	while(1) //得到整数和小数的长度
	{
		if(*p=='-'){mask|=0X02;p++;}//是负数
		if(*p==','||(*p=='*'))break;//遇到结束了
		if(*p=='.'){mask|=0X01;p++;}//遇到小数点了
		else if(*p>'9'||(*p<'0'))	//有非法字符
		{	
			ilen=0;
			flen=0;
			break;
		}	
		if(mask&0X01)flen++;
		else ilen++;
		p++;
	}
	if(mask&0X02)buf++;	//去掉负号
	for(i=0;i5)flen=5;	//最多取5位小数
	*dx=flen;	 		//小数点位数
	for(i=0;ialtitude=NMEA_Str2num(p1+posx,&dx);  
		MyPrintf("altitude=%d\r\n",GPS_Packet->altitude);
	}
	posx=NMEA_Comma_Pos(p1,2);								//得到纬度
	if(posx!=0XFF)
	{
		temp=NMEA_Str2num(p1+posx,&dx);		 	 
		GPS_Packet->latitude=temp/NMEA_Pow(10,dx+2);	//得到°
		rs=temp%NMEA_Pow(10,dx+2);				//得到'		 
		GPS_Packet->latitude=GPS_Packet->latitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为° 
		
		MyPrintf("latitude=%d\r\n",GPS_Packet->latitude);
	}
			 
 	posx=NMEA_Comma_Pos(p1,4);								//得到经度
	if(posx!=0XFF)
	{												  
		temp=NMEA_Str2num(p1+posx,&dx);		 	 
		GPS_Packet->longitude=temp/NMEA_Pow(10,dx+2);	//得到°
		rs=temp%NMEA_Pow(10,dx+2);				//得到'		 
		GPS_Packet->longitude=GPS_Packet->longitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为° 
		
		MyPrintf("longitude=%d\r\n",GPS_Packet->longitude);
	}
	
	posx=NMEA_Comma_Pos(p1,6);								//定位状态
	if(posx!=0XFF){
		GPS_Packet->status=*(p1+posx)-'0';
		MyPrintf("status=%d\r\n",GPS_Packet->status);
	}
}

void Ublox_Send_Date(unsigned char* dbuf,unsigned short len)
{
	unsigned short j;
	for(j=0;jSR&0X40)==0);//循环发送,直到发送完毕   
		USART2->DR=dbuf[j];  
	}	
}


u8 GPS_Check(u8 *buff)
{
	u8 *p_start,*p_process;
	//,*p_end;
//	u8 temp[500];
	u8 res=0;
//	u16 cnt=0,i=0;
//	MyPrintf("检查GPS数据\r\n");
	p_start =(u8*)strstr((const char*)buff,"$GPRMC");
	if(p_start!=NULL)				//找不到地理位置信息,返回无效
	{
			if(strstr((const char*)p_start,"$GPVTG")!=NULL && strstr((const char*)p_start,"$GPGGA")!=NULL)
				
					if(strstr((const char*)p_start,"$GPGSA")!=NULL && strstr((const char*)p_start,"$GPGSV")!=NULL)
						
						if((p_process=(u8*)strstr((const char*)p_start,"$GPGLL"))!=NULL)
						{	
//								MyPrintf("检查无效字符\r\n");
								if((strstr((const char*)p_process,"V"))!=NULL)	res=0xFF;
						}
	}else	res=1;					
	
//	MyPrintf("GPS Check Result=%d\r\n",res);
	return res;										//返回无效定位数据
}


H文件:

#ifndef _NEO_6M_
			#define _NEO_6M_

			#include "global.h"
			
			//UBLOX NEO-6M 配置(清除,保存,加载等)结构体
			__packed typedef struct  
			{										    
				unsigned short header;					//cfg header,固定为0X62B5(小端模式)
				unsigned short id;						//CFG CFG ID:0X0906 (小端模式)
				unsigned short dlength;				//数据长度 12/13
				unsigned int clearmask;				//子区域清除掩码(1有效)
				unsigned int savemask;				//子区域保存掩码
				unsigned int loadmask;				//子区域加载掩码
				unsigned char devicemask; 		  	//目标器件选择掩码	b0:BK RAM;b1:FLASH;b2,EEPROM;b4,SPI FLASH
				unsigned char  cka;		 			//校验CK_A 							 	 
				unsigned char  ckb;			 		//校验CK_B							 	 
			}_ublox_cfg_cfg; 
			
			//UBLOX NEO-6M UART端口设置结构体
			__packed typedef struct  
			{										    
				unsigned short header;					//cfg header,固定为0X62B5(小端模式)
				unsigned short id;						//CFG PRT ID:0X0006 (小端模式)
				unsigned short dlength;				//数据长度 20
				unsigned char  portid;					//端口号,0=IIC;1=UART1;2=UART2;3=USB;4=SPI;
				unsigned char  reserved;				//保留,设置为0
				unsigned short txready;				//TX Ready引脚设置,默认为0
				unsigned int mode;					//串口工作模式设置,奇偶校验,停止位,字节长度等的设置.
				unsigned int baudrate;				//波特率设置
				unsigned short inprotomask;		 	//输入协议激活屏蔽位  默认设置为0X07 0X00即可.
				unsigned short outprotomask;		 	//输出协议激活屏蔽位  默认设置为0X07 0X00即可.
				unsigned short reserved4; 				//保留,设置为0
				unsigned short reserved5; 				//保留,设置为0 
				unsigned char  cka;			 		//校验CK_A 							 	 
				unsigned char  ckb;			    	//校验CK_B							 	 
			}_ublox_cfg_prt; 
			
			//UBLOX NEO-6M 刷新速率配置结构体
			__packed typedef struct  
			{										    
				unsigned short header;					//cfg header,固定为0X62B5(小端模式)
				unsigned short id;						//CFG RATE ID:0X0806 (小端模式)
				unsigned short dlength;				//数据长度
				unsigned short measrate;				//测量时间间隔,单位为ms,最少不能小于200ms(5Hz)
				unsigned short navrate;				//导航速率(周期),固定为1
				unsigned short timeref;				//参考时间:0=UTC Time;1=GPS Time;
				unsigned char  cka;					//校验CK_A 							 	 
				unsigned char  ckb;					//校验CK_B							 	 
			}_ublox_cfg_rate; 			
			
			unsigned char Ublox_Cfg_Prt(unsigned int baudrate);
			unsigned char Ublox_Cfg_Cfg_Save(void);
			void Ublox_Send_Date(unsigned char* dbuf,unsigned short len);
			void NMEA_GPGGA_Analysis(GPS_PacketTypeDef *GPS_Packet,unsigned char *buf);
			unsigned char Ublox_Cfg_Rate(unsigned short measrate,unsigned char reftime);
			u8 GPS_Check(u8 *buff);
#endif

			
			



你可能感兴趣的:(STM32)