目录
1、头文件
2、工程文件
3、实现
3.1、扫描串口
3.2、配置串口
3.3、打开串口
3.4、读取数据
3.5、发送数据
早在 QT4 时代,那时候 QT 并没有实现串口相关的类,记得那时候写的一个上位机是使用了老外实现的一个串口类(具体的类名字忘记了,反正很长)并调用了它的接口,到了 QT5 时代,QT 库已经自带了串口相关的类,这里主要聊下这么使用这个玩意,并自己简单的实现了一个串口 Demo;
先上图为证:
对应的代码已经上传到:《用 QT 实现的串口收发程序》
实现的部分包括:
1、扫描当前可用的串口
2、配置串口参数
3、开启串口并显示状态
4、进行接收(支持 Hex 和 Char 显示)和清屏
5、发送数据(支持 Hex 和 Char 发送)
6、显示/清除当前接收和发送的数据总数
那么下面就来简述一下整个过程
1、头文件
在 QT5 版本中,和串口相关的头文件主要需要包含:
#include <QtSerialPort/QSerialPortInfo>
#include <QtSerialPort/QSerialPort>
2、工程文件
打开你的 QT 工程文件(xxx.pro),在其中添加如下:
QT += serialport
3、实现
在做完1、2步骤后,就可以使用串口类了,最主要使用到的类是 QSerialPort 还有一个辅助的类 QSerialPortInfo
3.1、扫描串口
一般的,在使用串口之前呢,都需要先去轮询当前有哪些串口可用,这里用到 QSerialPortInfo 类:
void HiSerialPortMainWindow::ScanPort()
{
qDebug()<<tr("扫描存在的串口");
ui->progressBar->setValue(20);
ui->progressBar->reset();
ui->portComboBox->clear();
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){
qDebug() << "Name : " << info.portName();
qDebug() << "Description : " << info.description();
qDebug() << "Manufacturer: " << info.manufacturer();
qDebug() << "isBusy(): " << info.isBusy();
ui->portComboBox->addItem(info.portName());
}
ui->progressBar->setValue(100);
}
这个ScanPort 是一个 SLOT,对应的就是按下 【Scan Ports】这个 QPushButton 的时候的响应函数;函数中轮询了当前系统中的所有串口,并将其名字添加到了串口的 QComboBox 中;我们需要通过下拉菜单选择期望打开的那个串口;
3.2、配置串口
在使用串口之前呢,不仅仅需要指定哪一个串口,还需要对它进行配置,包含波特率、停止位、数据位、校验位等等;这里我们主要使用到了 QSerialPort 类,首先需要定义它:
HiSerialPort = new QSerialPort();
配置的时候呢,需要调用它的一些列接口:
HiSerialPort->setPortName(ui->portComboBox->currentText());
HiSerialPort->setBaudRate(getBaud());
HiSerialPort->setParity(getParity());
HiSerialPort->setDataBits(getDataBits());
HiSerialPort->setStopBits(getStopBits());
// TODO: FlowControl
HiSerialPort->setFlowControl(QSerialPort::NoFlowControl);
HiSerialPort->setReadBufferSize(1024);
这里,由于下拉菜单获取到的都是 QString,在调用串口配置的代码里需要进行一些转换,这里直接贴代码,不多说:
enum QSerialPort::BaudRate HiSerialPortMainWindow::getBaud()
{
bool ok;
enum QSerialPort::BaudRate ret;
switch (ui->baudComboBox->currentText().toLong(&ok, 10))
{
case 1200:
ret = QSerialPort::Baud1200;
break;
case 2400:
ret = QSerialPort::Baud2400;
break;
case 4800:
ret = QSerialPort::Baud4800;
break;
case 9600:
ret = QSerialPort::Baud9600;
break;
case 19200:
ret = QSerialPort::Baud19200;
break;
case 38400:
ret = QSerialPort::Baud38400;
break;
case 57600:
ret = QSerialPort::Baud57600;
break;
case 115200:
ret = QSerialPort::Baud115200;
break;
default:
ret = QSerialPort::UnknownBaud;
break;
}
return ret;
}
enum QSerialPort::Parity HiSerialPortMainWindow::getParity()
{
QString CurrentParityBit = ui->ParityBitComboBox->currentText();
if (CurrentParityBit == "None") return QSerialPort::NoParity;
else if (CurrentParityBit == "Odd") return QSerialPort::OddParity;
else if (CurrentParityBit == "Even") return QSerialPort::EvenParity;
return QSerialPort::UnknownParity;
}
enum QSerialPort::DataBits HiSerialPortMainWindow::getDataBits()
{
QString CurrentDataBits = ui->dataBitComboBox->currentText();
if (CurrentDataBits == "5") return QSerialPort::Data5;
else if (CurrentDataBits == "6") return QSerialPort::Data6;
else if (CurrentDataBits == "7") return QSerialPort::Data7;
else if (CurrentDataBits == "8") return QSerialPort::Data8;
return QSerialPort::UnknownDataBits;
}
enum QSerialPort::StopBits HiSerialPortMainWindow::getStopBits()
{
QString CurrentStopBits = ui->stopBitComboBox->currentText();
if (CurrentStopBits == "1") return QSerialPort::OneStop;
else if (CurrentStopBits == "1.5") return QSerialPort::OneAndHalfStop;
else if (CurrentStopBits == "2") return QSerialPort::TwoStop;
return QSerialPort::UnknownStopBits;
}
3.3、打开串口
配置完毕后,当然是打开串口,开启的方式以读写的方式打开:
HiSerialPort->open(QSerialPort::ReadWrite)
这个 open 有一个 bool 类型的返回值,ture 为打开成功,false 为打开失败;
3.4、读取数据
QSerialPort 类实现了一个 SIGNAL,为 readyRead(),即,当有数据的时候,会产生这个 SIGNAL,所以我们自己定义好 SLOT ,并在自己定义的这个 SLOT 函数(ReadSerialData())中去拿去串口数据:
void HiSerialPortMainWindow::ReadSerialData()
{
QByteArray buffer = HiSerialPort->readAll();
QString Str;
if (!buffer.isEmpty())
{
if (RXFormatAsHex)
{
int data_len = buffer.length();
// Handle the Hex Case
for (int var = 0; var < data_len; var++)
{
QString tempStr;
tempStr = QString::number(buffer.at(var), 16);
if (buffer.at(var) < 0x10)
{
tempStr = "0" + tempStr;
}
tempStr = /*"0x" + */tempStr.toUpper() + " ";
Str += tempStr;
}
}
else
{
Str = buffer;
}
ui->textBrowser->moveCursor(QTextCursor::End);
ui->textBrowser->insertPlainText(Str);
RXCount += buffer.size();
UpdateLCDNumber(0);
}
}
拿串口数据的方式是通过调用 QSerialPort 的 readAll() 接口,返回的是 QByteArray 类型的数据,读到的数据就放在这里;
这里需要注意两点:
1、比如对方发送 0x11、0x22、0x33、0x44、0x55,的数据,很可能会触发很多次 ReadSerialData() 调用,并不是收完这 5 个数据,才会触发一次,但是读走的数据,下次不会重复读到,这点可以放心
2、这里需要去判断读取的 QByteArray 的 buffer 是不是空,调试的时候遇到过,空数据,也触发了一次进入这个函数
3.5、发送数据
串口的数据发送是靠调用 QSerialPort 的 write 接口实现,传入的数据依然是 QByteArray 类型:
void HiSerialPortMainWindow::WriteSerialData()
{
QByteArray data;
if (!CurrentSerialPortOpened)
{
QMessageBox::warning(this,tr("Warning"),
tr("Please Open Port First"),QMessageBox::Ok);
return;
}
if (TXFormatAsHex)
{
QString Str = ui->sendContentlineEdit->text();
QStringList DataList = Str.split(" ");
qDebug() << "DataList " << DataList;
for(int i = 0; i < DataList.length(); i++)
{
bool ok;
QString tempStr = DataList.at(i);
if (tempStr != " " && tempStr != "")
{
qDebug() << "tempStr " << tempStr;
data.append((char)tempStr.toInt(&ok, 16));
}
}
qDebug() << "Send QByteArray "<< data;
HiSerialPort->write(data);
TXCount += data.size();
}
else
{
QByteArray ba = ui->sendContentlineEdit->text().toLatin1();
HiSerialPort->write(ba.data());
TXCount += ba.size();
}
UpdateLCDNumber(1);
}
这里我对数据进行了 Hex 或者 Char 的处理,以及 QLineEdit 的按照空格切分;
其他的没啥了,亲测可用,经验证与下位机收发数据(Hex 和 Char)均正常。