版本 | 作者 | 日期 | 备注 |
---|---|---|---|
0.1 | loon | 2018.10.26 | 初稿 |
串口对于上位机配置软件来说是很重要的,因为我们不可避免的要用到串口、CAN、ModBus等,而目前我使用的Qt5.6.0是具有Serial Port和Serial Bus的相关类的。
目前我们先了解serial port相关的类,然后我将参照其示例做一个满足我需要的串口工具,这可能会作为一个长期的项目放在GitHub上,会逐步将我工作和研究过程中需要的一些功能加进去(由于notepad++只能用在windows上其实我还想利用Qt将notepad++的功能仿照一下做个用于linux的文本编辑器的,可惜暂时串口工具比较重要,只好以后找时间了,目前我的ubuntu上用的vim+sublime text3)。
OK,还是先根据帮助文档,直接搜索serial,发现Qt serial port和Qt serial bus,我们本次总结先说明整个serial port类:
我们根据帮助文档说明首先可以了解到其使用范围和局限性:
Qt Serial Port provides the basic functionality, which includes configuring, I/O operations, getting and setting the control signals of the RS-232 pinouts.
The following items are not supported by this module:
Qt串口提供了基本的功能,包括配置,I/O操作,获取和设置RS-232输出的控制信号。
本模块不支持以下项目:
那么根据这些说明你首先要确认一下Qt目前的串口类是否能满足你的需要,根据上面所说,基本满足我的需要。
To use these classes in your application, use the following include statement:
#include
To link against the module, add this line to your qmake .pro file:
QT += serialport
说的很明确:在.pro文件中增加serialport,使用前增加对应的头文件。
分为两大类:
QSerialPort | Provides functions to access serial ports |
---|---|
QSerialPortInfo | Provides information about existing serial ports |
QSerialPort用于提供访问串行端口的功能 ,QSerialPortInfo用于提供关于现有串行端口的信息。
当然要了解更多在帮助文档中有QSerialPort的所有成员列表。
Public Types
enum | BaudRate { Baud1200, Baud2400, Baud4800, Baud9600, …, UnknownBaud } |
---|---|
enum | DataBits { Data5, Data6, Data7, Data8, UnknownDataBits } |
enum | Direction { Input, Output, AllDirections } |
flags | Directions |
enum | FlowControl { NoFlowControl, HardwareControl, SoftwareControl, UnknownFlowControl } |
enum | Parity { NoParity, EvenParity, OddParity, SpaceParity, MarkParity, UnknownParity } |
enum | PinoutSignal { NoSignal, TransmittedDataSignal, ReceivedDataSignal, DataTerminalReadySignal, …, SecondaryReceivedDataSignal } |
flags | PinoutSignals |
enum | SerialPortError { NoError, DeviceNotFoundError, PermissionError, OpenError, …, UnknownError } |
enum | StopBits { OneStop, OneAndHalfStop, TwoStop, UnknownStopBits } |
Public Functions
QSerialPort(QObject *parent = Q_NULLPTR) | |
---|---|
QSerialPort(const QString &name, QObject *parent = Q_NULLPTR) | |
QSerialPort(const QSerialPortInfo &serialPortInfo, QObject *parent = Q_NULLPTR) | |
virtual | ~QSerialPort() |
qint32 | baudRate(Directions directions = AllDirections) const |
bool | clear(Directions directions = AllDirections) |
void | clearError() |
DataBits | dataBits(const |
SerialPortError | error() const |
FlowControl | flowControl() const |
bool | flush() |
Handle | handle() const |
bool | isBreakEnabled() const |
bool | isDataTerminalReady() |
bool | isRequestToSend() |
Parity | parity() const |
PinoutSignals | pinoutSignals() |
QString | portName() const |
qint64 | readBufferSize() const |
(deprecated) bool | sendBreak(int duration = 0) |
bool | setBaudRate(qint32 baudRate, Directions directions = AllDirections) |
bool | setBreakEnabled(bool set = true) |
bool | setDataBits(DataBits dataBits) |
bool | setDataTerminalReady(bool set) |
bool | setFlowControl(FlowControl flowControl) |
bool | setParity(Parity parity) |
void | setPort(const QSerialPortInfo &serialPortInfo) |
void | setPortName(const QString &name) |
void | setReadBufferSize(qint64 size) |
bool | setRequestToSend(bool set) |
bool | setStopBits(StopBits stopBits) |
StopBits | stopBits() const |
Reimplemented Public Functions
virtual bool | atEnd() const |
---|---|
virtual qint64 | bytesAvailable() const |
virtual qint64 | bytesToWrite() const |
virtual bool | canReadLine() const |
virtual void | close() |
virtual bool | isSequential() const |
virtual bool | open(OpenMode mode) |
virtual bool | waitForBytesWritten(int msecs) |
virtual bool | waitForReadyRead(int msecs) |
Signals
void | baudRateChanged(qint32 baudRate, QSerialPort::Directions directions) |
---|---|
void | breakEnabledChanged(bool set) |
void | dataBitsChanged(QSerialPort::DataBits dataBits) |
void | dataTerminalReadyChanged(bool set) |
void | error(QSerialPort::SerialPortError error) |
void | flowControlChanged(QSerialPort::FlowControl flow) |
void | parityChanged(QSerialPort::Parity parity) |
void | requestToSendChanged(bool set) |
void | stopBitsChanged(QSerialPort::StopBits stopBits) |
下面是对如何使用串口的详细描述:
您可以使用QSerialPortInfo助手类获得关于可用串行端口的信息,该类允许枚举系统中的所有串行端口。这对于获得您想要使用的串口的正确名称非常有用。您可以将helper类的对象作为参数传递给setPort()或setPortName()方法,以分配所需的串行设备。
设置端口后,可以使用open()方法以只读(r/o)、只读(w/o)或读写(r/w)模式打开它。
注意:串行端口总是以独占访问打开(也就是说,没有其他进程或线程可以访问已经打开的串行端口)。
成功打开后,QSerialPort尝试确定端口的当前配置并初始化自身。您可以使用setBaudRate()、setDataBits()、set奇偶校验()、setStopBits()和setFlowControl()方法将端口重新配置到所需的设置。
使用pinout信号有两个属性:QSerialPort::dataTerminalReady, QSerialPort::requestToSend。还可以使用pinoutSignals()方法查询当前pinout信号集。
一旦知道端口已经准备好可以读或写,就可以使用read()或write()方法。或者也可以调用readLine()和readAll()方便的方法。如果不是一次读取所有数据,那么剩余的数据将在稍后可用,因为新的传入数据将附加到QSerialPort的内部读取缓冲区。可以使用setReadBufferSize()限制读取缓冲区的大小。
使用close()方法关闭端口并取消I/O操作。
请看下面的例子:
int numRead = 0, numReadTotal = 0;
char buffer[50];
forever {
numRead = serial.read(buffer, 50);
// Do whatever with the array
numReadTotal += numRead;
if (numRead == 0 && !serial.waitForReadyRead())
break;
}
如果waitForReadyRead()返回false,则连接已关闭或发生错误。
使用阻塞串行端口进行编程与使用非阻塞串行端口进行编程是完全不同的。阻塞串行端口不需要事件循环,通常会导致更简单的代码。然而,在GUI应用程序中,为了避免冻结用户界面,阻塞串行端口只能在非GUI线程中使用。
有关这些方法的详细信息,请参阅示例应用程序。
QSerialPort类还可以与QTextStream和QDataStream的流操作符(操作符<<()和操作符>>())一起使用。但是有一个问题需要注意:在尝试使用操作符>>()重载操作符读取数据之前,确保有足够的数据可用。
其实读完上面的描述后,在对串口读写有一定基础的基本上已经知道如何使用Qt自带的串口了,只要再结合示例看一下Qt的串口操作基本就没什么问题了。
Public Functions
QSerialPortInfo() | |
---|---|
QSerialPortInfo(const QSerialPort &port) | |
QSerialPortInfo(const QString &name) | |
QSerialPortInfo(const QSerialPortInfo &other) | |
~QSerialPortInfo() | |
QString | description() const |
bool | hasProductIdentifier() const |
bool | hasVendorIdentifier() const |
bool | isBusy() const |
bool | isNull() const |
QString | manufacturer() const |
QString | portName() const |
quint16 | productIdentifier() const |
QString | serialNumber() const |
void | swap(QSerialPortInfo &other) |
QString | systemLocation() const |
quint16 | vendorIdentifier() const |
QSerialPortInfo & | operator=(const QSerialPortInfo &other) |
Static Public Members
QList | availablePorts() |
---|---|
QList | standardBaudRates() |
详细使用描述:
提供关于现有串行端口的信息。
使用静态函数生成QSerialPortInfo对象列表。列表中的每个QSerialPortInfo对象表示一个串行端口,可以查询端口名称、系统位置、描述和制造商。QSerialPortInfo类还可以用作QSerialPort类的setPort()方法的输入参数。
找到Qt Serial Port,点击Examples,则可以看到Qt Serial Port module的使用示例的详细说明,在示例中输入serial则搜索到对应的工程文件。
示例名 | 示例功能说明 |
---|---|
Blocking Master Example | Shows how to use the synchronous API of QSerialPort in a worker thread.( 演示如何在工作线程中使用QSerialPort的同步API。 ) |
Blocking Slave Example | Shows how to use the synchronous API of QSerialPort in a non-GUI thread.( 演示如何在非gui线程中使用QSerialPort的同步API。 ) |
Command Line Enumerator Example | Shows how to get information about serial devices in a system.( 演示如何获取系统中串行设备的信息。 ) |
Command Line Reader Async Example | Shows how to receive data asynchronously over serial port.( 演示如何通过串口异步接收数据。 ) |
Command Line Reader Sync Example | Shows how to receive data synchronously over serial port.( 显示如何通过串口同步接收数据。 ) |
Command Line Writer Async Example | Shows how to send data asynchronously over serial port.( 演示如何通过串口异步发送数据。 ) |
Command Line Writer Sync Example | Shows how to send data synchronously over serial port.( 显示如何通过串口同步发送数据。 ) |
Enumerator Examplelport-enumerator-example.html) | Shows how to display information about serial devices in a system.( 演示如何显示系统中的串行设备信息。 ) |
Terminal Example | Shows how to use various features of QSerialPort.( 演示如何使用QSerialPort的各种特性。 ) |
各个例子都分析学习一下,下面我们直接分析最后一个例子。
最后一个例子基本上已经有了串口工具的影子了。
例子具体的设计与代码说明在帮助文档中有,我们大致过一下。
大致说:这个例子展示了QSerialPort类的IO操作和配置等主要特点,并且用QSerialPortInfo显示了当前系统中可用的串口信息。QSerialPort提供两种通用的编程途径:异步非阻塞和同步阻塞。
然后阐述了一下两种方式:
异步(非阻塞)的方式时:当控件返回到Qt的事件循环当中时,将调度并执行操作。QSerialPort在操作完成时发出信号。例如,QSerialPort::write()立即返回。当数据发送到串行端口时,QSerialPort发出bytesWritten()。同步(阻塞)方法:在非gui和多线程应用程序中,可以调用waitFor…()函数(例如QSerialPort::waitForReadyRead())来挂起调用线程,直到操作完成。
在这个例子中用的是异步方式。要看同步方式那么去看上面我说的其它示例。
这个例子目前包含三个GUI窗体小部件:
MainWindow - 是主应用程序窗口,它包含串行端口编程的所有工作逻辑,包括配置、I/O处理等,同时继承QMainWindow。
Console - 是主窗口的中心小部件,显示传输或接收的数据。小部件派生自QPlainTextEdit类。
SettingsDialog - 是一个对话框,用于配置串口,以及显示可用的串口和有关串口的信息。
OK,到这里已经不必多说了,程序的结构已经清晰了,就顺着MainWindow—SettingsDialog—Console的流程一步一步去搞清楚每一个细节的含义就可以了。主要还是信号和槽以及事件的内容,这部分暂时不展开来说,暂时知道就这么用就行。
说到底,Qt本身是一个框架,我们目前在此基础上开发软件大多数情况下就是调用框架本身提供的API,此外,其将底层的系统调用进行了封装成为所谓的信号槽机制以及事件机制等,了解其使用简单,但要深入了解其原理则不得不了解组件、模块、框架、架构、信号、操作系统等一系列知识,这是一个积累和扩展的过程,了解的越多,边界越大,但也更接近本质,更能灵活的运用。
后面计划开发一个适合自己用的串口调试工具,也许会结合网络编程开发远程调试功能。