STM32Cube的串口实战(一)GPS+BD模块

串口系列
STM32Cube的串口设置(一)即学即用
STM32Cube的串口设置(二)一个串口接收另一个串口发送

通过串口设置之前的部分大家应该基本会使用多个串口配合了,今天就来找个东西练练手,第一个拿GPS+BD开刀(用的是ATK_S1216F8_BD模块,STM32F767的芯片)。

实验目的
【将串口3连接的GPS+BD模块发送的信号转送到串口1通过USB打印出来,搜集其中相关信息获取需要的信息,单独打印】

一、基本思路

1、需要了解GPS+BD模块的波特率以及工作特性,确保UART3可以收到模块发送的数据
2、接收到数据后,转存至数据处理区,将数据条条单独分析
3、数据分析完毕之后将需要的经纬度位置、世界标准时间、GPS卫星数、BD卫星数等信息放到相应存储空间
4、向UART1发送UART3的原数据
5、向UART1发送处理后的数据

【GPS+BD模块是一个与卫星的通信装置,但是它只能向卫星请求当地的定位,所以与STM32的连接部分只存在GPS+BD的单方面发送信息,为单工传输,不涉及UART3的发送】

二、操作步骤

要求:通过串口向PC发送一段字符

1、根据自己的stm32的芯片型号来选择,我这里是STM32F767IGTx

在这里插入图片描述

2、选好芯片之后照旧设置RCC为外部时钟

在这里插入图片描述

3、使能串口1、3(usart1、usart3),如图:

模式设为异步(Asynchronous)其他默认,波特率可以自己改,USART1为115200Bits/s,USART3为38400Bits/s。
STM32Cube的串口实战(一)GPS+BD模块_第1张图片
STM32Cube的串口实战(一)GPS+BD模块_第2张图片
之后再使能串口1、3中断
STM32Cube的串口实战(一)GPS+BD模块_第3张图片

4、设置中断优先级,如图:

STM32Cube的串口实战(一)GPS+BD模块_第4张图片
STM32Cube的串口实战(一)GPS+BD模块_第5张图片

设置中断优先级在这里插入图片描述

5、看原理图,找到串口对应引脚,如图:

我这里是

PA10——>USART1_RX  
PA9——>USART1_TX

PB11——>USART3_RX  
PB10——>USART3_TX

串口1
串口3

6、根据对应引脚设置串口引脚,如图:

找到PA9、PA10引脚左键点击分别选择USART1_TX和USART1_RX
(不用担心选错选反,针脚的功能是ST公司已经定义好了的)
在这里插入图片描述
STM32Cube的串口实战(一)GPS+BD模块_第6张图片

7、设置时钟树,如图:

这里会搞的按自己习惯搞,不会搞的默认就好,但是不能有里面是红色的框(红色框就是错了意思)
在这里插入图片描述

8、项目设置,如图:

红框里的按照自己的Keil版本来
在这里插入图片描述
个人喜欢把.c/.h文件分开
在这里插入图片描述

9、点击右上角的‘GENERATE CODE’直接生成代码,如图:

在这里插入图片描述

10、生成代码后用Keil打开项目并在Application/User中找到usart.c并在/USER CODE BEGIN 0/后添加如下代码,如图:

#include 
struct __FILE 
{ 
	int handle; 
}; 

FILE __stdout;       
void _sys_exit(int x) 
{ 
	x = x; 
} 
int fputc(int ch, FILE *f)
{ 	
	while((USART3->ISR&0X40)==0);
	USART3->TDR=(uint8_t)ch;      
	return ch;
}
uint8_t USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.

//串口发送缓存区 	
__align(8) uint8_t USART3_TX_BUF[USART3_MAX_SEND_LEN]; 	//发送缓冲,最大USART3_MAX_SEND_LEN字节  	  
//串口接收缓存区 	
uint8_t USART3_RX_BUF[USART3_MAX_RECV_LEN]; 				//接收缓冲,最大USART3_MAX_RECV_LEN个字节.

//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
uint16_t USART_RX_STA=0;       //接收状态标记	
uint16_t USART3_RX_STA=0; 

