西门子PLC的字符串格式化输出是将数值、时间、状态等信息转换为可读文本的核心技术,广泛应用于HMI显示、日志记录、通信报文构建等场景。本文基于S7-1200/1500系列,从基础指令到工程实战,完整拆解实现方法。
一、核心指令:S7-1200/1500的字符串处理基础
1.1 字符串数据类型结构
西门子PLC的字符串由最大长度、当前长度和字符数组三部分组成。以 String[20] 为例:
| 字节偏移 | 内容 | 说明 |
|---|---|---|
| 0 | 20 | 最大允许字符数(声明时固定) |
| 1 | 实际长度 | 当前存储的有效字符数 |
| 2~21 | 字符数据 | ASCII码存储,每个字符占1字节 |
关键认知:修改字符串时,PLC自动维护字节1的当前长度值,无需手动计算。
1.2 必备指令清单
| 指令名称 | 功能 | 典型应用场景 |
|---|---|---|
Strg_VAL |
字符串转数值 | 解析扫码枪、仪表返回的ASCII数据 |
Val_STRG |
数值转字符串 | 将温度、压力等过程值格式化为显示文本 |
Strg_Len |
获取字符串长度 | 计算报文总长度、校验累加 |
Concat |
字符串拼接 | 组合多字段报文(如"温度:25.5℃") |
Left/Right/Mid |
截取子串 | 提取设备地址、指令代码 |
Replace |
替换子串 | 模板化生成相似格式的多组报文 |
Find |
搜索子串位置 | 解析变长协议中的关键字定位 |
二、Val_STRG 深度解析:数值到字符串的格式化
Val_STRG 是实现格式化输出的核心指令,支持整数、浮点数的多种格式控制。
2.1 引脚功能详解
IN:待转换的数值(支持INT/DINT/REAL等)
SIZE:输出字符串的总宽度(含符号、小数点、前导零/空格)
PREC:小数位数(仅对REAL有效,整数自动忽略)
FORMAT:格式控制字(16位,决定对齐、进制、符号等)
OUT:目标字符串变量
2.2 FORMAT控制字位定义
| 位 | 名称 | 功能 |
|---|---|---|
| 0~3 | 对齐方式 | 0=右对齐左补空, 1=左对齐右补空, 2=右对齐左补零 |
| 4~7 | 符号处理 | 0=负数加负号正数不加, 1=始终显示符号 |
| 8~11 | 进制选择 | 0=十进制, 1=十六进制, 2=八进制 |
| 12~15 | 保留 | 必须为0 |
常用组合计算:
- 右对齐左补零 + 正负都显符号 + 十进制 =
2 + (1<<4) + 0=18(十六进制16#0012) - 左对齐 + 默认符号 + 十进制 =
1(十六进制16#0001)
2.3 典型调用示例
场景A:温度值格式化(右对齐左补零,保留1位小数)
// 假设:当前温度25.5℃,需格式化为"0025.5"(7字符宽)
"Temp_Real" := 25.5;
"FormatCode" := 16#0002; // 右对齐左补零
Val_STRG(
IN := "Temp_Real",
SIZE := 7,
PREC := 1,
FORMAT := "FormatCode",
OUT => "TempString"
);
// 结果:TempString = "0025.5 "(注意末尾可能有1个空格补足)
场景B:设备编号左对齐(无符号整数)
// 设备号1234,格式化为"1234 "(8字符左对齐)
"DeviceID" := 1234;
"FormatCode" := 16#0001; // 左对齐
Val_STRG(
IN := "DeviceID",
SIZE := 8,
PREC := 0,
FORMAT := "FormatCode",
OUT => "IDString"
);
// 结果:IDString = "1234 "
场景C:十六进制地址显示
// 内存地址2047,显示为"07FF"(4字符大写十六进制)
"MemAddr" := 16#07FF;
"FormatCode" := 16#0200; // 十六进制模式(位8~11=2)
Val_STRG(
IN := "MemAddr",
SIZE := 4,
PREC := 0,
FORMAT := "FormatCode",
OUT => "HexString"
);
// 结果:HexString = "07FF"
三、复杂格式化:多字段组合与动态模板
实际工程中,单条 Val_STRG 往往不够,需要指令组合实现复杂格式。
3.1 固定格式报文生成
目标格式:T:25.5|P:1.02|F:1560(温度、压力、流量三字段)
// 步骤1:分别转换三个数值
Val_STRG(IN:="Temp", SIZE:=4, PREC:=1, FORMAT:=16#0002, OUT=>"StrTemp");
Val_STRG(IN:="Press", SIZE:=4, PREC:=2, FORMAT:=16#0002, OUT=>"StrPress");
Val_STRG(IN:="Flow", SIZE:=4, PREC:=0, FORMAT:=16#0002, OUT=>"StrFlow");
// 步骤2:拼接前缀和数值
Concat(IN1:="T:", IN2:="StrTemp", OUT=>"Part1");
Concat(IN1:="|P:", IN2:="StrPress", OUT=>"Part2");
Concat(IN1:="|F:", IN2:="StrFlow", OUT=>"Part3");
// 步骤3:最终组合
Concat(IN1:="Part1", IN2:="Part2", OUT=>"TempCombine");
Concat(IN1:="TempCombine", IN2:="Part3", OUT=>"FinalMsg");
3.2 动态宽度与条件格式
当数值范围变化大时,需动态计算SIZE避免截断或过度补零。
// 自动适应整数位数(以DINT为例)
IF "TargetValue" >= 100000 THEN
"CalcSize" := 7;
ELSIF "TargetValue" >= 10000 THEN
"CalcSize" := 6;
ELSIF "TargetValue" >= 1000 THEN
"CalcSize" := 5;
ELSIF "TargetValue" >= 100 THEN
"CalcSize" := 4;
ELSIF "TargetValue" >= 10 THEN
"CalcSize" := 3;
ELSE
"CalcSize" := 2; // 至少保留1位数字+可能的符号
END_IF;
Val_STRG(
IN := "TargetValue",
SIZE := "CalcSize",
PREC := 0,
FORMAT := 16#0000, // 右对齐左补空格
OUT => "AdaptiveStr"
);
3.3 时间戳格式化(DATE_AND_TIME转字符串)
标准时间类型需先拆分为组件,再分别格式化。
// 读取系统时间并拆解
"LocalTime" := READ_RTC(); // 或 NTP 同步后的时间
// 提取各时间分量(使用IEC功能块)
YEAR := "LocalTime".YEAR;
MONTH := "LocalTime".MONTH;
DAY := "LocalTime".DAY;
HOUR := "LocalTime".HOUR;
MINUTE := "LocalTime".MINUTE;
SECOND := "LocalTime".SECOND;
// 格式化为 "2024-01-15 08:30:45"
Val_STRG(IN:=YEAR, SIZE:=4, PREC:=0, FORMAT:=16#0002, OUT=>"StrYear");
Val_STRG(IN:=MONTH, SIZE:=2, PREC:=0, FORMAT:=16#0002, OUT=>"StrMon");
Val_STRG(IN:=DAY, SIZE:=2, PREC:=0, FORMAT:=16#0002, OUT=>"StrDay");
Val_STRG(IN:=HOUR, SIZE:=2, PREC:=0, FORMAT:=16#0002, OUT=>"StrHour");
Val_STRG(IN:=MINUTE, SIZE:=2, PREC:=0, FORMAT:=16#0002, OUT=>"StrMin");
Val_STRG(IN:=SECOND, SIZE:=2, PREC:=0, FORMAT:=16#0002, OUT=>"StrSec");
// 逐步拼接(为清晰分多步,实际可优化)
Concat(IN1:="StrYear", IN2:="-", OUT=>"TS1");
Concat(IN1:="TS1", IN2:="StrMon", OUT=>"TS2");
Concat(IN1:="TS2", IN2:="-", OUT=>"TS3");
Concat(IN1:="TS3", IN2:="StrDay", OUT=>"TS4");
Concat(IN1:="TS4", IN2:=" ", OUT=>"TS5");
Concat(IN1:="TS5", IN2:="StrHour", OUT=>"TS6");
Concat(IN1:="TS6", IN2:=":", OUT=>"TS7");
Concat(IN1:="TS7", IN2:="StrMin", OUT=>"TS8");
Concat(IN1:="TS8", IN2:=":", OUT=>"TS9");
Concat(IN1:="TS9", IN2:="StrSec", OUT=>"TimeStampStr");
四、高级技巧:数组循环与批量处理
4.1 批量设备状态上报
假设10台设备,每台需生成 "Dev01:RUN,T:45.2" 格式报告。
FOR #i := 0 TO 9 DO
// 计算设备编号字符串(01~10)
"TempDevNum" := #i + 1;
Val_STRG(IN:="TempDevNum", SIZE:=2, PREC:=0, FORMAT:=16#0002, OUT=>"DevNumStr");
// 获取该设备状态(假设从DB数组读取)
"DevStatus" := "DeviceDB".Status[#i]; // 0=STOP, 1=RUN, 2=ALARM
"DevTemp" := "DeviceDB".Temperature[#i];
// 状态码转字符串
CASE "DevStatus" OF
0: "StatusStr" := "STOP";
1: "StatusStr" := "RUN ";
2: "StatusStr" := "ALARM";
ELSE
"StatusStr" := "ERR ";
END_CASE;
// 格式化温度
Val_STRG(IN:="DevTemp", SIZE:=4, PREC:=1, FORMAT:=16#0002, OUT=>"TempStr");
// 组合单条记录:Dev01:RUN ,T:45.2
Concat(IN1:="Dev", IN2:="DevNumStr", OUT=>"LineStart");
Concat(IN1:="LineStart", IN2:=":", OUT=>"Line1");
Concat(IN1:="Line1", IN2:="StatusStr", OUT=>"Line2");
Concat(IN1:="Line2", IN2:=",T:", OUT=>"Line3");
Concat(IN1:="Line3", IN2:="TempStr", OUT=>"DeviceReport"[#i]);
// DeviceReport为String[20]数组,存储10条记录
END_FOR;
4.2 变长JSON风格报文构建
现代物联网通信常需JSON格式,需处理引号、逗号等特殊字符。
// 目标:{"temp":25.5,"alarm":false,"msg":"normal"}
// 注意:JSON中的双引号在PLC字符串中用两个双引号表示(转义)
// 数值部分
Val_STRG(IN:="SensorTemp", SIZE:=4, PREC:=1, FORMAT:=16#0000, OUT=>"JsonTemp");
Val_STRG(IN:="AlarmCount", SIZE:=3, PREC:=0, FORMAT:=16#0000, OUT=>"JsonAlarm");
// 构建组件(使用常量字符串)
// 常量定义:"开头大括号、字段名等
"{""temp"":"" 存储在 "JsonPartA"(实际内容为 {"temp":)
"","alarm":"" 存储在 "JsonPartB"
",""msg"":""" 存储在 "JsonPartC"
"""}"" 存储在 "JsonPartD"(实际内容为 "})
// 拼接流程
Concat(IN1:="JsonPartA", IN2:="JsonTemp", OUT=>"J1");
Concat(IN1:="J1", IN2:="JsonPartB", OUT=>"J2");
Concat(IN1:="J2", IN2:="JsonAlarm", OUT=>"J3");
Concat(IN1:="J3", IN2:="JsonPartC", OUT=>"J4");
// 处理动态消息文本(假设"MsgText"已赋值如"normal")
Concat(IN1:="J4", IN2:="MsgText", OUT=>"J5");
Concat(IN1:="J5", IN2:="JsonPartD", OUT=>"FinalJson");
关键提示:S7-1200/1500的字符串常量中,双引号用连续两个表示(""),这是IEC 61131-3标准的转义规则。
五、调试与常见问题
5.1 典型故障排查
| 现象 | 原因 | 解决 |
|---|---|---|
| 输出字符串为乱码 | 目标变量长度声明不足 | 检查 String[XX] 的XX是否大于SIZE参数 |
数值被截断显示##### |
SIZE小于实际数字位数 | 增加 SIZE或改用左对齐模式 |
| 浮点数精度丢失 | REAL类型仅6~7位有效数字 | 对高精度需求改用LREAL+String[40] |
| 中文显示异常 | PLC字符串仅支持ASCII | 中文需用Unicode编码或转交HMI处理 |
| 拼接后长度超限 | 中间结果超过String声明长度 | 拆分为多次短拼接,或使用更大容量String |
5.2 在线调试技巧
- 强制监控:在博图软件的监控表中,将字符串变量格式设为"字符串"而非默认的"十六进制",直观查看结果。
- 长度验证:同时监控字符串变量的第1字节(当前长度),确认与预期一致。
- 分步测试:复杂组合逻辑建议用临时中间变量存储每步结果,逐步验证。
六、S7-1500增强功能(与S7-1200差异)
| 功能 | S7-1200 | S7-1500 |
|---|---|---|
| 字符串最大长度 | 254字节 | 16382字节(String类型) |
| 宽字符支持 | 不支持原生WString处理 |
支持WString(Unicode,每字符2字节) |
S7String直接操作 |
需转换 | 可直接处理SIMATIC字符串格式 |
| 性能 | 基础指令集 | 新增AT覆盖、Variant指针等高效操作 |
S7-1500专用:使用VARIANT实现泛型格式化
// 通过VARIANT指针,同一段代码处理不同类型
"AnyToString"(
value := "InputVariant", // 可指向INT/REAL/DINT等任意类型
format := "%08.3f", // 类C语言格式描述符
result => "OutputString"
);
七、完整工程代码模板
以下为一个可直接导入的功能块 FB_StringFormatter,封装常用格式化模式。
FUNCTION_BLOCK "FB_StringFormatter"
VAR_INPUT
Enable : Bool; // 使能信号
Mode : Byte; // 0=标准数值, 1=时间戳, 2=设备状态
ValueReal : Real; // 浮点输入
ValueDInt : DInt; // 整数输入
Precision : Byte := 1; // 小数位(Mode=0时有效)
TotalWidth : Byte := 8; // 总宽度
END_VAR
VAR_OUTPUT
Done : Bool;
ResultStr : String[30];
END_VAR
VAR_TEMP
TempStr1, TempStr2 : String[15];
FormatCode : Word;
Year, Mon, Day, Hour, Min, Sec : Int;
END_VAR
CASE #Mode OF
0: // 标准数值模式
IF ABS(#ValueReal) >= 100000.0 THEN
// 大数用科学计数或调整宽度
#FormatCode := 16#0002; // 右对齐左补零
ELSE
#FormatCode := 16#0000; // 右对齐左补空格
END_IF;
Val_STRG(
IN := #ValueReal,
SIZE := UINT_TO_INT(#TotalWidth),
PREC := UINT_TO_INT(#Precision),
FORMAT := #FormatCode,
OUT => #ResultStr
);
#Done := TRUE;
1: // 时间戳模式(ValueDInt作为Unix时间或专用结构)
// 简化示例:假设ValueDInt分解为YYMMDDhhmmss
#Year := DINT_TO_INT(#ValueDInt / 10000000000);
#Mon := DINT_TO_INT((#ValueDInt / 100000000) MOD 100);
#Day := DINT_TO_INT((#ValueDInt / 1000000) MOD 100);
// ... 类似提取时分秒,然后按3.3节拼接
#ResultStr := "TimeMode_TODO"; // 实际需完整实现
#Done := TRUE;
2: // 设备状态模式
Val_STRG(IN:=#ValueDInt, SIZE:=3, PREC:=0, FORMAT:=16#0002, OUT=>#TempStr1);
Concat(IN1:="DEV", IN2:=#TempStr1, OUT=>#TempStr2);
// 根据ValueReal判断状态(0=STOP, 1=RUN)
IF #ValueReal > 0.5 THEN
Concat(IN1:=#TempStr2, IN2:=":RUN ", OUT=>#ResultStr);
ELSE
Concat(IN1:=#TempStr2, IN2:=":STOP", OUT=>#ResultStr);
END_IF;
#Done := TRUE;
ELSE
#ResultStr := "MODE_ERR";
#Done := FALSE;
END_CASE;
END_FUNCTION_BLOCK
调用示例:
// 格式化当前转速1560.5 RPM,8字符宽,1位小数
"Formatter".Enable := TRUE;
"Formatter".Mode := 0;
"Formatter".ValueReal := "MotorSpeed";
"Formatter".Precision := 1;
"Formatter".TotalWidth := 8;
// 结果:" 1560.5"(左补两个空格)
掌握上述方法后,可应对西门子PLC中绝大多数字符串格式化需求。核心要点在于:理解String数据结构、熟练运用Val_STRG的FORMAT控制、合理拆分复杂格式为多步简单操作。

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