文章目录
- 前言
- 一、功能概述
- 二、程序编写
-
- [1. 组态](#1. 组态)
- [2. 新建FB块"FB617_SYSTIME"](#2. 新建FB块“FB617_SYSTIME”)
- [3. 编程](#3. 编程)
-
- [1. DTL类型](#1. DTL类型)
- [2. Date_And_Time类型](#2. Date_And_Time类型)
- [3. Date_And_Time类型变量拆分](#3. Date_And_Time类型变量拆分)
- 三、数据类型对比
- 总结
前言
西门子博途编程平台中,可以使用RD_LOC_T指令读取本地时间。调用该指令时,其输出的变量可选择DTL或Date_And_Time数据类型。
本文将对RD_LOC_T指令的使用方法进行阐述,并详细对比DTL和Date_And_Time数据类型在程序处理中的区别,同时对字节和整型数据类型之间的转换方法进行讲解。
一、功能概述
- 读取本地时间;
- 读取DTL类型变量中的年、月、日、星期、时、分、秒等数据;
- 读取Date_And_Time类型变量中的年、月、日、星期、时、分、秒等数据。
二、程序编写
1. 组态
打开设备视图,选择CPU的属性-时间,将时区改为(UTC+08:00)北京时间。
2. 新建FB块"FB617_SYSTIME"
本程序仅作为实验验证,用于阐述DTL和Date_And_Time数据类型在编程上的区别。
建立如下引脚变量:

图1
其中,变量"SystemTimeByte"仅作为"SystemTimeDT"的拆分,不占用存储空间。使用AT指令,将其拆分为8个字节,用于读取不同字节的数据。AT指令的使用方法,详见文章《十二、SCL核心应用课项目实战_运行设备累计功能实现》。
"TempByte"作为"SystemTimeByte"数组的映射,仅用于DB块监控。
3. 编程
1. DTL类型

图2
首先使用DTL类型变量,下载程序,打开自动生成的背景数据块并监控,如图3所示。

图3
可以看到,DTL类型的变量占用12个字节,输出的值包含以下数据。若想使用某个数据,直接在程序中调用对应变量即可。
c
Year: WORD; // 年 (1-9999)
Month: BYTE; // 月 (1-12)
Day: BYTE; // 日 (1-31)
Weekday: BYTE; // 星期 (1=周日, 7=周六)
Hour: BYTE; // 时 (0-23)
Minute: BYTE; // 分 (0-59)
Second: BYTE; // 秒 (0-59)
Nanosecond: DWORD; // 纳秒 (0-999,999,999)
2. Date_And_Time类型
同理,将DTL类型变量替换为Date_And_Time类型变量,再次下载程序,监控DB块数据,如图5所示。

图4

图5
可以看到,DT类型的变量占用8个字节,其中每个字节都以 BCD 码形式存储日期时间的各个部分。相较于DTL类型变量,无法直接读取相应的年、月、日、星期、时、分、秒等数据,需要通过编写程序进行拆分。
3. Date_And_Time类型变量拆分
如图1所示,我们已将Date_And_Time类型变量拆分成了8个Byte类型变量组成的数组。对应字节存储以下信息:
c
字节0-1: 年(后两位,范围1990-2089)
字节2: 月
字节3: 日
字节4: 小时
字节5: 分钟
字节6: 秒
字节7: 星期(高4位为0,低4位表示星期几,其中1表示周日,2表示周一,...,7表示周六)
已知,星期数据存储在最后一个字节(即第 8 个字节,索引为 7)的低4位中,而对于其他字节(比如年份、月份等),每个字节存储两个BCD数字(高4位和低4位各代表一个数字)。故数据提取思路如下:
- 将DT转换为字节数组,以便访问每个字节,使用AT指令实现;
- 对于星期数据,将#SystemTimeByte[7]与16#0F进行"与"运算;
- 具体原理:16#0F是十六进制数,转换为二进制是0000 1111,当用一个字节(8位)的数据和16#0F进行AND运算时,高4位会与0000相与,结果一定是0;低4位与1111相与,结果保持原来的值,故可以得到存储星期信息的低4位字节数据;
- 而对于其它数据,需要分别提取高4位和低4位,然后组合起来;
- 具体方法:对于每个字节,拆分为高4位和低4位,高4位定义为字节为0,低4位定义为字节1,分别提取字节0和字节1的低4位数据(与16#0F进行AND操作);
- 其中,高4位数据,使用"右移位"指令将其移至低4位,然后与16#0F进行AND操作;
- 最后,将字节0的低4位乘以10,加上字节1的低4位,得到实际的两位数数据;
- 注意:年数据只有年份信息,没有世纪信息,如果要加上世纪信息,需要进一步编程处理,具体方法见代码。
代码如下:
c
REGION 提取年份
// Date_And_Time只支持1990-2089年
// 如果提取的两位数≥90:年份 = 1900 + 两位数
// 如果提取的两位数<90:年份 = 2000 + 两位数
#TempTens := (SHR(IN := #SystemTimeByte[0], N := 4)) AND 16#0F;
#TempOnes := #SystemTimeByte[0] AND 16#0F;
#Year := (#TempTens * 10) + #TempOnes;
IF #Year >= 90 THEN
#Year := 1900 + #Year;
ELSE
#Year := 2000 + #Year;
END_IF;
END_REGION
REGION 提取月份
#TempTens := (SHR(IN := #SystemTimeByte[1], N := 4)) AND 16#0F;
#TempOnes := #SystemTimeByte[1] AND 16#0F;
#Month := (#TempTens * 10) + #TempOnes;
END_REGION
REGION 提取日期
#TempTens := (SHR(IN := #SystemTimeByte[2], N := 4)) AND 16#0F;
#TempOnes := #SystemTimeByte[2] AND 16#0F;
#Day := (#TempTens * 10) + #TempOnes;
END_REGION
REGION 提取小时
#TempTens := (SHR(IN := #SystemTimeByte[3], N := 4)) AND 16#0F;
#TempOnes := #SystemTimeByte[3] AND 16#0F;
#Hour := (#TempTens * 10) + #TempOnes;
END_REGION
REGION 提取分钟
#TempTens := (SHR(IN := #SystemTimeByte[4], N := 4)) AND 16#0F;
#TempOnes := #SystemTimeByte[4] AND 16#0F;
#Minute := (#TempTens * 10) + #TempOnes;
END_REGION
REGION 提取秒钟
#TempTens := (SHR(IN := #SystemTimeByte[5], N := 4)) AND 16#0F;
#TempOnes := #SystemTimeByte[5] AND 16#0F;
#Second := (#TempTens * 10) + #TempOnes;
END_REGION
REGION 提取星期
// 星期信息存储在最后一个字节(即第 8 个字节,索引为 7)的低4位中
// 1表示周日,2表示周一,...,7表示周六
#Weekday := BYTE_TO_INT(#SystemTimeByte[7] AND 16#0F);
END_REGION
三、数据类型对比
| 特性 | DTL | Date_And_Time(DT) |
|---|---|---|
| 适用版本 | S7-1200/1500 新数据类 | S7-300/400 传统类型 |
| 字节大小 | 12字节 | 8字节 |
| 时间精度 | 纳秒 | 秒 |
| 日期范围 | 1-01-01 到 9999-12-31 | 1990-01-01 到 2089-12-31 |
| 存储方式 | 结构化数据 | BCD编码的紧凑格式 |
| 兼容性 | 仅S7-1200/1500 | S7-300/400/1200/1500 |
总的来说,DTL类型适用于:
S7-1200/1500新项目;
需要高精度时间戳;
需要大范围日期(跨越世纪);
需要结构化访问日期时间各部分。
Date_And_Time类型适用于:兼容旧项目(S7-300/400);
与旧系统通信;
只需要秒级精度;
节省存储空间。
总结
本文主要对博途RD_LOC_T指令的使用方法进行了讲解,同时详细对比分析了DTL和Date_And_Time数据类型的优缺点。其中,Date_And_Time类型读取时间信息时涉及到的字节转换算法,需要格外注意。