【STM32Cube_23】使用USART接收GPS数据并解析(L80-R)

发布者:ularof不加糖最新更新时间:2021-07-20 来源: eefocus关键字:STM32Cube  USART  GPS数据 手机看文章 扫描二维码
随时随地手机看文章

本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的 USART 外设,接收 GPS 模块的数据并解析。

1. 准备工作

硬件准备

  • 开发板
    首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):

  • GPS模块(L80-R)
    Quectel L80-R 是一款集成了贴片天线的紧凑型GPS模块,非常适合在物联网设备中使用,尤其适合在车载、个人跟踪、工业PDA及各种手持式设备中使用:

GPS模块的原理图如下:

软件准备

  • 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码;

  • 准备一个串口调试助手,这里我使用的是Serial Port Utility;

 

 

2.生成MDK工程

选择芯片型号

打开STM32CubeMX,打开MCU选择器:

搜索并选中芯片STM32L431RCT6:

配置时钟源

  • 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;

  • 如果使用默认内部时钟(HSI),这一步可以略过;

这里我都使用外部时钟:

配置GPS使能引脚

小熊派开发板设置了一个使能引脚,用于控制GPS模块的电源:

所以要配置这个使能引脚(PC9):

配置调试串口

小熊派开发板板载ST-Link并且虚拟了一个串口,原理图如下:

这里我将开关拨到AT-MCU模式,使PC的串口与USART1之间连接。

接下来开始配置USART1:

配置GPS模块通信串口

GPS模块与USART3串口相连接,接下来开始配置USART3,波特率9600:

NVIC配置

配置 USART3 的中断优先级,首先选择一个中断优先级分组:

然后设置优先级:

配置时钟树

STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz即可:

生成工程设置

代码生成设置

最后设置生成独立的初始化文件:

生成代码

点击GENERATE CODE即可生成MDK-V5工程:

3. 在MDK中编写、编译、下载用户代码

重定向printf( )函数

参考:【STM32Cube_09】重定向printf函数到串口输出的多种方法。

转发GPS模块的数据

GPS 使能后不断的接收信号定位,并输出数据,但是 GPS 模块与 USART3 连接,无法直接查看输出的数据,何谈解析,所以先将 USART3 接收到的数据使用 USART1 发送,在电脑上使用串口助手查看,如果对于USART的中断接收方式还不明白,可以查看这篇文章:【STM32Cube_07】使用USART发送和接收数据(中断模式)。

首先在 main.c 中实现 USART3 接收中断的回调函数:

/* USER CODE BEGIN 4 */
/* 中断回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
static uint8_t ch;
/* 判断是哪个串口触发的中断 */
if(huart ->Instance == USART3)
{
//将接收到的数据发送
HAL_UART_Transmit(&huart1, &ch, 1, 0Xffff);
//重新使能串口接收中断
HAL_UART_Receive_IT(&huart3, &ch, 1);
}
}
/* USER CODE END 4 */

然后修改main函数如下:

int main(void)
{
HAL_Init();

SystemClock_Config();

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART3_UART_Init();
/* USER CODE BEGIN 2 */

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

/* 使能GPS模块 */
printf("L80-R GPS Module test...rn");
HAL_GPIO_WritePin(GPS_EN_GPIO_Port,GPS_EN_Pin, GPIO_PIN_RESET);
printf("GPE Enable ok.rn");

/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}

编译下载,可以在串口助手上看到输出的数据:

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

定位成功后蓝色的LED开始闪烁,数据如下:

解析GPS的数据

将GPS数据转发取消,删除USART3添加的中断函数:

接下来开辟一块缓冲区,来存放接收的GPS数据,并且定义GPS数据结构体,用来存放解析GPS数据得到的经纬度信息:


/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/***************************************************

*GPS NMEA-0183协议重要参数结构体定义

*卫星信息

***************************************************/

__packed typedef struct

{

uint32_t latitude_bd; //纬度 分扩大100000倍,实际要除以100000

uint8_t nshemi_bd; //北纬/南纬,N:北纬;S:南纬

uint32_t longitude_bd; //经度 分扩大100000倍,实际要除以100000

uint8_t ewhemi_bd; //东经/西经,E:东经;W:西经

}gps_msg;


/* E53_ST1传感器数据类型定义 ------------------------------------------------------------*/

typedef struct

{

float Longitude; //经度

float Latitude; //纬度

} E53_ST1_Data_TypeDef;


gps_msg gpsmsg;

static unsigned char gps_uart[1000];

