ComSerialPort::ComSerialPort(QString PortName,qint32 BaudRate,qint32* state, QObject *parent) : QObject(parent) { thread = new QThread(); Serial = new QSerialPort(); *state = InitSerial(PortName,BaudRate); this->moveToThread(thread); Serial->moveToThread(thread); thread->start(); connect(thread, &QThread::finished, this,&QObject::deleteLater); }
时间: 2024-04-07 14:28:05 浏览: 115
这是一个串口通信类ComSerialPort的构造函数,其中参数PortName是串口名称,BaudRate是串口波特率,state是初始化状态指针,parent是父对象指针。
在构造函数中,首先创建一个QThread线程对象和一个QSerialPort串口对象。然后调用InitSerial函数初始化串口,并将初始化状态通过指针state返回。接着将当前对象和串口对象都移动到QThread线程中,并启动线程。最后通过connect函数将线程的finished信号与当前对象的deleteLater槽函数连接起来,以便在线程结束时自动销毁当前对象。
注意:这段代码只是ComSerialPort类的构造函数,无法判断整个类的功能和实现细节。
相关问题
#ifndef RS485_H #define RS485_H #include <QObject> #include <QSerialPort> #include <QSerialPortInfo> class RS485 : public QObject //声明了一个RS485类 { Q_OBJECT //启用Qt元对象系统功能,必须出现在类定义的私有区域,自动生成moc(元对象编译器)代码,使类支持:信号槽机制、属性系统(Q_PROPERTY)、运行时类型信息(qobject_cast) public: explicit RS485(QObject *parent = nullptr); //声明构造函数,explicit:禁止隐式类型转换,QObject *parent:遵循Qt对象树的内存管理机制,默认参数nullptr表示可以不指定父对象 ~RS485(); //析构函数 Q_INVOKABLE QStringList getAvailablePorts() const; // 获取可用端口号列表 bool openSerialPort(const QString &portName, QSerialPort::BaudRate baud , //波特率 QSerialPort::DataBits data , //数据位 QSerialPort::Parity parity , //校验位 QSerialPort::StopBits stop ); //停止位 //标准的RS-232和RS-485通信中,起始位都是1位,无需配置 void closeSerialPort(); qint64 sendData(const QByteArray &data); //发送数据 signals: void dataReceived(const QByteArray &data); //收到完整数据帧信号 void errorOccurred(const QString &error); private slots: void onReadyRead(); //处理原始数据接收 void onErrorOccurred(QSerialPort::SerialPortError error); private: QSerialPort m_serial; QByteArray m_buffer; // 数据缓存(处理粘包) }; #endif // RS485_H 这是我的RS485.h文件,帮我写一个RS485.cpp实现头文件中声明的函数功能
<think>嗯,用户让我帮忙实现一个RS485类的cpp文件,基于他们提供的头文件。首先,我需要仔细阅读他们的头文件,了解每个函数的作用和类的结构。
首先,头文件中RS485类继承自QObject,并且使用了Q_OBJECT宏,说明这个类需要Qt的元对象系统支持,比如信号和槽。构造函数是explicit的,防止隐式转换,析构函数也需要正确释放资源。
接下来看公有函数,getAvailablePorts被声明为Q_INVOKABLE,所以这个函数可能需要被Qt的元对象系统调用,比如在QML中使用。这个函数应该返回可用的串口列表,可以通过QSerialPortInfo::availablePorts()获取。
openSerialPort函数的参数包括端口名、波特率、数据位、校验位和停止位。需要正确设置QSerialPort的各个参数,并处理打开失败的情况。如果成功打开,可能需要连接相关的信号,比如readyRead和errorOccurred到对应的槽函数。
closeSerialPort函数应该关闭串口,可能需要检查是否已经打开,再调用close()。
sendData函数发送数据,这里要注意返回qint64,即实际发送的字节数。需要确保串口处于打开状态,否则返回-1或者错误处理。
信号部分有dataReceived和errorOccurred,说明当接收到数据或者发生错误时需要发出这些信号。私有槽函数onReadyRead和onErrorOccurred需要实现,处理数据接收和错误。
在onReadyRead中,需要读取所有可用数据,并处理粘包问题。用户使用了m_buffer作为缓存,可能需要根据特定的帧结构来分割数据帧。但头文件中没有明确的协议,比如帧头帧尾或者长度字段,所以可能需要用户自行实现这部分逻辑。这里可能需要添加注释说明,或者提供一个基本的缓存机制,让用户根据实际协议修改。
错误处理部分,onErrorOccurred需要接收QSerialPort::SerialPortError参数,并将其转换为错误信息发出信号。需要注意的是,有些错误可能是可以忽略的,比如超时等,但通常应该通知上层处理。
现在考虑实现各个函数的具体步骤:
1. 构造函数:初始化QSerialPort,设置父对象。可能不需要做太多事情,因为参数在open时设置。
2. getAvailablePorts:遍历QSerialPortInfo::availablePorts(),收集端口名称,返回QStringList。
3. openSerialPort:设置串口参数,尝试打开,并连接信号到槽。如果打开失败,返回false,并发出错误信号。
4. closeSerialPort:关闭串口,断开信号连接,清空缓存。
5. sendData:检查串口是否打开,然后写入数据,返回写入的字节数。如果未打开,返回-1。
6. onReadyRead:读取数据到缓存,然后可能需要解析完整的数据帧。由于没有具体协议,这里可能只是简单地将所有接收到的数据缓存起来,并发出dataReceived信号。或者需要用户自己实现拆包逻辑,比如根据特定的结束符。这里可能需要说明,当前的实现只是简单地将数据累积并每次收到数据就发送出去,但实际可能需要更复杂的处理。
例如,假设每次收到数据就立即触发dataReceived,但这样可能会有粘包问题。所以用户可能需要根据协议处理,比如按帧头帧尾分割,或者长度字段。因此,在onReadyRead中,可能需要将数据追加到m_buffer,然后检查是否有一个完整的帧,如果有,提取出来并发送信号,然后保留剩余的数据在缓存中。
但头文件中的注释提到“处理粘包”,所以m_buffer用于缓存。因此,在实现中,需要将每次读取的数据追加到m_buffer,然后处理可能的多个数据帧。例如,假设数据帧以换行符结束,那么可以按行分割。但这里没有明确说明,所以可能需要用户自定义处理逻辑。因此,在示例代码中,可能只是简单地将所有接收到的数据发出,或者提供一个基本的示例,比如当收到特定字节时触发。
但用户可能希望有一个更通用的处理方式,比如缓存数据直到达到某个条件。例如,如果协议是每个数据包以\r\n结尾,那么可以分割缓存中的数据。但当前头文件没有提供相关的方法,所以可能需要用户在onReadyRead中自行实现。因此,示例代码可能需要说明这一点,或者提供一个基础的框架,让用户补充协议处理部分。
例如,在onReadyRead中,读取数据到缓存,然后检查是否有完整帧,这里假设数据帧以换行符结束,然后分割并发送信号。但用户可能需要根据实际协议修改这部分代码。
另外,错误处理中,当发生错误时,应该发出errorOccurred信号,并携带错误信息。需要注意的是,QSerialPort::errorOccurred信号在Qt5中可能有所不同,需要确认连接是否正确。
现在,开始编写cpp文件的大致结构:
构造函数:初始化m_serial的父对象为当前RS485实例的父对象?或者因为RS485本身是QObject,m_serial的父对象应该是this?这样当RS485对象被删除时,m_serial也会自动删除。但通常,在Qt中,如果m_serial是成员变量,并且父对象已经设置,可能不需要显式设置父对象。或者在构造函数中,将m_serial的父对象设置为this,这样当RS485对象销毁时,m_serial也会被正确清理。
析构函数:关闭串口,但QSerialPort的析构函数应该会自动处理,但显式关闭可能更安全。
在openSerialPort中,设置各个参数,比如波特率、数据位等,然后调用open。如果成功,连接readyRead和errorOccurred信号到对应的槽函数。
在sendData中,检查串口是否打开,然后调用write,并返回写入的字节数。需要注意write是异步的,返回值表示成功排队发送的字节数。
在onReadyRead中,使用readAll()读取数据,追加到m_buffer,然后处理可能的多个数据帧。例如,假设数据帧以特定字符结尾,比如0x0A,那么可以分割数据。但这里需要用户根据协议实现,所以示例代码可能只是简单地将所有数据缓存,并发出dataReceived信号,或者等待直到满足条件。
例如,每次收到数据就追加到缓存,然后检查是否包含完整的数据包,如果有,提取出来,发出信号,并移除已处理的数据。例如:
while (m_buffer.contains(endMarker)) {
int pos = m_buffer.indexOf(endMarker);
QByteArray frame = m_buffer.left(pos + 1);
emit dataReceived(frame);
m_buffer = m_buffer.mid(pos + 1);
}
但用户需要根据实际协议定义endMarker,比如可能是"\r\n"。因此,在示例代码中,可能添加注释说明需要用户根据协议实现这部分逻辑,或者提供一个基本的示例。
总结:在实现onReadyRead时,需要处理数据粘包,但具体方法依赖于协议,因此代码中可能需要包含一个示例处理方式,并提醒用户根据实际情况修改。
另外,错误处理中,当发生错误时,需要将QSerialPort::SerialPortError转换为QString错误信息,可以通过检查error的值,并返回对应的错误描述。
例如,在onErrorOccurred槽函数中,如果error不是NoError,则发出errorOccurred信号,并携带错误字符串。
现在,逐步实现每个函数:
1. RS485构造函数:初始化m_serial的父对象为this,确保生命周期管理正确。
RS485::RS485(QObject *parent) : QObject(parent)
{
m_serial.setParent(this); // 确保串口对象的父对象是当前实例,方便内存管理
}
或者,因为m_serial是成员变量,其父对象默认是当前RS485实例的父对象?可能需要明确设置,或者让用户决定。不过通常,作为成员变量,如果RS485对象被正确设置父对象,m_serial的父对象应该是RS485实例,这样当父对象删除时,RS485实例会被删除,进而删除m_serial。但这里m_serial是直接成员,不是指针,所以不需要设置父对象?或者QSerialPort在构造时是否需要父对象?
实际上,QSerialPort m_serial;是作为成员变量存在的,它的父对象由构造函数决定。如果RS485的构造函数中,构造m_serial时没有指定父对象,那么它的父对象默认是当前RS485实例,因为QObject的成员变量在构造时,如果没有指定父对象,父对象会是创建它们的那个对象。或者需要显式调用setParent?
或者,因为m_serial是作为成员变量,其父对象自动是包含它的QObject派生类,即RS485实例。所以不需要显式设置。可能不需要在构造函数中做任何事情,除了可能的初始化。
因此,构造函数可能很简单:
RS485::RS485(QObject *parent) : QObject(parent)
{
}
析构函数:
RS485::~RS485()
{
closeSerialPort(); // 确保关闭串口
}
getAvailablePorts函数:
QStringList RS485::getAvailablePorts() const
{
QStringList ports;
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
ports.append(info.portName());
}
return ports;
}
openSerialPort函数:
bool RS485::openSerialPort(const QString &portName,
QSerialPort::BaudRate baud,
QSerialPort::DataBits data,
QSerialPort::Parity parity,
QSerialPort::StopBits stop)
{
if (m_serial.isOpen()) {
m_serial.close();
}
m_serial.setPortName(portName);
m_serial.setBaudRate(baud);
m_serial.setDataBits(data);
m_serial.setParity(parity);
m_serial.setStopBits(stop);
if (m_serial.open(QIODevice::ReadWrite)) {
connect(&m_serial, &QSerialPort::readyRead, this, &RS485::onReadyRead);
connect(&m_serial, &QSerialPort::errorOccurred, this, &RS485::onErrorOccurred);
return true;
} else {
emit errorOccurred(m_serial.errorString());
return false;
}
}
这里需要注意,当打开失败时,应该发出errorOccurred信号,并返回false。
closeSerialPort:
void RS485::closeSerialPort()
{
if (m_serial.isOpen()) {
m_serial.close();
}
m_buffer.clear();
disconnect(&m_serial, &QSerialPort::readyRead, this, &RS485::onReadyRead);
disconnect(&m_serial, &QSerialPort::errorOccurred, this, &RS485::onErrorOccurred);
}
发送数据:
qint64 RS485::sendData(const QByteArray &data)
{
if (!m_serial.isOpen()) {
emit errorOccurred("串口未打开");
return -1;
}
qint64 bytesWritten = m_serial.write(data);
if (bytesWritten == -1) {
emit errorOccurred(m_serial.errorString());
}
return bytesWritten;
}
处理接收数据的槽函数:
void RS485::onReadyRead()
{
QByteArray newData = m_serial.readAll();
m_buffer.append(newData);
// 示例:简单触发数据接收(无粘包处理)
// emit dataReceived(m_buffer);
// m_buffer.clear();
// 示例:按换行符分割(需要根据实际协议修改)
int pos;
while ((pos = m_buffer.indexOf('\n')) != -1) {
QByteArray frame = m_buffer.left(pos + 1); // 包含换行符
m_buffer = m_buffer.mid(pos + 1);
emit dataReceived(frame);
}
}
这里,示例代码展示了两种处理方式:一种是直接发送所有接收到的数据,另一种是按换行符分割帧。用户需要根据实际协议选择或修改。
错误处理槽函数:
void RS485::onErrorOccurred(QSerialPort::SerialPortError error)
{
if (error != QSerialPort::NoError) {
emit errorOccurred(m_serial.errorString());
}
}
需要注意,当error为NoError时,不发送信号。
在实现过程中,需要注意以下几点:
1. 在openSerialPort中,如果之前已经打开了串口,应该先关闭,再重新设置参数并打开。
2. 在发送数据时,检查串口是否已打开,否则返回错误。
3. 数据接收处理需要根据实际协议进行粘包处理,示例中提供了按换行符分割的示例,用户可能需要调整。
4. 错误处理中,当串口发生错误时,通过errorOccurred信号传递错误信息。
可能还需要处理QSerialPort的其他设置,比如流控制,但头文件中没有提到,所以暂时忽略。
最后,测试这个实现是否覆盖了头文件中的所有函数,并且信号和槽的连接正确。</think>以下是根据头文件实现的`RS485.cpp`示例代码,包含详细注释说明:
```cpp
#include "RS485.h"
#include <QDebug>
RS485::RS485(QObject *parent) : QObject(parent)
{
// 连接串口错误信号(Qt5语法)
connect(&m_serial, &QSerialPort::errorOccurred,
this, &RS485::onErrorOccurred);
}
RS485::~RS485()
{
closeSerialPort();
}
// 获取可用串口列表
QStringList RS485::getAvailablePorts() const
{
QStringList ports;
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
ports.append(info.portName());
}
return ports;
}
// 打开串口
bool RS485::openSerial
#include "qserial.h" #include<QDebug> qserial::qserial(Ui::MainWindow *ui, QObject *parent):QObject(parent), ui(ui), serialPort(new QSerialPort(this)) { QObject::connect(serialPort, &QSerialPort::readyRead, this, &qserial::onReadyRead); QList<QSerialPortInfo> PORT=QSerialPortInfo::availablePorts(); //获取可用串口 QList<qint32> bands=QSerialPortInfo::standardBaudRates(); //获取波特率 foreach (const QSerialPortInfo &port, PORT) { ui->port_chose->addItem(port.portName()); //将可用串口传入控件 } foreach ( qint32 band, bands) { ui->band_rate->addItem(QString::number(band)); //将可用波特率传入控件 } ui->band_rate->setCurrentText("115200"); } qserial::~qserial() { delete serialPort; delete ui; } bool qserial::connect() { // 设置串口名称(根据你的系统,可能需要使用具体的端口名称) serialPort->setPortName(ui->port_chose->currentText()); // 设置波特率 serialPort->setBaudRate(ui->band_rate->currentText().toInt()); // 设置数据位 serialPort->setDataBits(QSerialPort::Data8); // 设置停止位 serialPort->setParity(QSerialPort::NoParity); // 设置停止位长度 serialPort->setStopBits(QSerialPort::OneStop); // 设置流控制 serialPort->setFlowControl(QSerialPort::NoFlowControl); if (!serialPort->open(QIODevice::ReadWrite)) { return false; //返回打开失败 } else { return true; //返回打开成功 } } void qserial::dis_connect() { serialPort->close(); //关闭串口 } void qserial::write(const QString &Oder) { serialPort->write(Oder); } void qserial::onReadyRead() { // 读取所有可用数据 m_buffer += serialPort->readAll(); // 持续处理完整数据帧 while(true) { // 查找换行符位置 int endIndex = m_buffer.indexOf('\n'); if(endIndex == -1) break; // 没有完整帧则退出 // 提取完整数据(包含换行符) QByteArray frame = m_buffer.left(endIndex + 1); m_buffer = m_buffer.mid(endIndex + 1); // 移除已处理数据 // 将数据存入公共池 datapool::instance()->addData(frame.trimmed()); } } 好像onReadyRead()没有和serialport的数据到达函数connect
<think>好的,我现在需要帮助用户解决他们关于Qt串口通信的问题。用户提供的代码中,`onReadyRead()`函数似乎没有正确连接到`QSerialPort::readyRead`信号。首先,我需要仔细分析用户的代码,找出可能的问题所在。
首先,用户提到在构造函数中已经调用了`QObject::connect(serialPort, &QSerialPort::readyRead, this, &qserial::onReadyRead);`,看起来连接是正确的。但用户说好像没有连接上,这可能意味着信号没有被触发,或者槽函数没有被正确调用。我需要考虑几种可能性:
1. **连接错误**:检查connect语句是否正确,参数是否正确,比如信号和槽的签名是否匹配。`QSerialPort::readyRead`是一个信号,而`qserial::onReadyRead`是槽函数,这里使用的是新式语法连接,应该没有问题,但需要确认是否有拼写错误或者作用域问题。
2. **串口未正确打开**:如果串口没有成功打开,`readyRead`信号就不会被触发。在用户的`connect()`函数中,检查`serialPort->open`的返回值是否正确处理。如果返回false,说明串口打开失败,这时候即使连接了信号,也不会收到数据。
3. **数据未正确读取**:可能在`onReadyRead`函数中读取数据的方式有问题,比如缓冲区处理不当,导致数据没有被正确接收或处理。用户使用了`m_buffer`来累积数据,查找换行符,这可能有效,但如果发送的数据中没有换行符,或者数据分片到达,可能会导致处理逻辑不触发。
接下来,我需要逐步排查这些可能性。首先,检查连接语句是否正确。用户的代码中确实在构造函数中连接了信号和槽,使用的是正确的语法,所以这一步可能是正确的。但需要注意,`QSerialPort`的`readyRead`信号在Qt中确实存在,并且应该在新式连接中正常工作。
然后,检查串口是否成功打开。在`connect()`函数中,用户检查了`open`的返回值,如果返回false,函数会返回false。但用户可能在调用`connect()`时没有处理返回值,导致串口实际上没有打开,从而没有触发信号。需要用户确认在调用`qserial::connect()`时检查了返回值,并确保串口确实被打开了。
另外,检查串口配置是否正确,比如波特率、数据位、停止位等是否与设备匹配。如果配置错误,可能无法正确接收数据,即使串口打开了,也无法读取到数据,导致`readyRead`信号未被触发。
再者,考虑数据接收的问题。用户的`onReadyRead`函数使用了一个缓冲区来累积数据,直到遇到换行符。如果发送的数据中没有换行符,或者数据分多次到达,可能导致`indexOf('\n')`返回-1,从而不会处理数据。这时候缓冲区会一直累积,但不会触发数据处理。用户可能需要检查发送端是否发送了包含换行符的数据,或者调整处理逻辑,比如处理没有换行符的情况,或者使用其他分隔符。
此外,用户的`datapool::instance()->addData(frame.trimmed());`是否正确实现了数据存储,也需要确认。如果数据池没有正确显示或处理数据,可能会让用户误以为数据没有接收到,而实际上可能已经接收到了但未正确显示。
可能的解决方案包括:
1. **确保串口成功打开**:在调用`qserial::connect()`后检查返回值,确保返回true,或者在失败时输出错误信息,帮助调试。
2. **添加调试输出**:在`onReadyRead`函数中添加qDebug输出,确认是否被调用。如果被调用,可以查看接收到的数据内容,确认是否正确读取。
3. **检查数据发送端**:确认发送端确实发送了数据,并且数据格式正确,包含换行符或其他预期的分隔符。
4. **处理缓冲区残留数据**:在多次通信中,确保缓冲区被正确清空,避免旧数据影响新数据的处理。
5. **验证信号槽连接**:在运行时检查信号槽是否成功连接,可以通过`connect`的返回值或者使用Qt的信号槽调试工具。
最后,用户可能需要逐步调试,添加日志输出,确认各个步骤是否按预期执行,从而定位问题所在。</think>在Qt串口通信中,`readyRead`信号与槽函数的正确连接至关重要。根据你提供的代码,以下是关键分析和解决方案:
**错误现象定位**:
```cpp
QObject::connect(serialPort, &QSerialPort::readyRead, this, &qserial::onReadyRead);
```
虽然代码中存在该连接语句,但实际运行时`onReadyRead()`未被触发,常见原因有:
---
**核心问题排查表**:
| 问题可能性 | 验证方法 | 典型症状 |
|---------|--------|--------|
| 信号未正确连接 | 运行时输出连接状态 | 完全无数据接收 |
| 串口未成功打开 | 检查open()返回值 | 发送数据无响应 |
| 数据未完整接收 | 添加调试输出 | 收到部分数据 |
| 波特率不匹配 | 比对设备参数 | 接收到乱码 |
| 缓冲区处理异常 | 检查m_buffer状态 | 数据累积但未处理 |
---
**逐步调试方案**:
1. **验证信号连接有效性**(关键步骤)
```cpp
// 修改构造函数中的连接语句
bool isConnected = QObject::connect(serialPort, &QSerialPort::readyRead,
this, &qserial::onReadyRead);
qDebug() << "Signal connection status:" << isConnected; // 应输出true
```
2. **确保串口成功打开**
```cpp
bool qserial::connect()
{
// ...原有配置代码...
if (!serialPort->open(QIODevice::ReadWrite))
{
qDebug() << "Open failed:" << serialPort->errorString(); // 添加错误详情
return false;
}
qDebug() << "Port opened:" << serialPort->portName(); // 确认实际打开的端口
return true;
}
```
3. **添加接收调试输出**
```cpp
void qserial::onReadyRead()
{
qDebug() << "Data received! Buffer size:" << m_buffer.size(); // 验证触发
m_buffer += serialPort->readAll();
qDebug() << "Raw data:" << m_buffer; // 显示原始数据
// ...原有处理代码...
}
```
4. **验证物理连接参数**
```cpp
// 在connect()函数中添加配置验证输出
qDebug() << "Current settings:"
<< "\nPort:" << ui->port_chose->currentText()
<< "\nBaud:" << serialPort->baudRate()
<< "\nDataBits:" << serialPort->dataBits()
<< "\nParity:" << serialPort->parity();
```
---
**典型修正案例**:
```cpp
// 修改后的构造函数(添加安全验证)
qserial::qserial(Ui::MainWindow *ui, QObject *parent)
: QObject(parent), ui(ui), serialPort(new QSerialPort(this))
{
// 确保UI组件存在
Q_ASSERT(ui->port_chose != nullptr);
Q_ASSERT(ui->band_rate != nullptr);
// 带验证的信号连接
if(!QObject::connect(serialPort, &QSerialPort::readyRead,
this, &qserial::onReadyRead)){
qFatal("Failed to connect readyRead signal!");
}
// ...其余初始化代码不变...
}
```
---
**增强健壮性建议**:
1. 添加超时处理机制
```cpp
// 在类声明中添加
QTimer *receiveTimer;
// 构造函数中初始化
receiveTimer = new QTimer(this);
connect(receiveTimer, &QTimer::timeout, [this](){
if(!m_buffer.isEmpty()) {
qDebug() << "Unprocessed data:" << m_buffer;
m_buffer.clear();
}
});
receiveTimer->start(2000); // 每2秒清理残留数据
```
2. 完善错误处理
```cpp
// 在类中添加错误信号
signals:
void errorOccurred(const QString &msg);
// 修改open失败处理
if (!serialPort->open(QIODevice::ReadWrite))
{
emit errorOccurred(tr("打开失败: %1").arg(serialPort->errorString()));
return false;
}
```
---
**完整数据流验证流程**:
1. 使用虚拟串口工具(如com0com)创建端口对
2. 使用串口调试助手发送含换行符的测试数据
3. 观察控制台输出:
- 应看到"Data received!"调试信息
- 显示原始数据是否符合预期
4. 检查datapool中的数据是否完整
若经过上述调试仍无数据,建议:
1. 检查Qt版本是否包含串口模块(通过`QT += serialport`)
2. 在pro文件中确认模块包含:
```makefile
QT += core serialport widgets
```
3. 验证硬件驱动是否正确安装(设备管理器查看端口状态)
阅读全文
相关推荐









