目录

关于上位机一些内容

第一模块:物理层–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.收到数据先扔进去,然后根据协议逻辑(比如查找包头,计算长度)去检查缓存区里的数据够不够一个包。够了就取出来处理,不够就等下一次 DataReceived。

问:界面上要实时显示100个传感器数据,怎么做到不卡顿?

答:

  1. 通讯层:不要读取100次,而是用ModBus 的“批量读取”功能,一次读 100 个寄存器

  2. 渲染层:不要每毫秒都刷新UI,数据也就是数据,可以存内存。UI层可以用定时器,每 100ms-200ms从内存取最新值刷新一次界面(人眼反应不过来太多的刷新,高频刷新只会卡死UI线程)

问:你的WPF上位机有什么亮点?

答:使用了MVVM模式,结合Prism/MvvmLight框架,对于复杂的工业流程,使用状态机(State Machine)模式来管理设备,而不是写一堆if-else。界面上利用WPF的矢量绘图能力,做出了可缩放的设备监控图,比传统WinForms位图清晰且性能更好。