SCL 示例库/SCL 节拍发生器:10Hz/2Hz/1Hz 方波与上升沿
SCL 节拍发生器:10Hz/2Hz/1Hz 方波与上升沿
SCL 节拍发生器:用累加器和半周期常量生成 10Hz/2Hz/1Hz 方波与单周期上升沿;含完整源码和云仿真场景。
进阶
S7-1200
S7-1500
节拍
方波
边沿
Time
T_ADD
1 块 / 69 行
可仿真
TIA 已验证
一句话:三个 Time 累加器分别累加 CycleTime,到半周期就翻转电平并清零,翻转时输出一个扫描周期的边沿;10Hz/2Hz/1Hz 只是半周期常量不同。
导入到我的 TIA 项目
通过 TiaLink 把本示例的 1 个 SCL 块作为“外部源”导入你当前打开的 TIA Portal 项目:自动生成块、编译,并清理外部源。导入后不会自动保存;FB 示例导入后需要你在 OB 里调用。
节拍发生器用软件产生周期方波,常用于 LED 闪烁、HMI 刷新、周期任务触发。它的核心不是“定时器”,而是“累加时间 → 到半周期翻转 → 输出边沿”。
三路方波共用“累加 → 到半周期翻转 → 清零”的结构,半周期常量不同。
概述
FB_TaktGenerator 用三个累加器分别累计 CycleTime,当累加器达到各自的半周期常量时翻转电平并清零:
- 10Hz:半周期 50ms;
- 2Hz:半周期 250ms;
- 1Hz:半周期 500ms。
每次翻转时,Edge* 输出一个扫描周期的上升沿,方便调用方做“每周期执行一次”的动作。
你将学到
Time 累加与比较;
- 用
T_ADD 做时间加法;
- 半周期与频率的关系;
- 如何用场景验证“第 5 个扫描 2Hz 翻转”“第 10 个扫描 1Hz 翻转”。
接口与变量
下表由构建器从 SCL 源码的接口声明自动生成;页面上展示的代码就是导入 TIA 的同一份源码。
FB_TaktGenerator FB
| 区块 | 名称 | 类型 | 说明 |
|---|
| 输入 | CycleTime | Time | |
| 输出 | Takt10Hz | Bool | |
| 输出 | Takt2Hz | Bool | |
| 输出 | Takt1Hz | Bool | |
| 输出 | Edge10Hz | Bool | |
| 输出 | Edge2Hz | Bool | |
| 输出 | Edge1Hz | Bool | |
| 静态 | Acc10 | Time | |
| 静态 | Acc2 | Time | |
| 静态 | Acc1 | Time | |
| 静态 | State10 | Bool | |
| 静态 | State2 | Bool | |
| 静态 | State1 | Bool | |
| 静态 | Prev10 | Bool | |
| 静态 | Prev2 | Bool | |
| 静态 | Prev1 | Bool | |
| 常量 | HALF_10HZ | Time | |
| 常量 | HALF_2HZ | Time | |
| 常量 | HALF_1HZ | Time | |
程序结构
导入顺序以下表为准:被依赖的块/类型先导入;本页已按依赖排好。
- FB_TaktGenerator FB 多频率节拍与边沿 · 69 行 · 3 段
FB_TaktGenerator FB
多频率节拍与边沿 · 69 行 · 源文件 blocks/FB_TaktGenerator.scl
查看 / 复制完整块源码
FB_TaktGenerator.scl
1FUNCTION_BLOCK "FB_TaktGenerator"
2{ S7_Optimized_Access := 'TRUE' }
3VERSION : 0.1
4 VAR_INPUT
5 CycleTime : Time;
6 END_VAR
7
8 VAR_OUTPUT
9 Takt10Hz : Bool;
10 Takt2Hz : Bool;
11 Takt1Hz : Bool;
12 Edge10Hz : Bool;
13 Edge2Hz : Bool;
14 Edge1Hz : Bool;
15 END_VAR
16
17 VAR
18 Acc10 : Time;
19 Acc2 : Time;
20 Acc1 : Time;
21 State10 : Bool;
22 State2 : Bool;
23 State1 : Bool;
24 Prev10 : Bool;
25 Prev2 : Bool;
26 Prev1 : Bool;
27 END_VAR
28
29 VAR CONSTANT
30 HALF_10HZ : Time := T#50MS;
31 HALF_2HZ : Time := T#250MS;
32 HALF_1HZ : Time := T#500MS;
33 END_VAR
34
35BEGIN
36 REGION 累加时间
37 #Acc10 := T_ADD(IN1 := #Acc10, IN2 := #CycleTime);
38 #Acc2 := T_ADD(IN1 := #Acc2, IN2 := #CycleTime);
39 #Acc1 := T_ADD(IN1 := #Acc1, IN2 := #CycleTime);
40 END_REGION
41
42 REGION 翻转节拍
43 IF #Acc10 >= #HALF_10HZ THEN
44 #State10 := NOT #State10;
45 #Acc10 := T#0MS;
46 END_IF;
47 IF #Acc2 >= #HALF_2HZ THEN
48 #State2 := NOT #State2;
49 #Acc2 := T#0MS;
50 END_IF;
51 IF #Acc1 >= #HALF_1HZ THEN
52 #State1 := NOT #State1;
53 #Acc1 := T#0MS;
54 END_IF;
55 END_REGION
56
57 REGION 输出与边沿
58 #Takt10Hz := #State10;
59 #Takt2Hz := #State2;
60 #Takt1Hz := #State1;
61 #Edge10Hz := #State10 AND NOT #Prev10;
62 #Edge2Hz := #State2 AND NOT #Prev2;
63 #Edge1Hz := #State1 AND NOT #Prev1;
64 #Prev10 := #State10;
65 #Prev2 := #State2;
66 #Prev1 := #State1;
67 END_REGION
68END_FUNCTION_BLOCK
69
FB_TaktGenerator / 累加时间 — 每个周期加 CycleTime
FB_TaktGenerator.scl · 累加时间
36 REGION 累加时间
37 #Acc10 := T_ADD(IN1 := #Acc10, IN2 := #CycleTime);
38 #Acc2 := T_ADD(IN1 := #Acc2, IN2 := #CycleTime);
39 #Acc1 := T_ADD(IN1 := #Acc1, IN2 := #CycleTime);
40 END_REGION
目的:把扫描周期转换成可比较的时间。
条件 → 动作:每个扫描周期执行三次 T_ADD,分别累加 Acc10、Acc2、Acc1。
为什么:PLC 没有“固定 50ms 中断”时,只能用扫描周期累加来近似;CycleTime 越准确,节拍越准。
注意:CycleTime 必须由调用方给出;如果固定填 T#10MS,实际 OB 周期变化时节拍会漂移。
FB_TaktGenerator / 翻转节拍 — 到半周期就取反
FB_TaktGenerator.scl · 翻转节拍
42 REGION 翻转节拍
43 IF #Acc10 >= #HALF_10HZ THEN
44 #State10 := NOT #State10;
45 #Acc10 := T#0MS;
46 END_IF;
47 IF #Acc2 >= #HALF_2HZ THEN
48 #State2 := NOT #State2;
49 #Acc2 := T#0MS;
50 END_IF;
51 IF #Acc1 >= #HALF_1HZ THEN
52 #State1 := NOT #State1;
53 #Acc1 := T#0MS;
54 END_IF;
55 END_REGION
目的:在累加器达到半周期时翻转对应电平。
条件 → 动作:Acc10 >= 50ms → State10 := NOT State10,Acc10 := 0;2Hz/1Hz 同理。
为什么:方波的半周期就是翻转间隔;用 >= 而不是 =,可以容忍扫描周期不是半周期的整数倍。
注意:翻转后要把累加器清零,否则下一个周期会立刻再次满足条件,频率变成“每个扫描周期翻转一次”。
FB_TaktGenerator / 输出与边沿 — 电平 + 单周期脉冲
FB_TaktGenerator.scl · 输出与边沿
57 REGION 输出与边沿
58 #Takt10Hz := #State10;
59 #Takt2Hz := #State2;
60 #Takt1Hz := #State1;
61 #Edge10Hz := #State10 AND NOT #Prev10;
62 #Edge2Hz := #State2 AND NOT #Prev2;
63 #Edge1Hz := #State1 AND NOT #Prev1;
64 #Prev10 := #State10;
65 #Prev2 := #State2;
66 #Prev1 := #State1;
67 END_REGION
目的:输出方波电平,并在上升沿时给出一个扫描周期的脉冲。
条件 → 动作:Takt* := State*;Edge* := State* AND NOT Prev*;最后更新 Prev*。
为什么:很多调用方需要“每周期执行一次”,而不是“高电平期间一直执行”;边沿脉冲正好满足。
注意:Prev* 必须在边沿判断之后更新,否则边沿会丢失(和边沿检测示例同一个坑)。
仿真验证:让 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 节拍发生器:10Hz/2Hz/1Hz 方波与上升沿
// 复制后包含完整 SCL 源码和 MCP 参数说明
页面上只显示提示词开头;复制的内容包含全部块源码和 MCP 参数说明。
查看本站回归基线(固定场景,不是让你手写)
下面是本站构建时跑过的固定场景和预期输出,用来做回归对比或精确复现;不是要求你手写 scenario。推荐先让 Agent 自己写场景,再和这份基线对照。
EXPECTED
PASS 10 个 50ms 扫描 (1 step(s), 10 scan(s))
PASS 第 5 个扫描:2Hz 翻转 (1 step(s), 5 scan(s))
RESULT: ALL PASS (15 scan(s))
BASELINE JSON
{
"sources": [
{
"name": "blocks/FB_TaktGenerator.scl",
"source": "FUNCTION_BLOCK \"FB_TaktGenerator\"\n{ S7_Optimized_Access := 'TRUE' }\nVERSION : 0.1\n VAR_INPUT\n CycleTime : Time; // 本次扫描经过的时间,例如 T#10ms\n END_VAR\n\n VAR_OUTPUT\n Takt10Hz : Bool; // 10 Hz 方波\n Takt2Hz : Bool; // 2 Hz 方波\n Takt1Hz : Bool; // 1 Hz 方波\n Edge10Hz : Bool; // 10 Hz 上升沿(一个扫描周期)\n Edge2Hz : Bool; // 2 Hz 上升沿\n Edge1Hz : Bool; // 1 Hz 上升沿\n END_VAR\n\n VAR\n Acc10 : Time; // 10 Hz 累加器\n Acc2 : Time; // 2 Hz 累加器\n Acc1 : Time; // 1 Hz 累加器\n State10 : Bool; // 10 Hz 当前电平\n State2 : Bool; // 2 Hz 当前电平\n State1 : Bool; // 1 Hz 当前电平\n Prev10 : Bool; // 10 Hz 上一周期电平\n Prev2 : Bool; // 2 Hz 上一周期电平\n Prev1 : Bool; // 1 Hz 上一周期电平\n END_VAR\n\n VAR CONSTANT\n HALF_10HZ : Time := T#50MS; // 10 Hz 半周期\n HALF_2HZ : Time := T#250MS; // 2 Hz 半周期\n HALF_1HZ : Time := T#500MS; // 1 Hz 半周期\n END_VAR\n\nBEGIN\n REGION 累加时间\n #Acc10 := T_ADD(IN1 := #Acc10, IN2 := #CycleTime);\n #Acc2 := T_ADD(IN1 := #Acc2, IN2 := #CycleTime);\n #Acc1 := T_ADD(IN1 := #Acc1, IN2 := #CycleTime);\n END_REGION\n\n REGION 翻转节拍\n IF #Acc10 >= #HALF_10HZ THEN\n #State10 := NOT #State10;\n #Acc10 := T#0MS;\n END_IF;\n IF #Acc2 >= #HALF_2HZ THEN\n #State2 := NOT #State2;\n #Acc2 := T#0MS;\n END_IF;\n IF #Acc1 >= #HALF_1HZ THEN\n #State1 := NOT #State1;\n #Acc1 := T#0MS;\n END_IF;\n END_REGION\n\n REGION 输出与边沿\n #Takt10Hz := #State10;\n #Takt2Hz := #State2;\n #Takt1Hz := #State1;\n #Edge10Hz := #State10 AND NOT #Prev10;\n #Edge2Hz := #State2 AND NOT #Prev2;\n #Edge1Hz := #State1 AND NOT #Prev1;\n #Prev10 := #State10;\n #Prev2 := #State2;\n #Prev1 := #State1;\n END_REGION\nEND_FUNCTION_BLOCK\n"
}
],
"scenario": {
"docs": [
"blocks/FB_TaktGenerator.scl"
],
"block": "FB_TaktGenerator",
"tickMs": 50,
"seed": {
"CycleTime": "T#50MS"
},
"always": [
"!(Edge10Hz && !Takt10Hz)",
"!(Edge2Hz && !Takt2Hz)",
"!(Edge1Hz && !Takt1Hz)"
],
"scenarios": [
{
"name": "10 个 50ms 扫描",
"steps": [
{
"ticks": 10,
"expect": {
"Takt10Hz": false,
"Takt2Hz": false,
"Takt1Hz": true,
"Edge1Hz": true,
"Edge10Hz": false
}
}
]
},
{
"name": "第 5 个扫描:2Hz 翻转",
"steps": [
{
"ticks": 5,
"expect": {
"Takt10Hz": true,
"Takt2Hz": true,
"Takt1Hz": false,
"Edge2Hz": true
}
}
]
}
]
}
}
打开 PLC 云仿真
关键 SCL 语法
| 写法 |
含义 |
T_ADD |
两个 Time 相加 |
T#50MS / T#250MS / T#500MS |
时间常量 |
NOT |
布尔取反 |
>= |
比较 |
VAR CONSTANT |
常量区,运行中只读 |
常见坑
- 半周期和频率搞反:10Hz 的周期是 100ms,半周期是 50ms;填成 100ms 会得到 5Hz。
- 用
= 判断累加器:扫描周期不是整数倍时会错过;用 >=。
- 忘记清零:翻转后不清零,频率会变成扫描周期的一半。
- 在多个地方调用同一个 FB 实例:每个节拍通道需要独立的实例;不要共用。
CycleTime 抖动:真实 OB 周期会变,长时间运行会累积误差;高精度场合用硬件时钟或 RD_SYS_T。
变体与扩展
- 把三个通道扩展成
Array[0..9],用 CASE 给每个通道分配半周期,就是原工程的 10 路节拍发生器。
- 增加
Enable 输入,关闭时停止翻转并清零输出。
- 用
TON 替代累加器,可以得到更精确的周期,但每个通道需要一个定时器实例。
常见问题
SCL 怎么产生 1Hz 方波?
用 Time 累加器累加 CycleTime,到半周期 500ms 就翻转电平并清零;翻转时输出一个扫描周期的上升沿。10Hz/2Hz 只是半周期常量不同(50ms/250ms)。
为什么比较用 >= 不用 =?
扫描周期不一定是半周期的整数倍,用 = 可能永远等不到;用 >= 会在超过阈值的那次扫描翻转,再把累加器清零。
验证状态
| 项目 | 结果 | 日期 | 证据 |
|---|
| 本地仿真场景 | PASS(15 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 上完成。两者不能互相替代。