搞工控这些年,我最大的体会是:PLC编程的门槛从来不在“会不会写”,而在“能不能用工程思维把逻辑理清楚”。尤其是当你从梯形图转向ST语言(Structured Text,结构化文本)时,很多人会觉得不习惯,但一旦上手,就会发现ST在处理复杂算法、数据处理和结构化程序时,效率比梯形图高出一大截。
汇川H5U作为一款内置EtherCAT总线、支持CODESYS内核的中型PLC,在中小型自动化设备里用得越来越多。它的编程软件InoProShop基于CODESYS V3.5开发,支持ST、梯形图、功能块图等多种编程语言,其中最值得花时间的就是ST语言。它不仅能写逻辑,还能做数学运算、数组处理、协议解析,甚至能写自定义功能块,大大提升代码复用率。
这篇我就以自己的实际项目经验为线索,把汇川H5U的ST语言编程拆成7个步骤,从建工程到跑通一个完整的运动控制例程,每一步都讲清楚“为什么要这么做”和“实际踩过的坑”。不管你是刚接触PLC的新手,还是从三菱、西门子转过来的老手,这篇都适合。
1. 内容整体设计与思路拆解
1.1 为什么选H5U做ST语言入门
很多人问我,学ST语言为什么不直接学西门子博途,或者用CODESYS?我的回答是:H5U是一个性价比极高的入门载体。
第一,H5U硬件成本低。一台H5U-1614MTD(16点输入14点晶体管输出)的价格只有同级别西门子CPU的一半左右,但性能并不差,内置EtherCAT主站可以带32个轴,常规的伺服控制、脉冲控制、模拟量采集都能做。第二,InoProShop的ST编辑体验做得不错,有自动提示、变量交叉引用、在线监控,调试起来很直观,对新手非常友好。第三,H5U的资料和案例在国内工控圈里已经积累了很多,遇到问题容易搜到答案。
但更重要的是,ST语言本身是IEC 61131-3标准里的文本化编程语言,语法结构类似Pascal/C,学会了H5U的ST,切到汇川Easy系列、InoProShop支持的CODESYS平台,甚至西门子的SCL语言,上手成本都很低。也就是说,你花时间学的不只是H5U这一台设备,而是一套可以迁移的编程思维。
1.2 7步路线的学习逻辑
我把H5U的ST语言学习分成7步,每一步解决一个核心问题:
- 建工程、认识软件与ST编辑器。
- 理解变量类型与声明方式——这是ST的“词汇表”。
- 写第一条ST程序(启保停逻辑)——理解扫描周期与赋值顺序。
- 用IF/CASE做条件分支——这是所有设备逻辑的地基。
- 数据运算与转换——模拟量采集与工程量换算。
- 功能块封装与调用——让代码从“能跑”变成“好用”。
- 结合EtherCAT总线写一个完整的运动控制例程——把前面所有知识串起来。
这个路线的设计逻辑是:先建立工程概念,再掌握语法基础,然后通过小案例理解执行机制,最后直接上手一个接近实际项目的综合例程。每一步都有明确的输出物,学完就能看到自己的进步。
2. 核心细节解析与实操要点
2.1 建工程时容易忽略的几个设置
在InoProShop里新建H5U工程时,有几步关键配置直接影响后续开发:
- 设备选型:必须选择与硬件一致的CPU型号,比如H5U-1614MTD或H5U-3214MTD,选错了会导致某些指令不可用或IO映射错乱。
- PLC设置:H5U支持本地IO和远程EtherCAT从站,默认勾选本地IO。如果你的设备要用EtherCAT伺服,需要先在“EtherCAT配置”里扫描从站,再生成网络配置。
- 编程语言选择:新建程序组织单元(POU)时,语言类型选择“结构化文本”(ST)。一个工程里可以混合使用梯形图和ST,但建议逻辑部分统一用ST,维护起来更清晰。
- 扫描周期设置:默认任务周期是1ms,如果程序里做了大量浮点运算或通讯解析,建议把周期调整到2ms或5ms,避免任务超时报警。
建好工程后,强烈建议先编译一次空工程,确认软件环境和目标配置没问题,再开始写代码。这个习惯能帮你区分“软件报错”和“程序报错”,避免后面排查问题时分不清方向。
2.2 变量声明是ST语言的第一个分水岭
H5U的ST变量分三类:局部变量、全局变量和IO映射变量。很多人初学者写ST程序直接拿常量去比较、直接给输出点赋值,这在简单Demo里没问题,但一旦程序复杂起来,维护就是灾难。
我在实际项目里养成了一套自己的变量命名习惯:
- 输入信号前缀
di_(Digital Input),比如di_StartBtn。 - 输出信号前缀
do_(Digital Output),比如do_MainContactor。 - 模拟量输入用
ai_,模拟量输出用ao_。 - 内部中间变量用
b_(Bool)或r_(Real),比如b_IsRunning、r_TargetSpeed。
变量类型的选用也很关键。H5U的ST支持BOOL、INT、DINT、REAL、STRING、ARRAY、STRUCT等。初学者最常见的坑是:用INT存伺服位置,结果发现数值只能到32767,导致轴走到一半就报错。位置、计数这类变量,一律用DINT(32位整数)。浮点运算用REAL,但注意REAL是32位,精度有限,做高精度计算建议用LREAL。
提示:ST语言里变量名大小写不敏感,但InoProShop会自动把变量名规范为首字母大写。建议写代码时统一小写加下划线风格,和工程文件命名保持一致。
2.3 扫描周期与赋值顺序的理解
ST程序的执行方式是“从上到下、从左到右”顺序扫描,在一个扫描周期内,前面的赋值结果立即影响后面的语句。这和梯形图的“左母线到右母线”逻辑本质上一致,但ST更直观,因为它就是代码。
举个经典例子,启保停逻辑:
do_MainContactor := (di_StartBtn OR do_MainContactor) AND NOT di_StopBtn;这一行就实现了“启动优先、停止优先”的电机控制逻辑。如果换成梯形图,需要三行。但ST的坑在于:如果顺序写反了,比如先给do_MainContactor赋值,再读取它做运算,得到的结果就是上一次扫描周期的值。这在一些需要记忆功能的场合会出现“慢半拍”的问题。
解决办法有两种:一是严格在同一段逻辑里保持“先读后写”的顺序;二是用临时变量暂存,在程序末尾统一刷新输出。我写运动控制程序时,习惯把所有输出点的赋值集中在程序段末尾,这样既好查,也避免了扫描顺序带来的随机性。
2.4 任务周期与程序结构的匹配
H5U的程序任务可以是周期任务或事件任务。控制逻辑(伺服使能、运动指令)建议放在1ms或2ms的周期任务里,保证实时性;数据处理(模拟量滤波、通讯报文解析)可以放在10ms或50ms的任务里,减轻CPU负载。
InoProShop里新建任务时,需要指定调用哪个POU。我的习惯是:主程序Main放在周期任务里,子功能块在Main里通过ST指令调用,不把任务配置搞得太复杂。实际项目中,很多奇怪的“偶发故障”都是任务周期配置不合理造成的,比如在1ms任务里做了大量字符串处理,导致任务超时,CPU报错停机。
3. 实操过程与核心环节实现
3.1 第一条ST程序:启保停逻辑
打开InoProShop,新建工程,在“程序”下新建一个POU命名为Main,语言选ST,然后写这段代码:
// 电机启保停控制 b_StartCmd := di_StartBtn; b_StopCmd := di_StopBtn; IF b_StartCmd AND NOT b_StopCmd THEN do_Motor := TRUE; ELSIF b_StopCmd THEN do_Motor := FALSE; END_IF;下载到PLC后,用导线短接di_StartBtn对应的输入端子,观察do_Motor输出点是否导通。如果没反应,先查输入点有没有接对,再看PLC是否处于运行状态(RUN灯是否常亮)。
这里要解释一下为什么用IF...ELSIF...END_IF而不是直接把di_StartBtn塞进赋值语句。实际场景里,启动条件往往不是单一按钮,而是“按钮+前门关闭+气压正常+急停未触发”的串并联组合。用IF结构可以在条件分支里任意扩展,可读性和可维护性比一行嵌套赋值好得多。
3.2 IF/CASE状态机:自动加工流程
设备里最常见的逻辑就是状态机:待机、启动、加工、完成、报警。ST语言做状态机是降维打击。比如一个简易的自动剪切流程:
CASE b_Step OF 0: // 待机,等待启动信号 IF di_Start THEN b_Step := 10; END_IF; 10: // 夹紧 do_Clamp := TRUE; IF di_ClampDone THEN b_Step := 20; END_IF; 20: // 剪切 do_Cut := TRUE; IF di_CutDone THEN b_Step := 30; END_IF; 30: // 完成,松开 do_Clamp := FALSE; do_Cut := FALSE; b_Step := 0; END_CASE;注意CASE的步进值b_Step一定要初始化为0,而且每个分支结束前必须赋下一个状态值。如果某一步的条件永远不满足,程序就会卡在当前步。排查时用InoProShop的在线监控功能,直接看b_Step当前停在哪里,就能快速锁定问题。
我踩过一次坑:在状态10里同时给do_Clamp赋值TRUE,又在状态30里把它复位FALSE,但中间某个报警分支退出时忘了复位,导致设备即使停机了夹爪还扣着。后来我在每个状态进入时统一复位该状态下不需要的输出,才彻底解决。
3.3 数据运算:模拟量采集与工程量换算
ST的数值计算能力是梯形图无法比拟的。实际项目中,最常见的需求是读取温度传感器、压力变送器的4~20mA信号,换算成实际工程量。
假设你用H5U本地模拟量模块采集0~10V电压信号,模块分辨率是16位(0~27648),传感器量程是0~100摄氏度:
r_Raw := INT_TO_REAL(ai_TempRaw); // 原始值转浮点 r_Engineering := r_Raw / 27648.0 * 100.0; // 工程量换算如果要做滤波,可以加一个一阶低通滤波:
r_Filtered := r_Filtered + (r_Engineering - r_Filtered) * 0.1;这个低通滤波的系数0.1表示每次采样吸收10%的新值,系数越小滤波越强、响应越慢。我在实际项目里试过,采样周期1ms、系数0.05~0.1时,既能有效滤掉高频干扰,又不会让温度显示滞后太多。
ST语言的类型转换要特别留意。H5U不会自动做隐式类型转换,INT和REAL直接运算会报错。刚开始写程序时,编译报错有一半是类型不匹配造成的。我的习惯是:统一用LREAL做浮点运算,整数类型只在计数器和索引场景使用,减少转换次数。
3.4 功能块封装:把重复代码变成库
当程序里有两台伺服、三台气缸需要做相同的动作时,复制粘贴是最差的办法。正确做法是写一个功能块(FB),实例化多次。
举个例子,做一个气缸控制功能块:
FUNCTION_BLOCK FB_Cylinder VAR_INPUT b_ExtendCmd : BOOL; // 伸出指令 b_RetractCmd : BOOL; // 缩回指令 END_VAR VAR_OUTPUT b_ExtendOut : BOOL; // 控制伸出电磁阀 b_RetractOut : BOOL; // 控制缩回电磁阀 b_InPos : BOOL; // 到位信号 END_VAR VAR b_State : INT; // 内部状态 END_VAR然后在主程序里实例化:
FB_Cyl_A(b_ExtendCmd := di_CylAExtend, b_RetractCmd := di_CylARetract, b_ExtendOut := do_CylAExtend, b_RetractOut := do_CylARetract, b_InPos := di_CylAInPos);这样写的好处是:气缸的电磁阀控制逻辑(比如伸出到位自动关阀,防止线圈长时间通电发热)只需要写一次,后面所有气缸都用同一个功能块。需要修改报警逻辑时,只改功能块内部,全设备生效。
H5U的InoProShop里还能把功能块导出成库文件,下次做同类设备直接引用,开发效率翻倍。我自己的工控部件库就是这么一点点积累起来的。
3.5 结合EtherCAT总线:一个完整的伺服运动控制例程
H5U内置EtherCAT主站,配置汇川SV660N伺服的过程是:在InoProShop里双击“EtherCAT”,扫描从站,自动识别伺服,然后分配站号;接着在伺服对象字典里配置运行模式(位置模式、速度模式、转矩模式),把目标位置、目标速度、控制字等映射到PLC的IO变量;最后在程序里通过轴控指令驱动。
以位置控制为例,ST里写:
// 伺服使能 do_ServoEnable := TRUE; // 绝对定位:目标位置10000脉冲,速度5000脉冲/秒 H5U_SetAbsPos_Axis0(TRUE, 10000, 5000, 0, 1000);H5U_SetAbsPos是InoProShop提供的轴控功能块,参数含义是:脉冲数、速度、加减速时间、等待时间。使用前必须先在EtherCAT配置里建立好轴,并确认轴状态为“Standstill”(静止)或“Disabled”(未使能),否则指令不会执行。
这里有一个非常容易踩的坑:EtherCAT通讯建立需要几十到几百毫秒。程序刚上电时,如果立刻发使能和定位指令,伺服往往没反应。我的习惯是在程序中加一个延时判断:
b_EthercatReady := H5U_EtherCAT_GetStatus() = 0; // 总线状态正常 IF b_EthercatReady AND NOT b_ServoInitDone THEN // 延时500ms后使能 IF TON_ServoEnable.Q THEN do_ServoEnable := TRUE; END_IF; END_IF;实际调试中,运动控制类的故障有一半以上和时序有关,不是逻辑错了,而是“指令发得太早”。
3.6 ST与梯形图混用时的边界
H5U允许在同一个工程里混用ST和梯形图,但混用时要清楚它们的执行顺序。我一般遵循两条原则:
第一,逻辑控制用ST,视HMI动画类简单逻辑可放梯形图,但不要倒过来。ST的代码在复杂逻辑下可读性和可维护性远胜梯形图。
第二,全局变量尽量少用。无论是ST还是梯形图,都通过POU的输入输出接口传参,避免全局变量满天飞。我在一个项目里见过200多个全局变量,查一个问题需要打开十几个页面,极其痛苦。
3.7 在线调试与监控的技巧
InoProShop里的在线监控支持ST变量的实时数值显示。调试时,我会配合“写入”(强制赋值)功能给变量强制值,测试不同逻辑分支是否正常。
常用操作:右键变量,选“写入”,输入一个值,按回车。注意强制复位输出点时要确认设备处于安全状态,特别是气缸、伺服这类执行机构,强制输出可能导致设备突然动作,非常危险。我的习惯是:先断了气源和伺服使能,再强制输出。
另外,ST程序里设置的断点功能也很有用。H5U支持在ST代码行上打断点,在线运行时会停在断点处,可以单步执行查看每个变量的变化。排查复杂状态机问题,用断点比看监控数值方便得多。
4. 常见问题与排查技巧实录
4.1 编译报错集中在哪几类
ST编程的报错绝大多数集中在三类:类型不匹配、变量未声明、语法错误。
- 类型不匹配:比如把
BOOL赋值给INT,把INT和REAL直接相加。解决办法是显式转换,比如INT_TO_REAL()。 - 变量未声明:检查变量名是否拼写一致,InoProShop的变量名区分大小写不敏感,但下划线和拼写必须完全一致。
- 语法错误:最常见的是
IF忘记END_IF,CASE忘记END_CASE,或者分号漏写。ST的语句以分号结尾,漏一个分号编译到下一行才报错,排查时先看报错行上方最近的几行。
4.2 PLC不通信、下载不进去怎么办
H5U网口调试时,最常见的故障是电脑IP和PLC IP不在同一网段。H5U默认IP通常为192.168.1.6(不同批次可能有差异),电脑网卡需要设成同网段,比如192.168.1.10。InoProShop右下角的通讯状态会显示是否连接成功。
如果IP对不上又不知道PLC地址,可以通过InoProShop的“搜索设备”功能扫描同网段设备。另外,用网线直连时要注意,有些电脑的网卡会自动切换成“未识别网络”,需要在Windows网卡设置里手动指定IP。
注意:下载程序到运行中的PLC会停机,务必确认设备处于安全状态再下载。
4.3 输出点不动作的排查顺序
ST程序里明明写了do_Motor := TRUE,但输出点没电。排查顺序是:先看PLC的RUN灯是否常亮(程序是否在运行),再看在线监控里do_Motor是否为TRUE,再查输出点接线和负载电源。很多时候是模块接线端子没压紧,或者负载电源没开,而不是程序问题。经验法则:程序问题用在线监控查,硬件问题用万用表量,两者不能混为一谈。
4.4 程序跑飞或者卡死的原因
ST程序本身不会“跑飞”,但存在死循环的逻辑风险。比如WHILE循环条件永远为TRUE,程序就会卡在循环里,后续逻辑全部停摆。H5U的ST里要慎用WHILE,如果非用不可,一定要有循环次数的上限保护。
i_Count := 0; WHILE b_Find AND i_Count < 1000 DO // 查找逻辑 i_Count := i_Count + 1; END_WHILE;另外,数组越界也会导致程序异常。H5U对数组越界的处理是运行时报错,程序跳转到异常状态。所以数组索引计算时,务必加边界判断,比如:
IF i_Index >= 0 AND i_Index < 10 THEN r_Value := arr_Data[i_Index]; END_IF;4.5 伺服轴不使能或定位不准
EtherCAT伺服最常见的坑是电子齿轮比设置错误。SV660N的电子齿轮比由Pn202、Pn203参数决定(分子分母),如果设置不合理,一条定位指令发过去,实际走的距离和预期差距很大。解决办法是:在伺服调试软件里先手动点动,确认一圈脉冲数,再在PLC侧换算成实际位移单位。
定位不准的另一个常见原因是加减速时间设置过短,导致伺服过冲或跟踪误差过大报警。我的经验是:负载惯量比较大的场合,加减速时间不要小于500ms,速度曲线选择S型曲线而不是梯形曲线,能有效减少机械冲击。
5. 从能跑到好用:我的几点实在体会
做H5U的ST语言编程,最有意思的地方不是学会语法,而是你会发现编程思维变了。以前用梯形图,脑子里想的是“怎么把继电器电路翻译成逻辑图”,用了ST以后,精力集中在“工艺逻辑到底该怎么拆解”,这才是工程师的核心价值。
走完这7步之后,我还建议你做几件事:一是把常用逻辑(气缸控制、启停保护、报警复位)封装成自己的功能块库,以后做项目直接调用;二是找一台带EtherCAT伺服的设备,完整走一遍“总线配置—程序编写—带载调试”的流程,比看十篇教程都管用;三是养成随时备份代码的习惯,H5U的工程文件可以导出为.zip,我见过不少工程师电脑硬盘损坏后全部代码丢失的惨剧。
在实际项目中,我建议新人在第一台设备上用ST重写一遍以前用梯形图做过的逻辑,对比两种方式在可读性、调试效率上的差异。不用追求“强行用ST写一切”,简单的启停逻辑用梯形图更直白,涉及数据分析、状态机和运动控制的地方用ST更顺手。工具没有好坏,适合场景的才是最好的。
最后分享一个小技巧:写完ST程序后,多用InoProShop的“自动格式化”功能(快捷键Ctrl+Shift+F),让代码缩进整齐。代码的排版整洁程度,往往和逻辑严谨程度成正比。看着舒服,出错的概率也低。