C#串口通信详解

串口通信详解

串口通信详解:从基本原理到 C# 上位机工业现场应用

在 C# 上位机、SCADA、MES、工业自动化、设备监控等项目中,串口通信是非常基础也非常重要的一项技术。

例如:

  • 上位机读取空压机运行数据
  • 上位机读取电表、电流表、电压表
  • 上位机控制水泵、风机
  • PLC 与电脑之间交换数据
  • 温度、压力、流量传感器向上位机发送数据
  • 变频器接收启动、停止、频率设置命令
  • 工业仪表通过 RS485 接入监控系统
  • 多台设备通过 Modbus RTU 接入 SCADA 系统

这些场景背后,经常都能看到:

串口 + RS485 + Modbus RTU

需要特别注意:

串口通信、RS485、Modbus RTU 不是同一个东西。

可以先建立一个整体概念:

1
2
3
4
5
6
7
8
9
应用层
↓
Modbus RTU / 自定义协议
↓
串口通信
↓
RS485 / RS232
↓
物理线路

其中:

  • 串口通信:一种数据传输方式
  • RS232 / RS485:规定电气接口和信号传输方式
  • Modbus RTU:规定数据应该如何组织、设备如何请求和响应

一、什么是串口通信?

串口通信,全称是 Serial Communication。

它最核心的思想非常简单:

数据按照顺序,一个 bit 一个 bit 地发送。

例如一个字节:

1
10110010

不是 8 个 bit 同时发送,而是依次发送:

1
1 → 0 → 1 → 1 → 0 → 0 → 1 → 0

所以叫:

Serial,串行。

与之相对的是并行通信:

1
2
3
4
5
并行:

1 0 1 1 0 0 1 0
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
同时传输

串行通信:

1
1 → 0 → 1 → 1 → 0 → 0 → 1 → 0

串口最大的优势是:

  • 线路简单
  • 成本低
  • 实现简单
  • 设备兼容性高
  • 特别适合工业设备通信

因此即使现在工业现场大量使用工业以太网,RS232、RS485 等串行通信方式仍然大量存在。尤其是老旧设备、仪表、变频器、PLC、传感器等设备。


二、串口通信到底解决什么问题?

假设现在有一台电脑:

1
2
3
4
5
6
7
8
9
┌──────────────────┐
│ C# 上位机 │
└────────┬─────────┘
│
│ 通信
↓
┌──────────────────┐
│ 空压机 │
└──────────────────┘

电脑需要知道:

1
2
3
4
5
6
当前压力是多少?
当前温度是多少?
当前电流是多少?
当前频率是多少?
设备是否运行?
有没有报警?

设备也可能需要接收:

1
2
3
4
5
启动
停止
设置频率
设置压力
复位报警

这时候就需要一种设备之间约定好的通信方式。

串口负责:

把数据从 A 设备传到 B 设备。

但是串口本身并不知道:

1
01 03 00 10 00 02 C5 CD

到底是什么意思。

这就需要更上层的通信协议。

例如:

1
Modbus RTU

所以可以理解为:

1
2
3
4
5
6
7
8
串口
= 运输道路

Modbus RTU
= 交通规则

数据
= 运输的货物

三、串口通信中的几个核心概念

实际开发中经常看到:

1
2
3
4
5
COM3
9600
8
None
1

这几个参数非常重要。

通常完整配置类似:

1
2
3
4
5
COM3
BaudRate = 9600
DataBits = 8
Parity = None
StopBits = 1

也就是:

1
2
3
4
5
串口:COM3
波特率:9600
数据位:8
校验位:None
停止位:1

如果通信双方配置不一致,就可能无法正常通信。


四、什么是 COM 口?

在 Windows 中,串口通常显示为:

1
2
3
4
5
COM1
COM2
COM3
COM4
...

例如 USB 转 RS485 后,Windows 可能显示:

1
USB Serial Port (COM5)

那么 C# 程序就可以打开:

1
COM5

可以通过 Windows 的设备管理器查看。

例如:

1
2
3
4
5
设备管理器
↓
端口(COM 和 LPT)
↓
USB-SERIAL CH340 (COM5)

这意味着:

1
2
3
4
5
6
7
8
9
C#程序
↓
COM5
↓
USB转RS485
↓
RS485线路
↓
工业设备

五、什么是波特率?

波特率:

