使用STM32读取Ublox GPS模块的UBX协议数据详解

使用模块为图中所示的GPS模块,内部集成UBLOX-M10芯片,可以采用NMEA和UBX协议,使用UBX协议可以输出东北天三个方位速度值,符合本项目要求,所以使用UBX协议输出数据。

模块上位机设置

虽然模块商家提供了输出UBX协议的C语言例程,但是比较繁琐,我们可以使用UBLOX官方上位机软件进行模块设置,从而省去代码中更繁琐的配置指令。

打开u-center软件进行配置

  1. 上位机配置这里网上参考教程比较多,不再浪费时间,提供一篇大家参考:https://blog.csdn.net/u014421313/article/details/126214774

  1. 这里要注意的一点是,我们使用UBX协议时往往只需要输出PVT这一帧数据就可以得到所有的数据,所以在上位机设置时设置为只输出UBX这一帧数据。

按F9打开Messages,找到图示的NAV。

找到NAV下方的PVT右击使能,当字体为黑色说明这一条数据开始输出,要注意如果想只输出这一条数据帧,就要确保没有使能其他语句,即保证其他语句都是灰色的。

硬件连接

使用stm32f103zet6,Ublox连接串口二,使用串口一进行串口助手查看数据。

串口部分代码

串口初始化

void uart_init(u32 bound)
{
    //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);    //使能USART1,GPIOA时钟

    //USART1_TX   GPIOA.9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    //复用推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9

    //USART1_RX      GPIOA.10初始化
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

    //Usart1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;        //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);    //根据指定的参数初始化VIC寄存器

    //USART 初始化设置

    USART_InitStructure.USART_BaudRate = bound;//串口波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
    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(USART1, &USART_InitStructure); //初始化串口1
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
  USART_Cmd(USART1, ENABLE);                    //使能串口1 

}
void uart2_init(u32 bound)
{
    //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);    //使能USART2,GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    //USART2_TX   GPIOA.2
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.9
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    //复用推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9

    //USART2_RX      GPIOA.3初始化
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA10
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

    //Usart2 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;        //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);    //根据指定的参数初始化VIC寄存器

    //USART 初始化设置

    USART_InitStructure.USART_BaudRate = bound;//串口波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
    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_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启串口接受中断
  USART_Cmd(USART2, ENABLE);                    //使能串口2

}

串口2中断接收部分

void USART2_IRQHandler(void)                    //串口2中断服务程序
{
    static uint8_t datacnt=0,state=0; //计数器,状态标志位
    u8 Res;
#if SYSTEM_SUPPORT_OS         //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    OSIntEnter();    
#endif
    if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收中断()
        {
            Res =USART_ReceiveData(USART2);    //读取接收到的数据
//            databuf[datacnt++]=Res;
            if(state==0) //验证帧头0xb5
            {
                if(Res==0xb5)
                {
                    databuf[datacnt++]=Res;
                    state=1;
                }
                else
                {
                    datacnt=0;
                    state=0;
                }
            }
            
            else if(state==1)  //验证帧头0x62
            {
                if(Res==0x62)
                {
                    databuf[datacnt++]=Res;
                    state=2;
                }
                else
                {
                    datacnt=0;
                    state=0;
                }                
            }

            else if(state==2) //验证class 0x01
            {
                if(Res==0x01)
                {
                    databuf[datacnt++]=Res;
                    state=3;
                }
                else
                {
                    datacnt=0;
                    state=0;
                }                
            }
            else if(state==3)  //验证ID 0x07
            {
                if(Res==0x07)
                {
                    databuf[datacnt++]=Res;
                    state=4;
                }
                else
                {
                    datacnt=0;
                    state=0;
                }                
            }
            else if(state==4) 
            {
                databuf[datacnt++]=Res;
                if(datacnt>=100)
                {
                    if((databuf[98]==checksum(databuf,96)>>8)&&(databuf[99]==(checksum(databuf,96)&0XFF)))
                    {
                        USART2_RX_STA=1;
                        datacnt=0;
                        state=0;
                    }
                 
                }
                                        
            }

    }     
#if SYSTEM_SUPPORT_OS     //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    OSIntExit();                                               
#endif
} 

在串口2接收中断中对数据进行判断,PVT语句有0xb5和0x62两个帧头,0x01为Class,0x07为id,加上数据一共100个字节。需预先定义一个状态标志位state,当判断到0xb5时,state为1,然后下一次接收判断0x62,以此类推。

此外UBX协议还有两个校验位,位于最后两位。手册中这两位的定义为:

所以需要我们编写一个判断校验位的函数

uint16_t checksum(uint8_t*data,uint8_t len)
{
    uint8_t* p=data+2;
    uint8_t j;
    uint32_t CK_a=0,CK_b=0,CK_A=0,CK_B=0;
    uint16_t sum;
    for(j=0;j<len;j++)
    {
        CK_a+=*p++;
        CK_b+=CK_a;
    }
    CK_A=CK_a&0xff;
    CK_B=CK_b&0xff;
    sum=CK_A<<8|CK_B;
    return sum;
}

这个函数把所得两个8位校验位合并为一个16位,在使用时进行移位运算即可得到两个校验位。通过判断校验位两个值使串口二标志位USART2_RX_STA=1,说明数据接收完成。

最后在主函数中写一下串口一把串口二接收的数据通过串口助手显示在屏幕上:

 int main(void)
 {        
     u16 t;  
    delay_init();             //延时函数初始化      
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    uart_init(115200);     //串口初始化为115200
    uart2_init(115200);
     LED_Init();                 //LED端口初始化
    KEY_Init();          //初始化与按键连接的硬件接口
     while(1)
    {
        LED1=0;
        if(USART2_RX_STA==1)
        {
            LED0=0;
            
            for(t=0;t<100;t++)
            {
                
                USART_SendData(USART1,databuf[t]);
                while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
            }
//            delay_ms(1000);
        }    

    }     
 }

如果需要提取数据中的经纬度、速度等信息,可以通过手册中对PVT语句的解析,使用相应的字节进行转换,这里要注意数据为小端形式。

接收的数据如下:

有需要解答的大家可以留言一下,第一次发博客,不太熟练。

物联沃分享整理
物联沃-IOTWORD物联网 » 使用STM32读取Ublox GPS模块的UBX协议数据详解

发表评论