Qt (6) -- Qt5.5.1实现通用串口程序

最近教研室在做高速相机,通过图像采集卡和自己做的高速相机链接,从而采集和收集图像。图像采集卡和高速相机是通过Cameralink连接,其中也包含了相机和图像采集卡之间的串口的硬件借口,在老师的要求下,实现了两种串口程序,一种是通过cameralink的API实现串口数据的传输,一种则是通过USB转RS422的转接器,实现对高速相机的寄存器的读写。本文主要介绍了通过Qt的QSerialPort和QSerialPortInfo两个类,实现的串口程序。

本文采用的的开发环境是VS2010+Qt5.5.1版本,所有程序不是通过Qt Creator编译的,如果有需要可以介绍VS2010和Qt环境的搭建和简单的使用。

  • QSerialPort

QSerialPort这个类是从QT5.1开始引入的,之前都是通过QIODevice自己定义串口类,从而实现串口程序的开发。现在引入这个类了,将会非常方便的开发串口程序。为了使用这个类,需要在工程目录和附加依赖项中加入include的路径,以及链接库的路径,以及链接库的名称:

  • 项目--->属性---> C++ ---> 常规 --->C:\Qt\Qt5.5.1\5.5\msvc2010\include\QtSerialPort
  • 项目--->属性---> 输入++ ---> 附加依赖项--->Qt5SerialPort.lib(如果是Debug版本,则是Qt5SerialPortd.lib)版本
  • 源文件或者头文件需要加入#include<QSerialPort>

串口的信息可以通过QSerialPortInfo类获得,通过这个类,你可以正确的确定你要开启的串口,同时可以获得串口的描述性信息以及厂家信息。串口有三种打开模式,即ReadOnly,WriteOnly,以及ReadWrite。同时可以设置其停止位,波特率,数据位,校验方式以及流控,对应的函数方式分别为:setStopBits(),setBaudRates(),setDataBits(),setParity(),以及setFlowControl()。

串口数据的写入是通过writeData(const char * data, qint64 maxSize)以及write(const char * data, qint64 maxSize),前者是protected属性,只能在子类中访问,而后者则是public属性。在串口打开并且具有可写属性,即可通过write函数写入数据。

串口数据的读入是通过
readData(char * data, qint64 maxSize) ,read(qint64 maxSize)实现的,如果需要一次性读完所有的数据,则可以通过readAll()全部读取串口缓冲区中的数据。

串口内部的缓冲区大小可以通过:setReadBufferSize(qint64 size)实现。当设定缓冲区大小时,串口只能接收size大小的数据流,因此存在数据丢书的可能。当设置为0的时候,并不是指的缓冲区大小为0,而是无穷大,这样就可以保存数据的全部接收完整。这是缓冲区大小的默认属性。

是否有新的数据读入缓冲区是通过readReady()信号来来确定的。这是通过时间驱动的方式来确定是否有数据可以读入。此外还有waitForReadyRead()来等待轮询是否有数据到达串口,但是这是一种阻塞性读入,个人觉得不是太好,所以写串口的时候采用的是事件驱动的方式来确定是够有数据可以读入。

  • QSerialPortInfo

Use the static functions to generate a list of QSerialPortInfo objects. Each QSerialPortInfo object in the list represents a single serial port and can be
queried for the port name, system location, description, and manufacturer. The QSerialPortInfo class can also be used as an input parameter for the setPort() method of the QSerialPort class.

可能会用得比较多的函数是description(),manufacturer(),以及serialNumber()。从而得到描述性信息,比如通信端口。USB转串口等描述串口的信息、串口的生产商信息以及串口名,在电脑上表现为COM~等。

  • 如何获取电脑上所有的串口

