跳到正文
Learn-SCL
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 里调用。

TiaLink 设置

默认 http://localhost:8080。也可以在本页地址后加 ?tialink= 临时指定。

TiaLink 启动时若设置了 --api-token,这里要填相同的值;只保存在本机浏览器 localStorage,不会上传。

节拍发生器用软件产生周期方波,常用于 LED 闪烁、HMI 刷新、周期任务触发。它的核心不是“定时器”,而是“累加时间 → 到半周期翻转 → 输出边沿”。

SCL 节拍发生器波形:10Hz 半周期 50ms、2Hz 250ms、1Hz 500ms
三路方波共用“累加 → 到半周期翻转 → 清零”的结构,半周期常量不同。

概述

FB_TaktGenerator 用三个累加器分别累计 CycleTime,当累加器达到各自的半周期常量时翻转电平并清零:

  • 10Hz:半周期 50ms;
  • 2Hz:半周期 250ms;
  • 1Hz:半周期 500ms。

每次翻转时,Edge* 输出一个扫描周期的上升沿,方便调用方做“每周期执行一次”的动作。

你将学到

  • Time 累加与比较;
  • 用 T_ADD 做时间加法;
  • 半周期与频率的关系;
  • 如何用场景验证“第 5 个扫描 2Hz 翻转”“第 10 个扫描 1Hz 翻转”。

接口与变量

下表由构建器从 SCL 源码的接口声明自动生成;页面上展示的代码就是导入 TIA 的同一份源码。

FB_TaktGenerator FB

区块名称类型说明
输入CycleTimeTime
输出Takt10HzBool
输出Takt2HzBool
输出Takt1HzBool
输出Edge10HzBool
输出Edge2HzBool
输出Edge1HzBool
静态Acc10Time
静态Acc2Time
静态Acc1Time
静态State10Bool
静态State2Bool
静态State1Bool
静态Prev10Bool
静态Prev2Bool
静态Prev1Bool
常量HALF_10HZTime
常量HALF_2HZTime
常量HALF_1HZTime

程序结构

导入顺序以下表为准:被依赖的块/类型先导入;本页已按依赖排好。

  1. 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;   // 本次扫描经过的时间,例如 T#10ms
6   END_VAR
7 
8   VAR_OUTPUT
9      Takt10Hz : Bool;    // 10 Hz 方波
10      Takt2Hz : Bool;     // 2 Hz 方波
11      Takt1Hz : Bool;     // 1 Hz 方波
12      Edge10Hz : Bool;    // 10 Hz 上升沿(一个扫描周期)
13      Edge2Hz : Bool;     // 2 Hz 上升沿
14      Edge1Hz : Bool;     // 1 Hz 上升沿
15   END_VAR
16 
17   VAR
18      Acc10 : Time;       // 10 Hz 累加器
19      Acc2 : Time;        // 2 Hz 累加器
20      Acc1 : Time;        // 1 Hz 累加器
21      State10 : Bool;     // 10 Hz 当前电平
22      State2 : Bool;      // 2 Hz 当前电平
23      State1 : Bool;      // 1 Hz 当前电平
24      Prev10 : Bool;      // 10 Hz 上一周期电平
25      Prev2 : Bool;       // 2 Hz 上一周期电平
26      Prev1 : Bool;       // 1 Hz 上一周期电平
27   END_VAR
28 
29   VAR CONSTANT
30      HALF_10HZ : Time := T#50MS;   // 10 Hz 半周期
31      HALF_2HZ : Time := T#250MS;   // 2 Hz 半周期
32      HALF_1HZ : Time := T#500MS;   // 1 Hz 半周期
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 次免费调用。

  1. 复制提示词(包含完整 SCL 源码和任务说明)。
  2. 粘贴给已配置 sim MCP 的 Agent,让它先 scl_validate,再执行自己写的 scl_run_scenario。
  3. 看 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 云仿真PASS2026-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 上完成。两者不能互相替代。

来源与许可

本页示例改编自 github.com/lopez-dev/Siemens-SCL-Source-Files 的 TaktGenerator/FB_Taktgenerator.scl,原作者 Daniel Cáceres López,以 MIT 许可发布。中文注释、接口调整、仿真场景与页面讲解由 Learn-SCL 编写。

原文件用系统时钟和 10 路 UDT 输出;本站改为 CycleTime 累加 + 3 路输出,保留“累加-翻转-边沿”的核心结构。

示例源码以 MIT 许可发布(上游仓库);中文讲解与页面内容版权归汇智网(Hubwiz)。官方参考:Siemens TIA Portal。

把这个示例带进你的工程

TiaLink

把 TIA Portal 接到你的智能体:导入、编译、读回核对、PLCSIM 仿真。本地运行,工程数据不出厂。

了解 TiaLink →

PLC 云仿真

不装博途,把 SCL 放到云端逐扫描执行:校验、仿真、场景验收。新用户 100 次免费。

打开云仿真 →

Learn-LAD

同一个团队的梯形图示例库:逐网络讲解、变量表、TiaLink 一键导入。

浏览 LAD 示例 →