1
Baud Rate

表示通信速度。

工业设备中常见:

1
2
3
4
5
6
7
8
1200
2400
4800
9600
19200
38400
57600
115200

例如:

1
9600

可以简单理解为:

每秒传输约 9600 个符号。

在常见的 UART 异步串口场景中,波特率与 bit/s 密切相关,但严格来说两者并非任何情况下都完全等价。

工业设备最常见的配置之一就是:

1
2
3
4
9600
8
N
1

也就是:

1
2
3
4
9600 baud
8 data bits
No parity
1 stop bit

六、什么是数据位?

DataBits 表示:

每个数据字符包含多少个有效数据位。

常见:

1
2
7
8

工业通信中:

1
8

非常常见。

例如:

1
10110010

就是:

1
8 bit

七、什么是校验位?

Parity:

1
2
3
None
Even
Odd

常见:

1
2
3
None
Even
Odd

它的作用主要是:

对数据传输进行简单的错误检测。

例如:

1
None

表示:

1
无校验
1
Even

表示:

1
偶校验
1
Odd

表示:

1
奇校验

如果设备要求:

1
9600 8 E 1

而你的程序配置成:

1
9600 8 N 1

那么即使:

1
2
3
COM口正确
RS485线路正确
设备地址正确

也可能无法正常通信。


八、什么是停止位?

StopBits 表示:

一个数据帧结束之后,用多少位表示结束。

常见:

1
2
3
1
1.5
2

工业设备中:

1
1

非常常见。

例如:

1
9600 8 N 1

就是:

1
2
3
4
9600 波特率
8 数据位
N 无校验
1 停止位

这是一种非常常见的串口参数组合。


九、UART 与 RS232、RS485 到底是什么关系?

这是初学者最容易混淆的地方。

可以简单理解成:

1
2
3
4
5
6
7
8
9
10
11
UART
↓
数字数据怎么组织、发送
↓
RS232 / RS485
↓
电气信号怎么在线路上传输
↓
Modbus RTU
↓
这些数据具体代表什么

严格来说:

UART 是一种串行收发硬件/逻辑机制;RS232、RS485 是电气接口标准;Modbus RTU 是通信协议。

因此:

1
2
3
4
5
UART ≠ RS485

RS485 ≠ Modbus

Modbus ≠ 串口

它们属于不同层次。


十、RS232 是什么?

RS232 是一种经典的串行通信接口标准。

它通常适合:

1
2
3
4
5
设备 A
│
│
│
设备 B

也就是:

点对点通信。

典型应用:

  • 电脑连接工业仪表
  • PLC 编程口
  • 设备调试
  • 打印机
  • 老式工业设备
  • 仪器仪表

RS232 的特点:

1
2
3
4
点对点
短距离
设备数量少
实现简单

因此如果只有:

1
电脑 ↔ 一台设备

RS232 就可能非常合适。


十一、RS485 是什么?

RS485 是工业自动化中非常重要的一种串行接口标准。

它最大的优势之一是:

可以让多个设备挂在同一条总线上。

例如:

1
2
3
4
5
6
7
8
9
                 ┌── 电表
│
├── 温度仪
│
电脑 ── RS485 ───┼── 压力传感器
│
├── 变频器
│
└── PLC

这就是典型的工业现场结构。

RS485 使用差分信号,相比 RS232 更适合工业环境中的较长距离和噪声环境;RS485 也常用于多设备总线。


十二、为什么工业现场喜欢 RS485?

因为工厂环境非常复杂。

例如:

1
2
3
4
5
6
7
8
电机
变频器
接触器
继电器
空压机
水泵
风机
PLC

这些设备工作时可能产生各种电磁干扰。

如果通信线路抗干扰能力不足,就可能出现:

1
2
3
4
5
数据错误
CRC错误
偶发超时
通信断开
数据跳变

RS485 使用差分传输:

1
2
A线
B线

接收端关注的是两条线之间的电压差,而不是单纯某一根线相对于地的电压,因此在工业环境中通常具有更好的抗共模干扰能力。


十三、RS485 和 Modbus RTU 是什么关系?

这是做上位机必须理解的一个概念。

例如:

1
2
3
4
5
6
7
C#上位机
↓
Modbus RTU
↓
RS485
↓
变频器

这里:

1
Modbus RTU

是协议。

1
RS485

是物理通信接口。

