一、硬件介绍
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模块连线:
三、软件核心代码
#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,7); 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); } } /* 函数功能: 根据逗号位置提取数据 函数参数: char *gps_src GPS源字符串地址 char *buff 存放提取的数据 char *find_head 查找的GPS数据头 例如:$GNVTG int cnt 逗号的偏移量 返回值:提取的字节数。 */ 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; } /* * 计算GPS数据指定数据的校验和 * char *src_str GPS源字符串 * char *find_str GPS数据头 例如:$GNVTG * 返回值: -1表示失败 0表示成功 */ int UART_ReadWriteThread::GPS_GetCheckSum(const char *src_str,const char *find_str) { const char *tmp_p; /*1. 开始解析数据*/ 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"); }