1. 报警闪一下就消失这件事到底卡在哪儿做过产线设备的人大概率都遇到过这种场景HMI 上一条报警记录刚跳出来操作工还没来得及看清是哪个工位报的画面就恢复正常了。等设备停下来去查历史报警记录里干干净净好像什么都没发生过。操作工说“刚才明明闪了一下”工程师翻遍日志找不到证据最后只能归到“可能是干扰”或者“你看错了”。这个问题的根子往往不在 HMI也不在网络而在 PLC 程序里对报警信号的处理方式。尤其是用三菱 iQ-R 系列做产线控制的场合如果报警逻辑只是简单地把故障位直接映射到 HMI 显示那么一个持续几十毫秒的瞬时故障在 HMI 的刷新周期面前就是一闪而过。HMI 的轮询周期通常在 100ms 到 500ms 之间而一个限位开关抖动、一个热继电器瞬动、一次通讯丢包造成的故障位翻转可能只有 20ms 到 50ms。信号来了又走HMI 那一帧刚好没扫到这条报警就等于没发生过。标题里说的“历史欠账”指的就是这个——故障确实发生过但系统没有留下任何痕迹等到需要追溯的时候账是空的。解决思路也很直接在 PLC 侧用 FB功能块把报警信号锁存住让任何一个瞬时故障都被捕获并保持直到操作工确认之后才复位。这样 HMI 看到的就是一个稳定的报警状态而不是一闪而过的脉冲。这篇文章面向的是有 iQ-R 基础、写过梯形图但还没系统用过 FB 和 ST 的工程师也适合正在被“报警抓不住”这个问题困扰的现场调试人员。我会把锁存逻辑的设计思路、FB 的封装方法、ST 语言的具体实现、R_TRIG 的使用要点以及实际调试中踩过的坑完整地拆一遍。代码可以直接抄参数可以照着改但更重要的是理解每一步为什么这么做。2. 为什么必须用 FB 做锁存而不是在主程序里写一堆触点2.1 直接映射报警的三个致命问题很多现场程序是这么写的故障输入 X0 直接驱动 HMI 上的报警位 M100梯形图上就是一根线。这种写法在单点、慢速故障上勉强能用但放到真实产线上会暴露三个问题。第一个问题是脉冲丢失。PLC 的扫描周期通常在 1ms 到 10ms 量级X0 如果只接通一个扫描周期就断开M100 也只会保持一个扫描周期。HMI 根本来不及读到这个状态。有人会说“那我在 HMI 里做上升沿检测”但 HMI 的脚本执行周期比 PLC 扫描周期慢几个数量级同样抓不住。第二个问题是无法追溯。故障消失之后M100 归零历史报警记录里什么都没有。夜班发生的偶发故障白班来查的时候完全没有线索。产线停机的真正原因被掩盖同样的故障会反复出现。第三个问题是确认机制缺失。操作工看到报警之后故障可能已经自己恢复了报警也跟着消失。操作工没有机会确认“我看到了这条报警”系统也无法区分“故障已恢复”和“操作工已确认”这两个完全不同的状态。2.2 FB 锁存解决的到底是什么用 FB 做锁存核心是把报警的生命周期拆成三个阶段触发、保持、复位。触发阶段捕获任何形式的故障信号不管是持续高电平还是瞬时脉冲保持阶段把报警状态锁住HMI 始终能读到复位阶段由操作工手动确认或者由系统在特定条件下自动清除。这三个阶段分开之后报警就不再是一个瞬态信号而是一个有状态的对象。每个报警点都有自己的“未触发 / 已触发未确认 / 已确认”状态HMI 可以据此做不同的显示历史记录也可以完整保留。用 FB 而不是在主程序里散写还有一个工程上的理由复用。一条产线可能有几十上百个报警点如果每个点都写一遍锁存逻辑程序会变得极其臃肿改一个逻辑要改几十处。把锁存逻辑封装成一个 FB每个报警点只需要调用一次传入不同的输入输出变量就行。改逻辑只改 FB 内部所有调用点自动生效。2.3 为什么选 ST 而不是梯形图iQ-R 支持梯形图、ST、FBD 等多种编程语言。做锁存 FB我更倾向于用 ST。原因有三个。第一逻辑表达更紧凑。锁存逻辑涉及条件判断、状态保持、边沿检测用 ST 写出来就是几行 IF 语句用梯形图要画一堆自保持回路和置位复位指令可读性差很多。第二参数化更方便。FB 里需要根据输入参数决定行为比如是否自动复位、锁存时间多长。ST 里直接写 IF 判断就行梯形图里做参数化逻辑非常别扭。第三调试更直观。ST 代码可以在线监控变量值逻辑分支一目了然。梯形图的能流在复杂逻辑下很难追踪。当然这不是说梯形图不能用。如果团队维护习惯是梯形图用梯形图封装 FB 也完全可以只是代码量会大一些。下面的实现我以 ST 为主关键位置会说明梯形图对应的写法。3. 锁存 FB 的核心设计从信号到状态的完整链路3.1 报警信号的三种形态与对应处理在动手写代码之前先要把报警信号分类。不同形态的信号锁存策略不一样。信号形态典型场景锁存策略复位方式持续高电平电机过载、温度超限直接锁存故障消失后仍保持手动确认瞬时脉冲限位抖动、通讯瞬断边沿检测后锁存手动确认间歇性闪烁接触不良、干扰延时确认后锁存手动确认或自动持续高电平最简单故障位为 ON 就锁存。瞬时脉冲需要用 R_TRIG 做上升沿检测捕获到上升沿就置位锁存。间歇性闪烁最麻烦需要加一个确认延时信号持续超过设定时间才认为是真故障避免干扰造成的误报。一个设计良好的锁存 FB应该能通过参数选择处理哪种形态而不是为每种形态写一个 FB。3.2 R_TRIG 在锁存逻辑里的角色R_TRIG 是 iQ-R 里的上升沿检测功能块输入信号从 OFF 变 ON 的那个扫描周期输出为 ON 一个周期。在锁存逻辑里它的作用是把任意宽度的脉冲转换成一次性的置位信号。假设故障信号 X0 只接通了 30msPLC 扫描周期是 5ms那么 X0 会连续 6 个扫描周期为 ON。如果直接把 X0 接到锁存 FB 的置位端锁存会在这 6 个周期里反复被置位虽然结果一样但逻辑上不够干净。用 R_TRIG 之后只有第一个扫描周期产生置位脉冲后续周期不再重复触发。更重要的是R_TRIG 保证了即使信号在 FB 被调用之前就消失了只要它曾经出现过就会被捕获。这是锁存的核心价值——不依赖 HMI 的刷新不依赖操作工的观察PLC 自己把账记下来。3.3 锁存、确认、复位三个状态的分离很多现场程序把“故障消失”和“报警清除”当成一回事这是历史欠账的另一个来源。故障消失只说明当前条件恢复正常不代表这条报警已经被处理过。如果故障消失就自动清除报警操作工可能根本没看到下次同样的问题还会发生。正确的做法是把状态拆开触发状态故障信号出现过报警被锁存HMI 显示红色。确认状态操作工按了确认按钮报警从红色变成黄色或灰色但记录保留。复位状态确认之后且故障条件已消失报警才被清除可以接受下一次触发。这三个状态用两个位就能表达一个锁存位一个确认位。锁存位置位表示报警激活确认位置位表示操作工已知晓。复位条件是“确认位为 ON 且故障信号为 OFF”。4. 手把手写一个可复用的报警锁存 FB4.1 FB 的接口定义先定义 FB 的输入输出变量。这是整个设计的基础接口定好了内部逻辑就是填空。FUNCTION_BLOCK FB_AlarmLatch VAR_INPUT i_xFault : BOOL; // 故障信号输入 i_xAck : BOOL; // 确认按钮 i_xReset : BOOL; // 复位信号可选 i_tConfirmDly : TIME : T#500MS; // 确认延时 i_xAutoReset : BOOL : FALSE; // 是否自动复位 END_VAR VAR_OUTPUT q_xAlarmActive : BOOL; // 报警激活HMI显示用 q_xAlarmAcked : BOOL; // 报警已确认 q_xNewAlarm : BOOL; // 新报警脉冲用于记录 END_VAR VAR fbTrigFault : R_TRIG; // 故障上升沿 fbTrigAck : R_TRIG; // 确认上升沿 tonConfirm : TON; // 确认延时 xLatched : BOOL; // 锁存位 xAcked : BOOL; // 确认位 END_VAR接口里几个关键点说明一下。i_tConfirmDly是确认延时用来过滤间歇性闪烁默认 500ms现场可以根据信号质量调整。i_xAutoReset决定故障消失后是否自动清除报警默认 FALSE也就是必须手动确认。q_xNewAlarm是一个脉冲输出报警第一次触发时为 ON 一个周期用来驱动历史记录或者报警弹窗。4.2 核心逻辑的 ST 实现内部逻辑分四步边沿检测、延时确认、锁存置位、复位处理。// 第一步故障信号上升沿检测 fbTrigFault(CLK : i_xFault); // 第二步确认延时过滤间歇性信号 tonConfirm(IN : i_xFault, PT : i_tConfirmDly); // 第三步锁存置位 // 条件故障上升沿 或 故障持续超过确认延时 IF fbTrigFault.Q OR tonConfirm.Q THEN xLatched : TRUE; xAcked : FALSE; // 新报警触发时清除确认状态 END_IF; // 第四步确认处理 fbTrigAck(CLK : i_xAck); IF fbTrigAck.Q AND xLatched THEN xAcked : TRUE; END_IF; // 第五步复位处理 IF i_xReset THEN xLatched : FALSE; xAcked : FALSE; ELSIF i_xAutoReset AND xAcked AND NOT i_xFault THEN xLatched : FALSE; xAcked : FALSE; END_IF; // 第六步输出赋值 q_xAlarmActive : xLatched; q_xAlarmAcked : xAcked; q_xNewAlarm : fbTrigFault.Q;这段代码的逻辑很直白。故障上升沿或者持续超过延时就置位锁存并清除确认状态。确认按钮按下且报警处于锁存状态就置位确认。复位信号或者自动复位条件满足就清除锁存和确认。有一个细节需要注意xAcked : FALSE在锁存置位时执行意味着如果同一个报警点在已确认但未复位的情况下再次触发确认状态会被清除HMI 上会重新变成未确认的红色。这是符合现场逻辑的——同一个故障再次发生操作工需要重新确认。4.3 梯形图等效写法如果团队坚持用梯形图核心逻辑可以用置位复位指令实现。故障上升沿或者延时到达SET 锁存位确认按钮上升沿且锁存位为 ONSET 确认位复位信号RST 锁存位和确认位。延时用 TON 指令边沿检测用 LDP 指令。逻辑等价只是代码量大概是 ST 的三倍。4.4 在 iQ-R 里注册和调用 FBFB 写完之后需要在 iQ-R 工程里注册。在 GX Works3 里新建一个 FB 文件把上面的代码贴进去编译通过之后就可以在主程序里调用了。调用的时候每个报警点声明一个 FB 实例VAR fbAlarm_Estop : FB_AlarmLatch; fbAlarm_Overload : FB_AlarmLatch; fbAlarm_AirPress : FB_AlarmLatch; END_VAR fbAlarm_Estop(i_xFault : X0, i_xAck : M100, i_tConfirmDly : T#200MS, q_xAlarmActive M200, q_xNewAlarm M201); fbAlarm_Overload(i_xFault : X1, i_xAck : M100, i_tConfirmDly : T#1S, q_xAlarmActive M210, q_xNewAlarm M211);每个实例独立维护自己的锁存状态互不干扰。确认按钮 M100 可以共用按一次确认所有当前激活的报警。如果需要对每个报警单独确认就给每个实例传不同的确认位。5. 实操中必须注意的参数与细节5.1 确认延时到底设多少i_tConfirmDly这个参数没有标准答案取决于现场信号的质量。设得太短干扰造成的误报会被锁存设得太长真故障的响应会变慢。我的经验值是对于机械限位、接近开关这类信号设 100ms 到 200ms 就够了能过滤掉大部分抖动。对于模拟量转换出来的故障位比如温度超限设 500ms 到 1s避免传感器噪声造成误报。对于通讯状态位设 2s 到 3s因为网络瞬断很常见太敏感会导致大量无意义的报警。有一个简单的调试方法先把延时设为零运行一段时间观察哪些报警是频繁闪烁的针对这些点单独加延时。不要一上来就给所有点设一个很大的延时那样会掩盖真实的快速故障。5.2 R_TRIG 实例不能共用这是新手最容易踩的坑。R_TRIG 是一个功能块每个实例有自己的内部状态。如果把同一个 R_TRIG 实例用在两个不同的信号上边沿检测会完全错乱。// 错误写法两个信号共用一个 R_TRIG 实例 fbTrig(CLK : X0); IF fbTrig.Q THEN ... END_IF; fbTrig(CLK : X1); // 这里会覆盖上一个的状态 IF fbTrig.Q THEN ... END_IF; // 正确写法每个信号独立实例 fbTrigX0(CLK : X0); fbTrigX1(CLK : X1);在 FB 内部每个调用实例会自动拥有独立的 R_TRIG 实例所以把 R_TRIG 放在 FB 的 VAR 区是安全的。但如果直接在全局程序里用 R_TRIG一定要给每个信号声明独立的实例。5.3 锁存位的掉电保持产线断电再上电锁存位如果丢失历史欠账又回来了。对于重要的报警锁存位需要设置掉电保持属性。在 iQ-R 里把对应的 M 位或者 D 寄存器设置为锁存类型断电后值会保留。但要注意掉电保持的锁存位在上电后需要有一个明确的处理策略。是保持报警状态等操作工确认还是上电自动清除这取决于设备的安全要求。一般来说安全相关的报警上电后应该保持直到确认设备状态正常普通工艺报警可以上电自动清除。5.4 HMI 侧的配合PLC 侧锁存做好了HMI 侧也要配合。报警显示不要直接读故障输入位要读 FB 的q_xAlarmActive输出。报警确认按钮要接到 FB 的i_xAck输入而不是直接清除 HMI 上的报警显示。历史报警记录用q_xNewAlarm脉冲触发这样每条报警只记录一次不会因为锁存位持续为 ON 而反复记录。记录内容至少包含报警编号、触发时间、确认时间、复位时间。6. 常见问题与排查技巧实录6.1 报警锁存了但无法复位最常见的原因是复位条件不满足。检查i_xAck是否已经置位如果确认位没有置位复位逻辑不会执行。另一个原因是故障信号一直为 ON自动复位模式下NOT i_xFault条件不满足。这时候需要先排除故障再复位。还有一种情况是多个 FB 实例共用了同一个确认位但某个实例的锁存位没有被正确置位导致确认逻辑不执行。排查方法是逐个实例监控xLatched和xAcked的状态。6.2 报警频繁误触发先看i_tConfirmDly是不是设得太短。把延时加大观察误报是否减少。如果加大延时后仍然误报说明不是抖动问题而是信号本身有干扰。检查传感器接线、屏蔽层接地、电源质量。对于模拟量信号检查滤波参数。还有一种可能是 R_TRIG 实例共用导致的逻辑错乱。检查每个信号是否有独立的边沿检测实例。6.3 历史记录里同一条报警重复出现这是因为用了锁存位而不是新报警脉冲去触发记录。锁存位在报警持续期间一直为 ON如果记录触发逻辑是电平触发就会每个扫描周期记录一次。改成用q_xNewAlarm的上升沿触发每条报警只记录一次。6.4 上电后报警状态异常检查锁存位的掉电保持设置。如果锁存位设置了保持上电后会恢复断电前的状态这是预期行为。如果没有设置保持上电后锁存位归零但故障信号可能仍然存在下一个扫描周期会重新触发锁存。需要根据设备的安全策略决定哪种行为是正确的。6.5 常见问题速查表现象可能原因排查方法解决措施报警闪一下就消失未使用锁存直接映射监控故障位和HMI位改用FB锁存锁存后无法复位确认位未置位或故障未消失监控xAcked和i_xFault先确认再复位排除故障报警频繁误报确认延时太短或信号干扰加大延时观察调整延时检查接线历史记录重复用电平触发记录检查记录触发逻辑改用新报警脉冲上电后报警异常掉电保持设置不当检查锁存位保持属性按安全策略调整多个报警互相影响R_TRIG实例共用检查边沿检测实例每个信号独立实例7. 几个让锁存更稳的进阶技巧7.1 报警分级与不同锁存策略不是所有报警都需要同样的处理。可以把报警分成三级一级是安全报警必须手动确认且掉电保持二级是工艺报警手动确认但不需要掉电保持三级是提示信息自动复位即可。在 FB 里加一个i_nLevel输入参数根据级别决定是否自动复位、是否掉电保持。这样一套 FB 就能覆盖所有报警类型不用为每级写不同的逻辑。7.2 报警触发时间的记录q_xNewAlarm脉冲触发时把当前系统时间写入一个寄存器就是报警的触发时间。iQ-R 有内置的时钟功能用DATE_AND_TIME类型变量读取当前时间。确认和复位时同样记录时间这样历史记录里就有完整的时间线。7.3 批量报警的确认策略一条产线同时触发多个报警时操作工按一次确认是确认所有报警还是只确认当前显示的那一条这取决于 HMI 的设计。如果确认按钮是全局的所有 FB 实例共用i_xAck一次确认全部。如果需要对每条报警单独确认HMI 上每条报警旁边放一个确认按钮分别接到对应实例的i_xAck。我个人的经验是对于安全报警逐条确认更稳妥避免操作工习惯性按确认而忽略重要信息。对于工艺报警全局确认效率更高。7.4 FB 的版本管理与复用FB 写好之后建议导出成库文件在不同项目之间复用。GX Works3 支持 FB 的导出和导入导出时把接口定义和内部逻辑一起打包。新项目直接导入改一下调用参数就能用。版本管理方面每次修改 FB 逻辑都要记录版本号和修改内容。现场调试时如果发现 FB 有问题能快速定位是哪个版本引入的。8. 写在最后的一点个人体会报警锁存这件事技术上并不复杂一个 FB 几十行代码就能解决。但它在现场的价值远超代码本身。我见过太多产线因为报警抓不住同一个故障反复停机每次排查都从零开始工程师疲于奔命。加了锁存之后历史记录完整了故障追溯有据可查很多偶发问题第一次发生就能定位到根因。FB 封装带来的复用性也是实打实的收益。一条产线几百个报警点如果每个都手写锁存逻辑程序维护会变成噩梦。用 FB 之后新增报警点就是加一行调用改逻辑只改一处。这个投入产出比做过大项目的人都懂。最后分享一个小技巧FB 调试阶段可以加一个i_xSimulate输入强制置位锁存位用来测试 HMI 的报警显示和确认流程不用真的去触发故障信号。这个功能在 HMI 画面调试时特别省事不用等现场信号直接在 PLC 里模拟就行。
阅读完成 · 觉得有帮助?