Tricon功能块速查:引脚类型、STDLIB与标定时序避坑

发布时间:2026/11/5 4:22:13
Tricon功能块速查:引脚类型、STDLIB与标定时序避坑

简介:《tricon功能块》是一份面向工业自动化初学者与Tricon控制系统入门者的技术说明文档,围绕功能块编程中的数据类型、标准函数、标准功能块及符号含义展开,帮助读者建立对数据处理与控制逻辑的基础认识。资源包仅含1个doc文件,大小约237KB,内容集中,便于快速查阅和留存。文档梳理了BOOL、INT、REAL、TIME等数据类型,并说明数据类型转换、数学运算、比较选择、字符串处理等标准编程函数,以及双稳态、边沿检测、计数器和定时器等功能块;同时对输入输出符号与内部信号符号作了对应解释,并涉及STDLIB常用功能说明和TCXLIB模拟量标定。已有431人学习,适合在阅读程序、查引脚含义或搭建基础控制逻辑时作为速查参考。

1. 一份功能块速查手册:从引脚符号到标准库的完整对照

组态软件里从库中拖出一个块,左边一列引脚,右边一列引脚,名字都是三四个字母:w、a、d、c、y、v,类型还混着 DINT、REAL、BOOL——这是很多人第一次翻开 Tricon 功能块说明时的场景。它并不是什么新框架,而是一份把标准库函数逐条列清楚的手册:左输入右输出,每个引脚后面标注吃哪种数据类型,STDLIB 里从 ABS 到 XOR 的功能说明,TCXLIB 里的模拟量标定,再加上 RS/SR、边沿、计数、定时这些双稳态与时序块。它解决的是“这个引脚该接什么、接错了会怎样”这类非常具体的问题,适合刚接手项目对着引脚发愣的新手,也适合需要快速确认某个块的入参语义、不想翻几百页资料的老手。

2. 引脚与数据类型:DINT 和 REAL 之间隔着一条标定链

2.1 数据类型不是装饰,它标记的是信号走到了哪一段

这份说明里列了十几种数据类型,看着像语言规范,实际上每一个都对应现场的一段物理链路。BOOL 是开关量,只有通断两态;DINT 是双整型,在这个库里通常承载 AD 采样出来的原始码值;REAL 是实数,承载换算之后的工程量;TIME 是时间段,专门喂给定时器的 PT 引脚;TOD 和 DT 一个管时间点一个管日期时间组合,靠 CONCAT 拼起来。把这张表记住的意义不在于背名字,而在于看到某个引脚标着 DINT 时,你能立刻反应过来“这里还是原始码,没标定过”。

数据类型承载内容常见误用
BOOL数字量、开关量,只有 0/1拿去做算术,隐式转成 0/1 参与加减
DINT双整型,模拟量原始码直接和工程量比较,差出几个数量级
REAL实数,标定后的工程量累加次数过多后出现尾数漂移
INT整型,范围比 DINT 窄计数器溢出后被截断成负数
TIME时间段,如 10s用整数毫秒赋值,单位对不上
TOD / DATE / DT时间点、日期、日期时间CONCAT 的两个入参类型接反
WORD / DWORD字型、双字型,位串操作载体和整型混用做位运算,符号位被误读
STRING字符型,主要用于字符串特性函数长度不足被静默截断
LREAL长实数,精度高于 REAL在只支持 REAL 的运算里被降级

真正容易翻车的是 DINT 和 REAL 这一对。DINT 是整数域,REAL 是实数域,中间那道坎叫标定,由 TCXLIB 负责。很多初学者的第一反应是把 DINT 直接接到 REAL 引脚上,编译器不报错,因为存在隐式转换,程序照跑,但数值全错——原始码可能是 0 到 4095,工程量可能是 0 到 100.0,接错了就是几十倍的偏差,还会被当成“传感器坏了”排查半天。

2.2 I/O 符号:w/a/v/y 表示链路位置,而非数据类型