因此:

RS485 是路,Modbus RTU 是规则。

Modbus RTU 通常运行在 RS485 上,也可以使用 RS232 等串行接口。它广泛用于 PLC、变频器、电表、传感器、远程 I/O 等工业设备。


十四、什么是 Modbus RTU?

Modbus 是工业自动化领域非常经典的通信协议。

它采用典型的:

1
2
3
请求
↓
响应

模式。

例如:

1
2
3
4
5
6
7
8
9
上位机
│
│ 请求:读取压力
↓
PLC / 仪表
│
│ 响应:压力 = 0.85MPa
↓
上位机

所以它非常适合:

1
2
3
监控
采集
控制

十五、Modbus RTU 中的设备地址

假设 RS485 总线上挂了 4 个设备:

1
2
3
4
5
6
7
8
9
10
11
          ┌── 设备1
│ 地址:1
│
上位机 ───┼── 设备2
│ 地址:2
│
├── 设备3
│ 地址:3
│
└── 设备4
地址:4

上位机发送:

1
01 ...

表示:

1
找地址 1 的设备

发送:

1
03 ...

表示:

1
功能码 03

例如读取保持寄存器。

因此一条 Modbus RTU 请求可能类似:

1
01 03 00 10 00 02 C5 CD

可以粗略拆解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
01
↓
设备地址

03
↓
功能码

00 10
↓
起始寄存器

00 02
↓
读取两个寄存器

C5 CD
↓
CRC校验

实际项目中,寄存器地址、数据类型、缩放比例、字节序等都必须以设备厂家的协议说明书为准。


十六、为什么上位机需要“协议文档”?

这是实际工作中非常重要的一点。

很多刚开始做上位机的人会认为:

“我把 COM 口打开,然后 Read/Write 就可以了。”

实际上远远不够。

设备厂家一般会给你一份:

1
设备通信协议

里面可能规定:

1
2
3
4
5
6
7
8
9
10
11
12
13
设备地址
波特率
数据位
校验方式
停止位
功能码
寄存器地址
数据类型
数据长度
读写权限
数据缩放比例
异常码
CRC算法

例如:

1
2
3
4
5
压力
寄存器:40001
类型:16-bit
单位:0.01MPa
读写:只读

那么设备返回:

1
85

上位机可能需要计算:

1
2
3
85 × 0.01
=
0.85 MPa

因此:

上位机开发真正困难的地方,往往不是打开串口,而是正确理解和实现设备通信协议。


十七、C# 如何实现串口通信?

.NET 提供:

1
System.IO.Ports.SerialPort

用于串口操作。Microsoft 官方文档也将 SerialPort 定义为用于控制串口资源的类,并支持同步和事件驱动的 I/O。

最基本的代码:

1
2
3
4
5
6
7
8
9
using System.IO.Ports;

SerialPort serialPort = new SerialPort();

serialPort.PortName = "COM3";
serialPort.BaudRate = 9600;
serialPort.DataBits = 8;
serialPort.Parity = Parity.None;
serialPort.StopBits = StopBits.One;

然后打开:

1
serialPort.Open();

Microsoft 的 SerialPort.Open() 用于打开新的串口连接,如果端口不存在、已经被其他程序占用或参数非法等,会抛出相应异常。

关闭:

1
serialPort.Close();

十八、C# 发送数据

例如:

1
2
3
4
5
6
7
8
9
10
11
byte[] data =
{
0x01,
0x03,
0x00,
0x10,
0x00,
0x02
};

serialPort.Write(data, 0, data.Length);

这里发送的是:

1
01 03 00 10 00 02

但是如果使用 Modbus RTU:

1
CRC

通常也需要计算并追加到数据帧中。

所以真实项目中一般不会直接在业务代码里到处写:

1
serialPort.Write(...)

而是进行封装。


十九、推荐的 C# 上位机串口架构

实际项目中可以设计成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌─────────────────────────────┐
│ WPF UI │
│ 设备状态 / 压力 / 温度 / 报警 │
└──────────────┬──────────────┘
↓
┌─────────────────────────────┐
│ DeviceService │
│ 设备业务逻辑 │
└──────────────┬──────────────┘
↓
┌─────────────────────────────┐
│ ModbusService │
│ 组包 / 解包 / CRC / 超时 │
└──────────────┬──────────────┘
↓
┌─────────────────────────────┐
│ SerialPortService │
│ COM / BaudRate / Read/Write │
└──────────────┬──────────────┘
↓
RS485 / USB
↓
PLC / 设备

这样可以把:

1
2
3
4
UI
业务逻辑
通信协议
串口

分开。

这比直接在按钮事件里面操作串口更加合理。


二十、串口接收数据

C# 可以使用:

1
DataReceived

事件接收数据。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
serialPort.DataReceived += SerialPort_DataReceived;

private void SerialPort_DataReceived(
object sender,
SerialDataReceivedEventArgs e)
{
int count = serialPort.BytesToRead;

byte[] buffer = new byte[count];

serialPort.Read(buffer, 0, count);
}

但是实际项目中不能简单认为:

1
2
3
一次 DataReceived
=
一整条完整消息

这是非常重要的。


二十一、为什么 DataReceived 不能直接当成完整数据包?

假设设备发送:

1
01 03 00 10 00 02 C5 CD

实际接收时可能变成:

第一次:

1
01 03 00

第二次:

1
10 00 02

第三次:

1
C5 CD

也可能一次全部收到。

所以:

串口接收是字节流,不应该简单依赖一次事件对应一次完整业务消息。

因此实际项目一般需要:

1
2
3
4
5
6
7
8
9
10
11
12
13
接收缓存
↓
不断追加字节
↓
判断是否形成完整帧
↓
校验长度
↓
校验CRC
↓
解析
↓
交给业务层

二十二、什么是数据帧?

串口协议通常会把一段数据组织成:

1
帧 Frame

例如:

1
2
3
┌──────┬──────┬──────────┬────────┐
│地址 │功能码│ 数据 │ CRC │
└──────┴──────┴──────────┴────────┘

例如:

1
01 03 00 10 00 02 C5 CD

整个就是一帧。

接收程序需要:

1
2
3
4
5
6
7
找到帧
↓
判断长度
↓
检查CRC
↓
解析数据

二十三、什么是 CRC?

CRC:

1
2
Cyclic Redundancy Check
循环冗余校验

主要用于检测数据传输过程中是否发生错误。

例如发送:

1
01 03 00 10 00 02

计算:

1
CRC

得到:

1
C5 CD

最终:

1
01 03 00 10 00 02 C5 CD

设备收到以后:

1
2
3
4
5
6
7
8
9
重新计算CRC
↓
和收到的CRC比较
↓
一致?
↓ ↓
是 否
↓ ↓
正常 数据错误

Modbus RTU 使用 CRC 进行帧校验。


二十四、串口通信实际工作场景一:空压站上位机

结合工业上位机项目来看,这是非常典型的应用。

例如:

1
2
3
4
5
6
7
8
9
10
11
                 ┌── 空压机1
│
├── 空压机2
│
C# WPF上位机 ────┼── 冷却塔
│
├── 循环水泵
│
├── 电表
│
└── PLC

可能采用:

1
2
3
RS485
+
Modbus RTU

上位机每隔一定时间轮询:

1
2
3
4
5
6
7
读取空压机状态
读取压力
读取温度
读取电流
读取功率
读取运行频率
读取报警状态

例如:

1
2
3
4
5
6
7
8
9
10
11
Timer
↓
读取设备1
↓
读取设备2
↓
读取设备3
↓
读取电表
↓
更新UI

最终 WPF 页面显示:

1
2
3
4
5
6
7
8
9
10
11
┌────────────────────────────┐
│ 空压站监控系统 │
├────────────────────────────┤
│ 空压机1:运行 │
│ 压力:0.82 MPa │
│ 温度:75℃ │
│ 功率:125 KW │
├────────────────────────────┤
│ 空压机2:停止 │
│ 压力:0 MPa │
└────────────────────────────┘

二十五、串口通信实际工作场景二:电表

例如工厂有:

1
2
3
4
5
总电表
车间1电表
车间2电表
空压站电表
生产线电表

多个电表可以通过 RS485 接入。

例如:

1
2
3
4
5
6
7
8
9
上位机
│
│ RS485
│
├── 地址1:总电表
├── 地址2:车间1
├── 地址3:车间2
├── 地址4:空压站
└── 地址5:生产线

上位机读取:

1
2
3
4
5
6
电压
电流
功率
功率因数
有功电能
无功电能

再进一步:

1
2
3
4
5
6
7
8
9
实时数据
↓
数据库
↓
历史曲线
↓
能耗统计
↓
报表

这就是典型的:

科技节能管理系统 / 能源管理系统


二十六、串口通信实际工作场景三:变频器

例如控制一台水泵:

1
2
3
4
5
6
7
C#上位机
↓
RS485
↓
变频器
↓
水泵

上位机可以读取:

1
2
3
4
5
运行状态
运行频率
输出电流
输出电压
故障代码

也可以发送:

1
2
3
4
启动
停止
设置频率
故障复位

例如:

1
设置频率 = 45Hz

上位机:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
业务层
↓
设置Frequency = 45
↓
转换为寄存器值
↓
组装Modbus帧
↓
CRC
↓
SerialPort.Write()
↓
RS485
↓
变频器

二十七、串口通信实际工作场景四:PLC

工业现场经常是:

1
2
3
4
5
6
7
传感器
↓
PLC
↓
RS485 / Ethernet
↓
上位机

PLC负责:

1
2
3
采集
控制
逻辑运算

上位机负责:

1
2
3
4
5
6
监控
显示
报警
数据记录
报表
配置

例如:

1
2
3
4
5
6
PLC读取:

压力 = 0.85MPa
温度 = 72℃
流量 = 120m³/h
电机状态 = 运行

上位机把这些数据显示出来。

因此:

上位机并不一定直接控制每一个设备,很多时候是通过 PLC 间接获取设备数据。


二十八、串口通信实际工作场景五:传感器

例如:

1
2
3
4
5
温度传感器
压力传感器
流量计
液位计
气体检测仪

传感器可能通过:

1
2
RS485
Modbus RTU

提供数据。

例如:

1
2
3
4
5
压力传感器
地址:01
寄存器:40001
数据:850
比例:0.001

上位机:

1
2
3
850 × 0.001
=
0.85 MPa

二十九、工业项目中的完整数据链路

以空压站为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
             工业现场
│
┌─────────────┼─────────────┐
↓ ↓ ↓
空压机 电表 温度传感器
│ │ │
└──────── RS485 ────────────┘
│
Modbus RTU
│
USB-RS485
│
COM3
│
C# WPF 上位机
│
DeviceService
│
ModbusService
│
数据解析
│
┌─────────┴─────────┐
↓ ↓
UI DB
↓ ↓
实时监控 历史数据
↓
报表分析

这就是一个非常典型的工业上位机系统。


三十、串口通信的典型开发流程

第一步:确认设备接口

首先问设备厂家:

1
2
3
4
RS232?
RS485?
RS422?
网口?

如果是:

1
RS485

继续确认。


第二步:确认通信参数

例如:

1
2
3
4
BaudRate = 9600
DataBits = 8
Parity = None
StopBits = 1

第三步:确认协议

例如:

1
Modbus RTU

或者:

1
厂家自定义协议

第四步:确认设备地址

例如:

1
2
3
4
空压机1 = 1
空压机2 = 2
电表 = 3
水泵 = 4

第五步:获取协议文档

重点查看:

1
2
3
4
5
6
7
8
9
寄存器地址
功能码
数据类型
数据长度
读写权限
数据比例
单位
字节序
CRC

第六步:使用串口工具测试

不要一上来就写 WPF。

可以先使用:

1
2
3
Modbus Poll
串口调试工具
厂家调试工具

确认设备能够正常通信。

例如:

1
2
3
4
5
6
7
COM3
9600
8N1

地址:1
功能码:03
寄存器:40001

测试成功以后,再写 C#。


三十一、为什么开发上位机时推荐先用串口调试工具?

因为可以把问题拆开。

假设 C# 程序通信失败:

1
2
3
4
5
6
7
C#代码
↓
串口
↓
RS485
↓
设备

到底哪里错了?

可能是:

1
2
3
4
5
6
7
8
9
COM口错误
波特率错误
校验错误
A/B接反
设备地址错误
寄存器错误
CRC错误
协议错误
设备没有响应

如果先用工具测试:

1
2
3
串口工具
↓
设备

可以先确定:

设备和通信线路是正常的。

然后再排查 C#。

这是实际工作中非常重要的调试思路。


三十二、串口通信常见故障

1. COM口被占用

例如:

1
COM3

已经被:

1
串口调试工具