void CameraSerialPort::getSerialPortID()
{
	 serialInfo=new QSerialPortInfo();
	 serialList=serialInfo->availablePorts();
	int nSerialnum=serialList.length();
	for(int i=0;i<nSerialnum;i++)
	{
		QString serialName=serialList[i].portName();
		QString serialDesp=serialList[i].description();
		serialPortAssitant.serialPortcomboBox->addItem(serialName);
	}

	QString currentPort=serialPortAssitant.serialPortcomboBox->currentText();
	portToOpen=currentPort;
	QString  portStatus=currentPort+" closed";
	serialPortAssitant.status->statusInfo->setText(portStatus.toUpper());
	QPalette font_palette;
	font_palette.setColor(QPalette::WindowText,Qt::red);
	serialPortAssitant.status->statusInfo->setPalette(font_palette);
}

因为直接从自己的项目文件拷过来的源码,这里稍微介绍一下属性:

1、变量的定义,在头文件中,这里没有贴出来,截取定义如下:

	QSerialPortInfo* serialInfo;
	QList<QSerialPortInfo>serialList;

2、QList<QSerialPortInfo>availablePorts() 返回的是一个关于QSerialPortInfo的列表,在数据结构QList中存储。

3、serialPortcomBox是一个QComboBox,下拉列表。

4、最后几行是用来显示串口的状态信息,达到的效果如图:

  • 打开串口并且通过串口写数据

得到串口信息后,就可以选择端口,进行打开和读写数据。贴出代码,然后在给分析分析:

