一、硬件介绍
GPS模块型号: 中科微电子ATGM336H-5N 系列模块
ATGM336H-5N 系列模块是 9.7X10.1 尺寸的高性能 BDS/GNSS 全星座定位导航模块系列的总称。该系列模块产品都是基于中科微第四代低功耗 GNSS SOC 单芯片—AT6558,支持多种卫星导航系统,包括中国的 BDS(北斗卫星导航系统),美国的 GPS,俄罗斯的 GLONASS,欧盟的 GALILEO,日本的 QZSS以及卫星增强系统 SBAS(WAAS,EGNOS,GAGAN,MSAS)。AT6558 是一款真正意义的六合一多模卫星导航定位芯片,包含 32 个跟踪通道,可以同时接收六个卫星导航系统的 GNSS 信号,并且实现联合定位、导航与授时。
二、软件功能介绍
解析GPS模块接收的数据,得到经纬度和速度。
软件支持GPS模式配置、指定的语句输出配置。
软件设计思路:
串口在子线程里完成数据接收,将解析的数据保存到全局类中,全局类里加了读写锁,防止多线程读写全局变量出现问题。
主UI线程里负责数据显示,解析的结果使用定时器2秒钟显示一次。
软件下载地址: https://download.csdn.net/download/xiaolong1126626497/12721684
软件运行效果:
GPS模块连线:
三、软件核心代码
3.1 uart_code.h
#ifndef UART_CODE_H
#define UART_CODE_H
#include <QSerialPort>
#include <QObject>
#include <QThread>
#include <QDebug>
#include <QString>
#include "config.h"
class OBD_Data
{
public:
QString V; //电瓶电压
QString R; //转速
QString S; //速度
QString P; //绝对气门开度
QString O; //发动机负荷
QString C; //冷却液温度
QString L; //油量
QString M; //此次行程米
QString F; //此次油耗L
QString XM; //瞬时油耗、百公里油耗
QString T; //运行时间
//QString E; //发动机状态 0熄火 1点火 2自动启动
QString A; //急加速次数
QString B; //急刹车次数
QString D; //故障码个数
QString GX;//X
QString GY;//Y
QString GZ;//Z
QString data;
};
class GPS_Data
{
private:
double lat; //纬度
double lng; //经度
QString speed; //速度
QReadWriteLock lock; //读写锁
QString save_src_data; //原始数据
public:
QString Data;
//构造函数
GPS_Data()
{
lat=0.0;
lng=0.0;
speed="0";
save_src_data="";
}
//读取纬度
double get_lat(void)
{
lock.lockForRead();
double tmp_lat=lat;
lock.unlock();
return tmp_lat;
}
//设置纬度
void set_lat(double tmp_lat)
{
lock.lockForWrite();
lat=tmp_lat;
lock.unlock();
}
//读取经度
double get_lng(void)
{
lock.lockForRead();
double tmp_lng=lng;
lock.unlock();
return tmp_lng;
}
//设置经度
void set_lng(double tmp_lng)
{
lock.lockForWrite();
lng=tmp_lng;
lock.unlock();
}
//读取速度
QString get_speed(void)
{
lock.lockForRead();
QString tmp_speed=speed;
lock.unlock();
return tmp_speed;
}
//设置速度
void set_speed(QString tmp_speed)
{
lock.lockForWrite();
speed=tmp_speed;
lock.unlock();
}
//读取原始数据
QString get_src_data(void)
{
lock.lockForRead();
QString tmp_speed=save_src_data;
lock.unlock();
return tmp_speed;
}
//设置原始数据
void set_src_data(QString tmp_save_src_data)
{
lock.lockForWrite();
save_src_data+=tmp_save_src_data;
if(save_src_data>1024)save_src_data.clear();
lock.unlock();
}
//清除原始数据
void set_src_clear(void)
{
lock.lockForWrite();
save_src_data.clear();
lock.unlock();
}
};
class UART_ReadWriteThread:public QObject
{
Q_OBJECT
public:
QByteArray byte_all;
UART_ReadWriteThread(QObject* parent=nullptr):QObject(parent){}
~UART_ReadWriteThread();
//成员变量
QSerialPort *UART_Config=nullptr; //串口---数据接收串口
int GPS_GetData(const char *gps_src,const char *find_head,char *buff,int cnt);
int GPS_GetCheckSum(const char *src_str,const char *find_str);
public slots:
void UART_ReadUasrtData(void);
void run();
void Open_UART(void);
void Close_UART(void);
void UART_Send(QString text);
signals:
void LogSend(QString text);
void SendUartState(qint8 state); //发送串口状态 true 表示连接 否则表示断开
};
#endif // UART_CODE_H
3.2 uart_code.cpp
#include "uart_code.h"
#include "config.h"
QString current_SerialPort=""; //当前串口端口号
qint32 current_SerialBaudRate=0; //当前波特率
class OBD_Data obd_data;
class GPS_Data gps_data;
UART_ReadWriteThread::~UART_ReadWriteThread()
{
}
void UART_ReadWriteThread::run()
{
//串口配置
if(UART_Config)
{
UART_Config->close();
delete UART_Config;
UART_Config=nullptr;
}
UART_Config =new QSerialPort;
//打开串口设备
Open_UART();
}
//当串口打不开的时候,间隔一段时间再次重新测试
void UART_ReadWriteThread::Open_UART(void)
{
//配置串口属性
UART_Config->setBaudRate(current_SerialBaudRate); //默认波特率
UART_Config->setDataBits(QSerialPort::Data8); //数据位
UART_Config->setParity(QSerialPort::NoParity); //奇偶校验
UART_Config->setStopBits(QSerialPort::OneStop);//停止位
UART_Config->setFlowControl(QSerialPort::NoFlowControl); //流控开关
UART_Config->setPortName(current_SerialPort); //COM的名称
//1 数据传输串口连接 2 数据传输串口断开
if(!(UART_Config->open(QIODevice::ReadWrite))) //打开的属性权限
{
LogSend("数据传输串口打开失败.\n");
SendUartState(2); //发送信号 通知状态改变
}
else
{
LogSend("数据传输串口打开成功.\n");
SendUartState(1); //发送信号 通知状态改变
//关联数据读信号
connect(UART_Config,SIGNAL(readyRead()),this,SLOT(UART_ReadUasrtData()),Qt::QueuedConnection);
}
}
//读取串口数据
void UART_ReadWriteThread::UART_ReadUasrtData()
{
QString byte;
byte=UART_Config->readAll();
LogSend(byte);
gps_data.Data+=byte;
//完整的1帧VTG--RMC数据在109字节左右
//if(gps_data.Data.size()>=200)
if(gps_data.Data.count("\r\n")>=2 && gps_data.Data.count("$GNRMC")>=1 && gps_data.Data.count("$GNVTG")>=1)
{
//保存原始数据
gps_data.set_src_data(gps_data.Data);
char *gps_p;
char gps_tmp_buff[50];
gps_p=gps_data.Data.toLocal8Bit().data();
//计算$GNRMC校验和
if(GPS_GetCheckSum(gps_p,"$GNRMC")==0)
{
//qDebug()<<"$GNRMC 校验和计算成功.\n";
// LogSend("$GNRMC 校验和计算成功\n");
//提取定位状态数据
GPS_GetData(gps_p,"$GNRMC",gps_tmp_buff,2);
//判断定位数据是否有效
if(gps_tmp_buff[0]=='A')
{
SendUartState(3); //发送信号 通知状态改变 定位成功
//qDebug()<<"定位成功.";
//提取纬度
GPS_GetData(gps_p,"$GNRMC",gps_tmp_buff,3);
QString lat=gps_tmp_buff; //得到纬度
//提取经度
GPS_GetData(gps_p,"$GNRMC",gps_tmp_buff,5);
QString lng=gps_tmp_buff; //得到经度
// qDebug()<<"当前纬度:"<<lat;
//qDebug()<<"当前经度:"<<lng;
unsigned int int_data;
double s_Longitude,s_latitude;
//转换纬度
s_latitude=lat.toDouble();
s_latitude=s_latitude/100;
int_data=s_latitude;//得到纬度整数部分
s_latitude=s_latitude-int_data;//得到纬度小数部分
s_latitude=(s_latitude)*100;
gps_data.set_lat(int_data+(s_latitude/60.0)); //得到转换后的值
//转换经度
s_Longitude=lng.toDouble();
s_Longitude=s_Longitude/100;
int_data=s_Longitude;//得到经度整数部分
s_Longitude=s_Longitude-int_data; //得到经度小数部分
s_Longitude=s_Longitude*100;
//gai guo le
gps_data.set_lng(int_data+(s_Longitude/60.0));
}
else
{
SendUartState(4); //发送信号 通知状态改变 定位失败
//GPS经纬度置0
gps_data.set_lat(0.0);
gps_data.set_lng(0.0);
}
}
else
{
//GPS经纬度置0
gps_data.set_lat(0.0);
gps_data.set_lng(0.0);
}
//计算GNVTG校验和
if(GPS_GetCheckSum(gps_p,"$GNVTG")==0)
{
//提取速度
GPS_GetData(gps_p,"$GNVTG",gps_tmp_buff,5);
gps_data.set_speed(gps_tmp_buff);
}
else
{
gps_data.set_speed("0");//当前速度置0
}
//清空数据
gps_data.Data.clear();
}
}
//关闭串口
//1 数据传输串口连接 2 数据传输串口断开
void UART_ReadWriteThread::Close_UART(void)
{
SendUartState(2); //发送信号 通知状态改变
if(UART_Config->isOpen())
{
UART_Config->close();
}
}
//发送数据
void UART_ReadWriteThread::UART_Send(QString text)
{
//qDebug()<<text;
//如果串口处于打开状态就发送数据
if(UART_Config->isOpen())
{
QByteArray byte=text.toLocal8Bit();
UART_Config->write(byte);
}
}
int UART_ReadWriteThread::GPS_GetData(const char *gps_src,const char *find_head,char *buff,int cnt)
{
const char *p;
int number=0; //提取的数量
int a=0;
p=strstr(gps_src,find_head);
if(p!=nullptr)
{
//查找逗号的位置
while(*p!='\0')
{
if(*p==',')a++; //记录逗号的数量
if(a==cnt)break; //逗号找成功了
p++;
}
p++; //跨过当前逗号
//提取数据
while(*p!='\0')
{
if(*p==',')break; //遇到逗号停止提取数据
buff[number]=*p;
p++;
number++;
}
buff[number]='\0';
}
return number;
}
int UART_ReadWriteThread::GPS_GetCheckSum(const char *src_str,const char *find_str)
{
const char *tmp_p;
tmp_p=strstr(src_str,find_str);
if(tmp_p!=nullptr)
{
unsigned char data=0;
tmp_p++;
//计算校验和
while(*tmp_p!='*' && *tmp_p!='\0')
{
data^=*tmp_p;
tmp_p++;
}
QString sum_str=QString::number(data,16);
QString sum_val;
//提取校验和
sum_val[0]=*(tmp_p+1);
sum_val[1]=*(tmp_p+2);
//qDebug()<<"sum_val:"<<sum_val;
//qDebug()<<"sum_str:"<<sum_str;
//判断校验和,不区分大小写
if(sum_str.compare(sum_val,Qt::CaseInsensitive)==0)
{
return 0; //校验正确
}
}
return -1;
}
3.3 widget.h代码
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QSerialPortInfo>
#include <QSerialPort>
#include <QPlainTextEdit>
#include <QScrollBar>
#include <QTimer>
#include <QThread>
#include <QDebug>
#include "uart_code.h"
#include "config.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = NULL);
~Widget();
void GetDeviceList();
QList<QSerialPortInfo> usart_device_list;
QSerialPort *UART_Config; //串口
QTimer timer;
qint8 connect_state=0; //串口连接状态
//串口线程
UART_ReadWriteThread uart_WorkClass; //串口读写-工作函数
QThread uart_Workthread; //串口读写--线程
private slots:
void Log_Text_Display(QString text);
void Log_Text_Display2(QString text);
void UART_State_Display(qint8 state); //串口状态显示
void update();
void on_pushButton_2_clicked();
void on_pushButton_clicked();
void on_pushButton_send_data_clicked();
void on_pushButton_mode_set_clicked();
void on_pushButton_open_all_clicked();
void on_pushButton_close_all_clicked();
void on_pushButton_open_rmc_clicked();
void on_pushButton_open_vtg_clicked();
signals:
void Start_Uart_Read();
void Uart_SendData(QString text);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
3.4 widget.cpp代码
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("GPS数据解析工具");
GetDeviceList();
connect(&timer, SIGNAL(timeout()), this, SLOT(update()));
timer.start(2000);
//串口初始化
//连接串口的日志信息
connect(&uart_WorkClass,SIGNAL(LogSend(QString)),this,SLOT(Log_Text_Display(QString)));
connect(&uart_WorkClass,SIGNAL(SendUartState(qint8)),this,SLOT(UART_State_Display(qint8)));
//连接串口初始化函数
connect(this,SIGNAL(Start_Uart_Read()),&uart_WorkClass,SLOT(run()));
//连接串口发送函数
connect(this,SIGNAL(Uart_SendData(QString)),&uart_WorkClass,SLOT(UART_Send(QString)));
//将串口工作对象移动到子线程里工作
uart_WorkClass.moveToThread(&uart_Workthread);
}
Widget::~Widget()
{
delete ui;
}
void Widget::GetDeviceList()
{
ui->comboBox->clear();
//获取系统串口设备
usart_device_list=QSerialPortInfo::availablePorts();
for(int i=0;i<usart_device_list.count();i++)
{
ui->comboBox->addItem(usart_device_list.at(i).portName());
}
}
void Widget::on_pushButton_2_clicked()
{
GetDeviceList();
}
void Widget::on_pushButton_clicked()
{
current_SerialPort=ui->comboBox->currentText(); //当前串口端口号
current_SerialBaudRate=ui->comboBox_baud->currentText().toInt(); //当前波特率
if(ui->pushButton->text()=="打开串口")
{
if(current_SerialPort.isEmpty()||current_SerialBaudRate<=0)
{
QMessageBox::information(this,"提示","请先选择串口COM和波特率\n在菜单栏的设置页面找到串口设置按钮.",
QMessageBox::Ok,QMessageBox::Ok);
}
else
{
ui->pushButton->setText("关闭串口");
uart_Workthread.start(); //启动串口线程
Start_Uart_Read(); //发送信号启动串口工作
}
}
else
{
uart_WorkClass.Close_UART(); //关闭串口
uart_Workthread.quit(); //退出线程
uart_Workthread.wait(); //等待线程退出
ui->pushButton->setText("打开串口");
}
}
void Widget::Log_Text_Display(QString text)
{
QPlainTextEdit *plainTextEdit_log=ui->plainTextEdit;
//设置光标到文本末尾
plainTextEdit_log->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
//当文本数量超出一定范围就清除
if(plainTextEdit_log->toPlainText().size()>4096)
{
plainTextEdit_log->clear();
}
plainTextEdit_log->insertPlainText(text);
//移动滚动条到底部
QScrollBar *scrollbar = plainTextEdit_log->verticalScrollBar();
if(scrollbar)
{
scrollbar->setSliderPosition(scrollbar->maximum());
}
}
void Widget::Log_Text_Display2(QString text)
{
QPlainTextEdit *plainTextEdit_log=ui->plainTextEdit2;
//设置光标到文本末尾
plainTextEdit_log->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
//当文本数量超出一定范围就清除
if(plainTextEdit_log->toPlainText().size()>4096)
{
plainTextEdit_log->clear();
}
plainTextEdit_log->insertPlainText(text);
//移动滚动条到底部
QScrollBar *scrollbar = plainTextEdit_log->verticalScrollBar();
if(scrollbar)
{
scrollbar->setSliderPosition(scrollbar->maximum());
}
}
void Widget::update()
{
QString text="";
QString gps_text=gps_data.get_src_data();
gps_data.set_src_clear(); //清除保存的原始数据
gps_text=gps_text.remove("\r");
gps_text=gps_text.remove("\n");
//串口连接成功的情况下才需要打印
if(connect_state==1)
{
text="GPS:";
text+=tr("纬度=%1,").arg(gps_data.get_lat(),0,'g',13); //纬度
text+=tr("经度=%1,").arg(gps_data.get_lng(),0,'g',13); //经度
text+=tr("速度=%1\n").arg(gps_data.get_speed());//速度
Log_Text_Display2(text);
text+=tr("原始数据:%1\n").arg(gps_text);//原始数据
Log_Text_Display2(text);
}
}
//串口状态显示
//1 数据传输串口连接 2 数据传输串口断开
void Widget::UART_State_Display(qint8 state)
{
if(state==1)
{
connect_state=state;
Log_Text_Display("串口连接\n");
}
if(state==2)
{
connect_state=state;
Log_Text_Display("串口断开\n");
}
if(state==3) //GPS定位成功
{
ui->label_gps_stat->setText("GPS定位状态: 定位成功");
}
if(state==4) //GPS定位失败
{
ui->label_gps_stat->setText("GPS定位状态: 正在定位中..请稍等");
}
}
//发送数据
void Widget::on_pushButton_send_data_clicked()
{
QString text=ui->lineEdit_send_data->text();
if(ui->checkBox->isChecked())
{
text+="\r\n";
Uart_SendData(text);
}
else
{
Uart_SendData(text);
}
}
void Widget::on_pushButton_mode_set_clicked()
{
Uart_SendData("$PCAS11,3*1E\r\n");
}
void Widget::on_pushButton_open_all_clicked()
{
Uart_SendData("$CCRMO,GGA,4,1*38\r\n");
}
void Widget::on_pushButton_close_all_clicked()
{
Uart_SendData("$CCRMO,GGA,3,1*3F\r\n");
}
void Widget::on_pushButton_open_rmc_clicked()
{
Uart_SendData("$CCRMO,RMC,2,1*23\r\n");
}
void Widget::on_pushButton_open_vtg_clicked()
{
Uart_SendData("$CCRMO,VTG,2,1*3A\r\n");
}
下面公众号有全套的单片机、QT、C++、C语言、物联网相关的教程,欢迎关注: