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。
在這裏插入圖片描述
在這裏插入圖片描述
之後再使能串口1、3中斷
在這裏插入圖片描述

4、設置中斷優先級,如圖:

在這裏插入圖片描述
在這裏插入圖片描述

設置中斷優先級在這裏插入圖片描述

5、看原理圖,找到串口對應引腳,如圖:

我這裏是

PA10——>USART1_RX  
PA9——>USART1_TX

PB11——>USART3_RX  
PB10——>USART3_TX

串口1
串口3

6、根據對應引腳設置串口引腳,如圖:

找到PA9、PA10引腳左鍵點擊分別選擇USART1_TX和USART1_RX
(不用擔心選錯選反,針腳的功能是ST公司已經定義好了的)
在這裏插入圖片描述
在這裏插入圖片描述

7、設置時鐘樹,如圖:

這裏會搞的按自己習慣搞,不會搞的默認就好,但是不能有裏面是紅色的框(紅色框就是錯了意思)
在這裏插入圖片描述

8、項目設置,如圖:

紅框裏的按照自己的Keil版本來
在這裏插入圖片描述
個人喜歡把.c/.h文件分開
在這裏插入圖片描述

9、點擊右上角的‘GENERATE CODE’直接生成代碼,如圖:

在這裏插入圖片描述

10、生成代碼後用Keil打開項目並在Application/User中找到usart.c並在/USER CODE BEGIN 0/後添加如下代碼,如圖:

#include <stdio.h>
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代替。
在這裏插入圖片描述

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;i<ilen;i++)	//得到整數部分數據
	{  
		ires+=NMEA_Pow(10,ilen-1-i)*(buf[i]-'0');
	}
	if(flen>5)flen=5;	//最多取5位小數
	*dx=flen;	 		//小數點位數
	for(i=0;i<flen;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;
}	  							 
//分析GPGSV信息
//gpsx:nmea信息結構體
//buf:接收到的GPS數據緩衝區首地址
void NMEA_GPGSV_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,"$GPGSV");
	len=p1[7]-'0';								//得到GPGSV的條數
	posx=NMEA_Comma_Pos(p1,3); 					//得到可見衛星總數
	if(posx!=0XFF)gpsx->svnum=NMEA_Str2num(p1+posx,&dx);
	for(i=0;i<len;i++)
	{	 
		p1=(unsigned char*)strstr((const char *)p,"$GPGSV");  
		for(j=0;j<4;j++)
		{	  
			posx=NMEA_Comma_Pos(p1,4+j*4);
			if(posx!=0XFF)gpsx->slmsg[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;i<len;i++)
	{	 
		p1=(unsigned char*)strstr((const char *)p,"$BDGSV");  
		for(j=0;j<4;j++)
		{	  
			posx=NMEA_Comma_Pos(p1,4+j*4);
			if(posx!=0XFF)gpsx->beidou_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;i<len;i++)
		{
			if(USART3_RX_BUF[i]==0X83)break;
			else if(USART3_RX_BUF[i]==0X84)
			{
				rval=3;
				break;
			}
		}
		if(i==len)rval=2;						//沒有找到同步字符
	}else rval=1;								//接收超時錯誤
    USART3_RX_STA=0;							//清除接收
	return rval;  
}
//配置SkyTra_GPS/北斗模塊波特率
//baud_id:0~8,對應波特率,4800/9600/19200/38400/57600/115200/230400/460800/921600	  
//返回值:0,執行成功;其他,執行失敗(這裏不會返回0了)
unsigned char SkyTra_Cfg_Prt(unsigned char baud_id)
{
	SkyTra_baudrate *cfg_prt=(SkyTra_baudrate *)USART3_TX_BUF;
	cfg_prt->sos=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;j<len;j++)//循環發送數據
	{
		while((USART3->ISR&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<rxlen;i++)USART1_TX_BUF[i]=USART3_RX_BUF[i];	   
 			USART3_RX_STA=0;		   	//啓動下一次接收
			USART1_TX_BUF[i]=0;			//自動添加結束符
			i=0;
			GPS_Analysis(&gpsx,(uint8_t*)USART1_TX_BUF);//分析字符串
			Gps_Msg_Show();				//顯示信息	
			if(upload)printf("\r\n%s\r\n",USART1_TX_BUF);//發送接收到的數據到串口1
 		}
		
		if((lenx%500)==0)
			HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
		lenx++;	

以上數據是將UART3接收到的數據通過轉存區分析後根據分析出的數據通過Gps_Msg_Show()函數通過UART1打印出來。

upload的作用是是否將UART3收到的原始數據轉發給UART1

編譯、下載、看結果

三、實驗驗證

正所謂沒有實驗結果的教程都是耍流氓,出結果

等待約30秒的GPS冷啓動後,收到衛星信息
在這裏插入圖片描述
該格式爲NMEA-0183協議,是美國國家海洋電子協會爲海用電子設備制定的標準格式,目前是GPS/北斗導航設備的標準協議,使用ASCII碼傳遞GPS定位信息。
以上爲upload=1時,在發送了我們需要的信息同時轉發了UART3的接收信息,現在將upload改爲0,看結果:
在這裏插入圖片描述
爲了保證接收信息的正確性,上圖我截取了我電腦的時間和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!!!!!!

接下來會推出一系列的關於串口外設使用的分享,有需要的猿們敬請關注!!!!!

以上內容歡迎大家轉載引用,標明出處即可!!!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章