简介:这份《Codesys程序开发指导》是一份面向PLC工程师、自动化技术人员及工业控制初学者的PDF技术文档,系统讲解基于IEC 61131-3标准的Codesys软PLC开发方法。内容覆盖五种标准编程语言(梯形图、功能块图、结构文本、顺序功能图、指令列表)的特点与选型,并深入介绍软PLC控制方案、Codesys实时核、软件安装流程、通信协议编程及实际控制系统实例,适合希望快速掌握Codesys项目开发与调试技能的读者。资源为单个PDF文件,压缩包大小12.6MB,便于下载后离线阅读或按章节查阅。目前已有160人学习,内容结构清晰,从标准概览到工程实现层层递进,兼具理论说明与实例引导,可作为入门自学和日常开发的参考资料。
1. 从硬PLC迁到CoDeSys:一个能写五种编程语言的软PLC环境
如果你和我一样,被某输送线设备的一套老旧PLC程序困住过——原厂软件绑定特定硬件、换一个品牌就得重学一套IDE、出差调试还得扛着加密狗——那看到CoDeSys(一般写作Codesys)时会有种“这账终于能算了”的感觉。它不是一个具体的PLC型号,而是一套符合IEC 61131-3标准的软PLC开发环境,一个工程里能混用梯形图、功能块图、结构化文本、顺序功能图、指令表五种编程语言,代码在硬件层之上抽象出来,后续换控制器平台时,应用层程序可以整体搬走。网络上关于它通信组态的资料多而散,这本开发指导最值钱的部分,是把“编程语言选择、任务周期配置、网络变量与协议通信”揉进同一套流程里讲,适合刚转软PLC的电气工程师,也适合需要把控制系统做成产品化项目的一线开发者。
2. 先理解IEC 61131-3的软件模型:设备、应用、任务、库之间怎么协作
CoDeSys的编程入口不是“写代码”,而是“搭软件模型”。目录里从标准概述直接跳到软件结构,说明这层模型决定了你后面所有程序组织方式。新手最常见的误解是拿它当普通IDE用,丢一堆POU进去就编译,结果在线监视变量时发现任务根本没执行。
2.1 五种编程语言怎么选:不是哪个好用,而是哪个场景该用哪个
IEC 61131-3把梯形图(LD)、功能块图(FBD)、结构化文本(ST)、顺序功能图(SFC)、指令表(IL)定为标准语言,后来又加入CFC连续功能图。这本书把这五种语言的特性和适用场景分开写,核心逻辑是这样:LD适合讲继电器回路的本质,电气出身的人一眼能懂;FBD适合做信号处理和闭环逻辑,把功能块拖出来、连线,比写IF语句直观得多;ST是高级语言,适合复杂数学运算、数组处理、协议解析这类“写句子”的场景;SFC天生是流程控制语言,适合按步骤走的工序,比如分拣、包装、加热炉段控;IL接近汇编,日常做项目基本不碰它,只有在需要极底层操作时才有意义。
我在实际项目里的分法是:设备启停、互锁、急停链路用LD,因为这个逻辑要从电气图纸对应过来,维护的人看着放心;模拟量处理、PID输出、数据计算用ST,写成函数方便单元测试;整条产线的工序动作用SFC编排,每一步的进入条件和退出条件一目了然。选语言不是看个人喜好,而是看你希望后续谁来维护这段逻辑,以及故障时现场人员能不能快速定位。资料里还有一个细节:同一段逻辑可以混合着写,LD里调用ST写的函数块,ST里也能实例化LD做的功能块,这个我在后面实战章里展开。
2.2 设备与应用:区分“硬件描述”和“逻辑描述”两个层面
CoDeSys工程左侧有两个容易混淆的节点:设备(Device)和应用(Application)。设备节点描述的是物理控制器、总线接口、远程IO站,它对应硬件配置;应用节点才是你真正写逻辑的地方,对应IEC 61131-3里的“程序组织单元+任务+全局变量”组合。先加设备还是先建应用,直接影响导入导出时的内容边界。我碰到过有人把Modbus TCP从站配置错加到PLC应用下面,结果网关变量全部失效,一查才发现设备树和应用树彻底乱了。
设备树里的每个子节点是一个“驱动”或“协议栈”,比如某个现场总线主站、某个串口模块。应用节点下面才是任务、全局变量、POU、配方、采样跟踪这些对象。这套结构的价值在于:硬件变动时(比如换了一个品牌的远程IO站),设备树重新映射,应用层的逻辑地址通过绑定变量自动找新通道,不需要改写业务代码。你要养成一个习惯:凡是涉及地址映射的配置,全部停留在设备树层面;应用层只认变量名,不认物理地址。一旦违反了,程序移植性就废了一半。
2.3 任务调度:周期、优先级和看门狗决定了程序会不会“飞”
任务(Task)是CoDeSys里最容易被忽略却最要命的对象。一个应用可以创建多个任务,每个任务有自己的周期和优先级,POU是被分配到任务里执行的,不是编译器自动调用的。资料里把任务单独列出来讲,对应到实际调试中就是“为什么有的程序块只在某个周期执行,有的却卡死”。常见的配置方式有两种:一个轮询任务跑全部逻辑,周期10ms;或者拆成多个任务——高速模拟量处理1ms、主逻辑5ms、人机交互界面刷新100ms。多任务模式下,相同变量在不同任务里读写时要格外小心数据一致性,这个我在避坑章再细说。
任务配置时有三个参数必须理解:周期(Interval),优先级(Priority),看门狗(Watchdog)。周期决定执行频率;优先级决定冲突时谁先跑;看门狗决定任务是否被判定超时。第一次建工程时直接默认参数跑还好,但一旦你加了协议通信或大量字符串处理,任务周期不满足执行时间需求,控制器会直接进Stop状态,现象就是程序跑着跑着突然停住,在线看状态才知道是看门狗超时。建议的排查姿势是:先开采样跟踪看任务的执行时间曲线,确认最长耗时点在哪个POU,再决定是优化代码还是放宽周期,而不是无脑把周期调大。
提示:同一应用下,多个任务对同一个变量做读写时,应保证只有一个任务写、其他任务只读,否则要加临界区保护。
3. 从安装到第一个工程跑通:软PLC的建工程流程和三大关键配置
软PLC方案的优势是把运行时(Runtime)装在通用硬件上,开发环境和目标系统分离。这本指导从安装要求开始讲,说明它对“第一次接触软PLC的人”是有引导设计的。这里梳理一遍我每次建工程都会走的流程,以及中间那些容易卡住的配置点。
3.1 安装时就要想清楚的三个决定
第一,开发环境装在物理机还是虚拟机。装虚拟机能隔离复杂驱动环境,但USB加密锁的透传、现场总线板卡驱动映射会在虚拟机里折腾你半天,我一般建议主力开发机直接物理安装;第二,是否需要实时核授权,如果只是做离线的逻辑仿真、熟悉语言语法,用仿真模式跑就行,但真机上线必须单独处理运行时授权,这部分的激活路径在安装文档里写得很细,照着做即可;第三,安装路径和版本不要混装多个大版本,不同版本生成的工程文件格式有差异,打开旧工程时如果提示工程版本过旧,升版本前先备份。
3.2 建工程的固定步骤:设备、应用、任务、POU的顺序别乱
很多人打开软件后习惯先去写ST代码,写完了发现没有目标设备,编译无误却无法登录。我的固定顺序是这样的:先新建工程并添加标准设备,然后添加应用,再在应用下建任务,最后才写POU。设备可以先选仿真器,后面再切换真实控制器;但任务一定要在建POU前配置好,因为POU需要指定归属哪个任务。
下面是用ST写第一个启停程序的完整过程,假设你已经建好一个空白应用:
// 在应用 -> POUs 下新建POU,语言选ST // 变量声明区 VAR bStart : BOOL; // 启动按钮,接到物理输入 bStop : BOOL; // 停止按钮 bRunning : BOOL; // 运行状态输出 nCount : INT; // 运行计数,用来验证扫描周期 END_VAR // 逻辑主体 bRunning := (bStart OR bRunning) AND NOT bStop; IF bRunning THEN nCount := nCount + 1; // 每次任务扫描加1 ELSE nCount := 0; // 停止时清零 END_IF这段代码的逻辑含义是:用置位优先思想模拟传统启保停回路。bRunning的赋值表达式等价于“启动信号或保持状态,且停止信号要断开”,这是把梯形图回路翻译成ST的典型写法。nCount用来在线监视时验证任务确实在周期执行——如果你在程序里看不到它增长,说明任务没跑起来。参数上,BOOL是布尔开关,INT是16位有符号整数,计数到32767会溢出,实际项目中计数多的话要用DINT。
写完这段后,编译通过就可以在线登录。登录成功后先看任务状态,再看变量监视窗口,手动置位bStart,观察bRunning是否会自保持。这个简单的闭环验证,能帮你确认为什么任务配置没问题、程序能跑。
3.3 下载与启动:分清“登录”“下载”“运行”三步
在线操作里最容易翻车的是把“登录”和“下载”当成一回事。登录只是让你连上控制器的运行时;下载才是把新编译的代码写到控制器里。如果控制器里已有运行程序,下载会提示是否停止当前程序,这时选择在线下载(Online Change)可以实现在不停机的情况下更新代码——这个功能是CoDeSys的强项,但只支持变量增减和实现体修改,不支持数据类型删除这类破坏性变更。如果改了结构体定义,就不允许在线下载,必须先停止再下载。现场维护时,能把这一点用明白,基本能省掉一半的停机时间。
3.4 采样跟踪和配方管理器:调试时别只盯着变量表
变量监视窗口看的是瞬态值,而采样跟踪(Sampling Trace)能记录一段时间内变量的变化曲线。这本书把采样跟踪列在应用对象里,是因为它和示波器的作用类似,尤其适合排查周期性波动问题:比如PID输出是否振荡、编码器位置跳变发生在哪个时刻、任务切换时共享变量是否被异常改写。
配方管理器(Recipe Manager)则是批量生产场景的利器。同一台设备要切换不同产品参数,传统做法是一堆参数变量轮着写,而配方管理器把一组变量快照绑定为一个配方,切换时自动下载。我第一次用它是在某灌装设备的项目上,三种规格的产品各对应一套灌装量、间隔时间、报警阈值,原来要改几十个变量,用配方后切换产品只需调一个索引。资料目录里这些内容都是连贯的,顺着看完就能决定哪些功能能在你的场景里直接用。
4. 五种语言与指令系统的配合:ST写算法、LD画逻辑、SFC排流程
这一章把五种语言放到一个模拟项目里实际跑一遍。模拟项目X是一个简单的三个工位分拣Demo:传送带启动、气缸推料、计数到位、循环。这个小系统能覆盖位逻辑、定时器、计数器、互锁、顺序控制这几类最常见的PLC编程要素。
4.1 ST写算法逻辑:IF、CASE、FOR循环在控制系统里的用法
ST和高级语言很接近,控制算法、数据转换、数组运算这类活都适合它。看一个典型片段,左侧是变址寻址,右侧是条件分支:
// 采集8通道模拟量,取其中最大的值 VAR arrAI : ARRAY[1..8] OF INT; // 8路模拟量采集值 nMaxVal : INT; // 最大值结果 i : INT; // 循环计数器 END_VAR nMaxVal := arrAI[1]; // 先把首通道赋给最大值 FOR i := 2 TO 8 DO IF arrAI[i] > nMaxVal THEN nMaxVal := arrAI[i]; // 发现更大的就更新 END_IF END_FOR这里ARRAY[1..8] OF INT声明了8个16位整数,下标从1开始——这一点和C语言从0开始不同,刚从高级语言转过来的人经常在数组越界上栽跟头。FOR循环里每次把当前通道值和已存的最大值比较,逻辑上没有难点,重点在于:这类算法代码必须放在一个确定周期的任务里,如果在事件触发的任务里跑循环,执行时长就无法预估,看门狗容易超时。这也是ST和LD本质差异:LD天然是周期扫描模型,ST允许你写耗时逻辑,但耗时不均匀也一样会拖垮实时性。
4.2 LD和FBD画回路:把电气逻辑翻译成图形语言
梯形图最基本的单元是常开触点和线圈,下面这一段等价于前面ST写的启保停:
| bStart bStop bRunning | |--| |------|/|-----------( )------| | bRunning | | |--| |--------------------| |第一行里bStart并联bRunning是自保持,bStop用常闭触点实现停止断开,bRunning线圈输出。之所以在ST和LD之间切换着用,是因为电气背景的人看图能直接对应继电器控制图;而写ST的人看这段可能需要换算一下。功能块图则把TON、CTU这类标准功能块拖出来连线,重点理解“使能端”和“输出端”的数据流,不需要研究电流方向。
这本书讲了两种视角的转换关系,用途是帮你统一团队里的维护习惯——有人习惯看图,有人习惯看代码。我的习惯是:安全联锁、急停回路一定用LD画成看得见的逻辑;PID运算、通信解析则用ST封装成黑匣子,调试时只关心输入输出,不用钻进内部。
4.3 SFC编排流程:工序步进用状态机而不是一堆线圈
SFC(顺序功能图)解决多步骤流程比LD或ST更优雅。看一个分拣Demo的SFC骨架:
初始化 -> 待料(等待光电触发) -> 推料(气缸伸出并计时) -> 计数判断(是否达到批量) ^ | | v 未达批量回到待料步骤 达批量回初始化,清空计数SFC每一步(Step)都有进入动作和退出动作,步与步之间是转移条件(Transition)。用ST写转移条件时,布尔表达式为真则自动跳转。相当于ST里的CASE step OF状态机写法,但SFC是图形化的,动作写在步里,条件写在转移线上。实际项目中SFC最典型的问题是什么?是漏写“初始化步的跳出条件”或者“并行分支的汇合条件”,结果程序卡在某个步永远不往下走,现场的现象就是设备动作到一半停住,急停还没触发,但程序也不报错。这种问题看变量监视是看不出来的,必须切到SFC编辑器看当前活跃步的颜色标记,一步一步追转移条件。
4.4 定时器、计数器、边沿检测:三条最少用的“元指令”
所以执行时间的信息需要结合第七章的目标:一个高水平的工程实施方案。
定时器最常用的TON(延时导通):输入IN从False变True后,PT指定的时间到了ET才累加到设定值,Q才输出True。和它对称的是TOF(延时断开)。计数器的CTU是递增计数器,CU引脚每个上升沿加1,Q在CV大于等于PV时置位。边沿检测这两年的常见坑是:R_TRIG的Q只保持一个扫描周期,用它来计数或触发动作时,如果目标POU的任务周期不一致,边沿就会被漏掉。所以跨任务传信号时,我建议直接传电平量,而不是传边沿脉冲,需要在接收端重建边沿。这些指令在职场的项目里天天都在用,真正高频的反而是一个普通逻辑块。指导书用了整章的指令表来打底,做新项目时不用到处翻手册,相当于提供了一个离线的指令速查表。
5. 常见问题排查:变量、任务周期、通信配置的实际数据参考
这一章汇集了我在CoDeSys项目里反复踩过的坑。每个都对应真实调试现场,按“现象 → 原因 → 解决”的格式整理。这些点比较琐碎,但值得放进工程笔记。
5.1 在线修改变量时程序突然重启
现象:在线模式下用强制功能(Force)修改某个布尔量,操作后控制器直接Stop,程序重新启动,生产流程中断。原因:强制变量如果作用于一个正在被多个任务读写的全局变量,可能导致一致性检查失败;更常见的是你强制的变量被声明成了常量或输入映射,CoDeSys不允许在线强制,于是触发运行时保护。解决:强制前先确认变量是普通VAR还是VAR_INPUT,VAR_CONSTANT不能强制;强制后不要跨任务持续刷新,操作完尽快解除强制,再观察变量值是否恢复正常。
5.2 更新代码时提示“在线下载不支持”
现象:修改了某个结构体定义或删除了一个全局变量,执行在线下载时直接弹窗报错。原因:在线下载(Online Change)只支持兼容性变更,新增变量、修改实现体这些可以做,但数据类型定义被修改后应用的结构映像变了,必须冷启动。解决:在工程树里点开“应用”看变更类型,如果是破坏性变更就不要硬点在线下载,直接停止控制器后完整下载。如果停产窗口非常短,你可以先把新逻辑放到新增POU里做成“增量变更”,把破坏性变更留到下次检修窗口。从那以后我每次上线前都会检查一遍本次变更的类型清单,提前分配好下载窗口。
5.3 程序能编译但CPU负载异常高
现象:空跑时CPU负载正常,一旦设备动作频繁,任务执行时间曲线明显抬高,控制周期不稳。原因:某个POU里做了耗时操作,比如在高速任务里处理字符串拼接、浮点开方运算或是大量的数组复制。ST写起来顺手,但浮点数运算在某些平台上比整数运算慢几倍,字符串处理更是吃内存。解决:用采样跟踪功能打开任务执行时间曲线,逐个POU加入耗时统计,找出最耗时的部分,把非实时数据加工搬到低速任务,高速任务里只做必须实时的事。另一个办法是给耗时算法加缓存,计算结果只有在输入变化时才重新计算。
5.4 Modbus TCP通信偶尔断线重连
现象:设备长时间运行后,某个从站的数据不再更新,重新下载程序又恢复正常。原因:TCP连接在异常断开后处于TIME_WAIT状态,CoDeSys的Modbus TCP客户端功能块如果没做周期性的重连逻辑,连接状态就卡在“建立中”或“断开”,永远不会自动恢复。解决:在通信管理功能块里加一个心跳变量每隔500ms读一次从站寄存器,连续多次读不到就把通信功能块复位重连。另外检查网关设备的TCP保活时间,比PLC的任务周期短一些,确保链路层能及时发现断线。通信协议这类配置官方文档里有标准参数,但“重连逻辑”必须自己写在应用层,这是工业现场的基本素养。
5.5 程序移植到另一个品牌控制器后模拟量地址错乱
现象:同一套应用代码,换了一台控制器后,模拟量通道读出来的数据对不上。原因:设备树里的IO映射没有重新绑定,应用层的变量绑定了旧的硬件地址,新硬件的输入通道顺序不同,自然数据错乱。解决:移植时分两步走——第一步把应用剥离开来单独导入,第二步在设备树里重新建立IO映射,让应用层变量通过映射表指向新设备的通道。这正好体现了CoDeSys软件模型的价值:变量名和物理地址解耦,每次换硬件只动设备树那一层,应用层不需要改逻辑。
注意:本节内容均基于通用CoDeSys开发环境,不同硬件平台的内核实现存在差异,遇到问题先以官方手册为准。
6. 进阶:把PLC程序做成产品
6.1 第一步:建立自己的功能块库
项目做完不是终点,把反复用的逻辑沉淀成库,才是兑现CoDeSys价值的方式。我在每个项目里都把“气缸控制”“电机启停联锁”“Modbus读取块”写成通用功能块,输入是按钮和传感器信号,输出是执行器和状态字,内部封装时序逻辑。新建库工程时勾选“库”类型,做完后导出为.lib文件,新项目引用它时自动带出版本管理。
对功能块编码有一条规定要养成:输入参数不带默认值或只在开发期带默认值,运行时所有输入必须由调用者显式赋值;输出不能依赖全局变量。坚持这个约定,你的功能块在任何任务里都能实例化,不担心数据串扰。
6.2 第二步:可视化界面和配方管理的配合
CoDeSys自带可视化编辑器,能画按钮、指示灯、数值显示、报警表格,适合设备厂商做单机HMI。我习惯把可视化绑定到同一个应用,这样变量名直接复用,不需要像传统HMI那样另建通信变量表。配合配方管理器,切换产品型号时可视化界面自动更新参数显示与报警上下限,现场工人不需要碰代码。
// 配方切换时,报警上限跟随配方参数更新 CASE rcpCurrent.byProductCode OF 1: rRealAlarmHi := rcpProductA.rMaxTemp; 2: rRealAlarmHi := rcpProductB.rMaxTemp; END_CASE这段代码说明配方和报警参数的关系:产品A和产品B各自有独立的上限值,切换配方后上限制自动更新,不会出现换产品忘改报警值的低级失误。rcpCurrent.byProductCode是当前配方的产品编号,rRealAlarmHi是实际用于比较的报警上限。参数上,配方数据结构里的rMaxTemp是REAL类型,温度值带小数点,用REAL存;产品编码用BYTE,够用且省内存。
6.3 第三步:远程监控与全局网络变量
几乎所有设备都要求“能远程看状态”。CoDeSys里最简单的方案是用全局网络变量:在应用里把需要监控的变量发布成网络变量,另一台设备或上位机通过以太网订阅。这个机制不需要写TCP/IP原始报文,配置好发布方和订阅方节点就自动同步。它的局限性在于实时性受网络延迟影响,不适合做运动控制级同步,但做设备状态采集、产量统计、报警上传完全够用。
实际项目的经验是:网络变量传递的数据要精简,尽量传状态字和统计数据,不要传每个扫描周期的实时模拟量;订阅端不要直接拿网络变量做控制逻辑,要经过本地缓存和心跳校验再用。把网络变量当“数据镜像”而不是“控制信号”,通信故障时设备才能安全停机而不是误动作。
6.4 一套适合复用的项目组织习惯
每次做新设备,我搭建的工程骨架基本是固定的:设备树下保留总线配置和IO映射;应用下分四个任务——安全逻辑任务(周期5ms)、主流程任务(周期10ms)、通信处理任务(周期50ms)、界面刷新任务(周期100ms);POU按功能分组命名,每个设备单元一个文件夹。这个结构经历过多次现场验证,是除了指令级细节外最能节约交接时间的地方。
以前有个项目我图省事,把所有逻辑塞进一个周期5ms的任务里,连续多少个POU不加文件夹,到了现场维护阶段,查一个报警要从几十个POU里翻半天,后悔药都来不及吃。从那以后我每次新建项目,都强制走一遍“设备树映射—多任务分配—POU分组”的顺序,新功能属于哪个周期、哪个文件夹,在一开始就定死。这套习惯比任何指令技巧都能减少故障排查时间。希望这份对CoDeSys开发指导的拆解能帮到你,至少少走几次弯路。
本文还有配套的精品资源,点击获取