Cx-Programmer中ST语言编程实战:从梯形图到结构化文本的混合编程指南
1. 为什么要在Cx-Programmer里折腾ST语言如果你平时主要用梯形图Ladder Diagram写PLC程序第一次听说要在Cx-Programmer里用ST语言Structured Text结构化文本编程大概率会有两种反应要么觉得“梯形图够用了何必折腾”要么觉得“ST是高级语言肯定很难上手”。我刚开始接触的时候也是这么想的直到遇到一个十几轴运动控制加复杂配方管理的项目梯形图写到后面自己都理不清逻辑才被迫转向ST。先说清楚Cx-Programmer是什么。它是欧姆龙OMRON系列PLC的编程软件主要面向CP、CJ、CS、NJ等系列的控制器。很多人对它的印象停留在“画梯形图”的阶段但实际上从Cx-Programmer 9.x版本开始它对IEC 61131-3标准的支持已经相当完整ST语言的编辑器、编译器和调试工具都能用只是很多人没去碰。ST语言本质上是一种类Pascal的高级文本语言它的核心价值在于处理数学运算、条件分支、循环、数组操作和字符串处理时比梯形图简洁得多。举个最直观的例子你要对一个长度为100的数组做冒泡排序梯形图里得写一大堆比较和传送指令而ST里两个嵌套循环加一个临时变量就搞定了。这不是炫技是实实在在的维护成本差异。那为什么很多人还是在Cx-Programmer里坚持用梯形图原因也很现实梯形图直观电气工程师看一眼就能懂现场调试时监控方便。而ST是文本逻辑藏在代码里不熟悉的人读起来费劲。所以我的建议从来不是“全面替换梯形图”而是混合编程——把逻辑控制、互锁、急停这些用梯形图写把数据处理、配方管理、通信协议解析、PID运算这些用ST写。Cx-Programmer支持在同一个工程里混用多种语言每个POUProgram Organization Unit可以独立选择语言类型这才是它真正好用的地方。这篇文章面向的是已经会基本梯形图、想在Cx-Programmer里把ST用起来的从业者。我会从环境配置、语法要点、和梯形图的互操作、调试技巧、常见坑这几个角度展开尽量把每一步的“为什么”讲清楚。如果你完全没碰过PLC编程建议先补一下基本概念再看否则有些术语会卡住。提示Cx-Programmer不同版本对ST的支持程度有差异。9.5以上版本对ST的语法检查、在线监控和断点调试支持比较完善如果条件允许尽量用较新版本。老版本比如9.0以下的ST编辑器功能很弱不建议在上面写复杂ST程序。2. 在Cx-Programmer里把ST环境搭起来2.1 新建工程时的语言选择与POU配置打开Cx-Programmer新建工程第一步是选PLC型号。这里有个容易被忽略的点不是所有欧姆龙PLC型号都支持ST。CP1E、CP1L这些低端机型对ST的支持有限CJ2、CS1、NJ系列支持比较完整。如果你选的型号不支持ST在新建POU的时候语言选项里根本看不到ST这不是软件问题是硬件限制。选好型号后在工程树的“New Program”或者右键“Program”里新建一个POU。新建对话框里会让你选语言这时候选“ST”就行。这里有个细节Cx-Programmer里POU的类型分Program、Function Block、Function三种。Program是主程序可以直接被任务调用Function Block和Function是子程序需要被调用才能执行。ST语言在这三种POU里都能用但用途不同。我的习惯是主逻辑用梯形图的Program数据处理和算法用ST的Function Block。为什么用Function Block而不是Function因为Function Block有内部状态可以定义静态变量适合做需要保持状态的逻辑比如PID控制器、状态机、计数器。Function没有内部状态每次调用都是独立的适合做纯计算比如单位换算、查表、CRC校验。新建ST POU之后编辑器会给你一个基本框架(* 这是一个ST语言的POU示例 *) (* 变量声明区在VAR...END_VAR之间 *) VAR nIndex : INT; fTemp : REAL; END_VAR (* 逻辑代码区 *) nIndex : 0; fTemp : 3.14;变量声明区和代码区是分开的这一点和梯形图不同。梯形图的变量是在符号表里统一管理的而ST的变量可以在POU内部声明也可以引用全局变量。这个设计的好处是封装性好坏处是如果变量命名不规范很容易和全局变量冲突。2.2 变量声明的类型与作用域ST的变量声明是很多人第一个卡住的地方。Cx-Programmer里ST的变量类型分基本类型和派生类型。基本类型包括BOOL、INT、DINT、REAL、LREAL、WORD、DWORD、STRING等派生类型包括数组、结构体、枚举。变量声明的作用域由关键字决定关键字作用域说明VAR局部变量只在当前POU内可见每次调用重新初始化VAR_STATIC静态变量只在当前POU内可见保持上次调用的值VAR_GLOBAL全局变量整个工程可见需要在全局变量表里声明VAR_INPUT输入参数只在Function Block和Function里用调用时传入VAR_OUTPUT输出参数只在Function Block和Function里用调用后返回VAR_IN_OUT输入输出参数传引用可以在POU内部修改外部变量这里最容易踩的坑是VAR和VAR_STATIC的区别。很多人写了一个计数器发现每次调用都从零开始就是因为用了VAR而不是VAR_STATIC。VAR在每次POU调用时都会重新初始化而VAR_STATIC会保持上一次的值。如果你要写一个累计计数的逻辑必须用VAR_STATIC。另一个坑是VAR_IN_OUT。它传的是引用不是值。这意味着你在POU内部修改这个变量外部的变量也会跟着变。这个特性在写交换函数、排序函数的时候很有用但如果不小心也会意外修改外部变量。我的经验是除非明确需要修改外部变量否则一律用VAR_INPUT传值避免副作用。2.3 编译选项与ST相关的设置Cx-Programmer的编译选项里有几个和ST相关的设置很多人从来没注意过。在“PLC”菜单下的“编译设置”里有一个“ST语言选项”标签页。这里面有几个关键选项严格类型检查打开后INT和DINT之间的隐式转换会报错必须显式转换。建议打开能避免很多类型不匹配的运行时错误。数组越界检查打开后运行时会检查数组下标是否越界越界会触发错误。建议打开但会稍微增加扫描周期。除零检查打开后除法运算的除数为零时会触发错误而不是产生未定义结果。强烈建议打开。这些选项默认可能是关闭的因为打开后会增加编译后的代码量和执行时间。但对于调试阶段打开它们能帮你快速定位问题。等程序稳定了再根据实际需求决定是否关闭。还有一个容易被忽略的点ST代码的编译顺序。Cx-Programmer会按照POU在工程树里的顺序编译如果POU A调用了POU B但B在A后面编译会报错。解决办法是把被调用的POU移到调用者前面或者在调用前先声明。这个和C语言的头文件包含类似但Cx-Programmer没有“前向声明”的概念只能靠调整顺序。3. ST语法里那些梯形图替代不了的写法3.1 条件分支IF和CASE的选择逻辑ST里的条件分支有两种IF语句和CASE语句。IF适合处理少量、不连续的条件CASE适合处理多个、基于同一个变量的离散值。先看IF的写法IF fTemperature 100.0 THEN bAlarm : TRUE; nAlarmCode : 1; ELSIF fTemperature 80.0 THEN bWarning : TRUE; nAlarmCode : 2; ELSE bAlarm : FALSE; bWarning : FALSE; nAlarmCode : 0; END_IF;这个逻辑用梯形图也能写但梯形图里要画三个比较指令加一堆置位复位ST里几行就搞定了。而且ELSIF的优先级顺序很直观从上到下依次判断第一个满足条件的分支执行后直接跳出不会继续判断后面的。CASE的写法CASE nState OF 0: bStart : FALSE; bStop : TRUE; 1: bStart : TRUE; bStop : FALSE; 2: bStart : FALSE; bStop : FALSE; nCounter : nCounter 1; 3..5: (* 处理状态3到5 *) bSpecial : TRUE; ELSE (* 处理未定义状态 *) nState : 0; END_CASE;CASE的一个强大之处是支持范围3..5和多个值用逗号分隔比如0,2,4。这在处理状态机的时候特别方便。梯形图里要实现类似的功能得用一堆比较指令加跳转可读性差很多。我的经验是状态机一律用CASE参数判断用IF。状态机的状态值是离散的、有限的CASE写出来一目了然。参数判断比如温度、压力、位置是连续的、有优先级的IF更合适。3.2 循环FOR和WHILE在PLC里的实际用途很多人觉得PLC里不需要循环因为PLC是循环扫描的每个扫描周期执行一次逻辑。但实际上循环在处理数组、批量数据、通信缓冲区的时候非常有用。FOR循环的写法FOR nIndex : 0 TO 99 DO IF arData[nIndex] fMax THEN fMax : arData[nIndex]; nMaxIndex : nIndex; END_IF; END_FOR;这段代码在一个扫描周期内找出数组里的最大值和它的下标。如果用梯形图写要么用100个比较指令代码量爆炸要么用间接寻址加循环梯形图里实现循环很别扭。ST里一个FOR循环就搞定了。WHILE循环的写法nIndex : 0; WHILE (nIndex 100) AND (arData[nIndex] 0) DO nSum : nSum arData[nIndex]; nIndex : nIndex 1; END_WHILE;WHILE适合循环次数不确定的情况比如遍历一个以0结尾的数组。FOR适合循环次数确定的情况。这里有一个非常重要的注意事项PLC里的循环是在一个扫描周期内执行的如果循环次数太多比如几万次会导致扫描周期严重超时看门狗报警。我的经验是单个扫描周期内的循环总次数控制在1000次以内超过的话要么拆分成多个扫描周期执行要么优化算法。曾经有个项目在ST里写了一个10000次的FOR循环做数据滤波结果PLC直接看门狗复位排查了半天才发现是循环的问题。3.3 数组和结构体批量数据处理的利器数组和结构体是ST相比梯形图最大的优势之一。梯形图里处理数组基本靠间接寻址可读性极差。ST里数组就是数组下标访问、批量赋值都很自然。数组声明VAR arTemperature : ARRAY[0..9] OF REAL; arStatus : ARRAY[1..16] OF BOOL; anCounter : ARRAY[0..4, 0..4] OF INT; (* 二维数组 *) END_VAR数组赋值和访问arTemperature[0] : 25.5; arTemperature[1] : arTemperature[0] 10.0; FOR nIndex : 0 TO 9 DO arTemperature[nIndex] : 0.0; END_FOR;结构体声明TYPE ST_Motor : STRUCT bRun : BOOL; bFault : BOOL; nSpeed : INT; fCurrent : REAL; nRunHours : DINT; END_STRUCT; END_TYPE VAR stMotor1 : ST_Motor; stMotor2 : ST_Motor; END_VAR结构体的好处是把相关的变量打包在一起传递和复制都很方便。比如你要把一台电机的所有状态传给一个Function Block处理直接传结构体就行不用一个个传参数。数组和结构体结合使用可以处理很复杂的数据结构。比如VAR astMotor : ARRAY[0..7] OF ST_Motor; END_VAR FOR nIndex : 0 TO 7 DO IF astMotor[nIndex].bFault THEN astMotor[nIndex].bRun : FALSE; END_IF; END_FOR;这段代码遍历8台电机任何一台有故障就停止运行。用梯形图写这个逻辑得用间接寻址加变址寄存器新手根本看不懂。3.4 字符串处理梯形图几乎做不了的事字符串处理是ST的另一个强项。梯形图里处理字符串基本只能靠通信指令和ASCII码转换非常麻烦。ST里字符串就是STRING类型支持拼接、比较、查找、截取。VAR sMessage : STRING[80]; sTemp : STRING[20]; nPos : INT; END_VAR sMessage : Motor 1 Fault; sTemp : CONCAT(Alarm: , sMessage); nPos : FIND(sMessage, Fault); IF nPos 0 THEN sTemp : LEFT(sMessage, nPos - 1); END_IF;这些字符串函数在Cx-Programmer的ST里都是内置的直接用就行。做HMI报警信息拼接、通信协议解析、配方名称管理的时候特别有用。不过要注意STRING类型在PLC里占用的内存比较大STRING[80]就占81个字节还有一个字节存长度。如果定义了大量STRING变量可能会超出PLC的内存限制。我的经验是STRING长度按实际需要定义不要动不动就STRING[255]。4. ST和梯形图混编时的互操作细节4.1 在梯形图里调用ST写的Function Block这是混编最常见的场景主逻辑用梯形图算法用ST写成Function Block然后在梯形图里调用。Cx-Programmer里这个操作很直接在梯形图编辑器里插入一个Function Block指令选择你写好的ST Function Block然后连接输入输出参数。但有几个细节要注意第一输入输出参数的数据类型必须匹配。ST Function Block里定义的VAR_INPUT是REAL梯形图里传进去的也必须是REAL。如果梯形图里是INT编译会报错。这时候需要在梯形图里加一个类型转换指令或者把ST里的参数类型改成INT。第二Function Block的实例化。在梯形图里调用Function Block需要给它分配一个实例Instance。这个实例保存了Function Block的内部状态VAR_STATIC变量。如果同一个Function Block被多处调用每处都需要独立的实例否则状态会互相干扰。Cx-Programmer里在调用指令的属性里可以设置实例名。第三EN和ENO的处理。梯形图里的Function Block调用有EN使能和ENO使能输出引脚。如果EN为FALSEFunction Block不执行ENO也为FALSE。这个特性可以用来做条件调用。但要注意如果Function Block内部有VAR_STATIC变量EN为FALSE时这些变量保持上次的值不会复位。4.2 在ST里读取梯形图的变量和IOST POU里可以直接访问全局变量和IO地址。全局变量在工程树的“Global Variables”里声明ST里直接用变量名就行。IO地址用AT关键字映射VAR_GLOBAL bStartButton AT %IX0.00 : BOOL; bMotorOutput AT %QX0.01 : BOOL; nAnalogInput AT %IW0 : INT; END_VAR在ST代码里这些变量就像普通变量一样使用IF bStartButton THEN bMotorOutput : TRUE; END_IF; fPressure : INT_TO_REAL(nAnalogInput) * 0.1;这里有一个容易踩的坑IO地址的映射关系必须和实际硬件配置一致。如果%IX0.00在实际硬件上是输入点0.00但在程序里映射成了别的地址程序逻辑就会出错。我的习惯是在全局变量表里把IO映射集中管理每个变量都加注释说明对应的物理点避免混乱。4.3 数据类型转换的隐式与显式规则ST是强类型语言不同类型之间的运算需要显式转换。Cx-Programmer里常用的转换函数函数说明示例INT_TO_REAL整数转浮点fTemp : INT_TO_REAL(nValue);REAL_TO_INT浮点转整数截断nValue : REAL_TO_INT(fTemp);INT_TO_DINT整数转双整数nBig : INT_TO_DINT(nSmall);BOOL_TO_INT布尔转整数nFlag : BOOL_TO_INT(bFlag);INT_TO_BOOL整数转布尔bFlag : INT_TO_BOOL(nFlag);REAL_TO_STRING浮点转字符串sText : REAL_TO_STRING(fTemp);这里要注意REAL_TO_INT是截断不是四舍五入。3.9转成INT是3不是4。如果需要四舍五入得自己加0.5再转nValue : REAL_TO_INT(fTemp 0.5);另一个坑是INT和DINT的溢出。INT是16位范围-32768到32767。如果两个INT相乘结果可能超出范围导致溢出。这时候需要先转成DINT再运算nResult : INT_TO_DINT(nA) * INT_TO_DINT(nB);如果直接写nResult : nA * nB;当nA和nB都是1000时结果1000000超出INT范围会得到一个错误的值。这个坑我在实际项目里踩过排查了很久才发现是数据类型的问题。5. ST程序的调试与在线监控5.1 在线监控ST变量的正确姿势Cx-Programmer的ST编辑器支持在线监控但和梯形图的监控方式不太一样。梯形图里你可以直接看到每个触点和线圈的通断状态ST里需要把变量添加到监控窗口。具体操作在ST编辑器里右键选择“Register to Watch Window”把需要监控的变量加进去。然后在在线模式下Watch Window会实时显示这些变量的值。这个功能很实用但有几个限制第一不是所有变量都能监控。VAR_INPUT和VAR_OUTPUT可以监控VAR和VAR_STATIC也可以但某些临时变量编译器优化掉的可能监控不到。第二监控会增加通信负载。如果一次监控几十个变量在线监控的刷新速度会变慢。我的经验是只监控关键变量不要一股脑全加进去。第三REAL类型的显示精度。Cx-Programmer默认显示REAL类型的小数点后几位可能不够可以在Watch Window的设置里调整显示格式。5.2 断点调试和单步执行的实际体验Cx-Programmer的ST编辑器支持断点和单步执行这个功能在排查复杂逻辑的时候非常有用。设置断点的方法是在代码行号旁边点击会出现一个红点。在线模式下程序执行到断点会暂停你可以查看当前所有变量的值然后单步执行Step Over或步入Step Into。但要注意断点调试会暂停PLC的扫描周期。如果PLC控制着正在运行的设备暂停扫描可能导致设备停机或危险。所以断点调试只能在设备停止状态下进行或者用模拟器调试。我的习惯是先在模拟器里用断点把逻辑跑通确认没问题了再下载到实际PLC。Cx-Programmer自带模拟器Simulator支持ST的断点调试这个功能很多人没用过但确实能省很多现场调试时间。5.3 常见运行时错误的排查思路ST程序常见的运行时错误有几类除零错误如果打开了除零检查除数为零时会触发错误。排查方法是检查所有除法运算的除数确保不为零。我的习惯是在除法前加判断IF nDivisor 0 THEN fResult : fDividend / INT_TO_REAL(nDivisor); ELSE fResult : 0.0; END_IF;数组越界如果打开了数组越界检查下标超出范围时会触发错误。排查方法是检查所有数组访问的下标确保在声明范围内。特别是用变量做下标的时候要确保变量的值在合法范围内。类型不匹配如果打开了严格类型检查隐式类型转换会报错。排查方法是检查所有赋值和运算确保类型一致。这个错误在编译时就能发现比运行时错误好排查。看门狗超时如果ST程序里的循环太多或太复杂扫描周期超过看门狗设定值PLC会报警。排查方法是减少循环次数或者把大循环拆分成多个扫描周期执行。6. 那些年我在ST上踩过的坑6.1 VAR和VAR_STATIC搞混导致的计数器归零这个坑我踩过不止一次。写了一个计数器Function Block逻辑很简单VAR nCount : INT; END_VAR nCount : nCount 1;调用的时候发现nCount永远是1不会累加。原因就是用了VAR而不是VAR_STATIC。VAR在每次调用时都会重新初始化为0所以nCount永远是011。改成VAR_STATIC就正常了。这个坑的隐蔽性在于编译不报错逻辑看起来也没问题但运行结果就是不对。新手很容易在这里卡住。我的经验是只要变量需要保持状态一律用VAR_STATIC。只有确实需要每次重新初始化的临时变量才用VAR。6.2 循环里的EXIT和CONTINUE用错位置ST支持EXIT和CONTINUE关键字用来提前跳出循环或跳过本次循环。但这两个关键字在Cx-Programmer里的行为和某些高级语言不同。EXIT是跳出整个循环CONTINUE是跳过本次循环的剩余部分直接进入下一次循环。看起来很简单但在嵌套循环里容易搞混FOR nI : 0 TO 9 DO FOR nJ : 0 TO 9 DO IF arData[nI, nJ] 0 THEN EXIT; (* 只跳出内层循环外层继续 *) END_IF; END_FOR; END_FOR;EXIT只跳出当前所在的循环不会跳出所有循环。如果要跳出所有循环需要用一个标志变量加判断。这个行为和C语言一致但和某些PLC编程习惯不同容易搞错。6.3 字符串比较的大小写敏感问题ST里的字符串比较是大小写敏感的。Motor和motor是不相等的。这个在HMI报警信息处理的时候容易出问题如果HMI传过来的字符串大小写和程序里预期的不一致比较就会失败。解决办法是要么统一大小写用UPPER或LOWER函数转换要么用不区分大小写的比较函数如果Cx-Programmer支持的话。我的习惯是在字符串比较前统一转成大写IF UPPER(sInput) MOTOR THEN (* 处理逻辑 *) END_IF;6.4 浮点数比较的精度陷阱浮点数比较是ST里最隐蔽的坑之一。REAL类型是32位浮点数精度有限。0.1 0.2在浮点数里不等于0.3而是等于0.30000001192...。所以直接比较两个浮点数是否相等往往会失败。正确的做法是比较差值是否小于一个很小的阈值IF ABS(fA - fB) 0.0001 THEN (* 认为相等 *) END_IF;这个坑在做PID运算、位置控制、模拟量处理的时候特别容易踩。我曾经写了一个位置判断逻辑目标位置是100.0实际位置是100.00001直接比较不相等导致逻辑一直不触发。后来改成差值比较就正常了。6.5 在线修改ST代码的注意事项Cx-Programmer支持在线修改ST代码Online Edit但和梯形图的在线修改有些不同。ST的在线修改实际上是重新编译整个POU然后下载差异部分。这个过程可能会导致POU内部的VAR_STATIC变量被复位。这意味着如果你在线修改了一个带状态机的ST Function Block修改下载后状态机的状态可能会丢失回到初始状态。如果设备正在运行这可能导致意外动作。我的经验是在线修改ST代码前先确认设备处于安全状态。如果设备不能停尽量只修改不影响状态的代码或者用梯形图做在线修改梯形图的在线修改对状态的影响小一些。7. 从梯形图思维切换到ST思维的关键转变7.1 从“并行执行”到“顺序执行”的思维差异梯形图和ST最大的思维差异在于执行模型。梯形图是“并行”的同一梯级里的多个支路同时判断从左到右、从上到下扫描。ST是“顺序”的代码从上到下依次执行每一行执行完才执行下一行。这个差异导致同样的逻辑在梯形图和ST里的写法可能完全不同。举个例子三个按钮控制一个灯任意一个按下灯就亮。梯形图里三个常开触点并联就行。ST里得写IF bButton1 OR bButton2 OR bButton3 THEN bLight : TRUE; ELSE bLight : FALSE; END_IF;看起来也简单但如果逻辑更复杂比如“按钮1按下且按钮2没按下或者按钮3按下了两次”梯形图里就得用辅助继电器和边沿检测ST里用IF和计数器就行。关键是要理解ST里的赋值是立即生效的。bLight : TRUE;执行后bLight马上变成TRUE后面的代码如果读bLight读到的就是TRUE。而梯形图里同一个扫描周期内线圈的状态变化不会影响前面的触点。这个差异在写互锁逻辑的时候特别重要。7.2 用状态机替代一堆互锁逻辑梯形图里处理复杂逻辑往往靠一堆互锁和辅助继电器。比如一个设备有手动、自动、报警、复位四个状态梯形图里得用一堆M继电器和比较指令。ST里用CASE写状态机清晰得多CASE nState OF 0: (* 空闲 *) IF bStart THEN nState : 1; END_IF; 1: (* 运行 *) IF bAlarm THEN nState : 2; ELSIF bStop THEN nState : 0; END_IF; 2: (* 报警 *) IF bReset THEN nState : 0; END_IF; END_CASE;这个状态机逻辑一目了然什么状态、什么条件、跳到什么状态。梯形图里要实现同样的逻辑得画一堆比较和跳转维护起来很痛苦。我的经验是任何超过3个状态的逻辑都值得用ST的状态机重写。前期花点时间后期维护省很多事。7.3 数据结构化带来的维护性提升梯形图里的数据是“散装”的每个变量独立定义独立使用。ST里可以把相关数据打包成结构体整体传递、整体处理。比如一个配方管理功能梯形图里可能定义了几十个独立的变量配方号、温度设定、压力设定、时间设定、速度设定……ST里定义一个结构体TYPE ST_Recipe : STRUCT nID : INT; fTemperature : REAL; fPressure : REAL; nTime : DINT; nSpeed : INT; END_STRUCT; END_TYPE VAR astRecipes : ARRAY[0..9] OF ST_Recipe; stCurrentRecipe : ST_Recipe; END_VAR这样配方数据的复制、比较、存储都变得很简单stCurrentRecipe : astRecipes[nSelected];一行代码搞定梯形图里得写几十个传送指令。这就是数据结构化带来的维护性提升。8. 关于ST在Cx-Programmer里的实际定位说了这么多ST的好处也得客观说一下它的局限。ST不是万能的在Cx-Programmer里它更适合做“辅助”而不是“主力”。ST适合的场景数学运算、数组处理、字符串处理、状态机、通信协议解析、配方管理、PID算法、数据记录。梯形图适合的场景电气互锁、急停逻辑、手动操作、IO映射、简单的条件控制。不适合用ST的场景安全逻辑安全PLC通常要求用梯形图或FBD、需要频繁在线修改的逻辑ST在线修改对状态影响大、团队里其他人不熟悉ST的项目维护成本高。我的实际做法是一个工程里梯形图占60%ST占40%。主程序用梯形图子程序和算法用ST。这样既保证了可读性又发挥了ST的优势。最后说一个很多人关心的问题学ST难不难。如果你有C语言或Pascal的基础ST基本一天就能上手语法很简单。如果没有编程基础从梯形图转ST大概需要一两周的适应期主要是习惯文本编程的思维方式。我的建议是先从简单的Function Block开始写比如单位换算、平均值计算慢慢过渡到复杂的状态机和数组处理。不要一上来就写几千行的ST程序那样容易挫败。提示Cx-Programmer的帮助文档里有ST语言的完整语法参考和示例虽然是英文的但写得很详细。遇到不确定的语法直接查帮助文档比在网上搜靠谱。我在实际项目里用ST处理过最多的是通信协议解析和配方管理。通信协议解析用ST写比梯形图简洁十倍不止。配方管理用结构体数组增删改查都很方便。这两个场景如果你遇到了强烈建议用ST试试用过就回不去了。

相关新闻

国内环境源码部署OpenAI Codex:CLI、沙箱与出口配置实战

国内环境源码部署OpenAI Codex:CLI、沙箱与出口配置实战

简介:面向国内开发者的OpenAI Codex配置方案源码包,覆盖Windows、macOS与Linux三大主流系统,解决官方接口访问受限、依赖安装与认证配置等痛点,适合需要快速接入AI编程助手的初中级开发者。压缩包共3个文件,包含inscod…

2026/10/18 15:11:58 阅读更多 →
X-AnyLabeling 加载 yolox-s-onnx 自动标注模型避坑指南

X-AnyLabeling 加载 yolox-s-onnx 自动标注模型避坑指南

简介:这份资源面向计算机视觉开发者与数据标注从业者,提供X-AnyLabeling结合YOLOX-S-ONNX的自动标注模型,用于在图像目标检测任务中快速生成初步标注框,降低人工标注成本。压缩包共2个文件,包含1个yaml配置文件与1个on…

2026/10/18 15:11:58 阅读更多 →
SeaTunnel Elasticsearch 连接器实战:四类典型场景的数据入仓与排查指南

SeaTunnel Elasticsearch 连接器实战:四类典型场景的数据入仓与排查指南

SeaTunnel Elasticsearch 连接器实战:四类典型场景的数据入仓与排查指南 【免费下载链接】seatunnel SeaTunnel is a multimodal, high-performance, distributed, massive data integration tool. 项目地址: https://gitcode.com/GitHub_Trending/se/seatunnel …

