在 TwinCAT 3 的 CASE 状态机中使用 R_TRIG 触发运动命令时,经常遇到一个令人困惑的现象:第一次触发正常,第二次开始就再也触发不了了。重启 PLC 后又能触发一次,然后又失效。
这是 TwinCAT 3 / IEC 61131-3 工程中出现频率最高的逻辑错误之一,根本原因在于对 R_TRIG 的工作原理和实例化位置理解有误。
R_TRIG(Rising Trigger)是一个带内部状态的功能块,它在每次调用时比较当前输入与上次输入,只有从 FALSE→TRUE 的上升沿瞬间输出一个周期的 TRUE 脉冲。关键点:它必须被每个周期都调用才能正确检测边沿。
// R_TRIG 基本用法 VAR fbRtrig : R_TRIG; bStart : BOOL; bPulse : BOOL; END_VAR fbRtrig(CLK := bStart); bPulse := fbRtrig.Q; // 仅在 bStart 上升沿的那个周期为 TRUE
最常见的错误是把 R_TRIG 调用放在 CASE 的某个分支内部。这意味着只有状态机走到那个分支时 R_TRIG 才被调用,其他时候它的内部状态冻结——下次再进入该分支时,CLK 可能已经是 TRUE,R_TRIG 认为"没有变化",不输出脉冲。
// ❌ 错误:R_TRIG 放在 CASE 内部 CASE nState OF 0: // 等待启动 fbRtrig(CLK := bStartCmd); // 只在 state=0 时被调用! IF fbRtrig.Q THEN nState := 10; END_IF 10: // 执行动作... IF bDone THEN nState := 0; END_IF END_CASE // 问题:state=10 期间用户按了 bStartCmd 再松开 // 回到 state=0 时 bStartCmd=FALSE,R_TRIG 看到的是 TRUE→FALSE→? // 下次 bStartCmd 变 TRUE,但 R_TRIG 上次记住的也是 TRUE → 不触发
// ✅ 正确:R_TRIG 在 CASE 外部,每个周期都被调用 fbRtrig(CLK := bStartCmd); // 放在 CASE 之前,每周期执行 CASE nState OF 0: IF fbRtrig.Q THEN // 只读 .Q 的值,不调用 FB nState := 10; END_IF 10: IF bDone THEN nState := 0; END_IF END_CASE
💡 原则:R_TRIG / F_TRIG 必须在主程序体的顶层调用,不能放在 IF / CASE / FOR 等条件分支内部。只有每周期都调用,内部状态才能正确跟踪信号变化。
在三槽液体混合 SCADA 项目中,操作员点击"启动混合"按钮后,WPF 通过 ADS 写入 bStartMix := TRUE,PLC 侧用 R_TRIG 检测上升沿启动状态机。初期版本把 R_TRIG 放在 Idle 状态分支内,导致:第一次点击正常启动,混合完成回到 Idle 后再点击无反应。
修复方法就是把 R_TRIG 调用移到 CASE 语句之前。修改后问题彻底消失,后续长期运行未再复现。
完整工程源码含正确的 R_TRIG 用法示例,已上传至 github.com/tc3-engineer。