蓝色港湾 发表于 2026-3-18 10:27:55

CoDeSys入门实战一起学习(二十二):自定义数据类型!数组+结构体+枚举,让程序结构更清晰

在PLC项目开发中,随着程序复杂度的提升,仅用标准类型和扩展类型,会出现两个问题:

[*]变量太多太杂:比如一台电机有型号、电压、电流、运行状态等多个参数,单独声明变量会导致变量表混乱,难以维护;


[*]数据关联性差:比如多个温度采集点、多个电机的参数,单独声明的变量无法体现“关联性”,程序可读性差。
而CoDeSys的自定义数据类型,就是为解决这些问题而生——允许开发者根据项目需求,将标准类型/扩展类型组合成新的、自定义的类型,核心包括数组、结构体、枚举、子范围,还有组合后的结构体数组。
自定义数据类型的核心优势是:结构化、模块化、可复用——用结构体封装设备参数,用枚举定义状态,用数组批量处理同类型数据,让程序从“一堆零散变量”变成“清晰的结构化模块”,后期维护、修改只需调整自定义类型,大幅提升开发效率。
本文将详解5种核心自定义数据类型,结合工业实战案例(电机参数封装、设备状态定义、温度采集批量处理),讲透声明、使用、实战技巧,让你的PLC程序更专业。
一、数组(ARRAY):批量处理同类型数据的“神器”
核心概念
数组是有序的同类型数据集合,将多个相同类型的变量整合为一个整体,通过下标访问单个元素——比如10个温度采集点,无需声明nTemp1~nTemp10,只需声明一个数组nTemp,通过下标nTemp、nTemp访问,大幅简化变量声明和批量处理。
CoDeSys数组特性

[*]支持一维、二维、三维(最大三维),满足绝大多数工业场景;


[*]下标可自定义(如、),建议从1开始,更符合工业编程习惯;


[*]元素类型可为任意标准/扩展/自定义类型(如INT、REAL、结构体、枚举);


[*]可通过数组向导快速声明,无需手动写语法。
1. 一维数组:最常用,批量处理单维度数据
声明格式:数组名:ARRAY[下限..上限] OF 数据类型;
核心用途:批量处理单维度数据(如多个温度采集点、多个电机的运行频率)。
实战案例:10个温度采集点的批量初始化
CoffeeScript
                        VAR


                        END_VAR



2. 二维数组:二维表格数据,如矩阵、多设备多参数
核心概念:二维数组可看作“数组的数组”,比如a,表示2行4列的二维表格,第一维表示“设备号”,第二维表示“设备参数号”。
实战案例:2台电机,每台4个运行参数(转速、电流、电压、温度)
CoffeeScript
                        VAR


                        END_VAR


3. 三维数组:三维空间数据,如多产线多设备多参数
声明格式:a OF REAL;(2条产线、3台设备、4个参数),工业场景中使用较少,按需选择。
数组的核心技巧与避坑

[*]初始化技巧:声明时直接赋值,支持批量重复赋值

[*]内存存储规则:一维数组按下标顺序存储,二维数组按行存储(先存第一行,再存第二行);
[*]越界保护:添加CheckBounds函数(右键Application→添加对象→用于隐含检查的POU),防止数组下标越界导致程序崩溃;
[*]数组向导:复杂数组用向导快速声明(输入助手→数组向导),选择维度、上下限、数据类型,自动生成代码,避免语法错误。
二、结构体(STRUCT):封装不同类型数据,模块化管理
核心概念
结构体是不同类型数据的有机集合,用于将与某一对象相关的所有参数(不同类型)封装成一个整体——比如一台电机的参数包括:型号(DWORD)、厂家(STRING)、额定电压(REAL)、额定电流(REAL)、运行状态(BOOL),这些参数类型不同,但都属于“电机”这个对象,用结构体封装后,可作为一个整体声明、赋值、传递,实现模块化管理。
核心优势

[*]关联性强:将对象的所有参数封装在一起,体现数据的内在联系;


[*]可复用:定义一次结构体,可在程序中多次声明变量(如多台电机共用同一个电机结构体);


[*]易维护:修改对象参数时,只需修改结构体定义,无需逐个修改变量;


[*]语法简洁:通过变量名.成员名访问参数,如stMotor.Product_ID。
声明与使用步骤(经典工业案例:电机参数封装)

[*]新建结构体DUT:右键Application→添加对象→DUT,选择结构,命名为Motor;
[*]定义结构体成员:按实际需求添加不同类型的成员,命名规范(如Nominal_Voltage表示额定电压);
[*]程序中声明结构体变量:可声明单个变量或数组(结构体数组,后续讲解);
[*]访问结构体成员:通过变量名.成员名进行赋值/读取。
实战案例1:定义电机结构体
新建结构体DUT,命名为Motor



实战案例2:声明并使用电机结构体变量


结构体的高级用法:嵌套结构体
结构体成员可嵌套另一个结构体,实现更复杂的对象封装——比如“产线”结构体,包含“产线编号”和多个“电机结构体”,示例:
// 先定义电机结构体Motor(如上)
                        // 再定义产线结构体,嵌套Motor结构体


                        // 使用:访问产线1的1号电机额定电压




三、结构体数组:批量管理多对象,工业场景核心用法
核心概念
结构体数组是结构体和数组的结合,用于批量管理多个同类型对象——比如一条产线有10台电机,每台电机都用Motor结构体封装,此时只需声明一个Motor类型的数组stMotor,即可批量管理10台电机的所有参数,是工业项目中最常用的自定义数据类型组合。
实战案例:一条产线有3台电机,批量管理电机参数
// 已定义电机结构体Motor(如上)
                        VAR


                        END_VAR


✅ 核心优势:批量初始化、批量遍历、单独访问,兼顾灵活性和模块化,适合多设备的PLC项目。
四、枚举(ENUM):定义离散状态,让程序更易读、更易维护
核心概念
枚举是离散状态的集合,用于将一组具有明确含义的离散值(如设备运行状态、故障类型)定义为一个枚举类型,枚举变量只能取集合中的值——比如设备的运行状态包括:停止、运行、故障、待机,用枚举定义后,可声明eRunStatus:RunStatus,变量值只能是Stop、Run、Fault、Standby,而非模糊的0、1、2、3。
核心优势

[*]程序可读性大幅提升:IF eStatus = Fault THEN比IF eStatus = 2 THEN更易理解,无需注释;


[*]避免错误赋值:枚举变量只能取定义的状态值,赋值其他值会编译报错;


[*]易维护:修改状态时,只需修改枚举定义,无需逐个修改程序中的数值。
声明与使用步骤

[*]新建枚举DUT:右键Application→添加对象→DUT,选择枚举;
[*]定义枚举成员:指定成员名称,可手动赋值(如Sun:=0),默认从0开始递增;
[*]程序中声明枚举变量,赋值并使用。
实战案例1:定义设备运行状态枚举
// 新建枚举DUT,命名为RunStatus
                        TYPE RunStatus :
                        (
                          Stop:=0,    // 停止,赋值0
                          Run,      // 运行,默认1
                          Fault,      // 故障,默认2
                          Standby   // 待机,默认3
                        ) INT;// 基本类型,默认INT,可指定为UINT/DINT等
                        END_TYPE

                        // 程序中声明并使用
                        VAR
                          eMotorStatus:RunStatus;// 电机运行状态,RunStatus类型
                        END_VAR

                        // 赋值:电机故障
                        eMotorStatus := Fault;

                        // 逻辑判断:电机故障时报警
                        IF eMotorStatus = Fault THEN
                          bAlarm := TRUE;// 报警灯亮
                        END_IF
实战案例2:定义星期枚举,实现周期状态切换
CoffeeScript
                        // 星期枚举,手动赋值
                        TYPE Weekday :
                        (
                          Sun:=0,
                          Mon,
                          Tue,
                          Wed,
                          Thu,
                          Fri,
                          Sat
                        ) INT;
                        END_TYPE

                        // 程序中实现每个周期切换一次星期
                        VAR
                          eWeek:Weekday := Sun;
                        END_VAR

                        // 周期切换
                        eWeek := eWeek + 1;
                        IF eWeek > Sat THEN
                          eWeek := Sun;
                        END_IF
五、子范围(SUBRANGE):限定数值范围,实现软件钳位
核心概念
子范围是某一整型的取值子集,用于将变量的取值范围限定在指定的上下限之间——比如将模拟量输出的数值限定在01000,将计数器的值限定在010000,赋值超出范围的值时,会自动被钳位到上下限(需添加范围校验函数),实现软件级的数值范围保护。
核心用途

[*]模拟量输入/输出的范围限定(如4-20mA对应0-1000,限定变量0~1000);


[*]计数器、定时器的数值范围限定,避免死循环;


[*]设备参数的范围限定(如电机转速限定0~3000r/min)。
声明与使用步骤

[*]新建子范围DUT:右键Application→添加对象→DUT,选择子范围;
[*]定义子范围:指定基本类型(只能是整型,如INT、UINT)、下限和上限;
[*]添加范围校验函数:CheckRangeSigned(有符号)和CheckRangeUnsigned(无符号),实现自动钳位;
[*]程序中声明子范围变量,赋值并使用。
实战案例1:限定模拟量输出范围0~1000
CoffeeScript
                        // 新建子范围DUT,命名为AO_Range,基本类型INT,0~1000
                        TYPE AO_Range :
                        INT RANGE 0 TO 1000;
                        END_TYPE

                        // 程序中声明并使用
                        VAR
                          nAO_Value:AO_Range;// 模拟量输出值,0~1000
                          nTemp:INT := 1200;   // 临时变量,超出范围
                        END_VAR

                        // 赋值:1200超出0~1000,自动钳位到1000
                        nAO_Value := nTemp;// nAO_Value = 1000
实战案例2:限定计数器范围,避免死循环
CoffeeScript
                        // 子范围:UINT,0~10000
                        TYPE Counter_Range :
                        UINT RANGE 0 TO 10000;
                        END_TYPE

                        // 程序中使用,避免FOR循环死循环
                        VAR
                          nCounter:Counter_Range := 0;
                        END_VAR

                        // 循环:nCounter最大为10000,不会超出,避免死循环
                        FOR nCounter:=0 TO 20000 DO
                          // 循环逻辑
                        END_FOR
核心避坑点

[*]子范围的基本类型只能是整型(SINT/USINT/INT/UINT等),不支持REAL/LREAL;


[*]必须添加范围校验函数(右键Application→添加对象→用于隐含检查的POU),否则赋值超出范围会编译报错,添加后会自动钳位;


[*]子范围变量可直接与基本类型变量运算,自动兼容。
总结
自定义数据类型是CoDeSys工业项目开发的核心,让程序从“零散变量”升级为“结构化模块”,核心使用原则:

[*]批量处理同类型数据:用数组(一维/二维/三维,按需选择);


[*]封装不同类型的对象参数:用结构体,实现模块化管理,支持嵌套;


[*]批量管理多对象:用结构体数组,工业场景最常用,兼顾灵活与规范;


[*]定义离散状态:用枚举,提升程序可读性,避免错误赋值;


[*]限定数值范围:用子范围,实现软件钳位,保护设备和程序。
掌握这些自定义数据类型,你就能写出结构清晰、易维护、可复用的PLC程序,适配从小型设备到大型产线的所有项目。下一篇我们将带来CoDeSys数据类型的实战总结与避坑指南,结合实际项目案例,讲透数据类型的选择技巧、内存优化方法和常见错误解决方案,让你彻底精通CoDeSys数据类型!
页: [1]
查看完整版本: CoDeSys入门实战一起学习(二十二):自定义数据类型!数组+结构体+枚举,让程序结构更清晰