← 全部文章 工业通讯

西门子 PLC 通讯(一):数据在 DB 块里怎么存

2022 年 6 月 23 日 · 3 分钟读完

目录
  1. 先关掉「优化的块访问」
  2. String
  3. WString
  4. 举例:存「哇哈」
  5. 其他常用类型
  6. 延伸

研究上位机通讯时派生出来的支线:用 S7 协议读 PLC,本质上是「从 DB10 的第 26 个字节开始读 n 个字节」,读回来的是一串原始字节,怎么解释全靠自己。所以要先搞清楚每种类型在 DB 块里的样子。

先关掉「优化的块访问」

S7-1200 / 1500 新建的 DB 块默认勾选了优化的块访问,这时变量没有固定的偏移地址(DB 编辑器里看不到「偏移量」一列),PLC 自己安排存储位置,外部只能按符号名访问(OPC UA 之类)。

S7.Net 这类走 PUT/GET 的库只能按绝对地址读写,所以要:DB 块右键 → 属性 → 常规 → 属性 → 取消勾选「优化的块访问」,然后编译。之后 DB 编辑器里会出现「偏移量」一列,下面说的地址都以它为准。

String

S7-300 / 400 / 1200 / 1500 里,String 其实是一个字符数组,前面加两个字节的头:

字节 内容
0 最大长度(声明时的长度,默认 254)
1 当前实际长度
2 ~ 字符,每个 1 字节

头部占了 2 字节,整个 String 最多 256 字节,所以字符最多 254 个。

声明两个变量看看偏移:

  1. String:默认长度 254,占 256 字节(偏移 0 ~ 255,下一个变量从 256 开始);
  2. String[10]:长度 10,占 12 字节(偏移 256 ~ 267,下一个从 268 开始)。

所以读一个 String,正确的做法是先读第 1 个字节拿到实际长度 n,再从第 2 个字节开始读 n 个字节。直接读满 254 个字节,后面会带上上次留下的旧字符。

String 里的字符按单字节处理,写中文会乱码,要中文就用 WString。

WString

WString 是宽字符(WChar)数组,每个字符 2 字节,用 UTF-16 编码,可以存中文。结构和 String 类似,只是每一项都变成了 2 字节:

字节 内容
0 ~ 1 最大长度(WChar 的个数,不是字节数)
2 ~ 3 当前实际长度(WChar 的个数)
4 ~ 字符,每个 2 字节

声明两个变量看看偏移:

  1. WString:默认长度 254 个字符,占 254 × 2 + 2 × 2 = 512 字节(偏移 282 ~ 793,下一个从 794 开始);
  2. WString[10]:长度 10,占 10 × 2 + 2 × 2 = 24 字节(偏移 794 ~ 817)。

长度最大可以声明到 16382。

举例:存「哇哈」

最大长度 实际长度 哇 哈
值 254 2 U+54C7 U+54C8
内存里的字节 00 FE 00 02 54 C7 54 C8

西门子 PLC 是大端存储:数据的高位放在低地址。比如最大长度 254(0x00FE),高位 00 在 DBB282,低位 FE 在 DBB283。字符也一样,「哇」的 54 在前、C7 在后。

C# 的 Encoding.Unicode 是小端,会得到 C7 54,所以和 PLC 交换 WString 时必须用 Encoding.BigEndianUnicode。编码的细节见 字符编码:ASCII、GBK 与 Unicode。

其他常用类型

类型 字节数 说明 地址写法
Bool 1 位 8 个 Bool 共用一个字节 DB10.DBX0.0 ~ DBX0.7
Byte 1 无符号 0 ~ 255 DB10.DBB1
Word 2 无符号 16 位 DB10.DBW2
Int 2 有符号 16 位 DB10.DBW8
DWord 4 无符号 32 位 DB10.DBD4
DInt 4 有符号 32 位 DB10.DBD10
Real 4 IEEE 754 单精度 DB10.DBD14
LReal 8 IEEE 754 双精度(仅 1200 / 1500) 没有 8 字节的地址写法,按字节读
Char / WChar 1 / 2 单个字符

几个容易踩的点:

  • 大端字节序:西门子 PLC 是大端(高字节在前),x86 的 PC 是小端。Int 100 在 PLC 里是 00 64,PC 上直接 BitConverter.ToInt16 会得到 0x6400 = 25600。S7.Net 内部已经帮你翻转了,自己解析原始字节时要注意。
  • Word 和 Int 是同样的 2 个字节,区别只在怎么解释最高位:FF FF 当 Word 是 65535,当 Int 是 -1。原理见 原码、反码和补码。
  • Real 是浮点数:4 个字节按 IEEE 754 解释,不能当成整数读。见 浮点数是怎么存的。
  • 偶数对齐:非优化 DB 里,Bool 和 Byte 之后的 Word / Int / Real 等会从偶数字节开始,中间会空出一个字节。所以地址一定以 DB 编辑器里的「偏移量」为准,不要自己累加。

延伸

  • Struct 和数组:Struct 内部按上面的规则依次排列,整体也从偶数字节开始;Bool 数组按位紧密排列。要批量读一整段,可以按偏移一次读出一大块字节,再在 C# 里按偏移切开解析,比一个一个变量读快得多(每次读写都是一次网络往返)。
  • S7.Net 的 ReadClass / ReadStruct:定义一个字段顺序、类型和 DB 完全一致的 C# 类,可以一次读出整个 DB。字段对不齐时会错位,所以前提是 DB 结构稳定,而且不含 String。
  • 时间类型:Time 是 4 字节的毫秒数(DInt);Date 是 2 字节,表示从 1990-01-01 起的天数;DTL 是 12 字节的结构体(年 2 字节,月、日、星期、时、分、秒各 1 字节,纳秒 4 字节)。

参考:西门子 String 和 WString 的存储。

下一篇:没有真 PLC 时,怎么用 PLCSIM Advanced 仿真一台 1500。

# 西门子 # PLC # S7

原文首发于 blog.csdn.net