2026/10/18 15:11:58 阅读更多 →

最新新闻

医疗知识图谱问答系统开发实战:数据清洗、Neo4j入库与Flask部署

医疗知识图谱问答系统开发实战:数据清洗、Neo4j入库与Flask部署

简介:一套基于Python的医疗知识图谱知识问答系统课程设计资源包,适合知识图谱与问答方向的初学者,帮助理解医疗实体关系建模、规则式对话流程及问句查询解析。压缩包共58个文件,约20MB,核心内容涵盖设计报告、源代码、…

2026/10/18 16:14:26 阅读更多 →
ROS2双臂协同抓取闭环:视觉-运动-仿真系统级对齐

ROS2双臂协同抓取闭环:视觉-运动-仿真系统级对齐

简介:本资源是一套面向机器人算法开发者与ROS2进阶学习者的双臂视觉抓取系统仿真方案,聚焦深度相机集成、手眼标定、三维环境建模与视觉引导抓取四大核心任务。项目基于ROS2Python构建,以OpenArm双臂机器人模型为载体,在MuJoCo高保…

2026/10/18 16:14:26 阅读更多 →
Mac 上 Cursor 配置 JDK 与 Maven 版本:TaoToken 统一 Key 接入实践

Mac 上 Cursor 配置 JDK 与 Maven 版本:TaoToken 统一 Key 接入实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 16:14:26 阅读更多 →
等了两年的GPT-5,它来了!!!TaoToken统一Key接入实测

等了两年的GPT-5,它来了!!!TaoToken统一Key接入实测

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 16:14:26 阅读更多 →
表单交互与验证——复杂表单的工程化实现与 TaoToken 统一 Key 接入

表单交互与验证——复杂表单的工程化实现与 TaoToken 统一 Key 接入

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 16:14:26 阅读更多 →
从零到上架:第一个App开发全流程复盘与踩坑教训

从零到上架:第一个App开发全流程复盘与踩坑教训

如果你问我,一个从没上过架的开发者,从零开始把第一个App做到能下载、能使用、能收到真实用户反馈,需要多久?我的答案是:大概两个半月。这个速度不算快,也不算慢,但这段时间里踩过的坑、想明白的…

2026/10/18 16:13:26 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →