设备断电再上电,触摸屏上统计好的当班产量直接归零,操作工在界面上设的那组配方参数全部回到默认值。老师傅站在屏前盯着看了半天,问我:“这PLC不是有断电保持吗?咋数据还是丢?”我说:“因为这些东西从头到尾就没进过PLC。”
这个场景在产线上太常见了。很多项目的数据分两部分:PLC寄存器里的,停电后靠PLC自带的掉电保持区或者电池撑着;另一部分是在HMI上临时维护的,比如画面上的累计产量、操作工现场调的补偿值、本地统计结果,这些默认存在威纶通HMI的LW寄存器里。LW本质上就是内存里的字,一掉电全没了。要把这部分数据救回来,核心就落在两个关键点上:RW寄存器做掉电保持的载体,宏指令在恰当的时机把数据从临时区搬运到保持区。
这篇文章我从寄存器原理讲到宏代码实现,再聊几个实际踩过的坑,包括不少人问的“未定义导致工程文件无法打开”的排查办法,尽量把能直接抄作业的部分都给你。
1. 你的LW数据为什么一断电就丢:先把存储机制搞明白
1.1 LW寄存器的真实身份:本质上就是RAM
LW在威纶通里叫本地字寄存器(Local Word),它比RW响应快、使用直接,画面上的数值显示、运算中间量、临时开关状态,基本都是通过LW和LB来做的。但你要清楚一个底层事实:LW的存储介质就是易失性内存,掉电即失。它就像一块白板,写满字一擦全没了。
这不是威纶通的设计缺陷,几乎所有不带掉电保持电路的HMI都是这个逻辑。本地寄存器默认就是易失的,因为它本来就没打算让你长期存放什么重要数据。
很多新手第一次做项目,把操作工输入的补偿值、光标位置、临时配方全都放在LW里,觉得“只要屏开着数据就在”。结果意外停电20秒,再上电全没了,操作工当场就急。这就是没搞清楚LW属性的代价。LW适合做运行时的“工作台”,不适合当“仓库”。
1.2 RW寄存器才是HMI自己的“掉电保险箱”
RW(Recipe Word,官方叫配方寄存器)就不一样。它落在非易失性存储区,你把数据写进去,断电再上电,数据依然还在。如果只是在HMI本地保留一些不想每次重新输入的参数,RW就是现成的“保险箱”。
RW还有一个我非常看重的特点:它在宏指令里的读写方式和LW几乎完全一致,用的还是GetData/SetData那一套函数。也就是说,你不需要额外学一种新的编程模式,只需要把目标寄存器类型从LW换成RW,数据就拥有了断电保持能力。
如果只是保存单个开关量,还可以用RB(Recipe Bit),也就是保持型位寄存器,一个位存一个状态,省空间。实际项目里,RW和RB经常搭配使用:一组参数用RW连续区域存,几个状态位用RB单独存,互不干扰。
1.3 为什么PLC有断电保持还是不够
这个问题每次做项目都会被问一遍。PLC的断电保持区确实能干这个事,比如三菱的锁存D区、西门子的保持性M区,断电后数据能留住。但PLC断电保持只管PLC地址空间里的数据,管不了HMI本地的内容。
实际现场最常见的情况是这样的:PLC只管电机启停、气缸动作、模拟量采集,而操作工在触摸屏上设置的设备参数、当班产量、检具补偿值,很多时候只存在HMI内部,PLC根本不知道这些数据的存在。PLC保持区再大,也救不了HMI自己内存里的东西。
换句话说,HMI本地的重要数据要想扛住断电,最直接的办法就是让HMI自己的RW寄存器来管。硬要绕道PLC再回来,不仅通信链路复杂,断电瞬间通信是否还能保证也是个问题。
2. RW寄存器不只是配方柜:容量规划与寿命这笔账
2.1 在EBPro里确认你的RW容量和型号差异
拿到一个新屏,别急着写宏。先在EasyBuilder Pro里把工程建好,在系统参数设置里找到寄存器相关配置页,确认当前HMI型号支持的RW地址范围。不同尺寸、不同系列的屏,RW地址数量会有差异,但中小尺寸屏通常以千字为量级,RW_A扩展寄存器的容量更充裕,适合存放大量配方。
大多数断电保存场景,用普通RW就够。RW_A更适合那种配方特别多、一组配方几十上百个字的场景,比如注塑机、橡胶硫化设备这类需要管理大量工艺配方的设备。
2.2 三类寄存器怎么选:一张表说清
| 寄存器 | 全称 | 是否掉电保持 | 典型用途 |
|---|---|---|---|
| LW | Local Word | 否 | 画面显示、临时计算、通信缓冲 |
| LB | Local Bit | 否 | 临时标志位、位状态 |
| RW | Recipe Word | 是 | 参数保存、断电保持数据、配方 |
| RB | Recipe Bit | 是 | 保持型开关位 |
| RW_A | 扩展Recipe Word | 是 | 大批量配方、历史参数 |
简单记法:LW和LB是“临时工”,RW和RB是“正式编制”。临时工负责跑得快,正式编制负责靠得住。
2.3 地址规划建议:建立一个LW到RW的映射表
RW寄存器虽然好用,但不能随手乱用,否则项目做到一半自己都记不清哪个地址存了什么。我的习惯是开工之前先建一张地址映射表,把运行区和保持区的对应关系定死:
| 数据内容 | LW地址(运行时) | RW地址(保持区) |
|---|---|---|
| 批次号 | LW100 | RW0 |
| 配方参数1-10 | LW101-LW110 | RW1-RW10 |
| 当班产量 | LW120 | RW11 |
| 设备状态标志 | LB50 | RB0 |
有了这张表,写宏的时候一目了然,后期想加减数据项也不用满工程翻代码。团队协作时,这张表还能直接贴到设计文档里,减少沟通成本。
2.4 Flash写入寿命这笔账:不能每秒都写
RW能掉电保持,底层一定依赖Flash一类的非易失性芯片。这类芯片的写入寿命是有限的,绝大多数HMI规格书上不会把这个参数写得很显眼,但写宏的人心里要有数:次数大致在10万次量级,不同型号有差异,数量级不会差太多。
这个数量级意味着什么?我帮你算笔账:
- 每秒往RW写一次,一天就是86400次,10万次寿命不到3天就耗尽;
- 每小时写一次,一天24次,10万次大概能撑11年;
- 每天写10次,能撑27年以上。
结论只有一句话:RW适合低频写入,不适合高频累加。那些每个扫描周期都在变、每秒都要更新的数据,老老实实放在LW里,隔一段时间或者等数据变化稳定后再往RW搬运。这个认知,直接决定下一步保存策略怎么选。
3. 三种保存策略的取舍:实时写、周期写还是断电触发
3.1 实时写:每次数据变化就同步
如果数据修改频率很低,比如一个班才改几次的工艺参数,最简单的方法是在每个修改控件的“修改后”宏里,直接把对应LW值SetData到RW。
优点非常明显:同步即时,断电损失最小。 缺点也明显:项目里修改控件一多,容易漏绑一两个;另外如果参数在运行中会频繁变化,实时写会比较快地消耗存储寿命。
适合场景:配方参数、检具补偿值、操作权限设置这类“低频但重要”的数据。我一般建议这种策略只用在最关键的几个参数上,不要全场铺开。
3.2 周期写:定时把LW整体同步到RW
这是我在实际项目里用得最多的一种方案。新建一条宏,把保存代码写在里面,然后把宏的触发方式设为“周期执行”,间隔根据数据重要程度从几十秒到几分钟都行。
优点:代码集中、结构清晰、维护方便,所有需要保持的数据在同一个宏里统一搬运。 缺点:从“最后一次同步”到“断电那一刻”之间的数据会丢。
怎么取舍?举个例子:当班产量这类数据,断电前丢了最近1分钟的量,重新上电后产量少了一点点,操作工基本能接受;但如果是正在校准的传感器零点补偿值,丢一次就得重新校准,这就不能用单纯的周期写。
所以我实际项目里的习惯是“周期写为主,关键数据实时写为辅”:常规数据一分钟同步一次,几个最要命的修正值单独挂实时保存宏,两边互不影响。
3.3 断电触发写:看着很美,实际要掂量
有团队用过“断电检测触发”方案:PLC电源侧加断电检测电路,检测到掉电时在保持区置一个标志位,HMI轮询到这个标志后立刻执行宏,把关键数据写进RW。
理论上非常完美:只在断电瞬间写一次,又及时又省寿命。但实际操作有几个隐藏问题:
- 从PLC检测到掉电到系统真正崩溃,中间通常只有几十到几百毫秒的窗口;
- 宏解释执行需要时间,数据量大了可能写不完;
- 如果HMI和PLC走以太网通信,掉电瞬间链路往往已经不稳定,HMI不一定能及时收到信号。
如果非要用这个方案,HMI侧需要加一个小UPS或者至少足够大的电源电容,给保存宏留出几百毫秒的供电窗口。这个方案适合断电无法预知、数据量又大、必须追到最后一刻的场景。对绝大多数普通项目而言,实时写加周期写已经足够可靠,没必要为了“完美”额外引入硬件可靠性风险。
3.4 三种策略对比
| 策略 | 数据丢失窗口 | 写寿命消耗 | 维护复杂度 | 适用场景 |
|---|---|---|---|---|
| 实时写 | 毫秒级 | 高 | 中 | 低频重要参数 |
| 周期写 | 同步间隔 | 低 | 低 | 产量、运行时间等常规数据 |
| 断电触发 | 取决于硬件 | 很低 | 高 | 断电前兆可检测的大型设备 |
4. 宏指令搬运数据的完整实现与逐行拆解
4.1 先规划一个具体场景
假定一台包装机,需要断电保存三个数据块:
- 批次号,1个字,运行期间存在LW100;
- 配方参数,10个字,运行期间存在LW101-LW110;
- 当班产量,1个字,运行期间存在LW120。
RW保持区规划成:
- RW0 存批次号;
- RW1-RW10 存配方参数;
- RW11 存当班产量。
4.2 保存宏:把LW搬到RW
macro_command main() short batch_no short recipe[10] short output_count // 第一步:从LW运行区读取需要保存的数据 GetData(batch_no, "Local HMI", LW, 100, 1) GetData(recipe[0], "Local HMI", LW, 101, 10) GetData(output_count, "Local HMI", LW, 120, 1) // 第二步:把读到的数据写入RW保持区 SetData(batch_no, "Local HMI", RW, 0, 1) SetData(recipe[0], "Local HMI", RW, 1, 10) SetData(output_count, "Local HMI", RW, 11, 1) end macro_command这段代码有几个人容易写错的地方,我逐个说:
- 数组声明和GetData的配合:
short recipe[10]声明了一个10个元素的数组,GetData(recipe[0], "Local HMI", LW, 101, 10)表示从LW101开始连续读10个字到recipe[0]到recipe[9]。这个“起始地址+读取长度”的组合是宏指令的基本玩法,一定不能搞混顺序。 - 数据类型匹配:LW和RW都是16位的字,用short去对应。如果你把一个32位的int数据存在LW120和LW121两个字里,宏里就要声明int变量,
GetData(my_int, "Local HMI", LW, 120, 1),此时长度参数写1代表一个32位数据、占两个连续字,不能机械理解成“1个字就是1个寄存器”。这块是新手最容易懵的地方。
4.3 恢复宏:开机把RW读回LW
macro_command main() short batch_no short recipe[10] short output_count // 从RW保持区读回备份数据 GetData(batch_no, "Local HMI", RW, 0, 1) GetData(recipe[0], "Local HMI", RW, 1, 10) GetData(output_count, "Local HMI", RW, 11, 1) // 写回LW运行区,让画面和逻辑恢复正常使用 SetData(batch_no, "Local HMI", LW, 100, 1) SetData(recipe[0], "Local HMI", LW, 101, 10) SetData(output_count, "Local HMI", LW, 120, 1) end macro_command把这个宏绑定到工程启动或者第一个窗口的“打开时宏”,上电后它会自动执行,画面显示值就恢复到断电前的状态。
4.4 触发条件怎么设置
EBPro里宏的触发方式比较多,常用的有这几种:
- 周期执行:适合定时同步,在宏属性里勾选“周期执行”并填间隔时间即可;
- 窗口打开或关闭时执行:适合初始化和清理;
- 按钮触发:适合手动保存操作;
- 工程启动时执行:适合上电恢复。
我实际项目里的组合是:保存宏用周期执行,间隔60秒;恢复宏挂首窗口打开时;真正要命的几个参数,单独在修改按钮上挂实时保存。这样整体既稳又省存储寿命。
4.5 宏指令常见编译问题排查清单
- 变量未声明:从别处复制代码段时经常漏掉声明语句,导致编译报未定义;
- 类型不匹配:int和short混用导致数据错位;
- 地址越界:RW地址超出了当前型号的可用范围;
- 长度参数错误:Count单位搞错,导致多读或少读;
- 字符串误用:宏变量里用了字符串但没按字符串API处理。
这些错误在编译输出窗口都会给出位置提示,按提示定位基本都能解决。解决完了再顺手把LW-RW映射表核对一遍,确认没有地址重叠或者越界。
5. 上电恢复、Flash寿命翻车与“未定义”报错怎么处理
5.1 上电恢复要接受的现实:总有一个“数据丢失窗口”
任何基于RW加宏的断电保存方案,都没法保证“断电那一瞬间的数据100%不丢”。最后一次成功备份之后新产生的变化,理论上都会丢,差别只在于窗口大小:实时写是毫秒级,周期写是几十秒到几分钟。
我一般在设计阶段就会跟客户把这一点讲清楚:系统恢复的是“最近一次成功备份”的数据,不是“断电瞬间”的数据。如果客户连这一点都不能接受,唯一的正解是加UPS或者使用带超级电容等掉电保持机制的控制系统,让HMI和PLC在断电后还能完整走完保存流程,这已经不是软件层面能单独解决的事了。
5.2 Flash寿命翻车的典型表现与应对
RW寄存器依赖存储芯片,频繁写确实会出问题。真把RW写废的时候,HMI表现出来的症状通常是:数据写入偶尔失败、保存的数据随机丢、严重时系统启动异常。
如果项目里出现“写次数并不多但还是丢数据”的情况,先检查是不是同一个RW地址被多个宏同时写,或者某个循环宏在疯狂执行写操作而没有限流。宏里的延时和条件判断不是摆设——频繁写之前加一句“数据有变化才执行”,往往能挡掉90%的无效写入。
如果确实有高频写躲不开的场景,优先考虑外部存储扩展,比如把数据按批次写入CSV到U盘或SD卡,而不是死磕RW寄存器。
5.3 “未定义”导致工程文件无法打开:排查链路分享
再聊一个很多人遇到的典型问题:威纶通HMI工程文件打不开,报“未定义”。我实际遇到过两次,都是在处理别人移交的工程时发生的,一打开EBPro编译,提示某处未定义,工程窗口要么根本不弹出来,要么弹出来了但没法正常编译运行。
我建议的排查链路是:
- 先看编译输出窗口的报错信息。EBPro一般会把“未定义”的标识符和位置列出来,双击报错行,通常会跳到宏编辑器对应的那一行。
- 检查那一行是不是写了不存在的变量名、函数名,或者把寄存器类型拼错了。最常见的原因是复制了同事的代码,里面引用了一个当前工程标签库里根本不存在的变量。
- 如果报错只在某个宏里,优先在那个宏里排查;如果整个工程大量报未定义,优先怀疑标签库或配方数据库文件损坏、或工程版本不兼容。
- 定位不到就找备份。没有备份的话,新建一个空白工程,把窗口、宏、数据元件分批复制过去,每复制一批就编译一次,反复几次,很快就能把问题模块收敛出来。
这个“分批迁移、逐步编译”的办法,比对着代码一行行猜效率高很多,本质上就是二分法,我强烈推荐。
再给一个预防建议:宏代码里尽量少用标签库变量名,直接用LW和RW地址。标签库一变动,宏里面引用变量的地方全部会变成未定义;而直接用寄存器地址的宏,基本不会受标签库变化影响,工程的可迁移性和健壮性都会好很多。
最后再分享一个调试习惯:每一台HMI首次上电调试时,我会故意做三次断电测试。分别在刚保存完、保存后10秒、接近同步周期时断电,然后重新上电观察数据恢复情况。这三次测试能直观暴露同步策略的薄弱点,比看十遍代码都管用。别嫌麻烦,断电保存这种事,真到现场出问题再补,代价就大了。