前言
Qt写上位机时,串口通信是个常用功能,在Qt4的时候有第三方模块QextSerialPort,到了Qt5.1官方提供了QSerialPort模块。
目录
补充:16进制文本(如“0A 13 EF”)转16进制数据(如0x0A 0x13 0xEF)
使用该模块需要在pro文件中添加:QT += serialport
主要使用两个类:QSerialPort和QSerialPortInfo
获取串口信息:QSerialPortInfo
获取串口名列表
- QStringList slist;
- foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
- //检测是否可用
- f(!info.isBusy())
- slist<<info.portName();
- }
除了串口名还能获取其他的相关信息,可以看文档,或者该链接 https://blog.csdn.net/mcu_tian/article/details/43527385
串口IO操作:QSerialPort
串口IO的主要操作有参数设置/开/关/读/写等
- //[1]串口设置
- QSerialPort *serialIo=new QSerialPort;
- serialIo->setPortName("COM4"); //串口名
- serialIo->setBaudRate(115200); //波特率
- serialIo->setDataBits(QSerialPort::Data8); //数据位
- serialIo->setParity(QSerialPort::NoParity); //校验位
- serialIo->setStopBits(QSerialPort::OneStop); //停止位
- serialIo->setFlowControl(QSerialPort::NoFlowControl);//流控制一般没用
-
- //[2]串口开启
- if(serialIo->open(QIODevice::ReadWrite)){
- qDebug()<<"串口已打开,读写模式";
- }else{
- qDebug()<<"串口打开异常"<<serialIo->errorString();
- serialIo->clearError();
- }
-
- //[3]数据发送
- const QByteArray send_data=ui->textSend->toPlainText().toUtf8();//一般没发字符串,特别是中文
- if(serialIo->isOpen()){
- serialIo->write(send_data);
- qDebug()<<"已发送:"<<QString::fromUtf8(send_data);
- }else{
- qDebug()<<"发送失败,串口未打开";
- }
- if(!serialIo->waitForBytesWritten(30000)){
- qDebug()<<"命令发送异常"<<serialIo->errorString();
- serialIo->clearError();
- }
-
- //[4]数据接收
- connect(serialIo,&QSerialPort::readyRead,this,[this](){
- if (serialIo->bytesAvailable()) {
- //串口收到的数据可能不是连续的,需要的话应该把数据缓存下来再进行协议解析,类似tcp数据处理
- const QByteArray recv_data=serialIo->readAll();
- //接收发送要一致,如果是处理字节数据,可以把QByteArray当数组一样取下标,或者用data()方法转为char*形式
- ui->textRecv->append(QString::fromUtf8(recv_data));//显示
- qDebug()<<"已接收:"<<QString::fromUtf8(recv_data);
- }
- });
-
- //[5]串口关闭
- serialIo->clear();
- serialIo->close();
实例操作
为了方便,ui我直接用的设计师拖得,工程文件我把百度云链接放在了最后,需要的可以下载
头文件MainWidget.h(记得pro里引入serialport模块)
- #ifndef MAINWIDGET_H
- #define MAINWIDGET_H
-
- #include <QWidget>
-
- class QSerialPort;
-
- namespace Ui {
- class MainWidget;
- }
-
- class MainWidget : public QWidget
- {
- Q_OBJECT
-
- public:
- explicit MainWidget(QWidget *parent = 0);
- ~MainWidget();
-
- void initSerial();//初始化串口设置
- void initMainUi();//初始化界面操作
-
- void openSerial();//槽函数-打开串口
- void closeSerial();//槽函数-关闭串口
- void refreshSerial();//槽函数-刷新串口名列表
-
- private:
- QStringList getSerialPortNames();//获取串口名列表
- void setSerialEnable(bool enabled);//串口开启时就不能动ui配置了
- void sendData();//槽函数-发送数据
- void recvData();//槽函数-接收数据
-
- private:
- Ui::MainWidget *ui;
-
- QSerialPort *serialIo;//串口io,一般可以把串口io扔到线程里去,避免阻塞ui
- };
-
- #endif // MAINWIDGET_H
实现文件MainWidget.cpp
- #include "MainWidget.h"
- #include "ui_MainWidget.h"
-
- #include <QSerialPort>
- #include <QSerialPortInfo>
- #include <QListView>
-
- #include <QDebug>
-
- MainWidget::MainWidget(QWidget *parent) :
- QWidget(parent),
- ui(new Ui::MainWidget)
- {
- ui->setupUi(this);
- initSerial();
- initMainUi();
- }
-
- MainWidget::~MainWidget()
- {
- delete ui;
- }
-
- void MainWidget::initSerial()
- {
- //[1]创建串口io对象
- serialIo=new QSerialPort(this);
- //数据接收处理
- connect(serialIo,&QSerialPort::readyRead,this,&MainWidget::recvData);
- //[2]界面初始化
- //注:items的选项值是根据文档中的枚举来写的
- //串口名
- refreshSerial();
- ui->boxPortName->setView(new QListView(this));
- //波特率
- QStringList baudrateList;
- baudrateList<<"1200"<<"2400"<<"4800"<<"9600"<<"19200"<<"38400"<<"57600"<<"115200";
- ui->boxBaudRate->addItems(baudrateList);//添加下拉列表选项
- ui->boxBaudRate->setEditable(true);//串口波特率可编辑
- ui->boxBaudRate->setCurrentText("115200");//界面中初始值
- ui->boxBaudRate->setView(new QListView(this));//该设置是配合qss的,不然item行高设置没效果
- //数据位
- QStringList databitList;
- databitList<<"5"<<"6"<<"7"<<"8";
- ui->boxDataBits->addItems(databitList);
- ui->boxDataBits->setCurrentText("8");
- ui->boxDataBits->setView(new QListView(this));
- //校验位
- QStringList parityList;
- parityList<<"No"<<"Even偶"<<"Odd奇"<<"Space"<<"Mark";
- ui->boxParity->addItems(parityList);
- ui->boxParity->setCurrentText("No");
- ui->boxParity->setView(new QListView(this));
- //停止位
- QStringList stopbitList;
- stopbitList<<"1"<<"1.5"<<"2";
- ui->boxStopBits->addItems(stopbitList);
- ui->boxStopBits->setCurrentText("1");
- ui->boxStopBits->setView(new QListView(this));
- //流控制
- QStringList flowctrlList;
- flowctrlList<<"No"<<"Hardware"<<"Software";
- ui->boxFlowControl->addItems(flowctrlList);
- ui->boxFlowControl->setCurrentText("No");
- ui->boxFlowControl->setView(new QListView(this));
- }
-
- void MainWidget::initMainUi()
- {
- //点击串口[开启]/[关闭]按钮
- connect(ui->btnOpen,&QPushButton::clicked,this,[this](){
- if(ui->btnOpen->text()=="打开"){
- openSerial();
- }else{
- closeSerial();
- }
- });
- //点击串口[刷新]按钮-刷新串口名列表
- connect(ui->btnRefresh,&QPushButton::clicked,this,&MainWidget::refreshSerial);
- //点击数据[发送]按钮
- connect(ui->btnSend,&QPushButton::clicked,this,&MainWidget::sendData);
- }
-
- void MainWidget::openSerial()
- {
- const QString portnameStr=ui->boxPortName->currentText();
- if(!portnameStr.isEmpty()){
- QSerialPortInfo info(portnameStr);
- if(info.isBusy()){
- qDebug()<<"当前串口繁忙,可能已被占用,请确认后再连接"<<portnameStr;
- return;
- }
- //
- qint32 baudrate=ui->boxBaudRate->currentText().toInt();
- QSerialPort::DataBits databit;
- switch (ui->boxDataBits->currentIndex()) {
- case 0:databit=QSerialPort::Data5; break;
- case 1:databit=QSerialPort::Data6; break;
- case 2:databit=QSerialPort::Data7; break;
- case 3:databit=QSerialPort::Data8; break;
- default:databit=QSerialPort::Data8; break;
- }
- QSerialPort::Parity parity;
- switch (ui->boxParity->currentIndex()) {
- case 0:parity=QSerialPort::NoParity; break;
- case 1:parity=QSerialPort::EvenParity; break;
- case 2:parity=QSerialPort::OddParity; break;
- case 3:parity=QSerialPort::SpaceParity; break;
- case 4:parity=QSerialPort::MarkParity; break;
- default:parity=QSerialPort::NoParity; break;
- }
- QSerialPort::StopBits stopbit;
- switch (ui->boxStopBits->currentIndex()) {
- case 0:stopbit=QSerialPort::OneStop; break;
- case 1:stopbit=QSerialPort::OneAndHalfStop; break;
- case 2:stopbit=QSerialPort::TwoStop; break;
- default:stopbit=QSerialPort::OneStop; break;
- }
- QSerialPort::FlowControl flowcontrol;
- switch (ui->boxFlowControl->currentIndex()) {
- case 0:flowcontrol=QSerialPort::NoFlowControl; break;
- case 1:flowcontrol=QSerialPort::HardwareControl; break;
- case 2:flowcontrol=QSerialPort::SoftwareControl; break;
- default:flowcontrol=QSerialPort::NoFlowControl; break;
- }
- //串口配置设置
- serialIo->setPortName(portnameStr);
- serialIo->setBaudRate(baudrate);
- serialIo->setDataBits(databit);
- serialIo->setParity(parity);
- serialIo->setStopBits(stopbit);
- serialIo->setFlowControl(flowcontrol);//这个我一般没用
- if(serialIo->open(QIODevice::ReadWrite)){
- qDebug()<<"串口已打开,读写模式";
- setSerialEnable(false);//改变ui状态
- }else{
- qDebug()<<"串口打开异常"<<portnameStr<<serialIo->errorString();
- serialIo->clearError();
- setSerialEnable(true);
- }
- }else{
- qDebug()<<"未找到可用串口,请确认串口连接正常后点击刷新";
- }
- }
-
- void MainWidget::closeSerial()
- {
- serialIo->clear();
- serialIo->close();
- qDebug()<<"串口已关闭";
- setSerialEnable(true);
- }
-
- void MainWidget::refreshSerial()
- {
- ui->boxPortName->clear();
- ui->boxPortName->addItems(getSerialPortNames());
- }
-
- QStringList MainWidget::getSerialPortNames()
- {
- QStringList slist;
- foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
- //检测是否可用
- if(!info.isBusy())
- slist<<info.portName();
- }
- if(slist.isEmpty()){
- qDebug()<<"未找到可用串口,请确认串口连接正常后点击刷新";
- }
- return slist;
- }
-
- void MainWidget::setSerialEnable(bool enabled)
- {
- //打开成功就false不能再修改配置,关闭状态true可以进行设置
- ui->btnRefresh->setEnabled(enabled);
- ui->btnOpen->setText(enabled?QString("打开"):QString("关闭"));
- //可以把btn和配置分在两个widget里,这样直接设置widget的enable就没这么麻烦了
- ui->boxPortName->setEnabled(enabled);
- ui->boxBaudRate->setEnabled(enabled);
- ui->boxDataBits->setEnabled(enabled);
- ui->boxParity->setEnabled(enabled);
- ui->boxStopBits->setEnabled(enabled);
- ui->boxFlowControl->setEnabled(enabled);
- }
-
- void MainWidget::sendData()
- {
- //注意收发的编码问题,我一般只是发命令吗和字节数据,没怎么发字符串,用latin1就行了
- const QByteArray send_data=ui->textSend->toPlainText().toUtf8();
- if(send_data.size()<=0)
- return;
- if(serialIo->isOpen()){
- serialIo->write(send_data);
- qDebug()<<"已发送:"<<QString::fromUtf8(send_data);
- }else{
- qDebug()<<"发送失败,串口未打开";
- return;
- }
- //Qt新版本默认值是30 000
- if(!serialIo->waitForBytesWritten(30000)){
- qDebug()<<"命令发送异常"<<serialIo->errorString();
- serialIo->clearError();
- }
- }
-
- void MainWidget::recvData()
- {
- if (serialIo->bytesAvailable()) {
- //串口收到的数据可能不是连续的,需要的话应该把数据缓存下来再进行协议解析,类似tcp数据处理
- const QByteArray recv_data=serialIo->readAll();
- //接收发送要一致,如果是处理字节数据,可以把QByteArray当数组一样取下标,或者用data()方法转为char*形式
- ui->textRecv->append(QString::fromUtf8(recv_data));
- qDebug()<<"已接收:"<<QString::fromUtf8(recv_data);
- }
- }
由于时间有限,所以有两个点没有写,一是一般我把串口IO放在子线程中避免阻塞ui;二是串口协议得解析,可以参照TCP数据的处理,先缓存起来再循环判断,一般一帧数据有帧头、帧长度、帧命令码、帧数据、帧尾/CRC校验等字段。
时间仓促,可能代码有bug,望大佬纠正。
附上百度云链接 https://pan.baidu.com/s/1gzHC6Q4fBZ_G1Db7tMcIaw
提取码 inxh 文件名TestSerialPort.rar
补充:16进制文本(如“0A 13 EF”)转16进制数据(如0x0A 0x13 0xEF)
有时候需要这种功能,输入十六进制文本,发送的是对应的十六进制数据,Qt中通过QByteArray类可以轻松的搞定。
- QString str="0A 13 EF"; //假设为文本框读取到的字符串
- QByteArray temp=QByteArray::fromHex(str.toLatin1());
- qDebug()<<str<<temp;
(输出结果中的十六进制因为被Qt做了转义,所以0x0A是个换行符)
当然,也可以把十六进制数据转为十六进制文本。
- const char arr[]={0x0A,0x13,0xEF};
- QByteArray temp=QByteArray(arr,3); //假设为收到的数据
- QString str=temp.toHex(' ').toUpper(); //老版本Qt的toHex没有参数设置,需要自己分割
- qDebug()<<temp<<str;
(2019-07-24晨)
来源:CSDN
作者:总是加班的狗
链接:https://blog.csdn.net/baidu_37503452/article/details/104293993