关于上位机一些内容
第一模块:物理层–RS232与RS485
1.串口通讯(Serial Communication)核心参数
不管是用 232 还是 485,在写代码(C# SerialPort)时,都需要配置以下 4 个参数:
- 波特率(Baud Rate):传输速度,常见9600,115200。
- 数据位(Data Bits):通常为 8。
- 停止位(Stop Bits):通常为 1。
- 校验位(Parity):奇检验、偶校验或无(None)
- “通常设备文档会给,比如 9600,N, 8,1”。
2.RS232 vs RS485
| 特性 | RS232 | RS485 | 解释 |
|---|---|---|---|
| 通讯方式 | 点对点(1对1) | 总线式(1对多) | 232只能连一个设备,485可以一根线上串几十个设备 |
| 距离 | 短(15米以内) | 长(约1200米) | 232只能放在电脑旁,485可以拉到工厂另一头 |
| 抗干扰 | 差 | 强(差分信号) | 485用两根线电压差传数据,工厂里电机多干扰大,多用485 |
| 工作模式 | 全双工 | 半双工 | 232像打电话(能同时说和听),485像对讲机(讲的时候不能听,听的时候不能讲) |
485开发注意事项:
因为 RS485 是半双工,如果发送指令后立刻去读数据,可能会读不到.通常需要在发送后 Thread.Sleep(10)毫秒,或者等待缓冲区有数据了再读.
第二模块:C#串口编程(System.IO.Ports)
容易出问题的地方:线程
1.核心类:SerialPort
SerialPort port = new SerialPort("COM1", 9600, Parity.None, StopBits.One);
port.Open();
// 发送数据
port.Write(new byte[]{0x01, 0x03,...}, 0, length);2.接受数据的两种方式
轮询模式(Polling):开一个 Task 或 Timer,死循环去 port.Read(),虽然很笨,但是在工控领域很稳定,容易控制时序
事件模式(DataReceived):
port.DataReceived +=(s, e) =>{
// 这里的代码是在【后台线程】跑的
// 不能在此处直接更新 WPF 界面
//正确做法
Application.Current.Dispatcher.Invoke(()=>{
// 更新 UI
});
};沾包和分包:
串口发数据是一流水的。你发“Hello”,可能第一次收到"He",第二次收到"llo".
解决方法:必须有协议,比如规定"以换行符结尾", 或者“固定长度”,或者“头部+长度+内容+校验”。没有协议的裸传就是耍流氓。
第三模块:网络通讯—Socket(TCP/IP)
1.TCP vs UDP
- TCP:可靠,有连接。丢包会重发,顺序不会乱。(PLC 通讯基本都有 TCP)
- UDP:快,不可靠,无连接。丢了就丢了。(视频流,高频传感器会用到)
2.客户端(Client) vs 服务端(Server)
- 通常上位机(WPF程序)是 Client
- PLC 或 仪表 是 Server
- 需要写代码去 Connect 设备的 IP 和端口
3.Socket开发的痛点(沾包/拆包)
此问题在 TCP 中比 串口更严重,因为 TCP是"流协议"
提问:你发了两个包 A 和 B,服务端可能收到的是 AB 连在一起(粘包),或者 A 的一半和 A 的 另一半(拆包)
回答:需要定义应用层协议。通常做法:包头+数据长度+数据体+校验。读取时先读包头,解析出长度,再读指定长度的字节。
第四模块:Modbus
1.Modbus RTU vs Modbus TCP
- RTU:跑在串口(232/485)上,数据是Hex(十六进制)格式。
- TCP:跑在网线上,外面包了一层TCP头
2.核心概念:寄存器(Register)
PLC里的内容地址
- 线圈(Coil):开关量(bool),比如灯亮/灭,电机启/停。
- 保持寄存器(Holding Register):数值(short/ushort),比如温度255,速度1000。
3.报文举例
例如读取1号站,从地址100开始读取第二个寄存器:
发送(Hex): 01 03 00 64 00 02 [CRC校验码]
- 01:站号
- 03:功能码(读保持寄存器)
- 00 64:起始地址(100)
- 00 02:数量
实际开发一般不自己拼字节,有一些库可以使用 NModbus4, HslCommunication(国产,收费但是有免费版)
第五模块:如何架构上位机
问:“以前的程序界面一卡一卡的,因为通讯把UI堵死了”
解决方案:
1.MVVM:必须用。UI 和 业务彻底分离
2.异步架构:所有 SOcket/SerialPort 操作全部扔到 Task.Run 或者 使用 async/await
3.心跳检测:另开一个线程,每秒问一下PLC “你还在吗”, 如果超时,UI上显示“设备离线”红色报警
4.全局异常捕获:工业现场环境恶劣,线缆松动常有的事。Socket断开时,软件不能崩,要能自动重连(Auto Reconnect)
一些问答
问:你遇到过串口接受数据不完整怎么办?
答:我会在内存里建立一个缓存区List
问:界面上要实时显示100个传感器数据,怎么做到不卡顿?
答:
-
通讯层:不要读取100次,而是用ModBus 的“批量读取”功能,一次读 100 个寄存器
-
渲染层:不要每毫秒都刷新UI,数据也就是数据,可以存内存。UI层可以用定时器,每 100ms-200ms从内存取最新值刷新一次界面(人眼反应不过来太多的刷新,高频刷新只会卡死UI线程)
问:你的WPF上位机有什么亮点?
答:使用了MVVM模式,结合Prism/MvvmLight框架,对于复杂的工业流程,使用状态机(State Machine)模式来管理设备,而不是写一堆if-else。界面上利用WPF的矢量绘图能力,做出了可缩放的设备监控图,比传统WinForms位图清晰且性能更好。