简介本资源是面向工业自动化工程师、PLC编程初学者及控制系统开发人员的《ST语言编程手册》完整版PDF系统讲解IEC 61131-3标准下的Structured Text结构化文本编程核心知识解决PLC逻辑实现复杂、语法理解零散、数据类型应用不熟等典型痛点。手册涵盖ST基本原理语法图、语义规则、字符与标识符规范、数字/布尔/字符串处理、源文件结构语句与注释、四大类数据类型基本型、用户自定义STRUCT/ENUM/ARRAY、技术目标型、系统型及变量声明与赋值表达式等关键内容目录层级清晰、示例详实适合作为案头工具书或系统入门教材。资源为单个4.22MB PDF文件内容完整无删减已获4414人学习下载读者可直接获取权威语法体系、标准化编程范式与可复用的数据结构设计方法快速提升工业控制程序的可读性、健壮性与工程化水平。1. ST语言不是“高级Python”它专为PLC控制逻辑而生写错一行就停机但写对了能扛住产线7×24小时连续运行你手头这份《(完整版)ST语言编程手册》不是教你怎么写更优雅的算法而是教你如何在毫秒级响应、零容错、无垃圾回收、内存固定分配的工业控制器里把“按下启动按钮→电机正转→温度超限自动停机→故障灯闪烁”这一串物理动作翻译成CPU能咬住不放、永不崩溃的确定性指令。STStructured Text是IEC 61131-3五大标准编程语言中最接近高级语言的一种但它骨子里仍是硬实时控制语言——没有try-catch没有动态内存申请变量声明即绑定物理地址数组越界不会抛异常只会静默覆盖隔壁寄存器导致气缸突然伸出或安全门意外解锁。我曾在某汽车焊装线调试中因一个未初始化的BOOL数组索引在凌晨三点整条产线急停排查三小时才发现是ST里FOR i : 0 TO 9 DO循环中i被意外赋值为-1触发了隐式类型截断。这本手册的价值不在于语法有多像C而在于它告诉你哪些语法糖能用、哪些看似合法的写法在PLC扫描周期里会变成黑匣子、哪些注释位置会影响编译器优化路径。适合正在啃国产PLC开发文档的现场工程师、刚从MATLAB/Simulink转向实际产线部署的控制算法工程师以及需要把仿真模型落地为可交付ST代码的系统集成商。2. 从空白工程到第一个可下载程序用Codesys V3.5完成ST最小闭环验证2.1 创建符合IEC 61131-3标准的ST项目结构Codesys是当前最主流的开源PLC开发环境支持多厂商硬件其项目结构严格遵循IEC 61131-3标准。新建项目时必须包含以下四个核心元素缺一不可设备Device选择目标PLC硬件型号如Beckhoff CX5140、倍福CX5140、汇川H3U等决定可用资源IO点数、任务周期、内存大小配置Configuration定义通信总线EtherCAT/Profinet、IO模块映射、任务调度策略资源Resource对应PLC的CPU核心每个资源可挂载多个任务Task任务Task真正执行ST代码的调度单元需设置执行周期如10ms、优先级、触发方式周期/事件。提示新手常误将全部逻辑写进MAIN程序组织单元POU却不配置任务——代码编译通过但永远不会被执行。任务是ST代码的“发动机”POU只是“燃料配方”。创建步骤以Codesys V3.5.18.20为例# 1. File → New Project → Standard project (IEC 61131-3) # 2. Device: 选择 Beckhoff CX5140模拟环境用 # 3. Configuration: 默认即可含EtherCAT主站 # 4. Resource: 默认 Resource1 # 5. 在 Resources → Resource1 → Tasks 下右键 → Add Task # - Name: MainTask # - Interval: 10 ms # - Priority: 10数值越小优先级越高 # 6. 右键 MainTask → Add Object → Program → Name: MAIN此时生成的MAIN是一个空的ST程序组织单元位于PLC_PRG命名空间下等待你填入第一行可执行逻辑。2.2 写出能点亮LED的ST代码从变量声明到循环扫描PLC程序本质是无限循环扫描读输入→执行用户逻辑→写输出→等待下一个周期。ST代码必须嵌入这个框架。以下是最小可运行示例实现“启动按钮按下时输出Q0.0置位并保持松开后延时2秒复位”// 文件MAIN.st位于 PLC_PRG 下 PROGRAM MAIN VAR // 输入映射需在设备配置中关联物理端口 StartBtn : BOOL; // %I0.0启动按钮常开触点 // 输出映射 MotorRun : BOOL; // %Q0.0电机运行指示灯 // 内部变量非保持型 Timer1 : TON; // IEC标准定时器功能块 bStart : BOOL : FALSE;// 启动标志初始值必须显式声明 END_VAR // 主逻辑边沿检测 定时器调用 bStart : StartBtn AND NOT bStart; // 上升沿检测防抖关键 IF bStart THEN MotorRun : TRUE; ELSIF Timer1.Q THEN MotorRun : FALSE; END_IF; // 定时器调用必须放在逻辑块末尾且每次扫描都调用 Timer1(IN : NOT MotorRun, PT : T#2S);关键参数说明与逻辑拆解TON是IEC 61131-3标准定时器功能块IN为使能输入PT为预设时间T#2S 2秒Q为输出计时完成为TRUEbStart : StartBtn AND NOT bStart是经典上升沿检测写法依赖变量在上一周期的值因此bStart必须声明初始值: FALSE否则首次扫描结果不可预测Timer1(...)必须在每次扫描中无条件调用否则定时器内部状态无法更新——这是ST与普通高级语言最根本差异功能块不是函数调用而是状态机实例所有变量名前缀%I/%Q为Codesys自动映射的物理地址标识实际部署时需在设备配置中将StartBtn绑定到具体输入端口如LocalBus.EtherCAT.Device1.Inputs.StartButton。编译成功后点击Online → Login连接模拟PLC再点击Download下载程序。此时在Online → Observe Variables中添加StartBtn和MotorRun手动修改StartBtn值为TRUE再FALSE可观察到MotorRun先变TRUE2秒后自动变FALSE——最小闭环验证完成。3. ST语言三大“玄学陷阱”为什么编译通过却行为诡异3.1 隐式类型转换BOOL参与算术运算时的静默截断现象某温度控制逻辑中IF (TempActual TempSetPoint) THEN CoolValve : TRUE; END_IF;正常工作但加入调试变量后DebugVar : TempActual - TempSetPoint;导致冷却阀失控常开。原因DebugVar被声明为INT而TempActual和TempSetPoint为REAL。ST中REAL - REAL结果仍为REAL但赋值给INT时发生隐式截断非四舍五入且无编译警告。更致命的是当TempActual TempSetPoint时差值为负REAL截断为负INT而后续某处代码误将DebugVar当作无符号数使用如ARRAY[0..10] OF INT索引负值被解释为极大正数越界访问数组。解决所有跨类型赋值必须显式转换DebugVar : INT(ROUND(TempActual - TempSetPoint));在Codesys中启用Project → Options → Compiler → Warnings → Enable all warnings并勾选Warning for implicit type conversion对关键控制变量如阀门状态、电机使能禁用INT/REAL/BOOL混用统一用BOOL或带范围检查的USINT。3.2 数组索引越界不报错但覆盖相邻变量现象某包装机计数逻辑中CounterArray : ARRAY[0..9] OF UINT;用于记录10个工位产量。当Index : 15; CounterArray[Index] : CounterArray[Index] 1;执行后MotorEnable变量紧邻数组声明之后意外变为FALSE导致主电机停机。原因ST中数组不进行运行时边界检查。CounterArray[15]实际访问的是CounterArray起始地址15×2字节UINT2字节处的内存恰好覆盖了下一个变量MotorEnableBOOL占1字节但按字节对齐可能占1或2字节。该问题在不同PLC厂商编译器中表现不一有的填充padding有的紧凑排列导致同一份代码在A品牌PLC正常在B品牌PLC崩溃。解决声明数组时预留安全余量CounterArray : ARRAY[0..15] OF UINT;即使只用0~9所有数组访问前强制校验IF Index 0 AND Index 9 THEN CounterArray[Index] : CounterArray[Index] 1; ELSE // 写入诊断寄存器或触发报警 DiagCode : 101; // 数组索引越界 END_IF;在Codesys中启用Memory layout viewView → Memory Layout查看变量实际内存地址确认关键数组前后无敏感变量。3.3 功能块实例重用同一个FB被多个POU调用时的状态污染现象MAIN程序中调用PID_Controller(FB_PID)控制温度另一独立程序SECURITY中也调用同一个FB_PID实例控制安全阀。当温度PID正在积分饱和时安全阀突然全开且无法关闭。原因FB_PID是一个功能块Function Block其内部变量如IntegralTerm,LastError属于实例状态。若两个POU共用同一个实例名如都写PID1(...))则它们共享同一套内部状态。温度环的积分项被错误地应用到了安全阀控制上。解决每个逻辑上下文必须使用独立实例// MAIN.st 中 TempPID : FB_PID; // 实例1 TempPID(...); // SECURITY.st 中 SafetyPID : FB_PID; // 实例2完全独立 SafetyPID(...);禁止在全局变量区GVL声明功能块类型变量如GVL.PIDInstance : FB_PID;因其会被所有POU共享对于需跨POU传递的计算结果使用VAR_IN_OUT接口或全局结构体GVL中声明STRUCT内含REAL/BOOL等基础类型而非功能块实例。4. 把手册变成生产力ST代码自动生成与跨平台适配实战4.1 用Python脚本批量生成标准化ST模块以Modbus RTU从站寄存器映射为例工业现场常需为数十个传感器/执行器生成一致的ST读写逻辑。手动编写易出错且维护困难。我们采用“配置表驱动”方式用Excel定义寄存器地址、数据类型、缩写名Python脚本生成ST代码。配置表modbus_config.xlsx结构RegisterAddrDataTypeVarNameDescriptionScale40001UINTTankLevel水箱液位0-100%1.040002INTPumpSpeed水泵转速rpm0.140003BOOLValveOpen出水阀开关状态—生成脚本gen_modbus_st.py核心逻辑import pandas as pd def generate_st_code(config_file: str) - str: df pd.read_excel(config_file) st_lines [] # 1. 生成变量声明 st_lines.append(VAR_GLOBAL) for _, row in df.iterrows(): dtype_map {UINT: USINT, INT: INT, BOOL: BOOL} st_lines.append(f {row[VarName]} : {dtype_map[row[DataType]]};) st_lines.append(END_VAR\n) # 2. 生成Modbus读取逻辑假设已存在ModbusRead函数 st_lines.append(PROGRAM MAIN) st_lines.append(VAR) st_lines.append( ModbusData : ARRAY[0..999] OF BYTE; // 假设Modbus缓冲区) st_lines.append(END_VAR\n) # 3. 生成寄存器解析逻辑 for _, row in df.iterrows(): addr int(row[RegisterAddr]) - 40001 # 转换为数组索引 if row[DataType] UINT: st_lines.append(f {row[VarName]} : WORD_TO_USINT( (ModbusData[{addr*2}] * 256) ModbusData[{addr*21}] );) elif row[DataType] INT: st_lines.append(f {row[VarName]} : BYTE_TO_INT( (ModbusData[{addr*2}] * 256) ModbusData[{addr*21}] );) elif row[DataType] BOOL: bit_pos (addr % 8) # 假设BOOL存于字节低位 st_lines.append(f {row[VarName]} : (ModbusData[{addr//8}] AND (16#01 SHL {bit_pos})) 0;) return \n.join(st_lines) # 生成代码 with open(modbus_mapping.st, w, encodingutf-8) as f: f.write(generate_st_code(modbus_config.xlsx))生成效果modbus_mapping.stVAR_GLOBAL TankLevel : USINT; PumpSpeed : INT; ValveOpen : BOOL; END_VAR PROGRAM MAIN VAR ModbusData : ARRAY[0..999] OF BYTE; END_VAR TankLevel : WORD_TO_USINT( (ModbusData[0] * 256) ModbusData[1] ); PumpSpeed : BYTE_TO_INT( (ModbusData[2] * 256) ModbusData[3] ); ValveOpen : (ModbusData[0] AND (16#01 SHL 0)) 0;血泪经验脚本生成的代码必须人工审核三点——① 地址偏移是否正确尤其4xxxx/3xxxx寄存器起始差异② 字节序大端/小端是否匹配设备手册③ BOOL位操作是否与实际硬件接线一致有些设备用高位bit表示第一个BOOL。生成只是起点验证才是关键。4.2 一份ST代码适配三家PLC通过编译器指令实现硬件抽象国产PLC汇川、信捷、禾川底层IO映射规则不同直接写%Q0.0会导致代码无法移植。解决方案用#IFDEF编译器指令封装硬件层。硬件抽象层Hardware_Abstraction.st// 根据编译器宏定义选择IO映射 #IFDEF HUICHUAN #DEFINE INPUT_START_BTN %IX0.0 #DEFINE OUTPUT_MOTOR_RUN %QX0.0 #ELSIF XINJIE #DEFINE INPUT_START_BTN %IX1.0 #DEFINE OUTPUT_MOTOR_RUN %QX1.0 #ELSIF HECHUAN #DEFINE INPUT_START_BTN %IX2.0 #DEFINE OUTPUT_MOTOR_RUN %QX2.0 #ELSE #ERROR Please define target PLC vendor: HUICHUAN, XINJIE or HECHUAN #ENDIF // 全局变量声明统一使用抽象名 VAR_GLOBAL StartBtn : BOOL; MotorRun : BOOL; END_VAR // 硬件映射逻辑在MAIN中调用 PROGRAM MAIN VAR // 无需声明物理地址由宏定义接管 END_VAR // 将物理输入映射到逻辑变量 StartBtn : INPUT_START_BTN; // 将逻辑变量映射到物理输出 OUTPUT_MOTOR_RUN : MotorRun;在Codesys中设置编译宏Project → Options → Compiler → Preprocessor Definitions→ 添加HUICHUAN或XINJIE/HECHUAN。这样业务逻辑层MAIN中除映射外的所有控制算法完全不感知硬件差异更换PLC只需改一个宏定义重新编译即可。我曾用此方法在48小时内完成某灌装线从汇川H3U到信捷XD5E的整机迁移代码复用率92%仅IO映射部分需调整。5. 验证ST代码可靠性的三把尺子静态检查、仿真测试、现场灰度发布5.1 用Codesys Static Analysis做代码健康度扫描Codesys内置静态分析器可发现90%的低级错误。启用路径Tools → Static Analysis → Configure勾选关键规则规则ID检查项严重等级为什么必须开SA001未初始化的变量赋值High防止启动瞬间随机值触发误动作SA007数组索引超出声明范围Critical直接导致内存覆盖安全红线SA012功能块未调用未连接INMedium定时器/PID等不调用不工作逻辑失效SA025浮点数直接等于比较HighREAL精度问题导致条件永远不满足运行分析后双击告警可跳转到问题行。例如SA001会标出Counter : UINT;声明后未赋初值应改为Counter : UINT : 0;。注意静态分析不能替代逻辑验证但它能帮你把“人肉找错”时间从3小时压缩到15分钟。5.2 在PLCsim Advanced中做闭环仿真测试Codesys官方仿真器PLCsim Advanced支持真实PLC固件级仿真可加载.cpx文件Codesys编译产物并连接虚拟HMI/IO设备。仿真流程编译项目生成MyProject.cpx启动PLCsim Advanced →File → Load Project→ 选择.cpx在IO Configuration中添加虚拟输入模块如Digital Input 8ch将StartBtn映射到Channel 0启动仿真Online → Start在Online → Observe Variables中监控MotorRun在仿真器界面点击Channel 0按钮观察MotorRun是否按预期动作进阶用Scripting功能编写自动化测试脚本模拟1000次启停循环验证定时器累积误差。关键技巧在仿真中开启Trace功能Online → Trace → Start可记录每个扫描周期内所有变量值导出CSV后用Python分析时序偏差——这是发现“毫秒级抖动”的唯一可靠方法。5.3 现场灰度发布用“双逻辑并行运行”实现零风险上线新ST程序上线最怕“一上就停机”。某食品厂采用“双逻辑并行”策略旧逻辑V1与新逻辑V2同时运行但仅V1控制物理输出V2输出接入诊断寄存器持续比对两者结果。实施步骤在MAIN中并行调用两套逻辑// 旧逻辑继续控制设备 OldLogic(StartBtn, MotorRun_Out); // 新逻辑仅诊断 NewLogic(StartBtn, MotorRun_Diag);添加一致性校验IF MotorRun_Out MotorRun_Diag THEN DiagCounter : DiagCounter 1; IF DiagCounter 100 THEN // 连续100次不一致 AlarmCode : 999; // 触发高级别报警 END_IF; ELSE DiagCounter : 0; // 清零计数器 END_IF;输出控制仍走旧逻辑%Q0.0 : MotorRun_Out;连续运行72小时DiagCounter始终为0证明V2逻辑完全等效切换控制权%Q0.0 : MotorRun_Diag;旧逻辑降级为备份。这种做法让新代码在真实负载下“热身”充分比单纯仿真更可信。我坚持用这招上线过17个产线升级项目零次非计划停机。最后说句实在话这本《(完整版)ST语言编程手册》真正的价值不在它写了多少语法而在于它逼你养成一种肌肉记忆——看到任何一行ST代码第一反应不是“它能不能编译”而是“它在第几个扫描周期会执行它的变量在内存里占哪几个字节如果网络中断100ms这段逻辑会不会卡死” 当你开始用PLC的思维去思考时间、内存和确定性你就真的入门了。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?