[开发工具] STM32CubeMX-23 | 使用USART接收GPS数据并解析(L80-R)

[复制链接]
2921|45
 楼主| characteristic 发表于 2020-4-14 14:05 | 显示全部楼层
转发GPS模块的数据
GPS 使能后不断的接收信号定位,并输出数据,但是 GPS 模块与 USART3 连接,无法直接查看输出的数据,何谈解析,所以先将 USART3 接收到的数据使用 USART1 发送,在电脑上使用串口助手查看,如果对于USART的中断接收方式还不明白,可以查看这篇**:
        【STM32Cube_07】使用USART发送和接收数据(中断模式)。
         https://bbs.21ic.com/icview-2938322-1-1.html
 楼主| characteristic 发表于 2020-4-14 14:05 | 显示全部楼层
首先在 main.c 中实现 USART3 接收中断的回调函数:

  1. /* USER CODE BEGIN 4 */
  2. /* 中断回调函数 */
  3. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
  4. {
  5.     static uint8_t ch;
  6.     /* 判断是哪个串口触发的中断 */
  7.     if(huart ->Instance == USART3)
  8.     {
  9.         //将接收到的数据发送
  10.         HAL_UART_Transmit(&huart1, &ch, 1, 0Xffff);
  11.         //重新使能串口接收中断
  12.         HAL_UART_Receive_IT(&huart3, &ch, 1);
  13.     }
  14. }
  15. /* USER CODE END 4 */
 楼主| characteristic 发表于 2020-4-14 14:06 | 显示全部楼层
然后修改main函数如下:

  1. int main(void)
  2. {
  3.     HAL_Init();

  4.     SystemClock_Config();

  5.     /* Initialize all configured peripherals */
  6.     MX_GPIO_Init();
  7.     MX_USART1_UART_Init();
  8.     MX_USART3_UART_Init();
  9.     /* USER CODE BEGIN 2 */

  10.     /* 使能USART3接收中断 */
  11.     HAL_UART_Receive_IT(&huart3, (uint8_t*)gps_uart, 1);

  12.     /* 使能GPS模块 */
  13.     printf("L80-R GPS Module test...\r\n");
  14.     HAL_GPIO_WritePin(GPS_EN_GPIO_Port,GPS_EN_Pin, GPIO_PIN_RESET);
  15.     printf("GPE Enable ok.\r\n");

  16.   /* USER CODE END 2 */

  17.   /* Infinite loop */
  18.   /* USER CODE BEGIN WHILE */
  19.   while (1)
  20.   {
  21.     /* USER CODE END WHILE */

  22.     /* USER CODE BEGIN 3 */
  23.   }
  24.   /* USER CODE END 3 */
  25. }
 楼主| characteristic 发表于 2020-4-14 14:06 | 显示全部楼层
编译下载,可以在串口助手上看到输出的数据:

刚上电时蓝色的LED灯保持常亮状态,表示未定位成功,数据如下:

530195e9552f874f73.png
 楼主| characteristic 发表于 2020-4-14 14:07 | 显示全部楼层
定位成功后蓝色的LED开始闪烁,数据如下:

668925e95531ee72fe.png
 楼主| characteristic 发表于 2020-4-14 14:08 | 显示全部楼层
解析GPS的数据
将GPS数据转发取消,删除USART3添加的中断函数:

23075e95533e39c33.png
 楼主| characteristic 发表于 2020-4-14 14:08 | 显示全部楼层
接下来开辟一块缓冲区,来存放接收的GPS数据,并且定义GPS数据结构体,用来存放解析GPS数据得到的经纬度信息:

  1. /* Private variables ---------------------------------------------------------*/
  2. /* USER CODE BEGIN PV */
  3. /***************************************************\
  4. *GPS NMEA-0183协议重要参数结构体定义
  5. *卫星信息
  6. \***************************************************/
  7. __packed typedef struct
  8. {
  9.     uint32_t latitude_bd;                    //纬度   分扩大100000倍,实际要除以100000
  10.     uint8_t nshemi_bd;                        //北纬/南纬,N:北纬;S:南纬   
  11.     uint32_t longitude_bd;              //经度 分扩大100000倍,实际要除以100000
  12.     uint8_t ewhemi_bd;                        //东经/西经,E:东经;W:西经
  13. }gps_msg;

  14. /* E53_ST1传感器数据类型定义 ------------------------------------------------------------*/
  15. typedef struct
  16. {
  17.         float    Longitude;                //经度
  18.         float    Latitude;        //纬度
  19. } E53_ST1_Data_TypeDef;

  20. gps_msg              gpsmsg;
  21. static unsigned char gps_uart[1000];
  22. E53_ST1_Data_TypeDef E53_ST1_Data;

  23. /* USER CODE END PV */
 楼主| characteristic 发表于 2020-4-14 14:09 | 显示全部楼层