说明里那组小写字母很有价值:w 是未标定的模拟量输入(DINT),a 是定标模拟输入(REAL),v 是未标定的模拟量输出(DINT),y 是定标模拟输出(REAL),d 是数字量输入(BOOL),c 是数字量输出(BOOL),t 是温度输入(DINT),p 是脉冲输入(REAL)。这八个符号本质上是给信号贴“段位标签”:w 和 a 成对,v 和 y 成对,前者是原始域,后者是工程域,中间夹着标定环节。

符号含义数据类型链路位置
w未标定模拟量输入DINTAD 采样后、标定前
a定标模拟输入REAL标定后、参与逻辑运算
v未标定模拟量输出DINT逻辑运算后、DA 前
y定标模拟输出REAL送 DA 之前的工程量
d数字量输入BOOL开关量采集
c数字量输出BOOL开关量输出
t温度输入DINT温度专用通道原始值
p脉冲输入REAL频率/计数类信号

看懂这套符号之后,读别人写的程序会快很多。看到运算区里出现 w 开头的变量,就知道这个值还没标定,后面必然有一步转换;看到 a 开头的变量参与了比较和限幅,说明这段逻辑工作在工程域,可以直接对着工艺参数核对。这种命名约定比任何注释都可靠,因为它把“我有没有漏掉标定”这件事变成了肉眼可查的问题。

2.3 内部信号符号:能不能写,决定了它能不能当中间量

内部信号表里是另一套小写字母:r 是内部模拟量(REAL),i 是内部模拟量(DINT),e 是内部读写模拟量,f 是内部自读数字量,g 是内部读/写数字量,k 是固定常数(REAL/BOOL),t 是时间常数(TIME),m 是报警信号(BOOL)。这组符号的关键差别在“可写性”上:标了“读写”的 e 和 g 可以两边用,标了“自读”的 f 只能读,往只读量上写在某些组态环境里不报错但值会被覆盖,查起来非常难受。

符号含义类型可写性
r内部模拟量REAL可作中间量
i内部模拟量DINT可作中间量
e内部读写模拟量—读/写
f内部自读数字量BOOL只读
g内部读/写数字量BOOL读/写
k固定常数REAL/BOOL常量,不应被赋值
t时间常数TIME常量,不应被赋值
m报警信号BOOL状态位

这里有个容易忽略的细节:t 在两个表里出现了两次,在 I/O 表里是温度输入(DINT),在内部信号表里是时间常数(TIME)。同一个字母,靠位置区分语义,光看变量名首字母判断类型会出错。我一般的习惯是,凡是遇到 t 开头的量,先看它出现在 I/O 段还是内部段,再看声明区里的实际类型,两步都确认过才敢往下写。

注意:符号约定只在命名规范被遵守时有效,接手别人的程序先扫一遍变量声明区,确认有没有人把 w 开头的前缀用在了 REAL 变量上。

2.4 三步落地:拿到一个陌生块先做类型核对

第一步是把引脚类型在声明区里固定下来,不依赖隐式转换;第二步是显式标定,把 DINT 原始码搬到 REAL 域;第三步是把所有比较、限幅都放在同一个域里做。这三步顺序不能乱,先转类型再标定会把比例因子算错,先比较后标定则会拿原始码去比工程量。

(* 第一步:引脚类型在声明区钉死,别指望隐式转换 *) VAR wRaw : DINT; (* 未标定模拟量输入,AD 原始码 *) aEng : REAL; (* 定标后的工程量,供后续比较/限幅 *) dStart : BOOL; (* 数字量输入 *) cAlarm : BOOL; (* 数字量输出 *) END_VAR (* 第二步:原始码进 REAL 域之前必须先过标定 *) aEng := TCX_SCALE(wRaw); (* 标定块名以工程实际库为准 *) (* 第三步:比较全部在 REAL 域完成,避免整数截断 *) IF aEng >= 80.0 THEN cAlarm := TRUE; ELSE cAlarm := FALSE; END_IF;