占用。

然后 C#:

1
serialPort.Open();

可能直接失败。

所以实际开发时:

1
关闭其他串口工具

或者确认端口是否被其他程序占用。


三十三、波特率错误

设备:

1
9600

程序:

1
115200

结果:

1
2
3
4
乱码
无法解析
CRC错误
无响应

所以双方必须一致。


三十四、A/B线接反

RS485 很常见的问题:

1
2
A
B

接反。

例如:

1
2
上位机 A ───── 设备 B
上位机 B ───── 设备 A

可能导致:

1
完全无法通信

因此现场排查时:

检查 RS485 A/B 接线是非常常见的一步。

但需要注意,不同厂家对 A/B、D+/D− 的标识可能存在命名差异,不能只看字母,最好结合设备手册和实际测试判断。


三十五、设备地址错误

例如设备实际:

1
地址 = 2

但是程序发送:

1
地址 = 1

那么:

1
设备2不会响应

因为它不是在呼叫自己。


三十六、寄存器地址错误

例如厂家文档:

1
2
压力:
40001

你却读取:

1
40002

程序可能:

1
有响应

但是:

1
数据完全不对

这类问题比“完全没响应”更难排查。


三十七、数据类型错误

设备返回:

1
00 55

你认为:

1
整数

但是设备实际定义可能是:

1
2
3
4
5
有符号整数
无符号整数
浮点数
32位整数
32位浮点数

例如:

1
0x0055

转换:

1
85

再根据比例:

1
2
3
85 × 0.01
=
0.85

因此:

通信成功 ≠ 数据正确。


三十八、字节序问题

这是工业通信中比较容易踩坑的地方。

例如一个 32 位数据:

1
12 34 56 78

不同设备可能采用不同字节顺序。

例如:

1
2
3
4
ABCD
CDAB
BADC
DCBA

所以如果一个浮点数读取出来:

1
非常离谱

除了检查寄存器地址,还要检查:

1
2
Byte Order
Word Order

也就是:

字节序 / 字序。


三十九、串口通信为什么需要超时?

假设:

1
2
3
上位机发送请求
↓
等待设备响应

但是设备:

1
2
3
4
断电
掉线
故障
线路断开

如果程序无限等待:

1
线程一直卡住

所以一般设置:

1
2
ReadTimeout
WriteTimeout

例如:

1
读取超时:1000ms

如果 1 秒没有响应:

1
认为本次通信失败

然后可以:

1
2
3
重试
记录日志
更新设备状态

四十、工业上位机一般如何处理通信失败?

不要简单:

1
通信失败 → 程序崩溃

而应该:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
发送请求
↓
等待响应
↓
超时?
┌──┴──┐
否 是
↓ ↓
解析 重试
↓
仍失败?
↓
标记离线
↓
记录日志
↓
UI报警

例如:

1
2
3
空压机1:在线
空压机2:在线
空压机3:通信异常

系统仍然应该继续运行。


四十一、工业上位机为什么需要通信线程?

假设 UI 线程直接执行:

1
2
读取设备
等待响应 1 秒

那么:

1
2
3
4
5
UI线程
↓
等待设备
↓
界面卡顿

因此实际项目一般会把通信任务放到:

1
2
3
4
后台线程
Task
定时任务
异步任务

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
UI线程
↓
显示界面

后台通信线程
↓
读取设备
↓
解析数据
↓
更新数据模型
↓
通知UI

这样可以保证:

设备通信不会阻塞界面。


四十二、实际项目中的通信轮询

例如有:

1
2
3
4
设备1
设备2
设备3
设备4

可以:

1
2
3
4
5
6
7
8
9
每500ms
↓
读取设备1
↓
读取设备2
↓
读取设备3
↓
读取设备4

但是如果设备很多:

1
100台设备

就不能简单地:

1
一个循环全部同步读取

否则很容易导致:

1
2
3
通信耗时过长
设备响应延迟
UI数据不及时

所以实际项目可能需要:

1
2
3
4
5
6
7
通信队列
设备调度
轮询周期
优先级
超时机制
重试机制
离线检测

四十三、推荐的上位机通信模块

如果你以后开发 C# WPF 上位机,可以把通信层设计成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Communication
│
├── SerialPortService
│
├── ModbusRtuClient
│
├── DeviceManager
│
├── DevicePollingService
│
├── ProtocolParser
│
├── Crc16
│
└── CommunicationLogger

