6

[Qt开发]一口气搞懂串口通信 - 轩先生。

 1 year ago
source link: https://www.cnblogs.com/Leventure/p/17473880.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

🐊🐊🐊🐊🐊好多小鳄鱼

一、关于串口通信:

Qt的确有自己的串口通信类,就是QSerialPort,但是我们在使用过程中因为要更加定制化的使用串口通信类减小开发的难度,所以我们会提供一个串口通信类,也就是这个SerialPortHelper类。

首先我们要知道什么是串口,串口通信就是机器和系统之间的一个通信协议,你可以将它理解为共享内存,可以根据需要向其中写入内容,然后在需要的时候从中读取数据。不过需要注意的是,在Qt的封装下,你不需要知道串口内的数据是否是给你的,还是你发的,因为统统都是你的。

串口通信什么?

大概了解了一下什么是串口通信,那么我们来看一下串口通信的通信手册大概是什么样。

image

由上我们可以看到,串口通信消息大概就是一串16进制的字符按照特定的规定,然后向串口中写入这些消息就可以了。比如前面这三个41 54 64这三个数字就是固定写死的,而36则是这个协议的通信号,是用于区分不同消息码的,比如上述这条指令的消息码是36,另外一条消息

然后后续标了颜色的内容就是实际上这条消息码中具体携带的数据,这个就不过多介绍了。

不过需要注意的是,硬件返回的数据或者向硬件发送的数据不一定都是像人一样的从左到右,有些命令特别是偏长的命令很多都是要求从右到左,也就是常说的小端序,低字节在前高字节在后,比如:

image

给出两端函数示例(实际上这两个函数在Qt中都有,这里只是展示一下具体是什么意思而已):

//将接收到的小端字节序数据转换为无符号整数
QString SerialPortHelper::getLittleEnd(const QByteArray& data)
{
	if (data.size() > 8) return "";
	qulonglong result = 0;
	for (int i = 0; i < data.size(); ++i)
	{
		qulonglong tmp = (uchar)data[i];
		result += tmp <<= i * 8;
	}
	return QString::number(result);
}

//大端字节序数据转换为浮点数
QString SerialPortHelper::getBigEndFlt(const QByteArray& data)
{
	const int fltLen = 4;
	if (data.size() != fltLen) return "null";
	float result = 0;
	uchar fltArr[fltLen];
	for (int i = 0; i < fltLen; ++i)
	{
		fltArr[fltLen - i - 1] = data.at(i);
	}
	return QString::number(*(float*)fltArr, 'f', 9);
}

ok,接下来还有四位数字,这个要分开来说前两位和后两位。

其中前两位是CRC校验码,这个是需要对前面的数字进行一个基本的校验,具体我就不太懂了,这里提供一个函数,供参考:

void SerialPortHelper::CRC16_2(const QByteArray& ba, uchar* crcBuf)
{
	int pos, i;
	uchar* buf = (uchar*)ba.data();
	int len = ba.size();
	unsigned int crc = 0xFFFF;
	for (pos = 0; pos < len; pos++)
	{
		crc ^= (unsigned int)buf[pos]; // XOR byte into least sig. byte of crc
		for (i = 8; i != 0; i--) // Loop over each bit
		{
			if ((crc & 0x0001) != 0) // If the LSB is set
			{
				crc >>= 1; // Shift right and XOR 0xA001
				crc ^= 0xA001;
			}
			else // Else LSB is not set
			{
				crc >>= 1; // Just shift right
			}
		}
	}
	//高低字节转换
	crc = ((crc & 0x00ff) << 8) | ((crc & 0xff00) >> 8);
	//qDebug() << QString().sprintf("CRC:%04x", crc);
	crcBuf[0] = crc >> 8;
	crcBuf[1] = crc;
}

最后两位就是固定的了,用两个固定搭配来分割各种字符段

串口通信使用流程

我们一般使用串口类,主要流程如下:

1.获得基本参数:
我们需要获得这个串口的一些基本参数,其中包含内容如下:

QString portName = "NULL";
int baudRate = 921600;
QSerialPort::DataBits dataBits = QSerialPort::Data8;
QSerialPort::StopBits stopBits = QSerialPort::OneStop;
QSerialPort::Parity parity = QSerialPort::NoParity;

我们需要在启动这个端口调用的时候设置好这些属性,才能获取正确的COM口消息和发送消息,给出一个启动串口的示例:

