ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Delphi串口通信实战:TComPort组件安装、配置与调试指南

Delphi串口通信实战:TComPort组件安装、配置与调试指南 简介TComPort 4.14是一套面向Delphi 7至XE7及C Builder开发者的串口通信组件库包含TComPort、TComDataPacket、TComComboBox、TComLed、TComTerminal等常用控件可快速实现串口收发、数据包解析与终端交互适合工控、嵌入式调试及桌面工具开发场景。压缩包共173个文件大小仅1.68MB以dpk/bpk工程包、pas/dfm源码与窗体、res资源文件为核心另有hpp头文件及dcu预编译单元便于直接安装或二次编译目录中可见CPortLibCB*.bpk、DsgnCPort*.bpk等设计期与运行期包并附带ComExample*.bpr示例工程可对照学习多版本环境下的配置方式。已有666人学习资源体积精简、结构清晰既提供完整组件源码与设计期注册包也包含示例程序能帮助Delphi/C Builder开发者降低串口编程门槛快速集成到现有项目中。 做嵌入式上位机这么多年串口通信这一关绕不过去。早年间在 Delphi 7 下写数据采集程序后来项目迁移到 XE7手里的串口组件换了好几轮最后一直在用的还是 TComPort 4.14。这组件在 Delphi 7 到 XE7 这个跨度里非常罕见地保持了稳定而且源码开放、可定制对做工业控制、仪器仪表通信、GPS 数据解析这类场景来说真的是省心又省力。这篇东西就把我在实际项目中折腾 TComPort 的经验整理出来从选型理由、安装适配、核心用法到踩坑实录一次性给全。1. 为什么在串口开发里选它TComPort 的定位与选型对比1.1 它是谁一个被验证了十几年的社区方案TComPort 是一套基于 Windows 底层通信 API 封装的 Delphi/CBuilder 串口组件库最初由 Dejan Crnila 等人开发维护采用 CPL 许可开源。4.14 是最后一个广为流传的稳定版本之后项目基本停更但正因为不再频繁改动它的行为模式固定、踩坑经验丰富在工业界反而成了“稳定的代名词”。这套组件最核心的价值在于它把 CreateFile、SetCommState、WaitCommEvent、ReadFile、WriteFile 这一串 Windows API 复杂度封装成了几个 TComponent 拖拽控件开发者不需要懂重叠 I/O 细节不需要自己维护接收线程拖一个 TComPort 到窗体上设置好串口号和波特率写几行事件代码就能收发数据。对项目周期紧、工期长的交付场景来说这种抽象程度是刚需。1.2 与 MSComm、Spcomm、纯 API 开发相比很多初学者喜欢拿 TComPort 和 VB 时代的 MSComm 控件比。MSComm 在 Delphi 下用确实能跑但它依赖 OCX 注册部署环境里面一旦换了台工控机忘记注册控件程序直接起不来排查起来非常痛苦。TComPort 是纯 Delphi 源码包静态编译进 EXE没有运行时注册依赖拷到哪台机器都能跑。Spcomm 也是老牌串口控件但它对 Unicode 字符串的支持一直不彻底在 Delphi 2009 之后的版本里做中文收发需要自己做 AnsiString 和 UnicodeString 的来回转换容易踩乱码坑。TComPort 4.14 在 XE2 以后虽然也要注意字符编码转换但它的 ReadStr/WriteStr 设计得更规整配合 RawData 二进制读写处理起来可控得多。纯 API 开发是另一条路。自己开线程 重叠 I/O 事件驱动的确能做出极高效率的串口通信层但代码量至少 1000 行起步而且串口拔插、超时重试、缓冲溢出的边界情况非常考验经验。项目如果是为了交货不是做产品级通信中间件用 TComPort 是性价比最高的选择。2. 安装与版本适配从 Delphi 7 到 XE7 该怎么装2.1 源码包里到底有什么解压 TComPort 4.14 源码包后你会看到按照 Delphi 版本号组织的目录结构比如Delphi7、Delphi2009、DelphiXE2、DelphiXE7等子文件夹。每个版本目录下都有对应的运行时包.dpk和设计期包.dpk其中 ComPort 是核心包CPort 是包含 TComPort 和 TComDataPacket 等设计期控件的包。这里有个容易忽略的点源码包里还有Source公共源码目录和Examples示例目录。安装前先把 Source 目录放到一个固定的、不会随意变动的位置因为之后每个新工程都要把这条路径加进 Library 搜索路径里。很多人装完组件把源码目录挪走下次编译时 IDE 报找不到CPort.hpp或CPort.pas就是这个原因。2.2 编译安装的操作步骤以 XE7 为例完整安装流程如下打开 Delphi XE7点击File Open Project定位到DelphiXE7目录下的ComPort_DXE7.dpk。在 Project Manager 中右键该包选择Compile确认编译无错误。再右键选择Install安装成功后工具面板会新增一个CPort选项卡里面有 TComPort、TComDataPacket、TComTerminal 等组件。打开Tools Options Delphi Options Library Library Path把Source目录路径加进去点 Save。这里必须加否则新建工程时 IDE 无法解析 TComPort 单元。Delphi 7 的安装方式一样只是对应的 .dpk 文件名是ComPort_D7.dpk。整个安装动作本身不难难的是版本对应的 .dpk 选对。如果你用 Delphi XE 或 XE3源码包目录里不一定有完全对应的子目录此时最省事的方式是选最近版本的 .dpk 强行打开IDE 会提示升级包格式一般都能成功转换。注意安装组件前务必关闭正在运行的同类组件包比如旧版 TComPort 或其他串口控件否则设计期包安装时可能出现类名冲突IDE 直接报Class TComPort already exists。2.3 关于 Unicode 字符集那点事Delphi 2009 是分水岭string 类型从 AnsiString 变成 UnicodeString。TComPort 4.14 发布时主要面向 Delphi 7后期才适配了新版本所以它在 XE2 以上的行为需要特别注意串口物理上传输的是字节流没有编码概念但 TComPort 的 ReadStr/WriteStr 方法接收的参数类型是 string。在 XE7 里string 默认是 UTF-16直接把读到的字节转成 string再发给下位机极容易出现乱码。正确思路是把串口数据当作字节流处理。发送时用WriteStr前先做 AnsiString 转换接收时用ReadStr读出来再按约定编码转回或者干脆走Read/Write的字节数组重载。后面实战代码部分我会把两种模式都写出来。3. 核心组件与关键参数真正上线前必须搞懂的配置3.1 TComPort 组件的关键属性TComPort 组件的属性面板里最核心的配置项有这么几组Port串口号直接填COM3或COM4这种字符串。注意在 Windows 下如果串口号大于 COM9必须写成\\.\COM10的格式否则 CreateFile 调用会失败这在 USB 转串口设备多的工控机上特别常见。BaudRate波特率枚举支持 300、600、1200、2400、4800、9600、19200、38400、57600、115200 等常用值枚举名如br9600、br115200。DataBits数据位支持db5、db6、db7、db8。Parity.Bits校验位支持prNone、prOdd、prEven、prMark、prSpace。注意它是嵌套属性在代码里要像ComPort.Parity.Bits : prNone这样赋值。StopBits停止位支持sb1、sb1_5、sb2。Buffer输入输出缓冲区包含InputSize和OutputSize默认 4096。在做高速率大数据量传输时建议调大比如 8192 或 16384减少底层读写的上下文切换频次。FlowControl流控制可以分别设置接收和发送的流控方式常用fcHardwareRTS/CTS或fcSoftwareXON/XOFF。短距离、低速设备通常不启用流控把两端的属性都设成fcNone。Timeouts超时配置ReadIntervalTimeout和ReadTotalTimeoutConstant对接收超时判定很关键。默认值在低速设备下可能不够我一般把ReadIntervalTimeout设成 50ms 左右既能及时断帧又不至于频繁触发。这些参数的物理意义必须清楚波特率决定 1 秒传多少比特一个数据帧通常由 1 位起始位 数据位 校验位 停止位构成。所以 9600 波特率、8 数据位、无校验、1 停止位时有效数据速率是 9600 / 10 960 字节/秒。在做大数据量传输时用这个公式先估算速率再决定缓冲区和超时参数能少走很多弯路。3.2 事件机制从 OnRxChar 到线程同步TComPort 最核心的事件是OnRxChar它在内部接收线程读到数据后被触发参数 Count 表示当前缓冲区内可读取的字节数。在这个事件里必须尽快把数据读走否则后续数据继续到达可能导致缓冲溢出丢数据。典型写法是直接把 Count 传给 ReadStr 或 Read一次性取完。另一个重要约束是OnRxChar 运行在 TComPort 的内部接收线程上下文中不是 VCL 主线程。你在事件里直接操作 Memo、Edit 等 VCL 控件偶发情况下会引起主线程和接收线程的资源竞争表现为界面卡死或直接抛异常。解决方式是使用线程安全的同步手段比如 TThread.Queue 把 UI 更新调度到主线程或者用 Synchronize。示例代码我放在下一节一起讲。3.3 TComDataPacket把数据包解析省下来的利器TComDataPacket 是 TComPort 的搭档控件它负责从串口字节流里去“找”一帧完整数据包。你不需要在 OnRxChar 里手动拼帧而是给它定义包的结束条件它自己会在后台缓冲拼接。核心属性有两个ComPort绑定关联的 TComPort 实例。IncludeBeginning、IncludeEnding控制返回的数据包是否包含起始符和结束符。包停止条件属性组StartString起始字符串、StopString结束字符串、PacketSize固定长度、MaxBufferSize缓冲上限。举个例子很多 GPS 模块输出以$开头、以回车换行结束的 NMEA 语句。把 StartString 设为$StopString 设为#13#10TComDataPacket 就会自动从数据流中截出完整的 NMEA 句子并通过 OnPacket 事件交给你。比自己在 OnRxChar 里用状态机拼帧省太多事。4. 一个完整可复用的实战代码4.1 窗体部署与初始化新建一个 Delphi 工程放一个 TComPort、一个 TComDataPacket、一个 Memo、两个 Button、一个 Edit 用于填写要发送的命令。在 FormCreate 里做初始化procedure TForm1.FormCreate(Sender: TObject); begin ComPort.Port : COM3; ComPort.BaudRate : br9600; ComPort.DataBits : db8; ComPort.Parity.Bits : prNone; ComPort.StopBits : sb1; ComPort.Buffer.InputSize : 8192; ComPort.Buffer.OutputSize : 8192; ComPort.Timeouts.ReadIntervalTimeout : 50; ComPort.Open; end;注意 Port 属性也可以留在设计期在 Object Inspector 里赋值。Open 之后可以通过 ComPort.Connected 属性判断状态。4.2 打开、发送、接收、关闭的完整流程发送部分以发送一条 AT 指令为例procedure TForm1.btnSendClick(Sender: TObject); var Cmd: string; begin if not ComPort.Connected then Exit; Cmd : edtCommand.Text; ComPort.WriteStr(Cmd #13#10); end;接收部分推荐把 UI 更新切回主线程procedure TForm1.ComPortRxChar(Sender: TObject; Count: Integer); var RecvStr: AnsiString; begin if Count 0 then Exit; SetLength(RecvStr, Count); ComPort.ReadStr(RecvStr, Count); TThread.Queue(nil, procedure begin Memo1.Lines.Add(string(RecvStr)); end); end;这里我特意用了 AnsiString 接收再转 string就是为了避免 XE7 下 Unicode 编码转换带来的乱码。如果下位机返回的是纯 ASCII 文本这样处理最稳。关闭释放也要养成习惯procedure TForm1.FormDestroy(Sender: TObject); begin if ComPort.Connected then ComPort.Close; end;4.3 关于二进制数据和中文编码的处理有些设备传输的不仅仅是文本还有二进制帧比如 Modbus RTU 的 16 进制报文。这时候再用 ReadStr 就未必合适了应该用字节数组重载procedure TForm1.ComPortRxChar(Sender: TObject; Count: Integer); var Buffer: array of Byte; I: Integer; HexStr: string; begin if Count 0 then Exit; SetLength(Buffer, Count); ComPort.Read(Buffer[0], Count); // 直接读字节 HexStr : ; for I : 0 to Count - 1 do HexStr : HexStr IntToHex(Buffer[I], 2) ; TThread.Queue(nil, procedure begin Memo1.Lines.Add(HexStr); end); end;中文设备协议里字符串通常以 GBK 编码发送。XE7 的 TComPort 不做编码转换你收到的是原始字节。拿到以后需要手动用 TEncoding 把 GBK 字节流转成 UnicodeStringfunction GBKBytesToStr(const Bytes: TBytes): string; begin Result : TEncoding.GetEncoding(936).GetString(Bytes); end;反过来发送中文前先把 string 转成 GBK 字节流再调用ComPort.Writeprocedure SendChineseText(const Text: string); var Bytes: TBytes; begin Bytes : TEncoding.GetEncoding(936).GetBytes(Text); if Length(Bytes) 0 then ComPort.Write(Bytes[0], Length(Bytes)); end;这一条非常非常关键。我做某个温控设备对接时设备要求汉字用 GBK 编码默认的 WriteStr 直接传 string 在 XE7 下会把 UTF-16 内容当 AnsiString 发出去设备端解析全是乱码。改成显式编码后一次通过。5. 高频问题与排查心法5.1 常见问题速查表现象可能原因排查方向Open 时报 Access denied串口被其他程序占用比如串口助手仍开着关掉占用程序或用注册表/设备管理器确认串口被占用打开后收不到数据波特率/数据位/停止位与设备不一致重新核对设备手册示波器或逻辑分析仪看波形报错 Cannot perform this operation on an open dataset串口打开状态下重复设置了 Buffer 等属性修改串口参数前先 Close设置完再 Open接收中文乱码编码不匹配XE7 下的 string 默认 UTF-16按 4.3 节方法显式转 GBK 或 UTF8USB 转串口拔掉后崩溃设备驱动移除后句柄失效在 FormCloseQuery 或异常处理里检测 Connected并重新枚举端口OnRxChar 里操作 UI 导致偶发卡死跨线程访问 VCL 控件改用 TThread.Queue 或 Synchronize 更新 UI大数据量接收丢帧缓冲区过小或 OnRxChar 处理时间太长调大 Buffer.InputSize不要在事件里做耗时解析COM10 以上端口无法打开Windows 对 COM 端口名的特殊格式要求端口名写成\\.\COM105.2 几个值得注意的坑第一个坑是“无效的授权说明”。网上流传的一些 TComPort 4.14 修改版或安装包打包了不该有的 DLL 或注册表项运行 Delphi 时报授权类错误。正版 4.14 源码包是纯 pas 文件编译不依赖任何第三方 DLL。遇到这类报错建议直接把源码重编一次不要用网上别人编译好的 bpl。第二个坑是“串口参数不能热改”。设备运行中用户可能从界面切换波特率。直接改 BaudRate 属性在 TComPort 里通常可以生效但稳妥做法是 Close 再 Open。我遇到过连续多次热切换后底层句柄状态异常的情况表现为某次切换后 OnRxChar 不再触发关掉重开又恢复。后来统一改成“先 Close改完参数再 Open”的模式问题彻底消失。第三个坑和 Dont Sleep 省电策略有关。有些笔记本或工控主板在电池供电时会进入 USB 选择性暂停USB 转串口芯片也跟着休眠串口应用表现出来就是长时间不通信后突然卡死或设备离线。处理方式是在 Windows 电源管理里禁用 USB 选择性暂停同时在上位机加心跳包机制定期发送查询指令激活链路。第四个坑是线程生命周期。如果你在 DLL 动态库中创建了 TComPort必须在卸载 DLL 之前确保串口已关闭、接收线程已退出否则 DLL 卸载时进程直接崩溃。这个情况在插件式架构的系统中特别容易踩我建议在 DLL 的入口函数里显式释放窗体并调用 ComPort.Close而不是依赖析构顺序。5.3 编码问题补充排查串口调试里最隐蔽的问题往往是“设备明明有返回程序就是解析不对”。此类问题 80% 出在编码。快速定位法先用串口助手最好是支持 HEX 显示的抓一次原始报文判断设备返回的是 ASCII 字符串还是二进制帧。如果是 ASCII再看有无中文有中文确认 GBK 还是 UTF-8用助手的编码转换功能对比如果是二进制帧直接按字节处理不要往字符串上靠。还有个惯用技巧在 OnRxChar 里第一行先持久化计数把每次触发的 Count 写入日志。通过计数变化可以看出数据是分几次到达的帮助判断是粘包还是半包。配合 TComDataPacket 做分包时这个日志尤其管用。6. 我的最终部署体会TComPort 4.14 说不上完美但它在一个工具类组件应有的位置上是够格的。真正把它用好靠的不是背属性列表而是理解串口通信的本质字节流、波特率、缓冲、超时、线程边界。把这一套想清楚Delphi 7 也好XE7 也罢甚至换成 C Builder思路都一样。最后再分享一个我在实际项目中养成的小习惯凡是涉及串口参数的界面一定要提供“读取当前配置”和“保存配置到文件”两个按钮把端口、波特率、校验方式等持久化到 INI 或注册表。现场调试时设备更换、端口漂移是家常便饭有个能直接改参数不重新编译的入口能省下大把出差成本。这套做法我用了很多年每次项目验收都因此少挨不少骂。本文还有配套的精品资源点击获取
返回列表