今天继续我们的小白教程,老鸟就不要在这浪费时间了😊。

前面一期我们讲了CODESYS工程的基本组成,我想经过几期的学习大家应该对如何使用CODESYS的开发环境有了基本的了解,从这一期开始我们介绍一下CODESYS的ST语言相关的基础知识。本期主要介绍一下ST中的主要关键字和变量类型,以及这些类型使用,顺便会介绍一下CODESYS与C语言的一些使用上的差异。

一、基本变量类型

CODESYS的ST变量类型跟其它编程语言类似,包括基本类型、数组、枚举、结构、联合等,还有指针、引用,另外功能块也算是一个类型。

1.基本类型

基本数据类型如下表:

数据类型

CODESYS-ST

32位平台

布尔

BOOL

8Bit(0或1)

整型

BYTE

8Bit(0~255)

WORD

16Bit(0~2^16-1)

DWORD

32Bit(0~2^32-1)

LWORD

64Bit(0~2^64-1)

SINT

8Bit(-128~127)

UINT

8Bit(0-255)

INT

16Bit(-2^15~2^15-1)

UINT

16Bit(0~2^16-1)

DINT

32Bit(-2^31~2^31-1)

UDINT

32Bit(0~2^32-1)

LINT

64Bit(-2^64-1~2^64-1)

ULINT

64Bit(0~2^64-1)

实数

REAL

32Bit

LREAL

64Bit

字符串

STRING

1~255,默认80

这里需要注意的是CODESYS的基本数据类型与C语言类似,但是在字长上有差别。CODESYS中的INT在32位平台上默认为16位,而C语言的int在32位平台上默认为32位。另外对于浮点数,REAL与C语言的float对应,LREAL与C语言的double对应。

2.数组

数组定义的形式如下:

变量名 : ARRAY[0..N] OF 变量类型;

变量名 : ARRAY[0..N,0..M] OF 变量类型;

注意:

(1)变量名不区分大小写。

(2)变量类型可以是基本类型、结构、联合或者功能块。

(3)数组可以下标可从0开始,也可以不从0开始(这一点比C语言方便)。

(4)N和M可以用常量来代替。

数组初始化方式:

rPos : ARRAY[0..6] OF LREAL:=[7(5.0)];

rPos : ARRAY[0..6, 0..6] OF LREAL:=[7(5.0), 7(7.0)];

rPos : ARRAY[0..2] OF LREAL:=[5.0, 6.0, 7.0];

rPos : ARRAY[0..2, 0..2] OF LREAL:=[5.0, 6.0, 7.0, 8.0, 9.0, 10.0];

注意:

(1)默认会初始化为0。

(2)CODESYS还有一个数组的数组的定义方式也可以定义多维数组。

 二维数组定义如下:

aiPoints : ARRAY[1..2,1..3] OF INT := [1,2,3,4,5,6];

数组的数组定义如下:

ai2Boxes : ARRAY[1..2] OF ARRAY[1..3] OF INT := [ [1, 2, 3], [ 4, 5, 6]];

这里需要注意不要把数组的引用方式aiPoints[1, 2]和ai2Boxes[1][2]混淆了,这两种引用方式是对应不同的定义方式。

3.枚举

枚举由用户定义的数据类型,定义后可以在整个工程中使用。枚举定义可以像功能块和函数一样在“Applicationà添加对象”中选择添加。

TYPE ErrID :

(

name1 :=0,

);

END_TYPE

注意:

(1)在枚举声明中,通常至少声明2个成员。

(2)枚举的数据类型默认为INT,但用户可以指定其他数据类型。支持的类型包括:INT | UINT | SINT | USINT | DINT | UDINT | LINT | ULINT | BYTE | WORD | DWORD | LWORD

4.结构和联合

结构和联合是由用户定义的数据类型,将不同的变量组合成一个独立单元。结构和联合也可以在“Applicationà添加对象”中添加。

结构的定义方式如下:

TYPE name:

STRUCT

END_STRUCT

END_TYPE

联合的定义方式如下:

TYPE name :

UNION

END_UNION

END_TYPE

注意:

(1)结构定义后,结构名称在整个工程中可以作为基本数据类型来定义变量、数组或指针等。

(2)结构也可以作为其他结构的成员。

