Skip to content

SerialPort 串口收发示例(Test1)

概述

本示例演示如何使用 cSerialPort 类实现串口持续收发通信。程序打开 COM4,使用 cTimer 定时器每 2 秒发送一次 hello,并通过事件驱动方式自动接收对端返回的数据,实时显示在列表框中。

项目结构

Test1/
  ├── Form1.frm       # 主窗体,串口收发逻辑
  ├── COM3_TEST.vbp   # VB6 项目文件
  ├── COM3_TEST.vbw   # 工程组文件
  └── pic/            # 截图目录

下载源码 [ 注意:重新到 bin 目录注册 DLL 文件 ]

代码请到首页下载 VBMAN 项目,解压后,打开包含所有 DEMO 项目。

核心代码解析

1. 对象声明

使用 WithEvents 声明串口和定时器对象,以便接收事件回调:

vb
Private WithEvents m_Port As cSerialPort   ' 串口对象(接收事件)
Private WithEvents m_Tmr As cTimer         ' 定时器对象(定时发送)

2. 启动串口

窗体加载时自动启动 COM4 通信:

vb
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 Sub

3. 定时发送

cTimer 每 2 秒触发一次,发送 hello

vb
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 Sub

4. 事件驱动接收

当输入缓冲区有新数据到达时,DataReceived 事件自动触发,读取并显示:

vb
Private Sub m_Port_DataReceived(ByVal BytesCount As Long)
    Dim s As String
    s = m_Port.ReadExisting()
    Logs "[RX] " & s
End Sub

5. 错误处理

串口通信错误通过事件回调捕获:

vb
Private Sub m_Port_ErrorOccurred(ByVal ErrorCode As Long, ByVal ErrorMsg As String)
    Logs "[ERR] " & ErrorMsg
End Sub

6. 停止与清理

vb
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

功能说明

  1. 持续通信

    • 使用 cTimer 定时器每 2 秒自动发送 hello
    • 通过 StartMonitoring 启动事件监控,自动接收数据
  2. 事件驱动架构

    • DataReceived 事件:有数据到达时自动触发
    • ErrorOccurred 事件:通信错误时自动触发
    • m_Tmr_Timer 事件:定时发送触发
  3. 实时日志

    • 收发记录实时显示在 ListBox
    • 每条记录带时间戳,新记录显示在顶部

技术要点

  1. WithEvents 事件绑定:通过 WithEvents 声明对象变量,VB6 自动生成事件处理过程,无需手动注册回调。

  2. 异步监控模式StartMonitoring 50 以 50ms 间隔轮询串口缓冲区,检测到新数据时触发 DataReceived 事件,不阻塞 UI 线程。

  3. 对象生命周期管理StopCOM4 中按顺序停止定时器、关闭串口、释放对象,避免资源泄漏。先 Enabled = FalseSet Nothing

  4. 配置参数:示例使用 9600/8/N/1 的常见配置,使用枚举常量 br9600ptNonesb1 设置(编译为 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 或将源码加入工程。

扩展建议

  1. 改用其他端口:修改 m_Port.PortName = "COM3" 等以适配实际硬件。
  2. 动态配置:在窗体上添加端口名、波特率输入框,运行时动态设置。
  3. 十六进制显示:将接收数据以十六进制显示,便于调试二进制协议。
  4. 发送自定义数据:添加文本框和发送按钮,支持手动发送任意数据。
  5. 信号线监控:订阅 PinChanged 事件,监控 CTS/DSR/RING 状态变化。
  6. Modbus 协议:基于本示例扩展,实现 Modbus RTU 主站/从站通信。
  7. 数据分帧:当设备连发导致粘包时,使用分帧功能识别每一帧(详见下文)。

进阶:使用分帧解决粘包问题

问题场景

本示例使用 DataReceived 事件 + 50ms 轮询读取数据。当设备连续发送多帧数据时(如设备快速发了几次响应),50ms 内缓冲区可能堆积好几帧,ReadExisting() 一次读出所有数据,导致帧粘连无法解析。

方案 B:帧间隔超时分帧

利用设备发完一帧后的短暂停顿作为天然分帧点:

vb
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 分隔):

vb
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

详细的分帧原理、四种协议模式(分隔符/起止标记/长度前缀/固定长度)、调参与排错,请参阅 串口分帧协议专题文档

VB6及其LOGO版权为微软公司所有