void CameraSerialPort::Write()
{
	<span style="background-color: rgb(255, 204, 51);">QString sendMsg=serialPortAssitant.sendLine->text();</span>
	<span style="background-color: rgb(255, 204, 0);">QByteArray temp=sendMsg.toLatin1();</span>
	if(IsSendByHex)
	{
		temp=HexStrToByteArray(sendMsg);
	}
	<span style="background-color: rgb(255, 204, 0);">char *sendContent=temp.data();</span>
	<span style="background-color: rgb(255, 204, 0);">qint64 sendedNum=serialPort->write(sendContent,temp.count())</span>;

	//---------------判断发送数据是否成功----------------------//
	if(sendedNum==-1)
	{
		errorValue=serialPort->error();
		if(IsShowCurrentTime)
		{
			errorInfo=" ";
			currentTime=QDateTime::currentDateTime();
			timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
			errorInfo=QString::fromLocal8Bit("错误提示信息   ");
			errorInfo+=timeinfo;
			errorInfo+="\n";
		}
		else
		{
			errorInfo=" ";
			errorInfo=QString::fromLocal8Bit("错误提示信息   ");
			errorInfo+="\n";
		}
		serialPortAssitant.ReciveWidget->append(errorInfo+getValueContent(errorValue));
		return;
	}
	//-------------显示发送数据-----------------------//

	//temp的size的依据是是否以16进制发送
	sendCount+=temp.count();
	serialPortAssitant.status->TxByte->setText(QString::number(sendCount));
	QString showSendMsg;
	if(IsShowSendMsg)
	{
		if(IsShowCurrentTime)
		{

			currentTime=QDateTime::currentDateTime();
			timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
			showSendMsg+=QString::fromLocal8Bit("发送数据 : ");
			showSendMsg+=timeinfo;
			showSendMsg+="\n";
			//判断显示16进制还是ACSII字符
			if(IsSendByHex)
				showSendMsg+=ByteArrayToHexStr(temp);
			else
				showSendMsg+=temp;
		}
		else
		{
			showSendMsg=QString::fromLocal8Bit("发送数据 : ");
			if(IsSendByHex)
				showSendMsg+=ByteArrayToHexStr(temp);
			else
				showSendMsg+=temp;

		}
		serialPortAssitant.ReciveWidget->append(showSendMsg);

	}
	IsWrittenSuccessed=true;
}
void CameraSerialPort::sendData()
{
	if(!IsSerialPortOpen)
	{
			if(serialPort!=NULL)
			{
				serialPort->close();
			}
			<span style="background-color: rgb(255, 204, 0);">serialPort=new QSerialPort(portToOpen);</span>
			if(serialPort==NULL)
			{
				errorValue=serialPort->error();
				QString errorContent=getValueContent(errorValue);
				if(IsShowCurrentTime)
				{
						errorInfo=" ";
						currentTime=QDateTime::currentDateTime();
						timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
						errorInfo=QString::fromLocal8Bit("错误提示信息   ");
						errorInfo+=timeinfo;
						errorInfo+="\n";
				}
					else
				{
						errorInfo=" ";
						errorInfo=QString::fromLocal8Bit("错误提示信息   ");
						errorInfo+="\n";
				}
					serialPortAssitant.ReciveWidget->append(errorInfo +errorContent+QString::fromLocal8Bit(", 请重新选择正确的端口\n"));
					return;
			}

		<span style="background-color: rgb(255, 204, 0);">if(!serialPort->open(QIODevice::ReadWrite))</span>
		{
			errorValue=serialPort->error();
			QString errorContent=getValueContent(errorValue);
			if(IsShowCurrentTime)
			{
				errorInfo=" ";
				currentTime=QDateTime::currentDateTime();
				timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
				errorInfo=QString::fromLocal8Bit("错误提示信息   ");
				errorInfo+=timeinfo;
				errorInfo+="\n";
			}
			else
			{
				errorInfo=" ";
				errorInfo=QString::fromLocal8Bit("错误提示信息   ");
				errorInfo+="\n";
			}
			serialPortAssitant.ReciveWidget->append(errorInfo +errorContent);
			return;
		}
		<span style="background-color: rgb(255, 0, 0);">connect(serialPort,SIGNAL(readyRead()),this,SLOT(onReadyRead()));</span>
		serialPort->setDataBits(QSerialPort::Data8);
		serialPort->setStopBits(QSerialPort::OneStop);
		serialPort->setParity(QSerialPort::NoParity);
		serialPort->setFlowControl(QSerialPort::NoFlowControl);

		QString serialStatusInfo;
		serialStatusInfo=serialPortAssitant.serialPortcomboBox->currentText().toUpper();
		serialStatusInfo+=" OPENED";
		serialStatusInfo+=" , ";
		serialStatusInfo+=QString::number(serialPort->baudRate());
		serialStatusInfo+=" , ";
		serialStatusInfo+=serialPortAssitant.DataWidthcomboBox->currentText();
		serialStatusInfo+=" , ";
		serialStatusInfo+=serialPortAssitant.ParityWidthcomboBox->currentText().toUpper();
		serialStatusInfo+=" , ";
		serialStatusInfo+=serialPortAssitant.StopWidthcomboBox->currentText();
		serialStatusInfo+=" , ";
		serialStatusInfo+=serialPortAssitant.FLowControlcomboBox->currentText().toUpper();
		QPalette font_palette;
		font_palette.setColor(QPalette::WindowText,Qt::darkCyan);
		serialPortAssitant.status->statusInfo->setText(serialStatusInfo);
		serialPortAssitant.status->statusInfo->setPalette(font_palette);

		serialPortAssitant.sendBtn->setText(QString::fromLocal8Bit("发送"));
		IsSerialPortOpen=true;
	}

else
	{
		if(IsRepeatSend)
		{
			repeatSend->start();
		}
		Write();
	}
}

首先看write函数:以下是从write函数中抽离出来的几行关键代码:

        QString sendMsg=serialPortAssitant.sendLine->text();
<span style="white-space:pre">	</span>QByteArray temp=sendMsg.toLatin1();
	char *sendContent=temp.data();
	qint64 sendedNum=serialPort->write(sendContent,temp.count());

1、第一行是从QLineEdit获取需要发送的数据信息;

2、第二行到第三行代码是需要把QString转换为char *的数据类型。

3、第四行则是通过QIODevice类的成员函数write写出数据。

4、剩余的部分是一些细节的错误提示显示以及显示信息,以及非常重要的是以ASCII形式发出数据还是以16进制发送数据。

接着是sendData()函数。

serialPort=new QSerialPort(portToOpen);
if(!serialPort->open(QIODevice::ReadWrite))
connect(serialPort,SIGNAL(readyRead()),this,SLOT(onReadyRead()));	 

