简介:面向PLC编程人员的H5U-ST简易编程手册,围绕AutoShop软件中的ST(结构化文本)编程展开,适合需要从梯形图过渡到高级语言编程的自动化工程师。手册系统讲解了ST工程创建、子程序/FB/FC插入、变量声明、D/RW字元件类型定义等基础操作,并重点剖析IF、CASE、WHILE、REPEAT、FOR、EXIT、RETURN等流程控制指令的语法与防死循环要点。内容还涉及R_TRIG/F_TRIG边沿触发及基本轴控指令表,帮助读者快速建立ST编程知识框架。资源为1个PDF文件,压缩包仅1.21MB,便于随身查阅;现有2043人浏览学习,适合作为现场调试与程序维护时的速查参考。
1. H5U-ST 编程手册:从梯形图到结构化文本,一份能直接照着写的实操指南
做 PLC 项目的人大概率遇到过这种场景:轴控逻辑越写越长,梯形图里一堆置位复位和跳转,改一个动作要翻好几页;换个机型复用程序,改参数改到头晕。H5U 系列支持 ST(结构化文本)之后,这类问题有了直接的解法——把复杂流程塞进 ST 页面,用接近高级语言的写法管理轴控、循环和数据处理。这一篇基于 H5U-ST 简易编程手册整理出了完整实操路径:工程怎么建、变量怎么定义、D 元件怎么变成浮点或双整型、IF/CASE/循环指令怎么写、轴控和凸轮指令怎么调、在线修改有什么限制、死循环和数组越界会怎么报错。适合两类人:有梯形图基础、想把流程控制搬到 ST 里的工程师,以及新项目直接选 ST、需要快速确认指令写法的入门者。文字部分我尽量按手册原意讲,该给代码给代码,该标坑标坑。
2. 工程创建与变量定义:先解决“ST 代码写在哪、D0 怎么变浮点”
2.1 新建 ST 工程与往梯形图工程里插 ST 子程序
手册第一条路径是新建 ST 工程:打开 AutoShop 软件,点新建工程,编辑器列表里选编程语言 ST,确认后默认程序页面就是文本编辑。这意味着整个工程以 ST 为第一语言,主程序、子程序、FB/FC 都可以选 ST 或梯形图混合使用。实际项目中,我一般建议新建 ST 工程而不是梯形图工程,因为 ST 工程里混入梯形图页面容易,反过来在梯形图工程里把逻辑全部搬到 ST 会比较费劲——不是不能,是没必要。
第二种路径适用于已有梯形图工程、只想把局部逻辑用 ST 实现的情况:右键程序页,插入子程序,类型选结构化文本 ST。FB/FC 的创建路径同理。这里有个实战细节:插入的子程序默认就是文本编辑页面,不需要额外配置;程序页和子程序之间可以用变量表和全局变量做数据交换。
// 新建 ST 子程序后在编辑区写一段最简测试代码 bStart := TRUE; IF bStart THEN iCount := iCount + 1; END_IF;这段代码验证两件事:变量名不区分大小写(手册明确说明与梯形图一致),以及 ST 页面里可以直接用赋值操作符:=。bStart和iCount如果还没在变量表里声明,编译会报未定义——这就引出下面的变量声明方式。
2.2 变量声明的两种方式与 D 元件类型定义
变量声明有两条路:一是在变量表里先声明再用,二是在程序编辑区直接写变量名然后按 Enter,或者点一下程序基本块外的区域,系统会自动弹出变量定义框。手册把第二种叫“自动弹出声明”,实际用起来很顺手:写完一串新变量名,点个空白处,弹框里把类型选好,一次声明完。
字元件类型这块是新手最容易踩的第一个坑。以 D 元件为例:程序里写 D0,默认是 16 位整型;要当 32 位整型用,写成D0:D;要当浮点用,写成D0:E。RW 元件同理。这个后缀语法和很多日系 PLC 的软元件指定方式不同,容易习惯性写成D0DINT或者D0_REAL之类,编译直接报错。
// D 元件类型定义的正確写法 D0 := 100; // D0 默认 16 位整型 D10:D := 100000; // D10 作为 DINT(32 位整型) D20:E := 3.14; // D20 作为 REAL(浮点) // 定义数组并使用 arrData[0] := D10:D; arrData[1] := D20:E;参数说明:D0:D里的冒号是类型分隔符,D0是起始地址,D表示 DINT,E表示 REAL。数组arrData需要在变量表里声明为ARRAY [0..9] OF INT之类的类型,下标从 0 开始。这里有个容易忽略的点:如果你把 D20 定义为浮点型,又用整数赋值,程序会有隐式转换——INT 可以隐式转成 REAL,但反过来不行。手册里明确写了隐式转换规则:INT 可以隐式转 DINT 或 REAL,其他组合必须用显式转换函数。
3. 流程控制指令:IF/CASE 与循环的正确写法及死循环边界
3.1 IF 与 CASE:条件变量的类型限制是硬规矩
IF 指令的语法结构和高级语言几乎一样,但有个细节:条件是布尔表达式,也就是比较运算或布尔变量。手册里的语法框架是IF <布尔表达式1> THEN <指令> ELSIF <布尔表达式2> THEN <指令> END_IF;,注意拼写是ELSIF,不是ELSE IF。这个拼写在 ST 编辑器里会自动变色提示,但如果你从别的语言带习惯过来,头几次编译报错很正常。
// IF 多条件判断示例:根据当前模式切换目标速度 IF iMode = 0 THEN rSpeed := 10.0; ELSIF iMode = 1 THEN rSpeed := 50.0; ELSIF iMode = 2 THEN rSpeed := 100.0; ELSE rSpeed := 0.0; END_IF; // CASE 示例:条件变量是整数,值可以并列 CASE iStep OF 0: bHome := TRUE; 1, 2, 3: bMoving := TRUE; 10..20: bFastMode := TRUE; ELSE bStop := TRUE; END_CASE;逻辑说明:iMode = 0时走第一分支,不满足就依次检查 ELSIF;都不满足走 ELSE。CASE 里1, 2, 3表示这几个值都执行同一段指令,10..20是范围写法。手册特别强调 CASE 的条件变量只能是整数,你写CASE rSpeed编译直接报类型错误,不想翻页查手册就记住这条。区间写法10..20在调试时很好用,批量置位场景比逐个列值干净。
3.2 FOR / WHILE / REPEAT:三种循环的先判断与后判断差异
FOR 循环的语法是FOR <INT_Var> := <INIT_VALUE> TO <END_VALUE> [BY <步长>] DO <指令> END_FOR;。手册里说得清楚:先检查条件再执行,如果初始值大于结束值,指令一次都不执行;BY 不写默认步长为 1。实际写轴控找原点或者数组扫描时,FOR 是最常用的,因为循环次数是确定的。
// FOR 循环:给数组连续赋值 FOR i := 0 TO 9 DO arrData[i] := i * 10; END_FOR; // WHILE 循环:条件不满足就跳出,需要自己保证能退出 WHILE bRunning AND iGuard < 1000 DO iGuard := iGuard + 1; arrData[iGuard] := arrData[iGuard - 1] + 1; END_WHILE; // REPEAT 循环:至少执行一次,然后判断条件 REPEAT iCount := iCount + 1; UNTIL iCount >= 5 END_REPEAT;逻辑说明:FOR 循环的计数器i必须是整数类型,循环体执行后自动加步长。WHILE 和 REPEAT 的关键差异在判断时机——WHILE 是先判断后执行,条件第一次就是 FALSE 时循环体一次不跑;REPEAT 是先执行后判断,循环体至少跑一次。参数说明:iGuard < 1000是我推荐的写法,给 WHILE 加一个计数上限,防止变量条件一直为 TRUE 导致死循环。手册在这里多次强调死循环风险——一旦循环条件永远为 TRUE,程序会进入死循环状态,此时 PLC 数码管会交替闪烁 Er 15 00 50 82,其中 Er 1500 是看门狗超时,Er 5082 是死循环报警。这不是吓唬人,我见过现场把 FOR 的结束值写成变量、结果该变量被循环体修改导致永不退出的案例,最后靠看门狗复位。
3.3 EXIT / RETURN:循环内的提前退出与 POU 提前返回
EXIT 用于退出 FOR、WHILE、REPEAT 循环,RETURN 用于直接离开当前 POU(程序组织单元)。RETURN 的典型用法是参数校验:条件不满足直接返回,后续代码不执行。手册给的例子很直观:IF b=TRUE THEN RETURN; END_IF; a:=a+1;,如果 b 为 TRUE,a:=a+1这行永远不会执行。
// EXIT 示例:在数组里找第一个非零值,找到就退出循环 FOR i := 0 TO 99 DO IF arrData[i] <> 0 THEN iFirstIndex := i; EXIT; END_IF; END_FOR; // RETURN 示例:子程序内做安全校验 IF bEstop THEN bEnable := FALSE; RETURN; END_IF; // 只有急停未触发时才会执行到这里 bEnable := TRUE;逻辑说明:EXIT 只跳出当前一层循环,嵌套循环里它只作用于最近的那个 FOR/WHILE/REPEAT。RETURN 则是整个 POU 返回,效果等同于梯形图里的条件跳转到程序末尾。参数说明:bEstop是急停输入变量,bEnable是输出使能变量;RETURN 之后的所有语句哪怕写在同一扫描周期内也不会执行。实际调试时,RETURN 用多了代码可读性会下降,我一般只在入口校验和异常处理里用,正常流程逻辑尽量用 IF……ELSE 包住。
4. 轴控与运动控制指令:从 MC_Power 到插补、凸轮的调用套路
4.1 基本轴控指令:MC_Power 与定位指令的引脚规范
手册里基本轴控指令列表接近二十条,从 MC_Power(使能)、MC_Reset(复位)、MC_ReadStatus(读状态)到 MC_MoveRelative/MC_MoveAbsolute/MC_MoveVelocity/MC_Home,覆盖了单轴运动控制的全部基本动作。这些指令在 ST 里调用时是函数块实例的形式,必须先建实例再调用。
// 在功能块实例表中已添加 mcPower 实例 mcPower( Enable := bEnable, Axis := Axis0, Status => bPowerStatus, Error => bPowerError, ErrorID => wPowerErrorID );参数说明:Enable是使能输入;Axis是轴引用,写轴名而不是轴号;Status是输出状态,用=>连接;Error和ErrorID是故障输出。手册强调 FB(功能块)输入参数除了轴参数是必须输入的外,其他引脚可以不输入;输出参数可以留空。但Axis这个引脚在实际编译中属于强制项,漏掉会报错。第一次写轴控代码时最常见的编译错误就是把=>写成:=,输出参数必须用=>,输入参数用:=。
定位指令的调用结构类似:
// 相对定位示例 mcMoveRelative( Execute := bTrigger, Axis := Axis0, Distance := 100.0, Velocity := 50.0, Acceleration := 10.0, Deceleration := 10.0, CurveType := 0, Done => bDone, Busy => bBusy, CommandAborted => bAborted, Error => bError, ErrorID => wErrorID );参数说明:Distance是相对位移量,单位取决于轴组态里的用户单位设置;Velocity/Acceleration/Deceleration分别对应速度、加速时间、减速时间;CurveType是加减速曲线类型,常用 0 表示梯形曲线。执行结果通过Done、Busy、Error反馈:Execute上升沿触发指令,Busy为 TRUE 表示运动中,Done为 TRUE 表示到位。这里有个习惯做法:触发位bTrigger不要一直置 TRUE,用脉冲或沿信号,否则指令可能一直被重新触发。
4.2 插补、凸轮与编码器指令:轴组和主从关系的特殊引脚
手册里轴组相关指令包括 MC_MoveLinear(直线插补)、MC_MoveCircular(圆弧插补)、MC_GroupStop(停止轴组运行)、MC_GroupPause(暂停轴组运行),以及凸轮齿轮类指令 MC_CamIn/MC_CamOut/MC_GearIn/MC_Phasing。这类指令的调用与单轴指令不同,Axis 参数换成轴组引用,且插补指令需要先建立轴组坐标系。
// 凸轮开始动作 mcCamIn( Execute := bCamStart, Master := Axis0, Slave := Axis1, CamTable := CamTable0, ScalingFactor := 1.0, MasterOffset := 0.0, SlaveOffset := 0.0, Active => bCamActive, Error => bError, ErrorID => wErrorID ); // 齿轮同步 mcGearIn( Execute := bGearStart, Master := Axis0, Slave := Axis1, RatioNumerator := 1, RatioDenominator := 2, MasterOffset := 0.0, SlaveOffset := 0.0, Active => bGearActive );逻辑说明:MC_CamIn 的Master是主轴,Slave是从轴,CamTable是凸轮表数据,需要预先在轴控参数里配置;ScalingFactor是缩放系数,用于调整从轴跟随的主轴行程比例。MC_GearIn 的RatioNumerator/RatioDenominator组成齿轮比,上面例子中主轴转 1 圈从轴转 2 圈。参数说明:Active输出为 TRUE 表示主从关系已建立,解除时调用 MC_CamOut/MC_GearOut 并让Execute触发。编码器相关指令 HC_Counter、ENC_Counter 等也在手册里列全了,用法与轴控指令一致——先建实例,再按引脚填参数。实践上,凸轮和齿轮的现场调试难点不在指令调用,而在凸轮表的生成和相位偏移量计算,这部分的验证必须先空载跑,再带负载,避免机械冲击。
5. 在线修改与异常处理避坑:改程序、死循环、数组越界的现象与对策
5.1 在线修改:能改什么、不能改什么,边界必须清楚
手册里在线修改这块写得非常具体,我提炼成三条红线。第一,函数名范围外不能改:你只能在函数始末处添加分号、注释符号、空格、换行、TAB,函数标号不能变。第二,程序文件级不支持增删、不支持重命名、不支持修改文件属性、不支持对子程序加减密。第三,全局变量可以增删变量、可以修改变量名和注释,其他特性不支持修改。
// 在线修改允许的操作:在函数范围内增加变量声明 VAR bTempStop : BOOL; // 新增的临时变量,本次在线修改周期内有效 iTempCount : INT; END_VAR逻辑说明:这段放在函数声明区,通过在线修改下装后,变量会附加到当前运行程序里。手册补充了 FB/FC 局部变量的规则:不能增删改 IN/INOUT/OUT 类型变量,但 Var 类型变量可以增删;本次在线修改周期前增加的 Var 变量,支持修改变量名和注释,其他特性不改;本次周期内增加的 Var 变量,所有特性都可以改。实际调试时,我一般把需要在线调整的参数放在全局变量或本次新增的 Var 变量里,把 IN/OUT 接口留作工艺接线,基本不会踩改动限制的雷。
5.2 死循环与看门狗:报错代码怎么读
手册对死循环的处理很有意思——系统会自动检查并报错跳出,而不是让程序一直卡住。现象是 PLC 数码管交替闪烁Er 15 00 50 82,诊断界面显示看门狗超时和死循环报警。运行逻辑是先报 Er 1500(看门狗超时),再报 Er 5082(死循环报警),程序停止运行。
// 容易引发死循环的写法:条件永远为 TRUE WHILE bAlwaysTrue DO // 没有任何修改 bAlwaysTrue 的语句 END_WHILE; // 正确的写法:让循环条件可退出 iLoop := 0; WHILE iLoop < 10 DO iLoop := iLoop + 1; END_WHILE;逻辑说明:左侧代码的问题是bAlwaysTrue在循环体内从未被修改,条件恒为 TRUE,循环无法退出。右侧代码在循环体内自增计数器,到达上限即退出。参数说明:iLoop是 INT 类型计数器,初始为 0,每执行一次循环体加 1,直到iLoop = 10时条件为 FALSE 退出。配合第 3 章的看门狗说明,建议每个 WHILE/REPEAT 循环都加计数器上限,这比依赖系统看门狗更可控——看门狗触发后整个程序停运,现场设备停在那里,排查成本远高于写循环时多加一个变量。
5.3 数组越界与除 0:报错处理和系统兜底逻辑
手册提到数组越界分两种情况:常量下标在编译期间检查,变量下标在运行期间检查。越界后软件报错,并把越界值用数组最大下标表示。这部分是“系统兜底”逻辑,不代表你可以不检查数组边界。除 0 保护也特殊:如果除数为 0,系统自动将 0 替换为 1,继续执行,但 PLC 数码管会交替闪烁Er 50 81。
// 数组越界示例:变量下标越界后,系统用最大下标兜底 arrTest : ARRAY [0..9] OF INT; iIndex := 15; arrTest[iIndex] := 100; // 实际写入 arrTest[9],并报 Er 50 80 // 避免越界的写法 IF iIndex >= 0 AND iIndex <= 9 THEN arrTest[iIndex] := 100; ELSE // 边界外不执行写入 END_IF;逻辑说明:左侧代码iIndex = 15超出数组上标 9,系统不会直接崩溃,而是把写入目标变成arrTest[9](最大下标),同时报错。右侧代码在写入前做边界校验,下标合法才写入。参数说明:ARRAY [0..9]声明了 10 个元素,下标范围是 0 到 9。手册还提醒数组下标不能是软元件、不能是表达式、FB/FC 中数组下标不能是全局变量、复杂结构的变量中不允许出现 2 个变量数组下标——这几条全部是编译期限制,留意即可。除 0 保护更要注意:自动把 0 换 1 只保证程序不跑飞,结果的数值是错的,工艺计算场景下必须自行判断除数是否为 0。
5.4 FB、FC 嵌套层数与引脚规则:8 层、6层与必填参数的边界
FB/FC 嵌套调用不能超过 8 层,SBR 子程序嵌套不能超过 6 层。这个限制在写大型程序时会真实遇到。FB 的输入参数除了轴参数外可以不输入,输出参数也可以空;但 FC 的输入参数必须全部输入,否则编译报错,输出参数可以为空。快速调出引脚的方式是输入左括号(后按 TAB 键自动补全。
// FB 调用:轴参数必须输入,其他可以省略 testfb(m0, m1); // 中间的输入参数可以省略某些引脚 // FC 调用:输入参数必须写全,输出参数可留空 testfc(m0, => m1); // m0 是输入,m1 是输出参数说明:FB 实例化后调用,引脚可以不填全,未填的输入参数用默认值;FC 的输入参数是编译强制的,少一个都报错。嵌套层数这个限制,我的处理方式是把深层调用的逻辑拆成独立子程序,用变量表传递数据,而不是继续往深处嵌——8 层看起来很多,但实际项目里轴控 + 工艺 + 报警嵌套很容易就达到,与其依赖限制不如从设计上控制层数。
6. 快捷输入、注释与类型转换:把调试效率拉满的习惯
ST 编辑器日常用得最多的三个提速功能:TAB 补全引脚、注释快捷键、鼠标悬浮看变量提示。TAB 补全是我换到 ST 后最明显的效率提升点——输入指令名和左括号后按 TAB,引脚自动列全,不用手动敲引脚名。
// 输入 MC_Jog( 后按 TAB 自动补全 MC_Jog( Enable := ???, Axis := ???, JogForward := ???, JogBackward := ???, Velocity := ???, Acceleration := ???, Deceleration := , CurveType := , Busy => , CommandAborted => , Error => , ErrorID => );参数说明:默认带???的引脚是强制输入项,必须填;没带???的引脚(比如Deceleration、CurveType以及输出引脚)可以根据实际情况决定是否填写。手册特别提醒工具箱快捷输入功能暂时关闭,原因是梯形图指令转换 ST 形式未全部更新,所以 TAB 补全才是当前可用的主路径。注释方面,//用于单行注释,(* *)用于多行注释,选中代码按Ctrl+/可以快速注释或取消注释——这个快捷键和很多文本编辑器一致,几乎零学习成本。鼠标悬浮在变量名上会显示变量名、类型、注释;悬浮在 FB/FC 上会显示函数类型、注释、输入输出参数详情,排查别人写的程序时这个功能比翻变量表快得多。
类型转换是另一个常见坑。手册列了显式转换函数:INT_TO_DINT、INT_TO_REAL、REAL_TO_INT、DINT_TO_REAL等,以及TO_REAL、TO_DINT、TO_BOOL这类简化写法。隐式转换只有三条:INT 可以隐式转 DINT 或 REAL,其他都必须显式写转换函数。常数表示也值得提一笔:默认整数直接写,带下划线的写法如10#100_10表示十进制 10010,16#FF_AE_12表示十六进制数,2#1100_1111表示二进制数——调试编码器或比较开关量组合时,二进制写法一眼能看出位状态。
// 显式类型转换示例 iVal := 100; rVal := INT_TO_REAL(iVal); // 整数转浮点 dVal := INT_TO_DINT(iVal); // 整数转双整型 iBack := REAL_TO_INT(rVal); // 浮点转整数,会截断小数 // 常数带下划线的写法 iHexVal := 16#FF_AE; // 十六进制 iBinVal := 2#1010_1111; // 二进制逻辑说明:实数转整数时小数部分直接截断,不会四舍五入——做配方换算时要留意,该加 0.5 再加。勾选“常数以下划线分隔”的目的是提高可读性,16#FF_AE比16#FFAE更容易看清字节边界。我在现场调伺服参数时养成了一个习惯:所有轴控指令的Velocity、Acceleration引脚都写成浮点数常量或变量,避免整数除法隐式转 REAL 出现精度丢失;每次写完循环必检查退出条件;在线修改前先看一眼函数标号有没有变、变量是不是本次周期新加的。从那以后,ST 程序的现场故障率明显降下来了,大部分坑在写的时候就能避开。希望帮到你。
本文还有配套的精品资源,点击获取