这段代码里,wRaw 的取值范围由卡件分辨率决定,可能是 0 到 4095,也可能是 0 到 65535,写死一个假设值是最常见的错误来源;aEng 的范围由量程决定,80.0 这个阈值必须和量程同一量纲;TCX_SCALE 是占位名,实际工程里调用的是库里那个标定块,参数一般是原始码、原始下限、原始上限、工程下限、工程上限五个。第三步的 IF 之所以单独写,是为了让后来接手的人一眼看出阈值作用在哪个域上,而不是埋在一个复合表达式里。

3. STDLIB 函数:取整、比较、选择三组函数的入参与边界

3.1 类型转换函数族:TO的命名规律与截断风险

说明里那句“形如TO的都是数据类型转换函数”其实已经把规律讲透了:源类型在前,目标类型在后,中间用TO连接。BOOL_TO_DWORD 就是把布尔转成双字,这是状态字打包的基础操作。命名规律好记,但转换本身是有损的,方向不同损失不同。

转换方向是否有损说明
BOOL → DWORD无损TRUE 变 1,FALSE 变 0
INT → DINT无损位宽扩展,符号位保留
DINT → REAL可能丢精度超过 24 位有效位后尾数被截
REAL → DINT有损小数部分被丢弃,不是四舍五入
REAL → INT有损同上,且可能超出 INT 范围

REAL_TO_INT 这一类要特别小心,它做的是截断,不是四舍五入,2.9 转过去是 2 而不是 3。如果业务上需要四舍五入,得先用 CEIL/FLOOR 加 0.5 自己拼,或者干脆留在 REAL 域里算完再转。另一个容易忽视的点是 DINT_TO_REAL,DINT 能表示到二十多亿,REAL 的尾数只有 24 位,超过一千六百万左右就开始丢低位,做累计流量这类大数累加时,误差会一点点攒起来。

状态字打包是个典型场景,把一串布尔量压进一个 DWORD 上传给上位机,用 BOOL_TO_DWORD 逐个转换再按位或起来就行:

(* 把两个布尔状态位打包进 DWORD 的低两位 *) VAR dBit01 : BOOL; dBit02 : BOOL; dwWord : DWORD; END_VAR dwWord := BOOL_TO_DWORD(dBit01) OR SHL(BOOL_TO_DWORD(dBit02), 1);

BOOL_TO_DWORD 的入参是单个 BOOL,返回 0 或 1;SHL 是左移,把第二位挪到 bit1 的位置;最后用 OR 合并。这样拼出来的 dwWord 只有低两位有意义,高位是 0,上位机解析时按位掩码取值即可。参数上唯一要确认的是位移方向和位序,不同组态环境对 bit0 是最低位还是最高位的约定可能不同,接上位机之前先用一个已知状态验一遍。

3.2 取整与对数:CEIL/FLOOR 的方向、LN/LOG/EXP/EXPT 的定义域

取整函数两个方向:CEIL 输出大于等于输入的最小整数,再转成实型,说明书给的例子是输入 1.1 输出 2.0,输入 1.0 输出 1.0;FLOOR 输出小于等于输入的最大整数,例子是 2.8 输出 2.0。这两个函数在批量配料、分段计量里用得很多,比如按整袋数算需求量,向上取整保证不会少发。

函数语义示例边界
CEIL向上取整后转 REAL1.1 → 2.0,1.0 → 1.0输入已是整数时不变
FLOOR向下取整后转 REAL2.8 → 2.0负数方向需实测确认
ABS取绝对值-3.5 → 3.5整数最小值取绝对值会溢出
LN自然对数—定义域 x > 0
LOG以 10 为底的对数—定义域 x > 0
EXPe 的 X 次方—输入过大时溢出
EXPTx1 的 x2 次方—底数为负且指数非整数时无定义

