今天突然想写一篇帖子是因为,今天自己犯了一个想当然的错误,写出来分享给大家
第一部分 错误程序分析
为了避免浪费大家时间,所以,先就把自己的观点给抛出来
FC 块接口Input/output对于复杂数据类型参数,是按引用/地址方式传递的。
既然按地址传,那这个参数本质上就是个 数据类型Pointer,我可以按照Pointer类型进行分析处理。
基于以上观点,自己编写了一个程序,程序简单介绍如下
一个PLC数据类型(UDT)-PointerAsStruct 用于对Pointer数据类型进AT,结构如下
TYPE " PointerAsStruct"
VERSION : 0.1
STRUCT
Datablock { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt;
Offset { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : DWord;
END_STRUCT;
END_TYPE
l 一个FC功能,部分类容如下
FUNCTION " CalcPreStopWeight" : DInt
TITLE = CalcPreStopWeight
{ S7_Optimized_Access := 'FALSE' }
FAMILY : ASW
VERSION : 1.0
VAR_INPUT
BaseWeight : DInt;
InMode : Int;
BinCtrlDB_PLC : "BinCtrl_PLC";
BinCtrlDB_PLCAsStruct AT BinCtrlDB_PLC : "PointerAsStruct";
BinCtrlDB_PCReadBins : Pointer;
BinCtrlDB_PCReadBinsAsStruct AT BinCtrlDB_PCReadBins : "PointerAsStruct";
BinCtrlDB_PCWriteExt : Pointer;
BinCtrlDB_PCWriteExtAsStruct AT BinCtrlDB_PCWriteExt : "PointerAsStruct";
BinCtrlDB_ASW : Pointer;
BinCtrlDB_ASWAsStruct AT BinCtrlDB_ASW : "PointerAsStruct";
END_VAR
VAR_TEMP
tmpActive : Bool;
tmpByteOffset : DInt;
tmpDBNo : UInt;
tmpNoOfBin : Int;
tmpActiveBins : Int;
tmpIdx : Int;
tmpParseAddr : Struct
PCReadBinsStartAddr : DInt;
PCReadBinsPerBin : DInt;
PCWriteExtStartAddr : DInt;
PCWriteExtPerBin : DInt;
ASWStartAddr : DInt;
END_STRUCT;
tmpPreStopWeight : Int;
tmpPreStopWeightBuffer : Array[0..#MAX_DISCHARGERS] of Int;
tmpRet : Int;
END_VAR
VAR CONSTANT
MAX_DISCHARGERS : Int := 64;
OFFSET_MASK : DWord := 16#F_FFFF;
END_VAR
BEGIN
IF #BinCtrlDB_PLCAsStruct.Datablock <> #BinCtrlDB_PCReadBinsAsStruct.Datablock
OR #BinCtrlDB_PLCAsStruct.Datablock <> #BinCtrlDB_PCWriteExtAsStruct.Datablock
THEN
#ASW_UL_Sec_CalcPreStopWeight := #BaseWeight;
RETURN;
ELSIF NOT #BinCtrlDB_PLC.ToASW.InfActive THEN
#ASW_UL_Sec_CalcPreStopWeight := #BaseWeight;
RETURN;
ELSE
;
END_IF;
以上程序运行结果:
#BinCtrlDB_PLCAsStruct.Datablock 中的值不是实参的DB号;
#BinCtrlDB_PCReadBinsAsStruct.Datablock中的值是实参的DB号;
基于结果分析,下个正确结论:
FC 块接口Input/output复杂数据类型参数,是“按引用/地址方式传入”的;但这并不等于该参数在语言层面就是 Pointer数据类型。
AT 是对一个变量的覆盖,必须和被覆盖变量字节相同,因此对BinCtrlDB_PLC使用AT 也应该是,覆盖数据类型”BinCtrl_PLC”,而不是一Pointer数据类型
所以,对输入参数BinCtrlDB_PLC,进行AT,并且数据类型是"PointerAsStruct";就是致命错误了
BinCtrlDB_PLC : "BinCtrl_PLC";
BinCtrlDB_PLCAsStruct AT BinCtrlDB_PLC : "PointerAsStruct";
总结下,自己错误原因:1.有点想当然了;2.对于AT 的使用上存在误区
第二部分 再次学习下参数传递方式
我们,知道当一个程序块被调用时,程序员通过块接口传递参数,参数传递主要通过以下2种方式:
l 以拷贝方式传递(值传递)- Transfer as copy (Call by value)
l 以指针方式传递(引用传递)- Transfer as pointer (Call by reference)
看看博途手册中,S7-1200/1500对于FB/FC 2种类型的程序块参数传递机制

基于以上表格,我们可以看到以下几点
1. 对于复杂数据类型,通过InOut接口传递,是默认以指针方式传递的,这就意味着,这些实参在FC/FB内部是可以进行读写操作的。
2. 对于FC来说复杂数据类型通过Input与Output块接口定义的参数,都是以指针方式传递的,但是对于FB来说确是以拷贝方式传递,核心原因是FB有自己的实例。
3. 对于工艺对象,不管参数定义在块接口的什么区域,全部按照以指针方式传递的
4. 最后,一个很重要的事情,如果,在优化与非优化块之间传递参数,始终都是以拷贝方式进行传递的,同时,还涉及到高低字节顺序互换处理,所以,要避免在优化与非优化块之间进行大量数据的传递
FB与FC对于以指针方式传递方式还有以下不同
1. FC Input/output复杂类型参数传递,需要依赖与调用者的局域堆栈,它是把实参的引用地址拷贝一份到调用者的局域堆栈中
2. FC块接口中定义的参数,必须连接实参;FB中InOut中定义的数据可以不赋予实参,对于Input/output数据直接保存在FB实例中
3. 定义在InOut中的复杂数据类型,它是对实参地址的直接引用