在结构化文本(ST)编程中调用定时器功能块(如 TON 和 TOF)时,必须显式声明并实例化,不能像梯形图(LAD)中那样直接拖放一个定时器符号、填写参数就完成调用。这是 ST 语言语法本质决定的——它基于 Pascal 风格的强类型函数块调用机制,所有功能块(FB)都属于“类模板”,必须先创建具体对象(即实例),才能访问其输入、输出和内部状态。
一、为什么 ST 中不能“直接使用”TON/TOF?
梯形图中的定时器(例如 TON T37, T#5S)看似“直接”,实则是编辑器在后台自动完成了三件事:
- 隐式声明一个名为
T37的TON类型变量; - 自动绑定该变量到当前网络的作用域;
- 静态分配其背景数据块(DB)或内部存储空间。
而 ST 是纯文本语言,没有图形上下文,也不支持隐式声明。你写的每一行代码,都必须明确告诉 PLC:
- 这个定时器叫什么名字(实例名);
- 它的数据类型是什么(
TON或TOF); - 它的输入参数(
IN,PT)由谁提供; - 它的输出(
Q,ET)要赋给哪个变量。
否则,编译器会报错:
Error 42: 'TON' is a function block type, not a function. An instance must be declared.
二、正确调用步骤(以 TON 为例)
1. 声明定时器实例变量
在 VAR 或 VAR_GLOBAL 区域中,声明一个具名变量,类型为 TON:
VAR
MyTimer1 : TON; // ✅ 正确:声明一个名为 MyTimer1 的 TON 实例
MyTimer2 : TOF; // ✅ 同理,TOF 实例
END_VAR
⚠️ 注意:
TON和TOF是系统预定义的功能块类型(IEC 61131-3 标准),首字母大写,不可写成ton或Ton;- 实例名(如
MyTimer1)必须符合标识符规则:以字母或下划线开头,仅含字母、数字、下划线,且不能与关键字冲突; - 不可省略分号
;,这是 ST 的语句终结符。
2. 在程序体中调用该实例
在 PROGRAM 主体或 FUNCTION_BLOCK 内部,以函数块调用语法执行:
MyTimer1(IN := StartButton, PT := T#8S);
这行代码等效于:
- 将
StartButton的布尔值传给MyTimer1的IN输入端; - 将时间常量
T#8S(8 秒)传给PT(预设时间)输入端; - 执行
MyTimer1的内部逻辑(检测上升沿、计时、置位Q、更新ET)。
✅ 关键点:
- 调用必须使用 带参数名的赋值语法(
IN := ...,PT := ...),不可省略参数名; - 参数顺序不重要,但参数名必须准确(
IN,PT,Q,ET是标准接口,大小写敏感); PT必须是TIME类型,如T#500MS、T#2S、T#1M30S,不可写成整数5000或字符串"5s"。
3. 读取输出结果
定时器执行后,通过实例名访问其输出成员:
IF MyTimer1.Q THEN
**启动电机**;
END_IF;
MotorRunTime := MyTimer1.ET; // 获取已计时长(TIME 类型)
Q 是 BOOL 类型,表示定时完成;
ET 是 TIME 类型,表示当前已计时长(从 IN 为 TRUE 开始累计,IN 为 FALSE 时清零)。
三、TON 与 TOF 的核心区别及调用要点
| 特性 | TON(延时接通定时器) |
TOF(延时断开定时器) |
|---|---|---|
| 触发条件 | IN 从 FALSE → TRUE 的上升沿开始计时 |
IN 从 TRUE → FALSE 的下降沿开始计时 |
| Q 输出 | 计时 ≥ PT 时 Q 置 TRUE,IN 为 FALSE 时 Q 立即变 FALSE |
IN 为 TRUE 时 Q 始终为 TRUE;IN 变 FALSE 后,计时 PT 时间,再将 Q 置 FALSE |
| ET 行为 | IN 为 TRUE 时累加;IN 为 FALSE 时 ET 清零 |
IN 为 TRUE 时 ET 清零;IN 为 FALSE 时开始累加,达 PT 后停止 |
| 典型用途 | 启动延时(如:按下启动按钮后 3 秒再开泵) | 停止延时(如:松开停止按钮后,风机再运行 10 秒散热) |
调用 TOF 的语法完全一致,仅需替换实例类型和逻辑理解:
VAR
CoolDownTimer : TOF;
END_VAR
// 在程序体中:
CoolDownTimer(IN := MotorStopSignal, PT := T#10S);
IF CoolDownTimer.Q THEN
**保持风机运行**;
END_IF;
四、常见错误及修正方案
❌ 错误 1:把 TON 当作函数调用
// 错误!TON 不是函数,无返回值,不能这样写:
IF TON(IN := X, PT := T#3S).Q THEN ...
✅ 修正:必须先声明实例,再调用,最后读取 .Q:
MyT : TON;
MyT(IN := X, PT := T#3S);
IF MyT.Q THEN ...
❌ 错误 2:漏掉 IN 参数
// 错误!TON 至少需要 IN 和 PT,缺一不可:
MyT(PT := T#3S); // 编译报错:missing required parameter 'IN'
✅ 修正:显式提供 IN:
MyT(IN := X, PT := T#3S);
❌ 错误 3:混淆 ET 类型与比较方式
// 错误!ET 是 TIME 类型,不能直接与整数比较:
IF MyT.ET > 5000 THEN ... // ❌ 类型不匹配
✅ 修正:用 TIME_TO_MS() 转换,或直接与 TIME 常量比较:
IF MyT.ET >= T#5S THEN ... // ✅ 推荐:同类型比较
IF TIME_TO_MS(MyT.ET) >= 5000 THEN ... // ✅ 转换为毫秒整数
❌ 错误 4:在 FOR 或 WHILE 循环内重复声明实例
FOR i := 1 TO 5 DO
TempTimer : TON; // ❌ 错误!VAR 声明不能在执行区
TempTimer(IN := ..., PT := ...);
END_FOR;
✅ 修正:所有实例必须在 VAR 区统一声明,循环内只调用:
VAR
TimerArray : ARRAY[1..5] OF TON; // 声明数组实例
END_VAR
FOR i := 1 TO 5 DO
TimerArray[i](IN := Trigger[i], PT := T#2S);
END_FOR;
五、进阶技巧:批量管理与复位控制
▶ 批量声明多个定时器(数组方式)
当需要多个同类定时器(如 10 台电机各自独立延时),用数组避免重复声明:
VAR
MotorStartTimers : ARRAY[1..10] OF TON;
MotorStopSignals : ARRAY[1..10] OF BOOL;
MotorStartCmds : ARRAY[1..10] OF BOOL;
END_VAR
// 在程序体中循环调用:
FOR motorID := 1 TO 10 DO
MotorStartTimers[motorID](
IN := MotorStartCmds[motorID],
PT := T#3S
);
IF MotorStartTimers[motorID].Q THEN
MotorRunning[motorID] := TRUE;
END_IF;
END_FOR;
▶ 手动复位定时器(清除 ET 和 Q)
TON/TOF 默认在 IN 为 FALSE 时自动清零 ET 并复位 Q,但有时需强制复位(如故障急停后清空所有计时):
// 方法 1:用 RESET 指令(部分平台支持,非 IEC 标准)
// MyTimer1.RESET := TRUE; // 非通用,慎用
// 方法 2:最通用安全做法——重新调用并传 FALSE 给 IN
MyTimer1(IN := FALSE, PT := MyTimer1.PT); // 强制清零 ET,Q 变 FALSE
// 方法 3:封装复位逻辑(推荐)
ResetTimer(IN := ResetSignal, Instance := ADR(MyTimer1));
其中 ResetTimer 是自定义功能块,内部执行:
Instance^.IN := FALSE;
Instance^.PT := Instance^.PT; // 保持原设定
六、与梯形图的等效对照(帮助迁移理解)
假设梯形图中有如下网络:
|----[ I0.0 ]-----------------( TON T37, T#5S )----|
| |
|----[ T37.Q ]-----------------------------( Q0.0 )--|
对应 ST 的完整等效代码为:
VAR
T37 : TON; // 声明实例,名称与 LAD 中一致便于追踪
END_VAR
// 程序体:
T37(IN := "I0.0", PT := T#5S);
"Q0.0" := T37.Q;
注意:"I0.0" 和 "Q0.0" 是符号地址(加引号表示字符串地址,实际取决于 PLC 品牌是否启用符号寻址;若用绝对地址,则写 I0.0 不加引号,但需确认变量已映射)。
七、调试建议:如何验证定时器是否正常工作
- 在线监控变量:在编程软件中打开变量表,添加
MyTimer1.IN,MyTimer1.Q,MyTimer1.ET,观察其变化; - 插入中间变量辅助判断:
Debug_TON_IN := MyTimer1.IN; Debug_TON_Q := MyTimer1.Q; Debug_TON_ET := TIME_TO_MS(MyTimer1.ET);将这些变量加入监控,排除逻辑误判;
- 用
TP(脉冲定时器)验证边沿:若TON不触发,先用TP测试IN信号是否有干净上升沿; - 检查
PT是否为零:T#0S会导致Q立即置位,易被误判为“未计时”。
八、扩展:自定义定时器封装(可选高级用法)
若项目中频繁使用带使能、复位、超时报警的复合定时器,可封装为自定义 FB:
FUNCTION_BLOCK SafeTON
VAR_INPUT
EN : BOOL; // 使能
IN : BOOL; // 启动信号
PT : TIME; // 定时时间
RESET: BOOL; // 复位信号
END_VAR
VAR_OUTPUT
Q : BOOL; // 输出
ET : TIME; // 已计时
ERROR: BOOL; // 超时未动作报警(可选)
END_VAR
VAR
InternalTON : TON;
LastIN : BOOL;
END_VAR
IF NOT EN THEN
Q := FALSE;
ET := T#0S;
ERROR := FALSE;
ELSIF RESET THEN
InternalTON(IN := FALSE, PT := PT);
Q := FALSE;
ET := T#0S;
ELSE
InternalTON(IN := IN, PT := PT);
Q := InternalTON.Q;
ET := InternalTON.ET;
IF IN AND NOT InternalTON.Q AND InternalTON.ET >= PT THEN
ERROR := TRUE; // 卡在临界点未触发
END_IF;
END_IF;
调用方式:
MySafeTimer(EN := TRUE, IN := Start, PT := T#10S, RESET := FaultReset);
MotorReady := MySafeTimer.Q;
该封装提升了复用性与鲁棒性,但初学者应先掌握原生 TON/TOF 实例化。
九、不同 PLC 品牌的细微差异(兼容性提醒)
| 品牌 | TON 类型路径 |
实例声明语法 | 注意事项 |
|---|---|---|---|
| Siemens S7-1200/1500 | TON(系统库默认可用) |
MyT : TON; |
支持 TONR(保持型),PT 可为 LTIME |
| Beckhoff TwinCAT | Tc2_Standard.TON |
MyT : Tc2_Standard.TON; |
需先引用 Tc2_Standard 库 |
| Codesys | TON(标准库) |
MyT : TON; |
可通过 TON、TOF、TP 统一调用 |
| Rockwell Logix | TON(标签数据类型) |
MyT : TON; |
实例名即标签名,IN/Q 为子元素(MyT.IN) |
无论品牌,实例化原则不变:声明 → 调用 → 读取。
十、总结核心口诀(便于记忆)
- 声明是前提:没
VAR MyTimer : TON;,一切调用皆非法; - 调用带参数:
MyTimer(IN := ..., PT := ...),缺一不可; - 输出靠点取:
MyTimer.Q、MyTimer.ET,不可省略实例名; - TOF 看下降:
IN变FALSE才启动,别与TON逻辑混淆; - ET 比较要小心:务必用
TIME常量或转换函数,勿直比整数。
掌握这一机制,就打通了 ST 编程中功能块调用的底层逻辑,后续对 CTU(增计数器)、MOVE(移动指令)、SEL(选择器)等所有功能块的理解都将一通百通。

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