本次使用STM32F4的USART1对GPS模块进行驱动,并且将GPS的时间、经纬度通过串口打印出来。
gps模块与接线图
注意:GPS 模块需放到窗户边/阳台,否则可能收不到 GPS 信号。
1、GPS驱动配置(gps.c)
#include "includes.h"
//定义接收信息结构体变量
struct GPS_Data Save_Data;
/*****************************
函数名:clrStruct
函数参数:无
函数返回值:无
函数功能:清除接收结构体信息
函数描述:
*****************************/
void clrStruct(void)
{
Save_Data.isGetData = false;
Save_Data.isParseData = false;
Save_Data.isUsefull = false;
memset(Save_Data.GPS_Buffer, 0, 200); //清空
memset(Save_Data.UTCTime, 0, 20);
memset(Save_Data.latitude, 0, 20);
memset(Save_Data.N_S, 0, 2);
memset(Save_Data.longitude, 0, 20);
memset(Save_Data.E_W, 0, 2);
}
/*****************************
函数名:parseGpsBuffer
函数参数:无
函数返回值:无
函数功能:解析接收结构体信息
函数描述:
*****************************/
void parseGpsBuffer(void)
{
char *subString=NULL;
char *subStringNext=NULL;
char i = 0;
if (Save_Data.isGetData)
{
Save_Data.isGetData = false;//串口中断接收一次。解析一次
// printf(Save_Data.GPS_Buffer);
//解析数据信息
for (i = 0 ; i <= 9 ; i++)
{
if (i == 0)
{
//如果接收到的数据信息中没有,
if ((subString = strstr(Save_Data.GPS_Buffer, ",")) == NULL)
errorLog(1); //解析错误
}
else
{
subString++;//第一个,出现的位置的下一个字符数据
if ((subStringNext = strstr(subString, ",")) != NULL)//下一个,出线的位置
{
char usefullBuffer[2];
switch(i) //提取两个,之间的字符数据
{
case 1:memcpy(Save_Data.UTCTime, subString, subStringNext - subString);break; //获取UTC时间
case 2:memcpy(usefullBuffer, subString, subStringNext - subString);break; //定位信息是否有效
case 3:memcpy(Save_Data.latitude, subString, subStringNext - subString);break; //获取纬度信息
case 4:memcpy(Save_Data.N_S, subString, subStringNext - subString);break; //获取N/S
case 5:memcpy(Save_Data.longitude, subString, subStringNext - subString);break; //获取经度信息
case 6:memcpy(Save_Data.E_W, subString, subStringNext - subString);break; //获取E/W
case 7:break;
case 8:break;
case 9:memcpy(Save_Data.UTCDay, subString, subStringNext - subString);break; //获取UTC日期
default:break;
}
subString = subStringNext; //指向下一个,后数据信息
Save_Data.isParseData = true;
if(usefullBuffer[0] == 'A')
Save_Data.isUsefull = true;
else if(usefullBuffer[0] == 'V')
Save_Data.isUsefull = false;
}
else
{
errorLog(2); //解析错误
}
}
}
}
}
/*****************************
函数名:printGpsBuffer
函数参数:无
函数返回值:无
函数功能:打印接收的数据信息
函数描述:
*****************************/
void printGpsBuffer(void)
{
u8 year,month,day,hour,minute,second,week;
double latitude,longitude;
if (Save_Data.isParseData)
{
Save_Data.isParseData = false;
//获得时间
hour =(Save_Data.UTCTime[0]-'0')*10+(Save_Data.UTCTime[1]-'0')+8;
minute =(Save_Data.UTCTime[2]-'0')*10 +(Save_Data.UTCTime[3]-'0');
second =(Save_Data.UTCTime[4]-'0')*10 +(Save_Data.UTCTime[5]-'0');
if(Save_Data.isUsefull)
{
Save_Data.isUsefull = false;
//获得纬度
latitude= atof( Save_Data.latitude);
latitude/=100;
//获得经度
longitude= atof( Save_Data.longitude);
longitude/=100;
//获得日期
year =(Save_Data.UTCDay[4]-'0')*10+(Save_Data.UTCDay[5]-'0');
month =(Save_Data.UTCDay[2]-'0')*10 +(Save_Data.UTCDay[3]-'0');
day =(Save_Data.UTCDay[0]-'0')*10 +(Save_Data.UTCDay[1]-'0');
printf("当前时间: 20%d-%d-%d %d:%d:%d\r\n",year,month,day,hour,minute,second);
printf("当前纬度 %s:%f°\r\n",Save_Data.N_S,latitude);
printf("当前经度 %s:%f°\r\n",Save_Data.E_W,longitude);
}
else
{
printf("GPS DATA is not usefull!\r\n");
}
}
}
void errorLog(int num)
{
while (1)
{
printf("ERROR%d\r\n",num);
}
}
2、GPS函数声明(gps.h)
#ifndef GPS_H
#define GPS_H
#include "stm32f4xx.h"
#define false 0
#define true 1
//GPS接收帧格式结构体
struct GPS_Data
{
char GPS_Buffer[200]; //整体数据长度
char isGetData; //是否获取到GPS数据
char isParseData; //是否解析完成
char UTCTime[20]; //UTC时间
char latitude[20]; //纬度
char N_S[2]; //N/S
char longitude[20]; //经度
char E_W[2]; //E/W
char isUsefull; //定位信息是否有效
char UTCDay[20]; //UTC日期
};
extern struct GPS_Data Save_Data;
void clrStruct(void);
void errorLog(int num);
void parseGpsBuffer(void);
void printGpsBuffer(void);
#endif
3、串口驱动配置(usart1.c)注意串口中断函数
#include "includes.h"
/*****************************
函数名:USART1_init
函数参数:
int boand : TX/RX 的波特率
函数返回值:无
函数功能:实现开发板对应USART1的发送/接收初始化配置
函数描述:
USART1_TX :PA9
USART1_RX : PA10
*****************************/
void USART1_init(int boand )
{
int OVER8 =0 ;
float USARTDIV ;//放入波特率寄存器中的数值
int DIV_Fraction; //表示USARTDIV的小数部分
int DIV_Mantissa ;//表示USARTDIV的整数部分
/*****对USART1的TX RX管脚 复用配置******/
//打开PA的外设时钟
RCC->AHB1ENR |=(1<<0);
/******USART1_TX PA9复用功能配置*****/
//模式寄存器
GPIOA->MODER &=~(0X3<<18); //清零
GPIOA->MODER |=(0X2<<18); //复用功能
//具体的复用功能配置
GPIOA->AFR[1] &=~(0XF<<4); //清零
GPIOA->AFR[1] |=(0X7<<4); //复用成USART1_TX
/******USART1_RX PA10复用功能配置*****/
//模式寄存器
GPIOA->MODER &=~(0X3<<20); //清零
GPIOA->MODER |=(0X2<<20); //复用功能
//具体的复用功能配置
GPIOA->AFR[1] &=~(0XF<<8); //清零
GPIOA->AFR[1] |=(0X7<<8); //复用成USART1_RX
/******USART1的发送/接收控制功能配置****/
//打开USART1的外设时钟
RCC->APB2ENR |=(1<<4);
//串口工作使能
USART1->CR1 |=(1<<13);
//禁止奇偶校验
USART1->CR1 &=~(1<<10);
//发送器使能
USART1->CR1 |=(1<<3);
//接收器使能
USART1->CR1 |=(1<<2);
//不发送断路字符
USART1->CR1 &=~(1<<0);
#ifdef MY_OVER8 //16倍
USART1->CR1 &=~(1<<15);
OVER8 =0;
#else //8倍
USART1->CR1 |=(1<<15);
OVER8 =1;
#endif
/*****TX/RX的波特率配置*****/
USARTDIV =(float)84000000/(8*(2-OVER8)*boand);
DIV_Mantissa =USARTDIV;
DIV_Fraction =(USARTDIV-DIV_Mantissa)*8*(2-OVER8);
USART1->BRR = DIV_Mantissa<<4 | DIV_Fraction;
/*******接收中断相关配置 *******/
//接收中断使能
USART1->CR1 |=(1<<5);
//USART1 NVIC中断配置 分组值: 7-2
MY_NVIC_init(7-2,1,1,USART1_IRQn);
}
/*****************************
函数名:USART1_Sendbyte
函数参数:
u8 data :要发送的数据
函数返回值:无
函数功能:实现串口1发送一个字节数据
函数描述:
*****************************/
void USART1_Sendbyte(u8 data)
{
//等待上次数据发送完
while((USART1->SR &(1<<6))==0)
{
;
}
//发送该次数据
USART1->DR =data;
}
/*****************************
函数名:USART1_Sendstring
函数参数:
u8 *buf : 字符指针,
表示要发送字符串的首地址
函数返回值:无
函数功能:实现串口1发送一个字符串
函数描述:
*****************************/
void USART1_Sendstring(u8 *buf)
{
while(*buf !='\0')
{
USART1_Sendbyte(*buf);
buf++;
}
USART1_Sendbyte('\r');
USART1_Sendbyte('\n');
}
/*****************************
函数名:USART1_Receivebyte
函数参数:无
函数返回值:u8 data :接收到的数据
函数功能:实现串口1接收一个字节数据
函数描述:
*****************************/
u8 USART1_Receivebyte(void)
{
u8 data;
//等待接收到数据
while((USART1->SR &(1<<5))==0)
{
;
}
//读出接收到的数据
data =USART1->DR;
//把接收到的数据返回出去
return data;
}
/*****************************
函数名:USART1_Receivestring
函数参数:u8 *buf :接收到的数据
函数返回值:无
函数功能:实现串口1接收一个字符串
函数描述:
用户自定义结束标志
*****************************/
void USART1_Receivestring(u8 *buf)
{
u8 ch;
while(1)
{
ch =USART1_Receivebyte();
//判断接收到的是否是结束标志
if(ch ==' ' || ch =='\r' || ch=='\n')
{
break;
}
*buf =ch;
buf++;
}
*buf ='\0';//添加一个结束标志
}
#pragma import(__use_no_semihosting_swi) //取消半主机状态
struct __FILE { int handle; /* Add whatever you need here */ };
FILE __stdout;
int fputc(int ch, FILE *f) {
USART1_Sendbyte(ch);
return (ch);
}
int ferror(FILE *f) {
/* Your implementation of ferror */
return EOF;
}
void _ttywrch(int ch) {
USART1_Sendbyte(ch);
}
u16 point1 = 0;
char USART_RX_BUF[200]; //接收缓冲,
//中断服务函数
void USART1_IRQHandler(void)
{
u8 Res;
Res =USART1->DR; //读取接收到的数据
if(Res == '$') //重新开始接收数据
{
point1 = 0;
}
USART_RX_BUF[point1++] = Res;
//确定是否收到"GPRMC"这一帧数据
if(USART_RX_BUF[0] == '$' && USART_RX_BUF[4] == 'M' && USART_RX_BUF[5] == 'C')
{
if(Res == '\n') //接收到该帧 数据的最后一个
{
memset(Save_Data.GPS_Buffer, 0, 100); //清空
memcpy(Save_Data.GPS_Buffer, USART_RX_BUF, point1); //保存数据
Save_Data.isGetData = true;
point1 = 0;
memset(USART_RX_BUF, 0, 200); //清空
}
}
if(point1 >= 200)
{
point1 = 200;
}
}
4、串口函数声明(usart1.h)
#ifndef USART1_H
#define USART1_H
#include "stm32f4xx.h"
//如果采用16倍过采样,定义该宏
//如果采用8倍过采样,屏蔽冠宏
#define MY_OVER8
void USART1_init(int boand );
void USART1_Sendbyte(u8 data);
void USART1_Sendstring(u8 *buf);
u8 USART1_Receivebyte(void);
void USART1_Receivestring(u8 *buf);
#endif
5、主函数(main.c)
#include "includes.h"
int main()
{
//串口初始化
USART1_init(9600);
//清除接受结构体信息
clrStruct();
while(1)
{
//解析数据
parseGpsBuffer();
//打印数据
printGpsBuffer();
}
return 0;
}