(3)可以通过EXTENDS扩展现有结构,方式如下:

TYPE name EXTENDS basename :

STRUCT

   …

END_STRUCT

END_TYPE

扩展后的结构同时拥有name和basename结构的成员。

(4)结构还有一个专属数据类型“BIT”,用于定义位变量,其值只能是TRUE和FALSE。

5.指针

指针用于在运行时存储对象的内存地址。定义方式如下:

name: POINTER TO 数据类型/结构/功能块;

实例如下:

a : INT;

b : INT;

pt : POINTER TO INT;

pt := ADR(a); //变量a的地址赋值给指针pt

b := pt^; //把a的值赋值给b

注意:

(1)指针在定义是必需指定类型,不同类型指针不能互相转换。

(2)指针本身是DWORD类型。

(3)指向IO的指针会报错。

(4)指针指向的数据可以通过[]来进行索引访问。索引组件的数据类型和大小由定义的数据类型决定,索引访问是通过将索引相关的偏移量i*SIZEOF(<base type>)添加到指针的地址来实现的。

正确方式:piData[i] := (piData + i * SIZEOF(INT))^;

错误方式:piData[i] != (piData + i)^

6.引用

引用隐式引用另一个对象。访问时,引用被隐式取消引用,因此不需要特殊的内容运算符^(如指针)。定义方式如下:

name : REFERENCE TO <data type> ;

注意:

(1)编译器版本>= V3.3.0.0,引用默认会初始化。

(2)最好不要对设备输入进行引用,没有写权限会让编译器报警。

(3)引用类型不能用作数组、指针或引用的基类型。

(4)不能对BIT类型进行引用。

以下为引用的错误用法:

在函数或功能块传递参数时,可以使用引用从而避免使用指针。

7.功能块

功能块应该说是由用户定义的一个独立的功能单元,类似于C++ 的类。它是一个具有完整功能的模块,使用时跟定义各种变量类型一样需要先声明实例才能使用。功能块的定义同样可以在“Applicationà添加对象”中添加。

其实这5、6、7这几个内容我在写的时候一直犹豫要不要讲,尤其是指针,对于新手来说完全可以不用,以避免出错^o^。为了保持内容的完整性,上面还是做一个简单的介绍。

二、变量作用域

常用的变量作用域包括全局变量、局部变量、静态变量等。

1.全局变量

全局变量通常在整个程序范围内定义,会放在“Application”树下面。其添加方法是在“Application”上点右键,在“添加对象”中选择“全局变量列表…”。在弹出的窗口中输入全局变量的定义域名称,默认是“GVL”。

在左侧设备树“Application”下会增加“GVL”,双击打开后可以看到:

全局变量的定义必须是在字段“VAR_GLOBAL…END_VAR”之内。全局变量在整个工程范围内有效,定义后工程内所有的程序、函数、功能块都可以访问。这里需要注意的是,在全局变量上方有一个宏定义“{attribute 'qualified_only'}”,其作用是限定全局变量的使用必须指明定义域,即上面新建全局变量时输入的名称“GVL”。比如上图中定义的“rPoint”变量在程序中使用时需要写“GVL.rPoint”,否则会报错。如果觉得这样用起来麻烦,可以把这个宏注释掉,这样可以直接使用“rPoint”。

需要注意的是如果在程序块中局部变量与全局变量重名,则在该块中优先使用局部变量。建议在使用时局部变量尽量不要与全局变量重名,要么就给全局变量指明定义域,这样方便区分。

对于编译器版本>=3.2.0.0,CODESYS总是在本地POU变量之前初始化全局变量。

2.局部变量

局部变量通常在程序、函数或功能块内部定义。局部变量的定义必须是在字段“VAR…END_VAR”之内。比如上一期工程里面main函数的变量定义都属于局部变量。

局部变量只在定义的程序块范围内有效。

3.静态变量

静态变量的定义必须是在字段“VAR_STAT…END_VAR”之内。

注意:

(1)CODESYS在第一次调用每个块时初始化静态变量。

(2)静态变量只能从声明变量的命名空间中访问,类似C语言中的静态变量。

(3)当应用程序离开块时,静态变量保留其值。例如,可以使用静态变量作为函数调用的计数器。

4.输入/输出变量

输入/输出变量是程序块(POU)接口的一部分,用于声明程序块的输入/输出变量。

定义方式如下:

关键字 name

VAR_IN_OUT

    <variable name> : <data type>;

END_VAR

关键字:FUNCTION、FUNCTION_BLOCK、METHOD、PRG

注意:

(1)程序块调用时不能直接将常量(文字)指定给VAR_IN_OUT类型的参数。VAR_IN_OUT变量通过引用方式传递(类似C语言的引用),直接使用对应的变量,在程序块内部不会生成副本。

(2)VAR_IN_OUT变量在其使用的程序块内是可读可写的,内部对变量的修改在程序块执行完成后会保留。

(3)VAR_IN_OUT变量不能通过“功能块实例名称”直接远程读或写。功能块中只有VAR_INPUT和VAR_OUTPUT变量可以这样使用。

(4)如果字符串变量作为参数传递,则实际变量和形式变量的长度应该相同。否则,传递的字符串可能会被无意操作。VAR_OUTPUT CONSTANT参数不会出现此问题。

(5)程序块调用时不能将位变量直接指定给VAR_IN_OUT类型的参数,因为位变量没有直接的内存地址,无法通过引用方式传递。位变量的传递需要通过一个中间变量。

5.功能块输入和输出变量

输入输出变量用于功能块的输入和输出定义。输入变量的定义必须是在字段“VAR_INPUT…END_VAR”之内。输出变量的定义必须是在字段“VAR_OUTPUT…END_VAR”之内。

6.功能块临时变量

临时变量定义必须是在字段“VAR_TEMP…END_VAR”之内。

注意

(1)临时变量只能在程序块和功能块中使用。

(2)每次调用块时,CODESYS都会初始化临时变量。

三、变量保持类型

1.常量

常量变量在全局变量列表或编程对象的声明部分中声明。在实现中,常量变量可以通过实例路径以只读方式访问。大部分时候是用来定义一些需要改但是不经常改的值。

定义方式如下:

作用域 CONSTANT

    name : 数据类型 := 初始值;

END_VAR

作用域:VAR、VAR_INPUT、VAR_STAT、VAR_GLOBAL

VAR_GLOBAL:全局常量,在整个工程中有效。

VAR:局部常量,仅在定义的程序块内有效。

VAR_STAT:静态变量

注意在运行过程中常量值不能修改。

2.保留变量

保留变量由关键字RETAIN声明。保留变量在热启动后会保留其值,但在重新加载程序、下载或冷启动后不会保留。

定义方式如下:

作用域 RETAIN

    <identifier>: <data type>;

END_VAR

作用域类型:VAR、VAR_INPUT、VAR_OUTPUT、VAR_IN_OUT、VAR_STAT、VAR_GLOBAL

不允许使用AT关键字分配输入、输出或内存地址。

3.持久变量

持久变量由PERSISTENT关键字声明。持久变量会存储在外部文件中,因此会一直保留。

全局持久变量定义方式如下:

VAR_GLOBAL PERSISTENT RETAIN

    <identifier> : <data type> (:= <initialization>)?;

 <instance path to POU variable>

END_VAR

程序块中的定义方式如下:

作用域 PERSISTENT RETAIN

    name : <data type>;

END_VAR

作用域:VAR、VAR_INPUT、VAR_OUTPUT、VAR_IN_OUT、VAR_STAT、VAR_GLOBAL

注意:

(1)不允许使用AT关键字分配输入、输出或内存地址。

(2)在持久变量列表中使用不能使用指针(POINTER TO数据类型)。重新下载应用程序时,指针类型的位置可能会改变,将会导致不可预料的结果。

四、后记

因为是介绍ST的基础知识,所以上面把相关的角落都扫了一下,应该说需要用到的地方都过了一下。新手在看的时候,如果发现现在用不到的可以先跳过,只看自己感兴趣的就好了。实际上CODESYS提供了在线帮助文档,这是一个使用CODESYS的百科全书。但是大部分资料是英文的,而且有很多写的真的不太好懂(就是那种单词全认识,什么意思不知道?!!感觉英语全都白学了,好尴尬~~~)。这也是为什么会有这些文章的原因之一,希望对大家有帮助^o^。

------------------

原创不易,感兴趣的多支持

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