serialPort = new QSerialPort();
serialPort->setPortName(param.portName);
if (serialPort->open(QIODevice::ReadWrite))
{
	serialPort->setBaudRate(param.baudRate);
	serialPort->setDataBits(param.dataBits);
	serialPort->setStopBits(param.stopBits);
	serialPort->setParity(param.parity);
	//通知串口接收到消息的信号函数
	connect(serialPort, &QSerialPort::readyRead, this, &CarControlModel::dataReceive);	
	qDebug() << "connect:" << param.portName;
	emit checkConnableRetSig(true);

	setStartTime();
	//emit comConnStatus(true);
	return true;

See?其实很简单的的,这个对象实际上就做了一件事,通知你现在来数据了。注意,这个readyRead 并不是发给你接到的数据,而是通知你现在串口接到消息了,而且这个消息还很有可能不是一条一条的,可能是一段一段的,就是消息可能不完整,这里给出一个示例。

void CarControlModel::dataReceive()
{
	if (serialPort != nullptr && serialPort->isOpen())
	{

		QByteArray buffer = serialPort->readAll();
		analysisData(buffer);
	}
}

void CarControlModel::analysisData(const QByteArray& dataArr)
{
	//消息断开的情况
	qDebug() << "recv data: " << dataArr.toHex(' ');
	dataBuff.append(dataArr);
	QList<QByteArray> dataList;
	for (;;)
	{
		int index = dataBuff.indexOf(QByteArray(gEnd, gEndLen));
		if (index == -1) break;
		dataList.append(dataBuff.mid(0, index + gEndLen));
		dataBuff = dataBuff.right(dataBuff.size() - index - gEndLen);
	}
	if (dataList.size() == 0) return;
	。。。。

也就是说,接到串口指令之后可能还需要你做一个解析,把接到的消息拆分出来处理。

向串口发送指令:

发送指令的话,就比较简单了,比如:

qint64 CarControlModel::startCentralCMD()
{
	QByteArray allArr, funDataArr;
	funDataArr.clear();
	funDataArr.append(0x32);
	funDataArr.append(0x01);  //0x01启动中心码盘
	code(allArr, funDataArr);
	return writeData(allArr);
}

//传入功能码数据区,组合成完整的指令存入数组
void CarControlModel::code(QByteArray& allArr, const QByteArray& funDataArr)
{
	allArr.clear();
	allArr.append(gId, gIdLen);
	allArr.append(gAddress, gAddressLen);
	allArr.append(funDataArr);
	uchar crcBuf[gCrcLen];
	CRC16_2(allArr, crcBuf);
	allArr.append((char*)crcBuf, gCrcLen);
	allArr.append(gEnd, gEndLen);
}
//串口发送消息出去
qint64 CarControlModel::writeData(const QByteArray& data)
{
	qDebug() << "send data: " << data.toHex(' ');
	qint64 ret = -1;
	if (serialPort != nullptr && serialPort->isOpen())
	{
		ret = serialPort->write(data);
		if (!serialPort->waitForBytesWritten(10000))
		{
			//emit sendWarningSig("发送失败:" + data.toHex(' '));
			qDebug() << QString("发送失败:" + data.toHex(' '));
		}
	}
	else
	{
		emit sendWarningSig("工控主串口未连接");
	}
	return ret;
}

Recommend

  • 7

    串口数据抓取及串口通信模拟 Gowabby ...

  • 18

    Electron使用串口通信 Electron使用串口通信比调用DLL更加方便,在串口通信中,需要首先安装依赖: npm install --save serialport 然后我们在src/background.ts中加...

  • 12
    • www.cnblogs.com 3 years ago
    • Cache

    QT串口通信

    QT 中串口通信需要使用到 QSerialPort 类,需要在 pro 文件中添加 Qt += serialport 才能够正常使用 头文件中添加 ...

  • 12
    • www.cnblogs.com 3 years ago
    • Cache

    C#串口通信编程类(修改版)

    C#串口通信编程类(修改版) 这是从网上down下来的一个串口通信类,发现close函数忘记了设置Opened属性为false 还有后面s...

  • 8

    C#结合串口通信类实现串口通信源代码(原创) mycom是串口通信类,在本blog前一篇中有完整代码。(

  • 13

    前言 前段时间赶项目的过程中,遇到一个调用RS485串口通信的需求,赶完项目因为楼主处理私事,没来得及完成文章的更新,现在终于可以整理一下当时的demo,记录下来。 首先说一下大概需求:这个项目是机器视觉方面的,AI...

  • 13

    HAL 库开发笔记(四)- 串口通信串口通信算得上是单片机中最常用、最基础的一种通讯方式。常用可作为一种调试的手段,与单片机通讯监控数据、发送指令,也可以用作两个单片机之间互相通讯使用。基本原理

  • 3

    一、设计概述 1. UART串口通信设计目的 随着 FPGA/CPLD 器件在控制领域的广泛使用,开发嵌于 FPGA/CPLD 器件内部的通用异步收发器,以实现 FPGA/CPLD 开发系统与 PC 机之间的数据通信是很有实际意义的。FPGA/CPLD与单片机、ARM等器件不同,...

  • 7

    基于.Net C# 通信开发-串口调试助手   基于.Net C# 通信开发-串口调试助手   串口调试助手,广泛应用于工控领域的数据监控、数据采集、数据分析等工作,可以帮助串口应用设计、开发、测试人员检查所开发的串口应用软硬件的数据收发状况...

  • 8

    硬件通信:串口、I2C、SPI、I2S 开发例程 作者:Hello_Kun 2023-04-07 09:14:31 我们做一个简单的串口通信实验,将一串自定义数据通过Hi3861的UART1发送到蓝牙模块(或者其他开发板)。开发流程有以下几步:配置并...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK