硬核实战:C# + WPF 打造半导体晶圆与石墨岛搬移上位机系统
项目背景是半导体行业里非常典型的一个场景:晶圆从料盒里取出来、放置到石墨岛上、再搬入腔体进行工艺处理。整个过程中机械动作看起来并不复杂,真正难的是上位机这一层的调度逻辑、异常处理和对位补偿。做到后面你会发现,C#和WPF只是工具,核心是对工艺流程的抽象能力,以及对设备稳定性的敬畏。这篇文章我准备把整个项目从需求拆解到技术选型、核心模块实现,再到现场联调踩坑,完整地过一遍,希望能给正在做或准备做半导体设备上位机的朋友一点参考。
这套系统面对的物料主要有两类:一类是常见的硅晶圆,直径从2英寸到6英寸都有;另一类是石墨岛,也叫石墨盘或石墨舟,上面有规则排列的晶圆卡槽,批量承载晶圆进入高温炉管或PECVD腔体。上位机要控制的动作,就是机械手在晶圆盒、预对准器、石墨岛之间完成取片、校准、放片、搬移这批动作。
1. 项目需求拆解:到底"上位机"在管什么
1.1 晶圆搬移的行业痛点与设备构成
半导体设备的操作流程,在没接触过的人眼里可能就是"把东西从A挪到B"。但在现场干过的人都知道,晶圆的脆弱程度和石墨岛的水平精度,会让"挪动"这两个字变得极其苛刻。晶圆薄到一定程度、边缘稍有磕碰就会产生碎片;石墨岛批次之间尺寸存在公差,还可能因为高温工艺产生碳粉污染。人工操作效率低、一致性差,碎片率和划伤率居高不下,于是设备厂家才会引入自动搬移系统,让机械手配合上位机做标准化动作。
这类设备的机械构成并不花哨:晶圆盒装载台(FOUP/FOSB底座)、SCARA机械手或真空机械手、预对准器(找平找中心)、石墨岛承载台、若干电磁阀控制的真空吸盘、光电传感器,以及一个负责逻辑控制的上位机和一块运动控制卡。我的这套方案里运动控制卡用的是固高的GT系列,IO和气压检测也是通过板卡扩展,安全回路单独走硬件互锁。上位机运行在Windows工控机上,和运动控制卡、PLC、工艺腔体控制器之间通过不同协议通信。
1.2 核心功能清单:六件事必须一次理清
我习惯在项目启动阶段就把上位机的职责边界列成表格,避免后面开发过程中需求像滚雪球一样膨胀。这套系统最终落地的功能清单是这样的:
| 功能模块 | 要管的具体内容 | 关键输出物 |
|---|---|---|
| 流程调度 | 取片、放片、搬移、复位等动作的先后顺序与触发条件 | 状态机流转表 |
| 运动控制 | 机械手各轴的坐标点位、速度、加速度、回零方式 | 轴状态和到位信号 |
| 气路真空控制 | 吸盘启停、吹气阀动作、真空建立判断、泄漏检测 | 真空压力阈值与状态 |
| 数据管理 | 配方参数、生产批次记录、报警日志、操作履历 | 本地SQLite或MySQL |
| 安全联锁 | 门开关、急停、超行程、在位检测的联动处理 | 硬件互锁信号 |
| 人机交互 | 实时状态监控、自动/手动切换、参数配置、报警提示 | WPF界面 |
每一个模块单独拿出来都是上位机开发的基本功,难的是它们之间的时序配合。比如,机械手在取片过程中,真空阀应该在哪个坐标点打开,打开后要等多久再去检测真空压力,如果压力没到阈值要走哪条恢复路径——这些细节才是真正决定设备稳定性的地方。
1.3 需求边界:哪些必须交给硬件,哪些是软件的活
跟客户对需求的时候,我最常跟对方划清的一条线是:软件永远不碰安全关键回路。急停、门锁、超行程保护、电机使能的下电逻辑,这些必须设计在硬件层。上位机可以做监控、可以发指令、可以在异常时报警,但不能成为"最后的安全保障"。原因很简单,Windows系统会蓝屏、程序可能被任务管理器杀掉、网线可能被老鼠咬断,任何纯软件方案在半导体设备上都不够可靠。
另一个需要提前沟通的边界是"无人化"。客户希望设备全自动运行,我在技术上可以做到,但异常恢复的兜底环节还是需要人介入。比如碎片发生后,机械手要怎么摆臂才不会二次碾压碎片,这需要现场人员先做物理确认,再由软件提供半自动的"碎片清理模式"。这个边界不提前说清楚,验收阶段一定会变成需求变更的拉锯战。
2. 技术选型与架构设计的底层逻辑
2.1 C# + WPF 凭什么成为首选方案
上位机开发的主流路线无非是C++/Qt、C#/WinForm、C#/WPF、LabVIEW这几条。我选C# + WPF综合考量了开发效率、生态成熟度和界面表现力三个维度。
C#在工控圈子的生态太成熟了。固高、雷赛等运动控制卡的SDK都提供C#的调用接口,海康、大恒的工业相机SDK同样支持C#,连各种仪表传感器的串口通信例子也基本是C#写的。相比C++,C#不需要手动管理内存、不怕指针越界,开发周期至少缩短三分之一;相比LabVIEW,C#在复杂业务逻辑的建模层面要灵活得多,版本管理、单元测试、CI/CD整个软件工程链路都更顺。
WPF作为界面层的好处,用过的人基本不会再回WinForm。数据绑定(Data Binding)、MVVM模式、样式和模板(Style/Template)、依赖属性(DependencyProperty)这些能力,让界面层代码量比WinForm少很多。特别是WPF基于矢量渲染,高DPI缩放不变形,在触摸屏工控一体机和外接大显示器混用的场景下表现明显更好。实际上,你如果只用WinForm做简单的数据展示,那没问题;但像这种带完整流程管理、配方管理、多级权限的设备控制软件,WPF的MVVM架构能把复杂度摊得很开,代码结构清晰得多。
2.2 环境搭建的坑:VS2022里WPF模板不见了怎么办
网上经常有人问"VS2022中WPF的可选模板不见了",这个十有八九是装Visual Studio的时候没有勾选".NET桌面开发"工作负载。找模板最快的方式是在VS Installer里修改,勾选".NET 桌面开发"组件后补装即可,不用重装整个IDE。
另一个选型问题是.NET Framework和.NET 6/8怎么选。我现在做新项目基本直接上.NET 6以上版本。老框架虽然兼容性强,但长期支持版本更稳,异步编程、依赖注入、配置系统的体验都好很多。唯一要留意的是一些老的运动控制卡或读卡器驱动只提供了.NET Framework的封装,遇到这种情况才需要把启动项目降到.NET Framework或者单独做一个进程间通信的适配。
界面层如果涉及嵌入式网页或操作手册预览,要特别小心WPF默认的WebBrowser控件。它调用的是IE内核,在高DPI屏上显示效果差,字体模糊、排版错乱,换成CefSharp或WebView2都是更好的选择。
2.3 运动控制卡对接:DllImport与点位封装
运动控制卡基本是以动态库的形式提供接口。固高GT系列就是gts.dll,C#侧最简单的兼容方案是DllImport逐函数声明。初始化流程一般分四步:打开设备、复位轴、设置运动模式、使能。点位运动的关键调用是这样:
[DllImport("gts.dll", EntryPoint = "GT_Open")] public static extern short GT_Open(); [DllImport("gts.dll", EntryPoint = "GT_ClrSts")] public static extern short GT_ClrSts(); [DllImport("gts.dll", EntryPoint = "GT_AxisOn")] public static extern short GT_AxisOn(int axis); [DllImport("gts.dll", EntryPoint = "GT_SetPrfPos")] public static extern short GT_SetPrfPos(int axis, double pos); [DllImport("gts.dll", EntryPoint = "GT_SetVel")] public static extern short GT_SetVel(int axis, double vel); [DllImport("gts.dll", EntryPoint = "GT_SetAcc")] public static extern short GT_SetAcc(int axis, double acc); [DllImport("gts.dll", EntryPoint = "GT_Move")] public static extern short GT_Move(int axis, double dist); [DllImport("gts.dll", EntryPoint = "GT_GetPrfPos")] public static extern short GT_GetPrfPos(int axis, out double pos);点位运动发下去之后,绝对不要立刻认为机械手已经到达。运动卡的GT_Move只是把目标位置写入运动规划器,轴是否真正到位需要单独读取轴的到位信号和实际位置残差。这个我在第4章的故障排查里会展开讲,模块前期的架构就要为“硬到位确认”留好接口。
机械手点位数据必须做了一层“场景坐标映射”,不能直接把坐标写死在业务代码里。也就是说,机械手各槽位取放点的坐标、中间过渡点的坐标、安全高度的坐标,全部存放在配方表里,通过槽位编号去索引。换机台调零时,只需要重新示教基点和偏移量,而不需要改一行代码。
2.4 通信层设计:Modbus、TCP和本地数据库的分工
设备侧不只有运动控制卡,还有安全PLC、工艺腔体控制器、MES系统。通信层我做成分层设计,尽量让每种通信方式各司其职:
- Modbus TCP:用来和PLC交换IO信号、报警码、运行状态。C#侧可以用NModbus4库,也可以用自研的ModbusClient封装。轮询周期控制在50ms量级,实时性足够且不会把PLC通信挤爆。
- 自定义TCP:用于和工艺腔体控制器、MES对接。报文一般是“报文头+长度+命令字+数据+CRC校验”的结构,自己写一个解析器并不复杂,关键是要处理好粘包和拆包问题。接收缓冲区做增量解析,把不完整的包暂存起来,等下一个包到齐后再合并处理。
- SQLite/MySQL:保存配方、生产记录和报警日志。现场网络经常断,业务数据先写本地SQLite,再通过同步服务上传MES,比直接依赖网络中断更安全。
写TCP接收逻辑时,所有读取必须在独立线程跑,绝对不能占用UI线程。接收缓冲区的逻辑要严谨,因为网络抖动导致数据错位是整个行业里排查成本最高的一类bug。
2.5 设备抽象层(DAL):架构里最值钱的那一层
上位机最怕把设备代码和业务代码揉在一起。我最开始做这套系统时也犯过这个错,后来加第二台设备、换第三种机械臂型的时候改到怀疑人生。正确的做法是加一层设备抽象层(Device Abstraction Layer),把机械手、真空阀、传感器、PLC都封装成统一接口,上层流程调度只面向接口编程。
这层抽象带来的好处是实打实的:
- 现场换不同品牌的运动控制卡,只需要新增一个实现类,业务流程完全不用改;
- 开发阶段可以用模拟器代替真实硬件,不用长期占着机器;
- 自动化测试可以做流程回归,模拟器里每跑一遍都能提前发现逻辑缺陷。
典型接口设计:
public interface IWaferHandler { bool Home(); bool PickWafer(string slotId); bool PlaceWafer(string slotId); bool GetCurrentState(out HandlerState state); } public class GtsGripperHandler : IWaferHandler { // 走gts.dll的真实设备实现 } public class SimulatedGripperHandler : IWaferHandler { // 模拟器实现,用于联调和测试 }我在这个项目里最庆幸的事情,就是花了整整一天时间把模拟器做得足够真实。后面真机联调发生的所有问题几乎都在模拟环境里预先发现过一轮,几百次联调下来没有发生过一次真正的碎片事故。
3. 核心模块实现:从状态机到人机界面
3.1 状态机设计:把流程抽象成可恢复的事件驱动
整个上位机最核心的模块是流程调度。我采用有限状态机(FSM)来组织设备的生产流程,状态机的核心价值是:每个时刻系统都只处于一个明确的状态,每个状态都有唯一的进入条件和退出条件,出现异常时系统能明确知道“现在在哪一步”“下一步可以做什么”。
简化后的状态表大致如下:
| 状态 | 进入条件 | 执行动作 | 退出条件 |
|---|---|---|---|
| Initializing | 开机 | 硬件自检、各轴回零 | 自检完成且无报警 |
| Idle | 系统就绪 | 等待启动命令 | 收到Start指令 |
| Picking | 收到取片信号 | 机械手取片、真空建立 | 晶圆吸牢并抬升到安全高度 |
| Aligning | 晶圆就位 | 预对准器找正 | 位置补偿完成 |
| Placing | 晶圆就绪 | 放片动作、真空断开 | 晶圆放置在目标槽位 |
| Paused | 异常/暂停 | 停止运动、保持当前真空 | 人工确认后恢复 |
| Error | 不可恢复故障 | 声光报警、禁止后续动作 | 人工清除报警并复位 |
状态机的实现,我用一个ProcessContext上下文对象保存当前状态,用事件触发状态迁移。每个状态都对应一个实现IProcessState接口的类,这样就避免了大量switch-case堆在一起,后续新增流程分支也很方便。
3.2 晶圆位置校正与取放流程的实现细节
晶圆取放的业务逻辑看着很简单——机械手伸到晶圆下方、真空吸附、抬起来、移走到目标位置、断开真空。但真正上设备后你会发现,每个环节都有坑。
第一个坑是FOUP槽位内的晶圆位置并不固定。每片晶圆在卡槽内都会有几毫米的偏移,批次不同偏差更大。如果机械手按照固定坐标去取片,要么碰擦到晶圆边缘,要么吸盘吸不住。我这里的方案是取片前先做一次“找位检测”:用对射光电或者相机读取晶圆边缘位置,把偏移量算出来,补偿进运动指令。视觉方案成本高一些,但可靠性和适应性都好很多。
第二个坑是真空吸附必须做稳定判定。吸盘接触晶圆表面后,真空压力需要掉到负压阈值以下才说明吸牢了。晶圆本身有翘曲、边缘有缺口,或者石墨岛表面碳粉污染,都会导致真空值异常。所以取片成功的判断不能只看“真空阀已打开”,要看传感器返回的压力值是否在300ms窗口内稳定在阈值以下。我在项目里设定的阈值是-30kPa,低于这个值且在300ms内不反弹,才判定吸牢。
public bool PickWafer(string slotId) { // 1. 查找槽位补偿量,并移动到取片位置 var offset = GetSlotOffset(slotId); _motion.MoveTo(_pickPosition.Add(offset)); if (!WaitForInPosition(_pickPosition.Add(offset), 2000)) return false; // 2. 打开真空,并等待真空稳定建立 _vacuumValve.Open(); if (!WaitForVacuumStable(-30, 500)) { _vacuumValve.Close(); // 失败必须先关真空! return false; } // 3. 抬升到安全高度,确认晶圆离开槽位 _motion.MoveTo(_safeHeightPosition); return WaitForInPosition(_safeHeightPosition, 2000); }注意这段代码必须运行在流程线程,不能放在UI线程。UI线程一旦卡住,机械手就停不下来,这在运动控制里是最大的安全隐患。
3.3 石墨岛搬移流程与工艺腔体联动
石墨岛搬移比单片晶圆取放多一个维度——它是批量操作的集合。石墨岛上可能有几十个晶圆槽位,每个槽位内晶圆的状态可能不同,有些是正放、有些是倒放、有些槽位是空的。搬移流程通常是三段:
- 机械手先把石墨岛上的所有晶圆取出,放到过渡区域或晶圆盒内暂存;
- 石墨岛本体被推入工艺腔体,或者从腔体内移出;
- 工艺结束后,机械手把晶圆按对应槽位放回石墨岛。
石墨岛的尺寸误差比晶圆大得多,而且石墨材料容易掉粉,真空吸盘表面一旦沾上石墨粉尘,吸附力会急剧下降。所以我在每次吸附石墨岛前增加了一个“吹气清屑”步骤:先开吹气阀,吹掉接触区的粉尘,再开启高真空吸附。这个动作看着不起眼,现场碎片率有很大一部分就是因为石墨粉尘导致真空泄漏、机械手半路掉片。
石墨岛进出腔体的位置检测也需要单独处理。不同批次的石墨岛在承载台上的前后位置会有明显偏差,我在承载台入口装了一对射传感器,每次进岛都要做正向到位确认。如果传感器没触发,流程立即停止,禁止机械手放片。这个位置偏差说明机械装配有变动,不应该靠软件去凑,做设备的人都知道,机械上的公差不能靠程序去硬解。
3.4 异常恢复路径:失败之后“可以做什么”和“绝对不能做什么”
异常恢复是我在这个项目里花时间最多的部分。很多上位机系统能跑,但一出异常就停在那不动,操作员只能干瞪眼等厂家远程。真正好用的设备,异常恢复路径应该是设计过的。
我的做法是给每个流程步骤定义明确的恢复入口点。比如在取片流程中:
- 如果真空建立超时:先关闭真空阀,再退回安全高度,然后进入待机状态,等待人工确认或重试;
- 如果机械手取片后运动中断:先保持当前真空状态,禁止机械手自由移动,提示操作员进入手动模式缓慢退片;
- 如果放片后真空断开失败:报警并禁止机械手离开当前槽位,防止晶圆被带飞。
每个异常分支,我都写在状态机表里,代码里面对应一个异常处理器。开发验收时,我把这些异常分支做成了一页纸的“异常处理矩阵”,现场调试时出了问题,看一眼就知道系统下一步应该做什么,非常实用。
3.5 人机界面的设计:操作工才是真正的用户
WPF界面不是给研发人员自嗨的,操作工人每天盯着它8小时,界面设计的优先级非常明确。
主操作区永远是最醒目的。开始、暂停、复位这三个大按钮放在固定位置,字号要够大,颜色区分明显,而且绝对不允许被弹窗遮挡。状态显示用颜色和图案双重表达:绿色代表正常、黄色代表警示、红色代表报警。不能只靠颜色,因为现场有色弱或者色盲的操作员,所以还需要组合闪烁和文字提示。
自动模式和手动模式必须严格分离。手动模式下,自动流程按钮全部置灰,防止操作员在机械手运行中误触自动启动。配方参数要做到界面上可改、数据库持久化,不能为了省事硬编码在程序里。
MVVM实现上,核心是INotifyPropertyChanged驱动界面刷新。我封装了一个ObservableObject基类,所有ViewModel都继承它,通过PropertyChanged事件自动更新绑定。监控数据通过定时器从设备层拉到ViewModel,再通过绑定刷新界面,整个代码结构非常干净。
public class MainViewModel : ObservableObject { private string _currentStateText; public string CurrentStateText { get => _currentStateText; set => SetProperty(ref _currentStateText, value); } private bool _isAutoModeEnabled; public bool IsAutoModeEnabled { get => _isAutoModeEnabled; set => SetProperty(ref _isAutoModeEnabled, value); } }界面上的趋势图和数据曲线,我用的是OxyPlot这个库,轻量且社区活跃,非常适合上位机场景。用它画机械手各轴的实时位置曲线,调试时看运动轨迹特别直观。
4. 硬核问题排查实录:哪些坑是你一定会踩的
4.1 WPF界面卡顿与跨线程操作
WPF的UI线程只有一个,如果在UI线程里做耗时操作,比如读取大数据库、同步调用运动控制卡接口,界面就卡死。这个问题在项目初期就出现过:一启动设备监控,整个窗体白屏好几秒。
解决思路是所有耗时操作全部异步化。用async/await和Task.Run把设备操作放到线程池执行,UI线程只负责发指令和收结果。设备事件如果需要更新界面,就用Dispatcher.BeginInvoke切换到UI线程。但如果异步化没有配合并发控制,又会引入新问题:机械手还在执行取片动作,用户已经在界面上点了“复位”,两条流程同时在设备层跑,极易出事故。
所以上位机的流程操作必须加一个“流程锁”,用SemaphoreSlim保证同一时刻只允许一个流程命令进入设备层。其他命令要么等待,要么直接返回忙碌提示。这个锁要放在设备入口处,而不是UI层,才能真正挡住所有入口的误操作。
4.2 通信超时与机械手“假到位”的排查过程
现场最诡异的问题之一:设备跑了几个小时,机械手偶尔会在半路停住,上位机显示运动完成,但轴实际没到位。查了一整天,最后定位到运动控制卡的GT_Move返回成功,但轴因为伺服报警被紧急停止,导致运动指令没有真正执行。
排查思路很典型:
- 初查运动卡轴的报警位,发现是Servo报警;
- 翻控制卡日志,报警原因是超程;
- 但超程感应器并没有实际触发,是接线端子接触不良导致的高频毛刺;
- 重新压接端子后问题消失。
这件事的教训很深刻:上位机发指令不能“发完就不管”,必须每两个动作之间做一次“硬到位确认”。读取轴的驱动状态、到位信号、实际位置残差,三样都满足才认为动作完成。这就是半导体设备稳定性和普通自动化设备最大的差异。
4.3 真空报警偶发性误报的根因分析
气路上的真空传感器偶尔会误报泄漏,压力明明正常,界面却弹出报警。这个问题很难定位,因为它不是每次都能复现。
排查过程分了四步:
- 把传感器采样周期从100ms缩短到20ms,报警依然存在;
- 增加软件滤波,连续5次低于阈值才报警,误报率降低了,但没有根除;
- 测试中发现电磁阀动作瞬间,真空吸盘刚接触晶圆表面时会有一个瞬时压力波动,传感器响应速度跟不上,误判为泄漏;
- 最终方案是动作开始后加300ms的稳定延时,再进入真空检测逻辑,并且用“检测窗口”而不是“单个采样点”来判断真空是否正常。
这个问题提醒我,软件滤波和硬件物理惯性要一起考虑。只看代码是看不出问题的,必须回到设备现场,对照气路动作时序逐段分析。
4.4 快速部署与版本迭代的工程化经验
半导体设备的上位机版本迭代非常频繁,尤其是客户试产阶段,几乎每周都有功能更新和缺陷修复。工程化上我有三点经验想分享:
第一,配置外置。所有的IP地址、串口参数、运动速度、点位数据全部放配置文件或数据库,绝不硬编码。发布新版本时保留配置文件,只替换程序集,现场不用重新配参数。
第二,日志系统越早搭越好。从项目第一天就开始记录:设备事件、操作记录、异常堆栈、关键通信帧数据。现场出问题,第一件事是拉日志。没有日志的设备维护,等于在黑房间里猜。
第三,版本号一定要在界面上显示。客户现场倒了几个版本之后,你根本不知道机器上跑的哪一版代码,所以程序标题栏长期显示版本号、编译时间和代码版本号,强烈建议养成这个习惯。
4.5 其他常见问题速查表
除了上面几个重点案例,我把项目里遇到的其他常见问题也整理成了一张快速排查表,开发时对照着检查,能省掉很多重复排查的时间:
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| WPF界面启动白屏 | 后台线程在UI线程做耗时操作 | 检查构造函数是否有同步IO,改用异步加载 |
| DataGrid单元格内容显示不全 | 列宽未设置自适应或固定过窄 | 设置ColumnWidth="*"或SizeToFit,启用ToolTip展示完整内容 |
| Modbus通信偶发超时 | 轮询周期太密或从站响应慢 | 增大超时时间,将读IO和写IO分线程 |
| U盘安装程序后运行报错 | 缺少.NET运行时环境 | 发布时选“框架依赖”加自包含发布,或打包安装器 |
| 触摸屏上按钮没反应 | 面积太小或未适配触控事件 | 按钮最小尺寸建议不小于40x40像素,关闭点击延迟 |
| 串口乱码 | 波特率/数据位/校验位不一致 | 核对设备手册,检查串口转接线是否为交叉线 |
4.6 分享一个卡了很久的“编译能过、运行崩”的WPF绑定问题
MVVM里如果绑定写错,编译期不会报错,运行期也只是在输出窗口打一条警告。这种问题最坑的地方在于,界面看起来一切正常,但某个按钮就是触发不了命令。
排查了半天,发现是我实现ICommand的时候没有正确实现CanExecute的变更通知。虽然DelegateCommand在很多框架里已经封装好了,但自研实现时很容易漏掉CanExecuteChanged的触发。如果你用Prism框架,它会提供DelegateCommand,支持ObservesProperty自动刷新CanExecute,这个细节特别适合界面比较复杂、按钮状态经常变化的场景。
5. 一些沉淀下来的经验和体会
这个项目做完以后,我对半导体设备上位机开发的理解又深了一层。最有感触的一点是:上位机真正的核心技术不是编程语言,而是对工艺流程的抽象理解和对稳定性的偏执。C#和WPF只是工具,真正决定项目成败的是状态机是否严谨、异常恢复路径是否清晰、通信异常是否可控。
另一个体会是,上位机开发没有太多“银弹”。你可以把架构设计得很优雅,但上线之后还是会被现场各种奇奇怪怪的物理问题教育。机械的公差、线缆的松动、气路的波动,最后都会以软件bug的形式暴露出来。所以做好日志、做好状态复位、做好异常分支,比任何炫技都重要。
如果再做类似的设备项目,我一定会在一开始就引入自动化测试,用模拟器做流程回归,把测试平台搭得更完善。界面设计上也可以往远程诊断和预测性维护方向延伸,让现场人员更高效地处理故障。设备联网采集工艺数据这块也是后续很有价值的扩展方向,对客户优化工艺参数帮助很大。
最后再分享一个容易被忽视的细节:设备和上位机联调阶段,永远先在模拟器环境下把完整流程跑通三遍再上真机。真机上的每一次误操作,轻则报警停机,重则碎一片晶圆,那种成本不是你写代码的进度能换得回来的。我们团队在这个项目里往往为了赶时间想跳过模拟测试,最后都是靠提前把异常分支设计清楚、把模拟器做得足够真实,才换来现场联调的从容。磨刀不误砍柴工,这句话在设备上位机行业里永远适用。