对数和指数这三个函数在实际项目里出现频率不高,但一旦用上就必须加保护。LN 和 LOG 在输入为 0 或负数时,不同运行环境的处理方式不一样,有的返回一个极小值,有的直接让整个块输出异常,后续所有依赖这个值的逻辑全部跑偏。我一般会在调用前加一层判断,把输入钳到正数区间,宁可输出一个明确的限幅值,也不让异常值流下去。EXPT 的底数为负时同样要小心,整数指数没问题,非整数指数在实数域里没有定义。

三角函数的说明很简短:ACOS、ASIN、ATAN 的输出单位都是弧度,COS 是余弦。弧度这个单位很多人会忘,工艺上算角度习惯用度,直接把度数喂进去结果会差五十多倍。转换关系是度乘 π 除以 180,我习惯在声明区放一个常量 kPi,所有涉及角度的运算都显式换算一次,不靠记忆。

3.3 比较、限幅、选择与中位数:GE/LE/EQ/NE/LIMIT/MUX/MEDIAN

比较函数这一组最直白:GT 是大于,GE 是大于等于,LT 是小于,LE 是小于等于,EQ 是相等,NE 是不等于,条件成立输出 1。真正要留意的是浮点数的 EQ,两个 REAL 变量做相等比较经常会得到意外结果,因为计算过程里的尾数误差让它们差了那么一点点。工程上的常见做法是设一个死区,判断绝对差值小于死区就认为相等。

LIMIT 是三段式限幅:输入值 IN 处于 MIN 和 MX 之间时原样输出,小于 MIN 输出 MIN,大于 MX 输出 MX。这个块在模拟量输出回路里几乎是标配,防止下游执行机构收到超量程指令。参数就三个,IN、MIN、MX,注意 MIN 和 MX 的名字,最大值那个引脚叫 MX 不叫 MAX,写代码时按库里的实际引脚名来。

MUX 是输入选择器,由 K 值决定输出哪一个输入:K=0 输出从 K 往下数第 1 个输入值,K=3 输出第 4 个。索引从 0 开始这一点必须记牢,从 1 开始数会整体错一位,而且因为类型相同不会报错,只会选错通道。MEDIAN 取中间数,输入引脚为奇数时输出中间那个,为偶数时输出两个中间数的平均值,说明书给的例子是 1、3、5 输出 3,1、3 输出 2,1、3、5、7 输出 4。

(* 通道选择 + 限幅:先钳 K,再取值,最后限幅 *) VAR aCh1, aCh2, aCh3, aCh4 : REAL; iK : INT; aPick : REAL; aLimited : REAL; END_VAR (* K 越界时会取到未定义值,先钳到 0..3 *) IF iK < 0 THEN iK := 0; ELSIF iK > 3 THEN iK := 3; END_IF; aPick := MUX(iK, aCh1, aCh2, aCh3, aCh4); aLimited := LIMIT(IN := aPick, MIN := 0.0, MX := 100.0);

MUX 的 K 是整型,负数和越界值的行为在说明书里没写,所以先钳位;四个通道的数据类型必须一致,混着 REAL 和 DINT 会被隐式转换,转换发生在选择之前还是之后要看实现,不要赌。LIMIT 的 MIN 和 MX 是常量还是变量都行,但两者的顺序不能反,MIN 大于 MX 时输出行为未定义。中位数在传感器冗余里很实用,三取二、五取三的场景直接接 MEDIAN 比手写排序干净得多。

3.4 位运算与位串:AND/OR/XOR/NOT 在状态字里的用法

布尔函数这一组是逻辑门:AND 与、OR 或、XOR 异或、NOT 取反。说明里把 XOR 的真值表列得很清楚,True 异或 False 得 True,False 异或 True 得 True,False 异或 False 得 False,True 异或 True 得 False。异或的用途比另外三个窄,但在校验和奇偶校验里很常见,也常用于“状态翻转”,给一个脉冲就把输出取反。

