ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

PLC子程序编程详解:从梯形图杂乱到模块化结构

PLC子程序编程详解:从梯形图杂乱到模块化结构 写PLC程序这几年我最大的体会是很多设备逻辑不复杂但梯形图一多就乱改一个动作要找半天触点调试的时候谁都救不了你。后来我把程序里那些反复出现的逻辑全拆成了子程序整个工程量直接从“一团毛线”变成了“抽屉柜”每个抽屉拉开就是一块完整功能互不干扰。这篇文章就围绕PLC里的程序控制指令重点讲讲子程序Subroutine这套东西怎么用、为什么该用、以及我踩过哪些坑。先说清楚一个概念PLC的程序控制指令是一类“改变程序走向”的指令的总称不只是子程序。它包含条件跳转CJ、子程序调用CALL、子程序返回RET、循环控制FOR/NEXT、中断返回IRET/DI/EI这些。子程序是其中用得最多、也最能改善程序结构的一类。这篇文章适合刚入门想着把程序写规整的PLC新手也适合已经在做非标设备、觉得梯形图越来越难维护的调试工程师。我会结合三菱FX3U、西门子S7-200 SMART、汇川H5U这三种主流PLC的实际指令把子程序的调用机制、实操写法、常见故障全部过一遍。1. 先认识整个程序控制指令家族子程序到底“被谁调用、又往哪返回”我一直觉得理解子程序的前提是先把程序控制指令这个家族捋清楚。PLC虽然叫“可编程逻辑控制器”但它的执行方式并不是像电脑那样“每次执行一行遇到函数就跳走”而是按照扫描周期从左到右、从上到下把用户程序从头执行到尾。程序控制指令干的事就是在这个固有扫描流程里“人为改变方向”。常见控制指令分四类第一种是跳转指令三菱里是CJ Pxxx西门子S7-200 SMART里是JMP作用就是跳过中间的某段程序不执行。第二种是子程序调用指令三菱是CALL Pxxx西门子是CALL SBRx作用是跳到子程序里执行执行完再回到主程序的断点继续往下走。第三种是循环指令三菱是FOR/NEXT用于重复执行某段逻辑固定的次数。第四种是中断指令比如三菱的EI/DI、IRET它不是靠扫描周期来触发的而是靠硬件事件比如高速计数器、定时中断触发优先级比主程序高得多。子程序指令在这四个类别里最特殊因为它是唯一一个“既要跳出去、又必须跳回来”的指令。跳转指令跳完就不回来了循环指令是在原地打转中断指令干脆进入另一个独立的程序环境。只有子程序调用完之后还要回到原来的位置继续执行CALL指令下面的那一条。这个“返回断点”的机制是子程序能够模块化的根基。你要是把这个机制理解错了后面写多层嵌套的时候一定会乱套。再往细了说三菱的子程序调用指令有CALL、CALLP还有FCALL这种所谓“文件调用”。这里的CALL是指定程序编号调用P后面跟的是程序号比如CALL P1就是调用程序号为1的子程序。CALLP则是上升沿触发方式的调用只在条件从OFF变ON的那一个扫描周期里执行调用动作。FCALL更特殊一点它允许在子程序内部再调用另一个子程序文件但实际上在FX3U这类小型机上用得很少绝大多数项目用CALL和CALLP就够了。西门子的方式和三菱不太一样。S7-200 SMART里主程序MAIN下面可以建很多子程序SBR_0、SBR_1……调用的时候直接用CALL指令加子程序名比如CALL SBR_0, 也可以带参数。西门子的主程序和子程序的关系更像“主程序是总调度子程序是被调度的工位”。它不像三菱那样有1~64的程序编号概念而是用符号名来区分这个在写复杂项目的时候反而更直观。这里有个关键认知子程序不是“复制粘贴的捷径”而是“控制程序执行边界的规则”。说它控制执行边界是因为PLC扫描周期的固有时序被你用CALL指令打断了。主程序执行到CALL时扫描周期并不会停止只是CPU把当前执行位置记下来然后跳到子程序入口开始执行执行到RET再恢复。大家要明白这个“当前执行位置”的保存是靠PLC内部的程序堆栈完成的。堆栈有深度限制三菱FX3U的嵌套层数最多是5级也就是说CALL套CALL不能超过5层超了会直接报程序错误或者运行时异常。这也解释了为什么子程序虽好用但不能无限嵌套——你的程序结构一旦深到五六层本身就说明设计不合理了。2. 子程序的底层设计逻辑为什么一段功能要单独“封装”起来很多人第一次接触子程序会觉得“这不就是把一段程序挪个位置吗”还真不是。子程序的价值不在于移动代码而在于建立“被调用”和“被忽略”两种状态。主程序里没调用某个子程序的时候那个子程序里面的代码一个扫描周期都不会去执行里面的输出点不会刷新定时器不计时计数器不计数。这一点极其重要因为它是子程序能同时实现“功能隔离”和“执行效率优化”的根本原因。2.1 用结构化编程的思路来理解子程序我经常打一个比方一台整机设备就像一条流水线主程序是车间主任子程序是每个工位的操作规程。车间主任不可能每个工位都亲自上手干活的——那样他一天到晚就在流水线上来回跑效率低还容易漏事。他只需要按流程说一句“一号工位开始”“二号工位停止”工位上的工人就按照自己手里的规程干活。子程序就是这个“工人手里的规程”主程序只负责决定“什么时候调用哪个工位”。这样拆完以后整个设备的逻辑就变成了主程序只关注设备整体状态流程比如上电复位、启动条件判断、自动/手动切换、报警汇总。子程序1控制气缸A的伸出、缩回、到位检测、超时报警。子程序2控制电机B的正转、反转、停止以及电流过载保护。子程序3处理触摸屏上设置的参数包括配方切换、数据上下限判断。这个好处是立竿见影的。你不必在主程序里翻几百行梯形图找气缸A的动作了直接看子程序1就行。改气缸A的时间参数也不会影响电机B的逻辑。这就像把原来写在一张大草稿纸上的所有公式重新整理成一本带目录的笔记本每一章只管一件事。我在做一个“十字路口红绿灯PLC程序”的练手项目时就在主程序里用了一个“交通模式状态机”把东西直行、东西左转、南北直行、南北左转四个相位分别放在四个子程序里。主程序里只有二十来行负责根据时间计数器切换调用哪个相位子程序。要改某个相位的绿灯时间只要进到对应子程序里改定时器设定值完全不影响其他相位。这种“大程序拆小块”的思路才是子程序真正的设计价值所在。2.2 扫描周期与调用机制为什么子程序能“按需执行”子程序的第二个底层优势是它能够做到“按需执行”这个能力源于扫描周期的工作方式。PLC的扫描周期分成输入采样、程序执行、输出刷新三个阶段。程序执行阶段CPU按顺序扫描所有指令。如果一条CALL指令的条件不满足CPU就直接跳过它继续往下扫描主程序的其他指令。只有当条件满足CALL才会把执行权交给对应的子程序。所以我常跟人说子程序调用是“条件驱动”的。也就是说子程序里那些代码不是每个扫描周期都在跑而是只有当触发条件成立的那个扫描周期才开始跑。假设你的主程序有500行子程序合起来还有1000行。如果不拆子程序那么每个扫描周期CPU都要把1500行全部扫一遍拆了子程序以后主程序可能只有200行剩下的1300行分布在各个子程序里只有被调用的时候才扫描。在CPU型号固定、扫描周期要求很短比如伺服跟随控制的项目里这种优化是实打实的性能提升。但这里又引出一个容易被忽略的问题子程序调用指令本身是有“执行次数”和“触发沿”的讲究的。很多人都知道普通CALL指令的输出条件如果是X0常开触点那么只要X0一直为ON每个扫描周期CALL指令都会被执行。如果X0一直为ON子程序就会被无数个扫描周期连续执行。这在很多场合其实是没问题的因为子程序执行完就回到原位继续扫描主程序属于“重复进入”不是“嵌套”。但如果你在子程序里用了定时器、计数器这类有状态保持的器件连续反复调用与只调用一次最终产生的结果和时序是完全不同的。这个坑我后面会专门讲。2.3 全局变量与子程序局部器件哪些数据会“串味”子程序设计还有一个绕不开的话题——变量作用域。三菱FX3U的编程里虽然有局部变量这个概念但很多初学者用的还是全局软元件比如M中间继电器、D数据寄存器。在主程序里用了M100子程序里也用了M100那么它们其实是同一个M100谁先执行谁就改变了它的状态。西门子S7-200 SMART要好一些它的子程序支持形式参数IN、OUT、IN_OUT和局部变量表真正的局部变量只影响子程序内部不会往外“串味”。我的建议是在中小型设备项目里哪怕用的是三菱这种全局软元件为主的环境也要人为规定一套“变量分区”规则。比如M0~M99主程序状态标志位。M100~M199子程序1内部使用的标志位。M200~M299子程序2内部使用的标志位。D0~D99触摸屏交互参数。D100~D199子程序1内部计算数据。D200~D299子程序2内部计算数据。这样虽然不能从语法上强制隔离但能在习惯上避免“串味”。我看到过不止一次现场故障就是两个子程序无意中用了同一个M点导致设备动作乱跳。如果项目是用西门子或者汇川的CODESYS平台能用局部变量就尽量用局部变量语法层面的隔离比任何人为约定都可靠。3. 三大主流PLC的子程序实操对比从指令写法到参数传递光讲理论有点虚直接上手看三个平台的实操写法。我分别用三菱FX3UGX Works2、西门子S7-200 SMARTMicro/WIN SMART、汇川H5UCODESYS做例子。3.1 三菱FX3UCALL指令与程序编号三菱FX3U的程序结构里主程序后面可以挂最多64个子程序P0~P63。每个子程序以P编号开始以RET结束。主程序结束用FEND指令这是个很容易忘的大坑——FEND是主程序结束的标志如果忘了写FENDCPU会以为主程序还没结束直接顺序往下“走”进子程序区域子程序就会在非调用状态下被反复当主程序执行。调用格式很简单。假设我有一个子程序P1负责温度PID算法触发条件是M50LD M50 CALL P1这里的执行语义是当M50为ON时每个扫描周期都会跳去执行一次P1执行完RET回到CALL下面一行继续扫描。如果我只想在M50从OFF变ON的瞬间调用一次就要用CALLPLD M50 CALLP P1CALLP是脉冲沿调用。那这种“只调用一次”有什么意义呢常见的就是数据初始化。比如设备上电时把配方参数从掉电保持区D区加载到运算区你只需要上电沿触发这一次就够了没必要每个扫描周期反复加载。这种场景如果用普通CALL程序没有任何实质错误但是每个周期都在做重复的无意义读取浪费扫描时间。用CALLP一次搞定干净利落。三菱的嵌套写法比如P1里又调用P2最多5层超出就报错“CALL OVER 5 LEVEL”。我实际写程序一般控制在2~3层能用一层解决就不上两层。另外还要注意三菱子程序里的输出点如果对应的硬件输出被强制了子程序里的写入是不生效的这在调试强制输出的时候很容易把人搞懵。子程序的“程序号”要避开主程序占用的编号同时要养成给程序编号写注释的习惯。GX Works2里双击P编号就可以给子程序命名比如P1注释写“温度PID计算”P2写“气缸顺序控制”这样维护起来一目了然。我见过一个设备程序里几十个子程序全是P0、P1这种无注释名字出问题的时候根本没法快速定位是哪个功能块——这种程序维护成本非常高。3.2 西门子S7-200 SMARTSBR子程序与带参调用西门子的组织方式更“现代”一点。项目树里默认有主程序MAIN可以添加SBR_0、SBR_1等多个子程序。调用方式是在梯形图里放置CALL指令下拉选择要调用的子程序比如CALL SBR_0。如果没有参数传递写法就是CALL SBR_0S7-200 SMART的梯形图里CALL指令有个梯形图符号条件满足时执行。它默认的调用方式和三菱的CALL一致——条件为ON时每个扫描周期都调用。如果想做“上升沿调用”梯形图里要在调用指令前面串联一个“上升沿检测触点”P触点或者用SM0.0常ON配合上升沿。西门子子程序的精华在于支持带参数调用。你在SBR_0的属性里可以编辑局部变量表定义IN类型输入参数、OUT类型输出参数、IN_OUT类型输入输出参数。比如我要做一个“电机启动延时统计”的子程序输入参数是启动信号bStart输出参数是运行时间iRunTime// SBR_0 局部变量定义 // L0.0 IN bStart : BOOL // 启动信号 // LW1 OUT iRunTime : INT // 运行时间调用的时候直接写CALL SBR_0, I0.0, VW100这样I0.0的值作为输入传给子程序内部的bStart子程序算出的运行时间写到VW100里。好处是同一个SBR可以被不同设备部位复用了——只要传入不同的输入参数、输出地址一套逻辑就能管好几个数据块。这种“一套代码多处复用”是子程序最有价值的使用方式比三菱那种纯全局软元件的方式更安全。在Micro/WIN SMART里新建子程序以后别忘了给它起个有意义的名字。我习惯用“设备_功能”的格式比如“Mixer_StirCtrl”“Pump_AlarmMon”。S7-200 SMART的CALL指令支持中文注释但子程序名最好用英文不然部分版本在符号表里显示会乱码。3.3 汇川H5U从FB函数块到结构化文本到了汇川H5U这种支持CODESYS平台的PLC子程序这个概念就更高级了。它不再叫“子程序”而是叫POUProgram Organization Unit程序组织单元包含程序PROGRAM、函数功能块FB、函数FUN三种类型。和传统PLC子程序最贴近的是PROGRAM类型它相当于一个独立的程序单元可以被主程序调用FB则是一种带内部存储的“类子程序”支持输入输出参数和内部变量适合封装需要记忆状态的控制逻辑。在汇川H5U里调用一个FB的写法大概是这样的用结构化文本ST语言写stPump : PumpControl( bEnable : xEnable, bRunCmd : xManualRun, rSetPoint : rSpeedRef, rFeedback : rActualSpeed, eFaultCode wFaultCode );这里PumpControl是一个功能块每次给它传入使能信号、运行命令、设定值、反馈值它内部处理完以后把故障码输出到wFaultCode。这个FB可以被调用多次分别控制三台泵只需定义三个不同的FB实例变量。这比传统PLC子程序单副本的方式又进了一步——传统子程序的内部软元件是固定的同一个子程序被多段逻辑同时用到时还得小心数据打架而FB天然支持“多实例”每个实例有自己的内部存储空间。对刚从三菱、西门子转过来的工程师我建议先从PROGRAM入手把它理解成“西门子带参数的SBR”就行等把结构化文本的语法跑顺了再过渡到FB。不要一上来就啃面向对象那一套PLC程序的本质仍然是“输入-处理-输出”FB封装的是处理逻辑不是业务模型。4. 子程序的进阶用法与现实场景拆解从PID到数采再到多工位调度子程序不是只能做“把功能拆出来”这么简单它真正强大的地方在于把一套逻辑模板化然后用不同参数去实例化。下面结合几个真实项目场景讲讲怎么玩。4.1 温度PID控制把算法封装进子程序很多初学者在PID控制上栽跟头主要是因为PID运算和模拟量采集、输出刷新混在主程序里扫描周期不固定导致微分项抖动。我做一个“基于PLC的冷库监控系统设计”时直接在子程序里封装了温度PID模块。子程序接受五个输入设定温度SV、当前温度PV、比例带P、积分时间I、微分时间D输出是阀门开度控制值MV。关键点是PID运算有内部状态——积分项累积值和上一次的偏差值——这些状态必须在子程序内部用保持型寄存器保存。如果用普通寄存器每次子程序调用结束以后内部变量被清零积分项就永远累积不起来温度会一直稳态偏差。我的做法是用带断电保持区的D寄存器保存积分项并把“采样周期”作为一个参数也传进去确保PID运算的扫描频率是可控的。这里有个非常实用的子程序写法用SM412三菱里每10ms变化一次的时钟脉冲或者用固定扫描周期模式。不要让PID运算跟着主程序扫描周期“随缘跑”否则温度PID的波动会很大温差忽高忽低。说实话网上很多人问“PLC温度PID波动温差大如何调节”十有八九不是PID参数不对而是子程序调用方式和采样周期没固定住导致运算间隔不均匀。把子程序做成“定时调用”后温差一般能收敛一半以上。4.2 多工位设备调度状态机子程序的组合拳非标设备里最常见的结构是多个工位轮流动作。我之前做过一个三工位装配机每个工位有独立的夹紧、进给、检测动作。用子程序实现的核心思路是主程序里放一个状态机状态寄存器的值决定“调用哪个工位的子程序”。比如状态值为1时CALL P11工位1动作状态值为2时CALL P12工位2动作状态值为0时什么都不调设备在等待。这种设计最大的好处是——工位子程序内部逻辑完全独立工位1的调试不影响工位2。而且状态机本身只进行状态判断、转移结构极清晰。在设备联调的时候我可以单独把某个工位的子程序强制调用起来测试不影响其他工位的执行。调试效率比单块大梯形图高太多了。你还可以利用子程序“不被调用就不执行”的特性做安全联锁。比如在手动模式下主程序根本不调用自动运行子程序那么自动运行子程序里即使有驱动电机的指令也不会执行相当于多了一层软安全保护。这种“通过调用与否来控制整个功能块是否激活”的思路比在子程序内部加一堆使能位判断要可靠得多因为不执行的代码逻辑上再也不可能误动作。4.3 通讯与数采场景子程序如何管理Modbus与OPC UA数据流聊到热词里频繁出现的Modbus、OPC UA读取PLC数据子程序在这种场景下的角色往往是“数据帧组织/解析”和“周期刷新”。我做数据采集时比如用上位机组态王、WINCC通过Modbus TCP读PLC往往会在PLC里写一个通讯协议处理子程序。子程序负责把设备状态、温度、压力等运行数据打包到连续的保持寄存器区这样上位机只需要连续读一段寄存器就能拿到所有数据不需要一个地址一个地址去读。这里有个很典型的组织方式PLC内部数据区分成“运行数据区”和“参数设定区”。子程序每隔固定时间把分散在各处的运行数据比如设备状态字、当前温度、当前压力搬运到连续的“下发数据区”D100~D120同时把“参数设定区”D200~D220的数据搬运到各功能子程序里去使用。这个搬运和映射的过程全部在一个“数据管家”子程序里完成主程序不碰这些地址。这样Modbus通讯和OPC UA服务器读到的数据始终是有序且无冲突的项目上位机联调的时候很少出现数据错位问题。还有一点值得提现在很多人关注“AI PLC代码生成”其实好的AI辅助编程也特别依赖子程序结构。你把一个设备的I/O表、动作流程写成注释或伪代码发给AIAI比较容易生成带清晰的模块划分的子程序框架。如果你把整个程序需求一股脑给AI让它生成一个巨型梯形图生成出来的东西基本没法用。子程序化不仅有利于人维护也有利于AI生成更规范的PLC代码这一点算是新技术时代对程序结构提出的新要求了。4.4 多轴机械臂与伺服轴控子程序里的运动控制封装热词里还有“PLC管理六轴机械臂伺服”“PLC电机顺启逆停定时器”这些场景同样适合用子程序封装。机械臂的控制通常有两种模式PLC直接发脉冲/总线指令控制伺服或者PLC与机械臂控制器通过Modbus/IO通讯交换指令。无论哪种主程序都不应该把整条机械臂动作宏命令码写在主扫描区而是应该封装成“动作执行子程序”。比如某机械臂需要依次完成“抓取→搬运→放置”流程。我定义了一个“机械臂搬运”子程序子程序内部通过步进状态字一步步发指令、等待完成反馈、再发下一步指令。主程序只需要在到达搬运时机时调用这个子程序并根据子程序输出的“完成标志”来切换主状态机。子程序内部的等待时间、超时判定都放在里面主程序不被这些细节拖累。伺服控制还有一点要注意伺服的使能信号和急停信号这种安全关键信号尽量不要放在子程序里而要放在主程序每周期都扫描的段里保证急停响应的时间最短。子程序适合“命令类动作”安全类逻辑永远保持在全时扫描的主程序里这个边界一定要分清楚。我在实际调试里见过有人把急停逻辑放进了子程序结果条件没触发就不扫描急停按下毫无反应这种设计错误是绝对应该避免的。5. 踩坑实录与排查技巧这些年我掉进去过的子程序的坑子程序用好了是神器用不好是隐形炸弹。下面这几个问题几乎每个项目都大概率碰到过我整理成速查表每条都附上排查思路。常见问题现象根因排查思路子程序里的线圈在主程序也被使用设备动作乱跳、输出异常双重线圈同一Y地址被两处赋值查找Y地址交叉引用统一所有权子程序里用了定时器但不被触发定时器偶发不动作子程序处于非调用状态定时器不计时确认调用条件必要时改用累计型定时器主程序忘了FEND/RET子程序代码被当主程序反复执行程序区域边界未定义检查FEND、RET是否成对出现嵌套层数超过限制报程序错误CPU停机CALL套CALL超过5层查看错误码减少嵌套层数子程序内部用了全局M与另一子程序冲突设备偶尔误动变量作用域未隔离规划变量分区或改用局部变量/FB条件调用与上升沿混用子程序执行次数不对对CALL与CALLP语义不熟对比需求选择普通调用或脉冲调用子程序内PID采样时间间隔不均温度波动大、PID发散PID运算扫描周期未固定固定采样周期子程序定时调用带参数调用时参数类型不匹配编译报错或结果错误IN/OUT参数定义错误检查局部变量表类型和长度5.1 双重线圈问题怎么查双重线圈是子程序最容易引发的隐性故障。一个Y输出点在主程序通电了一部分又在子程序里控制同一动作两个地方都写了线圈PLC在编译时往往只是警告不报错。程序扫描到最后一次写入的线圈值才生效——子程序调用顺序靠前主程序的线圈写在后面那么主程序会覆盖反过来也一样。排查方法就是利用编程软件里的交叉引用表搜一下这个Y点在哪些程序块里出现过一旦出现两处以上线圈赋值就必须重构。另外三菱的编程软件里双击错误信息能跳转到出现重复线圈的位置西门子Micro/WIN SMART也能看到符号表多出红色的重复标记。实际排查的时候不要只盯着梯形图看要用交叉引用和地址使用一览表效率高得多。5.2 嵌套堆栈到底怎么理解三菱的嵌套限制是5层西门子没有明确说层数但也不建议超过8层。理解这个限制的关键在于“堆栈是有空间上限的”。每次CALL都往堆栈里压入一个返回地址RET时弹出。压入次数太多堆栈溢出程序直接进入停机状态。这个报错通常出现在程序运行中而不是下载时所以现场突然停机排查起来很迷惑。我一般会做一个“调用层级清单”把程序涉及的所有子程序关系画成一张树状图纯手写或者用表格列。每一层最多几个子程序、哪个子程序由谁调用都列清楚。超过三层的就要简化——把子程序内部再拆分或者把参数化的逻辑合并。这套方法在维护旧设备程序时尤其有用能快速看明白整个程序的调度骨架。5.3 条件调用中的定时器与计数器陷阱这一条简直是我强调过最多遍的坑。三菱FX3U的T0~T199是普通定时器它的计时是在扫描周期里累计的。如果T0所在的子程序在某个扫描周期没被调用那个周期T0就不计时。这意味着子程序的定时时间不完全等于“真实世界的时间”而是约等于“被调用的扫描周期次数 × 每个扫描周期时间”。如果有中间几个周期没调用定时时间就会拉长。很多人在十字路口红绿灯、抢答器这类项目里觉得红绿灯切换时间不准、抢答锁定时间漂移原因就在这。解决的办法有三个一是把定时器放在主程序里主程序每周期都扫描计时准二是用累计型定时器配合触发沿做累计计时三是用专门的定时中断子程序把定时功能放到固定扫描周期里执行。我在项目的定时精度要求从严的地方比如触摸屏上设定1秒钟的等待时间都是用第一种方案——把时间到标志做成位变量子程序里判断这个标志。5.4 编译错误与运行错误速查三菱FX3U常见报错6405、6409这些往往和子程序区域有问题有关。6409经常表示“无FEND”或者“程序步超出范围”西门子常见的错误是“无RET”“调用参数的局部变量表没有正确初始化”。碰到编译报错先别急着改程序按这个顺序检查看FEND/RET是否配对、看CALL的目标编号/名称是否存在、看嵌套层数是否超限、看符号表是否有重名。运行中偶发故障怎么抓建议开着编程软件的在线监控重点观察子程序调用条件的变化时机。如果某次故障发生前X0、M20有一个瞬时抖动子程序被误调用了一下那问题就在外围信号或者触点抖动上。子程序调用条件的可靠性是整个程序能不能稳定运行的关键——宁可多写一个联锁条件也不要裸奔触发。调试子程序还有一个非常实用的技巧在子程序入口放一个M0上升沿把入口时间戳存到D寄存器里在RET前面再放一个M0下降沿把出口时间戳存到另一个D寄存器。这样就能精确测得这个子程序每次实际占用了多少扫描时间。这个技能在排查扫描周期超时问题的时候简直是“照妖镜”能秒杀很多找不到原因的偶发故障。6. 最后分享一点个人的习惯我在实际项目里一般会把子程序当作“设备操作说明书”来写——每个子程序开头用注释写清楚这个模块的I/O清单、运行前提、输出结果里面用注释标注每一步动作的工艺目的。有人觉得注释浪费时间但等到设备卖了三年、客户换了两拨工程师、原程序再拿出来维护的时候那几句注释比什么都值钱。编写规范的习惯具体到动作上就是给每个子程序起名和注释、给每个参数写类型和范围越是大型项目越要坚持。再给一个小技巧子程序里的步进逻辑尽量用“当前步号寄存器比较触点”的方式写不要用一堆连续的SET/RST去控制M点。步进寄存器加调用配合触摸屏显示当前步号排查问题的时候严重事半功倍。这套模式我在三菱、西门子、汇川上都用过结构几乎一模一样只是指令助记符略有差别。子程序这东西初看是“指令”用久了你就会理解它其实是“程序架构”。把整个设备拆成一个个边界清晰的子程序维护、调试、交接都会轻松很多。这篇文章从指令机制讲到三大平台的实操写法再到具体应用和排查技巧基本覆盖了我这些年做PLC项目时和子程序打交道的全部经验。如果后面你在项目里遇到子程序相关的奇怪故障顺着上面的排查表一条条过大概率能在几分钟内找到方向。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进