uint8_t aRxBuffer[RXBUFFERSIZE];//HAL库使用的串口接收缓冲

以上代码主要保证将UART1的输出直接用printf代替。
STM32Cube的串口实战(一)GPS+BD模块_第7张图片

11-A、生成代码后用Keil打开项目并在Application/User中找到main.c:

在/USER CODE BEGIN PV/后添加如下代码

extern uint8_t USART3_RX_BUF[800];//重申明外部转存空间

在/* USER CODE BEGIN WHILE */后添加如下代码

	HAL_UART_Receive_IT(&huart3,USART3_RX_BUF,1);	
	HAL_UART_Transmit(&huart1,USART3_RX_BUF,sizeof(USART3_RX_BUF),100);
	\\开启中断

在/* USER CODE BEGIN 4 */后添加回调函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

	if(huart->Instance == USART3)
	{
		HAL_UART_Transmit(&huart1,USART3_RX_BUF,1,100);//串口1发送接收buff里的东西	
		HAL_UART_Receive_IT(&huart3,USART3_RX_BUF,1);	//重新开启串口3接收中断	
	}
}

编译、下载

该操作是将UART3的原数据不进行处理直接转发到UART1,操作简单,不涉及数据处理。

11-B、在项目中新建两个文件,分别为gps.h和gps.c:

gps.h

#ifndef __GPS_H
#define __GPS_H	 

//GPS NMEA-0183协议重要参数结构体定义 
//卫星信息


__packed typedef struct  
{										    
 	unsigned char num;		//卫星编号
	unsigned char eledeg;	//卫星仰角
	unsigned short azideg;	//卫星方位角
	unsigned char sn;		//信噪比		   
}nmea_slmsg; 
//北斗 NMEA-0183协议重要参数结构体定义 
//卫星信息
__packed typedef struct  
{	
 	unsigned char beidou_num;		//卫星编号
	unsigned char beidou_eledeg;	//卫星仰角
	unsigned short beidou_azideg;	//卫星方位角
	unsigned char beidou_sn;		//信噪比		   
}beidou_nmea_slmsg; 

//UTC时间信息
__packed typedef struct  
{										    
 	unsigned short year;	//年份
	unsigned char month;	//月份
	unsigned char date;	//日期
	unsigned char hour; 	//小时
	unsigned char min; 	//分钟
	unsigned char sec; 	//秒钟
}nmea_utc_time;   	   
//NMEA 0183 协议解析后数据存放结构体
__packed typedef struct  
{										    
 	unsigned char svnum;					//可见GPS卫星数
	unsigned char beidou_svnum;					//可见北斗卫星数
	nmea_slmsg slmsg[12];		//最多12颗GPS卫星
	beidou_nmea_slmsg beidou_slmsg[12];		//暂且算最多12颗北斗卫星
	nmea_utc_time utc;			//UTC时间
	unsigned int latitude;				//纬度 分扩大100000倍,实际要除以100000
	unsigned char nshemi;					//北纬/南纬,N:北纬;S:南纬				  
	unsigned int longitude;			    //经度 分扩大100000倍,实际要除以100000
	unsigned char ewhemi;					//东经/西经,E:东经;W:西经
	unsigned char gpssta;					//GPS状态:0,未定位;1,非差分定位;2,差分定位;6,正在估算.				  
 	unsigned char posslnum;				//用于定位的GPS卫星数,0~12.
 	unsigned char possl[12];				//用于定位的卫星编号
	unsigned char fixmode;					//定位类型:1,没有定位;2,2D定位;3,3D定位
	unsigned short pdop;					//位置精度因子 0~500,对应实际值0~50.0
	unsigned short hdop;					//水平精度因子 0~500,对应实际值0~50.0
	unsigned short vdop;					//垂直精度因子 0~500,对应实际值0~50.0 

	int altitude;			 	//海拔高度,放大了10倍,实际除以10.单位:0.1m	 
	unsigned short speed;					//地面速率,放大了1000倍,实际除以10.单位:0.001公里/小时	 
}nmea_msg;
 	////////////////////////////////////////////////////////////////////////////////////////////////////
