简介:这份《欧姆龙ST编程中文手册》面向刚接触欧姆龙PLC结构化文本编程的工程师、自动化专业学生及设备调试人员,旨在解决ST语言功能块使用、变量定义与编程规范不熟悉的问题。资源为单一PDF文档,压缩包约2.26MB,内容围绕CX-Programmer 6.1环境展开,依次讲解功能块介绍与类型、功能块规格与限制、变量、将功能块定义转换为库文件、应用程序及版本升级信息,并补充编程环境设置、基本语法、安全注意事项与应用注意事项,目录层级清晰,便于按章节检索。该手册已有7329人学习,适合作为入门查阅与现场调试时的案头参考。读者可从中了解数字量/模拟量、计时器、计数器等常见功能块的组织方式,掌握变量声明、赋值、条件与循环语句的写法,并借助技术规格、实例规格和限制说明规避配置错误,提升编程效率与程序可维护性。
1. 手册定位:先看你手里的 CPU 在不在支持清单里
有人拿到编号 W447-CN5-02 的欧姆龙ST编程中文手册,翻两页发现通篇在讲功能块、变量、实例,以为拿错了书。这份文档的正身是 CX-Programmer 6.1 的功能块操作手册,ST 只是它支持的两种算法描述语言之一,另一条路是梯形图。它要解决的问题很具体:把现场反复出现的逻辑——四点滤波、报警回差、阀位换算、工位互锁——打包成一个可复用、可反复实例化的块,而不是每次复制几十行梯形图再改地址。适合手上跑着 CS1-H、CJ1-H、CJ1M、CP1H 这几类 CPU、又想把程序做成模块化结构的电气工程师;如果你连 ST 的IF、FOR都没写过,先补语法再来,附录 B 只给规格,不教入门。
2. 功能块不是函数:ST 在这套体系里到底占什么位置
很多人第一次接触功能块,会把它当 C 语言的函数理解,写完之后发现调用两次、地址打架、内部变量互相污染,这就是概念没对齐。这一章把三层结构拆开,再说清 ST 在里面能干什么、不能干什么。
2.1 定义、库文件、实例是三个不同的东西
功能块定义是模板,里面只有两样东西:一份变量定义表(输入、输出、输入输出、内部、外部),一段用梯形图或 ST 写的算法。定义本身不占 PLC 的用户程序存储区,也不产生任何运行数据。把定义打包导出,得到的是一个库文件,CX-Programmer 里以.cxf后缀保存,这是唯一能在项目之间、工程师之间传递的单位。
真正占用资源的是实例。你在梯形图里把功能块拖进回路,给它起个名字,比如FB_Filter_01,这一刻才在内存里生成一份独立的内部变量副本。同一套滤波定义调四次,就有四份Buf数组、四个写指针,互不干扰。这跟传统子程序最大的差别在于:子程序靠固定地址传参,第二次调用必须换一套地址;功能块靠实例名隔离,第二次调用只要换个实例名。
提示:实例名在同一个任务内必须唯一,跨任务重名在编译阶段就会报错,不要指望下载时才发现。
库文件的维护是这套机制里最容易被忽视的一环。现场调试时改了一版算法,直接在主程序里改实例是可以的,但下次别人从库里重新拖一个进来,用的还是老版本。我一般会要求项目里留一份库文件的版本记录,改完算法先更新库、再更新实例,顺序反了就会出现"同一个项目里两个实例行为不一致"的玄学问题。
2.2 ST 适合写什么,梯形图适合写什么
两种语言在同一个功能块里只能二选一,定义的时候就要定下来。选型不是喜好问题,是维护成本问题。
梯形图适合互锁、时序、单点逻辑,尤其是需要现场电工直接看懂的部分。ST 适合算术运算、循环、数组搬移、状态机、带多个分支条件的判断。举个具体例子:把 12 路模拟量做限幅、标定、求平均,梯形图要写几十个比较指令和加法指令,改一个系数要翻三屏;ST 里就是一个FOR循环加两行算式,参数改在变量表里,现场不用动程序。
手册附录 B 给的 ST 规格覆盖了这些内容:赋值、IF/ELSIF/ELSE、CASE、FOR、WHILE、REPEAT,以及EXIT、RETURN这类流程控制。附录 A 给数据类型,常用的有BOOL、INT、UINT、DINT、UDINT、REAL、WORD、DWORD,还有对应的 BCD 类型。附录 C 单独讲外部变量,这一块后面第 3 章会展开。
下面这段是我常用的四点滑动平均,用来演示 ST 在功能块里的典型写法:
(* 功能块:4 点滑动平均滤波 *) (* 变量表登记: *) (* Input : In INT 原始采样值 *) (* Input : Rst BOOL 清零 *) (* Output : Ave INT 平均值 *) (* Internal : Buf ARRAY[0..3] OF INT *) (* Internal : WIdx INT 写指针 *) (* Internal : I INT 循环变量 *) (* Internal : Sum DINT 累加和,用 DINT 防溢出 *) IF Rst THEN FOR I := 0 TO 3 DO Buf[I] := 0; (* 缓冲区全部清零 *) END_FOR; WIdx := 0; Ave := 0; RETURN; (* 清零周期直接返回,不参与计算 *) END_IF; Buf[WIdx] := In; (* 新值覆盖最旧的一格 *) WIdx := WIdx + 1; IF WIdx > 3 THEN WIdx := 0; (* 环形指针回绕 *) END_IF; Sum := 0; FOR I := 0 TO 3 DO Sum := Sum + Buf[I]; END_FOR; Ave := Sum / 4; (* 整数除法,余数丢弃 *)逻辑上有三个点值得说。第一,Rst用了RETURN提前退出,避免清零周期把 0 也当成一次采样写进缓冲,否则复位后前四个周期的平均值会偏小。第二,累加用DINT而不是INT,四点INT相加在极端值下会溢出,INT的范围是 -32768 到 32767,两路 30000 相加就翻车了。第三,Sum / 4是整数除法,如果你的采样值本身带小数,要把Sum和Ave都改成REAL,并且注意REAL在部分低端 CPU 上运算周期更长。
参数上最常改的是缓冲深度。把ARRAY[0..3]改成ARRAY[0..7],同时把WIdx > 3改成WIdx > 7、除数改成 8,三处必须同步改。漏改一处的结果是平均值恒偏小,而且不报错,这是纯逻辑错误,编译器和手册都救不了你。
2.3 支持边界:型号、版本、任务类型三道门槛
手册开篇就把适用范围写死了:CX-Programmer 6.1 版本、CP1H 全系,以及 3.0 版本以上的 CS1-H、CJ1-H、CJ1M。这三条里最容易踩的是版本——2.0 版及以下的 CS/CJ 系列 CPU 根本不支持功能块,程序下载进去之后,所有实例会被当成非法指令,用户程序既不能编辑也不能执行。
第二道门槛是任务类型。含功能块的程序可以正常上传和下载,但含功能块的任务无法下载到任务单元中,上传是允许的。这个不对称非常坑:你在线上能读出来,改完再写回去就失败,现场如果没带备份,只能现场重写。我的习惯是任务单元里的逻辑一律不用功能块,改用普通子程序,牺牲一点封装性换下载的确定性。
第三道门槛是 CPU 单元内部的功能块规格,手册 2-7 节按型号列了可用实例数、程序步骤数上限。实例不是免费的,每个实例都要吃掉一块内部变量区,规格表里那列数字是硬上限,超了编译直接拦。2-8 节还给了功能块程序步骤数和实例执行时间,做周期敏感的项目时要拿这张表估一下扫描时间增量,别等上线才发现周期从 4ms 涨到 9ms。
还有一条容易忽略的限制写在 2-3 节,是一张禁用指令表。跨扫描周期、带跳转性质的控制指令基本都在里面,功能块内部的算法被设计成"每个周期从头执行到尾",任何试图跳出这个模型的东西都会被拦。动手写复杂功能块之前,先把这张表过一遍,比写完再一条条删效率高得多。
3. 在 CX-Programmer 6.1 里做出第一个能下载的功能块
概念理清之后,落到操作。这一章按真实顺序走一遍:变量表怎么填、AT 怎么设、库文件怎么生成、实例怎么调、下载和备份怎么收尾。每一步都给出可对照的字段和判据。
3.1 变量表:五类变量各管什么
功能块的变量表有五个方向,填错方向是新手最常见的翻车点。判据很简单:看这个数据从哪来、到哪去。
| 变量类型 | 数据来源 | 能否被调用方赋值 | 典型用途 |
|---|---|---|---|
| 输入变量 | 调用方传入 | 能 | 采样值、使能位、设定值 |
| 输出变量 | 算法内部产生 | 只能读 | 计算结果、状态标志 |
| 输入输出变量 | 调用方传入并被修改 | 能 | 累计量、需要回写的状态 |
| 内部变量 | 算法内部使用 | 不能 | 缓冲区、中间计算、循环变量 |
| 外部变量 | 直接绑定 I/O 地址 | 不能 | 需要 AT 设定的地址访问 |
输入输出变量在新手眼里容易和输入变量混。区别在于语义:输入变量是"算之前就定好的值",输入输出变量是"算完可能被改、并且要带回给调用方的值"。比如一个累计计数器功能块,PV应该是输入输出变量,因为每个周期它都会被算法加一,调用方还要读它。
内部变量和外部变量的分界线是地址。内部变量不绑定任何 I/O 地址,纯内存;外部变量必须带 AT 设定,直接映射到实际地址。手册里反复强调的一条规则是:分配到基本 I/O 单元、特殊 I/O 单元和 CPU 总线单元的地址,无法注册为全局符号,也就无法被指定为外部变量,这类场景一律用带 AT 设定的内部变量兜住。
注意:功能块算法里的输入变量是在算法执行之前成批赋值的,输出变量是在算法执行之后成批写出的。如果你的逻辑依赖"执行到某一行时输入刚好变化",这个时序假设不成立,必须改用内部变量或外部变量。
3.2 AT 设定与多字操作数的强制规则
AT 设定是功能块里最需要敬畏的一栏。手册里的原话大意是:当指令操作数要指定多个字中的首地址或末地址时,操作数必须用带 AT 设定的变量或外部变量来指定,或者变量的长度必须与指令要处理的数据长度完全一致。三种情况分别对应三种编译结果,下面这张表是我自己整理的对照:
| 场景 | 变量写法 | 编译结果 |
|---|---|---|
| 长度固定,变量长度一致 | ARRAY[0..9] OF INT,指令处理 10 字 | 通过 |
| 长度固定,变量长度不一致 | ARRAY[0..9] OF INT,指令处理 16 字 | 编译报错 |
| 长度由常量指定 | 指令第二操作数为常量 16 | 编译报错 |
| 长度由变量指定 | 指令第二操作数为INT变量 | 不报错,仅警告 |
最后一行是真正危险的地方。编译器不报错,只在输出窗口给一条警告,很多人点掉就忘了。运行时的实际行为是:指令会按变量里给的长度去读写,超出数组元素数量的那部分照样执行,比如数组注册了 10 个元素、指令要处理 16 个字,多出来的 6 个字会被读写。如果这 6 个字正好被别的指令用着,就是一次没有征兆的误动作。
正确写法是用带 AT 设定的内部变量,让地址和长度都显式固定下来:
(* 反例:长度靠变量传入,编译期查不出越界 *) FOR I := 0 TO Len - 1 DO Dst[I] := Src[I]; END_FOR; (* 正例:长度固定,数组元素数与指令处理长度严格一致 *) (* 变量表:Src ARRAY[0..15] OF INT,Dst ARRAY[0..15] OF INT *) FOR I := 0 TO 15 DO Dst[I] := Src[I]; END_FOR;第一段的问题不在语法,在于Len是运行期才知道的值,编译器的静态检查够不着。如果业务上确实需要可变长度,把上限写死在数组定义里,运行时用IF Len > 16 THEN Len := 16;做钳位,宁可少搬几个字,也不要越界。
3.3 从定义到库文件再到实例调用
顺序是这样的:新建功能块定义、选语言(梯形图或 ST)、填变量表、写算法、编译、生成库文件、在主程序里调用。
编译这一步会拦下大部分结构性错误:变量方向填反、数组长度不匹配、用了禁用指令、实例重名。编译通过不等于逻辑正确,但编译不通过一定有问题,先把红字清完再谈仿真。
生成库文件之后,功能块会出现在项目树的库列表里,可以直接拖进梯形图回路。拖动的时候 CX-Programmer 会让你填实例名和输入参数。这里有个细节:输入参数如果是常量数值,必须加前缀,&表示十进制、#表示十六进制,也可以带+、-号。原因在于不加前缀的纯数字会被当成 CIO 区地址解析,你输入20,它理解成 CIO 0020,而不是数值 20。这个坑我在第一次用的时候踩得很实,滤波器的设定值一直是个莫名其妙的地址值。
调用之后的实例长这样,FB_Filter_01是实例名,后面跟的是参数绑定:
(* 主程序中的实例调用,此处以 ST 形式示意参数绑定关系 *) (* 输入参数:In 绑定 D100,Rst 绑定 W0.00 *) (* 输出参数:Ave 绑定 D200 *) FB_Filter_01(In := D100, Rst := W0.00, Ave => D200);:=是输入方向,=>是输出方向,这个符号在欧姆龙 ST 里是固定的,写反了编译不过。变量名必须和功能块定义里的完全一致,大小写敏感度按项目设置走,我一般要求全大写,省得跨项目迁移时出岔子。
3.4 下载、在线编辑与 BKUP 灯
下载本身没有特殊性,含功能块的程序和普通程序走同一个通道。真正需要盯的是在线编辑之后的备份动作。
CS1-H、CJ1-H、CJ1M、CP1H 这几类 CPU 在执行了在线编辑之后,会把用户程序和参数区数据备份到内置闪存,备份期间 CPU 单元前面的 BKUP 灯亮。这段时间里断电,数据就不备份了,下次上电跑的还是旧程序。现场调试最容易出的事故就是在这一刻拉了总闸,血泪经验是:看到 BKUP 灯亮,先等它灭,再动任何开关。
备份状态可以在 CX-Programmer 里查,PLC 属性里打开存储器备份状态显示,查看菜单里调出 PLC 存储器备份状态窗口。这个窗口我一般会在调试开始时一直开着,比事后猜有没有备份成功靠得住。
在线编辑还有一条副作用:周期时间会延长。手册的安全注意事项里明确要求,只有在确认周期延长不会造成不良影响之后才能做在线编辑,否则输入信号可能读不到。做高速计数或者短脉冲捕捉的项目,在线编辑前先把周期时间监控打开,涨超过 20% 就别在production时段动。
4. 数组和多字操作数:编译期报错与运行期越界
这一章单独拎出来讲,是因为功能块里绝大多数"莫名其妙"的故障都出在这里。编译能过的写法不一定安全,编译不过的写法反而可能是好事。
4.1 首字末字为什么强制 AT
欧姆龙的指令体系里,很多指令的操作数是"首地址+长度"的形式,比如块传送、块比较、字符串处理。功能块的变量表如果不带 AT,变量在内存里的位置由编译器分配,你不控制它前后是什么。一旦指令按长度往后读,读到的就是别人家的数据。
所以手册的规则是:由操作数指定多个字中的第一个或最后一个时,这个操作数无法用普通输入变量充当,必须用带 AT 设定的内部变量,或者用外部变量。手册还补了一句更细的:可以在内部数组变量中交替地指定第一个或最后一个元素,意思是数组元素本身可以作为首地址的载体,前提是数组长度和指令长度对得上。
这条规则的实践版本是:凡是涉及多字操作的,变量表里一律显式写 AT,地址自己排,排完之后在注释里标明这段地址归谁用,避免两个功能块抢同一片区域。
4.2 长度不固定时的静默越界
前面表格里那行"长度由变量指定,不报错只警告",值得单独展开。手册举的例子很具体:指令操作数长度为 16,实际变量表里注册的元素数是 10,指令会对超出元素数的那部分执行读写,也就是对注册元素之后的 6 个字做读或写。如果这 6 个字还被其它指令用着,就会发生意外动作。
这类问题的排查成本极高,因为现象是"偶发误动作",跟输入信号、扫描周期、甚至环境温度都看不出关联。我的做法是在功能块定义阶段就把所有数组长度写成常量,宁可多定义几个不同长度的功能块,也不做"万能长度"的通用块。长度可变的通用块看着优雅,实际是把编译期能查出的问题推到了运行期,这笔账不划算。
还有一条相关的注意事项:如果输入变量数据不是布尔格式,输入参数里只写了数值(比如20),CIO 区地址中的实际值会被跳过。这句话和前面说的前缀问题是同一件事的两面,绕不开。
4.3 输入变量的成批传递时序
功能块的执行模型是:算法开始之前,所有输入参数的值成批传到输入变量;算法执行完之后,输出变量的值成批写回参数。中间过程不传递。
这意味着两件事。第一,算法内部读输入变量,读到的永远是本周期开始时的快照,不会中途变化。第二,如果某个输入值需要在算法执行到一半时被修改,并且后续指令要用新值,只能改用内部变量或外部变量。
手册给的建议是:需要在算法执行指令的过程中让数值经过参数到达输入变量时,用内部变量或外部变量代替输入变量。从输出变量写回参数也是同样的时序。这条规则在单点逻辑里几乎无感,一旦写带中间状态的状态机,就会变成"为什么我这个分支的判断用的还是旧值"的经典疑问。
提示:写状态机时,把所有中间状态放内部变量,输入变量只在每个周期开头读一次并立刻转存到内部变量,后面的逻辑全部基于内部变量跑,能省掉一大类时序问题。
5. 避坑与排查:五条现场记录
下面五条都是我在实际项目里遇到过、并且能在手册里找到对应条款的问题。每条按现象、原因、解决三段写,能对号入座就直接用。
5.1 下载成功但实例不执行
现象:程序下载完成,编译无报错,监控里实例的输入有值、输出恒为 0,功能块内部变量一动不动。
原因:目标 CPU 是 2.0 版及以下的 CS/CJ 系列,不支持功能块。程序能下进去,但所有实例被当作非法指令,既不编辑也不执行。手册在应用注意事项里写得很明确,只是这条藏在正文中间,容易漏。
解决:先确认 CPU 单元型号和版本,对照手册开头的适用范围。不支持就只能改回普通子程序实现,或者换 CPU。已经下进去的非法程序要重新下载一份不含功能块的版本清掉,否则后续在线编辑会一直报奇怪的结构错误。
5.2 常量参数变成了地址
现象:功能块调用时输入参数填了20,运行结果完全不对,监控发现该参数读到的值是一个跟预期无关的数字。
原因:不加前缀的纯数字被解析成 CIO 区地址,20被理解成 CIO 0020 的当前值,而不是常数 20。
解决:常量一律加前缀,十进制写&20,十六进制写#0014。这条规则对手册里所有输入参数都成立,包括定时器的设定值、比较指令的比较值。
5.3 多字指令读写到了隔壁变量
现象:一个块传送相关的功能块运行一段时间后,另一个完全无关的功能块的内部变量值被改了,改的值还正好是本周期搬过来的数据。
原因:数组长度和指令处理长度不一致,或者长度由变量指定导致编译器没拦住,指令越界读写到了相邻区域。
解决:把数组长度和指令长度对齐,长度写成常量。已经上线的项目如果改不动结构,至少要在变量表里给相关数组前后各留一段空的填充变量当缓冲区,用空间换安全。
5.4 在线编辑后断电,程序回到旧版本
现象:在线改了一段逻辑,监控确认生效,第二天早上上电发现改的东西没了。
原因:在线编辑后 CPU 会把程序备份到内置闪存,备份期间 BKUP 灯亮,这段时间断电,备份没完成,上电加载的还是旧程序。
解决:养成看 BKUP 灯的习惯,灯亮期间不动电源。调试前打开 PLC 存储器备份状态窗口,确认备份完成再收工。另外在线编辑会延长周期时间,改完顺手看一眼周期监控,涨太多要评估。
5.5 任务单元写不回去
现象:从任务单元上传程序正常,改完之后下载报错,提示任务单元不支持。
原因:含功能块的任务无法下载到任务单元中,上传是允许的。这个不对称是设计如此,不是故障。
解决:任务单元里的逻辑不用功能块,改用普通子程序。如果已经用了,把功能块展开成等效的梯形图或子程序再下载。项目规划阶段就把这条定下来,比后期返工省事。
6. 把手册当查表工具:三张索引表和一条核对习惯
这份手册七百多页不可能通读,我的用法是把它拆成三张索引表,遇到问题直接定位页码,而不是从头翻。
第一张是型号支持表,放在手册开头和 2-7 节。左边写你手上的 CPU 型号,右边写三件事:是否支持功能块、可用实例数上限、程序步骤数上限。这张表我一般抄在项目笔记第一页,选型阶段就要用。CJ1M 和 CP1H 的实例上限不一样,同一个程序从 CJ1M 迁到 CP1H 可能要重新分配实例。
第二张是报错到条款的映射表,靠编译器和手册对照积累:
| 编译/运行现象 | 对应条款位置 | 处置动作 |
|---|---|---|
| 编译报错,提示操作数长度不一致 | 2-5 节操作数注意事项 | 对齐数组长度与指令长度 |
| 编译报错,提示变量方向错误 | 1-3 节变量定义 | 检查输入/输出/内部方向 |
| 编译通过但仅告警 | 2-5 节长度由变量指定 | 立即改为常量长度,别放过 |
| 实例不执行 | 应用注意事项,CPU 版本 | 核对 CPU 版本是否 3.0 以上 |
| 下载到任务单元失败 | 应用注意事项,任务单元 | 改用普通子程序 |
第三张是 ST 语法速查,对应附录 B。不需要背,写的时候把CASE、FOR、REPEAT三段的语法结构抄在便签上贴在显示器边,一个月就记住了。附录 A 的数据类型表同理,重点是INT和DINT的边界、REAL在哪些 CPU 上可用、BCD 类型和二进制类型的转换关系。
真正让我少踩坑的是最后这条习惯:每次新建功能块,先把变量表全部填完、AT 全部标好、注释全部写完,再动手写算法。听起来很笨,但变量表是功能块的骨架,骨架歪了后面全是补丁。我早年习惯边写算法边加变量,结果一个功能块改到第三版的时候,变量表里躺着七个没用的内部变量、两个方向填错的输入变量,编译能过,仿真也对,上线第二周才发现某个内部变量和相邻功能块的 AT 区域重叠,排查花了整整两天。
从那以后我每次新建功能块,都强制走一遍"变量表填满 → AT 排布画在纸上 → 长度全部写成常量 → 注释补齐 → 再写第一行算法"的流程,多花二十分钟,省下的是现场两天的返工。这份手册的价值不在于读一遍,而在于把它当成一张随时能翻的规则表,遇到报错先查表再动手,别靠猜。希望帮到你。
本文还有配套的精品资源,点击获取