1、Qt 串行通讯161109Qt 串行通讯Hanford2016年11月09日目 录第1章 Qt 串行通讯 11.1 配置.pro文件 11.2 查询串口信息 11.3 配置、打开串口 31.4 setRequestToSend在Windows上的BUG 51.5 读取串口数据 61.6 发送串口数据 71.7 同步读取 71.8 本文示例代码 81.9 Qt 示例代码 10第1章 Qt 串行通讯最近要在 Android 手机上开发串行通讯程序,为此学习了一下Qt的串行通讯。本文中,Qt的版本为 5.7.0。1.1 配置.pro文件使用 Qt 5.7.0 创建“Qt Widgets Applic
2、ation”类型的项目,然后修改.pro文件,如下图所示:图1.1给变量 QT 增加serialport,说明程序里将使用串行通讯相关的类。1.2 查询串口信息本节将通过代码查找系统里的串口,然后填入下图所示的下拉列表框中。图1.2函数 QSerialPortInfo:availablePorts 会返回系统所有的串口,它的使用请参考如下代码:#include #include std:map mapPort;QString sPort;int nPort;foreach (const QSerialPortInfo &info,QSerialPortInfo:availablePorts()
3、/foreach 遍历QSerialPortInfo:availablePorts() 的返回值 sPort = info.portName(); /串口名称,如:COM5 nPort = GetIntInStr(sPort); /根据串口名称获取串口号,如:5 if(nPort = 0) mapPortnPort = sPort; /根据串口号排序,加入 map 函数GetIntInStr 根据串口名称(如COM5)获取串口号(如:5),其代码如下:/*从字符串里提取整数s in 字符串返回:提取出来的整数,-1 表示错误*/int GetIntInStr(const QString&s)
4、bool bOK = false; /是否发现了数字 int n = 0; int nLenS = s.length(); /字符串长度 ushort c = 0; for(int i = 0;i = 0 & c = 9) bOK = true; n = n * 10 + (c - 0); if(!bOK) n = -1;/没有数字,返回 -1 return n;根据 std:map mapPort填充下拉列表框QComboBox cboPort的代码如下:ui-cboPort-clear();for(std:map:iterator it = mapPort.begin(); it != m
5、apPort.end();+it) ui-cboPort-addItem(it-second);1.3 配置、打开串口配置、打开串口的代码如下:#include m_port = new QSerialPort();m_port-setPortName(COM1); /打开 COM1m_port-setBaudRate(9600); /波特率:9600m_port-setParity(QSerialPort:NoParity); /校验法:无m_port-setDataBits(QSerialPort:Data8); /数据位:8m_port-setStopBits(QSerialPort:O
6、neStop); /停止位:1m_port-setFlowControl(QSerialPort:NoFlowControl); /流控制:无if(m_port-open(QIODevice:ReadWrite)/成功打开串口 m_port-setRequestToSend(true); /设置 RTS 为高电平 m_port-setDataTerminalReady(true); /设置 DTR 为高电平首先new一个QSerialPort对象,然后设置该对象的串行通讯参数,最后调用QSerialPort:open函数打开串口。这里需要说明一下流控制。通讯的双方A和B,假如A给B发送数据时,
7、B反应过慢,A不管不顾的不停发送数据,结果会导致数据丢失。为了防止这种情况发生,可使用流控制(也叫握手)。软件流控制(XON/XOFF):通讯的一方(B)如果不能及时处理串口数据,会给对方(A)发送XOFF字符,对方接收到这个字符后,会停止发送数据;B不再忙的时候,会给A发送XON字符,A接收到这个字符后,会接着发送数据。软件流控制最大的问题就是不能传输XON和XOFF。硬件流控制(RTS/CTS):硬件流控制需要按下图连接两个串口设备的RTS和CTS。图1.3通讯的一方(B)如果不能及时处理串口数据,会设置自己的RTS为低电平,B的RTS连着对方(A)的CTS,A发现自己的CTS为低电平,将
8、停止发送数据;B不再忙的时候,会设置自己的RTS为高电平,A发现自己的CTS为高电平,将接着发送数据。上面的代码中,设置流控制为无,其含义为:不管对方是否能够反应过来,这边只管发送数据。当流控制为硬件时,系统会自动管理RTS和DTR的状态。否则,应该设置RTS和DTR为高电平,通知对方可以发送串口数据了。1.4 setRequestToSend在Windows上的BUG经测试,流控制为无时,调用m_port-setRequestToSend(true);是没有任何效果的。下面的Qt源代码节选自文件C:QtQt5.7.05.7Srcqtserialportsrcserialportqserial
9、port_win.cpp(C:QtQt5.7.0是Qt的安装目录)bool QSerialPortPrivate:setRequestToSend(bool set) if (!:EscapeCommFunction(handle, set ? SETRTS : CLRRTS) setError(getSystemError(); return false; return true;上面的代码调用EscapeCommFunction(handle,SETRTS)似乎没什么问题,但是请看下面的代码:bool QSerialPortPrivate:setFlowControl(QSerialPor
10、t:FlowControl flowControl) . . . dcb.fRtsControl = RTS_CONTROL_DISABLE; switch (flowControl) case QSerialPort:NoFlowControl: break; case QSerialPort:SoftwareControl: break; case QSerialPort:HardwareControl: dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; break; . . .硬件流控制时dcb.fRtsControl为RTS_CONTROL_HANDSH
11、AKE,这个没问题。问题出在dcb.fRtsControl = RTS_CONTROL_DISABLE上,它直接禁用了RTS,所以EscapeCommFunction(handle,SETRTS)并不能设置RTS为高电平。那么m_port-setDataTerminalReady(true)为什么又是正常的呢?看代码:bool QSerialPortPrivate:setDataTerminalReady(bool set) . . . dcb.fDtrControl = set ? DTR_CONTROL_ENABLE : DTR_CONTROL_DISABLE; . . .set 为 tr
12、ue 时,dcb.fDtrControl 为 DTR_CONTROL_ENABLE,所以可以设置DTR为高电平。1.5 读取串口数据m_port-readAll(QIODevice:readAll)用来读取串口数据。不过,它是异步执行的。什么是异步呢?那就是即使对方还没有发送串口数据,m_port-readAll也会立即返回,而不是傻傻的等着对方发送数据过来后再返回。既然是异步的,那么何时读取串口数据就成为了关键。Qt提供的方案就是使用信号、槽。connect(m_port,SIGNAL(readyRead(),this,SLOT(slotReadData();当对方发送串口数据后,将触发m_
13、port的信号QIODevice:readyRead。上面的代码将信号readyRead与槽函数slotReadData连接了起来,因此槽函数slotReadData将被调用,其代码如下:void Widget:slotReadData() QByteArray data; const int nMax = 64 * 1024; for(;) data = m_port-readAll(); /读取串口数据 if(data.isEmpty() /没有读取到串口数据就退出循环 break; /读取到的串口数据,加入到QByteArray m_dataCom m_dataCom.append(da
14、ta); if(m_dataCom.size() nMax) /防止 m_dataCom 过长 m_dataCom = m_dataCom.right(nMax); ui-txtRecv-setText(m_dataCom); /将 m_dataCom 显示到文本框 ui-txtRecv-moveCursor(QTextCursor:End); /移动文本框内的插入符1.6 发送串口数据m_port-write(QIODevice:write)用来发送串口数据,不过它也是异步的。也就是说:代码m_port-write(123);会立即返回,至于数据123何时会发送给对方,那是操作系统的事情。操
15、作系统不忙的时候,才会做此项工作。参考如下代码:char szData32;strcpy(szData,123);m_port-write(szData);m_port-write(szData);这行代码会立即返回。操作系统在空闲时才会将szData里的数据发送给对方。问题是:m_port-write返回后,变量szData有可能已经被析构掉了,此时操作系统再访问szData所指向的内存,结果不可预料。为此,需要改进上述代码,将异步通讯更改为同步通讯:char szData32;strcpy(szData,123);m_port-write(szData);m_port-waitForByt
16、esWritten(5000);就增加了一行代码m_port-waitForBytesWritten(5000);其含义为:操作系统把串口数据szData发送出去后,m_port-waitForBytesWritten才会返回。不过,总不能无限制等下去吧?5000就是等待时间的最大值,其单位为毫秒,5000毫秒就是5秒。1.7 同步读取异步通讯的效率比较高,但是代码结构比较复杂。有时,需要同步读取。如:给对方发送字符串 Volt,对方回应电压值 5。代码会这么写:m_port-write(Volt);m_port-waitForBytesWritten(5000);QByteArray dat
17、a;for(;) data = m_port-readAll(); /读取串口数据 if(!data.isEmpty() /读到数据了,退出循环 break; 通过一个无限循环,将异步读取变成了同步读取。不过,上述代码运行时,CPU占用率将会达到100%(单核CPU)。为此,需要改进代码:m_port-write(Volt);m_port-waitForBytesWritten(5000);QByteArray data;while(m_port-waitForReadyRead(3000) data = m_port-readAll(); /读取串口数据 if(!data.isEmpty()
18、 /读到数据了,退出循环 break; 修改了一行代码m_port-waitForReadyRead(3000),其含义为等待对方发送串口数据过来。如果对方发送串口数据过来了,它返回true,然后使用m_port-readAll读取串口数据;如果对方在3秒内都没有发送串口数据过来,它返回false,退出循环。1.8 本文示例代码本文示例代码已上传至 git 服务器,具体如下:为了便于测试,可使用Virtual Serial Port Driver 7.1创建一个串口对COM100、COM200,如下图所示:图1.4上图的COM100、COM200是虚拟出来的。COM100发送的数据将会被COM200接收到,反之亦然。下图是测试界面:图1.5本文示例代码打开了COM100,另一个串行通讯程序打开了COM200。两者可以相互发送数据。1.9 Qt 示例代码安装完 Qt 5.7.0 后,Qt 串行通讯的示例代码也被安装了,如下图所示:图1.6C:QtQt5.7.0是笔者安装Qt 5.7.0时的安装目录。需要进一步了解Qt串行通讯的可查看这些示例。如:creaderasync 表示异步读取;creadersync 表示同步读取更多信息查看Qt帮助。
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1