1、第一行是通过portToOpen实例化QSerialPort,构造函数为: QSerialPort(const QString & name, QObject * parent = Q_NULLPTR)

2、第二行是打开串口,打开模式是ReadWrite,可写可读

3、第三行是通过readRead()信号来实现串口数据的读出,事件驱动的方式。这一行我在源代码中加了红色,原因是,一定要在打开串口后,实现readyRead()信号和对应的槽函数的连接,如果在没有初始化串口成功后,然后信号才能启动。我最开始在CameraSerialPort这个类的初始化中就定义了这个信号槽的链接,一直没有读到串口数据,结果在网上找了半天的原因,也没有找到这个问题。想了一下,然后把代码移到了这里,就可以了。

打开串口和发送数据的结果如图:

  • 串口接收数据

void CameraSerialPort::Read()
{
	<span style="background-color: rgb(255, 204, 0);">if(serialPort->bytesAvailable()<0)</span>
	{
		serialPortAssitant.ReciveWidget->setText("No data");
		return;
	}	

	<span style="background-color: rgb(255, 204, 0);">QByteArray temp;</span>
	<span style="background-color: rgb(255, 204, 0);">temp=serialPort->readAll();</span>

	QString  receiveMsg;
	if(IsReceiveByHex)
		receiveMsg=ByteArrayToHexStr(temp);
	else
		receiveMsg=temp;

	if(receiveMsg.isEmpty())
	{
		if(IsShowCurrentTime)
		{
			errorInfo=" ";
			currentTime=QDateTime::currentDateTime();
			timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
			errorInfo=QString::fromLocal8Bit("错误提示信息   ");
			errorInfo+=timeinfo;
			errorInfo+="\n";
		}
		else
		{
			errorInfo=" ";
			errorInfo=QString::fromLocal8Bit("错误提示信息   ");
			errorInfo+="\n";
		}
		serialPortAssitant.ReciveWidget->append(errorInfo +QString::fromLocal8Bit("没有读到数据\n"));
		return;
	}

	//接收到的字节数以初始的bytes为依据
	reciveCount+=temp.count();
	serialPortAssitant.status->RxByte->setText(QString::number(reciveCount));
	QString showReciveMsg;
	if(IsShowCurrentTime)
		{

			currentTime=QDateTime::currentDateTime();
			timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
			showReciveMsg+=QString::fromLocal8Bit("接收数据 : ");
			showReciveMsg+=timeinfo;
			showReciveMsg+="\n";
			showReciveMsg+=receiveMsg;
		}
		else
		{
			showReciveMsg=QString::fromLocal8Bit("接收数据 : ");
			showReciveMsg+=receiveMsg;
		}
		serialPortAssitant.ReciveWidget->append(showReciveMsg);

}

void CameraSerialPort::onReadyRead()
{
	Read();
}

1、读入数据之前,需要判断缓冲区是否有数据,有数据才去读数据

2、如果有数据,则全部读出缓冲区数据

  • 总结

大致的有关Qt的串口类就介绍完了,主要是QSerialPort以及QSerialPortInfo两个类的使用,当然也需要了解QIODevice。

时间: 2024-10-08 23:55:59

Qt (6) -- Qt5.5.1实现通用串口程序的相关文章

【Qt】Qt5.12连接MySQl5.7(亲自测试成功)