//SkyTra S1216F8 配置波特率结构体
__packed typedef struct
{
	unsigned short sos;            //启动序列,固定为0XA0A1
	unsigned short PL;             //有效数据长度0X0004; 
	unsigned char id;             //ID,固定为0X05
	unsigned char com_port;       //COM口,固定为0X00,即COM1   
	unsigned char Baud_id;       //波特率(0~8,4800,9600,19200,38400,57600,115200,230400,460800,921600)
	unsigned char Attributes;     //配置数据保存位置 ,0保存到SRAM,1保存到SRAM&FLASH,2临时保存
	unsigned char CS;             //校验值
	unsigned short end;            //结束符:0X0D0A  
}SkyTra_baudrate;
//////////////////////////////////////////////////////////////////////////////////////////////////// 	
//SkyTra S1216F8 配置输出信息结构体
__packed typedef struct
{
	unsigned short sos;            //启动序列,固定为0XA0A1
	unsigned short PL;             //有效数据长度0X0009; 
	unsigned char id;             //ID,固定为0X08
	unsigned char GGA;            //1~255(s),0:disable
	unsigned char GSA;            //1~255(s),0:disable
	unsigned char GSV;            //1~255(s),0:disable
	unsigned char GLL;            //1~255(s),0:disable
	unsigned char RMC;            //1~255(s),0:disable
	unsigned char VTG;            //1~255(s),0:disable
	unsigned char ZDA;            //1~255(s),0:disable
	unsigned char Attributes;     //配置数据保存位置 ,0保存到SRAM,1保存到SRAM&FLASH,2临时保存
	unsigned char CS;             //校验值
	unsigned short end;            //结束符:0X0D0A  
}SkyTra_outmsg;
//////////////////////////////////////////////////////////////////////////////////////////////////// 	
//SkyTra S1216F8 配置位置更新率结构体
__packed typedef struct
{
	unsigned short sos;            //启动序列,固定为0XA0A1
	unsigned short PL;             //有效数据长度0X0003; 
	unsigned char id;             //ID,固定为0X0E
	unsigned char rate;           //取值范围:1, 2, 4, 5, 8, 10, 20, 25, 40, 50
	unsigned char Attributes;     //配置数据保存位置 ,0保存到SRAM,1保存到SRAM&FLASH,2临时保存
	unsigned char CS;             //校验值
	unsigned short end;            //结束符:0X0D0A  
}SkyTra_PosRate;
//////////////////////////////////////////////////////////////////////////////////////////////////// 	
//SkyTra S1216F8 配置输出脉冲(PPS)宽度结构体
__packed typedef struct
{
	unsigned short sos;            //启动序列,固定为0XA0A1
	unsigned short PL;             //有效数据长度0X0007; 
	unsigned char id;             //ID,固定为0X65
	unsigned char Sub_ID;         //0X01
	unsigned int width;        //1~100000(us)
	unsigned char Attributes;     //配置数据保存位置 ,0保存到SRAM,1保存到SRAM&FLASH,2临时保存
	unsigned char CS;             //校验值
	unsigned short end;            //结束符:0X0D0A 
}SkyTra_pps_width;
//////////////////////////////////////////////////////////////////////////////////////////////////// 	
//SkyTra S1216F8 ACK结构体
__packed typedef struct
{
	unsigned short sos;            //启动序列,固定为0XA0A1
	unsigned short PL;             //有效数据长度0X0002; 
	unsigned char id;             //ID,固定为0X83
	unsigned char ACK_ID;         //ACK ID may further consist of message ID and message sub-ID which will become 3 bytes of ACK message
	unsigned char CS;             //校验值
	unsigned short end;            //结束符 
}SkyTra_ACK;
//////////////////////////////////////////////////////////////////////////////////////////////////// 	
//SkyTra S1216F8 NACK结构体
__packed typedef struct
{
	unsigned short sos;            //启动序列,固定为0XA0A1
	unsigned short PL;             //有效数据长度0X0002; 
	unsigned char id;             //ID,固定为0X84
	unsigned char NACK_ID;         //ACK ID may further consist of message ID and message sub-ID which will become 3 bytes of ACK message
	unsigned char CS;             //校验值
	unsigned short end;            //结束符 
}SkyTra_NACK;


