串口通讯
Darra PLC 通过 SerialPortAdapter 支持 RS-232 和 RS-485 串口通讯, 适用于连接传感器、仪表、条码扫描器等串口设备。
支持接口
| 接口 | 线缆 | 距离 | 拓扑 | 典型场景 |
|---|---|---|---|---|
| RS-232 | 3~9 线 | ≤15m | 点对点 | 条码扫描器、打印机 |
| RS-485 | 2 线 (A/B) | ≤1200m | 多点总线 | 仪表、变频器、传感器 |
连接配置
{
"protocol": "SerialPort",
"portName": "COM3",
"baudRate": 9600,
"dataBits": 8,
"parity": "None",
"stopBits": 1,
"handshake": "None",
"readTimeout": 1000,
"writeTimeout": 1000,
"encoding": "ASCII"
}
常用波特率: 2400, 4800, 9600, 19200, 38400, 57600, 115200
通讯模式
请求/响应模式
PLC 主动发送请求帧, 等待设备回复:
PLC ──[请求帧]──→ 设备
PLC ←──[响应帧]── 设备
被动接收模式
设备主动发送数据, PLC 被动接收 (如条码扫描器):
PLC ←──[数据帧]── 设备 (触发扫描)
自定义协议帧
支持配置自定义帧格式:
| 字段 | 说明 | 示例 |
|---|---|---|
| 帧头 | 起始标识 | 0x02 (STX) |
| 地址 | 设备地址 | 0x01 |
| 功能码 | 操作类型 | 0x03 (读取) |
| 数据 | 有效载荷 | 可变长度 |
| 校验 | CRC/BCC/LRC | CRC16-Modbus |
| 帧尾 | 结束标识 | 0x03 (ETX) |
代码示例
// 创建串口连接
var adapter = PLCDriverFactory.Create(ProtocolId.SerialPort);
await adapter.ConnectAsync(new ConnectionConfig
{
PortName = "COM3",
BaudRate = 9600,
DataBits = 8,
Parity = System.IO.Ports.Parity.None,
StopBits = System.IO.Ports.StopBits.One
});
// 发送请求并接收响应
byte[] request = new byte[] { 0x02, 0x01, 0x03, 0x00, 0x00, 0xXX, 0xXX, 0x03 };
await adapter.WriteAsync("raw", request);
var response = await adapter.ReadAsync("raw", 256);
// 断开连接
await adapter.DisconnectAsync();
PLC 变量映射
串口接收的数据可映射到 PLC 变量:
接收缓冲区[0..1] → MW6000 (INT, 传感器值 1)
接收缓冲区[2..3] → MW6002 (INT, 传感器值 2)
接收缓冲区[4..7] → MD6004 (REAL, 温度值)
- 总线两端需加 120 ohm 终端电阻
- 同一总线最多 32 个节点 (标准驱动器), 使用增强驱动器可达 256 个
- 避免星形接线, 应使用菊花链拓扑
- 发送完成后须切换为接收模式 (半双工)
使用 IDE 内置的串口监视器可以查看原始收发数据 (十六进制/ASCII 双模式), 便于协议调试。
常见串口协议
PLC 串口对接的设备多种多样, 典型协议:
| 协议 | 设备 | 特点 |
|---|---|---|
| Modbus RTU | 变频器、温控仪 | 标准工业协议 |
| Modbus ASCII | 老旧仪表 | 可打印字符, 效率低 |
| 自定义 ASCII | 称重仪 / 条码枪 | STX + 数据 + ETX |
| 自定义二进制 | 专用设备 | 各厂商私有 |
| DNP3 | 电力 SCADA | 多字节 |
| IEC 60870-5 | 电力调度 | 多字节 |
| SECS-I | 半导体设备 | 复杂 |
Darra 通过协议解析器 (Protocol Parser) 框架把不同协议抽象成统一的"帧读写"接口。
帧分割策略
串口是字节流, 需要确定"一帧到哪里结束":
| 策略 | 适用 |
|---|---|
| 起止符 | 自定义 ASCII, 如 STX/ETX 包裹 |
| 固定长度 | 每帧 N 字节, 约定好 |
| 长度字段 | 帧头里含有长度字节 |
| 帧间超时 | Modbus RTU: 3.5 字符时间 |
| 换行符 | 文本协议 (AT 指令) |
| 校验和触发 | 累计到出现有效 CRC 才切帧 |
协议解析器按配置选择一种或多种组合。
协议解析器配置示例
ASCII 称重仪
帧格式: STX(0x02) + 重量(6 ASCII) + 单位(2 ASCII) + CR(0x0D)
示例: 02 30 30 31 32 35 30 4B 47 0D → "001250KG"
{
"protocol": "Custom",
"framing": {
"type": "StartStop",
"start": [0x02],
"end": [0x0D]
},
"parser": [
{ "name": "weight", "offset": 0, "length": 6, "type": "ASCII" },
{ "name": "unit", "offset": 6, "length": 2, "type": "ASCII" }
]
}
条码枪
帧格式: 条码 + CR+LF
{
"framing": {
"type": "LineTerminator",
"terminator": [0x0D, 0x0A]
},
"parser": [
{ "name": "barcode", "type": "ASCIIUntilTerminator" }
]
}
二进制仪表
帧格式: AA 55 + 长度(1B) + 数据 + CRC16 LSB/MSB
{
"framing": {
"type": "LengthPrefixed",
"header": [0xAA, 0x55],
"lengthOffset": 2,
"lengthSize": 1,
"lengthIncludesHeader": false
},
"parser": [
{ "name": "cmd", "offset": 3, "length": 1, "type": "UInt8" },
{ "name": "value", "offset": 4, "length": 4, "type": "Int32BE" }
],
"validator": {
"type": "CRC16",
"offset": 8,
"polynomial": "Modbus"
}
}
RS-485 半双工
RS-485 是半双工, 发送和接收不能同时。发送时需要:
- 切换到发送模式 (某些 USB-485 适配器自动切, 有些靠 RTS)
- 发完数据 + 一个字符的"发送完成"等待时间
- 切换回接收模式
Darra 适配器的 Rs485Mode 支持:
| 模式 | 实现 |
|---|---|
| Auto | 适配器硬件自动切换 (推荐) |
| RTS | 用 RTS 信号控制 |
| DTR | 用 DTR 信号控制 |
| Timed | 发完后固定等一段时间 |
虚拟串口
开发阶段可以用虚拟串口对组做仿真:
- com0com (Windows, 开源)
- socat (Linux)
- 虚拟串口驱动 (Darra 自带的
DarraVCP)
一对虚拟串口 (COM100 ↔ COM101):
- Darra PLC 连 COM100
- 测试脚本连 COM101 发模拟数据
流控制
| 流控制 | 用途 |
|---|---|
| 无 | 默认, 大多数场景 |
| RTS/CTS | 硬件流控, 快速串口 (115200+) 防止缓冲溢出 |
| XON/XOFF | 软件流控, 文本协议 |
| DTR/DSR | 设备状态指示 |
工业场景大多无流控, 靠协议层超时/重传。
编码
串口传输的字节如何解读成字符串:
| 编码 | 字节 | 支持 |
|---|---|---|
| ASCII | 1 B | 英文 |
| UTF-8 | 可变 1~4 B | 多语言, 推荐 |
| GB2312 / GBK | 可变 1~2 B | 中文老系统 (注: 此处仅串口协议解码, 非 EtherCAT 设备字符串. EtherCAT 字符串走 UTF-8/ASCII/Latin-1 兜底) |
| Shift-JIS | 可变 1~2 B | 日文设备 |
| Latin-1 | 1 B | 西欧 |
PLC 侧的 STRING 类型默认 UTF-8, 串口帧解析时需要配置对端编码一致, 否则中文乱码。
多串口
一台机器可能挂多个串口 (主机 COM1, USB 扩展 COM3COM10, PCI 卡 COM11COM20)。IDE 侧:
- 每个串口独立一个 Adapter 实例, 互相隔离
- 可以同一协议挂多个串口 (如 4 路 Modbus RTU 并行)
- 诊断面板按 COM 号显示每个串口的流量
串口转网络
物理串口不够用或距离远, 可以用串口服务器 (Serial-to-Ethernet):
PLC 电脑 —— TCP —— 串口服务器 ——串口—— 设备
Darra 支持直接连串口服务器的透传端口:
await adapter.ConnectAsync(new ConnectionConfig {
Host = "192.168.1.200",
Port = 4001, // 串口服务器第一个串口的透传端口
Protocol = "ModbusRTU"
});
适配器把 TCP 流当串口用, 上层协议无需变化。
命令重试策略
串口通讯不稳定, 重试是常态:
{
"retry": {
"maxAttempts": 3,
"delayMs": 100,
"backoff": "linear"
}
}
重试仅针对"超时"和"校验和错", 不重试"业务错" (如命令不支持)。
排错
| 症状 | 处理 |
|---|---|
| 打不开串口 | COM 号被占 / 无权限 |
| 收到乱码 | 波特率、校验位、停止位不匹配 |
| 偶发丢字节 | 波特率太高 / 流控未开 |
| 半双工回显 | RS-485 发送时采到自己的信号, 靠时序忽略 |
| 长帧解析错 | 帧间超时设小了, 帧被拆散 |
| 收发乱序 | 多线程并发访问同一串口, 加锁 |
| USB 串口断开后 COM 号变 | 设备管理器里改成固定 COM |