3. Codesys基础回顾
在深入Codesys开源库应用与自定义库封装之前,必须夯实IEC 61131-3编程语言的基础。Codesys作为全球领先的软PLC开发环境,其核心引擎完全遵循IEC 61131-3标准,支持五种标准编程语言:结构化文本(ST)、功能块图(FBD)、梯形图(LD)、顺序功能图(SFC)和指令表(IL)。这五种语言并非孤立存在,而是可以在同一个工程中混合使用,通过POU(程序组织单元)进行模块化封装。本章将系统回顾这五种语言的核心语法、适用场景以及Codesys工程组织的逻辑结构,为后续的库开发与封装建立坚实的语法与架构基础。
上图展示了IEC 61131-3编程模型的核心架构。五种语言围绕同一个控制逻辑核心,通过Codesys工程组织进行统一调度。理解每种语言的语法特征和最佳应用场景,是后续开发高质量库文件的前提。下面我们将逐一深入剖析。
3.1 结构化文本(ST):高级算法与复杂逻辑的首选
核心原理
ST语言是一种类似Pascal的高级编程语言,它支持复杂的数据类型、循环、条件分支、函数调用等结构化编程特性。ST语言在Codesys中是最灵活、功能最强大的语言,特别适合实现复杂的数学运算、数据处理、通信协议解析以及状态机逻辑。其编译器优化程度高,执行效率接近C语言。
核心语法要素
| 语法类别 | 关键字/示例 | 说明 |
|---|---|---|
| 赋值语句 | A := B + 1; | 使用 := 进行赋值,语句以分号结束 |
| 条件分支 | IF...THEN...ELSIF...ELSE...END_IF; | 支持多条件嵌套判断 |
| 选择分支 | CASE...OF...ELSE...END_CASE; | 适合多值离散判断,比IF更清晰 |
| 循环语句 | FOR...DO...END_FOR;WHILE...DO...END_WHILE; | 支持定长和不定长循环 |
| 函数调用 | Result := F_Calc(Param1, Param2); | 调用自定义函数或库函数 |
| 注释 | (* 注释内容 *) | 支持多行注释 |
实战场景:温度PID控制算法实现
在工业温度控制场景中,我们通常使用ST语言编写PID算法。以下是一个典型的ST代码片段:
PROGRAM PRG_TempControl
VAR
rSetPoint : REAL := 100.0; (* 目标温度 *)
rPV : REAL; (* 过程变量 *)
rError : REAL;
rIntegral : REAL;
rDerivative : REAL;
rOutput : REAL;
rKp : REAL := 2.5;
rKi : REAL := 0.8;
rKd : REAL := 0.1;
rPrevError : REAL;
rSampleTime : TIME := T#100MS;
END_VAR
(* 主控制循环 *)
rError := rSetPoint - rPV;
rIntegral := rIntegral + rError * TIME_TO_REAL(rSampleTime);
rDerivative := (rError - rPrevError) / TIME_TO_REAL(rSampleTime);
rOutput := rKp * rError + rKi * rIntegral + rKd * rDerivative;
rPrevError := rError;
(* 输出限幅 *)
IF rOutput > 100.0 THEN
rOutput := 100.0;
ELSIF rOutput < 0.0 THEN
rOutput := 0.0;
END_IF;
TIME_TO_REAL() 转换函数是必须的,因为 TIME 类型不能直接与 REAL 进行算术运算。此外,积分饱和问题需要通过输出限幅和积分清零逻辑来处理。
常见误区
- 滥用全局变量:在ST中直接访问全局变量会导致程序耦合度高,难以维护和复用。应通过功能块的输入/输出接口传递数据。
- 忽略初始化:ST中的变量默认初始化为零值,但自定义复杂结构体或数组时,必须显式初始化,否则可能产生未定义行为。
- 循环陷阱:在
WHILE循环中忘记更新退出条件,会导致PLC看门狗超时,触发系统停机。
3.2 功能块图(FBD)与梯形图(LD):逻辑控制的双璧
核心原理
FBD和LD都是图形化编程语言,它们通过连线表示数据流和逻辑流。FBD侧重于信号处理和数据流,适合模拟量处理和复杂功能块组合;LD则源自继电器控制电路,适合数字量逻辑控制,如电机启停、互锁保护等。在Codesys中,这两种语言可以互相转换,且都支持调用ST编写的功能块。
操作流程:LD编程的关键步骤
- 定义变量:在变量表中声明输入(如启动按钮
bStart)、输出(如接触器bMotor)和中间变量。 - 放置触点/线圈:在LD编辑器中,使用常开触点(
--| |--)和线圈(--( )--)构建逻辑。 - 添加功能块:拖入定时器TON、计数器CTU等标准功能块,并连接输入输出。
- 编译检查:使用Codesys的编译功能检查连线错误和类型不匹配。
案例拆解:电机星三角启动控制
在电机控制柜中,星三角启动是常见的降压启动方式。使用LD语言实现时,需要三个接触器(主接触器KM1、星接触器KM2、角接触器KM3)和一个定时器T1。逻辑要求:启动时KM1和KM2吸合,延时5秒后KM2断开、KM3吸合。同时必须保证KM2和KM3不能同时吸合,否则短路。
| 步骤 | 动作 | LD实现方式 |
|---|---|---|
| 1 | 按下启动按钮 | 常开触点 bStart 闭合 |
| 2 | KM1吸合并自锁 | KM1线圈并联自身常开触点 |
| 3 | KM2吸合,定时器启动 | KM2线圈串联KM3常闭触点,TON定时器启动 |
| 4 | 5秒后切换 | TON输出Q置位,断开KM2,吸合KM3 |
| 5 | 停止 | 停止按钮 bStop 常闭触点断开所有线圈 |
常见误区
- 线圈重复输出:同一个线圈在LD中只能输出一次,否则编译报错。若需多处控制,应使用中间变量或置位/复位指令。
- 忽略扫描周期:LD程序按从左到右、从上到下的顺序扫描。如果逻辑依赖扫描顺序,可能导致意想不到的结果。建议使用显式的边沿检测指令(R_TRIG/F_TRIG)。
3.3 顺序功能图(SFC)与指令表(IL):流程控制与底层操作
核心原理
SFC是一种描述顺序控制过程的图形化语言,它将复杂的控制流程分解为多个步骤(Step)和转换条件(Transition)。SFC非常适合批处理、流水线控制等具有明确状态顺序的应用。IL则是一种类似汇编的低级语言,使用累加器(ACCU)进行操作,目前在工程中已较少使用,但在某些遗留系统或性能极敏感的场合仍有价值。
SFC核心结构
- 步骤(Step):包含一组动作(Action),可以是ST代码或LD逻辑。
- 转换(Transition):定义从一个步骤切换到下一个步骤的条件,通常是一个布尔表达式。
- 分支与合并:支持选择分支(IF...ELSIF)和并行分支(AND),用于处理多路径流程。
实战场景:自动化灌装线流程
一个典型的灌装线包含以下步骤:待机 → 空瓶到位 → 灌装 → 封盖 → 送出。使用SFC可以清晰表达:
步骤 S0: 待机
动作: 所有执行器关闭
转换 T0: 启动按钮按下 AND 空瓶到位传感器 ON
步骤 S1: 灌装
动作: 打开灌装阀,启动灌装泵
转换 T1: 液位传感器达到高限
步骤 S2: 封盖
动作: 关闭灌装阀,启动封盖电机
转换 T2: 封盖完成传感器 ON
步骤 S3: 送出
动作: 启动传送带电机
转换 T3: 送出传感器 ON → 返回 S0
IL语言简述
IL语言使用类似 LD(加载)、ST(存储)、ADD(加法)等指令操作累加器。例如:
LD Var1
ADD Var2
ST Result
这段代码将Var1加载到累加器,加上Var2,然后将结果存储到Result。IL语言在Codesys中仍受支持,但官方推荐优先使用ST或FBD。
常见误区
- SFC死锁:如果转换条件永远不满足,流程会卡死在当前步骤。必须设计超时或强制跳转逻辑。
- IL可读性差:IL代码难以调试和维护,除非有特殊需求,否则不建议在新项目中使用。
3.4 Codesys工程组织:从任务到库的架构思维
核心原理
Codesys的工程组织遵循分层架构:Project(工程)→ Application(应用)→ POU(程序组织单元)→ Task(任务)。POU是代码复用的基本单元,分为三种类型:程序(PRG)、功能块(FB)和函数(FUN)。PRG由任务调度执行,FB和FUN则被PRG或其他FB调用。
工程组织最佳实践
| 层级 | 内容 | 管理要点 |
|---|---|---|
| Project | 包含设备配置、网络通信、全局变量 | 使用版本控制(如SVN/Git)管理工程文件 |
| Application | 独立的运行环境,可包含多个任务 | 每个应用有独立的变量命名空间 |
| POU | PRG/FB/FUN | FB应封装独立功能,FUN不应有静态变量 |
| Task | 定义执行周期和优先级 | 将实时性要求高的任务设为高优先级 |
操作流程:创建自定义功能块并封装为库
- 创建FB:在工程中新建功能块,定义输入/输出变量和内部变量。
- 编写逻辑:使用ST或FBD实现功能。
- 测试验证:在本地工程中编写测试PRG,模拟输入并监控输出。
- 封装库:右键点击FB,选择“保存为库元素”,设置版本号和文档说明。
- 生成库文件:通过“库管理器”导出为
.library文件。
案例拆解:将PID控制器封装为可复用库
假设我们已经编写了一个PID功能块 FB_PID,现在要将其封装为库供多个项目使用。首先,检查FB的接口:输入为 rSetPoint、rPV、rKp、rKi、rKd,输出为 rOutput。内部变量全部声明为VAR(非VAR_GLOBAL)。然后,在库管理器中创建新库,将FB拖入,填写版本号1.0.0.0,添加帮助文档。最后,编译生成 PID_Lib.library 文件。在其他工程中,只需通过“库管理器”安装此文件,即可像使用标准库一样调用 FB_PID。
常见误区与复盘
- 任务周期设置不当:将PID控制放在周期为1秒的任务中,会导致控制效果极差。应根据被控对象的响应速度设置任务周期(如10ms-100ms)。
- 库版本混乱:库更新后未同步更新版本号,导致不同项目使用不同版本的库,排查问题时难以定位。
- 忽略库的依赖关系:如果库A依赖库B,封装时必须声明依赖关系,否则在目标工程中加载会报错。