开发平台:主处理器是意法半导体的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();
}
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);
}
}
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