SCL 边沿检测:上升沿与下降沿
SCL 上升沿/下降沿怎么写:用一个 Bool 保存上一周期状态,代码只有 3 行;含完整 FB、接口表和云仿真场景。
入门
S7-1200
S7-1500
边沿检测
Bool
FB
状态位
1 块 / 29 行
可仿真
TIA 已验证
一句话:SCL 边沿检测用 Rising := Signal AND NOT Prev; Falling := NOT Signal AND Prev; Prev := Signal。Prev 必须是静态变量,而且要在判断之后更新。
导入到我的 TIA 项目
通过 TiaLink 把本示例的 1 个 SCL 块作为“外部源”导入你当前打开的 TIA Portal 项目:自动生成块、编译,并清理外部源。导入后不会自动保存;FB 示例导入后需要你在 OB 里调用。
边沿检测是 PLC 程序里最基础、也最容易被写错的一段逻辑:它要回答“这个信号是不是刚刚从 0 变成 1(或从 1 变成 0)”,而不是“它现在是不是 1”。
Signal 上升沿 → Rising 单周期脉冲;Signal 下降沿 → Falling 单周期脉冲。
概述
这个示例把上一扫描周期的 Signal 保存在静态变量 Prev 里,本周期先比较、再更新:
Rising = Signal AND NOT Prev:本周期为 1、上周期为 0,就是上升沿。
Falling = NOT Signal AND Prev:本周期为 0、上周期为 1,就是下降沿。
两个输出都只在一个扫描周期内为 1,所以可以直接用来做“按一次加一”“启动一次流程”“记录一次报警”这类一次性动作。
你将学到
- 为什么边沿检测必须有一个“上一周期状态”变量;
- 为什么必须先判断边沿,再更新历史值;
- 一个 FB 如何同时输出上升沿和下降沿;
- 如何用云仿真验证“边沿只出现一个扫描周期”。
接口与变量
下表由构建器从 SCL 源码的接口声明自动生成;页面上展示的代码就是导入 TIA 的同一份源码。
FB_EdgeDetection FB
| 区块 | 名称 | 类型 | 说明 |
|---|
| 输入 | Signal | Bool | |
| 输出 | Rising | Bool | |
| 输出 | Falling | Bool | |
| 静态 | Prev | Bool | |
程序结构
导入顺序以下表为准:被依赖的块/类型先导入;本页已按依赖排好。
- FB_EdgeDetection FB 上升沿/下降沿检测 · 29 行 · 2 段
FB_EdgeDetection FB
上升沿/下降沿检测 · 29 行 · 源文件 blocks/FB_EdgeDetection.scl
查看 / 复制完整块源码
FB_EdgeDetection.scl
1FUNCTION_BLOCK "FB_EdgeDetection"
2{ S7_Optimized_Access := 'TRUE' }
3VERSION : 0.1
4 VAR_INPUT
5 Signal : Bool;
6 END_VAR
7
8 VAR_OUTPUT
9 Rising : Bool;
10 Falling : Bool;
11 END_VAR
12
13 VAR
14 Prev : Bool;
15 END_VAR
16
17BEGIN
18 REGION 边沿判断
19
20 #Rising := #Signal AND NOT #Prev;
21 #Falling := NOT #Signal AND #Prev;
22 END_REGION
23
24 REGION 保存历史状态
25
26 #Prev := #Signal;
27 END_REGION
28END_FUNCTION_BLOCK
29
FB_EdgeDetection / 边沿判断 — 当前值与历史值比较
FB_EdgeDetection.scl · 边沿判断
18 REGION 边沿判断
19
20 #Rising := #Signal AND NOT #Prev;
21 #Falling := NOT #Signal AND #Prev;
22 END_REGION
目的:根据当前 Signal 和上一周期的 Prev 判断上升沿/下降沿。
条件 → 动作:Signal=1 且 Prev=0 → Rising=1;Signal=0 且 Prev=1 → Falling=1;其他情况两个输出都为 0。
为什么:边沿不是信号电平,而是电平的变化。只比较当前值无法知道“刚刚变化”。
注意:两个输出都是单扫描周期脉冲;如果后续逻辑需要保持,必须自己用 S/R 或自锁。
FB_EdgeDetection / 保存历史状态 — 把当前值留到下一周期
FB_EdgeDetection.scl · 保存历史状态
24 REGION 保存历史状态
25
26 #Prev := #Signal;
27 END_REGION
目的:让下一个扫描周期还能知道“上一周期是什么”。
条件 → 动作:每个扫描周期无条件执行 Prev := Signal。
为什么:Prev 是 FB 的静态变量,掉电保持/扫描间保持;如果放在 VAR_TEMP,每个周期都会丢失,边沿就永远检测不到。
注意:这一行必须放在边沿判断之后。如果先更新 Prev,本周期比较时 Prev 已经等于 Signal,两个边沿都会丢失。
仿真验证:让 Agent 写场景并执行(不需要博途)
推荐:让 Agent 自己设计场景
不需要你手写 scenario。把下面的提示词复制给已连接 PLC 云仿真 MCP 的 AI 助手(Claude Code / Cursor / VS Code 等),它会阅读源码、自己设计正常/边界/异常场景,并调用 scl_run_scenario 执行;新用户有 100 次免费调用。
- 复制提示词(包含完整 SCL 源码和任务说明)。
- 粘贴给已配置 sim MCP 的 Agent,让它先
scl_validate,再执行自己写的 scl_run_scenario。
- 看 Agent 返回的 PASS/FAIL、输入/期望/实际;需要精确复现时,再对照下方本站回归基线。
AGENT PROMPT
你已连接 PLC 云仿真 MCP(sim-cloud)……
任务:阅读下面的 SCL 示例,自己设计 3 个场景并用 scl_run_scenario 执行:
1) 正常路径
2) 边界/临界值
3) 异常、互锁或超时
每个场景请给出输入、期望、实际结果和 PASS/FAIL,并说明场景设计理由。
不要照抄现成场景。
示例:SCL 边沿检测:上升沿与下降沿
// 复制后包含完整 SCL 源码和 MCP 参数说明
页面上只显示提示词开头;复制的内容包含全部块源码和 MCP 参数说明。
查看本站回归基线(固定场景,不是让你手写)
下面是本站构建时跑过的固定场景和预期输出,用来做回归对比或精确复现;不是要求你手写 scenario。推荐先让 Agent 自己写场景,再和这份基线对照。
EXPECTED
PASS 上升沿只出现一个扫描周期 (5 step(s), 5 scan(s))
RESULT: ALL PASS (5 scan(s))
BASELINE JSON
{
"sources": [
{
"name": "blocks/FB_EdgeDetection.scl",
"source": "FUNCTION_BLOCK \"FB_EdgeDetection\"\n{ S7_Optimized_Access := 'TRUE' }\nVERSION : 0.1\n VAR_INPUT\n Signal : Bool; // 现场信号(按钮、传感器、运行反馈等)\n END_VAR\n\n VAR_OUTPUT\n Rising : Bool; // 上升沿:本周期为 1,且上周期为 0\n Falling : Bool; // 下降沿:本周期为 0,且上周期为 1\n END_VAR\n\n VAR\n Prev : Bool; // 上一扫描周期的 Signal\n END_VAR\n\nBEGIN\n REGION 边沿判断\n // 上升沿 = 当前为 1 且上一周期为 0;下降沿相反。\n #Rising := #Signal AND NOT #Prev;\n #Falling := NOT #Signal AND #Prev;\n END_REGION\n\n REGION 保存历史状态\n // 必须放在边沿判断之后:先比较,再更新历史值。\n #Prev := #Signal;\n END_REGION\nEND_FUNCTION_BLOCK\n"
}
],
"scenario": {
"docs": [
"blocks/FB_EdgeDetection.scl"
],
"block": "FB_EdgeDetection",
"tickMs": 10,
"always": [
"!(Rising && Falling)"
],
"scenarios": [
{
"name": "上升沿只出现一个扫描周期",
"steps": [
{
"set": {
"Signal": false
},
"expect": {
"Rising": false,
"Falling": false
}
},
{
"set": {
"Signal": true
},
"expect": {
"Rising": true,
"Falling": false
}
},
{
"set": {
"Signal": true
},
"expect": {
"Rising": false,
"Falling": false
}
},
{
"set": {
"Signal": false
},
"expect": {
"Rising": false,
"Falling": true
}
},
{
"set": {
"Signal": false
},
"expect": {
"Rising": false,
"Falling": false
}
}
]
}
]
}
}
打开 PLC 云仿真
关键 SCL 语法
| 写法 |
含义 |
#Signal |
访问 FB 的局部变量/参数 |
AND / NOT |
逻辑与 / 逻辑非 |
:= |
赋值 |
VAR |
FB 的静态变量,扫描间保持 |
REGION ... END_REGION |
给代码分段,方便阅读和讲解 |
常见坑
- 把
Prev 写成 VAR_TEMP:临时变量每个周期重建,边沿检测失效。
- 先更新
Prev 再判断:本周期比较的是“自己和自己”,永远检测不到边沿。
- 用边沿输出直接驱动长时间动作:边沿只有一个周期,必须自锁或置位。
- 在 OB 里用全局 M 点做
Prev:可以工作,但多个调用点会互相干扰;FB 静态变量更安全。
变体与扩展
- 需要“上升沿 + 下降沿”两个独立 FC 时,可以把两段逻辑拆成
FC_EdgePos / FC_EdgeNeg,用 IN_OUT 传入历史位;本页为了仿真合并成一个 FB。
- 需要检测“信号变化”而不区分方向时,可以用
Signal XOR Prev。
- 对安全相关信号,边沿检测不能替代安全功能;安全逻辑应使用 F-CPU / 安全程序。
常见问题
SCL 上升沿为什么要保存上一周期的信号?
边沿是“本周期为 1、上周期为 0”的变化,不是电平本身。必须把上一周期的 Signal 存到静态变量 Prev,再在判断之后更新;Prev 放 VAR_TEMP 或先更新再判断,都会让 Rising 永远为 FALSE。
手写边沿和 R_TRIG 哪个好?
两者等价:R_TRIG 把 Prev 封装在实例里,适合标准库;手写三行更透明,方便在云仿真里逐扫描看变量。多调用点不要共用一个 Prev,否则会互相干扰。
验证状态
| 项目 | 结果 | 日期 | 证据 |
|---|
| 本地仿真场景 | PASS(5 scans) | 2026-11-01 | 页面下方 `scl_run_scenario` 请求可复现 |
| PLC 云仿真 | PASS | 2026-11-01 | `scl_run_scenario`(托管 MCP) |
| TIA 导入编译 | PASS(0 error / 0 warning) | 2026-11-01 | 本地 SCL 仿真与 PLC 云仿真(scl_run_scenario)场景通过;TIA Portal V19 + TiaLink 1.4.0.0 批量导入 PLC_2,编译 0 错误 / 0 警告,已保存。 |
本地仿真引擎验证语法、结构和逐扫描行为;TIA 编译验证在 Windows + TIA Portal + TiaLink 上完成。两者不能互相替代。