ST怎么写时间差计算:Diff := Time_End - Time_Start; (需注意数据类型)

发布于 2026-03-15 05:22:33 · 浏览 2 次 · 评论 0 条

在结构化文本(ST)编程中,计算两个时间点之间的时间差是自动化控制系统中最基础也最易出错的操作之一。典型写法如 Diff := Time_End - Time_Start; 表面简洁,但若忽略数据类型匹配、时基单位、溢出边界与隐式转换规则,轻则结果偏差数秒,重则触发运行时错误或 PLC 停机。以下内容不讲理论,只列可立即验证、可逐行执行的实操步骤。


一、明确 ST 中时间数据类型的本质

PLC 的 ST 语言(IEC 61131-3 标准)中,时间值不是浮点数,也不是整数,而是带单位的结构化数据类型。常见时间类型有:

类型 含义 存储方式(典型) 示例赋值
TIME 相对时间间隔(时:分:秒.ms) 32位有符号整数(毫秒为单位) t#5s, t#1h2m300ms
DATE 日历日期(年-月-日) 32位整数(自1970-01-01起天数) d#2024-06-15
TIME_OF_DAY / TOD 当日时刻(时:分:秒.ms) 32位无符号整数(毫秒为单位) tod#14:30:00.500
DATE_AND_TIME / DT 日期+时刻完整时间戳 64位整数(微秒级精度) dt#2024-06-15-14:30:00.500

