好多小鳄鱼

一、关于串口通信:

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

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

串口通信什么?

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

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

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

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

给出两端函数示例(实际上这两个函数在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;
}

[Qt开发]一口气搞懂串口通信的更多相关文章

  1. 【Qt开发】Qt5.7串口开发

    QT5有专门的串口类:  QSerialPort:提供访问串口的功能  QSerialPortInfo:提供系统中存在的串口的信息  具体使用方法:  1.在pro文件中加入: QT += seria ...

  2. 微信小程序开发之搞懂flex布局1——Flexbox

    Flexbox ——弹性布局 Flexbox is a layout model for displaying items in a single dimension — as a row or as ...

  3. 微信小程序开发之搞懂flex布局2——flex container

    容器的概念,是用来包含其它容器(container)和项目(item). flex container——flex容器 A flexbox layout is defined using the fl ...

  4. 微信小程序开发之搞懂flex布局3——Flex Item

    Flex Item flex容器的子元素就是这个容器的flex item. The direct children of a Flex Container (elements with display ...

  5. 微信小程序开发之搞懂flex布局4——Main Axis

    Main Axis——主轴 当flex-direction设置为row或row-reverse时,主轴的方向为水平方向.此时flex item为行内级元素. 当flex-direction设置为col ...

  6. 微信小程序开发之搞懂flex布局5——cross axis

    Cross Axis——交叉轴,与Main Axis(主轴)垂直交叉. main axis is row or row-reverse the cross axis runs down the col ...

  7. STM32F407 串口通信实验 视频第27节 个人笔记

    前言 第26节也是串口,笔记链接在此:https://www.cnblogs.com/YuQiao0303/p/10019362.html github地址:https://github.com/Yu ...

  8. Qt编写串口通信程序全程图文解说

    (说明:我们的编程环境是windows xp下,在Qt Creator中进行,假设在Linux下或直接用源代码编写,程序稍有不同,请自己修改.) 在Qt中并没有特定的串口控制类,如今大部分人使用的是第 ...

  9. 转:Qt编写串口通信程序全程图文讲解

    转载:http://blog.csdn.net/yafeilinux/article/details/4717706  作者:yafeilinux (说明:我们的编程环境是windows xp下,在Q ...

  10. Qt串口通信

    1. Qt串口通信类QSerialPort 在Qt5的的更新中,新增了串口通信的相关接口类QSerialPort,这使得在开发者在使用Qt进行UI开发时,可以更加简单有效地实现串口通信的相关功能. 开 ...

随机推荐

  1. 免费1年服务器,部署个ChatGPT专属网页版

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 白皮袄个免费1年服务器,部署个ChatGPT专属网页版! api.openai.com por ...

  2. Centos7安装配置MySQL 5.6

    Centos7安装配置MySql 5.6 首先下载MySql5.6的安装包,具体安装方式分为yum安装与离线安装.在新版本的CentOS7中,默认的数据库已更新为了Mariadb,而非 MySQL. ...

  3. R语言网络数据爬取

    现在大家对爬虫的兴趣不断高涨,R和PYTHON是两个非常有力的爬虫工具.Python倾向于做大型爬虫,与R相比,语法相对复杂,因此Python爬虫的学习曲线会相对陡峭.对于那些时间宝贵,又想从网上获取 ...

  4. 二进制安装k8s v1.25.4 IPv4/IPv6双栈

    二进制安装k8s v1.25.4 IPv4/IPv6双栈 https://github.com/cby-chen/Kubernetes 开源不易,帮忙点个star,谢谢了 介绍 kubernetes( ...

  5. 一键部署十个服务脚本--可拆分---java+mysql+redis+nginx+rocketmq..等等

    java + mysql +redis + minio + nginx + rocketmq + rocketmq-console + elasticsearch + kibana + logstas ...

  6. pysimplegui之进度表one_line_progress_meter

    我们的代码中都有循环.'等待,看着文本窗口中滚动过去的计数器不是很快乐吗?一行代码如何获得一个进度表,其中包含有关您的代码的统计信息? one_line_progress_meter(title, c ...

  7. 12年经验的大龄程序员,都用什么写 API 文档?

    写代码,程序员不害怕. 写文档,每个程序员都害怕! 为什么? 技术优先,我们更倾向于将技能和精力更多地放在编写代码上,如果 API 工具不好使,不便捷,同步麻烦,测试看不懂,更会大大地打击编写文档的积 ...

  8. day84:luffy:优惠活动策略&用户认证&购物车商品的勾选/结算

    目录 1.课程列表页活动和真实价格计算 1.优惠活动策略的model表结构 2.课程列表页显示优惠类型名称 3.课程列表页显示真实价格 4.将优惠类型名称和真实价格显示到前端页面上 5.课程列表页显示 ...

  9. PyInstaller打包的文件闪退

    问题描述:使用PyInstaller打包的pycharm写的python程序,打包好后从windows上打开一直闪退 一.双击exe文件闪退,从cmd命令行中与加载程序,可以看到具体的报错 D:\di ...

  10. 快速重拾 Tmux

    Tmux 是一个 Linux (Mac OS也支持)下的终端复用器,相较于 Screen 更为强大,但快捷键和操作逻辑也更复杂,一段时间不用,就很容易忘记相关的命令和快捷键.本文旨在通过一个简单的场景 ...