然后编写解析GPS数据的函数,先要在开头加上头文件支持:

  1. /* Private includes ----------------------------------------------------------*/
  2. /* USER CODE BEGIN Includes */
  3. #include <stdio.h>
  4. #include <string.h>

  5. /* USER CODE END Includes */
 楼主| characteristic 发表于 2020-4-14 14:09 | 显示全部楼层
开始编写解析GPS数据所使用的函数:

  1. /* USER CODE BEGIN 4 */

  2. /***************************************************\
  3. * 函数名称: NMEA_Comma_Pos
  4. *    函数功能:从buf里面得到第cx个逗号所在的位置
  5. *    输入值:
  6. *    返回值:0~0xFE,代表逗号所在位置的便宜
  7. *                     0xFF,代表不存在第cx个逗号
  8. \***************************************************/

  9. uint8_t NMEA_Comma_Pos(uint8_t *buf,uint8_t cx)
  10. {
  11.     uint8_t *p = buf;
  12.     while(cx)
  13.     {
  14.         if(*buf=='*'||*buf<' '||*buf>'z')return 0xFF;
  15.         if(*buf==',')cx--;
  16.         buf++;
  17.     }
  18.     return buf-p;
  19. }
 楼主| characteristic 发表于 2020-4-14 14:10 | 显示全部楼层
  1. /***************************************************\
  2. * 函数名称: NMEA_Pow
  3. *    函数功能:返回m的n次方值
  4. *    输入值:底数m和指数n
  5. *    返回值:m^n
  6. \***************************************************/
  7. uint32_t NMEA_Pow(uint8_t m,uint8_t n)
  8. {
  9.     uint32_t result = 1;
  10.     while(n--)result *= m;
  11.     return result;
  12. }
 楼主| characteristic 发表于 2020-4-14 14:10 | 显示全部楼层
  1. /***************************************************\
  2. * 函数名称: NMEA_Str2num
  3. *    函数功能:str数字转换为(int)数字,以','或者'*'结束
  4. *    输入值:buf,数字存储区
  5. *                     dx,小数点位数,返回给调用函数
  6. *    返回值:转换后的数值
  7. \***************************************************/
  8. int NMEA_Str2num(uint8_t *buf,uint8_t*dx)
  9. {
  10.     uint8_t *p = buf;
  11.     uint32_t ires = 0,fres = 0;
  12.     uint8_t ilen = 0,flen = 0,i;
  13.     uint8_t mask = 0;
  14.     int res;
  15.     while(1)
  16.     {
  17.         if(*p=='-'){mask |= 0x02;p++;}//说明有负数
  18.         if(*p==','||*p=='*')break;//遇到结束符
  19.         if(*p=='.'){mask |= 0x01;p++;}//遇到小数点
  20.         else if(*p>'9'||(*p<'0'))//数字不在0和9之内,说明有非法字符
  21.         {
  22.             ilen = 0;
  23.             flen = 0;
  24.             break;
  25.         }
  26.         if(mask&0x01)flen++;//小数点的位数
  27.         else ilen++;//str长度加一
  28.         p++;//下一个字符
  29.     }
  30.     if(mask&0x02)buf++;//移到下一位,除去负号
  31.     for(i=0;i<ilen;i++)//得到整数部分数据
  32.     {
  33.         ires += NMEA_Pow(10,ilen-1-i)*(buf[i]-'0');
  34.     }
  35.     if(flen>5)flen=5;//最多取五位小数
  36.     *dx = flen;
  37.     for(i=0;i<flen;i++)//得到小数部分数据
  38.     {
  39.         fres +=NMEA_Pow(10,flen-1-i)*(buf[ilen+1+i]-'0');
  40.     }
  41.     res = ires*NMEA_Pow(10,flen)+fres;
  42.     if(mask&0x02)res = -res;
  43.     return res;
  44. }
 楼主| characteristic 发表于 2020-4-14 14:11 | 显示全部楼层
  1. /***************************************************\
  2. * 函数名称: NMEA_BDS_GPRMC_Analysis
  3. *    函数功能:解析GPRMC信息
  4. *    输入值:gpsx,NMEA信息结构体
  5. *                 buf:接收到的GPS数据缓冲区首地址
  6. \***************************************************/
  7. void NMEA_BDS_GPRMC_Analysis(gps_msg *gpsmsg,uint8_t *buf)
  8. {
  9.     uint8_t *p4,dx;            
  10.     uint8_t posx;     
  11.     uint32_t temp;      
  12.     float rs;  
  13.     p4=(uint8_t*)strstr((const char *)buf,"$GPRMC");//"$GPRMC",经常有&和GPRMC分开的情况,故只判断GPRMC.
  14.     posx=NMEA_Comma_Pos(p4,3);                                //得到纬度
  15.     if(posx!=0XFF)
  16.     {
  17.         temp=NMEA_Str2num(p4+posx,&dx);              
  18.         gpsmsg->latitude_bd=temp/NMEA_Pow(10,dx+2);    //得到°
  19.         rs=temp%NMEA_Pow(10,dx+2);                //得到'         
  20.         gpsmsg->latitude_bd=gpsmsg->latitude_bd*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为°
  21.     }
  22.     posx=NMEA_Comma_Pos(p4,4);                                //南纬还是北纬
  23.     if(posx!=0XFF)gpsmsg->nshemi_bd=*(p4+posx);                     
  24.      posx=NMEA_Comma_Pos(p4,5);                                //得到经度
  25.     if(posx!=0XFF)
  26.     {                                                  
  27.         temp=NMEA_Str2num(p4+posx,&dx);              
  28.         gpsmsg->longitude_bd=temp/NMEA_Pow(10,dx+2);    //得到°
  29.         rs=temp%NMEA_Pow(10,dx+2);                //得到'         
  30.         gpsmsg->longitude_bd=gpsmsg->longitude_bd*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为°
  31.     }
  32.     posx=NMEA_Comma_Pos(p4,6);                                //东经还是西经
  33.     if(posx!=0XFF)gpsmsg->ewhemi_bd=*(p4+posx);         
  34. }
  35. /* USER CODE END 4 */
 楼主| characteristic 发表于 2020-4-14 14:11 | 显示全部楼层