⚠️ 关键结论:

  • 只有 TIMETOD 类型支持直接相减,且结果仍为 TIME
  • DATE 相减得 INT(单位:天);
  • DT 相减得 TIME(单位:纳秒级,但实际精度取决于 PLC 硬件);
  • TIMETOD 不可混用相减(编译报错);
  • 所有时间字面量(如 t#5s)必须带单位前缀 t#d#tod#dt#

二、正确写出时间差计算的 4 个必要步骤

  1. 声明变量时显式指定类型,禁止依赖默认推导:

    VAR
        Time_Start : TIME;
        Time_End   : TIME;
        Diff       : TIME;  // 必须是 TIME,不是 INT 或 REAL
    END_VAR
  2. 确保两个时间变量已赋有效值(未初始化的 TIME 默认为 t#0s,但逻辑上可能不满足业务起点):
    触发采集动作(例如上升沿检测),执行 Time_Start := TON1.Q ? TON1.ET : Time_Start;
    触发结束条件(例如传感器到位),执行 Time_End := TON2.Q ? TON2.ET : Time_End;

    注:TON1.ET 是定时器经过时间,其类型恒为 TIME,可直接赋值。

  3. 执行减法前确认顺序与符号安全性
    判断 Time_End >= Time_Start 成立后,执行 Diff := Time_End - Time_Start;
    若无法保证顺序(如手动输入、HMI 修改),必须加保护

    IF Time_End >= Time_Start THEN
        Diff := Time_End - Time_Start;
    ELSE
        Diff := t#0s;  // 或触发报警:Alarm_TimeReverse := TRUE;
    END_IF
  4. 验证结果是否在 TIME 有效范围内
    TIME 类型最大值为 t#24d_20h_31m_23.647s(即 2^31−1 毫秒 ≈ 24.8 天)。若预期差值超此限:

    • 改用 DWORD 存储毫秒值
      Time_Start_ms : DWORD := UINT_TO_DWORD(ULINT_TO_UINT(TIME_TO_DT(Time_Start))); // 不推荐,精度损失
    • 更优方案:用 LREAL 存毫秒浮点值
      VAR
          Start_ms : LREAL;
          End_ms   : LREAL;
          Diff_ms  : LREAL;
      END_VAR
      Start_ms := TIME_TO_MS(Time_Start);  // IEC 61131-3 内置函数,返回毫秒数值
      End_ms   := TIME_TO_MS(Time_End);
      Diff_ms  := End_ms - Start_ms;

      TIME_TO_MS() 返回 LREAL,无溢出风险,支持高达 1e15 毫秒(约 31,688 年)。


三、避坑清单:9 个高频错误及修正方法

错误代码示例 错误原因 正确写法
Diff := 1000 - Time_Start; INTTIME 不能直接运算 Diff := t#1000ms - Time_Start;
Diff := TOD_END - TOD_START; TOD 类型名错误(应为 TOD_END Diff := TOD_END - TOD_START;(变量名需全大写或按项目规范)
Diff := Time_End + (-Time_Start); ST 不支持一元负号作用于 TIME 改用 IF 判断顺序,不强行套数学恒等式
Diff := INT_TO_TIME(1000); INT_TO_TIME 非标准函数(多数 PLC 不支持) t#1000ms 字面量或 TIME#1000MS(品牌语法差异)
Diff := Time_End - Time_Start * 1000; 乘法优先级导致 Time_Start*1000 先算(非法) 加括号:Diff := Time_End - (Time_Start * 1000); ❌ 仍错 → 改为 Diff := Time_End - t#1s;
Diff := ABS(Time_End - Time_Start); ABS() 不接受 TIME 参数 先转数值:ABS(TIME_TO_MS(Time_End) - TIME_TO_MS(Time_Start))
Diff := Time_End - Time_Start; // 结果为负 未检查时间顺序,负值会截断为最大正数 IF Time_End >= Time_Start THEN ... ELSE ... END_IF
Diff := T#5S - T#3S; T# 是旧版语法(部分 PLC 已弃用) 统一使用 t#5s(小写 t + 井号 + 小写单位)
Diff := Time_End - Time_Start; // 在 FB 初始化段 变量未初始化即运算,值为 t#0s FBENTRUE 后首次触发时赋初值

四、跨品牌实操对照表(主流 PLC)

不同厂商对时间函数的支持存在细微差异,以下为实测有效的写法(基于最新固件):

功能 Siemens S7-1200/1500 (TIA Portal) Rockwell Logix 5000 (Studio 5000) Beckhoff TwinCAT 3
获取当前运行时间 Tonic() : TIME(需启用系统时钟) GSV(“WallClock”, “DateTime”, …) → 提取 TimeOfDay GET_LOCALTIME()TOD
时间转毫秒数值 TIME_TO_MS(Time_Var) TOD_TO_DINT(TOD_Var) / 1000(微秒→毫秒) TOD_TO_LREAL(TOD_Var) / 1000.0
毫秒数值转 TIME MS_TO_TIME(LREAL_Var) DINT_TO_TIME(INT_Var * 1000)(需先×1000) LREAL_TO_TIME(LREAL_Var * 1000.0)
安全时间差计算宏 自定义 FC:输入 T1,T2,输出 TIMELREAL ms AOI 封装:含溢出检测与单位选择 FUNCTION_BLOCK F_TimeDiff(内置 LREAL 输出选项)

✅ 统一建议:所有新项目,一律使用 TIME_TO_MS() + LREAL 存储差值。它规避了 TIME 类型所有边界问题,且 LREAL 在 PLC 中普遍支持双精度(15 位有效数字),对毫秒级计时足够精确。


五、真实场景调试技巧(现场可用)

Diff := Time_End - Time_Start; 运行结果异常时,按以下顺序排查:

  1. 用在线监控看原始值

    • 在变量表中添加 Time_StartTime_EndDiff,设置显示格式为 HH:MM:SS.mmm
    • 触发一次流程,观察三者是否均为非零且符合逻辑顺序。
  2. 检查定时器 ET 是否被复位

    • Time_Start 来自 TONET,确认 TONIN 信号在采集后保持 TRUE 至少一个扫描周期,否则 ET 归零。
  3. 验证时间字面量单位

    • t#1s 表示 1 秒,t#1000ms 等价,但 t#1000 默认单位是毫秒(某些品牌),务必统一用带单位写法
  4. 抓取中间变量做断点

    Temp_Start := Time_Start;  // 在线监控此变量
    Temp_End   := Time_End;
    Diff := Temp_End - Temp_Start;

    避免编译器优化隐藏中间值。

  5. 用 HMI 强制写入测试边界值

    • 强制 Time_Start := t#24d_20h_31m_23s;,再设 Time_End := t#24d_20h_31m_24s;,观察 Diff 是否溢出(应为 t#1s,而非归零或负值)。

六、终极安全模板(可直接复制使用)

// ======== 声明区(全局或 FB 内)========
VAR
    Time_Start : TIME := t#0s;
    Time_End   : TIME := t#0s;
    Diff_ms    : LREAL := 0.0;  // 推荐:毫秒级浮点结果
    Is_Valid   : BOOL := FALSE;
END_VAR

// ======== 执行区(循环调用)========
// 假设 Start_Trigger 和 End_Trigger 为 BOOL 信号
IF Start_Trigger AND NOT Start_Trigger_LAST THEN
    Time_Start := TON_Start.ET;  // 或 GET_LOCALTIME()
END_IF;
Start_Trigger_LAST := Start_Trigger;

IF End_Trigger AND NOT End_Trigger_LAST THEN
    Time_End := TON_End.ET;
END_IF;
End_Trigger_LAST := End_Trigger;

// 安全计算(含顺序检查 + 溢出防护)
IF Time_End >= Time_Start THEN
    Diff_ms := TIME_TO_MS(Time_End) - TIME_TO_MS(Time_Start);
    Is_Valid := TRUE;
ELSE
    Diff_ms := 0.0;
    Is_Valid := FALSE;
END_IF;

此模板满足:类型明确、顺序防护、溢出免疫、结果可读、易于调试。

评论 (0)

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

扫一扫,手机查看

扫描上方二维码,在手机上查看本文