
简介这是面向PLC开发者的H5U-ST简易编程手册聚焦IEC 61131-3结构化文本ST编程语言帮助初学者快速掌握工程创建、变量声明、字元件类型定义及流程控制指令的编写方法。资源为PDF文档仅1个文件压缩包大小约1.21MB目录清晰可离线或在移动设备上随时查阅。手册详细展示了在AutoShop中新建ST工程、插入ST子程序/FB/FC的完整流程并说明D0:D、D0:E等字元件类型写法系统梳理了IF、CASE…OF、WHILE…DO、REPEAT、EXIT、RETURN、FOR七类流程控制指令的语法与执行逻辑提示了顺序、边界和防死循环的要点。此外还列入了MC_Power、MC_MoveAbsolute等基础轴控指令以及凸轮、齿轮指令概览便于运动控制开发参考。已有2043人学习该资源适合自动化工程师、PLC程序员及工控领域学习者作为ST编程的案头速查手册。1. H5U-ST 简易编程手册到底解决什么问题一个从梯形图转 ST 的现场工程师视角很多人第一次看到“H5U-ST 简易编程手册”这个标题会以为它只是一份入门文档。实际上它对应的是一个非常具体的现场诉求你在 H5U 系列 PLC 上做逻辑控制但梯形图程序越改越乱几十个互锁点、一堆中间继电器看得头晕听说 ST 语言能让代码密度高一个数量级想知道值不值得切、怎么切、切的时候会踩什么坑。ST 的好处是逻辑表达紧凑复杂联锁和状态流转用几行文本就能写清楚但反直觉的结论是ST 写不好翻车速度和梯形图一样快坑甚至更隐蔽。下面按“先立概念、再上手写、最后排坑”的顺序把 H5U-ST 这条路从头走一遍。适合两类人一类是从梯形图转过来的老现场另一类是刚接触 ST 的新手这篇的核心是把“能编译”变成“能在产线上稳定跑”。2. 在 H5U 工程里新建 ST 程序POU 类型、变量声明与第一段可运行代码这一章解决的是“从哪下手”的问题。H5U 的编程环境里新建程序不是直接打开一个文本编辑器就写而是先要选对“程序组织单元”的类型类型选错了后面任务调度、变量生命周期全都会出问题。我一般会先花十分钟把新建 POU、变量声明、下载运行这条链路走通再开始写正式逻辑。2.1 为什么 H5U 的 ST 程序要区分程序组织单元从扫描周期看 POU 的作用在 H5U 的编程软件里新建一个 ST 程序时通常会给三个类型选项程序、功能块、函数。很多人不理解为什么要分这么细直接把它们当成同一个“写代码的地方”。实际差别在于它们和扫描周期的关系。先看“程序”程序是一个任务的入口点可以理解成梯形图里的主程序段由任务周期或事件触发一个周期结束之后把输出结果稳定在映像区。再看“功能块”功能块带内部状态比如一个定时器、一个计数器、一个电机控制封装每次调用都会记住自己的内部变量同一个功能块可以被实例化成多个副本彼此独立。最后是“函数”函数没有内部状态输入一组参数算出一个输出比如模拟量换算、字节拼接、位组合适合做“纯计算”。在 ST 里为什么必须区分因为扫描周期是顺序执行的程序只在特定任务里跑功能块可以被多个程序反复调用函数则可以在表达式任意位置嵌入。如果把你需要保持状态的一段算法写成了函数那每次调用的状态都会丢如果把你只需要算一次的转换写成了功能块那每次调用都在白白占用内存。H5U 工程里我一般这样选主流程用“程序”可复用的电机、阀、气缸控制封装成“功能块”数值换算和校验用“函数”。新手最常见的毛病是拿函数去封装“带记忆”的逻辑。比如写一个延时启动函数输入一个按钮信号和延时时间输出一个延时后的结果表面看能编译实际跑起来输出永远等于输入。原因就是函数没有内部状态延时需要的计时数据在函数返回时全部丢失。判断标准很简单如果这段逻辑在两次调用之间需要记住任何数据它就必须做成功能块。2.2 变量声明区怎么写全局变量、局部变量与直接寻址的三类坑ST 程序的变量声明写在程序头部的 VAR 段里。H5U 工程里常见的声明段有这几类VAR 是局部变量只在当前 POU 内有效VAR_GLOBAL 是全局变量整个工程共享VAR_INPUT 和 VAR_OUTPUT 是功能块的对外接口VAR_RETAIN 是掉电保持变量适合存产量、配方号、运行模式。新手最容易在声明类型上翻车明明只是在一个程序里用的中间标志写到了全局变量表里一旦程序复制两份变量冲突立刻出现。还有一类声明是直接寻址写法类似 AT %IX0.0 或 AT %QX0.0把变量固定映射到某一个物理 IO 点上。这类写法的好处是地址直观坏处是换点表要改程序。我一般只在模拟量通道和高速输入这类必须绑定硬件的信号上做直接映射普通中间逻辑全部用符号变量。下面给一个最小声明区示例后面的代码都按这个风格来写。PROGRAM POU_Motor VAR bStart AT %IX0.0 : BOOL; // 启动按钮直接映射到物理输入 bStop AT %IX0.1 : BOOL; // 停止按钮 bMotor AT %QX0.0 : BOOL; // 电机输出 bFault AT %IX0.2 : BOOL; // 故障信号 bRun : BOOL; // 内部自锁标志局部变量 END_VAR bRun : (bStart OR bRun) AND NOT bStop; bMotor : bRun AND NOT bFault;代码逻辑第一行是经典启保停bStart 为 TRUE 时把它自身置位并保持bStop 为 TRUE 时清零第二行在自锁基础上加上故障切断bFault 一旦为 TRUE输出立刻断开。这个逻辑只在当前任务周期内生效bRun 在每一轮扫描开始时读取的是上一周期的结果所以能形成锁存。参数说明AT 后面的地址必须与 H5U 工程的 IO 映射点表一致bStart 如果接到急停常闭触点上那就要写成 AND NOT bStart 再参与逻辑千万不要在点表里反转地址。这是照着点位表反着写的坑接线图是常闭程序里写的却是高电平有效现场能查半天“灵异现象”。写完这段代码后编译下载到 PLC在监控表里强制 bStart 为 TRUE可以看到 bMotor 接通再强制 bStop 为 TRUE输出断开。这条最小链路验证通过后再开始写正式逻辑。提示在线修改程序后变量初始值不会自动重新写入具体恢复方法见第 5.5 节。2.3 最小可运行代码一个带使能和复位的电机启保停上面那段代码只是“能编译”实际现场往往还需要两个东西一个使能总开关一个故障复位流程。补一段可以抄作业的代码PROGRAM POU_MotorFull VAR bEnable AT %IX0.3 : BOOL; // 总使能只有为 TRUE 才允许运行 bReset AT %IX0.4 : BOOL; // 故障复位按钮 bFaultAck : BOOL; // 故障确认标志 END_VAR // 使能条件总使能且无故障确认等待 IF bEnable THEN bRun : (bStart OR bRun) AND NOT bStop; ELSE bRun : FALSE; END_IF // 故障复位按下复位按钮时将输出封锁解除 IF bReset THEN bFaultAck : TRUE; END_IF bMotor : bRun AND NOT bFault AND bFaultAck;这段的差别在于总使能断开时强制清掉自锁这在安全回路里很常见故障复位不会直接清除 bFault而是等待外部故障点恢复后再按下复位bFaultAck 才置位。参数说明bFaultAck 是内部变量掉电后默认初始化为 FALSE如果你的工艺要求故障复位状态掉电保持就得把它挪到 VAR_RETAIN 段里。这一点在简易手册的变量表格里往往只写“保持/非保持”不写工程上什么时候选保持实际操作时按“掉电后是否需要记住上次状态”来判断。这里还要多说一句V 段的局部变量默认不保持掉电必然消失如果需要在上电后恢复上次的运行模式、当前配方号必须单独放进 VAR_RETAIN。很多临时变量其实不应该保持比如按钮的抖动过滤结果、一次扫描里的中间状态保持反而会造成重启后逻辑混乱。3. 任务配置与扫描周期H5U-ST 程序跑得稳不稳的根源程序写好了下载到 H5U 里能不能按你想的节奏跑取决于任务配置。任务配置是 ST 程序最容易出“黑匣子”问题的板块逻辑明明对输出却不及时多半不是逻辑错是任务周期和优先级没配明白。3.1 任务类型与优先级后台任务和定时任务的区别H5U 编程环境里常见三类任务。第一类是周期任务按固定时间间隔触发比如 2ms、5ms、10ms适合运动控制和高速 IO 处理第二类是自由运行任务上一个周期执行完立刻开始下一个周期逻辑量大时周期自动变长适合普通顺序控制第三类是事件任务由特殊事件或者信号上升沿触发相当于软中断。三类任务可以同时存在都挂到同一个 CPU 上调度。在任务配置里你还需要给每个任务设优先级。高优先级任务会打断低优先级任务执行但如果高优先级任务太多或者周期过长低优先级任务可能被“饿死”表现就是后台任务的扫描周期突然翻倍。配置任务有一套简单原则运动控制和高频 IO 用周期任务设备联锁和状态管理用自由运行任务通讯接收和报警录像这类突发性工作用事件任务。优先级只在必须保证执行时序时才拉开梯度不要为了“稳妥”把什么都设成高优先级。我见过一个案例有人把三个任务全设成最高优先级结果设备一上电三个任务互相抢占逻辑倒是都能跑但每个任务的周期都比设定值翻了两三倍动作肉眼可见地一顿一顿。把其中一个不重要的逻辑挪到后台任务后现象立刻消失。在 H5U 里建立工程时系统一般会默认创建任务结构但默认值往往优先保证“能跑”不保证“跑得稳”所以任务参数一定要自己过一遍。3.2 ST 定时器与计数器TON、TP、RTC 的几个量产参数选择ST 程序里用得最多的指令是定时器。H5U 工程里定时器通常是一个功能块实例化一次用一次不能像函数那样在表达式里直接调用。下面是一段在周期任务里运行的定时器示例VAR tDelay : TON; nBoxCount : CTU; END_VAR tDelay(IN : bSensor, PT : T#800MS); bDelayDone : tDelay.Q; nBoxCount(CU : bSensorRising, R : bBatchReset, PV : 500); bCountDone : nBoxCount.Q;定时器的逻辑说明tDelay 的 IN 是输入条件PT 是设定时间Q 是输出当 IN 一直为 TRUE 并且持续时间超过 PT 后Q 变为 TRUEIN 只要掉为 FALSE定时器内部计时立刻清零。计数器类似CU 是计数输入R 是复位PV 是设定值Q 在计数值到达 PV 时置位。参数选择要特别注意两点一是 PT 不要选得太短比如任务周期是 5ms你却把延时设成 10ms那计时误差可能接近一个任务周期动作一致性会很差。我自己取延时至少是任务周期的 20 倍以上例如 5ms 周期任务里的最小延时取 100ms 起步。二是计数器复位信号要和其他状态联锁量产场景里如果是“满箱计数”通常把输送停止信号也接到 R 上避免计数与搬运动作不一致。再补充一个关于 RTC 的选型实时时钟指令适合做相对时间戳比如记录故障发生时刻、记录批次起始时间不要拿它当定时器用。RTC 通常是秒级分辨率在中速逻辑里当延时用时间精度根本不够还会占用更多的处理时间。另一个容易忽视的点是 TON 的 Q 输出在到达设定时间后会一直保持 TRUE直到 IN 断开才复位如果你需要的是一闪而过的脉冲要么在 Q 后面跟上 R_TRIG要么用 TP 指令。3.3 扫描周期抖动怎么排查从任务监视看 ST 卡顿当程序跑起来后如果某个运动动作忽快忽慢首先不要怀疑硬件先看任务监视。H5U 编程环境的任务监视页通常会显示最小扫描周期、最大扫描周期、平均扫描周期和任务负载率。把这几个数值和周期设定值对比基本能定位到是哪层任务出问题。监视项含义关注建议最小扫描周期执行最快的一次耗时低于设定周期说明有空闲最大扫描周期执行最慢的一次耗时与设定周期偏差不要超过 1.5 倍平均扫描周期长时间平均值观察抖动趋势任务负载率任务执行时间占比不超过 80%否则降级或优化代码排查步骤一般是这样先看负载率如果超过 80%把任务设置里的周期调大一个档位或者把一部分非实时逻辑挪到自由运行任务再看最大扫描周期如果最大周期是正常值的几倍而且无规律出现多半是通讯指令或运动控制指令在某个分支里同步等待最后才去查具体 POU。扫描周期抖起来像玄学其实大多数抖动来源就两个高优先级任务抢占以及某个 POU 里出现了等待型系统调用。把任务列表展开看一遍“运行时间”列通常能直接看出是谁在捣乱。还有一个常用的做法在怀疑的 POU 里拆出几个临时输出变量在关键路径前后翻转用示波器看翻转间隔比反复读监视页快得多。注意任务周期越小调度开销越大普通逻辑别盲目用 1ms 周期。4. 从梯形图习惯迁移到 ST映射关系与写法边界用 ST 之前先做一次梯形图思维的“翻译”。下面这张映射表我每次给新人讲都会先丢出来照着它能把初期的语法损耗降到最低。4.1 触点、线圈、置位复位映射表与对应的 ST 写法梯形图元素ST 写法注意点常开触点bVar TRUE 或 IF bVar THEN变量类型必须是 BOOL常闭触点NOT bVar注意排除非 BOOL 类型线圈输出bOut : 条件表达式同一输出多处赋值时以最后的执行为准置位线圈条件成立时 bOut : TRUE需要显式复位否则保持复位线圈条件成立时 bOut : FALSE置位复位不能互相覆盖中间继电器用 BOOL 局部变量不要滥用全局变量光看表不够给一段对应实际代码// 两个常开触点串联 bOut1 : bIn1 AND bIn2; // 常闭触点并联 bOut2 : bIn1 OR NOT bStop; // 置位 / 复位 IF bSet THEN bKeep : TRUE; END_IF IF bReset THEN bKeep : FALSE; END_IF逻辑说明梯形图里触点串联就是逻辑与并联就是逻辑或常闭取反这几条规则可以覆盖九成基础回路。置位和复位在 ST 里不能像梯形图那样让某个输出线圈占据两行程序位置必须用 IF 结构显式控制如果同一个 bKeep 在两处被赋值最后一处生效这是 ST 和梯形图最本质的顺序差异。参数说明这类联锁逻辑放在自由运行任务里即可不建议放进高速周期任务除非这些联锁和运动轴插补有同步要求。这里还藏着一个梯形图时代不会遇到的问题ST 里一行表达式如果写得过长编译器会按运算符优先级自动处理但人的阅读顺序和编译器不一定一致。我习惯用括号把每一段与或逻辑包清楚哪怕多几层括号也比让下一个人猜优先级强得多。4.2 上升沿与下降沿在 ST 里的三个隐藏陷阱梯形图里上升沿触点用起来很自然ST 里对应的功能块是 R_TRIG 和 F_TRIG。注意它们也是带内部状态的功能块必须实例化使用不能像函数一样每次调用都现算。基础用法VAR rTrigStart : R_TRIG; END_VAR rTrigStart(CLK : bStart); IF rTrigStart.Q THEN bStartPulse : TRUE; END_IF逻辑说明R_TRIG 内部记住上一周期的 CLK 值当前 CLK 为 TRUE 且上一周期为 FALSE 时Q 输出一个扫描周期的 TRUE。用这个 Q 去触发计数器、状态机跳转都安全。参数注意CLK 必须是 BOOL 类型某些指令手册里也允许把它接到 WORD 的某一位但工程上我建议先用 BOOL 位变量避免字节对齐问题。三个隐藏陷阱依次要记清楚。第一上升沿功能块实例被两处代码调用内部状态错乱表现为本来只触发一次的计数器偶尔连跳两次。原因是同一实例的内部“上一周期值”被两处代码交替刷新。第二边沿信号放进周期较大的任务里容易被信号宽度和采样时机漏掉。第三在 FOR 循环内部调用 R_TRIG 更危险循环一次就已经刷新了内部状态第二次进来时它已经看不到上一次的沿了。这三个问题在梯形图时代几乎不会遇到迁移到 ST 后要当成基本功记下来。如果不确定边沿是否可靠可以自己写一段不带功能块的边沿检测IF bStart AND NOT bPrevStart THEN bStartPulse : TRUE; END_IF bPrevStart : bStart;这段代码和 R_TRIG 等价但 bPrevStart 是普通 BOOL 变量生命周期完全由自己的程序控制排查起来更直观。唯一要注意的是 bPrevStart 的更新语句必须放在判断语句之后否则本周期比较的已经是更新后的值边沿就永远检测不到了。4.3 H5U 对 ST 语法的支持边界哪些指令别硬写成函数不是所有能在梯形图里用的东西ST 里都能等价敲出来。常见的误区是把自己需要的算法都亲手写一遍比如模拟量滤波、PID、编码器计数转换。这些在 H5U 里往往已经有封装好的指令或功能块直接用就行自己写一版不仅耗资源还可能在边界条件下翻车。反过来哪些适合用 ST 自己写我的判断标准是纯组合逻辑、状态流转、批量运算、条件判断适合 ST涉及总线周期、轴运动规划、硬件中断别碰。比如“当前轴是否到位”这个判断可以写在 ST 里但要读取运动控制指令的返回参数不要自己根据脉冲数估算。还有一点边界值得注意ST 里调用系统指令时参数类型必须严格按照手册填写。BOOL 传给 REAL 在某些编译器里会自动转换但转换规则并不总是符合直觉比如把 TRUE 传给 REAL 得到 1.0这没问题可如果你把一个 INT 模拟量直接和 REAL 阈值比较隐患就藏在隐式转换里。切换到 ST 之后类型意识必须比梯形图时代强一个等级否则很多“偶尔出现一次”的怪现象找不到根因。5. H5U-ST 编程的五个避坑记录现象、原因与解决这里整理五个我在 H5U-ST 程序里踩过、且不止一次在别人程序里看到的坑。每一条都按“现象 → 原因 → 解决”写属于带血泪经验确认过的结论比单纯读语法手册要实用得多。5.1 模拟量换算的 INT 到 REAL 转换丢了小数现象温度采集通过模拟量模块读进来的是一个整数比如 0 到 27648 对应 0 到 100 度。换算公式写好后显示值总是跳整数比如 59 度、60 度中间的小数全部丢失。原因问题出在运算符两侧的数据类型。0 到 27648 是整数直接除以 27648 时PLC 编译器先把两个整数做除法得出的结果还是整数等于 0 或者 1赋给 REAL 变量时再转成浮点精度早就没了。解决先把被除数转成 REAL 再做除法。写法是fTemp : REAL(aiRaw) / 27648.0 * 100.0;逻辑说明REAL() 先把 aiRaw 从整形显式转成浮点27648.0 后面的小数点提醒编译器把整个表达式按浮点计算结果保留小数。参数说明分母里那个“.0”非常重要一旦漏掉编译器可能把除法还是按整数执行。如果你的 H5U 工程里 REAL() 函数名不同去手册查“类型转换”章节本质都是把源类型放到运算符右侧。5.2 数组越界没写保护导致相邻变量被改写现象程序里定义了一个数组按序号访问某一箱子的配方数据。某个特殊条件下配方内容突然变乱连数组后面相邻的变量都被改掉在线监视里看到一堆不可能出现的数值。原因索引变量算出了一个超过数组声明的最大值ST 代码往越界下标写数据写到了数组之外的连续内存区。部分 PLC 默认不做运行时越界检查所以程序不报错数据却已经被污染。解决在每次访问数组前做索引边界判断或者让索引先做饱和处理。下面是一个最小保护写法IF (idx 0) AND (idx 49) THEN recipeData[idx] : newValue; ELSE faultIndex : TRUE; END_IF逻辑说明idx 是数组下标数组声明了 50 个元素合法下标是 0 到 49每次写入前先做范围判断越界时置一个故障标志而不是继续执行。参数说明如果 H5U 编程环境提供数组上界查询函数也可以用但工程上最稳的还是这种显式判断移植到其他平台也不依赖特定系统函数。千万别在 FOR 循环里拿一个可能为负的变量做下标那是越界的重灾区。5.3 在 FOR 循环里调用定时器导致时序乱套现象一个贴标工位要依次给 12 个瓶子做停顿等待。用 FOR 循环包住一个定时器功能块运行效果是有的瓶子等太久有的瓶子没等够时间节奏完全不对。原因定时器功能块是有内部状态的在 FOR 循环里被连续调用多次每次调用都会读取并更新同一个实例的内部计时值第二圈循环进来时第一圈刚计到一半的剩余时间被当成起点重新算时序因此错乱。解决不要在任何形式的循环语句里调用定时器。需要为多个通道做独立延时就用数组实例VAR tDelayArr : ARRAY[0..11] OF TON; END_VAR tDelayArr[i](IN : bActive[i], PT : T#500MS); bDoneArr[i] : tDelayArr[i].Q;逻辑说明每个瓶子的延时由数组中的一个独立 TON 实例承担互不共享内部状态循环只负责把索引 i 对应的信号和实例对应起来。参数说明ARRAY[0..11] OF TON 表示一次声明 12 个定时器实例这种写法需要确认 H5U 编译器允许功能块实例数组如果不允许就拆成 12 个命名实例再用 CASE 语句调度。现场经验是为 12 个通道手写 12 行定时器调用可读性虽然差一点但比循环调用安全得多。5.4 任务周期设得太短1ms 周期导致任务负载报警现象为了让设备动作更“跟手”把周期任务设置成 1ms。下载后不久编程软件弹出任务负载率过高报警程序动作反而变迟钝运动轴偶尔丢步。原因任务周期越短单位时间内的调度次数越多同一个逻辑量放到 1ms 周期后CPU 消耗是 5ms 周期的 5 倍。如果还挂着几个通讯指令在任务里负载很容易超过安全阈值。解决周期设置要跟着逻辑量走不要盲目追快。高速 IO 和插补运动才用 1ms 到 2ms状态机、联锁、模拟量处理至少放到 5ms 或 10ms普通逻辑直接放自由运行任务。完成后去任务监视页看负载率稳定在 80% 以下才算合格。这里有个我自己常用的口诀任务周期往上取负载告警往下查。周期调大一档负载降一半设备动作人眼基本看不出差别。5.5 在线修改程序后变量初始值不生效现象在编程软件里在线改了一段 ST 代码又顺手把几个变量的初始值改了下载后在线监控显示新代码生效了但变量初始值还是老样子。原因PLC 的在线下载机制通常只更新程序逻辑不会重新初始化已有变量变量当前值保持不变初始值只有在冷启动或者执行“初始化变量”操作时才写入。解决下载完成后手动做一次初始化操作不同平台叫法可能是“热复位”“重新初始化变量”“冷启动”在 H5U 编程软件里找“在线”菜单下的“初始化变量”。如果工艺不允许整机复位就把需要重新设定初始值的变量单独做成一段启动逻辑在上电后第一个周期统一赋值IF bFirstScan THEN bRun : FALSE; idx : 0; nCount : 0; END_IF逻辑说明bFirstScan 是“第一个扫描周期”标志只在上电后的第一次扫描为 TRUE在这个分支里给关键变量赋初始值等于绕开在线下载的初始值限制。参数说明bFirstScan 不需要放在 VAR_RETAIN 里它只是个上电瞬间标志。如果某些故障标志需要掉电保持再用 VAR_RETAIN 单独声明。这个技巧在产线调试阶段非常值钱可以省掉很多整机重启。6. 把 H5U-ST 手册读薄一个能判断任务稳定性的运行时间自检块6.1 用自复位定时器做扫描稳定性的黑白盒子这一章给一个我长期在用的验证技巧往任务里塞一个自复位定时器用它的翻转周期判断任务调度是否稳定。这个方法不依赖厂商的特殊系统变量纯 ST 可写换平台也能复用也算把手册读薄了。PROGRAM POU_SelfCheck VAR tPulse : TON; bPulse : BOOL; nTick : INT; END_VAR tPulse(IN : NOT tPulse.Q, PT : T#50MS); bPulse : tPulse.Q; IF bPulse THEN nTick : nTick 1; // 翻转一次累加一次 END_IF逻辑说明tPulse 用自身输出取反作为输入组成自复位定时器。Q 为 FALSE 时 IN 为 TRUE开始计时计满 50ms 后 Q 变 TRUE下一周期 IN 变 FALSEQ 被复位再下一周期又重新计时。所以 bPulse 会输出一个周期约 100ms 的方波。验证方法把这段放进你怀疑不稳定的周期任务里在线监控 nTick10 秒内如果稳定计数大约 100 次说明该任务调度稳如果计数忽多忽少说明有高优先级任务在抢占或者任务内有指令阻塞。参数说明PT 值至少设为任务周期的 10 倍例如 5ms 周期任务里 PT 不要小于 50ms否则计时精度会被任务周期放大现场负载重的时候用两次较长间隔里的计数差值判断比盯着变量跳动更直观。如果你有示波器直接把 bPulse 映射到一个空闲输出点上量输出方波的周期得到的是最客观的任务稳定性证据。我早期调一条贴标机把 50ms 翻转当成 100ms 用整整查了一天——后来才明白读手册时要先搞清楚 TON 的输出是什么时刻生效的。希望帮到你。本文还有配套的精品资源点击获取