一、功能目標
1、windows/linux-Ubuntu下串口信息收發(ASCII/HEX)
2、串口相關參數可配置,打開端口後實時生效
3、端口信息根據系統COM口事實刷新3
4、定時自動重發
5、自動換行,log顯示接收時間
6、自定義報文自動回覆(用來與“握手”協議和“心跳”協議對接)
源碼下載請移步https://download.csdn.net/download/white_loong/11933343
可執行文件下載:https://download.csdn.net/download/white_loong/11934068
基本界面如下
開發環境ubuntu14.0.0+Qt 5.9.7、WIN10-64
二、實現(主要以Ubuntu爲主,win10相對簡單可直接移植)
1、查看串口方法
虛擬機加載可移動設備:虛擬機---》可移動設備----》需要加載的串口linux
linux查看串口 這篇博客比較詳細: https://blog.csdn.net/uncle_guo/article/details/80867169
2、建立Qt工程,獲取串口信息
使用的時候在 pro 添加這句導入模塊 QT += serialport
添加串口頭文件
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
3、功能1實現 串口信息收發(ASCII/HEX)
MainWindow添加私有變量
QSerialPort *myCom;
打卡串口相關操作
void MainWindow::opencom()
{
myCom = new QSerialPort("/dev/ttyS0"); //實例化
if (myCom->isOpen()) //串口是否打開?
{
myCom->clear();
myCom->close();
qDebug()<<"Serial port is closed";
}
myCom->open(QIODevice::ReadWrite); //以讀寫方式打開串口
if(myCom ->isOpen()){ //串口打開成功,設置參數
qDebug()<<"open serial success";
on_BaudRateBox_currentIndexChanged(ui->BaudRateBox->currentIndex());
on_DataBitsBox_currentIndexChanged(ui->DataBitsBox->currentIndex());
on_ParityBox_currentIndexChanged(ui->ParityBox->currentIndex());
on_stopbitBox_currentIndexChanged(ui->stopbitBox->currentIndex());
on_flowBox_currentIndexChanged(ui->flowBox->currentIndex());
connect(myCom,SIGNAL(readyRead()),this,SLOT(slotReadReady()));
//連接信號槽 當下位機發送數據QSerialPortInfo 會發送個 readyRead 信號,我們定義個槽void receiveInfo()解析數據
ui->opencom->setText("close");
}else{
qDebug()<<"open serial fail";
}
}
關閉串口相關操作
void MainWindow::closecom()
{
if (myCom->isOpen())
{
myCom->clear();
myCom->close();
myCom->deleteLater();
qDebug()<<"Serial port is closed";
delete myCom;
ui->opencom->setText("open");
}
}
串口接收信息
void MainWindow::slotReadReady()
{
QString buf;
if(ui->RXhexradioButton->isChecked()) //hex格式接收
{
buf = myCom->readAll().toHex()+ "\n";
forresp(buf);
qDebug()<<"buf"<<buf;
}else{ //ASCII格式接收
buf = myCom->readAll();
}
if (!buf.isEmpty()){
reNum += buf.size();
QString str = ui->rxtext->toPlainText();
str = str + LogTime + "\n"+ buf+"\n"; //LogTime 爲系統時間
ui->rxtext->clear();
ui->rxtext->append(str);
ui->label_rxnum->setText("Rx:"+QString::number(reNum, 10)); //顯示發送字符數量
}
buf.clear();
}
串口發送信息
void MainWindow::on_sendpushButton_clicked()
{
QString buf ;
QByteArray sendBuf;
if(ui->TXhexradioButton->isChecked()){ //hex格式發送
buf = ui->txtext->toPlainText().replace(QString(" "),QString(""));
convertStringToHex(buf, sendBuf);
}else{ //ascii格式發送
buf = ui->txtext->toPlainText();
sendBuf = buf.toLatin1();
}
seNum = seNum + sendBuf.length();
myCom->write(sendBuf); //串口發送
ui->label_txnum->setText("Rx:"+QString::number(seNum, 10)); //發送字符數量
qDebug()<<"txnum"<<seNum;
if(ui->txcomboBox->findText(buf)<0) //發送歷史緩存
{
ui->txcomboBox->addItem(buf);
}
if(ui->txcheckBox->isChecked()){ //發送回顯
QString str = ui->rxtext->toPlainText();
if(ui->TXhexradioButton->isChecked()){
str = str + LogTime + "\n" + sendBuf.toHex() + "\n";
}else{
str = str + LogTime + "\n" + sendBuf + "\n";
}
ui->rxtext->clear();
ui->rxtext->append(str);
}
}
4、功能2 串口相關參數可配置,打開端口後實時生效
以波特率爲例,其他參數類推即可
void MainWindow::on_BaudRateBox_currentIndexChanged(int index)
{
if(switchflag) //switchflag用來判斷串口是否打開
{
switch(index)
{
case 0:
myCom->setBaudRate(QSerialPort::Baud9600);
break;
case 1:
myCom->setBaudRate(QSerialPort::Baud19200);
break;
case 2:
myCom->setBaudRate(QSerialPort::Baud38400);
break;
case 3:
myCom->setBaudRate(QSerialPort::Baud115200);
break;
default:
myCom->setBaudRate(QSerialPort::Baud9600);
break;
}
}
}
5、功能3 端口信息根據系統COM口事實刷新
使用foreach每5s獲取一次串口信息,並刷新界面,打開串口時根據combobox值確定
foreach用法參考:https://www.cnblogs.com/lomper/p/3959771.html
void MainWindow::updateiu()
{
static QString lastComName,curComName;
static QStringList portName;
//讀取串口信息
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
portName += info.portName();
}
curComName = portName.join(",");
if(QString::compare(lastComName,curComName)){ //如果同上次獲取的串口信息有差異則刷新
ui->comname->clear();
lastComName = curComName;
ui->comname->addItems(portName);
}
curComName.clear();
portName.clear();
}
//啓動定時器添加頭文件
#include <QTimer>
#include <QTime>
fTimer,on_timer_timeout()需在類中聲明
//初始化界面添加
fTimer=new QTimer(this);
fTimer->setInterval (50) ;//設置定時週期,單位:毫秒
fTimer->start();
connect(fTimer,SIGNAL(timeout()),this,SLOT(on_timer_timeout()));
//定時器溢出處理
void MainWindow::on_timer_timeout()
{
//定時器中斷響
// qDebug()<<"Enter timeout processing function\n";
if(fTimer->isActive()){
fTimer->stop();
updateiu();
fTimer->setInterval (5000) ;
fTimer->start();
}
}
6、功能4 定時自動重發
根據自動重發控件狀態啓動或關閉定時器
void MainWindow::on_autosendBox_pressed()
{
if(ui->autosendBox->isChecked()){
if(sendinterval->isActive()) {
sendinterval->stop();
}
}else{
sendinterval->setInterval(ui->txtimespinBox->value()) ;//設置定時週期,單位:毫秒
sendinterval->start();
connect(sendinterval,SIGNAL(timeout()),this,SLOT(on_sendpushButton_clicked()));
}
}
7、功能5 log顯示接收時間
獲取系統時間,添加頭文件#include <qdatetime.h>
void MainWindow::real_timer()
{
QDateTime timeNow=QDateTime::currentDateTime();
QString str = timeNow.toString("MM-dd hh:mm:ss dddd");
ui->lable_time->setText(str);
LogTime = timeNow.toString("MM-dd hh:mm:ss"); //LogTime 爲全局變量,在顯示時添加即可
}
8、功能6 自定義報文自動回覆(用來與“握手”協議和“心跳”協議對接)
接收到固定報文後,根據用戶自定義信息自動回覆相應信息
比如 串口 發送0x23 --------------》設備
0x45 《--------------設備回覆
收到0x45 回覆0xc0 0x00 0xc0------------------》設備
0x67 《--------------設備回覆
收到0x67 回覆0x89------------------》設備
思路:使用ListWidget存儲固定報文和響應信息,可以手動增加刪除,專有按鍵管理啓動關閉此功能
//list ack fromat
//RECV+2*~~+recv.len(XX)+2*~~+recv len<99
//2*~~ + RESP+2*~~+resp.len+2*~~+resp
//新增自定義報文
void MainWindow::on_addpushButton_clicked()
{
QString buf,buf1;
buf = ui->recvtextEdit->toPlainText().replace(QString(" "),QString(""));
buf1= ui->resptextEdit->toPlainText().replace(QString(" "),QString(""));
buf = "RECV "+ QString::number(buf.length()) +" "+ buf ;
buf1= " RESP "+QString::number(buf1.length()) +" "+buf1;
ui->listWidget->addItem(buf);
ui->listWidget->addItem(buf1);
listnum++;
qDebug()<<"add list"<<listnum;
}
//刪除自定義報文
void MainWindow::on_deletepushButton_clicked()
{
if(ui->listWidget->currentItem() != Q_NULLPTR){
int i= i=ui->listWidget->currentRow();;
QString test = ui->listWidget->item(ui->listWidget->currentRow())->text();
if(test.indexOf("RECV")<0) {
i=ui->listWidget->currentRow()-1;
}
else{
i=ui->listWidget->currentRow();
}
QListWidgetItem *item1 = ui->listWidget->takeItem(i+1);
QListWidgetItem *item = ui->listWidget->takeItem(i);
delete item1;
delete item;
if(listnum) listnum--;
}
qDebug()<<"delete list"<<listnum;
}
//啓動、關閉
void MainWindow::on_startpushButton_clicked()
{
if(startorclose){
//free time stop
startorclose = 0;
ui->startpushButton->setText("start");
}else{
//start time start
ui->startpushButton->setText("stop");
ui->RXhexradioButton->setChecked(1);
startorclose = 1;
}
}
//響應,此函數在接收響應函數調用
//解析對比報文,迴應
void MainWindow::forresp(const QString &buff)
{
qDebug()<<"buff"<<buff.toLatin1();
if(listnum == 0 || startorclose ==0 ) return;
QString itemText,resp;
int recvlen,index=9,resplen,index1=10;
for (int j = 0; j < ui->listWidget->count(); j=j+2)
{
index=9;
index1=11;
itemText = ui->listWidget->item(j)->text();
recvlen = itemText.mid(6,2).toInt();
if(recvlen>9) index = index+1;
QString recv = itemText.mid(index,recvlen)+"\n";
if(!QString::compare(recv,buff)){
qDebug()<<"ok";
ui->listWidget->item(j)->setBackgroundColor(255);
resp = ui->listWidget->item(j+1)->text();
resplen = resp.mid(8,2).toInt();
if(resplen>9) index1 = index1+1;
resp = resp.mid(index1,resplen);
QByteArray sendBuf;
convertStringToHex(resp, sendBuf);
myCom->write(sendBuf);
QString str = ui->rxtext->toPlainText();
str = str + LogTime + " autoresponse "+ "\n" + resp + "\n";
ui->rxtext->clear();
ui->rxtext->append(str);
}
}
}
效果如下圖:log有autoresponse即爲自動回覆報文,藍色部分表示收到報文對比通過,立即發送下方響應報文