int NMEA_Str2num(unsigned char *buf,unsigned char*dx);
void GPS_Analysis(nmea_msg *gpsx,unsigned char *buf);
void NMEA_GPGSV_Analysis(nmea_msg *gpsx,unsigned char *buf);
void NMEA_BDGSV_Analysis(nmea_msg *gpsx,unsigned char *buf);
void NMEA_GNGGA_Analysis(nmea_msg *gpsx,unsigned char *buf);
void NMEA_GNGSA_Analysis(nmea_msg *gpsx,unsigned char *buf);
void NMEA_GNGSA_Analysis(nmea_msg *gpsx,unsigned char *buf);
void NMEA_GNRMC_Analysis(nmea_msg *gpsx,unsigned char *buf);
void NMEA_GNVTG_Analysis(nmea_msg *gpsx,unsigned char *buf);
unsigned char SkyTra_Cfg_Prt(unsigned char baud_id);
unsigned char SkyTra_Cfg_Tp(unsigned int width);
unsigned char SkyTra_Cfg_Rate(unsigned char Frep);
void SkyTra_Send_Date(unsigned char* dbuf,unsigned short len);
#endif

gps.c

#include "gps.h" 
#include "gpio.h" 							   
#include "usart.h" 								   
#include "stdio.h"	 
#include "stdarg.h"	 
#include "string.h"	 
#include "math.h"

const unsigned int BAUD_id[9]={4800,9600,19200,38400,57600,115200,230400,460800,921600};//模块支持波特率数组

