Qt的QSerialPort模块,提供了通过串口与硬件通信的功能。Qt程序利用QSerialPort,通过串口与硬件通信的步骤一般是:
- 通过
QSerialPortInfo::availablePorts()
接口列举所有可用串口 - 选择硬件对应的串口
- 创建
QSerialPort
的实例:QSerialPort port = new QSerialPort(portName)
- 配置串口,比如波特率之类的:
port->setBaudRate(QSerialPort::Baud19200);
- 打开串口:
port->open(QIODevice::ReadWrite)
,根据open
的返回结果,判断串口是否打开成功 - 利用SIGNAL
readyRead()
判断硬件是否发送了数据 - 收到signal后,利用
port->readAll()
读取缓存中的所有数据 - 对数据完成解析,然后使用数据
代码如下
QList<QSerialPortInfo> ports = QSerialPortInfo:::availablePorts();
// 假设ports不为空
QString targetPortName = ports.first().portName();
QSerialPort *port = new QSerialPort(targetPortName);
port->setBaudRate(QSerialPort::Baud19200);
if(!port->open(QIODevice::ReadWrite)) {
// open serial port failed
}
connect(port, &QSerialPort::readyRead, this, [=](){
QByteArray data = port->readAll();
decode(data); // 自定义解码数据
});
我在项目中使用上述代码一直没有问题,都能正常读取并解析数据,直到一次硬件更新,忽然无法从硬件获取数据了。
通过排查发现,readyRead
Signal不再被触发了,改成以轮询的方式读取后果然还是能够读到数据,并正确解析的。轮询方式如下:
while(true) {
QByteArray data = port->readAll();
decode(data);
}
这种方式虽然可以读取到数据,但即便使用多线程,对系统资源的占用也明显太高了,没有从根本上解决问题。
那么造成无法从硬件获取数据的根本原因是什么呢? 为什么之前work的代码,硬件更新后就无法正常工作了?难道是硬件的锅?
通过跟硬件团队沟通,发现原因在于打开串口时,串口的缓存已经满了,导致硬件不能再写入输入,也就不会触发readReady
Signal了,这个问题其他人也有提到过,比如这个。
那为了解决这个问题,有两种解决方案:
- 判断buffer是否已经满了,如果满了就读取数据
- 每次开始读取之前,先清空buffer
前者可以利用winbase.h
中的EV_RX80FULL
。Qt虽然可以使用winbase.h
中的接口,但毕竟不太方便,而且可能需要重构原来使用QSerialPort
模块的代码。
因此最后采用方案2,QSerialPort
模块提供了接口clear()
,作用是:
Discards all characters from the output or input buffer, depending on given directions directions. This includes clearing the internal class buffers and the UART (driver) buffers.
在开始读取数据之前,利用该接口清空buffer数据,硬件就可以继续写数据,并触发readReady
Signal。
代码如下
QList<QSerialPortInfo> ports = QSerialPortInfo:::availablePorts();
// 假设ports不为空
QString targetPortName = ports.first().portName();
QSerialPort *port = new QSerialPort(targetPortName);
port->setBaudRate(QSerialPort::Baud19200);
if(!port->open(QIODevice::ReadWrite)) {
// open serial port failed
}
port->clear(QSerialPort::AllDirections);
connect(port, &QSerialPort::readyRead, this, [=](){
QByteArray data = port->readAll();
decode(data); // 自定义解码数据
});
至此,问题解决。