SerialPort 串口收发示例(Test1)
概述
本示例演示如何使用 cSerialPort 类实现串口持续收发通信。程序打开 COM4,使用 cTimer 定时器每 2 秒发送一次 hello,并通过事件驱动方式自动接收对端返回的数据,实时显示在列表框中。
项目结构
Test1/
├── Form1.frm # 主窗体,串口收发逻辑
├── COM3_TEST.vbp # VB6 项目文件
├── COM3_TEST.vbw # 工程组文件
└── pic/ # 截图目录下载源码 [ 注意:重新到 bin 目录注册 DLL 文件 ]
代码请到首页下载 VBMAN 项目,解压后,打开包含所有 DEMO 项目。
核心代码解析
1. 对象声明
使用 WithEvents 声明串口和定时器对象,以便接收事件回调:
Private WithEvents m_Port As cSerialPort ' 串口对象(接收事件)
Private WithEvents m_Tmr As cTimer ' 定时器对象(定时发送)2. 启动串口
窗体加载时自动启动 COM4 通信:
Public Sub StartCOM4()
Set m_Port = New cSerialPort
Set m_Tmr = New cTimer
' 配置串口参数
m_Port.PortName = "COM4"
m_Port.Config.BaudRate = br9600
m_Port.Config.DataBits = 8
m_Port.Config.Parity = ptNone
m_Port.Config.StopBits = sb1
If Not m_Port.OpenPort() Then
Logs "[FAIL] 无法打开 COM4: " & m_Port.LastErrorMsg
Exit Sub
End If
' 启动串口监控(触发 DataReceived 事件)
m_Port.StartMonitoring 50
' 启动定时器,每2秒发送一次 hello
m_Tmr.Interval = 2000
m_Tmr.Enabled = True
Logs "[INFO] COM4 已打开,每2秒发送 hello,持续监听接收..."
End Sub3. 定时发送
cTimer 每 2 秒触发一次,发送 hello:
Private Sub m_Tmr_Timer()
If Not m_Port Is Nothing Then
If m_Port.IsOpen Then
m_Port.WriteText "hello"
Logs "[TX] hello"
End If
End If
End Sub4. 事件驱动接收
当输入缓冲区有新数据到达时,DataReceived 事件自动触发,读取并显示:
Private Sub m_Port_DataReceived(ByVal BytesCount As Long)
Dim s As String
s = m_Port.ReadExisting()
Logs "[RX] " & s
End Sub5. 错误处理
串口通信错误通过事件回调捕获:
Private Sub m_Port_ErrorOccurred(ByVal ErrorCode As Long, ByVal ErrorMsg As String)
Logs "[ERR] " & ErrorMsg
End Sub6. 停止与清理
Public Sub StopCOM4()
If Not m_Tmr Is Nothing Then
m_Tmr.Enabled = False
Set m_Tmr = Nothing
End If
If Not m_Port Is Nothing Then
If m_Port.IsOpen Then m_Port.ClosePort
Set m_Port = Nothing
End If
Logs "[INFO] 已停止"
End Sub功能说明
持续通信
- 使用
cTimer定时器每 2 秒自动发送hello - 通过
StartMonitoring启动事件监控,自动接收数据
- 使用
事件驱动架构
DataReceived事件:有数据到达时自动触发ErrorOccurred事件:通信错误时自动触发m_Tmr_Timer事件:定时发送触发
实时日志
- 收发记录实时显示在
ListBox中 - 每条记录带时间戳,新记录显示在顶部
- 收发记录实时显示在
技术要点
WithEvents事件绑定:通过WithEvents声明对象变量,VB6 自动生成事件处理过程,无需手动注册回调。异步监控模式:
StartMonitoring 50以 50ms 间隔轮询串口缓冲区,检测到新数据时触发DataReceived事件,不阻塞 UI 线程。对象生命周期管理:
StopCOM4中按顺序停止定时器、关闭串口、释放对象,避免资源泄漏。先Enabled = False再Set Nothing。配置参数:示例使用 9600/8/N/1 的常见配置,使用枚举常量
br9600、ptNone、sb1设置(编译为 DLL 后枚举随类型库导出,可直接使用)。
运行效果
2026/7/5 10:30:00 [INFO] COM4 已打开,每2秒发送 hello,持续监听接收...
2026/7/5 10:30:02 [TX] hello
2026/7/5 10:30:02 [RX] world
2026/7/5 10:30:04 [TX] hello
2026/7/5 10:30:04 [RX] world
...运行前提
- 需要一个可用的串口(COM4),或使用虚拟串口软件(如 com0com、Virtual Serial Port Driver)创建虚拟串口对。
- 对端设备收到
hello后应回应数据(如world)。 - 已注册 VBMAN.dll 或将源码加入工程。
扩展建议
- 改用其他端口:修改
m_Port.PortName = "COM3"等以适配实际硬件。 - 动态配置:在窗体上添加端口名、波特率输入框,运行时动态设置。
- 十六进制显示:将接收数据以十六进制显示,便于调试二进制协议。
- 发送自定义数据:添加文本框和发送按钮,支持手动发送任意数据。
- 信号线监控:订阅
PinChanged事件,监控 CTS/DSR/RING 状态变化。 - Modbus 协议:基于本示例扩展,实现 Modbus RTU 主站/从站通信。
- 数据分帧:当设备连发导致粘包时,使用分帧功能识别每一帧(详见下文)。
进阶:使用分帧解决粘包问题
问题场景
本示例使用 DataReceived 事件 + 50ms 轮询读取数据。当设备连续发送多帧数据时(如设备快速发了几次响应),50ms 内缓冲区可能堆积好几帧,ReadExisting() 一次读出所有数据,导致帧粘连无法解析。
方案 B:帧间隔超时分帧
利用设备发完一帧后的短暂停顿作为天然分帧点:
Public Sub StartCOM4()
Set m_Port = New cSerialPort
m_Port.PortName = "COM4"
m_Port.Config.BaudRate = br9600
If Not m_Port.OpenPort() Then
Logs "[FAIL] 无法打开 COM4: " & m_Port.LastErrorMsg
Exit Sub
End If
' 关键:启用分帧,30ms 无新数据 = 一帧结束
m_Port.FrameInterval = 30
' 轮询间隔建议 <= FrameInterval/2
m_Port.StartMonitoring 20
Logs "[INFO] COM4 已打开,分帧模式已启用"
End Sub
' 改用 FrameReceived 事件替代 DataReceived
Private Sub m_Port_FrameReceived(FrameData() As Byte)
Dim s As String
s = StrConv(FrameData, vbUnicode)
Logs "[RX帧] " & s & " (" & UBound(FrameData) + 1 & "字节)"
End Sub方案 C:协议分帧解析器
按设备协议精确切分(如按 \r\n 分隔):
Private m_Parser As cSerialFrameParser
Private Sub Form_Load()
Set m_Port = New cSerialPort
Set m_Parser = New cSerialFrameParser
' 配置协议分帧
m_Parser.SetDelimiterMode vbCrLf, False
m_Port.PortName = "COM4"
m_Port.Config.BaudRate = br9600
m_Port.FrameInterval = 50 ' 方案 B 粗切
m_Port.OpenPort
m_Port.StartMonitoring 20
End Sub
' 方案 B 触发时间帧 → 喂给方案 C 精切
Private Sub m_Port_FrameReceived(FrameData() As Byte)
m_Parser.AppendData FrameData
Do While m_Parser.HasFrame()
Dim frame() As Byte
frame = m_Parser.GetFrame()
Logs "[完整帧] " & StrConv(frame, vbUnicode)
Loop
End Sub详细的分帧原理、四种协议模式(分隔符/起止标记/长度前缀/固定长度)、调参与排错,请参阅 串口分帧协议专题文档。