目录 00. 目录 01. Qt5.12连接MySQL5.7测试代码 02. Qt5.12连接MySQL5.7问题描述 03. 解决办法 04. 结果测试 05. 打赏 00. 目录 01. Qt5.12连接MySQL5.7测试代码 qDebug() << QSqlDatabase::drivers(); QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); db.setHostName("192.168.2

我的php学习笔记:php通用采集程序

php采集程序构建基本步骤: 采集程序是什么?获取远程数据(文字.图片.图片)并快速保存到本地或指定地址. 如天气预报(小偷程序): 远程获取-->替换内容-->展示给用户 如实时更新的新闻(采集内容): 远程获取-->提取内容-->分类存储-->读取内容--->展示内容 ************************* 设计PHP采集入库UML 列表正则: 终端正则: ************************** file_get_contents()获取远

MSP430FR5739串口程序

今天急着用这个片子的串口,匆忙中调试串口也话费了一段时间,在网上下了一个程序,忽然就把所有问题搞清楚了,只是中断就看着头文件中寄存器写的,虽然通讯正常,不过不确定有没有写错.代码如下: #include <msp430fr5739.h> void Uart_Init(void){ P2SEL1 |= BIT0 + BIT1; P2SEL0 &= ~(BIT0 + BIT1); // P2.0,1 = USART0 TXD/RXD 选择端口用作接收和发送端口UCA0CTL1 |= UCS

php开发通用采集程序

php采集程序构建基本步骤: 采集程序是什么?获取远程数据(文字.图片.图片)并快速保存到本地或指定地址. 如天气预报(小偷程序): 远程获取-->替换内容-->展示给用户 如实时更新的新闻(采集内容): 远程获取-->提取内容-->分类存储-->读取内容--->展示内容 ************************* 设计PHP采集入库UML       列表正则:       终端正则: ************************** file_get_c

Delphi 使用 SPcomm 调试串口程序出现总是在程序断开的时候,才发送指令的问题。

问题如上, 在与嵌入式程序串口程序通讯的时候, 总是出现如上问题, 造成的原因把下面的True改成false就可以了. 下图Spcomm的属性页,几个True全改成False再试试

Android蓝牙串口程序开发

本文主要介绍了针对android的蓝牙串口上位机开发. 一.帧定义 android客户端按照一定的数据帧格式通过蓝牙串口发送数据到连接到MCU的蓝牙从机,MCU接收到数据后,按照帧格式的定义,接收数据帧,并解析数据帧,得到需要的数据. android客户端按照以下帧格式来发送和接收数据. 1.1客户端发送的数据帧内容 帧校验的和是从帧长开始到帧校验前所有数据的和对256取余.即sum%256. pitch roll yaw数据各16位,由高低8位组成.MCU接收数据时需要将其再复合成一个16位数

Java编写串口程序

用Java编写串口程序一般都会用到这个 http://fizzed.com/oss/rxtx-for-java 根据电脑的情况下载 解压以后有安装文档 For a JDK installation: Copy RXTXcomm.jar ---> <JAVA_HOME>\jre\lib\extCopy rxtxSerial.dll ---> <JAVA_HOME>\jre\binCopy rxtxParallel.dll ---> <JAVA_HOME>

【转】C# 串口操作系列(2) -- 入门篇,为什么我的串口程序在关闭串口时候会死锁

第一篇文章我相信很多人不看都能做的出来,但是,用过微软SerialPort类的人,都遇到过这个尴尬,关闭串口的时候会让软件死锁.天哪,我可不是武断,算了.不要太绝对了.99.9%的人吧,都遇到过这个问题.我想只有一半的人真的解决了.另外一半的人就睁只眼闭只眼阿弥佗佛希望不要在客户那里出现这问题了. 你看到我的文章,就放心吧,这问题有救了.我们先回顾一下上一篇中的代码 [c-sharp] view plaincopy void comm_DataReceived(object sender, Se

C# 串口操作系列(2) -- 入门篇,为什么我的串口程序在关闭串口时候会死锁 ?

C# 串口操作系列(2) -- 入门篇,为什么我的串口程序在关闭串口时候会死锁 ? 标签: c#objectuibyte通讯.net 2010-05-19 08:43 55212人阅读 评论(188) 收藏 举报  分类: 通讯类库设计(4)  版权声明:本文为博主原创文章,未经博主允许不得转载. 第一篇文章我相信很多人不看都能做的出来,但是,用过微软SerialPort类的人,都遇到过这个尴尬,关闭串口的时候会让软件死锁.天哪,我可不是武断,算了.不要太绝对了.99.9%的人吧,都遇到过这个问