PLC 中时间日期数据的运算处理
在工业控制中,记录设备运行时长、统计班次产量或生成报警时间戳,都离不开时间日期数据的处理。许多工程师在处理 TIME 和 DATE 类型时容易混淆,导致数据溢出或显示错误。本指南直接拆解核心步骤,帮你搞定 PLC 内的时间运算。
1. 认清核心数据类型
不同品牌的 PLC 对时间的定义略有差异,但主流标准(如 IEC 61131-3)基本一致。在编写程序前,确认 当前硬件支持的数据格式。
| 数据类型 | 英文标识 | 存储范围 | 典型用途 |
|---|---|---|---|
| 时间 | TIME |
毫秒级,约 24 天 | 定时器设定、短时延时 |
| 长时间 | LTIME |
毫秒级,约 292 年 | 累计运行时间、长周期统计 |
| 日期 | DATE |
年月日 | 日历功能、班次日期 |
| 日期时间 | DATE_AND_TIME |
年月日时分秒毫秒 | 报警时间戳、数据记录 |
| 长日期时间 | LDT |
高精度年月日时分秒 | 高速事件排序、精准同步 |
注意 TIME 类型的上限。若累计运行时间超过 24 天,TIME 类型会溢出归零。对于电机寿命统计等场景,强制使用 LTIME 或 DInt 自行计数。
2. 获取 PLC 系统当前时间
大多数现代 PLC 内置了系统时钟函数。以西门子 S7-1200/1500 为例,使用 GET_TOD 指令获取时间。
- 打开 程序编辑器,定位到主循环组织块
Main [OB1]。 - 拖入 系统函数
GET_TOD到网络段中。 - 连接 输出引脚
TOD到一个DATE_AND_TIME类型的全局变量,例如Global_Time。 - 启用 该指令,确保每个扫描周期或按需触发更新。
若使用三菱或欧姆龙 PLC,查阅 手册找到对应的特殊寄存器(如 D8013 等),通常需要通过指令将分散的年、月、日寄存器合并。
3. 时间数据格式转换流程
HMI 显示屏通常无法直接解析 TIME 或 DATE_AND_TIME 类型,需要转换为字符串 String 或整型 Int。以下流程图展示了标准转换逻辑。
执行 转换操作时,调用 标准库函数。在 TIA Portal 中,使用 CONV 指令或 T_CONV 功能块。
- 创建 一个
String类型变量HMI_Time_Show。 - 插入 指令
T_CONV(Time to String)。 - 设置 输入引脚
IN为Global_Time。 - 指定 输出引脚
OUT为HMI_Time_Show。 - 配置 格式参数,选择
YYYY-MM-DD hh:mm:ss模板。
4. 时间间隔与时长计算
计算两个时间点之间的差值是常见需求,例如计算设备单次工作时长。核心逻辑是将时间统一转换为毫秒数进行减法运算。
假设启动时间为 T_Start,停止时间为 T_Stop,时长 $T_{duration}$ 的计算公式如下:
$$T_{duration} = T_{Stop} - T_{Start}$$
在 PLC 程序中,遵循 以下步骤实现:
- 定义 三个
LTIME类型变量:Start_Time,Stop_Time,Run_Duration。 - 捕捉 启动信号上升沿,执行
GET_TOD并将结果存入Start_Time。 - 捕捉 停止信号上升沿,执行
GET_TOD并将结果存入Stop_Time。 - 编写 减法逻辑,计算
Run_Duration=Stop_Time-Start_Time。 - 转换
Run_Duration为DInt类型,单位通常为毫秒。 - 除以
1000得到 秒数,或 除以3600000得到 小时数。
若需计算总运行时间(累计值),避免 直接使用 TIME 累加。采用 DInt 计数器方案:
// 每秒中断程序或定时器中断
IF "Timer_1s".Q THEN
// 设备运行标志位为真时累加
IF "Device_Running" THEN
"Total_Run_Seconds" := "Total_Run_Seconds" + 1;
END_IF;
END_IF;
// 转换为小时显示
"Total_Run_Hours" := REAL("Total_Run_Seconds") / 3600.0;
保存 Total_Run_Seconds 到非易失性存储区(如 Retain 数据块或断电保持寄存器),防止断电后数据丢失。
5. 常见错误与避坑指南
时间处理看似简单,实则暗藏陷阱。以下表格列出了高频故障点及解决方案。
| 故障现象 | 可能原因 | 解决方案 |
|---|---|---|
| 时间显示乱码 | 数据类型不匹配 | 检查 HMI 变量类型是否与 PLC 一致,统一转换为 String |
| 累计时间归零 | 数据类型溢出 | 更换 TIME 为 LTIME 或使用 DInt 手动累加秒数 |
| 跨天计算错误 | 日期未参与运算 | 使用 DATE_AND_TIME 而非仅 TIME_OF_DAY,确保包含日期信息 |
| 时区不一致 | 服务器与 PLC 时区不同 | 统一 所有设备为 UTC 时间或在 HMI 层做时区偏移补偿 |
处理 跨天逻辑时,务必包含日期部分。若仅使用 TIME_OF_DAY,当晚 23:59 到次日 00:01 会被计算为负值。
验证 程序逻辑的正确性:
- 修改 系统时间到接近午夜时刻(如 23:59:50)。
- 模拟 设备运行跨越零点。
- 监控 时长计算变量,确认 数值连续增加而非跳变。
6. 实战:报警时间戳记录
当设备发生故障时,需要记录确切的报警发生时间。这需要结合边沿检测与时间抓取。
- 定义 一个结构体
Alarm_Log,包含Alarm_ID(Int) 和Alarm_Time(DATE_AND_TIME)。 - 创建 一个数组
Alarm_Buffer[1..10]用于存储最近 10 条报警。 - 监测 故障信号
Fault_Signal的上升沿。 - 触发 时间读取指令,获取 当前
DATE_AND_TIME。 - 写入 当前时间到
Alarm_Buffer的当前索引位置。 - 递增 索引指针,若超过 10 则 复位 为 1 实现循环覆盖。
以下是具体的 SCL 代码片段:
// 报警记录逻辑
IF "Fault_Signal" AND NOT "Fault_Signal_Pre" THEN
// 获取当前时间
"Current_Alarm_Time" := GET_TOD();
// 存入缓冲区
"Alarm_Buffer"[#Index].Time := "Current_Alarm_Time";
"Alarm_Buffer"[#Index].ID := "Fault_Code";
// 更新索引
#Index := #Index + 1;
IF #Index > 10 THEN
#Index := 1;
END_IF;
END_IF;
// 更新信号状态
"Fault_Signal_Pre" := "Fault_Signal";
下载 程序至 PLC 并 触发 一次故障信号。
查看 数据块 Alarm_Buffer 的内容。
确认 Time 字段记录了触发瞬间的系统时间,且格式正确。
对比 HMI 显示的时间戳,确保无误。

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