ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于C# WPF的晶圆与石墨岛搬移上位机系统实践

基于C# WPF的晶圆与石墨岛搬移上位机系统实践 我们直接进入正题。半导体产线里晶圆和石墨岛的搬移是每天都在发生的动作看似简单背后却是一整套上位机逻辑在支撑。这篇文章我写一下如何用 C# WPF 来搭建这么一套晶圆与石墨岛搬移上位机系统从架构设计到关键实现再到现场踩坑记录尽量把能说的干货都摆出来。先讲清楚这套系统到底解决什么问题。晶圆在扩散、氧化、薄膜等工艺段之间流转时不是裸片直接放在设备里而是需要承载在石墨岛石墨舟/石墨载具上通过机械手或人工推拉的方式完成装片、卸片、搬运。上位机的作用就是通过串口、网口或 PLC 信号与下位机交互控制搬移流程、监控设备状态、记录每一片晶圆的位置和工艺数据。换句话说它是这整个搬移流程的“大脑中枢”和“账房先生”。我真正开始做这个项目是在一条半导体扩散炉台的生产线上。现场设备是某型号的卧式扩散炉配套一台搬移机械手下位机是西门子 S7-1200 PLC上位机需要实现晶圆从片盒到石墨岛的自动搬移控制同时还要兼容手动模式、配方管理、历史追溯和生产计数。整套系统从零开始技术栈定的是 C# WPF MVVM通信走 S7 协议通过 s7netplus 库实现。1. 整体设计与架构拆解1.1 为什么选 C# WPF 而不是 WinForms 或 Web半导体设备的上位机软件说实话用 WinForms 的老系统一抓一大把很多设备商从 2010 年前后就开始用 WinForms 写客户端稳定是稳定但界面效果和开发效率放到今天已经明显跟不上。我这次选 WPF最主要的原因是 WPF 的数据绑定和 MVVM 模式天生适合这种“设备状态实时刷新 操作流程控制”的场景。设备上位机最大的痛点是什么是状态多、刷新快、操作按钮和状态显示强关联。机械手在动作的时候界面上的各个工位状态、报警信息、晶圆计数都在瞬息万变。如果用 WinForms 那种事件驱动 手动更新控件的方式代码里会塞满textBox1.Text ...这种语句而且一旦多线程更新 UI 还要频繁做Invoke写到最后自己都分不清状态是从哪里更新进去的。WPF 的绑定机制把这个问题从根本上解决了。我只需要把设备状态抽象成一个ObservableObject集合界面上的TextBlock、DataGrid、Ellipse等元素直接绑定对应的属性值后台线程只要更新属性值并触发PropertyChanged界面上所有关联控件自动刷新。整套系统写下来几乎找不到一处手动赋值 UI 控件的代码逻辑和视图彻底解耦后期加功能、改界面都不需要动核心逻辑。再说跨平台的问题。很多人问为什么不用 .NET MAUI 或者 Blazor。说实话.NET MAUI 做上位机还差一口气尤其是工控领域要用的串口通信、OPC UA、西门子 S7 协议这些库在 MAUI 上的支持和验证还远不如 WPF 成熟。WPF 虽然只支持 Windows但半导体产线的上位机99% 都是 Windows 主机这就足够了。1.2 系统整体分层与模块划分这套系统不是一个简单的单机程序我把它拆成了四个层次每层各司其职如果将来要换设备或者加模块不需要伤筋动骨。UI 展现层WPF 界面包括操作主界面、配方管理、报警列表、历史记录查询、用户登录管理几个页面。UI 层不写任何业务逻辑只管绑定和命令。业务逻辑层负责搬移流程的状态机调度、配方解析、报警判断、数据归档。这一层是系统的心脏所有流程控制都在这里完成。通信协议层封装了与 PLC 的 S7 通信、与机械手控制器的 Modbus TCP 通信、以及未来扩展用的 OPC UA 通信接口。这层对外提供统一的接口上层不需要关心底层是走网口还是串口。数据持久层使用 SQLite 存储配方、生产记录、报警日志。为什么不选 SQL Server工控机不一定有数据库服务环境SQLite 是文件型数据库部署零成本一张表几千条记录随便查。分层的好处我在项目中期体会特别明显。一开始机械手厂商给的通信协议是 Modbus RTU后来改成了 Modbus TCP我只需要替换协议层里的一个通信实现类上面的状态机和界面完全不需要动。这就是架构设计带来的实实在在的好处。2. 核心功能拆解与关键细节2.1 晶圆搬移流程的状态机设计晶圆从片盒到石墨岛的搬移流程看起来就是“取片-移动-放片”三个动作但真正落地的时候需要考虑的边界条件非常多。我画状态机的时候把整个流程切成了八个状态空闲、初始化、取片请求、取片执行、移动等待、放片请求、放片执行、完成复位。每个状态都有明确的进入条件、执行动作、超时时间和异常出口。比如在“取片执行”状态上位机先下发取片指令给机械手控制器此时启动一个 30 秒的超时计时器。如果机械手在超时时间内反馈完成信号状态切换到“移动等待”如果超时系统进入“取片超时报警”状态界面弹出报警提示同时禁止继续下发下一步指令。这里有一个设计中特别值得注意的点机械手在搬移晶圆的过程中只有“忙/闲”两个布尔信号回传给上位机但实际机械手内部还分为“夹紧”“抬升”“平移”“下降”“放松”等子动作这些子动作的细节机械手控制器自己管理不会回传给上位机。所以上位机无法精确知道晶圆当前到底在哪个机械位置只能靠“是否有动作进行中”这个信号来做判断。这在调试初期吃过大亏后面我单独讲。状态机的执行核心我用的是一个简单的单线程调度器通过CancellationTokenSource驱动循环每隔 100ms 扫描一次当前状态和外部信号然后根据状态转移表决定下一步动作。为什么不直接用async/await写流程因为设备流程里存在大量“等待外部信号”的环节用async/await会让流程函数切得非常碎不好调试。用状态机加轮询的方式逻辑是线性展开的现场工程师看代码也好理解。2.2 配方管理与数据绑定每个批次的产品工艺要求不同晶圆的数量、排列方式、石墨岛的槽位分布都可能不一样。所以系统必须有配方管理功能让操作员在更换产品时可以直接调出对应配方而不需要手动改参数。配方的核心参数包括批次号、晶圆规格4寸/6寸/8寸/12寸、片盒容量、石墨岛槽位数、搬移模式全自动/半自动、安全距离补偿、机械手速度等级、超时时间。这些参数统一存到 SQLite 的recipe表中每次生产前由操作员选择配方系统根据配方参数重新计算流程控制参数。这里想展开说一下数据绑定的细节。配方参数的编辑界面我直接用了 WPF 的DataGrid绑定一个ObservableCollectionRecipeItem每一行是一个参数项包括参数名、参数值、单位、上下限。操作员在界面上直接修改参数值绑定的源集合实时更新。修改完成后点击“保存”系统会遍历集合做一次数据合法性校验然后把数据写回recipe表。但这里有一个坑DataGrid的默认绑定在数据源更新时如果不是ObservableCollection界面不会自动刷新。另外如果后台线程修改了配方集合必须通过Application.Current.Dispatcher.Invoke回到 UI 线程来操作否则界面会直接崩溃。这是所有 WPF 开发者都会遇到的经典问题我在项目里写了一个统一的UiThreadHelper工具类来封装这个操作。2.3 与 PLC 的 S7 通信实现与西门子 S7-1200 的通信我用的库是s7netplus这是一个完全免费的开源库支持 S7-1200/1500/300/400 系列的以太网通信。相比官方S7.Nets7netplus的 API 设计更现代支持异步方法底层是直接构造 ISO-on-TCP 报文不需要在 PLC 侧额外配置通信模块。通信的核心封装是一个S7ClientService类内部维护Plc实例和读写锁。打开通信的方法很简单var plc new Plc(CpuType.S1200, 192.168.0.1, 0, 1); plc.Open();但这里有几个必须注意的点通信超时Plc对象的Read和Write方法默认超时时间很短在 NMI 上容易直接抛异常。我在封装层里把超时时间显式设置为 3000ms并且加了重试机制出错后最多重试 3 次每次间隔 500ms。数据类型对应S7 的DB1.DBW0字对应 C# 的ushortDB1.DBX0.0位对应boolDB1.DBD4双字对应uint。读写时如果不注意类型匹配很容出现数据错乱比如把uint写入DBD后又读出来变成一个错误的大数跟踪半天才发现是类型映射问题。多线程安全上位机中多个功能模块状态机、报警监控、数据采集都有可能会去读 PLC 数据如果并发读写同一个Plc实例底层 Socket 会崩。所以我在S7ClientService里用一个SemaphoreSlim保护所有读写操作同一时刻只能有一个线程在访问通信通道。通信数据的规划表格如下这是我和 PLC 工程师一起定的PLC 地址区域数据类型含义方向DB1.DBX0.0bool设备急停状态PLC → 上位机DB1.DBX0.1bool自动/手动模式PLC → 上位机DB1.DBX0.2bool搬移请求信号上位机 → PLCDB1.DBW2ushort机械手工位编号上位机 → PLCDB1.DBD4uint当前批次计数PLC → 上位机DB1.DBD8uint目标批次计数上位机 → PLCDB20.DBX0.0bool机械手忙PLC → 上位机DB20.DBX0.1bool机械手闲PLC → 上位机2.4 搬运过程中晶圆位置追踪这是这套系统最微妙的一部分——晶圆位置的追踪。晶圆在片盒里时每个槽位是否有片系统是知道的。机械手取走一片后对应槽位标记为空放到石墨岛后石墨岛的对应槽位标记为有片。这些状态全部维护在内存里的WaferTracker类中。具体实现是把片盒和石墨岛的槽位状态分别建模为两个数组bool[] magazineStatus和bool[] boatStatus。上位机每一次完整的“取片 放片”动作成功后都会同步更新这两个数组。同时界面上的示意图直接绑定这两个数组的值有片显示为绿色空位显示为灰色。操作员不需要盯着设备看一眼就能在屏幕上确认哪些槽位有片、哪些是空的。这里有一个涉及安全的关键逻辑当系统处于手动模式时操作员可能人工放入或取出一片晶圆此时槽位状态和上位机记录会不一致。我的处理方式是在手动模式下每当机械手完成一次动作都强制要求操作员在界面上确认“当前动作是否完成、槽位状态是否已变化”上位机通过确认按钮重新同步槽位数组。这个设计看起来多了一步操作但有效避免了因为状态不一致导致的撞片事故。3. 实操过程与核心环节实现3.1 环境搭建与依赖库选型我在开发这套系统的过程中环境方面其实踩了一些不必要的小坑这里直接列出来让后面的人少走弯路。软件环境我建议直接用以下组合Windows 10/11 专业版 x64 Visual Studio 2022.NET 6 或 .NET 8 WPF 项目模板。为什么用 .NET 6/8 而不是 .NET Framework 4.8因为s7netplus新版和一些依赖库都要求.NET Standard 2.0如果用老框架库的兼容性会很痛苦。另外.NET 6 的System.IO.Ports包是单独的需要通过 NuGet 安装如果只加了引用而忘了装包编译时就会报找不到串口类。依赖库清单s7netplus西门子 S7 以太网通信。CommunityToolkit.MvvmMVVM 框架比传统的 Prism 轻量得多适合中小型上位机项目。HandyControlWPF 控件库提供了一些现成的设备状态灯、报警弹窗、工艺控件界面风格比较适合工控场景。System.Data.SQLiteSQLite 的 ADO.NET 提供程序。Serilog日志组件。在所有关键流程节点都写日志后面排查现场问题全靠它。3.2 界面布局和操作流程的实际设计界面布局我分成了四个区域顶部状态栏、左侧设备状态区、中间流程控制区、右侧报警与日志区。顶部显示设备号、操作员、当前配方、系统时间。左侧每台设备用一组状态灯表示。中间是主要的操作区包含开始搬移、暂停、停止、手动/自动切换等按钮。右侧是报警列表和实时日志。这种布局不是随便排的原因很简单操作员站在设备前面戴着手套视线要在设备和屏幕之间来回切换。所有关键状态和操作按钮都要放在视线焦点能快速扫到的区域。红色报警一定要在右侧固定区域出现并且闪烁绝不能藏在下拉菜单里。这个经验是我在产线观察现场操作员实际使用习惯后总结出来的设计界面时只有坐在工位上模拟才能体会到什么叫顺手。操作员最常用的操作流程是选择配方 → 点击“开始搬移” → 系统自动完成搬运 → 结束后弹框提示“批次完成”。这几个按钮我全部放在界面正中间按钮尺寸做到了 120x48字体加粗。系统进入自动状态后中间的按钮变成灰色不可用只有“停止”按钮保持常亮。3.3 通信调试与信号联调实录真正的联调是整个项目最耗时、最容易让人崩溃的阶段。第一天上现场PLC 工程师说地址表已经做好了我直接把通信库接上结果读回来的数据全不对。排查了半天发现是 PLC 侧的 DB 块偏移地址和我代码里的地址差了 2 个字节。S7 的 DB 块中地址是按照“字节.位”来计算的如果 PLC 侧定义了多个 Bool 变量它们会按位紧凑排列之后的数据就会紧跟着排在后面不是按 2 字节对齐来的。这个点特别容易在两边定义不同数据类型时错位。更隐蔽的问题出现在“机械手忙/闲”信号的时序上。PLC 工程师提供的逻辑是机械手上升沿时忙信号为 True完成后忙信号为 False。但我实测发现机械手在连续搬移动作中忙信号会有大约 300ms 的“抖动”也就是在动作切换间隙忙信号会短暂变成 False 再变回 True。如果上位机在忙信号 False 的时候立刻执行下一步指令机械手根本反应不过来会导致指令丢失甚至停在半路报错。解决方案是在 PLC 侧加了一个 500ms 的滤波计时器忙信号只有持续为 False 超过 500ms才认为机械手真正空闲。这个参数是在现场反复调试后定下来的——太短会误判太长会拖慢节拍。3.4 数据记录与追溯模块的实现半导体生产对追溯性的要求非常严格每一片晶圆在什么时间、什么设备、什么配方条件下被搬移过都必须有记录。我在系统里设计了wafer_record表每一次搬移动作完成后写入一条记录字段包括动作时间、批次号、晶圆编号、源位置片盒槽位、目标位置石墨岛槽位、操作模式、操作员账号、异常标志。写入方式用的是批量插入每次搬移动作结束后把若干条记录打包成一批用事务进行提交避免频繁单条写入造成数据库性能瓶颈。系统同时保留最近 30 天的记录超出部分自动归档到历史库文件。追溯的查询界面做了按批次号、按时间区间、按设备号、按操作员四种过滤条件查询结果通过DataGrid展示并支持导出 CSV 文件方便工艺工程师拿到 Excel 里做分析。这个追溯模块看起来不起眼但有一次帮了大忙产线有一批晶圆出现了表面划伤工艺工程师需要知道这批晶圆在这台设备上到底是怎么被搬移的是通过哪些动作序列完成的是不是有一次动作异常导致了划片风险。我把原始记录调出来后发现那批晶圆在前一天晚上有一次“取片失败重试”记录重试次数超出正常范围。后来确认是机械手夹爪上有异物导致取片偏移正是这条追溯记录帮忙锁定了问题范围避免了整批次报废。4. 常见问题与排查技巧实录4.1 通信频繁断开且自动重连失败这是现场最常见的一个问题。现象是系统运行几分钟到几小时后上位机和 PLC 之间的通信突然断开程序卡死或一直报通信超时。我排查的第一步是先从 PLC 侧看诊断缓冲发现没有硬件故障。后来定位到是通信线程在长时间高频读写时偶发SocketException没有被捕获造成了线程异常退出通信状态一直没有恢复。解决方法是把通信的读取循环整个放进try-catch-finally在catch里记录异常日志然后主动重置Plc连接对象延迟 2 秒后重新执行Open()。另外在 PLC 侧把“上位机心跳”信号做成一个 1 秒周期翻转的 bool 变量PLC 如果连续 5 秒没有收到心跳翻转就判断上位机掉线自动把设备切回本地手动安全模式。这样即使通信中断设备不会失控。这里想补充一个很多人容易忽略的点工控机上运行的杀毒软件、系统更新服务都有可能在后台悄悄抢网络资源或扫描端口导致通信偶发中断。现场配置工控机时一定要把和 PLC 通信的网卡 IP 加入防火墙白名单并且关闭自动更新服务。我在一个现场遇到过通信瞬断但 log 里没有任何异常的怪事最后发现是 Windows 自动更新在夜间重启了网卡驱动。4.2 WPF 界面假死与线程阻塞问题WPF 的界面假死问题是新手最容易踩的坑。我在项目开发阶段就遇到过在搬移过程中点击一个按钮整个界面卡住十几秒钟然后才恢复响应。原因是在 UI 线程上同步执行了数据库写入操作或者调用了阻塞式的 PLC 读写方法。解决思路非常明确UI 线程永远不要做阻塞操作。所有数据库操作、通信操作、文件读写全部通过Task.Run放到线程池里执行结果通过IProgressT回调到 UI 线程更新。状态机调度本身也运行在独立的后台线程中它只通过线程安全的属性更新通知 UI绝不直接触碰界面控件。列一下我做的并发隔离规则状态机线程负责流程控制通过ConcurrentDictionary和SemaphoreSlim管理共享状态。通信线程负责周期性轮询 PLC 数据把数据写入到共享内存。UI 线程只负责绑定和展示通过命令触发操作时异步调用业务方法。日志写入走独立队列由后台队列消费线程负责落盘。这样设计后系统在自动运行过程中即使出现瞬间通信超时界面也不会有明显卡顿。我在界面假死这个问题上还有一段比较深刻的记忆项目做测试机械手动作过程中我执行了一次配方保存结果整个上位机界面卡了将近 5 秒。一开始我还以为是通信卡了后来在日志里看到的是 SQLite 写库造成了阻塞。当时我用的写法是在业务逻辑里顺序执行了INSERT和SELECT查询的数据量大了之后锁表时间变长。用了事务队列之后这个现象彻底消失。4.3 搬移计数与实际晶圆数量不一致还有一次现场反馈说生产结束后上位机显示搬移了 100 片但操作员清点片盒后发现只有 98 片。这个问题的根源在于“搬移动作完成”和“晶圆实际到位”不是同一个时刻。上位机是在机械手给出“放松完成”信号后就立即把槽位状态更新为“空/有片”。但机械手放松之后晶圆还需要一个小小的惯性继续落入槽位如果这个瞬间发生了一点点偏移晶圆可能没有完全落位槽位状态却已经被标记为“有片”了。这个问题的排查思路是给“放松完成”信号之后增加一个 200ms 到 500ms 的稳定延时再去驱动 PLC 读取晶圆传感器的实际在位信号这两个信号都满足后才确认槽位状态更新。后续如果有必要还可以加入视觉确认模块来做最终判定但纯 PLC 的方案已经能覆盖大部分场景。这个案例给我的教训是上位机不能完全信任机械手的“动作完成”信号必须结合传感器状态做一个二次确认。我的系统在最终阶段将“机械手放松完成”和“晶圆在位传感器”两个条件做成了逻辑与的关系只有两个信号都满足系统才认为搬移成功。现场实际跑下来计数错误的问题再没有出现过。4.4 几个隐藏的“经验性”避坑细节最后挑几个我在现场学到的、不太容易从教科书上看到的细节写出来。工控主机尽量选带串口和双网口的型号用一张独立网卡专门连接 PLC不要把办公网和工控网混在一张网卡上。我之前遇到过办公网络的风暴导致通信卡顿的情况后来物理隔离后问题消失。WPF 的动画不要滥用。工控界面上偶尔用一次闪烁提示报警是合适的但不要给所有控件加动画尤其是状态灯频繁切换时动画会让 CPU 占用飙升。备用 UPS 一定要配。半导体产线对突然断电非常敏感上位机软件本身再稳定也架不住断电丢配置。装好 UPS 后上位机做到断电后自动恢复到上次运行状态当天数据不丢失。上位机不要直接连接外网。产线的工控安全是底线关闭不必要的远程桌面端口特殊情况下需要维护时通过受控的跳板机访问。这一点在半导体行业尤其严格。5. UI 细节与扩展思考界面是整个系统的脸面也是操作员每天打交道最多的地方。我在 WPF 样式上花了相当多的时间因为一套工整的设备操作界面不仅仅是好看更是减少误操作的关键。状态灯控件的实现我是自定义的一个StatusIndicator控件通过依赖属性Status来设置颜色和闪烁行为。由于它是依赖属性天然支持绑定所以后台只要更新一个枚举值界面上所有相关状态灯都自动切换颜色非常简单优雅。这个控件还支持“确认闪烁”模式当状态异常时先闪烁红色 3 秒如果操作员没确认则持续闪烁迫使操作员关注到这个状态变化。在字体和配色方面我采用的是深色背景 高对比度前景色方案。深色界面在暗环境里不容易刺眼而且设备状态灯的亮色在暗背景下更醒目。字体我选择的是“微软雅黑”字号 14 以上确保远距离也能看清关键参数。不要用太花哨的字体工控场景最忌讳的就是过于艺术化的设计。后续扩展方向我也提前留好了接口。比如加装扫码枪在每个片盒和石墨岛都贴二维码扫码后自动识别配方免去手动输入的麻烦。再比如接入 OPC UA让上层 MES 系统能动态读取当前设备的状态和产量。这些扩展都不需要改动核心的状态机逻辑因为分层架构已经把可扩展性预留好了——通信层能加协议业务层能加动作UI 层能加页面。回头来看这套系统真正花时间的地方反而不是那些花哨的新技术而是这些插得到底的、处理起来很琐碎的“现场兼容性”问题。C# 和 WPF 的优势在于它们拥有完善的类库生态和大量的工业落地案例你去踩坑的时候能搜到一堆前人经验不至于真的孤军奋战。如果读者自己也要做类似的上位机项目我的建议是先别急着写代码把现场的电气图纸、IO 表、动作时序图全部拿在手里把状态设计图在纸上多画几遍。上位机最难的地方永远不在代码里而在你对现场设备理解得够不够深。
返回列表