一、功能目标
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即为自动回复报文,蓝色部分表示收到报文对比通过,立即发送下方响应报文