Skip to content

cSerialPort 高级进阶

📖 目录


流控制详解

流控制用于协调发送方和接收方的速率,防止数据丢失。cSerialConfig 支持五种流控制模式。

流控制类型

模式常量说明
无流控制fcNone不使用流控制
软件流控制fcXonXoff通过 XON/XOFF 字符控制
硬件流控制 RTS/CTSfcRtsCts通过 RTS/CTS 信号线控制
硬件流控制 DTR/DSRfcDtrDsr通过 DTR/DSR 信号线控制
混合流控制fcRtsCtsAndXonXoff同时使用硬件和软件流控制

软件流控制(XON/XOFF)

发送方在数据过多时收到 XOFF(&H13) 暂停发送,收到 XON(&H11) 恢复发送。

vb
cfg.FlowControl = fcXonXoff
' 可自定义 XON/XOFF 字符
cfg.XonChar = &H11
cfg.XoffChar = &H13

也可手动发送 XON/XOFF:

vb
sp.SendXOff    ' 请求对方暂停发送
sp.SendXOn     ' 请求对方恢复发送

硬件流控制(RTS/CTS)

使用 RTS/CTS 信号线进行硬件握手。发送方检测到 CTS 为高电平时才发送数据。

vb
cfg.FlowControl = fcRtsCts
' 设置 FlowControl 会自动将 RtsControl 设为 rcHandshake

注意:使用硬件流控制后,手动 SetRTS/SetDTR 可能被流控制机制覆盖。

DTR/RTS 控制模式

模式常量DTR/RTS 行为
禁用dcDisable/rcDisable信号线保持低电平
启用dcEnable/rcEnable信号线保持高电平
握手dcHandshake/rcHandshake由流控制自动管理
切换rcToggle仅 RTS,有数据时拉高

超时策略详解

Win32 串口超时由五个参数控制,理解它们的工作机制至关重要。

超时参数

参数说明
ReadIntervalTimeout两次读取字符间的最大间隔(ms)
ReadTotalTimeoutMultiplier每字节超时系数(ms/字节)
ReadTotalTimeoutConstant读取固定超时(ms)
WriteTotalTimeoutMultiplier写入每字节超时系数
WriteTotalTimeoutConstant写入固定超时(ms)

读取超时计算

总超时 = ReadTotalTimeoutMultiplier × 请求数据量 + ReadTotalTimeoutConstant

三种预设模式

1. 非阻塞读取

Read 立即返回,有数据就读,没数据返回空。

vb
cfg.SetNonBlockingRead
' 等价于:
' ReadIntervalTimeout = MAXDWORD (&HFFFFFFFF)
' ReadTotalTimeoutMultiplier = 0
' ReadTotalTimeoutConstant = 0

2. 阻塞读取

等待直到读取到指定字节数,可能永久阻塞。

vb
cfg.SetBlockingRead
' 等价于:
' ReadIntervalTimeout = 0
' ReadTotalTimeoutMultiplier = 0
' ReadTotalTimeoutConstant = 0

⚠️ 警告:阻塞模式下如果没有数据会永久挂起,建议配合 ReadTotalTimeoutConstant 使用。

3. 带超时读取

指定总超时时间。

vb
cfg.SetReadTimeout 2000    ' 2秒超时
' 等价于:
' ReadIntervalTimeout = 50
' ReadTotalTimeoutMultiplier = 0
' ReadTotalTimeoutConstant = 2000

自定义超时示例

按字节数计算超时,适合已知数据长量的场景:

vb
' 每字节 10ms + 固定 100ms
cfg.ReadIntervalTimeout = 50
cfg.ReadTotalTimeoutMultiplier = 10
cfg.ReadTotalTimeoutConstant = 100
' 读取100字节总超时 = 10×100 + 100 = 1100ms

二进制协议处理

发送二进制帧

vb
' 构建 Modbus RTU 请求帧
Dim frame(7) As Byte
frame(0) = &H01    ' 从站地址
frame(1) = &H03    ' 功能码:读保持寄存器
frame(2) = &H00    ' 起始地址高字节
frame(3) = &H00    ' 起始地址低字节
frame(4) = &H00    ' 寄存器数量高字节
frame(5) = &H0A    ' 寄存器数量低字节
frame(6) = &HC5    ' CRC 低字节
frame(7) = &HCD    ' CRC 高字节

sp.WriteData frame

接收并解析二进制帧

vb
Private Sub m_Port_DataReceived(ByVal BytesCount As Long)
    Dim buf() As Byte
    Dim n As Long
    n = m_Port.ReadData(buf)

    If n < 5 Then Exit Sub    ' Modbus 响应至少5字节

    ' 解析 Modbus 响应
    Dim slaveAddr As Byte
    Dim funcCode As Byte
    slaveAddr = buf(0)
    funcCode = buf(1)

    If funcCode And &H80 Then
        ' 异常响应
        Dim errCode As Byte
        errCode = buf(2)
        Debug.Print "Modbus 异常: " & errCode
    Else
        ' 正常响应
        Dim byteCount As Byte
        byteCount = buf(2)
        Debug.Print "收到 " & byteCount & " 字节数据"
    End If
End Sub

十六进制显示调试