函数输出为真的条件典型用途
AND所有输入同时为真联锁条件与
OR任一输入为真报警汇总或
XOR两个输入不同状态翻转、奇偶校验
NOT输入为假信号取反

位串函数作用在 WORD、DWORD 这类多位上,和布尔函数的区别在于操作对象:布尔函数吃 BOOL,逐位处理;位串函数吃整字,一次处理十六位或三十二位。做设备状态字、故障码打包时用的是位串,做单个联锁条件时用的是布尔。这两类混着用不会报错,但可读性会崩,后来的人分不清哪个是逻辑判断哪个是位操作。我的习惯是变量名带上类型前缀,dw 开头的一律是位串,d 开头的一律是布尔,扫一眼就知道该用哪组函数。

4. 标准功能块时序:RS/SR、边沿、计数、定时的四个实测点

4.1 RS 与 SR:复位优先和置位优先只差一个优先端

这两个双稳态功能块的引脚几乎一样,R 是复位,S 是置位,Q 是输出,差别只在同时为 1 时谁赢。RS 是复位优先,R 和 S 同时为 1 时输出 0;SR 是置位优先,同时为 1 时输出 1。说明里那句“有复位按钮时用,S 端接复位按钮”看着有点绕,实际意思是把复位动作接到优先端上,保证任何情况下按下去都能停下来。

功能块优先端R、S 同时为 1典型场合
RS复位优先Q = 0有急停、复位按钮的回路
SR置位优先Q = 1启动信号需要压过停机的回路

选哪个不是风格问题,是安全逻辑问题。停机路径上有手动复位按钮的,用 RS,保证按钮按下时输出一定是 0;启动指令需要保持、不能被瞬时的停止信号打断的,用 SR。这两个块的状态是保持的,掉电重启后的初值取决于组态设置,这一点在做上电自检时要专门确认一遍。

(* 复位优先:急停按下时无论 S 是什么,Q 都为 0 *) VAR rsTrip : RS; dReset : BOOL; (* 复位按钮,接 R *) dPermit : BOOL; (* 允许条件,接 S *) cRunning : BOOL; END_VAR rsTrip(R := dReset, S := dPermit); cRunning := rsTrip.Q;

调用时引脚顺序无所谓,但名字必须对,R 和 S 接反了功能就从复位优先变成置位优先,而且不会报错。Q 是输出,只读,不要往上面赋值。这类保持型块在调试阶段最好加一个强制复位入口,否则一旦置位又找不到复位条件,只能重新下载程序。

4.2 边沿检测:R_TRIG 与 F_TRIG 的脉冲只保持一个扫描周期

上升沿检测在 CLK 从 0 变成 1 时输出一个扫描周期的脉冲,下降沿检测在 CLK 从 1 变成 0 时输出一个扫描周期的脉冲。关键词是“一个扫描周期”,这个脉冲不会保持到你看见它为止,控制器扫过这一行之后的下一轮它就已经回 0 了。用它去触发计数器、触发一次写操作是正解,用它去驱动一个需要持续保持的输出就会只闪一下。

边沿检测最常见的翻车方式是被放在条件分支里。功能块实例需要每轮扫描都被调用才能正确记录上一次的 CLK 状态,如果写在 IF 里面,条件不成立时块根本没执行,内部保存的上一轮状态是旧的,等条件成立时算出来的沿就是错的,可能多出一个脉冲,也可能整个沿被吞掉。

(* 边沿块必须每轮都调用,条件只决定要不要用这个脉冲 *) VAR dButton : BOOL; rtOnce : R_TRIG; iCount : INT; END_VAR rtOnce(CLK := dButton); (* 无条件调用,记录上一轮状态 *) IF rtOnce.Q THEN (* 只在有上升沿时做动作 *) iCount := iCount + 1; END_IF;

R_TRIG 的入参只有 CLK,输出 Q 是脉冲。把调用写在 IF 外面、把动作写在 IF 里面,这个结构能避开绝大多数丢沿问题。下降沿用 F_TRIG,引脚一样,触发条件反过来。如果同一个信号需要同时取上升沿和下降沿,就建两个实例,不要试图用一个块兼做两件事。