//从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;isvnum=NMEA_Str2num(p1+posx,&dx);
	for(i=0;islmsg[slx].num=NMEA_Str2num(p1+posx,&dx);	//得到卫星编号
			else break; 
			posx=NMEA_Comma_Pos(p1,5+j*4);
			if(posx!=0XFF)gpsx->slmsg[slx].eledeg=NMEA_Str2num(p1+posx,&dx);//得到卫星仰角 
			else break;
			posx=NMEA_Comma_Pos(p1,6+j*4);
			if(posx!=0XFF)gpsx->slmsg[slx].azideg=NMEA_Str2num(p1+posx,&dx);//得到卫星方位角
			else break; 
			posx=NMEA_Comma_Pos(p1,7+j*4);
			if(posx!=0XFF)gpsx->slmsg[slx].sn=NMEA_Str2num(p1+posx,&dx);	//得到卫星信噪比
			else break;
			slx++;	   
		}   
 		p=p1+1;//切换到下一个GPGSV信息
	}   
}
//分析BDGSV信息
//gpsx:nmea信息结构体
//buf:接收到的北斗数据缓冲区首地址
void NMEA_BDGSV_Analysis(nmea_msg *gpsx,unsigned char *buf)
{
	unsigned char *p,*p1,dx;
	unsigned char len,i,j,slx=0;
	unsigned char posx;   	 
	p=buf;
	p1=(unsigned char*)strstr((const char *)p,"$BDGSV");
	len=p1[7]-'0';								//得到BDGSV的条数
	posx=NMEA_Comma_Pos(p1,3); 					//得到可见北斗卫星总数
	if(posx!=0XFF)gpsx->beidou_svnum=NMEA_Str2num(p1+posx,&dx);
	for(i=0;ibeidou_slmsg[slx].beidou_num=NMEA_Str2num(p1+posx,&dx);	//得到卫星编号
			else break; 
			posx=NMEA_Comma_Pos(p1,5+j*4);
			if(posx!=0XFF)gpsx->beidou_slmsg[slx].beidou_eledeg=NMEA_Str2num(p1+posx,&dx);//得到卫星仰角 
			else break;
			posx=NMEA_Comma_Pos(p1,6+j*4);
			if(posx!=0XFF)gpsx->beidou_slmsg[slx].beidou_azideg=NMEA_Str2num(p1+posx,&dx);//得到卫星方位角
			else break; 
			posx=NMEA_Comma_Pos(p1,7+j*4);
			if(posx!=0XFF)gpsx->beidou_slmsg[slx].beidou_sn=NMEA_Str2num(p1+posx,&dx);	//得到卫星信噪比
			else break;
			slx++;	   
		}   
 		p=p1+1;//切换到下一个BDGSV信息
	}   
}
//分析GNGGA信息
//gpsx:nmea信息结构体
//buf:接收到的GPS/北斗数据缓冲区首地址
void NMEA_GNGGA_Analysis(nmea_msg *gpsx,unsigned char *buf)
{
	unsigned char *p1,dx;			 
	unsigned char posx;    
	p1=(unsigned char*)strstr((const char *)buf,"$GNGGA");
	posx=NMEA_Comma_Pos(p1,6);								//得到GPS状态
	if(posx!=0XFF)gpsx->gpssta=NMEA_Str2num(p1+posx,&dx);	
	posx=NMEA_Comma_Pos(p1,7);								//得到用于定位的卫星数
	if(posx!=0XFF)gpsx->posslnum=NMEA_Str2num(p1+posx,&dx); 
	posx=NMEA_Comma_Pos(p1,9);								//得到海拔高度
	if(posx!=0XFF)gpsx->altitude=NMEA_Str2num(p1+posx,&dx);  
}
//分析GNGSA信息
//gpsx:nmea信息结构体
//buf:接收到的GPS/北斗数据缓冲区首地址
void NMEA_GNGSA_Analysis(nmea_msg *gpsx,unsigned char *buf)
{
	unsigned char *p1,dx;			 
	unsigned char posx; 
	unsigned char i;   
	p1=(unsigned char*)strstr((const char *)buf,"$GNGSA");
	posx=NMEA_Comma_Pos(p1,2);								//得到定位类型
	if(posx!=0XFF)gpsx->fixmode=NMEA_Str2num(p1+posx,&dx);	
	for(i=0;i<12;i++)										//得到定位卫星编号
	{
		posx=NMEA_Comma_Pos(p1,3+i);					 
		if(posx!=0XFF)gpsx->possl[i]=NMEA_Str2num(p1+posx,&dx);
		else break; 
	}				  
	posx=NMEA_Comma_Pos(p1,15);								//得到PDOP位置精度因子
	if(posx!=0XFF)gpsx->pdop=NMEA_Str2num(p1+posx,&dx);  
	posx=NMEA_Comma_Pos(p1,16);								//得到HDOP位置精度因子
	if(posx!=0XFF)gpsx->hdop=NMEA_Str2num(p1+posx,&dx);  
	posx=NMEA_Comma_Pos(p1,17);								//得到VDOP位置精度因子
	if(posx!=0XFF)gpsx->vdop=NMEA_Str2num(p1+posx,&dx);  
}
//分析GNRMC信息
//gpsx:nmea信息结构体
//buf:接收到的GPS/北斗数据缓冲区首地址
void NMEA_GNRMC_Analysis(nmea_msg *gpsx,unsigned char *buf)
{
	unsigned char *p1,dx;			 
	unsigned char posx;     
	unsigned int temp;	   
	float rs;  
	p1=(unsigned char*)strstr((const char *)buf,"GNRMC");//"$GNRMC",经常有&和GNRMC分开的情况,故只判断GPRMC.
	posx=NMEA_Comma_Pos(p1,1);								//得到UTC时间
	if(posx!=0XFF)
	{
		temp=NMEA_Str2num(p1+posx,&dx)/NMEA_Pow(10,dx);	 	//得到UTC时间,去掉ms
		gpsx->utc.hour=(temp/10000)+8;
		gpsx->utc.min=(temp/100)%100;
		gpsx->utc.sec=temp%100;	 	 
	}	
	posx=NMEA_Comma_Pos(p1,3);								//得到纬度
	if(posx!=0XFF)
	{
		temp=NMEA_Str2num(p1+posx,&dx);		 	 
		gpsx->latitude=temp/NMEA_Pow(10,dx+2);	//得到°
		rs=temp%NMEA_Pow(10,dx+2);				//得到'		 
		gpsx->latitude=gpsx->latitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为° 
	}
	posx=NMEA_Comma_Pos(p1,4);								//南纬还是北纬 
	if(posx!=0XFF)gpsx->nshemi=*(p1+posx);					 
 	posx=NMEA_Comma_Pos(p1,5);								//得到经度
	if(posx!=0XFF)
	{												  
		temp=NMEA_Str2num(p1+posx,&dx);		 	 
		gpsx->longitude=temp/NMEA_Pow(10,dx+2);	//得到°
		rs=temp%NMEA_Pow(10,dx+2);				//得到'		 
		gpsx->longitude=gpsx->longitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为° 
	}
	posx=NMEA_Comma_Pos(p1,6);								//东经还是西经
	if(posx!=0XFF)gpsx->ewhemi=*(p1+posx);		 
	posx=NMEA_Comma_Pos(p1,9);								//得到UTC日期
	if(posx!=0XFF)
	{
		temp=NMEA_Str2num(p1+posx,&dx);		 				//得到UTC日期
		gpsx->utc.date=temp/10000;
		gpsx->utc.month=(temp/100)%100;
		gpsx->utc.year=2000+temp%100;	 	 
	} 
}
//分析GNVTG信息
//gpsx:nmea信息结构体
//buf:接收到的GPS/北斗数据缓冲区首地址
void NMEA_GNVTG_Analysis(nmea_msg *gpsx,unsigned char *buf)
{
	unsigned char *p1,dx;			 
	unsigned char posx;    
	p1=(unsigned char*)strstr((const char *)buf,"$GNVTG");							 
	posx=NMEA_Comma_Pos(p1,7);								//得到地面速率
	if(posx!=0XFF)
	{
		gpsx->speed=NMEA_Str2num(p1+posx,&dx);
		if(dx<3)gpsx->speed*=NMEA_Pow(10,3-dx);	 	 		//确保扩大1000倍
	}
}  
//提取NMEA-0183信息
//gpsx:nmea信息结构体
//buf:接收到的GPS/北斗数据缓冲区首地址
void GPS_Analysis(nmea_msg *gpsx,unsigned char *buf)
{
	NMEA_GPGSV_Analysis(gpsx,buf);	//GPGSV解析
	NMEA_BDGSV_Analysis(gpsx,buf);	//BDGSV解析
	NMEA_GNGGA_Analysis(gpsx,buf);	//GNGGA解析 	
	NMEA_GNGSA_Analysis(gpsx,buf);	//GNGSA解析
	NMEA_GNRMC_Analysis(gpsx,buf);	//GNRMC解析
	NMEA_GNVTG_Analysis(gpsx,buf);	//GNVTG解析
}
///////////////////////////////////////////SkyTraq 配置代码/////////////////////////////////////
////检查CFG配置执行情况
////返回值:0,ACK成功
////       1,接收超时错误
////       2,没有找到同步字符
////       3,接收到NACK应答
unsigned char SkyTra_Cfg_Ack_Check(void)
{			 
	unsigned short len=0,i;
	unsigned char rval=0;
	while((USART3_RX_STA&0X8000)==0 && len<100)//等待接收到应答   
	{
		len++;
		HAL_Delay(5);
	}		 
	if(len<100)   	//超时错误.
	{
		len=USART3_RX_STA&0X7FFF;	//此次接收到的数据长度 
		for(i=0;isos=0XA1A0;		//引导序列(小端模式)
	cfg_prt->PL=0X0400;			//有效数据长度(小端模式)
	cfg_prt->id=0X05;		    //配置波特率的ID 
	cfg_prt->com_port=0X00;			//操作串口1
	cfg_prt->Baud_id=baud_id;	 	////波特率对应编号
	cfg_prt->Attributes=1; 		  //保存到SRAM&FLASH
	cfg_prt->CS=cfg_prt->id^cfg_prt->com_port^cfg_prt->Baud_id^cfg_prt->Attributes;
	cfg_prt->end=0X0A0D;        //发送结束符(小端模式)
	SkyTra_Send_Date((unsigned char*)cfg_prt,sizeof(SkyTra_baudrate));//发送数据给SkyTra   
	HAL_Delay(200);				//等待发送完成   
	return SkyTra_Cfg_Ack_Check();
} 
//配置SkyTra_GPS/北斗模块的时钟脉冲宽度
//width:脉冲宽度1~100000(us)
//返回值:0,发送成功;其他,发送失败.
unsigned char SkyTra_Cfg_Tp(unsigned int width)
{
	unsigned int temp=width;
	SkyTra_pps_width *cfg_tp=(SkyTra_pps_width *)USART3_TX_BUF;
	temp=(width>>24)|((width>>8)&0X0000FF00)|((width<<8)&0X00FF0000)|((width<<24)&0XFF000000);//小端模式
	cfg_tp->sos=0XA1A0;		    //cfg header(小端模式)
	cfg_tp->PL=0X0700;        //有效数据长度(小端模式)
	cfg_tp->id=0X65	;			    //cfg tp id
	cfg_tp->Sub_ID=0X01;			//数据区长度为20个字节.
	cfg_tp->width=temp;		  //脉冲宽度,us
	cfg_tp->Attributes=0X01;  //保存到SRAM&FLASH	
	cfg_tp->CS=cfg_tp->id^cfg_tp->Sub_ID^(cfg_tp->width>>24)^(cfg_tp->width>>16)&0XFF^(cfg_tp->width>>8)&0XFF^cfg_tp->width&0XFF^cfg_tp->Attributes;    	
	cfg_tp->end=0X0A0D;       //发送结束符(小端模式)
	SkyTra_Send_Date((unsigned char*)cfg_tp,sizeof(SkyTra_pps_width));//发送数据给SkyTraF8-BD  
	return SkyTra_Cfg_Ack_Check();
}
//配置SkyTraF8-BD的更新速率	    
//Frep:(取值范围:1,2,4,5,8,10,20)测量时间间隔,单位为Hz,最大不能大于20Hz
//返回值:0,发送成功;其他,发送失败.
unsigned char SkyTra_Cfg_Rate(unsigned char Frep)
{
	SkyTra_PosRate *cfg_rate=(SkyTra_PosRate *)USART3_TX_BUF;
 	cfg_rate->sos=0XA1A0;	    //cfg header(小端模式)
	cfg_rate->PL=0X0300;			//有效数据长度(小端模式)
	cfg_rate->id=0X0E;	      //cfg rate id
	cfg_rate->rate=Frep;	 	  //更新速率
	cfg_rate->Attributes=0X01;	   	//保存到SRAM&FLASH	.
	cfg_rate->CS=cfg_rate->id^cfg_rate->rate^cfg_rate->Attributes;    //校验值
	cfg_rate->end=0X0A0D;       //发送结束符(小端模式)
	SkyTra_Send_Date((unsigned char*)cfg_rate,sizeof(SkyTra_PosRate));//发送数据给SkyTraF8-BD 
	return SkyTra_Cfg_Ack_Check();
}
//发送一批数据给SkyTraF8-BD,这里通过串口3发送
//dbuf:数据缓存首地址
//len:要发送的字节数
void SkyTra_Send_Date(unsigned char* dbuf,unsigned short len)
{
	unsigned short j;
	for(j=0;jISR&0X40)==0);//循环发送,直到发送完毕   
		USART3->TDR=dbuf[j];  
	}	
}

