首页 / 资讯中心 / 文章详情

PLC联锁怎么用WAND/WOR/WXOR把BOOL逻辑改成字级位操作

PLC联锁怎么用WAND/WOR/WXOR把BOOL逻辑改成字级位操作 ★ FEATURED ARTICLE
做PLC联锁做了不少年头我越来越觉得一个问题很多写ST的人平时翻来覆去就是IF、FOR、CASE偶尔用一下算术指令但WAND、WOR、WXOR这三位“字位操作”老哥一直被当成生僻指令晾在旁边只有抄别人程序时才会碰见一次。实际上要说处理联锁逻辑最趁手的工具我觉得就是它们仨。今天不打算讲教科书式的定义就聊聊我是怎么把一堆散了架的BOOL联锁改写成字级位操作联锁的以及这么改完之后程序变短、排查变快、复用还简单的一套真实思路。这篇内容适合谁看正在做设备联锁、顺控、安全联锁逻辑但被大段BOOL条件搞得头大的人也适合那些刚学ST、想把位操作指令用明白的人。只要你看懂这篇文章至少能解决三件事知道WAND、WOR、WXOR分别能在联锁里干什么知道怎么用“状态字使能字”把几十条联锁条件压缩成几条ST语句知道真正现场调试时这三个指令会踩到什么坑。1. 为什么联锁逻辑值得用“字”来做1.1 传统BOOL联锁的四个痛点先看一个最常见的联锁写法IF bPressure_Low AND bLevel_Low AND bEStop THEN bTrip : TRUE; END_IF;如果只是三五个条件这么写一点问题都没有。但一套设备动辄十几路联锁信号出口压力、入口流量、轴承温度、振动、急停、阀门到位、手自动状态……这时候就麻烦了。第一个痛点是变量爆炸。每个联锁条件都要一个BOOL变量或者一个IO点。程序页翻起来光是声明变量的区域就占半屏点位表、注释、安全栅表格越堆越多。第二个痛点是改联锁逻辑要改程序。现场经常出现“某台泵振动信号误报暂时屏蔽两天”这种需求。传统做法是注释掉一行或者加一个“假标签”按钮。改一次程序重启一次PLC遇到不能停机的地方就被迫在运行中改逻辑风险很高。第三个痛点是调试效率低。在线监控时你看到十几个BOOL一个一个跳却说不清到底是谁把最终联锁拉起来的。就算加了一堆中间变量也只是多了一堆BOOL而已。你可能要同时盯几个变量窗口才能定位。第四个痛点是复用性差。一套泵组的联锁逻辑换一台设备、换一组位号就得复制、粘贴、改位号。如果你改漏一个那恭喜你现场会给你上一课。所以做多了之后你会发现把这些“同类型、同等级”的联锁信号按位塞进一个WORD里用“字指令”一起处理简直是直觉上就该做的事。1.2 从“一个信号一个变量”换成“一组信号一个字”用一个比喻解释这个思路传统BOOL联锁相当于你去超市买16样东西每样东西单独拿一个塑料袋拎着位操作联锁则是把这16样东西全塞进一个带隔层的购物箱箱子编号隔仓位号固定找东西时打开箱子一目了然。放到程序里一个WORD有16个位Bit0到Bit15。你让Bit0代表“出口压力低”Bit1代表“高位水池液位低”Bit2代表“P1泵振动”Bit3代表“P2泵振动”……那么这个WORD就成了一个“设备联锁状态字”一个变量承载了16个联锁信号。然后你再准备一个“使能字”也是16位每一位对应状态字的同一位。这一位是1就说明对应联锁条件参与动作这一位是0就相当于把这个条件旁路掉。这就是工程上很常用、很多人却没用起来的“使能掩码”机制。状态字像一块面板使能字像面板上的一排开关开关合上的回路才接入联锁。运行时想屏蔽哪个信号直接在线改使能字的对应位就行不用改程序不用停机。1.3 WAND/WOR/WXOR在联锁里的分工有了状态字和使能字接下来就是怎么用三条指令把它们组织成联锁逻辑了。我的习惯分配非常固定指令在联锁中的作用工程化理解WAND按位过滤用掩码筛选出需要参与联锁的有效触发位WOR按位合并把多个条件、多个状态汇总成一个字或把一个输出位“拉起来”WXOR按位比较判断两组状态是否完全一致常用于双通道反馈校验它们组合在一起就是一套“联锁求值流水线”状态字先经过使能字这个WAND过滤器再进WOR做汇总判断输出联锁结果。WXOR则专门负责那些“两路信号必须一致”的场合比如接触器辅助触点反馈和变频器运行反馈。记住这个分工后面看代码就顺了。2. WAND/WOR/WXOR的原理与易错点2.1 WAND用掩码决定谁参与联锁WAND按位把两个WORD做“与”运算规则很简单对应位都是1结果才为1否则为0。写出来一般是wResult : WAND(wStatus, wEnableMask);这个表达式想表达的逻辑是wStatus里的某一位是否有效由wEnableMask的同一位决定。掩码位为1就放行掩码位为0就清零。举个例子状态字wStatus 16#001F意思是Bit0到Bit4都为1五个条件全部触发。使能字wEnableMask 16#001B二进制是0000 0000 0001 1011也就是Bit0、Bit1、Bit3、Bit4为1Bit2为0。那么WAND的结果就是16#001BBit2被屏蔽掉其他四个位保留。这个操作在工程上非常直观。假设Bit2代表振动信号而现场振动探头正在检修随时会误报那你让使能字的Bit2等于0振动联锁就被旁路了其余联锁照常工作。而且这一切可以在运行状态下在线修改对维护人员极度友好。我说个容易犯的错把使能字写反本来想屏蔽某个位结果只剩这个位参与联锁。比如你写16#FFFB只清零Bit2那没问题但如果你写16#0004那就等于把其他所有位都屏蔽了只剩Bit2有效。这类错误在仿真时未必暴露一旦现场其他条件真实触发联锁却不动作那就要命了。所以我在程序里会把使能字做注释把位号表直接贴在注释里。WAND还有一个小用途判断某几个位是否“全部为0”。如果WAND(wStatus, 16#000F) 0说明低四位一个都没触发。这个写法在复位逻辑里很常用后面实战例子会用到。2.2 WOR合并条件、拉开输出WOR按位做“或”运算对应位只要有任意一个是1结果就是1。它在这里负责两件事。第一件事是“汇总”。联锁的本质是“任何有效条件触发就要动作”这天然就是一个或逻辑。你可以用WOR把多个字接起来wActive : WOR(wAct1, wAct2); wActive : WOR(wActive, wAct3);如果你用的是支持多操作数的ST平台写成 wActive : wAct1 OR wAct2 OR wAct3; 也完全等价。但是记住我们讨论的WAND/WOR/WXOR很多时候是作为系统函数调用的它接收两个字返回一个字这种函数式写法在跨平台移植时更稳妥。实际用的时候别忽略一个细节WOR的两拨操作数往往一个是“状态条件”另一个是“人为干预”。比如自动模式下联锁结果字wAct是1但同时有一个“调试握手解除”信号。你要是把这两个字做WOR调试状态下联锁也可能被拉下来这就不对了。所以WOR只合并处于同一逻辑层级、同一权限等级的信号不同权限等级的要用AND条件去控制。第二件事是“拉位”。在你构造状态字时常常需要把一个BOOL条件放进一个字的某个位。很多编译器支持“wStatus.3 : bP1Vib;”这种位元素赋值写法但如果你的平台不支持用WOR一样能实现IF bP1Vib THEN wStatus : WOR(wStatus, 16#0008); END_IF;这就是“置位”操作WOR有一个特点只有1能把0变成10不会把1变成0所以连续对同一个字做多次WOR不会误伤其他位。这在拼装状态字时非常安全因为我用循环扫描时并不知道哪个位已经被置过位。2.3 WXOR专门处理“不一致”WXOR按位做“异或”运算两个对应位不同结果位为1两个位相同结果为0。这个指令在联锁里的核心场景是“双通道反馈校验”和“状态变化识别”。什么叫双通道反馈校验举个例子电机的接触器辅助触点给一路反馈变频器的运行继电器又给一路反馈。正常情况下两个反馈应该是一致的接触器吸合变频器也输出运行接触器断开变频器也停止。如果两个反馈相反那系统内部必然有问题要么触点烧死、要么接线错误、要么继电器卡壳。这时用WXOR把两个反馈字一比较不一样的位就会变成1一眼就能找到异常通道。对应代码是wDiff : WXOR(wContactFb, wDriveFb); IF WAND(wDiff, 16#0003) 0 THEN // bit0或bit1对应设备反馈不一致触发报警 END_IF;这里有个非常隐蔽的坑WXOR比较的是“整个字16个位都一致”但你的两个反馈字里并不是每个位都有实际意义。如果一个字的Bit0对应接触器反馈另一个字Bit1也存了一些无关状态WXOR的结果就会把这种“无关差异”也算成不一致。所以要在WXOR之后再用WAND把有效位筛出来而不是直接判断wDiff是否不等于0。我就会把WXOR和WAND焊死在一条代码里从不单独判断。还有一个工程认知要强调“一致”不等于“都为1”。两路反馈都为0说明设备确实处于停机状态这也是一致WXOR结果位是0不是报警。很多新手一看到两路都是0就慌了以为异或只认“一高一低”其实两路都低是完全合法的停机状态。在联锁复位里WXOR也很好用如果要求“检修门关闭且门锁接通”才能复位两个信号状态一致且都为真才允许启动只要有一路没到位就用WXOR判定为不一致禁止复位。本质上这是一种比较器。2.4 宽度、类型和运算符的换算细节用这三个指令之前先确认你的操作数宽度。16位和32位是完全两套东西有的平台提供WAND只能操作WORD处理32位要换DAND或者DWAND。如果你混着用轻则编译报错重则数据被截位高16位悄悄丢掉程序看起来运行正常但某一天某个状态突然消失。类型尽量用无符号数也就是WORD、DWORD。如果用INT去存16#FFFF在INT里是-1你监控时看到的是一堆负数很容易产生误解。我的规矩是凡是位操作相关的变量一律声明为WORD绝不用INT更不用REAL。关于ST原生运算符和函数形式的关系多说一句。在IEC 61131-3语法里对字数据用AND、OR、XOR运算符本身是合法的。但在实际工程中有些PLC的ST编译器对“跨类型、跨长度的运算符重载”支持并不好用WAND/WOR/WXOR这种函数式指令反而更可靠也更接近底层指令表。你可以把两者看成同一个逻辑的不同写法但在代码风格上统一用函数式不乱交叉移植时能少挨不少刀。还有一个细节是优先级。ST里比较运算、算术运算、逻辑运算符的优先级容易被忽略。我见过有人写IF WAND(wStatus, wEnableMask) 0 AND bReset THEN这行代码在标准ST里其实有歧义风险。稳妥写法是把WAND结果先赋给一个中间变量wAct然后再把wAct和其他条件做组合。简单粗暴但百分之百不会因为优先级踩雷。从可读性上讲中间变量也是好习惯因为它能让你在监控软件里直接看到“当前有效联锁位”到底有哪些。3. 实战一套三泵联锁改造3.1 场景还原与原始痛点假设有这么一个输送系统三台给水泵P1、P2、P3并联向高位水池供水。每台泵有独立接触器和变频器系统里有出口压力、水池液位、每台泵的振动探头还有急停按钮。传统程序大概是这样的IF bPressLow AND bTankLow AND bEStop THEN bP1_Trip : TRUE; bP2_Trip : TRUE; bP3_Trip : TRUE; END_IF; IF bP1Vib AND bTankLow THEN bP1_Trip : TRUE; END_IF; IF bP2Vib AND bTankLow THEN bP2_Trip : TRUE; END_IF; IF bP3Vib AND bTankLow THEN bP3_Trip : TRUE; END_IF;看着还行那再让你加一个“手动模式屏蔽振动联锁”的条件加一个“复位按钮”加一个“低压持续3秒才动作”的延时这套代码就开始膨胀注释比代码还长。每次查问题你都得把这块IF逻辑从头到尾走一遍最要命的是你很难通过在线监控一眼看出“现在到底是哪个条件让这台泵停了”。改造目标很简单把联锁条件按位整理成WORD用WAND做使能筛选用WOR做汇总判断用WXOR做反馈校验。旁边加两张位分配表程序后期维护人员看懂一次以后就能自己加条件。3.2 状态字、使能字的地址规划先把状态字wStatus的位定义定死这是整个方案的地基位号含义触发电平Bit0出口压力低1触发Bit1高位水池液位低1触发Bit2P1振动1触发Bit3P2振动1触发Bit4P3振动1触发Bit5急停按下1触发Bit6~Bit15预留然后定使能字wEnableMask初始值。默认全部参与就是16#003F二进制是0000 0000 0011 1111Bit0到Bit5全为1。以后想屏蔽某一项在线把对应位改成0即可。同时定义P1、P2、P3的“泵触发位字”映射也就是把bit2、bit3、bit4分别翻译成三台泵各自的联锁输出。方便后面每条泵单独取位。3.3 完整的ST代码下面是主逻辑尽量写得规整方便直接抄PROGRAM PumpInterlock VAR // 联锁状态字、使能字 wStatus : WORD; wEnableMask : WORD : 16#003F; wAct : WORD; bTrip : BOOL; // 三台泵的联锁输出 bP1Trip : BOOL; bP2Trip : BOOL; bP3Trip : BOOL; // 双通道反馈校验 wCmdFb : WORD; // 变频器运行反馈bit0P1, bit1P2, bit2P3 wContactFb : WORD; // 接触器辅助触点反馈 wDiff : WORD; bFbErr : BOOL; bRunReset : BOOL; // 手动复位允许 END_VAR状态字拼装如果平台支持位元素访问就直接给位赋值如果不支持就用WOR把BOOL位拉起来。wStatus : 0; IF bPressLow THEN wStatus : WOR(wStatus, 16#0001); END_IF; IF bTankLow THEN wStatus : WOR(wStatus, 16#0002); END_IF; IF bP1Vib THEN wStatus : WOR(wStatus, 16#0004); END_IF; IF bP2Vib THEN wStatus : WOR(wStatus, 16#0008); END_IF; IF bP3Vib THEN wStatus : WOR(wStatus, 16#0010); END_IF; IF bEStop THEN wStatus : WOR(wStatus, 16#0020); END_IF;联锁求值wAct : WAND(wStatus, wEnableMask); IF WOR(wAct, 0) 0 THEN bTrip : TRUE; ELSE bTrip : FALSE; END_IF;三台泵各自取出联锁位bP1Trip : (WAND(wAct, 16#0004) 0); bP2Trip : (WAND(wAct, 16#0008) 0); bP3Trip : (WAND(wAct, 16#0010) 0);输出停机命令IF bTrip THEN bP1Run : FALSE; bP2Run : FALSE; bP3Run : FALSE; END_IF; // 也可以让每台泵独立使用 bPxTrip取决于逻辑结构双通道反馈校验wDiff : WXOR(wCmdFb, wContactFb); bFbErr : (WAND(wDiff, 16#0007) 0); // 只检查前三台泵如果bFbErr为TRUE可以追加报警、锁定复位、点亮HMI提示这就是联锁之外的故障诊断层。3.4 模拟验证的观察要点有了这套结构在线验证就变得非常清晰。先把wEnableMask设为16#003F然后把wStatus置成0此时wAct为0bTrip为FALSE三台泵都正常允许运行。接着给wStatus的Bit0置1模拟出口压力低。wAct WAND(16#0001, 16#003F) 16#0001不等于0bTrip变成TRUE。但三台泵的单独Trip位都是FALSE因为触发位是全局低压不是某一台泵特有的。工程表现就是三台泵整体联锁停机直接进复位流程。再模拟P1振动触发wStatus的Bit2置1wAct 16#0004bTrip为TRUE同时bP1Trip为TRUE另两台泵不受影响。任何时候你想知道“当前到底哪些联锁位有效”直接盯监控里的wAct用十六进制显示位号表一对照马上就知道是谁触发的。这比翻几十行BOOL状态快得多。最后模拟反馈校验把wCmdFb设为16#0001wContactFb设为0wDiff就是16#0001bFbErr为TRUE说明P1变频器反馈说在运行接触器却说没吸合要么接线有问题要么接触器触点坏了。整个验证过程我基本不动程序只在线改几个字的值就能覆盖主要联锁路径。这就是字位操作最实在的价值把系统状态压缩成“一个值”人可以直观地“看见”系统状态。4. 常见问题与排查技巧实录4.1 位号错位、信号闪烁与扫描时序最常见的问题是位号错位。不同图纸、不同PLC习惯Bit0到底是排在十六进制的最右边还是最左边很容易打架。十六进制16#0001表示最低位Bit0是1很多人脑子里却是“左边第一位”一下子就错位了。我的建议是在代码注释里画一张二进制的位分布图把“bit0压力、bit1液位……”这种对应写成表格贴在那里谁改谁知道。第二个问题是信号闪烁。现场触点抖动、模拟量临界点反复穿越、传感器信号瞬间丢失都会让状态字在几个扫描周期内跳来跳去。如果你直接WAND、WOR、WXOR触发位存在的时间可能只有一个扫描周期。处理手段是加滤波延时确认信号维持一定时间后再置入状态字。尤其做联锁对“瞬时抖动”绝不能手软。第三个问题藏在扫描时序里。状态字的拼装、使能字的读取、联锁输出最好在程序里保持固定顺序先读入IO和中间BOOL拼装新状态字再做联锁求值最后输出。如果在同一个扫描周期里一段程序更新了状态字另一段程序又立刻用它做联锁判断没问题但如果你把输出关闭逻辑放在状态字拼装之前就可能发生“本周期读旧状态、下周期才生效”的情况现场表现就是联锁动作慢了一拍。WXOR还有个专属时序问题接触器和变频器动作时间天然有先后差。你发出启动命令的一瞬间接触器可能3毫秒就吸合反馈变频器可能50毫秒后才有运行反馈这期间WXOR必然查出“不一致”。所以双通道反馈校验必须加启动确认时间窗设备启动后的前几百毫秒不参与校验等状态稳定后再投入。少了这一步你的系统会天天误报警。4.2 联锁无法复位的典型坑联锁触发后无法复位是现场最常见的抱怨之一。用字位操作很容易查看wAct是不是已经归零。如果wAct还是非0说明某个联锁条件仍然存在系统当然不允许复位。但有一种情况是“条件明明消失了联锁还是不复位”。这时候要检查你用的变量类型和保持属性。状态字如果被声明成保持型变量断电前是故障态上电后故障条件虽然清了状态字却还停留在旧值。解决方法是上电初始化在程序启动的第一个周期把状态字、使能字、输出字全部归0或者赋一个已知的安全初值。还有一种很隐蔽的错误你在联锁复位时错误地修改了使能字。比方说为了强制复位有人把wEnableMask清零来旁路所有条件结果故障清了忘改回来使能字一直等于0联锁永久失效。这是我认为最危险的操作。如果需要临时屏蔽某几个条件正确做法是在复位完成后把使能字恢复原值且这个恢复动作要放在复位按钮的边沿里执行别在持续电平里执行。另外触点常开常闭搞反也会让状态字的某一位恒定是1。排查时不要只看程序里的BOOL名称要去机柜里对着实际触点状态和IO表核一遍。位操作让人“看得见”状态但如果采集源本身就反了字里装的就是错误事实再漂亮的逻辑也会做出错误判断。4.3 高效率排查的流程与习惯最后分享一下我现在的排查流程基本上是肌肉记忆了。第一步看wAct而不是看一堆BOOL。监控软件里把wAct设为十六进制显示。如果wAct不是0对照位号表就知道谁还在触发。这是最快的。第二步如果wAct是0但联锁还在就去查bTrip的置位逻辑里是否还有其他条件参与了或运算比如复位按钮没有接通、双通道校验错误被锁存了。也就是把“联锁源”和“联锁锁存”分开查。第三步如果怀疑WXOR误报分三步走先停掉校验功能或把wDiff的有效位掩码临时缩小再看当前设备处于什么状态。比如设备在运行wContactFb16#0007、wCmdFb16#0007wDiff0正常设备在停止两个都等于0也正常。只要两个字眼里的实际位分别成立WXOR结果才有意义。第四步多做小步试验。在线把wStatus强制成16#0000看整个联锁是否解除再依次给每一位置1看对应动作是否准确。这个方法成本最低也最能树立信心。实际上就是“输入矩阵遍历法”在做安全联锁验证时极其好使比对着纸面推演有效得多。顺手再提一个代码习惯给状态字里的关键位用常数命名而不是到处写16#0008这种裸数。我一般用CONSTANT BIT_P1VIB : WORD : 16#0008; END_CONSTANT这样写WAND(wStatus, BIT_P1VIB)这种代码读起来跟读注释一样清楚。裸数是代码里最差的注释尤其是十六进制三周后再看没人记得那个1是在第几位。写到最后实话实说WAND、WOR、WXOR单个拿出来都很简单真正难的是形成一种“用字来思考联锁”的习惯。我现在的做法是固定的信号按位分组装进状态字使能字负责旁路控制WAND做过滤WOR做汇总WXOR做比较最后把结果字对应到每个执行机构。这套模式我已经在好几套不同的设备上复用过了最大的感受就是改逻辑不再提心吊胆在线监控一眼能看到整个联锁系统的“全景图”。如果你现在还在写那种十几个BOOL变量嵌套的联锁我真心建议你拿一套小设备先试试这种写法用顺了之后你会发现自己回不去了。
阅读完成 · 觉得有帮助?
咨询建站