4.3 计数器:CTU/CTD/CTUD 的 PV、CV、LD 与 Q

递增计数器 CTU 在 CU 为 1 时从 PV 值开始递增,上限 32767,达到条件后 Q 变 1;递减计数器 CTD 在 LD 和 CD 为 1 时从 PV 值开始递减,下限 -32768,CV 小于等于 0 时 Q 变 1;CTUD 是两个的结合,CU 递增、CD 递减,两个方向都有对应的输出条件。PV 是预置值,CV 是当前值,这两个引脚的名字很像,接反了不会报错但行为完全不对。

功能块输入输出行为要点
CTUCU、R、PVQ、CVCU 上升沿递增,CV 达到 PV 时 Q 置位
CTDCD、LD、PVQ、CVLD 装载 PV,CD 递减至 0 时 Q 置位
CTUDCU、CD、R、LD、PVQU、QD、CV双向计数,两个方向各有输出

说明书里 CTU 那条写的是“CV 大于等于 0 时输出 Q 变为 1”,按常规的计数器语义,这个条件应该是 CV 达到 PV,两者对不上。这种描述与常规实现有出入的地方,不要靠猜,直接在调试环境里给 PV 设一个值、手动加几个脉冲,看 Q 到底在 CV 等于几的时候翻转,一次实测比读十遍说明都管用。CTD 的 LD 引脚语义同样值得实测,常规理解是 LD 为真时把 PV 装进 CV,说明书那句“LD 和 CD 为 1 时 CV 从 PV 值递减”容易被读成两个条件相与。

还有一个隐性问题:CV 的初值。如果 PV 没赋值、CV 上电是 0,CTU 可能在第一个脉冲就满足条件把 Q 置起来,现场表现是“一上电就报满”。计数器的 R 引脚要接一个明确的复位条件,别指望上电自动清零。

4.4 定时器:TON/TOF/TP 的 PT、ET 与 IN 的四种组合

三个定时器,TOF 是 1 变 0 延时输出,IN 从 1 变成 0 之后经过 PT,Q 才从 1 变成 0,ET 是已经过去的时间;TON 是 0 变 1 延时输出,IN 从 0 变成 1 之后经过 PT,Q 从 0 变成 1;TP 是脉冲输出,IN 从 0 变 1 时 Q 立即变 1 并保持 PT 时间。PT 是预置时间,类型是 TIME,ET 是当前已计时间,也是 TIME。

功能块触发Q 的动作典型用途
TONIN 上升沿延时 PT 后置 1,IN 变 0 立即复位启动延时、滤波确认
TOFIN 下降沿IN 变 0 后延时 PT 才复位停机延时、风机后吹
TPIN 上升沿立即置 1,保持 PT 后自动复位定长脉冲、单次动作

TIME 类型的写法是 T#5s 这种格式,不能直接给整数。有人图省事写 PT := 5000,如果编译能过,实际含义取决于实现,可能是五秒也可能是五毫秒,现场表现就是“设了五秒结果一瞬间就动作了”。我一般会在声明区把时间常量集中定义,统一用 T# 前缀,改参数时只改一处。

(* 启动延时确认:命令保持 PT 时间后输出才置位 *) VAR dStartCmd : BOOL; tonStart : TON; cRun : BOOL; tDelay : TIME := T#5s; tElapsed : TIME; END_VAR tonStart(IN := dStartCmd, PT := tDelay); cRun := tonStart.Q; tElapsed := tonStart.ET; (* 可用于进度显示或超时诊断 *)

TON 的 IN 一旦掉回 0,Q 和 ET 立即清零,没有保持功能。需要“按一下运行五秒”的效果,用 TP 而不是 TON。需要“停机之后再吹三十秒”的,用 TOF。三个块选错,逻辑看上去能跑,时序上差一个动作沿,排查的时候往往要盯很久的在线趋势才能看出来。