将以上两个代码放到项目中后再main.c函数中添加如下代码:

定义以下变量

uint8_t USART1_TX_BUF[USART3_MAX_RECV_LEN]; 					//串口1,发送缓存区
nmea_msg gpsx; 											//GPS信息
__align(4) uint8_t dtbuf[50];   								//打印缓存器
const uint8_t*fixmode_tbl[4]={"Fail","Fail"," 2D "," 3D "};	//fix mode字符串 

定义GPS数据显示函数:

void Gps_Msg_Show(void)
{
 	float tp;		   	 
	tp=gpsx.longitude;	   
	printf("Longitude:%.5f %1c   ",tp/=100000,gpsx.ewhemi);	//得到经度字符串 
	printf("\r\n");
	tp=gpsx.latitude;	   
	printf("Latitude:%.5f %1c   ",tp/=100000,gpsx.nshemi);	//得到纬度字符串 	
	printf("\r\n");	
	tp=gpsx.altitude;	   
 	printf("Altitude:%.1fm     ",tp/=10);	    			//得到高度字符串 			   
	printf("\r\n");
	tp=gpsx.speed;	   
 	printf("Speed:%.3fkm/h     ",tp/=1000);		    		//得到速度字符串	 
	printf("\r\n");	
	if(gpsx.fixmode<=3)														//定位状态
	{  
		sprintf((char *)dtbuf,"Fix Mode:%s",fixmode_tbl[gpsx.fixmode]);		
		printf("\r\n");		
	}	 	  
	printf("GPS+BD Valid satellite:%02d",gpsx.posslnum);	 		//用于定位的GPS卫星数	    
	printf("\r\n");	
	printf("GPS Visible satellite:%02d",gpsx.svnum%100);	 		//可见GPS卫星数
	printf("\r\n");	
	printf("BD Visible satellite:%02d",gpsx.beidou_svnum%100);	 		//可见北斗卫星数
	printf("\r\n");	
	printf("UTC Date:%04d/%02d/%02d   ",gpsx.utc.year,gpsx.utc.month,gpsx.utc.date);	//显示UTC日期
	printf("\r\n");	
	printf("UTC Time:%02d:%02d:%02d   ",gpsx.utc.hour,gpsx.utc.min,gpsx.utc.sec);	//显示UTC时间	  
	printf("\r\n\n\n");	
}  

