在结构化文本(ST)编程中,上升沿检测是电气自动化控制系统中最基础、最频繁使用的逻辑功能之一。它用于捕捉信号由“0”变为“1”的瞬时变化,常见于启动按钮触发、脉冲计数、状态切换、故障锁定等场景。ST 语言本身不提供内置的 RISING_EDGE 运算符(如某些厂商的 LD/FBD 中有),但通过标准 IEC 61131-3 功能块 R_TRIG(Retriggerable Rising Edge Trigger),可实现稳定、可复位、抗抖动、符合工业规范的上升沿识别。
以下内容全程基于 ST 语言语法,以 Siemens TIA Portal(V18+)、Codesys、3S CoDeSys、B&R Automation Studio 等主流平台通用规则编写,不依赖特定品牌扩展指令,所有代码可直接复制粘贴至 ST 编辑器中使用。
一、理解 R_TRIG 的行为逻辑
R_TRIG 是一个标准功能块(FB),其核心作用是:当 .CLK 输入信号从 FALSE 跳变为 TRUE 时,将 .Q 输出置为 TRUE,且该输出仅维持一个扫描周期(即下一个周期自动清零),除非 .CLK 在该周期内再次为 TRUE 并满足重触发条件。
关键特性说明:
.CLK:时钟输入端。必须为布尔量(BOOL)。仅当.CLK本次为 TRUE 且上一次为 FALSE 时,.Q才在本周期置位。.Q:上升沿输出端。类型为 BOOL。每个扫描周期开始时自动复位;仅在检测到有效上升沿的当周期为 TRUE。.CLK的历史状态由R_TRIG实例内部隐式保存(无需手动声明静态变量)。- 该功能块是“可重触发”(Retriggerable)的:若
.CLK在.Q = TRUE的同一周期内持续为 TRUE 或多次变化,.Q仍只在首个上升沿响应,不会重复置位——这保证了单次事件的唯一性。
✅ 正确理解:
.Q不是锁存器,不是自保持,也不是延时输出。它是纯粹的“边沿快照”,生命周期严格限定在检测发生的那个 PLC 扫描周期内。
二、在 ST 中实例化并调用 R_TRIG 的完整步骤
1. 声明 R_TRIG 实例变量
在程序组织单元(POU)的 VAR 区域(如 FB、FC 或 PRG 的变量声明段)中,声明一个具名的 R_TRIG 功能块实例。不能直接写 R_TRIG(CLK := x);(这是非法的无名调用),必须先命名实例。
VAR
myRisingEdge : R_TRIG; // 实例名称为 myRisingEdge,类型为 R_TRIG 标准功能块
inputSignal : BOOL := FALSE; // 示例输入信号(如 I0.0 对应的变量)
edgeDetected : BOOL; // 接收 .Q 输出的布尔变量
END_VAR
⚠️ 注意:R_TRIG 是功能块(FB),不是函数(FC),因此必须声明为变量,不能像 TON() 那样作为表达式直接调用。
2. 在 ST 主体中调用实例并连接引脚
在 BEGIN ... END; 之间,使用功能块调用语法为 .CLK 赋值,并读取 .Q:
myRisingEdge(CLK := inputSignal);
edgeDetected := myRisingEdge.Q;
✅ 正确写法解析:
myRisingEdge(...)是对已声明实例的调用;(CLK := inputSignal)是引脚赋值,语法为参数名 := 值;myRisingEdge.Q是访问该实例的输出成员,.是结构体成员访问运算符;- 每次调用都会更新内部状态,无需额外初始化或复位指令。
❌ 常见错误写法(全部无效):
R_TRIG(CLK := inputSignal).Q→ 无名调用,ST 不支持;R_TRIG(inputSignal)→ 缺少参数名,语法错误;myRisingEdge.Q := inputSignal→ 方向反了,.Q是只读输出;- 忘记调用
myRisingEdge(...)→.Q始终为 FALSE,因未触发状态更新。
3. 完整可运行示例(带注释)
以下是一个独立、可编译的 ST 程序片段(适用于 PRG 或 FB 的 BODY):
PROGRAM Main
VAR
// 输入信号(模拟按钮、传感器等)
btnStart : BOOL := FALSE; // 假设来自输入映射,如 %I0.0
// R_TRIG 实例
trigStart : R_TRIG;
// 上升沿标志(可用于触发后续动作)
startPulse : BOOL;
// 其他业务变量
motorRunning : BOOL := FALSE;
END_VAR
// 主逻辑
trigStart(CLK := btnStart);
startPulse := trigStart.Q;
// 利用上升沿启动电机(单次触发)
IF startPulse THEN
motorRunning := TRUE;
END_IF;
// 停止逻辑(示例:另一按钮下降沿)
// (此处略,仅说明上升沿用途)
执行过程逐周期说明(假设扫描周期为 2ms):
| 周期 | btnStart |
trigStart.CLK 当前值 |
上周期 CLK 值 |
是否上升沿? | trigStart.Q |
startPulse |
|---|---|---|---|---|---|---|
| 1 | FALSE | FALSE | —(初始) | 否 | FALSE | FALSE |
| 2 | TRUE | TRUE | FALSE | ✅ 是 | TRUE | TRUE |
| 3 | TRUE | TRUE | TRUE | 否 | FALSE | FALSE |
| 4 | FALSE | FALSE | TRUE | 否 | FALSE | FALSE |
可见:startPulse 仅在周期 2 为 TRUE,精准捕获跳变瞬间。
三、进阶用法与工程实践要点
▪ 多信号共用同一 R_TRIG 实例?不可行!
每个 R_TRIG 实例维护独立的内部历史状态。若尝试复用同一实例检测多个信号:
trigStart(CLK := btnStart); // ❌ 覆盖了之前状态
trigStart(CLK := sensorA); // ❌ 覆盖了 btnStart 的历史
结果:sensorA 的上升沿判断将错误依赖 btnStart 的上周期值,逻辑崩溃。
✅ 正确做法:为每个需检测的信号声明独立实例:
VAR
trigBtnStart : R_TRIG;
trigSensorA : R_TRIG;
trigReset : R_TRIG;
END_VAR
trigBtnStart(CLK := btnStart);
trigSensorA(CLK := sensorA);
trigReset(CLK := resetBtn);
startPulse := trigBtnStart.Q;
alarmRising := trigSensorA.Q;
resetActive := trigReset.Q;
▪ 如何实现“电平保持型”上升沿(即 Q 锁存直到手动复位)?
R_TRIG 本身不锁存,但可轻松组合 SR 触发器实现:
VAR
trigStart : R_TRIG;
startLatch : SR; // 标准置位复位触发器(SR 功能块)
btnStart, btnStop : BOOL;
startHeld : BOOL;
END_VAR
trigStart(CLK := btnStart);
startLatch(S1 := trigStart.Q, R := btnStop); // S1 置位,R 复位
startHeld := startLatch.Q0;
此时 startHeld 在首次按下 btnStart 后变为 TRUE,并保持,直至 btnStop 为 TRUE。
▪ 抗抖动处理(硬件/软件协同)
机械按钮存在毫秒级抖动(bounce),可能被误判为多次上升沿。R_TRIG 本身不滤波,需前置消抖:
- ✅ 推荐:在信号采集层(如 PLC 输入模块配置)启用硬件滤波(如 10 ms 滤波时间);
- ✅ 软件替代:用
TON定时器做软件消抖(延迟确认):
VAR
btnRaw : BOOL;
btnDebounced : BOOL;
debouncer : TON;
END_VAR
debouncer(IN := btnRaw, PT := T#20ms);
btnDebounced := debouncer.Q; // 仅当 btnRaw 持续 20ms 为 TRUE 后才输出 TRUE
// 再送入 R_TRIG
trigStart(CLK := btnDebounced);
四、与其他上升沿实现方式对比(表格)
以下对比 ST 中常见上升沿方案的可靠性与适用性:
| 方法 | 语法示例 | 是否标准 IEC 61131-3 | 抗扫描周期依赖 | 可复位性 | 推荐指数 | 说明 |
|---|---|---|---|---|---|---|
R_TRIG 实例 |
trig(CLK := x); y := trig.Q; |
✅ 全平台兼容 | ✅ 自动跨周期记忆 | ✅ 单周期自动复位 | ⭐⭐⭐⭐⭐ | 工业首选,语义清晰,无歧义 |
XOR + D 寄存器 |
y := x XOR x_prev; x_prev := x; |
✅ 通用 | ✅ 需手动保存上周期值 | ✅ 可控 | ⭐⭐⭐☆ | 需额外变量,易出错;x_prev 必须为 VAR RETAIN 或 VAR_GLOBAL |
NOT + AND 组合 |
y := (NOT x_prev) AND x; x_prev := x; |
✅ 通用 | ✅ 同上 | ✅ 可控 | ⭐⭐⭐ | 逻辑等效,但不如 R_TRIG 直观 |
厂商专用指令(如 Siemens P_TRIG) |
trig(CLK := x); y := trig.Q; |
❌ 仅 Siemens | ✅ | ✅ | ⭐⭐⭐⭐ | 行为同 R_TRIG,但非跨平台,不推荐用于可移植项目 |
注:
P_TRIG与R_TRIG在 Siemens 中几乎等价(P_TRIG不可重触发,R_TRIG可重触发),但在其他平台无定义,故统一使用R_TRIG保障兼容性。
五、典型错误排查清单
遇到 R_TRIG 不工作?按顺序检查:
- ✅ 实例是否已声明(
VAR区)? - ✅ 是否在每次循环中都调用了该实例?(漏调用 →
.Q永远 FALSE) - ✅
.CLK输入是否为BOOL类型?若传入INT或指针,编译报错或行为未定义; - ✅
.Q是否被后续逻辑覆盖或屏蔽?例如edgeDetected := FALSE;写在调用之后; - ✅ 输入信号是否真实变化?用监控表观察
btnStart波形,确认有明确 0→1 跳变; - ✅ 是否在函数(FC)中误用?
R_TRIG是 FB,只能在 FB 或 PRG 中声明,不能在 FC 中声明(FC 无实例存储空间);
六、完整工程模板(可直接复用)
// 文件:RisingEdgeHandler.st
// 功能:封装标准化上升沿检测,支持多通道、带使能控制
FUNCTION_BLOCK RisingEdgeDetector
VAR_INPUT
Enable : BOOL := TRUE; // 全局使能,禁用时忽略输入
CLK : BOOL; // 待检测信号
END_VAR
VAR_OUTPUT
Q : BOOL; // 上升沿脉冲(1 扫描周期)
END_VAR
VAR
internalTrig : R_TRIG;
filteredCLK : BOOL;
END_VAR
// 使能过滤
filteredCLK := CLK AND Enable;
// 执行上升沿检测
internalTrig(CLK := filteredCLK);
Q := internalTrig.Q;
调用方式:
// 在主程序中
VAR
detectorStart : RisingEdgeDetector;
btnStart : BOOL;
startPulse : BOOL;
END_VAR
detectorStart(Enable := TRUE, CLK := btnStart);
startPulse := detectorStart.Q;
此封装解耦了检测逻辑与业务逻辑,支持批量实例化与集中管理。
R_TRIG 的本质价值,在于将“边沿”这一时序概念,封装为可预测、可复用、可测试的确定性组件。它不隐藏状态,不引入隐式行为,完全符合结构化编程的透明性原则。只要严格遵循“声明 → 调用 → 读取”的三步链路,即可在任意符合 IEC 61131-3 的 PLC 平台上,稳定、高效、跨平台地实现上升沿检测。

暂无评论,快来抢沙发吧!