5. 避坑与排查:五条从组态现场带回来的记录

5.1 模拟量数值差了几十倍:w 接到了 a 引脚上

现象是画面上显示的压力值稳定但完全不靠谱,有时候是实际值的几十分之一,有时候又大得离谱,换传感器、换通道都没用,趋势曲线还特别平滑,不像干扰。

原因是信号段位接错了。w 开头的是未标定原始码,DINT,量程可能是 0 到 4095;a 开头的是标定后的工程量,REAL,量程 0 到 100.0。把 w 直接接到期望 a 的引脚上,隐式转换会把整数变成实数,数值本身没变,于是 2048 被当成 2048.0 送进工程量域,超量程几十倍。这个错误编译器不管,因为类型转换是合法的。

解决办法是把标定这一步显式补上,在 w 和 a 之间插入标定调用,参数按卡件实际分辨率和仪表量程填。排查时有个快速判断法:看这个值除以满量程码值再乘工程量程,能不能对上现场表针,能对上就是漏了标定。从那以后我养成一个习惯,凡是变量名以 w 或 v 开头,就不允许它直接出现在比较和显示逻辑里,必须先经过一次命名转换。

5.2 手动按钮按十次只认三次:边沿块写在条件分支里

现象是操作工反馈按钮不灵,按下去有时候计数加一,有时候什么都不发生,换按钮、换输入点、加去抖都没解决,用调试器看输入信号是好的。

原因是 R_TRIG 被写在了 IF 条件内部。功能块实例需要每轮扫描都被调用,才能把当前 CLK 状态存下来供下一轮比较。写在条件里,条件不成立的那些扫描周期块根本没跑,内部记的还是老状态,等条件成立时算出来的沿就是错的,脉冲被吞掉或者多出来一个。手动按钮的按下时间只有几百毫秒,扫描周期是几十毫秒,正好卡在这个模糊区间里,表现就是时灵时不灵。

解决办法是把边沿块的调用提到所有条件之外,无条件执行,条件只用来决定要不要使用 Q。这个改法对 R_TRIG 和 F_TRIG 都适用。顺带说一句,按钮去抖如果用定时器做,定时器同样要无条件调用,道理一样。

5.3 计数器一上电就报满:PV 和 CV 的初值没人管

现象是设备刚上电、一个脉冲都还没来,计数器输出 Q 就已经是 1,联锁直接挡住启动,复位一下能好,再上电又复现。

原因有两个层面。一是 CV 的上电初值不确定,某些组态下默认是 0,而判断条件如果是“CV 大于等于 0 就置位”,第一个扫描周期就满足了;二是 PV 没有显式赋值,预置值是 0 或者随机残留,条件自然成立。说明书里 CTU 那条“CV 大于等于 0 时输出 Q 变为 1”本身就值得实测核对,不能照字面理解。

解决办法是给 PV 一个明确的常量,给 R 引脚接一个可靠的上电复位或者手动复位,并且在调试阶段用强制功能把 CV 打到不同值,观察 Q 到底在哪个点翻转,把实际行为记在注释里。计数器这类有状态的块,上电初值一定要在自检清单里单独列一条。

5.4 定时器设了五秒,实际半秒就动作:PT 的单位没对齐

现象是延时启动的逻辑明显偏快,参数改大十倍也没变化,或者变化方向和预期相反,趋势上看输出几乎跟着输入走。

原因是 PT 引脚的类型是 TIME,赋值时用了裸整数。写 PT := 5000,在能编译通过的环境里,这个 5000 到底解释成五秒还是五毫秒,取决于底层实现和时间基准,不同版本还可能不一样。另一种情况是用了 TIME 常量但单位写错,T#5000ms 和 T#5s 数值上一样,可读性差一截,改参数的人容易看错。

