首页 > 其他分享 >PLC指针和引用类型

PLC指针和引用类型

时间:2024-04-18 16:46:15浏览次数:27  
标签:int ref DB PLC 引用 数组 数据类型 my 指针

使用环境是PLC1500,在1200中可能某些数据类型不可用。

  • Pointer

  • Any

  • Variant

  • References

  • 数组,数组DB,动态数组

1. Pointer
Pointer实际上会占用6个Bytes地址空间。前两个byte用来放DB块号(B#16#81表示I,82表示Q,83表示M,84表示DB,等等)或者0,后面四个放数据区,字节地址,位地址。
写法如下:

//指向db2.dbx12.0
P# DB2.DBX12.0
P# DB2.DBB12

//指向M12.0
P# M12.0

Pointer可以配合AT使用拆分输入的地址区:

.将输入的Pointer映射给DataStruct的结构。
.DataStruct并不会在FB/FC接口中表现出来。
.如果实际的In_Data比DataStruct长,则DataStruct只会映射出自己已有的内容。
.如果实际的In_Data比DataStruct短,则DataStruct会全部映射In_Data,多出来的部分保持初始值。

2. Any
Any和Pointer有点相似,但是Any不一样的是它带有数据长度
Any实际上会占用10个Bytes地址空间,分别由数据类型,数据长度,DB块号,存储器开始地址组成。
写法如下:

//从db2.dbx12.0开始,往后数100个字节
P# DB2.DBX12.0 BYTE 100
P# DB2.DBB12 BYTE1 00

//从M12.1开始,往后数10个位长度
P# M12.1 BOOL 1O

也可用AT拆分Any指针:

3. Variant

  • 相比于Pointer和Any,Variant无疑是更先进和占优的。前两者本质还是对一个简单数据类型的绝对地址寻址,如果是被优化块,或者是一样STRUCT和UDT一类复杂类型,他们就显得要麻烦一些。
  • Variant可以指向基本的数据类型(BOOL,INT,WORD)。
  • Variant不占用任何背景数据块或者工作存储器的空间,因为Variant的变量不是一个对象,而是实实在在对变量的引用。也正是因为如此,所以它不能在函数的static部分被定义,只能定义在输入输出等接口区域或者temp区域。
    如图,建立了两个Variant类型变量,它们可以指向任何数据。

有一些常用的指令来处理Variant:

  • TypeOf()VariantGet()VariantPut()
//判断time相加
IF TypeOf(#I_var1) = Time AND TypeOf(#I_var2) = Time THEN
    VariantGet(SRC := #I_var1,
               DST => #statTime1);
    VariantGet(SRC := #I_var2,
               DST => #statTime2);
    VariantPut(SRC:=#statTime2,
               DST:=#inout_var);
    
    #out_time := #statTime1 + #statTime2;
END_IF;
  • TypeOfelements()CountOfelements不常用,主要配合数组使用。

  • 使用DB_ANY_TO_VARAINT把DB_ANY变成VARIANT,然后自动判断引用对象类型。

    • 在某些场景中,比如物料信息,MES下发报文给PLC的时候,需要PLC自己去判断是哪个类型。在这种情况下自己建立几个表达物料类型的UDT,然后在DB中实例化。当报文进来的时候,去判断相应的类型然后做相应的事件,代码如下:
//DB_ANY_to_Varaint
#inout_var := DB_ANY_TO_VARIANT(in := #I_DB_Any, err => #statError);
IF #statError = 0 THEN
    CASE TypeOf(#inout_var) OF
        "_PracticeDBB".UDT_Type_A:
            ;
        "_PracticeDBB".UDT_Type_B:
            ;
    END_CASE;
END_IF;
  • IS_ARRAY指令判断是否是数组
//2.is_array
IF IS_ARRAY(#I_var1) THEN
    //...
    #temp := 0;   
END_IF;

4. References
在优化块中使用Ref()

  • 使用范围:

    • FC:Input/Output/Temp/Return
    • FB:Temp
    • OB:Temp
  • 要进行引用声明用REF_TO,指定被引用变量所需要的数据类型,不支持bool,如图:

  • 使用ref

//ref
//#temp_int是temp区的int类型数据;#my_int是static区的int数据类型
#temp_int := #my_int;//不使用ref直接赋值
#my_ref_int:=#my_int;//使用ref后不能直接赋值了,这句话在TIA portail中显示报错。
#my_ref_int := REF(#my_int);//可以使用ref()引用

.#my_ref_int:=#my_int在博图中是编程错误的。

  • ref的引用场景
    ^解引用,完成地址到值得转换。类似C语言中的p和*p。
    建立如下变量:

    对应测试代码如下:
//ref应用
#c := #a + #b;//此处c=15
#my_ref_int := REF(#a);//把a的地址给引用
#my_ref_int^ := 20;//因为引用类型指向#a,所以现在#a=20
#c := #a + #b;//由此,#c=25
#c := #my_ref_int^ + #b;//等同于c=a+b
  • ref不支持ARRAY[*],意味着不支持指向指针的指针。

  • 尝试用?=指令将varaint分配给一个ref,可以使得程序更加灵活。

//尝试结合varaint使用
#my_ref_UDT ?= #inout_var;
IF #my_ref_UDT <> null THEN
    #my_ref_UDT^.Array_byte[15] := #my_UDT.Array_byte[15];
END_IF;

5. 数组,数组DB,动态数组

  • 数组本身没啥好说的,常用数据类型,格式如下:
Array [n..j] of <数据类型>
  • 数组DB
    在项目新添加块中添加自己想要的数组DB。注意的是数组DB的属性始终是优化访问的。
    创建如下图:

.选择想要建立的数据类型,基本类型和复杂类型都可以
.选择需要的数组限值
.它用来存初始化阶段建立的类型和数量,后期不能修改,不能增减。适合用来做固定不变的,比如物料信息一类的存储。

  1. 数组DB的两个重要方法
  • ReadFromArrayDB
  • WriteToArrayDB
//数组DB 
#TmpInt:=ReadFromArrayDB(db := "my_array_DB", index := 10,value =>#my_IM0);
#TmpInt:=WriteToArrayDB(db := "my_array_DB", index := 5, value := #my_IM0);
  1. 在数组DB的属性设置中,有一个勾选项叫做仅存储在装载存储器中。这个是为了不占用数据工作存储器资源而设置的。因为如配方一类的数据量会比较大,用工作存储器存太浪费了,存在装载存储器中,内存不足还可以更换。
    选择数据工作存储器之后,两个指令要换成ReadFromArrayDBLWriteToArrayDBL
  • 动态数组
    动态数组(可变数组)的结构如下:
Array [*] of <数据类型>
  1. 动态数组可以被定义为多维化。

  2. 动态数组因为是基于引用做的,一般只能用In/InOut类型接口创建。

  3. 使用案例
    在创建FB的时候,有些时候可能并不是很清楚外部传入的数组长度到底是多长的(取决于外部调用FB时连接的管脚)。但是如果某一个计算需要用到数组边界(比如算平均值或者其他的),这时候动态数组的灵活性就显现出来了。我们可以用LOWER_BOUNDUPPER_BOUND来动态化我们的边界值。需要注意的是,计算出来的边界值是外部连接的固定数组的边界值,相当于外部管脚连接了数组长度就固定了,并不是高级语言里面的那种动态分配内存的概念,我因理解错误在这里踩过坑

    代码如下:

//动态数组方法
//DIM表示数组维度,1=一维数组
#L_value := LOWER_BOUND(ARR := #array_var, DIM := 1);
#U_value := UPPER_BOUND(ARR := #array_var, DIM := 1);

6. 总结
这就是一些指针数据类型的应用了,其实PLC里面还有些指令是用来操作地址的,比如PEEK和POKE。以后再讲。

标签:int,ref,DB,PLC,引用,数组,数据类型,my,指针
From: https://www.cnblogs.com/xiacuncun/p/18143390

相关文章

  • 智能指针
    芝士wa2024.4.8参考视频链接概述C++的指针包括两种:原始指针智能指针:unique_ptr,shared_ptr,weak_ptr智能指针是原始指针的封装,其优点是会自动分配内存,不用担心潜在的内存泄漏。各种指针中,最常用的是裸指针,其次是unique_ptr和shared_ptrweak_ptr是shared_ptr的一个补充......
  • C#对象引用更新问题
    在C#中,当你将一个对象赋值给另一个变量时,实际上是将对象的引用(内存地址)赋给了这个变量,而不是将对象本身复制一份。这意味着如果你修改了一个引用类型对象(如列表、类实例等),那么所有引用该对象的地方都会受到影响,因为它们都指向同一个内存地址。为了避免这样的问题,你可以通过以下......
  • vptr和vtbl(虚指针和虚函数表)
    vptr和vtbl(虚指针和虚函数表)c++代码的抽象类是->类当中只包含纯虚函数当一个类有虚函数,即便类当中没有成员变量.他的对象大小也会有一根指针大小->由操作系统决定指针多大虚函数子类的对象里面有父类的成分示例结构代码:#pragma#ifndef__VPTR_AND_VTBL__#define__V......
  • 引用循环与内存泄漏
    Rust的内存安全性保证使其难以意外地制造永远也不会被清理的内存(被称为 内存泄漏(memoryleak)),但并不是不可能。Rust并不保证完全防止内存泄漏,这意味着内存泄漏在Rust中被认为是内存安全的。这一点可以通过 Rc<T> 和 RefCell<T> 看出:创建引用循环的可能性是存在的。这会造......
  • rust引用计数智能指针Rc<T>
    大部分情况下所有权是非常明确的:可以准确地知道哪个变量拥有某个值。然而,有些情况单个值可能会有多个所有者。例如,在图数据结构中,多个边可能指向相同的节点,而这个节点从概念上讲为所有指向它的边所拥有。节点在没有任何边指向它从而没有任何所有者之前,都不应该被清理掉。为了启用......
  • 西门子PLC数据类型1-位、位序列、整数、浮点数、日期时间
     本文摘于西门子官网内容一、位、位序列、整数、浮点数、日期时间基本数据类型:包括位、位序列、整数、浮点数、日期时间。此外字符也属于基本数据类型,请参见文档 String 与 WString。此外BCD码虽然不属于数据类型,但也是一种数字表示方式。1.1位和位序列注意:虽然位......
  • C++ 默认参数与引用传递:语法、用法及示例
    C++默认参数默认参数概述在C++中,函数参数可以拥有默认值。这意味着,在调用函数时,如果省略了某个参数,那么将使用为该参数指定的默认值。设置默认参数默认参数值使用等号=符号进行设置,位于参数声明的类型之后。例如:voidmyFunction(stringcountry="Norway");在这个例......
  • 翻译|指针很复杂,或者说:字节里究竟有什么?
    本文原作者:RalfJung,原文地址:https://www.ralfj.de/blog/2018/07/24/pointers-and-bytes.html今天夏天,我再次完全使用Rust开发,并致力于(除其他事项外)为Rust/MIR开发一个“内存模型”。不过,在谈论我今年的想法之前,我终于要花点时间打破“指针很简单:他们只是一些整数”的神话了。......
  • 函数及指针
    c语言递归函数就是一个函数调用了函数本身要有一个明显结束的条件要有一个结束条件的趋势常用系统函数字符串函数标准库头文件<string.h>strlen(str)返回一个数组的长度(有元素的长度字符数组中的结束标识符不算)接收类型是size_tstrcpy(str,str1)将str1的字符串......
  • PLC报警消息处理
    在TIAProtal中,大致有以下几种方式可以获取报警信息:HMI侧组态报警Program_alarmProdiag第一种方式基本上是最常用和最简单的,基本步骤是先创建报警变量再组态报警文本,由HMI定时去轮询变量,通过监控变量值的变化触发。优点是配置简单,但是效率比较低,工作量比较大(以前项目上我会新......