
1. 报警为什么总是闪一下就没了1.1 一个几乎每个PLC工程师都遇到过的场景设备正在运行触摸屏上突然跳出一条报警操作工还没来得及看清内容报警就消失了。等你去查历史记录发现什么都没留下。操作工说刚才明明报警了你查程序发现报警位确实触发过但就是没锁住。这种情况在iQ-R系列PLC上尤其常见原因也很直接大多数报警信号是瞬时信号。比如一个热继电器动作可能只持续几百毫秒就恢复了一个伺服驱动器的报警输出在故障排除后自动复位。如果你的程序只是把报警位直接映射到HMI显示那它闪一下消失就是必然的。我在一个包装线项目上就吃过这个亏。客户反映偶尔会停机但查不到原因。到现场蹲了两天终于抓到一次伺服驱动器报了一个瞬时过载PLC程序里报警位只ON了大概200msHMI的刷新周期是500ms根本没来得及显示。操作工只看到设备停了不知道发生了什么。这个问题的本质不是报警信号太短而是程序没有做锁存处理。报警信号来了又走你的程序必须把它抓住直到操作工确认之后才放行。这就是FB锁存要解决的核心问题。1.2 锁存和自保持有什么区别很多人会把锁存和自保持混为一谈觉得都是把信号保持住。但实际使用中两者有本质区别。自保持SET/RST方式是最简单的做法报警信号ON时SET一个位确认按钮按下时RST这个位。这种做法能用但有几个明显的坑如果同一个报警位在确认后再次触发你需要额外的逻辑来处理多个报警同时触发时确认逻辑容易混乱没有记录报警的发生时间、持续时间等关键信息程序里到处散落着SET/RST指令维护起来很痛苦FB锁存则是把报警处理封装成一个功能块每个报警实例独立管理自己的状态。它不仅能锁存报警还能记录报警的发生时刻、确认时刻、持续时间甚至能区分首次报警和重复报警。更重要的是FB的封装特性让程序结构清晰100个报警和10个报警的代码复杂度几乎一样。我现在的习惯是任何需要追溯的报警一律用FB锁存不用SET/RST。多花十分钟写FB后面省下的是几十个小时的排查时间。1.3 iQ-R平台上做锁存的天然优势iQ-R系列PLC在报警处理上有几个别的平台比不了的优势这也是我为什么推荐在iQ-R上认真做FB锁存的原因。第一iQ-R的标签编程Label Programming让FB的接口定义非常清晰。你可以给每个报警定义一个有意义的标签名比如Alarm_ConveyorOverload而不是用M1001这种毫无意义的地址。调试的时候一眼就能看出是哪个报警。第二iQ-R的结构化文本ST支持非常完善。用ST写FB的逻辑比梯形图清爽得多尤其是涉及时间戳记录、状态机切换这些操作时ST的可读性远超梯形图。第三iQ-R的SD卡数据记录功能可以直接把FB里记录的报警历史写到CSV文件。这意味着你不需要额外的SCADA系统就能实现报警历史的持久化存储。对于中小型设备来说这个功能太实用了。第四iQ-R的模块化设计让FB可以跨项目复用。我现在的做法是维护一个标准的报警FB库新项目直接导入改改参数就能用。这套东西用了三年多从包装线到装配线到检测设备基本没怎么大改过。2. 报警FB的接口设计哪些引脚必须有2.1 输入引脚的设计逻辑一个报警FB的输入引脚看起来简单但设计不好后面会很麻烦。我经过多个项目的迭代现在固定用这几个输入引脚名数据类型说明是否必须i_xTrigBOOL报警触发信号必须i_xAckBOOL确认按钮必须i_xResetBOOL复位/清除必须i_sNameSTRING报警名称建议i_nPriorityINT报警优先级可选i_xEnableBOOL使能可选i_xTrig是报警源直接接你的报警条件。这里有个细节要注意不要在这个引脚前面加延时或滤波。很多人喜欢在报警信号上加一个TON延时防止抖动误报。我的建议是把这个延时做在FB内部用参数控制而不是在外面加。原因很简单如果延时在外面FB就不知道报警信号是什么时候真正开始的记录的时间戳就不准了。i_xAck是确认按钮。这里的关键是确认和复位要分开。确认是操作工看到了报警复位是报警条件消失了。这两个动作在时间上可能间隔很久。如果只有一个按钮操作工确认之后报警就消失了但实际上报警条件可能还在这就造成了假复位。i_xReset是复位。只有在报警条件消失且已确认的情况下复位才有效。这个逻辑必须在FB内部实现不能靠外部逻辑保证。i_sName是报警名称用STRING类型。iQ-R对STRING的支持很好可以直接在HMI上显示。我一般会把报警名称和报警代码都写进去比如E1023 输送带过载。2.2 输出引脚的设计逻辑输出引脚的设计直接决定了HMI上能显示什么信息。我常用的输出引脚引脚名数据类型说明o_xActiveBOOL报警激活中锁存o_xUnAckBOOL未确认报警o_xNewAlarmBOOL新报警单扫描周期o_tTrigTimeTIME触发时间戳o_tAckTimeTIME确认时间戳o_dDurationDINT持续时间秒o_nCountINT触发次数o_xActive是锁存后的报警位直接接HMI的报警显示。这个位在报警触发时ON在复位时OFF。注意确认不会让这个位OFF只有复位才会。o_xUnAck是未确认报警位。这个位在报警触发时ON在确认时OFF。HMI上可以用这个位来做闪烁效果——未确认的报警闪烁已确认的报警常亮。o_xNewAlarm是新报警位只在报警首次触发的那个扫描周期ON。这个位用来触发报警记录、声音、弹窗等一次性动作。o_tTrigTime和o_tAckTime是时间戳。iQ-R可以用TIME()函数获取当前时间或者用RTC指令读取实时时钟。我一般用RTC因为TIME()是系统运行时间断电就归零了而RTC是实时时钟断电靠电池保持。o_dDuration是持续时间单位秒。这个值在报警复位时计算并保持用来做报警统计。o_nCount是触发次数。同一个报警在确认后再次触发计数加一。这个值对于分析反复报警的问题非常有用。2.3 为什么不用iQ-R自带的报警功能iQ-R的GX Works3里有一个报警功能可以设置报警条件、报警消息、报警历史。很多人会问既然PLC自带了为什么还要自己写FB我试过用自带的报警功能结论是简单场景够用复杂场景不够灵活。自带报警功能的问题在于报警消息是静态的不能动态拼接变量值。比如你想显示当前温度85度超过上限80度自带功能做不到报警历史存储在PLC的内部缓冲区容量有限而且导出不方便报警的确认和复位逻辑是固定的不能自定义不能做报警分级、报警抑制、报警延时等高级功能自己写FB虽然前期投入大一点但后面想怎么改就怎么改。而且FB一旦写好复用成本几乎为零。3. 用ST写报警FB的核心逻辑3.1 状态机的设计报警FB的核心是一个状态机。我一般用四个状态IDLE无报警ACTIVE报警触发未确认ACKED已确认但报警条件还在WAIT_RESET报警条件消失等待复位状态转移逻辑IDLE - ACTIVE: i_xTrig TRUE ACTIVE - ACKED: i_xAck TRUE ACKED - IDLE: i_xTrig FALSE AND i_xReset TRUE ACTIVE - WAIT_RESET: i_xTrig FALSE WAIT_RESET - IDLE: i_xReset TRUE这个状态机保证了几个关键行为报警触发后必须确认才能复位报警条件消失后如果没确认报警仍然锁存确认后如果报警条件还在报警保持激活状态只有确认且条件消失后复位才有效3.2 ST代码实现下面是我实际项目中用的报警FB的ST代码框架。这个代码在iQ-R上跑了三年多稳定性没问题。(* 报警锁存FB - 状态机部分 *) CASE i_nState OF 0: (* IDLE *) o_xActive : FALSE; o_xUnAck : FALSE; o_xNewAlarm : FALSE; IF i_xTrig THEN i_nState : 1; o_xActive : TRUE; o_xUnAck : TRUE; o_xNewAlarm : TRUE; o_tTrigTime : GetRTC(); o_nCount : o_nCount 1; END_IF; 1: (* ACTIVE - 未确认 *) o_xActive : TRUE; o_xUnAck : TRUE; o_xNewAlarm : FALSE; IF i_xAck THEN i_nState : 2; o_xUnAck : FALSE; o_tAckTime : GetRTC(); ELSIF NOT i_xTrig THEN i_nState : 3; END_IF; 2: (* ACKED - 已确认 *) o_xActive : TRUE; o_xUnAck : FALSE; IF NOT i_xTrig THEN i_nState : 3; END_IF; 3: (* WAIT_RESET - 等待复位 *) o_xActive : TRUE; o_xUnAck : FALSE; IF i_xReset THEN i_nState : 0; o_xActive : FALSE; o_dDuration : CalcDuration(o_tTrigTime, GetRTC()); END_IF; END_CASE;这段代码有几个关键点第一o_xNewAlarm只在状态0到状态1的转移中ON一个扫描周期。这个位用来触发报警记录和声音不能持续ON否则会反复触发。第二时间戳用GetRTC()函数获取。这个函数是我自己封装的内部调用iQ-R的RTC读取指令返回一个TIME类型的数据。用RTC而不是系统运行时间是为了保证断电后时间戳仍然有意义。第三o_dDuration在复位时才计算。这样即使报警持续了很长时间持续时间也能准确记录。第四o_nCount在每次触发时加一。这个计数在复位时不清零用来统计同一个报警的触发次数。如果需要清零可以加一个单独的复位引脚。3.3 时间戳的处理细节时间戳看起来简单但在PLC里处理起来有几个坑。坑一RTC读取的格式。iQ-R的RTC指令返回的是BCD码格式的年月日时分秒需要转换成TIME类型。我封装了一个GetRTC()函数内部做BCD到二进制的转换然后计算从某个基准时间比如2000年1月1日0点到当前的秒数返回DINT类型。坑二时间戳的存储。TIME类型在iQ-R里是32位有符号整数单位是毫秒。如果从2000年开始算大概49天就会溢出。所以我的做法是用DINT存储秒数需要显示的时候再转换成日期时间格式。坑三断电保持。如果PLC断电RTC靠电池保持但FB内部的状态变量比如o_tTrigTime如果不设置断电保持就会丢失。我的做法是把所有报警FB的状态变量都放在断电保持区域这样即使断电报警历史也不会丢。坑四时间同步。如果设备联网最好用SNTP做时间同步。iQ-R支持SNTP客户端功能可以定期从时间服务器同步RTC。这样多台设备之间的报警时间戳才能对齐方便集中监控。4. 报警历史的存储与导出4.1 用SD卡做报警记录iQ-R的CPU模块自带SD卡插槽这是做报警历史存储最方便的方案。我的做法是每个报警FB在触发时把报警信息写入一个数据记录文件。具体实现方式在SD卡上创建一个CSV文件比如AlarmLog.csv每次报警触发时用SP.DATWR指令把一行数据追加到文件末尾数据格式时间戳、报警名称、报警代码、优先级、触发次数这个方案的优点是简单、可靠、不依赖外部系统。缺点是SD卡的写入寿命有限不能太频繁地写。我的经验是每分钟写入不超过10次SD卡用个五六年没问题。如果报警触发太频繁可以做一个缓冲先把报警记录写到PLC的内部缓冲区每5分钟批量写入SD卡一次。这样既保证了实时性又减少了SD卡的写入次数。4.2 在HMI上显示报警历史报警历史存在SD卡上但操作工不可能去看CSV文件。所以需要在HMI上做一个报警历史画面。我的做法是用iQ-R的文件读取指令把CSV文件的内容读到一个字符串数组里然后在HMI上显示。具体步骤在HMI上创建一个报警历史画面用一个列表控件显示PLC里做一个FB定期读取CSV文件的最后N行把读取到的数据转换成HMI能显示的格式HMI通过周期通信获取这些数据这个方案的好处是不需要额外的SCADA软件用普通的GOT触摸屏就能实现。缺点是HMI的显示能力有限一般只能显示最近100条左右的记录。如果需要更长的历史还是得上SCADA或者数据库。4.3 报警数据的分析价值报警历史不只是用来查原因的它还有很大的分析价值。我有个客户是做食品包装的他们用报警历史数据做了一件很有意思的事分析设备的OEE整体设备效率。具体做法是把报警历史按时间段统计找出报警高发时段把报警按类型统计找出最主要的停机原因把报警按设备统计找出最需要维护的设备这些分析结果直接指导了他们的预防性维护计划。比如发现某个伺服驱动器在连续运行4小时后容易报过载他们就把维护周期从8小时调整为4小时停机时间减少了30%。这个案例说明报警锁存不只是为了看到报警更是为了用好报警数据。如果你的报警只是闪一下就消失这些分析根本无从谈起。5. 实际项目中踩过的坑5.1 报警抖动导致的误报这是最常见的问题。一个机械触点式的限位开关在设备振动时可能产生几十毫秒的抖动PLC扫描周期是10ms就会捕捉到多次触发。我的解决方案是在FB内部加一个可调的滤波时间。具体做法(* 报警滤波 *) TON_1(IN : i_xTrig, PT : i_tFilterTime); xTrigFiltered : TON_1.Q;i_tFilterTime默认设200ms可以根据实际情况调整。注意滤波时间不能太长否则真正的报警会被延迟响应。我的经验值是100-500ms具体看报警的紧急程度。还有一个更隐蔽的坑滤波时间设了但报警记录的时间戳用的是滤波后的时间。这会导致记录的时间比实际发生时间晚。如果对时间精度要求高需要在滤波前记录时间戳滤波后触发报警。5.2 多个报警同时触发时的处理当设备发生重大故障时往往会有多个报警同时触发。比如电源故障会导致所有伺服同时报警。这时候如果每个报警都弹窗、都响声音操作工根本处理不过来。我的做法是引入报警优先级和报警抑制机制每个报警FB有一个i_nPriority输入1-5级5级最高当高优先级报警激活时低优先级报警只记录不显示报警确认时按优先级从高到低依次确认这个机制在FB内部实现不需要外部逻辑。具体做法是在FB里加一个全局的最高优先级变量每个FB实例在触发时检查自己的优先级是否高于当前最高优先级。5.3 确认按钮的防抖和互锁确认按钮是操作工最常按的按钮也是最容易出问题的按钮。问题一按钮抖动。操作工按一下PLC可能检测到多次ON/OFF。如果FB的确认逻辑是边沿触发就会多次确认。解决方案是在确认信号上加一个100ms的滤波。问题二误确认。操作工可能不小心碰到确认按钮把没看到的报警确认掉了。解决方案是加一个确认使能条件比如只有在报警画面打开时确认才有效。问题三批量确认。有时候操作工想一次确认所有报警。我的做法是加一个全部确认按钮这个按钮触发一个全局的确认信号所有FB实例都响应。但要注意高优先级的报警不应该被批量确认必须单独确认。5.4 FB实例的命名和注释FB写好了但如果实例命名乱七八糟后面维护还是很痛苦。我的命名规范是fbAlarm_设备名_报警名。比如fbAlarm_Conveyor1_Overload、fbAlarm_Robot1_ServoError。这样在交叉引用列表里一眼就能看出是哪个报警。注释也很重要。我一般会在FB实例的注释里写清楚报警的触发条件报警的后果停机/降速/仅提示报警的处理方法报警的优先级这些注释在调试和交接时非常有用。我见过太多项目报警FB写得很好但注释一片空白接手的人根本不知道每个报警是干什么的。6. 从单机到产线报警FB的规模化应用6.1 报警FB的标准化当一个项目从单机扩展到整条产线时报警FB的数量会从几十个增加到几百个。这时候如果没有标准化维护成本会指数级上升。我的标准化做法是统一的FB接口所有报警FB用同一个接口定义不允许自定义引脚统一的命名规范报警名称、报警代码、变量名都有固定的格式统一的优先级定义1-5级每级的含义在项目文档里写清楚统一的确认逻辑所有报警的确认和复位逻辑完全一致这套标准一旦定下来新报警的添加就变成了填表格填上报警名称、触发条件、优先级剩下的FB自动处理。6.2 报警FB的集中管理几百个报警FB实例如果分散在程序各处查找和修改都很麻烦。我的做法是集中管理把所有报警FB实例放在一个专门的程序块里比如AlarmManagement每个FB实例的输入信号从其他程序块引用过来报警的输出信号统一映射到一个报警数据块供HMI和SCADA读取这样做的优点是报警逻辑集中修改方便报警数据集中HMI配置简单报警统计集中分析方便。6.3 报警FB的性能考量几百个报警FB实例同时运行对PLC的扫描周期会有影响。我的实测数据是每个报警FB实例大约消耗0.5-1微秒的扫描时间。500个实例大约消耗0.25-0.5毫秒对于iQ-R来说完全可以接受。但如果报警FB里做了复杂的操作比如字符串处理、文件读写扫描时间会显著增加。我的建议是字符串处理比如拼接报警消息只在报警触发时做一次不要每个扫描周期都做文件读写用异步方式不要阻塞主扫描报警历史的统计和分析放在低速任务里做不要放在主任务里iQ-R支持多任务可以把报警FB分成两个任务高速任务处理报警的触发和锁存低速任务处理报警的记录和统计。这样既保证了实时性又不会拖慢主程序。7. 几个值得注意的细节7.1 报警FB的初始化FB实例在第一次运行时状态变量可能是随机值。如果不做初始化可能会出现上电就报警的怪现象。我的做法是在FB里加一个初始化标志IF NOT xInit THEN i_nState : 0; o_xActive : FALSE; o_xUnAck : FALSE; o_nCount : 0; xInit : TRUE; END_IF;这个初始化只在FB第一次运行时执行一次。注意初始化标志也要放在断电保持区域否则每次断电重启都会初始化报警历史就丢了。7.2 报警FB的在线修改在调试阶段经常需要修改报警FB的逻辑。iQ-R支持在线修改但有几个注意事项修改FB定义时所有实例都会受影响。如果只想改一个实例需要单独修改在线修改可能导致FB的状态变量被重置。如果报警正在激活状态修改后可能会丢失修改后要重新下载FB定义和所有实例下载过程中PLC会停止扫描我的建议是调试阶段尽量在离线状态下修改修改完统一下载。如果必须在线修改先确认没有正在激活的报警。7.3 报警FB的版本管理FB库用久了会有多个版本。如果没有版本管理很容易出现这个项目用的是哪个版本的FB的问题。我的做法是每个FB都有一个版本号写在FB的注释里每次修改FB版本号加一并在注释里写清楚修改内容项目文档里记录每个项目使用的FB版本FB库用Git管理每次修改都提交这套做法看起来麻烦但在多项目并行的时候能省下大量的排查时间。7.4 报警FB的测试FB写好了怎么测试我的做法是单元测试单独测试一个FB实例模拟各种输入组合验证输出是否正确集成测试把所有FB实例连起来模拟真实报警场景验证报警显示、记录、确认是否正常压力测试同时触发大量报警验证PLC的扫描周期是否在可接受范围内异常测试模拟断电、通信中断等异常情况验证报警历史是否丢失这些测试在项目初期花的时间会在后期调试和运维中加倍省回来。我见过太多项目FB写得很好但没测试上线后各种奇怪问题。8. 写在最后报警锁存这件事看起来简单但要做好并不容易。它涉及状态机设计、时间戳处理、数据存储、HMI交互、性能优化等多个方面。一个设计良好的报警FB不仅能解决报警闪一下就消失的问题还能为设备运维提供宝贵的数据支持。我在多个项目上迭代出来的这套报警FB方案核心思想就是把报警当作数据来管理而不是当作信号来处理。信号是瞬时的数据是持久的。只有把报警数据持久化才能做分析、做优化、做预防性维护。如果你现在还在用SET/RST做报警锁存建议花点时间改成FB。前期投入可能是一两天但后面省下的排查时间和维护成本绝对值得。