解决办法是统一用 T# 加单位的写法,5 秒就写 T#5s,500 毫秒就写 T#500ms,不要出现裸数字。时间常量集中放在声明区顶部,命名带 t 前缀,全项目共用一份。调试时把 ET 引脚接到趋势上,看它涨到 PT 用了多久,一眼就能判断单位对不对。

5.5 取整结果和手算对不上:说明书的示例本身有笔误

现象是照着说明书用 FLOOR,输入 2.8 得到 2.0 没问题,输入 2.0 却怎么都对不上例子里的 1.0,怀疑是自己用错了函数。

原因是示例写错了。FLOOR 的定义是输出小于等于输入的最大整数再转成实型,2.0 是整数,结果应该是 2.0,说明书里那句“输入为 2.0,输出为 1.0”与定义矛盾。类似的情况在负数上更明显,-2.3 的向下取整是 -3.0 而不是 -2.0,方向搞反的人不少。CEIL 的示例倒是自洽,1.1 得 2.0,1.0 得 1.0。

解决办法是以定义为准、以实测为准,不以示例为准。凡是涉及取整、截断、四舍五入的地方,在调试环境里跑一组边界值:正数小数、正数整数、负数小数、负数整数、零,五个点跑完再往下写。同一份说明里 CTU 的 Q 条件和 FLOOR 的示例这两处都对不上常规语义,这提醒我一件事:手册是参考,实测才是依据。

6. 标定与上电验证:把 DINT 原始值算成工程量的三步核对

TCXLIB 在这个库里只干一件事,模拟量的标定,也就是把 w 域的 DINT 原始码映射到 a 域的 REAL 工程量。线性标定的公式不复杂:工程量等于原始码减去原始下限,乘以工程量程,除以原始量程,再加上工程下限。麻烦的从来不是公式,是四个边界参数从哪来、对不对。

参数来源核对方法
原始下限卡件分辨率与接线方式短接输入端看采样值
原始上限同上给满量程信号看采样值
工程下限仪表量程下限对照铭牌
工程上限仪表量程上限对照铭牌

我一般分三步核对。第一步,断信号,看原始码是不是停在原始下限附近,偏差大说明零点漂移或者接线有问题;第二步,给一个中间量程的标准信号,比如量程一半,看算出来的工程量是不是也在一半位置,偏差成比例说明量程参数错了,偏差是固定值说明下限错了;第三步,把算出来的值接到限幅和报警逻辑上,人为越过阈值,确认报警输出跟着翻转。三步走完,这条模拟量通道才算通了。

(* 线性标定:四个边界参数作为常量集中管理 *) VAR wRaw : DINT; aEng : REAL; kRawMin : DINT := 0; kRawMax : DINT := 4095; kEngMin : REAL := 0.0; kEngMax : REAL := 100.0; END_VAR aEng := (DINT_TO_REAL(wRaw - kRawMin) / DINT_TO_REAL(kRawMax - kRawMin)) * (kEngMax - kEngMin) + kEngMin; (* 标定完再限幅,防止原始码越界时工程量飞出量程 *) aEng := LIMIT(IN := aEng, MIN := kEngMin, MX := kEngMax);

这段里先把差值转成 REAL 再相除,是为了避免整数除法把小数部分截掉,先除后转的话中间结果永远是整数,标定出来的值会呈现明显的台阶。限幅放在标定之后而不是之前,因为要限的是最终工程量,不是原始码。四个常量放在声明区,改量程只改这一处,不会漏。

验证这件事我吃过亏。早些年调一条模拟量通道,标定参数照着仪表铭牌填完就上线了,趋势看着挺正常,直到一次满负荷运行,上位机显示值卡在量程上限不动,现场表针还在往上走。拆开看是原始上限填成了卡件上限,比实际接线的量程大了一截,平时负载低看不出来。从那以后我每次做完标定,都会强制给三个点:零点、中点、满点,一个一个对着现场表针核,核完才敢把这段逻辑放进正式回路。希望帮到你。

本文还有配套的精品资源,点击获取