ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

ST语言定时器实战:TON/TOF原理、案例与调试技巧全拆解

2026/9/28 7:17:22 拓冰建站 浏览量
ST语言定时器实战:TON/TOF原理、案例与调试技巧全拆解 1. 为什么定时器逻辑我建议你用ST语言重写一遍干了这么多年PLC项目我自己的习惯是凡是涉及定时器的控制逻辑能用ST写就尽量用ST写。不是梯形图不好而是TON/TOF这类功能块放进ST里逻辑的可读性、复用性和批量修改效率完全不一样。很多同行一听到ST语言就发怵觉得那是搞软件开发的工程师才需要碰的东西实际上在定时器这个层面ST语言的门槛比想象中低得多核心就是理解TON和TOF两个功能块的行为再加上几个实战场景的锻炼。你可能正在用梯形图写程序也可能刚接触三菱或者西门子的ST语言不知道从哪里下手。本文就围绕ST语言定时器实战展开把TON/TOF的工作原理、实操案例和踩坑细节一次性讲透。无论你是在改老设备程序还是新项目刚起步只要涉及到延时启动、故障延时报警、循环闪烁、设备运行状态判断这些常见需求把这篇内容看明白定时器这块基本就能直接上手了。很多人觉得ST语言能做的事梯形图都能做。这话不假但ST的优势不在于能不能做而在于做得有多快、多稳。举个最实在的例子一个产线上有8台设备每台都需要独立的启动延时和停机延时。梯形图里你要复制8个定时器块改定时器编号改内部地址改注释稍不留神就改错一个。ST语言里你只需要定义一个定时器功能块数组一个FOR循环就搞定了。关键是整个逻辑一眼就能看完别人接手你的程序时也不会头疼。再说一个更隐蔽的点ST语言天然支持结构化的数据类型和时间字面量。像T#5s、T#500ms这种写法比梯形图里的K值常量直观得多。K5到底是0.5秒还是5秒还得去查型号手册T#5s就写在代码里谁看谁懂。这也是我为什么大力推荐做设备维护和调试的同行也学一点ST语言的原因你不一定要用它写整机程序但在排查定时器相关故障时看得懂ST代码能帮你省一半时间。接下来的内容没有废话直接从TON和TOF的内部机制开始讲然后用三个能直接抄进项目里的实战案例做演示最后把我在现场调试时踩过的坑一次性说清楚。这些坑如果你自己摸索可能要花一个礼拜才能搞明白这里直接帮你把效率拉满。2. TON和TOF的工作机制花5分钟彻底吃透定时器功能块看起来简单其实很多工程师在细节上理解得并不准确。我见过有人把TON和TOF混着用结果设备动作时序全乱了。所以先把原理掰开揉碎地讲明白后面写代码才不会翻车。2.1 TON接通延时定时器输入持续有效才开始走秒TON的全称是Timer On-Delay接通延时定时器。它的行为可以概括为一句话输入信号从断开变接通后开始计时计时时间到达预设值后输出才置为接通如果计时过程中输入断开了计时立即清零输出保持断开。听上去挺抽象我常用一个生活化的类比来解释TON就像家里的闹钟你按下按钮那一刻闹钟开始走秒5分钟后响铃如果从按下到5分钟之间你又按了一次取消键闹钟就归零了不会再响。对应到PLC里输入就是那个按钮输出就是响铃。用ST语言写一个TON的标准调用长这样// 实例化一个定时器 myTon : TON; // 程序段中调用扫描执行 myTon(IN : bStartSignal, // 输入信号 PT : T#5s); // 预设时间5秒 bDelayDone : myTon.Q; // 输出结果 tElapsed : myTon.ET; // 当前累计时间调试用注意这里有个容易被忽略的细节TON并不是输入一边接通就马上开始计时的它要检测到输入信号的上升沿才会启动计时。也就是说输入从0变成1的那一瞬间内部才开始走ET。如果程序扫描时输入一直是1没有从0变1的过程TON不会在第一个扫描周期就把Q置位。这个特性在实际应用中有个好处——你可以放心地让TON的输入持续为真它不会误动作只有信号变化时才会重新触发。2.2 TOF断开延时定时器输出断开反而开始计时TOF的全称是Timer Off-Delay断开延时定时器。它的行为和TON正好相反输入信号一接通输出立即接通输入断开后输出保持接通状态同时开始计时计时时间到达预设值后输出才断开。继续用生活类比TOF就像办公室走廊的感应灯。你走进走廊那一刻灯就亮了等你离开后感应器检测不到人灯不会马上熄灭而是保持继续亮30秒时间到了才关掉。ST语言的完整调用方式如下myTof : TOF; myTof(IN : bRunSignal, // 运行信号 PT : T#10s); // 延时断开时间10秒 bKeepAlive : myTof.Q; // 输出断开后仍保持10秒TOF有一个关键点在计时过程中也就是Q还保持接通时如果输入信号重新变成1TOF会立刻停止计时ET清零Q保持接通。这个行为在设备短暂中断但不想触发报警的场景下非常实用相当于给信号波动做了一层缓冲。2.3 四个引脚的关系一张表看清TON和TOF共用一套接口理解这四个参数的角色是正确使用的关键。引脚类型含义典型使用场景IN布尔输入触发定时器的输入信号按钮、传感器、接触器反馈PT时间输入预设时间值T#5s、T#100ms、TIME变量Q布尔输出定时器动作后的输出状态控制输出回路、触发报警ET时间输出当前已累计/剩余时间人机界面显示、调试监视PT参数建议不要用易失的M寄存器或内部变量去赋值因为一旦程序被改写或PLC断电这些值可能丢失或处于未知状态导致定时时间变成预期之外的数。最好在程序里直接用T#字面量或者由HMI设定的参数通过专用的保持性变量去控制这样调机时也方便观察。还有个进阶用法TON和TOF可以互相嵌套组合产生更复杂的时序逻辑。比如用TON的输出再作为TOF的输入或者两个定时器并联产生一个脉冲信号。这个在后面的实战案例里会提到。3. 三个可以直接抄进项目的TON/TOF实战写法原理理解了关键还是要落到应用里。下面这三个案例都是我在实际项目里反复用到的场景代码可以直接复制下来改改就用。3.1 星三角降压启动用TON搭出可靠的电机切换时序星三角降压启动是电机控制里的经典电路热搜里也有人专门问星-角降压启动主回路电路图请编写PLC控制星角降压启动的梯形图程序。基本逻辑是启动瞬间电机绕组先接成星形降低启动电流延时几秒后切换成三角形接法让电机进入全压运行。这里的延时切换就是TON最典型的应用场景。用ST语言实现时可以这样写// 定时器实例定义 tonStart : TON; // 切换延时 // 梯形图等价逻辑 - ST实现 bContactorMain : bStartCmd; // 主接触器按启动立即吸合 tonStart(IN : bStartCmd, PT : T#6s); // 6秒后允许切换到角接 bContactorStar : bStartCmd AND NOT tonStart.Q; // 星接触器启动后接通切换前断开 bContactorDelta : bStartCmd AND tonStart.Q; // 角接触器6秒后接通这段逻辑的精妙之处在于星形和角形两个接触器永远不会同时导通因为它们的条件在逻辑上是互斥的。bContactorStar需要tonStart.Q为0bContactorDelta需要tonStart.Q为1两个条件不可能同时成立。这里必须提醒一个硬件层面的问题星角切换瞬间即使PLC程序里两个接触器没有同时输出从星形断开到角形闭合之间仍然需要留出接触器的物理分断时间。常见做法是给星形接触器加一个OFF延时或者用TON的Q非输出配合额外的短延时。我实际项目里的做法是让星形输出断开后经过200ms再闭合角形这段间隔可以用另一个TON实现tonStarOff : TON; tonStarOff(IN : NOT tonStart.Q, PT : T#200ms); bContactorDelta : bStartCmd AND tonStarOff.Q;注意这个写法和三菱梯形图里T0、T1直接复位线圈的方式是等价的但ST的写法能让你更清晰地看到时间节点。如果你用的是三菱PLCST语法照样支持TON功能块只是注意定时器实例不能和梯形图的定时器编号冲突。3.2 断料/卡料实时检测TOF在运行状态监控里的正确打开方式设备正常运行时会持续给PLC反馈脉冲信号或者保持信号。如果设备中途卡料、断带或者异常停机这个反馈信号会消失。直接用常开点判断行不行不行因为现场信号难免有干扰或者设备短暂停滞一下就恢复。TOF在这个场景下的作用是反馈信号正常时输出一直为1反馈中断后输出保持一段时间时间到了才给出设备异常信号。实现代码如下tonAlarmDelay : TOF; tonAlarmDelay(IN : bFeedbackSignal, PT : T#3s); bAlarm : NOT tonAlarmDelay.Q; // Q为0时触发报警这段逻辑的意思是bFeedbackSignal正常为1时Q保持为1bAlarm为0不报警反馈信号中断后Q不会马上变0只要3秒内反馈恢复Q仍然是1程序不做报警处理只有反馈信号持续中断超过3秒Q才变为0bAlarm变为1这时候才报警。这个3秒缓冲的价值在于现场很多干扰和机械抖动都在毫秒或一两秒级别直接报警会导致频繁误报停机操作工被折腾得不行。加大到3秒缓冲后既能过滤掉短暂干扰又不会因为时间太长而让故障扩大。具体时长要根据设备特性调输送带上料位检测我一般取2到3秒需要安全联锁的取0.5秒以内。3.3 循环闪烁控制两个定时器配合实现设备状态指示设备运行指示灯、报警灯、蜂鸣器经常需要闪烁效果。梯形图里实现闪烁通常是两个定时器互锁或者用特殊辅助继电器。ST语言里用TON加自身输出反馈同样能实现而且代码更紧凑。经典双TON闪烁实现周期2秒亮1秒灭1秒tonFlashOn : TON; tonFlashOff : TON; tonFlashOn(IN : NOT tonFlashOff.Q, PT : T#1s); tonFlashOff(IN : tonFlashOn.Q, PT : T#1s); bFlash : tonFlashOn.Q;初始状态下tonFlashOff.Q为0NOT后为1tonFlashOn开始计时1秒后Q变为1tonFlashOn.Q为1NOT tonFlashOff.Q仍是1因为tonFlashOff还没计时完所以tonFlashOn继续保持1直到tonFlashOff计时到1秒后Q变为1NOT后变成0tonFlashOn的IN断开Q复位为0然后tonFlashOff的IN变成0它自己也复位Q回到0整个循环重新开始。这个写法原则上不要再用额外的M继电器去暂存中间状态输出直接用tonFlashOn.Q就行干净利落。如果想让闪烁周期不一样改PT参数就行了亮1.5秒灭0.5秒这样的非对称闪烁也照样实现。4. 现场调试中定时器最容易翻车的六个细节代码能跑起来只是第一步现场不出问题才算真正的会。定时器这块我踩过的坑不少挑六个最典型的拿出来说每一条都能在关键时刻救你一命。4.1 定时器变量掉电保持问题设备重启后逻辑全乱很多PLC的TON功能块实例默认不保持PLC断电重启后内部状态全部清零。如果你的控制逻辑依赖上次运行时定时器已经走了一半这种状态重启后会有问题。举一个我在立体仓库项目里遇到的实例设备因为安全门被打开而中途暂停此时一个10秒的定时器已经走过了6秒。操作工关好安全门重新上电PLC断电重启后定时器清零逻辑从头开始走。这本来问题不大问题在于定时器清零后触发了下一级逻辑的状态变化导致输送机在重启瞬间出现了一个短暂的错误输出差点把货架上的箱子顶歪。解决办法有两条路第一定时器时间到后把结果状态锁存到保持性变量里比如三菱的锁存继电器L或者西门子的保持性DB区后续逻辑只认锁存结果不认定时器的瞬时输出第二关键安全逻辑的定时器在断电重启后必须要求手动复位才能重新运行防止自动重启导致意外动作。我个人强烈建议安全回路相关定时器选第二条安全第一省不了这个事。4.2 别把ET当成实时时钟用扫描周期的影响比你想的大有一次调试时发现人机界面上显示的定时器剩余时间跳变非常不平滑一格一格地跳看着特别难受。排查到最后发现是程序里把ET值做了数值处理扫描周期又是几十毫秒导致显示刷新跟不上。这个现象背后的原理是TON/TOF的ET是在PLC的每个扫描周期结束时统一刷新的ET的最小变化单位是一个扫描周期的时间。扫描周期是10msET就按10ms步进增长扫描周期是50msET就按50ms步进。程序越庞大扫描周期越长定时器的精度就越差。所以有两个原则要记住第一需要精确计时的场景不要让程序扫描周期太长必要时把定时器相关逻辑放到中断任务里执行第二显示用或逻辑判断用时不要把ET直接和一个等于号做比较比如ET T#10s这种写法很容易因为扫描周期的误差漏匹配应该用ET T#10s。我见过工程师因为这个小细节写出来的定时器偶尔不动作查了一整天。4.3 同一个定时器功能块实例被多处调用结果不可预知这是新手最容易犯的问题。在梯形图里同一个定时器编号比如T0如果在两段程序里都被使用了三菱PLC会报警提示重复使用。但在ST语言里如果你习惯性地在多个程序段里写了myTon(IN:x1, PT:T#1s)然后又写myTon(IN:x2, PT:T#2s)编译器不一定会报错但运行时行为绝对是混乱的后一次调用会覆盖前一次调用的内部状态定时器的ET和Q都可能被重置。正确的做法是一个功能块实例对应一个独立的定时任务。如果有不同的延时需求分别定义不同的实例名比如tonStart、tonAlarm、tonBlink。我见过一个程序里为了节省变量空间反复复用一个定时器实例的结果逻辑时序完全对不上排查起来极其痛苦。定时器功能块本质上不占多少内存该开几个开几个千万别省这个。4.4 PT类型写错编译通过却永远不动作ST语言的TIME类型字面量有固定格式T#5s表示5秒T#500ms表示500毫秒T#1m30s表示1分30秒。问题在于有些工程师习惯了梯形图的K值写法在ST里直接写PT : 5这种情况下编译器会把它当成整数类型和TIME类型不匹配大概率编译报错但如果你刚好定义了一个整数变量编译器可能自动做了隐式转换运行后定时时间完全不是想要的5秒。我的建议是所有PT参数统一用T#字面量或者定义成TIME类型的变量由HMI参数写入。另外T#后面的大小写和单位写法在不同品牌PLC里略有差异三菱ST支持T#5S/T#5s西门子SCL同样兼容但如果你把代码移植到别的平台最好先查一下对应手册避免单位缩写不认。4.5 复位优先级的差异TON和TOF的复位行为不一样TON的复位行为是IN断开ET清零Q立即变0这是大家都知道的。但TOF的复位行为容易被忽略——TOF的IN断开后即使计时没结束如果你给TOF的实例做了一个R复位操作比如调用了Reset方法Q会立刻变0ET也清零。这个行为在某些需要急停立即断输出的场景下面是对的但在另一些场景下会带来风险。举个例子设备急停按下安全回路要求所有输出立即断开这时候即使你用了TOF保持信号也必须让急停信号优先复位TOF。但如果只是普通停机按钮我建议保留TOF的延时过程让设备完成一个完整的收尾动作比如副辊下降到位这时候就不要急停复位TOF了。这个逻辑差异需要在设计阶段就明确否则遇到现场突发情况很容易做出危险动作。4.6 仿真环境和实机的定时差异别等上电才发现ST语言写的定时器逻辑在PLC仿真软件里跑得挺好现场一上电却发现时间不对这种情况我也遇到过。原因主要有两个一是仿真环境通常扫描周期很短1ms级别而实机背负着大量通讯和运动控制扫描周期可能到50ms甚至更高定时精度自然不一样二是仿真环境的输入信号变化是手动给的电平跳变沿非常干净而现场信号有抖动需要滤波处理否则定时器会被反复触发。针对这种情况我的习惯是仿真阶段跑通逻辑就够验证顺序和互锁关系用但不要在仿真环境里卡着PT时间做精度验证现场调试阶段先强制信号测试单个定时器的实际时间确认扫描周期造成的误差在可接受范围内再联调整体逻辑。5. 进阶组合当定时器遇到IF语句、边沿检测和变频器启停会写单个定时器只是入门真正的价值在于把定时器和其他逻辑组合起来。这一节讲几个进阶玩法也是很多项目里实际会碰到的。5.1 三菱ST里IF语句和定时器的经典配合三菱ST语言的IF语句格式跟很多编程语言类似但要注意它和PLC扫描执行的特点结合。比如这样一段逻辑手动模式下电机可以随时启停自动模式下电机必须在启动信号持续3秒后才运行如果反馈信号在启动后5秒内没有到位就要报警停机。这种多状态判断逻辑用IF加TON写起来非常有条理// 三菱ST风格写法 IF bManualMode THEN bMotorRun : bManualStart; myTonManu(IN : FALSE); ELSE myTonAuto(IN : bAutoStart, PT : T#3s); bMotorRun : bAutoStart AND myTonAuto.Q; END_IF; IF bMotorRun AND NOT bMotorFeedback AND bStartCmd THEN myTonAlarm(IN : bStartCmd, PT : T#5s); bAlarmStop : myTonAlarm.Q; END_IF;注意这里IF的条件里出现了两个定时器它们的执行顺序和程序行号位置有关。三菱ST是自上而下扫描执行的所以一定要把定时器的调用放在使用它输出的逻辑之前否则同一个扫描周期里读到的是上一周期的旧值。这个顺序问题在梯形图里因为网络逐个扫描大家有直觉到了ST里新手容易当成高级语言来看忽略了扫描执行机制这是最容易出问题的地方。另外三菱ST里还可以直接用TON的实例变量来读取Q和ET比用功能块调用的OUT参数直观一些。但我个人习惯仍然用标准调用格式便于代码跨平台移植一旦从三菱的GX Works3迁到西门子的TIA Portal改动最小。5.2 边沿检测和TON配合只在信号瞬间触发一个定时器前面提到TON的IN需要上升沿才能启动计时但有时候你想让定时器只在一个瞬间比如说一个扫描周期内开始计时而不是随着输入一直保持。这个需求可以用上升沿检测指令配合实现。三菱ST里的写法是bRisingEdge : (bInput AND NOT bOldInput); // 检测上升沿 bOldInput : bInput; myTon(IN : bRisingEdge, PT : T#2s); bOutput : myTon.Q;这里有个关键技巧bRisingEdge只在bInput从0变1的那一个扫描周期为1之后立刻变回0。TON的IN收到一个上升沿后启动计时即使IN随即变回0TON也不会清零因为内部已经进入了计时状态。这正是TON启动一次走完全程的特性。类似的还有下降沿触发TOF可以用bFallingEdge : (NOT bInput AND bOldInput)来检测。这个方法在按钮只按了一下但要求设备运行一段固定时间的场景很实用。注意不要用三菱的上升沿脉冲指令PLS或者西门子的R_TRIG虽然它们也能做边沿检测但在这里直接用布尔运算可读性更高还能免去添加功能块的麻烦。5.3 多个定时器级联模拟变频器先确认后启动的时序热搜里有人问一台西门子PLC与32个变频器Modbus通讯控制是否可行这个问题涉及通讯负载和时序控制。用定时器做变频器启停的延时确认是常见需求PLC发启动指令后不是马上让变频器进入运行状态而是要等变频器反馈运行准备完成再经过一段延时确认通讯状态无误后才置运行位。用级联TON来模拟这个时序过程大致如下// 第一段启动指令保持 tonCmdHold(IN : bStartCmd, PT : T#1s); // 第二段等待变频器反馈反馈到位后再延时确认 tonFeedbackDelay(IN : tonCmdHold.Q AND bFreqReady, PT : T#500ms); // 第三段输出运行位 bFreqRun : tonFeedbackDelay.Q AND NOT bFault;这种级联方式比单个定时器加一堆中间标志位要清晰很多每个定时器负责一个独立的时间窗口而且每个窗口的起始条件都一目了然。调试时通过监控两个定时器的ET可以快速判断哪一段时序没走通。在32台变频器的场景下要注意的是变频器通讯本身有轮询周期即使通讯速度够快32台设备的读写在极端情况下也会达到几百毫秒甚至一秒多。所以延时时间设置要考虑到最坏情况下的通讯周期不能只看理论值。如果你用Modbus RTU串行通讯带32台变频器每台读写保持寄存器需要几十毫秒一轮轮询下来就要两三秒定时器的最小分辨率设到毫秒级没有意义。这时候我会把通讯超时判断放到独立的定时器里用500ms到1s级别的超时时间配合TOF过滤掉偶尔的通讯瞬时失败。另外一个实用的技巧是32台变频器用Modbus通讯做必要时可以在ST里用数组和循环来定义多个定时器实例而不是一个一个地实例化。比如定义一个TON数组每个元素对应一台变频器代码量直接少一个数量级而且将来新增设备只需要改数组大小和循环上限。这个方法在ST语言里实现起来非常顺畅梯形图里要做到批量就麻烦了。6. 调试定时器的三个现场小技巧最后分享几个现场调试的经验这些小习惯确实帮我省了不少时间。第一监控定时器状态时不要只看Q一定要看ET。ET是逐步增长的你能直观地看到定时器当前走到了哪一步。如果程序逻辑没反应先看ET有没有在涨没涨说明IN这个条件根本没通涨到一半停了说明IN中途断开了一下就能判断出问题方向。第二修改PD参数时尽量保持原逻辑不变。比如现场要求把延时从3秒改成5秒只改PT数值就够了不要顺手去改定时器的其他逻辑。定时器逻辑经常牵一发动全身旁边还挂了一堆互锁条件改动范围越小越安全。第三从一开始就统一所有定时器的命名规范。我一般用ton或tof前缀加上用途描述比如tonConveyorDelay、tofAlarmHold看名字就知道这个定时器是干什么的。命名规范的程序半年后自己回头改都省力更不要说同事接手了。定时器在PLC控制里看着简单但不是死记硬背指令格式的问题关键是理解它的内部特性和在时序里的位置。TON和TOF这一对功能块用好了设备启停、故障判断、状态切换这些最常见的控制需求基本都能稳稳拿捏。希望这篇ST语言定时器的实战拆解能帮你少走一段我当年走过的弯路。