最后编写使用中断方式接收数据到缓冲区,然后调用GPS数据解析函数的函数:

  1. void E53_ST1_Read_Data(void)
  2. {   
  3.   /* 使用中断方式接收一次数据 */
  4.     HAL_UART_Receive_IT(&huart3,gps_uart,1000);

  5.   /* 分析缓冲区的字符串,解析GPS数据 */
  6.     NMEA_BDS_GPRMC_Analysis(&gpsmsg,(uint8_t*)gps_uart);

  7.   /* 将解析到的经纬度数据存放到结构体中,便于其他函数使用 */   
  8.     E53_ST1_Data.Longitude=(float)((float)gpsmsg.longitude_bd/100000);   
  9.     E53_ST1_Data.Latitude=(float)((float)gpsmsg.latitude_bd/100000);   
  10. }
 楼主| characteristic 发表于 2020-4-14 14:12 | 显示全部楼层
当然,别忘了在 main 函数之前声明这些函数:

  1. /* Private user code ---------------------------------------------------------*/
  2. /* USER CODE BEGIN 0 */
  3. uint8_t NMEA_Comma_Pos(uint8_t *buf,uint8_t cx);
  4. uint32_t NMEA_Pow(uint8_t m,uint8_t n);
  5. int NMEA_Str2num(uint8_t *buf,uint8_t*dx);
  6. void NMEA_BDS_GPRMC_Analysis(gps_msg *gpsmsg,uint8_t *buf);
  7. void E53_ST1_Read_Data(void);
  8. /* USER CODE END 0 */
 楼主| characteristic 发表于 2020-4-14 14:12 | 显示全部楼层
大功告成,在main函数中调用:

  1. int main(void)
  2. {
  3.   HAL_Init();

  4.   SystemClock_Config();

  5.   /* Initialize all configured peripherals */
  6.   MX_GPIO_Init();
  7.   MX_USART1_UART_Init();
  8.   MX_USART3_UART_Init();

  9.   /* USER CODE BEGIN 2 */

  10.   HAL_UART_Receive_IT(&huart3, (uint8_t*)gps_uart, 1);
  11.   printf("L80-R GPS Module test...\r\n");

  12.   /* 使能GPS模块 */
  13.   HAL_GPIO_WritePin(GPS_EN_GPIO_Port,GPS_EN_Pin, GPIO_PIN_RESET);
  14.   printf("GPE Enable ok.\r\n");

  15.   /* USER CODE END 2 */

  16.   /* Infinite loop */
  17.   /* USER CODE BEGIN WHILE */
  18.   while (1)
  19.   {
  20.     /* USER CODE END WHILE */

  21.     /* USER CODE BEGIN 3 */
  22.     E53_ST1_Read_Data();
  23.     printf("Longitude: %f, Latitude: %f.\r\n", E53_ST1_Data.Longitude, E53_ST1_Data.Latitude);
  24.     HAL_Delay(2000);
  25.   }
  26.   /* USER CODE END 3 */
  27. }
 楼主| characteristic 发表于 2020-4-14 14:13 | 显示全部楼层
实验现象
编译下载后,尽量将小熊派开发板放在窗户边,等待定位成功,串口输出数据如下:

563925e955479c08b9.png
 楼主| characteristic 发表于 2020-4-14 14:13 | 显示全部楼层
定位成功后,定位数据如下:

377765e95549ecc202.png
 楼主| characteristic 发表于 2020-4-14 14:14 | 显示全部楼层
查看定位
有了GPS定位数据之后,可以在地图上查看具体的位置,也可以上传到华为云IoT平台查看,这里我使用 GPS经纬度查询工具进行查看:

      工具链接:http://www.gpsspg.com/maps.htm
263415e9554bdf32fe.png
 楼主| characteristic 发表于 2020-4-14 14:14 | 显示全部楼层
至此,我们已经学会如何接收GPS数据并解析出经纬度。
观海 发表于 2020-5-7 18:03 | 显示全部楼层
非常感谢楼主分享
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部