在main函数的主循环内添加如下代码:

	uint16_t i,rxlen;
	uint16_t lenx;
	uint8_t key=0XFF;
	uint8_t upload=1;



 while (1)
  {
		HAL_Delay(1);
		if(USART3_RX_STA&0X8000)		//接收到一次数据了
		{
			rxlen=USART3_RX_STA&0X7FFF;	//得到数据长度
			for(i=0;i

以上数据是将UART3接收到的数据通过转存区分析后根据分析出的数据通过Gps_Msg_Show()函数通过UART1打印出来。

upload的作用是是否将UART3收到的原始数据转发给UART1

编译、下载、看结果

三、实验验证

正所谓没有实验结果的教程都是耍流氓,出结果

等待约30秒的GPS冷启动后,收到卫星信息
STM32Cube的串口实战(一)GPS+BD模块_第8张图片
该格式为NMEA-0183协议,是美国国家海洋电子协会为海用电子设备制定的标准格式,目前是GPS/北斗导航设备的标准协议,使用ASCII码传递GPS定位信息。
以上为upload=1时,在发送了我们需要的信息同时转发了UART3的接收信息,现在将upload改为0,看结果:
STM32Cube的串口实战(一)GPS+BD模块_第9张图片
为了保证接收信息的正确性,上图我截取了我电脑的时间和GPS接收的时间,由图可知,GPS获取的时间为2019年9月1日14:41:01,我电脑右下角为2019年9月1日14:41,所以信息基本准确.

由于GPS收到的是国际标准时间UTC,但是中国北京在东8区,全国计时以北京时间为准,所以我在gps.c文件中的void NMEA_GNRMC_Analysis(nmea_msg *gpsx,unsigned char *buf)函数下,将收到的UTC时间的hour(小时部分)加了8小时,正好为北京时间。
在这里插入图片描述
到此试验成功。

		  Good Game!!!!!!

接下来会推出一系列的关于串口外设使用的分享,有需要的猿们敬请关注!!!!!

以上内容欢迎大家转载引用,标明出处即可!!!!!

你可能感兴趣的:(嵌入式,STM32,UART)