职责:

SerialPortService

负责:

1
2
3
4
打开串口
关闭串口
发送字节
接收字节

ModbusRtuClient

负责:

1
2
3
4
5
读取寄存器
写入寄存器
组装Modbus报文
解析Modbus报文
CRC

DeviceManager

负责:

1
2
3
4
设备列表
设备地址
设备状态
设备配置

DevicePollingService

负责:

1
周期性读取设备

CommunicationLogger

负责:

1
2
3
4
发送日志
接收日志
异常日志
超时日志

例如日志:

1
2
3
4
5
6
7
[09:30:01.123]
TX:
01 03 00 10 00 02 C5 CD

[09:30:01.145]
RX:
01 03 04 00 55 00 10 AA BB

这对于工业项目排查问题非常重要。


四十四、串口通信和上位机开发的核心关系

1
2
3
4
5
6
7
C#
WPF
MVVM
上位机
SCADA
MES
工业自动化

串口通信掌握:

1
2
第一层
什么是串口

↓

1
2
3
4
5
6
第二层
COM口
BaudRate
DataBits
Parity
StopBits

↓

1
2
3
4
5
第三层
UART
RS232
RS422
RS485

↓

1
2
3
4
5
6
第四层
数据帧
字节流
CRC
超时
重试

↓

1
2
第五层
Modbus RTU

↓

1
2
第六层
C# SerialPort

↓

1
2
3
4
5
第七层
通信线程
异步
轮询
队列

↓

1
2
第八层
工业现场故障排查

四十五、“什么是串口通信?”

串口通信是一种按照顺序逐 bit 传输数据的通信方式,在工业自动化和上位机开发中应用非常广泛。常见的接口有 RS232、RS485,其中 RS232 更适合点对点通信,RS485 更适合工业现场的多设备总线通信。

在实际项目中,我接触比较多的是 RS485 + Modbus RTU。上位机通过 USB 转 RS485 接口形成 COM 口,配置波特率、数据位、校验位和停止位,然后按照设备通信协议组装请求帧,通过串口发送给 PLC、变频器、电表、传感器等设备,接收到数据后进行帧解析、CRC 校验和数据转换,最后把结果展示在 WPF 上位机界面。

实际开发中还需要处理通信超时、重试、设备离线、数据缓存、CRC 校验以及多设备轮询等问题。

这个回答已经比较接近实际上位机开发工程师的面试表达。


四十六、最终理解:串口到底是什么?

可以用一张图记住:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    C# WPF 上位机
│
│
SerialPort
│
COM3 / COM4
│
USB转RS485
│
RS485
│
┌────────┼────────┐
│ │ │
PLC 电表 变频器
│ │ │
└────────┼────────┘
│
Modbus RTU
│
请求 / 响应
│
寄存器 / 数据 / CRC

真正做工业上位机,要形成这样的思维:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
设备
↓
接口
↓
串口
↓
RS485
↓
协议
↓
数据帧
↓
CRC
↓
解析
↓
业务数据
↓
UI

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
空压机
↓
RS485
↓
Modbus RTU
↓
读取寄存器
↓
得到 85
↓
按照协议 × 0.01
↓
0.85 MPa
↓
C#模型
↓
WPF
↓
界面显示:

空压站压力:0.85 MPa

这才是串口通信在真实上位机项目中的完整应用链路。


四十七、总结

串口通信本身并不复杂,真正需要掌握的是几个层次之间的关系:

概念 作用
串口通信 数据串行传输方式
UART 串行数据收发机制
RS232 点对点串行接口标准
RS485 工业现场常用差分串行接口
Modbus RTU 工业通信协议
COM口 Windows 对串口设备的编号
波特率 通信速度参数
DataBits 数据位
Parity 校验位
StopBits 停止位
CRC 数据完整性校验
Register 设备数据寄存器
SerialPort C# 中操作串口的 API
Polling 上位机周期性读取设备
Timeout 防止通信永久等待
Retry 通信失败后的重新请求

对于C# 上位机开发来说,最值得重点掌握的一条技术路线就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
C#
↓
SerialPort
↓
COM
↓
RS485
↓
Modbus RTU
↓
CRC
↓
寄存器
↓
设备数据解析
↓
WPF/MVVM
↓
SCADA监控