vb
Private Sub m_Port_DataReceived(ByVal BytesCount As Long)
    Dim buf() As Byte
    m_Port.ReadData buf

    Dim hexStr As String
    Dim i As Long
    For i = 0 To UBound(buf)
        hexStr = hexStr & Right$("0" & Hex$(buf(i)), 2) & " "
    Next
    Debug.Print "RX: " & Trim$(hexStr)
End Sub

虚拟串口与回环测试

虚拟串口软件

在没有物理串口的开发环境中,可使用虚拟串口软件创建串口对:

  • com0com(免费开源)
  • Virtual Serial Port Driver(商业软件)
  • VSPD(商业软件)

安装后创建虚拟串口对(如 COM3 ↔ COM4),向 COM3 发送的数据会出现在 COM4 的接收缓冲区。

回环测试代码

vb
Public Sub TestLoopback()
    Dim spTx As New cSerialPort
    Dim spRx As New cSerialPort

    ' 打开虚拟串口对的两个端口
    spTx.PortName = "COM3"
    spRx.PortName = "COM4"

    Dim cfg As New cSerialConfig
    cfg.BaudRate = br9600
    Set spTx.Config = cfg
    Set spRx.Config = cfg.Clone()

    If Not spTx.OpenPort() Then
        Debug.Print "打开 COM3 失败"
        Exit Sub
    End If
    If Not spRx.OpenPort() Then
        Debug.Print "打开 COM4 失败"
        spTx.ClosePort
        Exit Sub
    End If

    ' 清空缓冲区
    spTx.PurgeAll
    spRx.PurgeAll

    ' 发送
    Dim testStr As String
    testStr = "LOOPBACK_TEST_12345"
    spTx.WriteText testStr
    Debug.Print "发送: " & testStr

    ' 等待数据到达
    Dim t As Single
    t = Timer
    Do While spRx.InBufferCount < Len(testStr) And (Timer - t) < 2
        DoEvents
    Loop

    ' 接收
    Dim received As String
    received = spRx.ReadExisting()
    Debug.Print "接收: " & received

    If received = testStr Then
        Debug.Print "[PASS] 回环测试成功"
    Else
        Debug.Print "[FAIL] 回环测试失败"
    End If

    spTx.ClosePort
    spRx.ClosePort
End Sub

动态配置管理

运行时修改配置

vb
sp.OpenPort "COM3"

' 修改波特率
sp.Config.BaudRate = br115200
sp.ApplyConfig          ' 手动应用到已打开端口

' 从端口读取实际配置
sp.RefreshConfig
Debug.Print "实际波特率: " & sp.Config.BaudRate

配置克隆

vb
Dim cfg1 As New cSerialConfig
cfg1.BaudRate = br115200
cfg1.Parity = ptEven

' 克隆配置(互不影响)
Dim cfg2 As cSerialConfig
Set cfg2 = cfg1.Clone()

cfg2.BaudRate = br9600
Debug.Print cfg1.BaudRate   ' 仍为 115200

模式字符串

vb
Dim cfg As New cSerialConfig
cfg.FromModeString "baud=115200 parity=N data=8 stop=1"
Debug.Print cfg.ToModeString()   ' baud=115200 parity=N data=8 stop=1

Modbus RTU 实践

完整的 Modbus RTU 主站请求

vb
Public Function ModbusReadHoldingRegisters( _
    ByVal sp As cSerialPort, _
    ByVal SlaveAddr As Byte, _
    ByVal StartAddr As Integer, _
    ByVal Quantity As Integer) As Byte()

    ' 构建请求帧
    Dim frame(7) As Byte
    frame(0) = SlaveAddr
    frame(1) = &H03    ' 功能码:读保持寄存器
    frame(2) = (StartAddr And &HFF00) \ &H100
    frame(3) = StartAddr And &HFF
    frame(4) = (Quantity And &HFF00) \ &H100
    frame(5) = Quantity And &HFF

    ' 计算 CRC16
    Dim crc As Long
    crc = CalcCRC16(frame, 6)
    frame(6) = crc And &HFF
    frame(7) = (crc And &HFF00) \ &H100

    ' 清空缓冲区并发送
    sp.PurgeAll
    sp.WriteData frame

    ' 等待响应(超时1秒)
    Dim t As Single
    t = Timer
    Do While sp.InBufferCount < 5 And (Timer - t) < 1
        DoEvents
    Loop

    ' 读取响应
    Dim buf() As Byte
    Dim n As Long
    n = sp.ReadData(buf)
    If n >= 5 Then
        ModbusReadHoldingRegisters = buf
    End If
End Function

使用示例

vb
Dim sp As New cSerialPort
sp.PortName = "COM3"
sp.Config.BaudRate = br9600
sp.Config.DataBits = 8
sp.Config.Parity = ptEven      ' Modbus RTU 通常使用偶校验
sp.Config.StopBits = sb1
sp.OpenPort

' 读取从站1的保持寄存器,起始地址0,数量10
Dim response() As Byte
response = ModbusReadHoldingRegisters(sp, 1, 0, 10)

If UBound(response) >= 0 Then
    ' 解析响应数据
    Dim byteCount As Byte
    byteCount = response(2)
    Debug.Print "收到 " & byteCount & " 字节数据"
End If

sp.ClosePort

最后更新: 2026-07-05

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