cserialport类

焦点 2026-10-17 05:29:50 14

CSerialPort 这个类名,在 Windows 串口编程里出现的频率不低。我第一次接触它是在一个老工业控制项目上,当时需要同时管理四个 COM 口,分别接称重传感器、PLC、扫码枪和一台老式打印机。用 Win32 API 直接写 CreateFile、SetCommState、ReadFile 那一套,代码量不小,而且每个口都要重复一遍。后来同事丢给我一个 CSerialPort 类,大概两百行,封装了打开、配置、读写和关闭,瞬间把主逻辑从串口细节里解放出来。

它的核心其实就三件事:句柄、超时、缓冲区。类内部通常持有一个 HANDLE,通过 CreateFile 以 FILE_FLAG_OVERLAPPED 方式打开 COMx。同步串口读写容易卡死线程,所以这个类一般会配合 OVERLAPPED 结构做异步操作。超时参数用 COMMTIMEOUTS 设置,ReadIntervalTimeout 和 ReadTotalTimeoutConstant 这两个值调不好,读数据就会要么断帧要么延迟。我见过有人把 ReadIntervalTimeout 设成 0,结果每读一个字节就返回一次,CPU 直接飙到 20%。

写串口比读简单,但坑在流控。CSerialPort 类通常提供 SetFlowControl 之类的接口,支持 RTS/CTS 或 XON/XOFF。有次现场调试,扫码枪数据总是丢前几个字符,查了半天发现是硬件流控没开,接收缓冲区溢出。后来在类里加了一个 ClearBuffer 方法,每次打开串口先 PurgeComm 清空,问题才消失。这种细节,文档不会写,只有实际踩过才知道。

另一个容易忽略的是线程安全。CSerialPort 类如果只在一个线程里用,没问题。但很多项目会单独开一个接收线程,主线程负责发送。这时候对句柄的读写就要加临界区。我习惯在类里放一个 CRITICAL_SECTION,Write 和 Read 都包一层。有人用互斥量,也行,但临界区在同一个进程内更轻量。曾经有个项目因为没加锁,发送和接收同时操作 OVERLAPPED 结构,导致偶尔蓝屏——是的,串口驱动层出错真的会蓝屏。

跨平台方面,Windows 的 CSerialPort 和 Linux 的 termios 完全是两套东西。有人试图用条件编译把两者塞进同一个类,结果代码里全是 #ifdef _WIN32。我的看法是,不如分开写两个类,接口保持一致就行。硬要统一,反而增加维护成本。Qt 的 QSerialPort 做得不错,但那是另一个层面的封装了。

性能上,CSerialPort 类本身开销很小,瓶颈在串口硬件和驱动。波特率 115200 时,每毫秒大约 11 个字节。如果接收线程优先级不够,或者缓冲区设得太小,丢包几乎必然。我通常把输入缓冲区设成 4096 字节,输出 2048,然后接收线程用 WaitCommEvent 加 OVERLAPPED 等待 EV_RXCHAR 事件。别用轮询,轮询在低波特率下看着能用,一到 921600 就原形毕露。

最后说个实际案例。某次用 CSerialPort 类读电能表,Modbus RTU 协议,波特率 9600。类里默认读超时是 500ms,但表计响应偶尔要 700ms。结果就是随机读失败。把 ReadTotalTimeoutConstant 改成 1000 后稳定运行。所以别迷信默认值,现场环境千差万别,超时参数必须根据设备手册和实测调整。CSerialPort 类是个好工具,但它不会替你解决物理层和协议层的问题。

本文地址:http://www.79wt.cn/news/022d1899959.html
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

友情链接