设备已经发出一条完整报文,Qt窗口却先显示前半段,过一会儿才补齐后半段,这在串口开发中很常见。有时只是数据分批到达,有时则是接收程序提前清空了缓存。实现串口通信时,除了设置端口参数,还要处理报文边界,才能分清“暂时没收齐”和“确实丢了数据”。
一、Qt怎么实现串口通信
Qt提供QSerialPort进行串口读写,QSerialPortInfo用于查询可用端口。开始编写业务逻辑前,可以先完成一条指令的发送与应答验证。
1、准备串口模块和端口信息
①确认当前Qt安装包含Qt Serial Port模块,在项目构建配置中加入SerialPort依赖,并链接对应库。CMake与qmake项目需要分别配置。
②使用QSerialPortInfo的availablePorts获取端口列表,结合端口名、设备描述等信息选择目标设备。
③创建持续有效的QSerialPort对象,供后续打开、读写和关闭操作使用。
串口对象应覆盖整个通信过程。若把它作为普通局部对象创建在初始化函数里,函数结束后对象就会销毁,后续自然无法继续收发。
2、配置参数并打开串口
波特率相同还不够,数据位、校验位、停止位及流控也要与设备协议一致。流控设置错误时,可能出现端口打开成功却无法正常传输的情况。
①通过setPortName设置端口,使用setBaudRate、setDataBits、setParity和setStopBits配置通信参数。
②使用setFlowControl设置设备要求的流控方式,并检查各项设置是否成功。
③以QIODevice::ReadWrite模式打开端口,检查open的返回值;失败时记录errorString,并检查端口占用、访问权限和设备连接。
3、连接接收信号并发送指令
①将readyRead连接到接收槽,在槽函数中调用readAll,把本次读到的QByteArray追加到接收缓存。
②按设备协议组织发送字节,调用write,检查返回值,并通过bytesWritten等信息跟踪发送进度。
③连接errorOccurred,记录通信期间的错误,关闭程序或切换设备时释放端口。
write返回成功不等于设备已经收到并处理指令。需要确认设备执行结果时,还应解析它返回的应答。二进制协议也应直接按字节发送,避免把十六进制显示文本当成原始数据。
二、Qt串口接收数据不完整如何定位
先在解析和显示之前保存原始字节。只看界面上的文字,很难判断究竟是设备少发、程序少读,还是显示转换出了问题。
1、确认数据是否分批到达
①每次进入接收槽时,记录时间、本次读取长度及十六进制内容。
②让设备发送一条长度已知的报文,把连续几次接收记录按顺序拼接比较。
如果设备发送20字节,程序先读到8字节,随后又读到12字节,合并后内容正确,就属于分批接收。readyRead表示有新数据可读,readAll读取当前可读的数据,两者都不代表一条业务报文已经结束。
2、检查半帧是否被程序丢弃
①查看接收缓存是否定义为能够跨次保留的对象,排查每次进入槽函数都重新创建空缓存的写法。
②数据长度不足时保留现有内容,等待下次接收后继续追加。
③找到完整帧后只移除已经消费的字节,继续检查剩余内容,处理一次接收包含多帧的情况。
④检查接收流程是否调用clear,或无条件清空应用缓存;这些操作可能丢掉尚未处理的数据。
对于“帧头+长度+数据+校验”的协议,应先确认长度字段已经收齐,再等待整帧到达。长度值还要检查合理范围,避免一个错误长度让程序一直等待。
3、区分字节缺失和文本显示异常
①对比原始QByteArray的长度与十六进制日志,确认零字节、回车换行等内容是否真实存在。
②检查字符串转换是否明确传入长度,以及文本编码是否与设备一致。
二进制数据中可能包含零字节,用依赖字符串结束符的方式处理,会产生“后半段不见了”的现象。UTF-8文本若在多字节字符中间分批到达,逐批独立转换也可能出现乱码,应保留未完成的编码序列或使用有状态解码器。
4、排查接收处理是否被阻塞
接收槽内持续写文件、频繁刷新控件或执行耗时计算,会推迟后续事件处理。持续高速传输时,还可能进一步造成缓冲区压力。
①暂停复杂业务,只保留读取、缓存和必要日志,比较异常是否仍然出现。
②将耗时任务交给工作线程,控制界面刷新频率,保持接收处理简短。
③检查串口对象的线程归属,在所属线程内操作;异步接收需要该线程正常运行事件循环。
④若采用waitForReadyRead等阻塞接口,应安排在非界面线程中,并按协议循环收集数据,不能一次等待返回就认定整帧已经收到。
三、Qt串口接收异常修复后如何复测
程序改完后,可以拿之前接收失败的报文重新测试。除了核对最终结果,还要观察缓存中剩下了什么,防止某一帧虽然解析成功,却影响后续数据。
1、改变同一条报文的发送方式
①保存一条内容正确的原始报文,先完整发送,再拆成几段发送,比较解析结果。
②调整分段位置,让帧头、长度字段和校验字段分别跨越两次接收,检查程序能否继续等待并完成解析。
正常情况下,同一条报文无论分几次到达,最终都应得到相同结果。若改变分段位置就出现异常,应检查解析程序是否提前读取了尚未收齐的字段。
2、检查连续报文之间是否相互影响
①连续发送多条内容不同的报文,核对输出顺序、数量和字段值。
②在完整报文后追加半帧,确认程序处理完前面的内容后,仍保留后面的未完成数据。
③补发剩余字节,检查这条报文能否接着解析,缓存是否只移除了已经处理的部分。
3、观察持续运行后的接收情况
短时间测试通过后,还应按设备实际发送频率持续运行一段时间。记录累计接收字节数、成功解析帧数和校验失败次数,并观察缓存长度是否持续增长。若运行越久延迟越大,通常需要进一步比较数据到达速度与处理速度,检查日志写入、界面更新或业务计算是否拖慢了接收流程。
总结
串口数据分几次到达并不固定,接收程序需要能够保留半帧,也能从连续数据中取出多条完整报文。遇到内容缺失,原始字节记录可以帮助判断数据是否到达,缓存变化则能说明程序在哪里丢掉了内容。修复后,用不同分段方式和连续报文复测,比只重复发送一条短指令更有参考价值。希望这些方法能帮助大家理清Qt串口接收与解析的关系,让设备联调少一些反复尝试。如果您还有相关需求,欢迎联系我们交流咨询。