E53_ST1_Data_TypeDef E53_ST1_Data;


/* USER CODE END PV */


然后编写解析GPS数据的函数,先要在开头加上头文件支持:


/* Private includes ----------------------------------------------------------*/

/* USER CODE BEGIN Includes */

#include

#include


/* USER CODE END Includes */

开始编写解析GPS数据所使用的函数:


/* USER CODE BEGIN 4 */


/***************************************************

* 函数名称: NMEA_Comma_Pos

* 函数功能:从buf里面得到第cx个逗号所在的位置

* 输入值:

* 返回值:0~0xFE,代表逗号所在位置的便宜

* 0xFF,代表不存在第cx个逗号

***************************************************/


uint8_t NMEA_Comma_Pos(uint8_t *buf,uint8_t cx)

{

uint8_t *p = buf;

while(cx)

{

if(*buf=='*'||*buf<' '||*buf>'z')return 0xFF;

if(*buf==',')cx--;

buf++;

}

return buf-p;

}

/***************************************************

* 函数名称: NMEA_Pow

* 函数功能:返回m的n次方值

* 输入值:底数m和指数n

* 返回值:m^n

***************************************************/

uint32_t NMEA_Pow(uint8_t m,uint8_t n)

{

uint32_t result = 1;

while(n--)result *= m;

return result;

}

/***************************************************

* 函数名称: NMEA_Str2num

* 函数功能:str数字转换为(int)数字,以','或者'*'结束

* 输入值:buf,数字存储区

* dx,小数点位数,返回给调用函数

* 返回值:转换后的数值

***************************************************/

int NMEA_Str2num(uint8_t *buf,uint8_t*dx)

{

uint8_t *p = buf;

uint32_t ires = 0,fres = 0;

uint8_t ilen = 0,flen = 0,i;

uint8_t 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'))//数字不在0和9之内,说明有非法字符

{

ilen = 0;

flen = 0;

break;

}

if(mask&0x01)flen++;//小数点的位数

else ilen++;//str长度加一

p++;//下一个字符

}

if(mask&0x02)buf++;//移到下一位,除去负号

