QT开发之旅三串口设备调试工具
这里首先说明一下,这个为什么叫串口设备调试工具而不是串口调试工具,是因为这个工具比网络上的串口调试工具多出了一些真实需要的用来调试设备的功能,首先一点就是大部分的串口调试工具收到数据都是立即返回,这样的数据都是连着的,头一条数据和后一条数据头尾相连,对于调试着来说要看数据非常麻烦,而且在不断有数据过来时,要停下来看数据除非关闭串口,而不能在打开串口的时候看数据,因为不断有数据过来冲掉前面的数据显示,甚至有些还不能最大化等等情况,这个工具是根据将近四年的与硬件通信这块开发工具的切身实际需求量身定做的,还能模拟设备立即回复数据。之前很多QT开发的版本源码分成两套,一套在windows下编译,一套在linux下编译,甚至麻烦,虽然QT5.1之后的版本自带了串口通信类,本人测试过,貌似还有小问题,在快速收发数据时容易卡住,这次带来的源码不用做任何改动即可在多个平台编译。
项目名称:串口设备调试工具
开发环境:WIN7+QT4.7+QT CREATOR2.8+MINGW
已编译通过测试平台:XP、Win7、ubuntu、tiny210
技术实现:通过第三方串口通信类,解析协议并作出处理
基本功能:
1:支持16进制数据发送与接收。
2:支持windows下COM9以上的串口通信。
3:自动加载对应操作系统串口号。
4:实时显示收发数据字节大小以及串口状态。
高级功能:
1:可自由管理需要发送的数据,每次只要从下拉框中选择数据即可,无需重新输入数据。
2:可模拟设备回复数据,需要在主界面开启模拟设备回复数据。当接收到设置好的指令时,立即回复设置的回复指令。例如指定收到0x16 0x00 0xFF 0x01需要回复0x16 0x00 0xFE 0x01,则只需要在SendData.txt中添加一条数据16 00 FF 01:16 00 FE 01即可。
3:可定时发送数据和保存数据到文本文件:,默认间隔5秒钟,可更改间隔时间。
4:在不断接收到大量数据时,可以暂停显示数据来查看具体数据,后台依然接收数据但不处理,无需关闭串口来查看已接收到的数据。
5:每次收到的数据都是完整的一条数据,而不是脱节的,做了延时处理。
6:一套源码随处编译,无需更改串口通信类,已在XP/WIN7/UBUNTU/ARMLINUX系统下成功编译并运行。
如果有更好的建议或者意见,请Q我(517216493),谢谢!
运行截图:
粗略步骤:
第一步:布局好界面,控件命名好,建议用pascal命名法。
第二步:准备unix和windows串口通信第三方类qextserialport.h、qextserialport.cpp、qextserialport_global.h、qextserialport_p.h、qextserialport_unix.cpp、qextserialport_win.cpp。
导入到工程,在pro文件中这样写:
QT += core gui
TARGET = mySerialPortTools
TEMPLATE = app
SOURCES += main.cpp\
qextserialport.cpp\
frmmain.cpp
HEADERS += frmmain.h \
qextserialport_global.h \
qextserialport.h \
myhelper.h
win32 { SOURCES += qextserialport_win.cpp }
unix { SOURCES += qextserialport_unix.cpp }
FORMS += frmmain.ui
RESOURCES += \
main.qrc
MOC_DIR=temp/moc
RCC_DIR=temp/rcc
UI_DIR=temp/ui
OBJECTS_DIR=temp/obj
DESTDIR=bin
win32:RC_FILE=main.rc
CONFIG += warn_off #关闭警告
这样的话在不同平台下会自动加载对应平台的cpp实现文件来编译。
第三步:初始化主界面,自动加载对应串口号波特率等信息。
void frmMain::InitForm() { ReceiveCount=0; SendCount=0; IsShow=true; IsAutoClear=false; IsHexSend=true; IsHexReceive=true; IsDebug=false; QStringList comList;//串口号 QStringList baudList;//波特率 QStringList parityList;//校验位 QStringList dataBitsList;//数据位 QStringList stopBitsList;//停止位 #ifdef Q_OS_WIN//如果是windows系统 comList<<"COM1"<<"COM2"<<"COM3"<<"COM4"<<"COM5"<<"COM6" <<"COM7"<<"COM8"<<"COM9"<<"COM10"<<"COM11"<<"COM12" <<"COM13"<<"COM14"<<"COM15"; #else//如果是unix或者其他系统 comList<<"ttyUSB0"<<"ttyUSB1"<<"ttyUSB2"<<"ttyUSB3"<<"ttyUSB4"<<"ttyUSB5" <<"ttyS0"<<"ttyS1"<<"ttyS2"<<"ttyS3"<<"ttyS4"<<"ttyS5"<<"ttyS6" <<"ttyS7"<<"ttyS8"<<"ttyS9"; #endif ui->cboxPortName->addItems(comList); ui->cboxPortName->setCurrentIndex(0); baudList<<"50"<<"75"<<"100"<<"134"<<"150"<<"200"<<"300" <<"600"<<"1200"<<"1800"<<"2400"<<"4800"<<"9600" <<"14400"<<"19200"<<"38400"<<"56000"<<"57600" <<"76800"<<"115200"<<"128000"<<"256000"; ui->cboxBaudRate->addItems(baudList); ui->cboxBaudRate->setCurrentIndex(12); parityList<<"无"<<"奇"<<"偶"; #ifdef Q_OS_WIN//如果是windows系统 parityList<<"标志"; #endif parityList<<"空格"; ui->cboxParity->addItems(parityList); ui->cboxParity->setCurrentIndex(0); dataBitsList<<"5"<<"6"<<"7"<<"8"; ui->cboxDataBit->addItems(dataBitsList); ui->cboxDataBit->setCurrentIndex(3); stopBitsList<<"1"; #ifdef Q_OS_WIN//如果是windows系统 stopBitsList<<"1.5"; #endif stopBitsList<<"2"; ui->cboxStopBit->addItems(stopBitsList); ui->cboxStopBit->setCurrentIndex(0); //读取数据(采用定时器读取数据,不采用事件,方便移植到linux) myReadTimer=new QTimer(this); myReadTimer->setInterval(300); connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom())); //发送数据 mySendTimer=new QTimer(this); mySendTimer->setInterval(5000); connect(mySendTimer,SIGNAL(timeout()),this,SLOT(WriteMyCom())); //保存数据 mySaveTimer=new QTimer(this); mySaveTimer->setInterval(5000); connect(mySaveTimer,SIGNAL(timeout()),this,SLOT(SaveMyCom())); //显示日期时间 myTimer=new QTimer(this); myTimer->start(1000); connect(myTimer,SIGNAL(timeout()),this,SLOT(SetTime())); QDate dateNow=QDate::currentDate(); ui->labDate->setText(QString("日期:%1").arg(dateNow.toString("yyyy年MM月dd日 dddd"))); for (int i=1;i<=60;i++) { ui->cboxSend->addItem(QString::number(i)+"秒"); ui->cboxSave->addItem(QString::number(i)+"秒"); } ui->cboxSave->setCurrentIndex(4); ui->cboxSend->setCurrentIndex(4); ui->cboxSend->setEnabled(false); ui->cboxSave->setEnabled(false); this->ChangeEnable(false); this->ReadConfigData();//读取发送数据加载到下拉框 this->ReadSendData();//读取数据转发文件 ui->txtSend->installEventFilter(this);//安装监听器监听发送数据框回车响应 }
第四步:采用定时器读取串口数据,其实也可以采用事件机制,在几大平台也测试通过事件来收数据也可以,但是在windows下在快速收发大量数据时候居然会卡住,同样的代码在linux表现很好,不明原因,后面采用定时器读取机制,问题就没有了。
void frmMain::ReadMyCom() { //这个判断尤为重要,否则的话直接延时再接收数据,空闲时和会出现高内存占用 if (myCom->bytesAvailable()<=0){return;} myHelper::Sleep(100);//延时100毫秒保证接收到的是一条完整的数据,而不是脱节的 QByteArray buffer=myCom->readAll(); if (IsShow) { if (IsHexReceive) { QString tempDataHex=myHelper::ByteArrayToHexStr(buffer); ui->txtDataHex->append(QString("接收:%1 时间:%2") .arg(tempDataHex) .arg(QTime::currentTime().toString("HH:mm:ss"))); if (IsDebug)//2013-8-6增加接收数据后转发数据,模拟设备 { foreach(QString tempData,SendDataList) { QStringList temp=tempData.split(';'); if (tempDataHex==temp[0]) { //这里没有跳出循环,有可能一条数据会对应多条数据需要转发 myCom->write(myHelper::HexStrToByteArray(temp[1])); } } } } else { QString tempDataNormal=QString(buffer); ui->txtDataHex->append(QString("接收:%1 时间:%2") .arg(tempDataNormal) .arg(QTime::currentTime().toString("HH:mm:ss"))); if (IsDebug)//2013-8-6增加接收数据后转发数据,模拟设备 { foreach(QString tempData,SendDataList) { QStringList temp=tempData.split(';'); if (tempDataNormal==temp[0]) { //这里没有跳出循环,有可能一条数据会对应多条数据需要转发 myCom->write(temp[1].toAscii()); } } } } ReceiveCount=ReceiveCount+buffer.size(); ui->labReceive->setText(QString("接收:%1 字节").arg(ReceiveCount)); } } void frmMain::WriteMyCom() { QString str=ui->txtSend->currentText(); if (str==""){ui->txtSend->setFocus();return;}//发送数据为空 if (!myCom->isOpen()) { return; }//串口没有打开 QByteArray outData=str.toAscii(); int size=outData.size(); if (IsHexSend)//转化为16进制发送 { outData=myHelper::HexStrToByteArray(str); size=outData.size(); myCom->write(outData); } else { size=outData.size(); myCom->write(outData); } ui->txtDataHex->append(QString("发送:%1 时间:%2") .arg(str) .arg(QTime::currentTime().toString("HH:mm:ss"))); SendCount=SendCount+size; ui->labSend->setText(QString("发送:%1 字节").arg(SendCount)); if (IsAutoClear) { ui->txtSend->setCurrentIndex(-1); ui->txtSend->setFocus(); } } void frmMain::SaveMyCom() { QString tempData=ui->txtDataHex->toPlainText(); if (tempData==""){return;}//如果没有内容则不保存 QDateTime now=QDateTime::currentDateTime(); QString name=now.toString("yyyyMMddHHmmss"); QString fileName=name+".txt"; QFile file(fileName); file.open(QFile::WriteOnly | QIODevice::Text); QTextStream out(&file); out<<tempData; file.close(); }
这里有个细节说下,就是在接收数据函数里面,增加了一个判断if (myCom->bytesAvailable()<=0){return;}如果不这样的话,每次定时读取都会延时,内存占用很高,当然,如果采用事件机制的话,这里不需要任何延时或者判断。
可执行文件下载地址:http://download.csdn.net/detail/feiyangqingyun/6745003
源码猛点这里:http://download.csdn.net/detail/feiyangqingyun/6745011