ST语言中处理模拟量信号时,NORM_X 和 SCALE_X 是两个最常用、也最容易填错参数顺序的功能块。它们本身不复杂,但一旦参数位置颠倒或数据类型错配,程序不会报错,却会导致输出值恒为0、跳变、偏移100%或完全反向——这类故障在调试现场极难定位,常被误判为硬件损坏或传感器故障。
以下内容仅围绕参数填写顺序这一具体易错点展开,不讲原理推导、不堆砌标准定义,只告诉你:在哪填、填什么、为什么这个顺序不能换、填错后现象是什么、如何一秒自检。
一、先看结论:两个功能块的参数顺序对比表
| 功能块 | 输入引脚(IN) | 最小输入值(MIN_IN) | 最大输入值(MAX_IN) | 最小输出值(MIN_OUT) | 最大输出值(MAX_OUT) | 输出引脚(OUT) |
|---|---|---|---|---|---|---|
NORM_X |
X |
X_MIN |
X_MAX |
Y_MIN |
Y_MAX |
Y |
SCALE_X |
IN |
IN_MIN |
IN_MAX |
OUT_MIN |
OUT_MAX |
OUT |
⚠️ 关键差异:
NORM_X的第一参数是X(待转换的原始值),紧接着才是X_MIN、X_MAX;SCALE_X的第一参数是IN(待转换的原始值),紧接着才是IN_MIN、IN_MAX;
表面看顺序一致?不——真正陷阱藏在命名暗示与实际调用习惯的错位中。
二、为什么你会填错?——命名误导 + 编辑器补全惯性
多数工程师写 NORM_X 时,凭直觉认为:“既然是‘归一化’,那应该先给范围,再给值”,于是写出:
NORM_X(
X_MIN := 4.0,
X_MAX := 20.0,
Y_MIN := 0.0,
Y_MAX := 100.0,
X := CurrentRaw, // ❌ 错!X 放最后,但X是主输入,必须放第一位
Y => NormValue
);
这段代码语法合法、编译通过、运行不报错,但 Y 永远输出 0.0。原因:NORM_X 是 位置绑定(positional binding)优先 的功能块。当未使用 := 显式指定参数名时,PLC严格按声明顺序匹配参数。
NORM_X 的标准声明(IEC 61131-3 标准库)为:
FUNCTION_BLOCK NORM_X
VAR_INPUT
X : REAL; // 第1位:原始输入值 → 必须是第一个实参
X_MIN : REAL; // 第2位:输入下限
X_MAX : REAL; // 第3位:输入上限
Y_MIN : REAL; // 第4位:输出下限
Y_MAX : REAL; // 第5位:输出上限
END_VAR
VAR_OUTPUT
Y : REAL; // 第6位:归一化结果
END_VAR
因此,若你写成:
NORM_X(CurrentRaw, 4.0, 20.0, 0.0, 100.0, Y => NormValue);
✅ 正确:5个输入按声明顺序依次填入,CurrentRaw 占第1位,Y 自动接收计算结果。
而若你写成(带名参数但顺序混乱):
NORM_X(
X_MIN := 4.0,
X := CurrentRaw,
X_MAX := 20.0,
Y_MIN := 0.0,
Y_MAX := 100.0,
Y => NormValue
);
❌ 仍错误:X_MIN := 4.0 是第1个实参 → PLC将其赋给 X(第1位变量),X 被强制设为 4.0;X := CurrentRaw 是第2个实参 → 赋给 X_MIN(第2位变量),X_MIN 变成 CurrentRaw 的值;后续全部错位。最终 X 固定为 4.0,X_MIN 变成传感器值,逻辑彻底崩坏。
✅ 正确带名写法(推荐用于复杂项目):
NORM_X( X := CurrentRaw, X_MIN := 4.0, X_MAX := 20.0, Y_MIN := 0.0, Y_MAX := 100.0, Y => NormValue );
注意:所有 := 左侧名称必须与声明中 VAR_INPUT 名称完全一致(大小写敏感),且顺序可任意——因为此时是“名称绑定(named binding)”。
三、SCALE_X 的陷阱更隐蔽:IN/OUT 命名让你放松警惕
SCALE_X 声明为:
FUNCTION_BLOCK SCALE_X
VAR_INPUT
IN : REAL; // 第1位
IN_MIN : REAL; // 第2位
IN_MAX : REAL; // 第3位
OUT_MIN : REAL; // 第4位
OUT_MAX : REAL; // 第5位
END_VAR
VAR_OUTPUT
OUT : REAL; // 第6位
END_VAR
看似 IN 开头很清晰?问题出在:工程师常把 SCALE_X 当作“缩放器”,下意识认为“先输范围,再输值”,尤其当从 HMI 或 Excel 复制参数时,习惯按「输入范围→输出范围→当前值」排列:
// ❌ 典型错误:把当前值 CurrentRaw 放最后,以为是“输入值”
SCALE_X(4.0, 20.0, 0.0, 100.0, CurrentRaw, OUT => ScaledVal);
这行代码中:
4.0→ 赋给IN(第1位)→IN = 4.020.0→ 赋给IN_MIN(第2位)→IN_MIN = 20.00.0→ 赋给IN_MAX(第3位)→IN_MAX = 0.0100.0→ 赋给OUT_MIN(第4位)→OUT_MIN = 100.0CurrentRaw→ 赋给OUT_MAX(第5位)→OUT_MAX = CurrentRawOUT => ScaledVal→ 第6位,正常
结果:IN_MIN > IN_MAX(20.0 > 0.0),SCALE_X 内部计算时直接返回 OUT_MIN(即 100.0),ScaledVal 恒为 100.0,且不报警。
✅ 正确位置绑定写法:
SCALE_X(CurrentRaw, 4.0, 20.0, 0.0, 100.0, OUT => ScaledVal);
✅ 正确带名写法(强烈建议):
SCALE_X(
IN := CurrentRaw,
IN_MIN := 4.0,
IN_MAX := 20.0,
OUT_MIN := 0.0,
OUT_MAX := 100.0,
OUT => ScaledVal
);
四、填错后的四大典型现象及速查法
| 现象 | 可能原因 | 速查动作 |
|---|---|---|
Y 或 OUT 恒为 0.0 |
X 或 IN 位置错填为 X_MIN/IN_MIN,导致主输入被固定为极小值(如 0.0 或 4.0) |
查看该功能块调用处,确认第一个实参是否为你的实时变量(如 AI[0].Value) |
Y 或 OUT 恒为 100.0(或 OUT_MAX 值) |
IN_MIN 与 IN_MAX 颠倒,或 X_MIN > X_MAX,触发边界保护返回 OUT_MAX |
检查第2、第3个实参数值:是否 IN_MIN > IN_MAX?如 20.0, 4.0 就是错的 |
Y 或 OUT 随输入线性变化但全程偏移(如 4–20mA 对应 0–100,实测却为 5–105) |
Y_MIN/OUT_MIN 或 Y_MAX/OUT_MAX 填反(如把 OUT_MIN 当 OUT_MAX 填) |
检查第4、第5个实参:OUT_MIN 是否真比 OUT_MAX 小? |
Y 或 OUT 值随输入反向变化(输入增大,输出减小) |
IN_MIN 与 IN_MAX 数值正确但位置互换(如 IN_MIN := 20.0; IN_MAX := 4.0) |
在线监控 IN_MIN 和 IN_MAX 的实际值,确认大小关系 |
💡 速查口诀:“一值二下三上,四下五上六出”
- 第1位:必须是你读到的实时原始值(
AI[0].Value,PIW100,CurrentRaw)- 第2位:该信号的物理量下限(如
4.0,0.0,-10.0)- 第3位:该信号的物理量上限(如
20.0,100.0,+10.0)- 第4位:你要映射的目标下限(如
0.0,0.0,0.0)- 第5位:你要映射的目标上限(如
100.0,1.0,32767.0)- 第6位:
Y => ...或OUT => ...
五、实战防错三招(立即生效)
1. 统一用带名参数 + 按声明顺序书写(杜绝位置依赖)
// ✅ 推荐模板(复制即用)
NORM_X(
X := AI_4_20mA,
X_MIN := 4.0,
X_MAX := 20.0,
Y_MIN := 0.0,
Y_MAX := 100.0,
Y => FlowPercent
);
SCALE_X(
IN := PT100_Raw,
IN_MIN := 100.0, // PT100 0°C = 100Ω
IN_MAX := 138.5, // PT100 100°C = 138.5Ω
OUT_MIN := 0.0,
OUT_MAX := 100.0,
OUT => TempPercent
);
2. 在变量声明区加注释,锁定物理意义
VAR
AI_4_20mA : REAL; // 4–20mA电流信号,经AD转换,范围0–32767 → 但NORM_X中填4.0/20.0!
FlowPercent : REAL; // 归一化后0.0–100.0,供HMI显示
PT100_Raw : REAL; // PT100电阻值(Ω),非温度值!
TempPercent : REAL; // 温度百分比,0°C=0%,100°C=100%
END_VAR
⚠️ 注意:
NORM_X/SCALE_X的X_MIN/IN_MIN等,必须填信号的工程单位值(4.0 mA, 100.0 Ω),不是AD码值(0, 32767)。AD码换算应在前级完成,或用SCALE(整型版)处理。
3. 用临时变量做“范围检查”,运行期捕获错配
// 在主程序循环中插入(调试期启用,上线前删除)
IF AI_4_20mA < 3.9 OR AI_4_20mA > 20.1 THEN
// 触发报警或置位诊断位
Fault_4_20mA_OutOfRange := TRUE;
END_IF;
// 检查参数合理性(防止烧写错误配置)
IF 4.0 > 20.0 THEN // 永假,但编译器会警告;真实项目中用变量
// 这里永远不执行,但提醒你:X_MIN 必须 < X_MAX
END_IF;
六、附:常见信号工程单位对照表(直接填入 MIN/MAX)
| 传感器类型 | 信号形式 | X_MIN | X_MAX | 典型Y_MIN/Y_MAX | 备注 |
|---|---|---|---|---|---|
| 4–20mA 电流 | 电流 | 4.0 |
20.0 |
0.0, 100.0 |
填 mA 单位值,非电压或AD码 |
| 0–10V 电压 | 电压 | 0.0 |
10.0 |
0.0, 100.0 |
同上,单位 V |
| PT100 热电阻 | 电阻 | 100.0 |
138.5 |
0.0, 100.0 |
0°C=100Ω,100°C≈138.5Ω |
| K型热电偶 | mV(查表后) | 0.0 |
41.27 |
0.0, 1372.0 |
0–1372°C对应0–41.27mV(需冷端补偿) |
| Modbus RTU 浮点数 | 工程值 | 0.0 |
1000.0 |
0.0, 1000.0 |
若已为工程单位,SCALE_X 可跳过,直接用 LIMIT 限幅 |
✅ 记住:
X_MIN/X_MAX是你期望该通道接收到的物理量合理范围,不是传感器极限值,也不是AD极限值。
七、终极验证:用一个已知值手动验算
选一个确定值代入,心算结果是否合理。例如:
- 输入
AI_4_20mA = 12.0mA NORM_X(X:=12.0, X_MIN:=4.0, X_MAX:=20.0, Y_MIN:=0.0, Y_MAX:=100.0)- 手算:
(12.0 − 4.0) / (20.0 − 4.0) × (100.0 − 0.0) + 0.0 = 8.0 / 16.0 × 100 = 50.0 - 在线监控
Y是否为50.0?是 → 参数正确;否 → 检查第1~5位实参顺序。
此法10秒完成,比翻手册、查库、重编译快10倍。
不要背公式,只记顺序:一值二下三上,四下五上六出。

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