for(i=0;i{

ires += NMEA_Pow(10,ilen-1-i)*(buf[i]-'0');

}

if(flen>5)flen=5;//最多取五位小数

*dx = flen;

for(i=0;i{

fres +=NMEA_Pow(10,flen-1-i)*(buf[ilen+1+i]-'0');

}

res = ires*NMEA_Pow(10,flen)+fres;

if(mask&0x02)res = -res;

return res;

}

/***************************************************

* 函数名称: NMEA_BDS_GPRMC_Analysis

* 函数功能:解析GPRMC信息

* 输入值:gpsx,NMEA信息结构体

* buf:接收到的GPS数据缓冲区首地址

***************************************************/

void NMEA_BDS_GPRMC_Analysis(gps_msg *gpsmsg,uint8_t *buf)

{

uint8_t *p4,dx;

uint8_t posx;

uint32_t temp;

float rs;

p4=(uint8_t*)strstr((const char *)buf,"$GPRMC");//"$GPRMC",经常有&和GPRMC分开的情况,故只判断GPRMC.

posx=NMEA_Comma_Pos(p4,3); //得到纬度

if(posx!=0XFF)

{

temp=NMEA_Str2num(p4+posx,&dx);

gpsmsg->latitude_bd=temp/NMEA_Pow(10,dx+2); //得到°

rs=temp%NMEA_Pow(10,dx+2); //得到'

gpsmsg->latitude_bd=gpsmsg->latitude_bd*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为°

}

posx=NMEA_Comma_Pos(p4,4); //南纬还是北纬

if(posx!=0XFF)gpsmsg->nshemi_bd=*(p4+posx);

posx=NMEA_Comma_Pos(p4,5); //得到经度

if(posx!=0XFF)

{

temp=NMEA_Str2num(p4+posx,&dx);

gpsmsg->longitude_bd=temp/NMEA_Pow(10,dx+2); //得到°

rs=temp%NMEA_Pow(10,dx+2); //得到'

gpsmsg->longitude_bd=gpsmsg->longitude_bd*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为°

}

posx=NMEA_Comma_Pos(p4,6); //东经还是西经

if(posx!=0XFF)gpsmsg->ewhemi_bd=*(p4+posx);

}

/* USER CODE END 4 */


最后编写使用中断方式接收数据到缓冲区,然后调用GPS数据解析函数的函数:


void E53_ST1_Read_Data(void)

{

/* 使用中断方式接收一次数据 */

HAL_UART_Receive_IT(&huart3,gps_uart,1000);


/* 分析缓冲区的字符串,解析GPS数据 */

NMEA_BDS_GPRMC_Analysis(&gpsmsg,(uint8_t*)gps_uart);


/* 将解析到的经纬度数据存放到结构体中,便于其他函数使用 */

E53_ST1_Data.Longitude=(float)((float)gpsmsg.longitude_bd/100000);

E53_ST1_Data.Latitude=(float)((float)gpsmsg.latitude_bd/100000);

}

当然,别忘了在 main 函数之前声明这些函数:


/* Private user code ---------------------------------------------------------*/

/* USER CODE BEGIN 0 */

uint8_t NMEA_Comma_Pos(uint8_t *buf,uint8_t cx);

uint32_t NMEA_Pow(uint8_t m,uint8_t n);

int NMEA_Str2num(uint8_t *buf,uint8_t*dx);

void NMEA_BDS_GPRMC_Analysis(gps_msg *gpsmsg,uint8_t *buf);

void E53_ST1_Read_Data(void);

/* USER CODE END 0 */

大功告成,在main函数中调用:


int main(void)

{

HAL_Init();


SystemClock_Config();


/* Initialize all configured peripherals */

MX_GPIO_Init();

MX_USART1_UART_Init();

MX_USART3_UART_Init();


/* USER CODE BEGIN 2 */


HAL_UART_Receive_IT(&huart3, (uint8_t*)gps_uart, 1);

printf("L80-R GPS Module test...rn");


/* 使能GPS模块 */

HAL_GPIO_WritePin(GPS_EN_GPIO_Port,GPS_EN_Pin, GPIO_PIN_RESET);

printf("GPE Enable ok.rn");


/* USER CODE END 2 */


/* Infinite loop */

/* USER CODE BEGIN WHILE */

while (1)

{

/* USER CODE END WHILE */


/* USER CODE BEGIN 3 */

E53_ST1_Read_Data();

printf("Longitude: %f, Latitude: %f.rn", E53_ST1_Data.Longitude, E53_ST1_Data.Latitude);

HAL_Delay(2000);

}

/* USER CODE END 3 */

}

实验现象

编译下载后,尽量将小熊派开发板放在窗户边,等待定位成功,串口输出数据如下:

定位成功后,定位数据如下:

查看定位

有了GPS定位数据之后,可以在地图上查看具体的位置,也可以上传到华为云IoT平台查看,这里我使用 GPS经纬度查询工具进行查看:

工具链接:http://www.gpsspg.com/maps.htm


关键字:STM32Cube  USART  GPS数据 引用地址:【STM32Cube_23】使用USART接收GPS数据并解析(L80-R)

上一篇:【STM32Cube_21】使用DAC输出任意指定电压
下一篇:【STM32Cube_01】初识 STM32 Cube 生态系统

推荐阅读最新更新时间:2024-11-12 21:42

STM32-printf重定向到USART
在使用STM32的过程中,尤其是刚开始学习使用的时候,由于不知道自己的程序写的对不对,就经常需要一点验证的方法,点亮一个LED灯就是最简单的验证方法,但是有的时候还经常需要串口的输出来验证自己的程序是否正确,但是官方提供的函数库中用于串口发送的好像就一个USART_SendData(),通过外设USARTx发送单个数据,对于熟悉C语言的同学来说,这个函数还没有格式输出,当想要输出一个数字,或者字符串的时候,使用起来可能有点麻烦,现在有一个很好的方法就可以使用C语言中的printf()函数,而且使用方法是一样的。如何使用,很简单,我们只需要重新定向printf就可以,将它的数据用STM32的串口进行发送出去就可以了。 首先添加pr
[单片机]
【STM32学习笔记】USART 新特性
支持RXD和TXD管脚互换 很多时候,我们在外接RS232芯片时,很容易将RXD和TXD两根线接反。这类低级错误,一般是老司机才会犯。如果大家知道USART的TXD和RXD管脚可以互换,那么在连接外设RS232芯片时,如果发生错误,就不必再修改硬件,只需直接在软件中将RXD和TXD的管脚反转过来即可修正错误。 参考上图,设置SWAP位,即可将RXD和TXD管脚互换。 支持接收和发送的电平极性反转 第二个特性是,接收和发送的电平极性是可以反转的。通常默认串口电平是高电平为逻辑1,低电平为逻辑0;而在ST的USART中是可以将高电平设置为逻辑0,低电平设置为逻辑1的。这一特性,让我们在一些特殊的场景下灵活使用,举
[单片机]
STM8L在USART中使用DMA来发送与接收数据
以USART为例子来使用DMA 分两部分,第一为,DMA这个外设自身的配置;第二为,USART的DMA部分配置, DMA与USART的DMA配置 void SYS_DMA_Init(void) { CLK_PeripheralClockConfig(CLK_Peripheral_DMA1, ENABLE); /span //打开时钟,很重要 /* Deinitialize DMA channels */ DMA_GlobalDeInit(); DMA_DeInit(DMA1_Channel1); DMA_DeInit(DMA1_Channel2); /* DMA channel Rx of USART Co
[单片机]
STM8L在<font color='red'>USART</font>中使用DMA来发送与接收<font color='red'>数据</font>
【STM32Cube_07】使用USART发送和接收数据(中断模式)
1. 准备工作 硬件准备 首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi): 软件准备 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码; 准备一个串口调试助手,这里我使用的是Serial Port Utility; Keil MDK和串口助手Serial Port Utility 的安装包都可以在文末关注公众号获取,回复关键字获取相应的安装包: 2.生成MDK工程 — 初始化GPIO为输入 选择芯片型号 打开STM32CubeMX,打开MCU选择器: 搜索并选中芯片STM32L431RCT6: 配置时钟源 如果选择使用外部高速时钟(HSE),则需要在
[单片机]
【STM32Cube_07】使用<font color='red'>USART</font>发送和接收<font color='red'>数据</font>(中断模式)
STM32cube使用LAN8720芯片生成lwip初始代码的一些操作
首先使用cude设置好所有lan8720的引脚定义,包括REST和PHYAD0,下面是需要小改动的部分 Cube PHY address 根据PHYAD0选0,注意初始化引脚 PHY_SR 0x001F PHY_SPEED_STATUS 0x0004 PHY_DUPLEX_STATUS 0x0010 While(1)加入MX_LWIP_Process();函数 low_level_init加入LAN8720_RESET();函数 void LAN8720_RESET(void) { HAL_GPIO_WritePin(ETH_PHYAD0_GPIO_Port, ETH_PHYAD0_Pin, GPIO_PIN_RESE
[单片机]
STM32_USART 串口通讯详解
对51单片机有了解的都知道51单片机的串口通讯工作原理,我们单片机使用的电平TTL电平,为了使我们的的单片机与PC进行通信,就需要一个电平转换芯片,把TTL电平转换为USB电平(使用的USB接口,如果使用的DB9接口,电平转换芯片则为TTL转RS232电平芯片),然后通过对SBUF寄存器的读写操作来实现PC与MCU的通信。STM32的串口通讯原理与51相同。下面就对USART尽行具体的介绍。 USART(Universal synchronous asynchronous receivertransmitter )通用同步异步收发器,是STM32上基于串口通讯协议来实现与外部通信的一个外设,因为串口通讯协议的简单,便捷,所以在
[单片机]
STM32_<font color='red'>USART</font> 串口通讯详解
STM32之USART库函数USART_SendData的bug
1.最近在调试ATM32F103CB时发现,一串数据的最后一个字节总是发送不出去,用的是RS485收发; 2.代码如下: void uartReturn(unsigned char childBoardAddr) { uchar temp = 0; //must have temp += 0xAB; temp += childBoardAddr; temp += 0x30; temp += 0x01; temp += childBoardAddr; RS485_TX_EN; //enable rs485 tx sendByte(0xAB); sendByte(childBoardAddr); se
[单片机]
物联网之STM32开发三(USART串口)
内容概要: 串行通信的基本概念 串口寄存器介绍 STM32实现串口数据的收发 HAL串口库函数的使用及printf的实现 串行通信的基本概念: 内容概要: 通信的基本概念 USART介绍 串口的电路连接 串口的通信协议 同步通信和异步通信: 通信,最少要有两个对象,一个收,一个发。 同步通信:一般情况下同步通信指的是通信双方根据同步信号进行通信的方式。比如通信双方有一个共同的时钟信号,大家根据时钟信号的变化进行通信。 异步通信:是指数据传输速度匹配依赖于通信双方有自己独立的系统时钟,大家约定好通信的速度。异步通信不需要同步信号,但是并不是说通信的过程不同布。 串行通信和并行通
[单片机]
物联网之STM32开发三(<font color='red'>USART</font>串口)
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved