在结构化文本(ST)编程中,使用 TON(On-Delay Timer,接通延时定时器)实现精确延时是最基础也最频繁的操作。它不依赖硬件继电器,完全由 PLC 扫描周期和内部时基驱动,稳定、可复用、易调试。以下内容全程基于 IEC 61131-3 标准,适用于 Siemens TIA Portal(S7-1200/1500)、Codesys、B&R Automation Studio、Phoenix Contact PLC 等主流平台,所有代码均可直接复制粘贴运行。
一、TON 的本质:三个核心接口
TON 不是“函数”,也不是“指令块”,而是一个预定义功能块(FB)类型,必须先声明变量再实例化。它的行为由三个关键引脚控制:
| 引脚名 | 数据类型 | 作用说明 |
|---|---|---|
.IN |
BOOL |
启动信号:上升沿触发计时;FALSE 时自动复位 .Q 并清零 .ET |
.PT |
TIME |
预设时间值:必须为标准时间格式,如 T#5S、T#100MS、T#2.5S |
.Q |
BOOL(只读) |
输出信号:.IN = TRUE 且 .ET >= .PT 时变为 TRUE,否则为 FALSE |
.ET |
TIME(只读) |
已耗时间:从 .IN 上升沿开始累加,单位与 .PT 一致,最大值为 T#24D_20H_31M_23S_647MS |
⚠️ 注意:.IN 是电平敏感而非边沿敏感——但 TON 内部自动检测上升沿启动计时,因此你只需保持 .IN 为 TRUE 即可持续计时;若 .IN 中途变 FALSE,.ET 立即归零,.Q 立即变 FALSE。
二、四步完成 TON 实例化(无错误写法)
1. 声明 TON 实例变量(全局或局部)
在程序组织单元(POU)的 VAR 区声明一个具名实例,不能直接调用 TON() 而不声明变量:
VAR
myTimer: TON; // ✅ 正确:声明一个名为 myTimer 的 TON 实例
// timer1: TON(); // ❌ 错误:语法非法,ST 不支持匿名实例化
END_VAR
💡 命名建议:用动词+功能命名,如
motorStartDelay、safetyGuardConfirm、conveyorStopHold,避免t1、timer_01等无意义名称。
2. 设置 .PT(预设时间)——必须使用标准 TIME 字面量
.PT 必须赋值为 TIME 类型常量,不可用整数、实数或字符串代替:
myTimer.PT := T#5S; // ✅ 5 秒
myTimer.PT := T#100MS; // ✅ 100 毫秒
myTimer.PT := T#2S_500MS; // ✅ 2.5 秒(支持复合格式)
myTimer.PT := T#0.1S; // ❌ 错误:ST 不支持小数点前缀写法,会编译失败
myTimer.PT := 5000; // ❌ 错误:5000 是 INT,非 TIME 类型,类型不匹配
myTimer.PT := 'T#5S'; // ❌ 错误:字符串无法隐式转 TIME
🔍 时间单位缩写表:
MS:毫秒(1 ms = 0.001 s)S:秒M:分钟H:小时D:天
全部区分大小写,ms或Sec均无效。
3. 连接 .IN(启动条件)——支持任意 BOOL 表达式
.IN 可接单个变量、逻辑运算结果或复杂判断,只要最终为 BOOL:
// 场景1:按钮按下启动延时
myTimer.IN := startButton AND NOT motorRunning;
// 场景2:温度超限后延时报警(防抖)
myTimer.IN := temperature > 85.0;
// 场景3:多条件串联(所有满足才启动)
myTimer.IN := (doorClosed = TRUE) AND (emergencyStop = FALSE) AND (modeAuto = TRUE);
// 场景4:复位后重新允许启动(典型启保停逻辑)
myTimer.IN := startCommand AND NOT myTimer.Q AND NOT faultActive;
✅ 关键点:.IN 在每次扫描周期都被读取;TON 内部自动识别 第一个扫描周期内由 FALSE → TRUE 的跳变 作为计时起点。
4. 调用实例(执行计算)——必须显式调用
声明和赋值完成后,必须在代码体中调用该实例,否则 .Q 和 .ET 不会更新:
// ✅ 正确:调用 myTimer 实例,触发内部逻辑
myTimer();
// ❌ 错误:仅赋值不调用 → 计时器永远静止
// myTimer.PT := T#3S;
// myTimer.IN := TRUE;
// (缺少 myTimer(); 这一行)
调用位置建议放在主程序循环末尾或独立定时器处理段,确保每周期执行一次。
三、完整可运行示例:电机启动延时保护
以下是一个工业现场真实可用的 ST 代码段,实现“按下启动按钮后延时 3 秒启动电机,期间任意松开按钮则取消”:
PROGRAM MotorControl
VAR
startButton: BOOL; // 硬件输入:启动按钮(常开)
motorRunning: BOOL; // 输出状态反馈(来自接触器辅助触点或驱动器反馈)
motorCmd: BOOL; // 电机控制输出(DO 点)
startDelay: TON; // 实例化延时定时器
END_VAR
// 步骤1:设置预设时间 → 3 秒
startDelay.PT := T#3S;
// 步骤2:设置启动条件 → 按钮按下 且 电机未运行
startDelay.IN := startButton AND NOT motorRunning;
// 步骤3:执行定时器逻辑(必须!)
startDelay();
// 步骤4:输出控制逻辑
// 当延时完成(.Q = TRUE)且无故障时,输出电机命令
motorCmd := startDelay.Q AND NOT overTemperature AND NOT driveFault;
// (可选)复位后清除延时状态:无需手动操作,TON 在 .IN = FALSE 时自动复位
🔍 执行时序详解(假设扫描周期 10 ms):
| 扫描周期 | startButton |
startDelay.IN |
startDelay.Q |
startDelay.ET |
说明 |
|---|---|---|---|---|---|
| 1 | TRUE |
TRUE |
FALSE |
T#0MS |
检测到上升沿,开始计时 |
| 2–299 | TRUE |
TRUE |
FALSE |
T#10MS → T#2980MS |
每周期累加 10 ms |
| 300 | TRUE |
TRUE |
TRUE |
T#3000MS |
.ET == .PT,.Q 置 TRUE |
| 301+ | TRUE |
TRUE |
TRUE |
T#3000MS(锁存) |
.ET 停止增长,.Q 保持 TRUE |
若第 150 周期 startButton 变 FALSE |
→ IN=FALSE |
.Q=FALSE, .ET=0 |
立即复位 |
✅ 验证技巧:在监控表(Watch Table)中添加
startDelay.Q和startDelay.ET,手动切换startButton,观察.ET是否从 0 开始线性增长,.Q是否在目标时间点准确翻转。
四、常见错误及修正对照表
| 错误现象 | 错误代码片段 | 原因分析 | 正确写法 |
|---|---|---|---|
| 定时器不动作 | TON().IN := TRUE; |
未声明实例变量,试图匿名调用 | delay1: TON; → delay1.IN := TRUE; delay1(); |
| 编译报错 “type mismatch” | timer.PT := 5000; |
将整数赋给 TIME 类型 |
timer.PT := T#5000MS; 或 T#5S |
| 延时时间不准 | timer.PT := T#0.5S; |
小数点格式非法 | timer.PT := T#500MS; |
.Q 一闪即逝 |
timer.IN := buttonPulse; |
使用脉冲信号(单周期 TRUE),.IN 下一周期即 FALSE,导致 .ET 归零 |
改用自锁逻辑:timer.IN := buttonPulse OR timer.Q; 或外接置位信号 |
| 多个定时器互相干扰 | TON1(), TON2(); 但共用同一变量名 |
变量名重复或未分别声明 | delayHeat: TON; delayCool: TON; 分开声明并分别调用 |
五、进阶技巧:动态修改延时、级联延时、防抖滤波
▪ 动态修改 .PT(运行时调整)
只要在调用 ton() 之前 修改 .PT,新值立即生效(下次计时起用):
// 根据模式选择延时
IF modeHighSpeed THEN
conveyorDelay.PT := T#200MS;
ELSIF modeLowSpeed THEN
conveyorDelay.PT := T#800MS;
END_IF;
conveyorDelay.IN := productDetected;
conveyorDelay(); // 调用后按新 .PT 计时
▪ 两级延时(先等 2 秒,再等 5 秒)
利用前一级 .Q 触发下一级 .IN:
stage1: TON;
stage2: TON;
stage1.PT := T#2S;
stage1.IN := processStart;
stage1();
stage2.PT := T#5S;
stage2.IN := stage1.Q; // stage1 完成后启动 stage2
stage2();
▪ 按钮消抖(100 ms 硬件滤波)
debounceBtn: TON;
debounceBtn.PT := T#100MS;
debounceBtn.IN := rawButtonInput; // 直接接硬件信号
debounceBtn();
cleanButton := debounceBtn.Q; // 仅当稳定按下 ≥100ms 才输出
六、TON 与 TOF / TP 的关键区别(避免误用)
| 功能块 | 触发方式 | 复位方式 | 典型用途 | .Q 变 TRUE 条件 |
|---|---|---|---|---|
TON |
.IN 上升沿启动 |
.IN = FALSE |
启动延时、就绪确认 | .ET >= .PT |
TOF(Off-Delay) |
.IN 下降沿启动 |
.IN = TRUE |
停机延时、缓冲关闭 | .IN 变 FALSE 后 .ET >= .PT |
TP(Pulse Timer) |
.IN 上升沿启动 |
.ET >= .PT 后自动复位 |
生成固定宽度脉冲 | .IN = TRUE 且 .ET < .PT(即启动后整个时间段内为 TRUE) |
⚠️ 切勿用
TON实现“断电延时”——应选TOF;切勿用TON生成脉冲——应选TP。
七、性能与资源注意事项
- 每个 TON 实例占用约 12–16 字节内存(取决于平台),无上限限制,但建议单个 POU 不超过 200 个以保障扫描周期。
.ET为 64 位有符号整数(单位:毫秒),最大计时T#24D_20H_31M_23S_647MS,超出将溢出归零(极罕见)。- 所有 TON 共享 PLC 系统时钟,不受扫描周期波动影响(即使扫描周期从 10 ms 波动到 50 ms,延时精度仍为 ±10 ms)。
八、调试黄金法则(3 分钟定位问题)
- 看
.IN:用强制功能(Force)将.IN设为TRUE,观察.ET是否从 0 开始增长; - 查
.PT:确认值为合法TIME类型,且数值合理(如T#0S会导致.Q瞬时置位); - 验调用:检查是否遗漏
instance();行,这是 70% 的“不动作”问题根源; - 盯
.Q:.Q为TRUE后,.IN变FALSE会立刻使其变FALSE—— 若需保持,需外加自锁逻辑。

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