您现在的位置是:首页 > 智能机电
Qt实践录:串口调试助手
智慧创新站
2025-04-15【智能机电】215人已围观
简介由于项目需要使用到串口调试及测试,为了练手,使用Qt编写一个串口调试助手。本文按开发的过程进行简单介绍,同时也涉及部分用到的模块代码。详细代码参考源码仓库。开发过程工程相关Qt使用的串口类为`QSerialPort`,需要在工程文件中添加对应的库,如下:```QT+=coreguiserialpor...
由于项目需要使用到串口调试及测试,为了练手,使用Qt编写一个串口调试助手。本文按开发的过程进行简单介绍,同时也涉及部分用到的模块代码。详细代码参考源码仓库。
开发过程
工程相关
Qt使用的串口类为`QSerialPort`,需要在工程文件中添加对应的库,如下:
```
QT+=coreguiserialport
```
logo图标,注意是ico格式:
```
RC_ICONS=images/
```
```
RESOURCES+=\
```
USB设备检测依赖的库:
```
win32:LIBS+=-lSetupAPI-luser32
```
信号与槽
此机制及操作方式,可类比于MFC的界面设计和消息响应。实际上,笔者喜欢将槽函数称为响应函数。
串口相关
串口类声明:
```
includeQSerialPortInfo
QSerialPortserial;
```
枚举本机串口设备:
```
QSerialPortInfo::availablePorts()
```
串口参数设置:
```
("com4");//串口名称
(115200);//串口波特率
(QSerialPort::Data8);//数据位
(QSerialPort::OneStop);//停止位
(QSerialPort::NoParity);//校验位
(QSerialPort::NoFlowControl);//流控
```
注:Qt似乎只有无流控、软件流控、硬件流控这三种,无法区分RTS、DTR。
串口打开、关闭:
```
(QIODevice::ReadWrite);
();
```
串口数据发送:
```
(sData);
```
串口数据接收:
```
//串口数据到来时,会触发QSerialPort::readyRead事件,添加相应的响应函数
QObject::connect(serial,QSerialPort::readyRead,this,MainWindow::readyRead);
voidMainWindow::readyRead()
{
}
```
注意,串口数据类型为`QByteArray`。
自动检测USB
鉴于目前大部分场合使用的是USB串口线,所以添加对USB设备的检测。这里检测到USB设备时,再使用`QSerialPortInfo::availablePorts()`检测串口设备。
```
staticconstGUIDGUID_DEVINTERFACE_LIST[]=
{
//GUID_DEVINTERFACE_USB_DEVICE
{0xA5DCBF10,0x6530,0x11D2,{0x90,0x1F,0x00,0xC0,0x4F,0xB9,0x51,0xED}},
//GUID_DEVINTERFACE_DISK
{0x53f56307,0xb6bf,0x11d0,{0x94,0xf2,0x00,0xa0,0xc9,0x1e,0xfb,0x8b}},
//GUID_DEVINTERFACE_HID,
{0x4D1E55B2,0xF16F,0x11CF,{0x88,0xCB,0x00,0x11,0x11,0x00,0x00,0x30}},
//GUID_NDIS_LAN_CLASS
{0xad498944,0x762f,0x11d0,{0x8d,0xcb,0x00,0xc0,0x4f,0xc3,0x35,0x8c}}
////GUID_DEVINTERFACE_COMPORT
//{0x86e0d1e0,0x8089,0x11d0,{0x9c,0xe4,0x08,0x00,0x3e,0x30,0x1f,0x73}},
////GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR
//{0x4D36E978,0xE325,0x11CE,{0xBF,0xC1,0x08,0x00,0x2B,0xE1,0x03,0x18}},
////GUID_DEVINTERFACE_PARALLEL
//{0x97F76EF0,0xF883,0x11D0,{0xAF,0x1F,0x00,0x00,0xF8,0x00,0x84,0x5C}},
////GUID_DEVINTERFACE_PARCLASS
//{0x811FC6A5,0xF728,0x11D0,{0xA5,0x37,0x00,0x00,0xF8,0x75,0x3E,0xD1}}
};
//注册插拔事件
HDEVNOTIFYhDevNotify;
DEV_BROADCAST_DEVICEINTERFACENotifacationFiler;
ZeroMemory(NotifacationFiler,sizeof(DEV_BROADCAST_DEVICEINTERFACE));
_size=sizeof(DEV_BROADCAST_DEVICEINTERFACE);
_devicetype=DBT_DEVTYP_DEVICEINTERFACE;
for(inti=0;isizeof(GUID_DEVINTERFACE_LIST)/sizeof(GUID);i++)
{
_classguid=GUID_DEVINTERFACE_LIST[i];//GetCurrentUSBGUID();//m_usb-GetDriverGUID();
hDevNotify=RegisterDeviceNotification((HANDLE)this-winId(),NotifacationFiler,DEVICE_NOTIFY_WINDOW_HANDLE);
if(!hDevNotify)
{
DWORDErr=GetLastError();
}
}
```
响应`nativeEvent`事件:
```
boolMainWindow::nativeEvent(constQByteArrayeventType,void*message,long*result)
{
MSG*msg=reinterpret_castMSG*(message);
intmsgType=msg-message;
if(msgType==WM_DEVICECHANGE)//设备插入事件
{
PDEV_BROADCAST_HDRlpdb=(PDEV_BROADCAST_HDR)msg-lParam;
switch(msg-wParam){
caseDBT_DEVICEARRIVAL:
if(lpdb-dbch_devicetype==DBT_DEVTYP_DEVICEINTERFACE)
{
//PDEV_BROADCAST_DEVICEINTERFACEpDevInf=(PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
//QStringstrname=QString::fromWCharArray(pDevInf-dbcc_name,pDevInf-dbcc_size);
//qDebug()"arrive"+strname;
printDebugInfo("USBdevicearrive");
emitsig_deviceChanged(1);
}
break;
caseDBT_DEVICEREMOVECOMPLETE://设备移除事件
if(lpdb-dbch_devicetype==DBT_DEVTYP_DEVICEINTERFACE)
{
//PDEV_BROADCAST_DEVICEINTERFACEpDevInf=(PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
//QStringstrname=QString::fromWCharArray(pDevInf-dbcc_name,pDevInf-dbcc_size);
printDebugInfo("USBdeviceremoved");
emitsig_deviceChanged(0);
}
break;
}
}
returnfalse;
}
```
这里使用自定义的信号`sig_deviceChanged`,连接到函数`on_deviceChanged`:
```
QObject::connect(this,MainWindow::sig_deviceChanged,this,MainWindow::on_deviceChanged);
voidMainWindow::on_deviceChanged(intflag)
{
if(flag==1)
{
foreach(constQSerialPortInfoinfo,QSerialPortInfo::availablePorts())
{
if(-1==ui-cbPortName-findText(()))
ui-cbPortName-addItem(());
}
}
else
{
();
ui-btnOpen-setText(tr("打开串口"));
ui-btnOpen-setIcon(QIcon(":images/"));
}
}
```
界面逻辑
界面基本设置
在`initMainWindow`函数中对窗口进行基本设置,如标题、窗口大小,最小化最大化按钮,等等。
```
//对主窗口的初始化
voidMainWindow::initMainWindow()
{
setWindowTitle(tr("QtSerialPort"));
setMinimumSize(480,320);
Qt::WindowFlagswinFlags=Qt::Dialog;
winFlags=winFlags|Qt::WindowMinMaxButtonsHint|Qt::WindowCloseButtonHint;
setWindowFlags(winFlags);
}
```
控件贴图
新建资源文件,内容如下,再在QtCreator中添加该文件:
```
RCC
qresourceprefix="/"
fileimages//file
fileimages//file
fileimages//file
fileimages//file
fileimages//file
fileimages//file
/qresource
/RCC
```
也可以在QtCreator新建资源文件,右键添加图片。效果一样。
以打开串口按钮为例,设置文字和图标代码如下:
```
ui-btnOpen-setText(tr("打开串口"));
ui-btnOpen-setIconSize(ui-btnOpen-rect().size());
ui-btnOpen-setIcon(QIcon(":images/"));
```
注:资源文件qrc的前缀为`/`,images为工程下的目录,使用QIcon的参数形式为`:images/xxx`。
串口参数自动更新
为串口打开时,可以实时更改参数,但串口设备除外。响应QComboBox的`currentTextChanged`或`currentIndexChanged`事件。如下:
```
//串口设备直接用文本文字形式即可
voidMainWindow::on_cbPortName_currentTextChanged(constQStringarg1)
{
(arg1);
}
//如停止位等,需要用索引转换
voidMainWindow::on_cbStopbit_currentIndexChanged(intindex)
{
//qDebug()index;
//设置停止位
switch(index)
{
case0:(QSerialPort::OneStop);break;
case1:(QSerialPort::OneAndHalfStop);break;
case2:(QSerialPort::TwoStop);break;
default:break;
}
}
```
定时器
重载`timerEvent`函数:
```
十六进制
为方便调试,工具支持字符、十六进制数据的发送和显示。“十六进制字符串”转字符串等函数集如下:
```
inthexStringToString(QStringhexStr,QStringstr)
{
intret=0;
boolok;
hexStr=();
hexStr=();
QStringListsl=("");
foreach(QStrings,sl)
{
if(!())
{
charc=((ok,16))0xFF;
if(ok)
{
(c);
}
else
{
ret=-1;
}
}
}
str=retByte;
returnret;
}
inthexStringToHexArray(QStringhexStr,QByteArrayarr)
{
intret=0;
boolok;
hexStr=();
hexStr=();
QStringListsl=("");
foreach(QStrings,sl)
{
if(!())
{
charc=((ok,16))0xFF;
if(ok)
{
(c);
}
else
{
ret=-1;
}
}
}
returnret;
}
inthexArrayToString(QByteArrayhexArr,QStringstr)
{
intret=0;
str=('').toLower();
returnret;
}
```
其它
实际上,程序难度不大,特别是串口类的操作,因为`QSerialPort`提供了十分友好、方便的接口进行串口的设置、收发。如果要说有难度,可能在于界面逻辑的设计。如定时发送与单次发送,控件提示文字和图标显示,十六进制与字符串之间的转换,等等。
笔者在此工具基础上实现了对ESP8266的操作,包括指示LED灯、继电器、出厂恢复以及运行态的功能测试验证等操作。由于与本文关联不大,不再展开。
#代码仓库
代码以仓库为准,本文不一定全部囊括。本工程所有源码均可自由自主使用,包括但不限于添加、删除、修改,商用、自用。由此带来的成果/后果概与作者无关。限于水平能力,本程序无任何质量保证,本程序作者无提供服务之义务。
很赞哦!(142)