今天说一个PLC编程的基础知识:块调用的值传输与指针传输。我们以使用最广泛的博途PLC为例子。
在块调用时会传递参数,被传递的参数可以是值也可以是指针。这是一个知识点。不过平时对这个知识点没有什么感觉。我也如此,虽然知道这个事情,但是似乎与我平时编程关系不大。but上周的调试经历,让我在这个问题上整整付出了几个小时的心思才越过这道坎。这几个小时我更多在思考为什么我的预期与实际程序监控完全不同?心里的冲突很激烈。为了让伙计们少走类似的弯路,今天就说说块调用的值传输与指针传输。
为了突出问题,这里不把我遇到的真实问题拿来说。我把我遇到的真实问题简化后分享给大家。
先介绍这个例子。

图 1
项目如图1,很简单。1个FC,1个OB1,1个数据块。

图 2
数据块中的内容也很简单,如图2。我们整个项目只对Flag1进行操作。
图 3
如图3,在OB1中调用FC1。并设置观察点1,这个观察点用来观察Flag1的值。在OB1调用FC1时,引脚"tag_INOUT"的实际参数是Flag1。
图 4
图4是FC1的情况。该FC有一个IN-OUT引脚"tag_INOUT"。在该FC1内部对Flag1进行置位操作。到此,所有程序和数据到此就介绍完毕。
观察程序执行的情况。在FC1中对标志位进行置位,标志位的值是1;在观察点1,标志位是0。奇怪不?整个程序都没有复位标志位的程序,可是,可是,可是结果使人意外。伙计们阅读到此,可以先缓一缓神,不要急于向下看结果。先想5分钟这是为什么。我遇到的实际情况比这个要复杂得多。我遇到的问题卡了我几个小时。我怀疑了各个可能出问题的地方,试图修正程序,但都失败。
在观察点1,标志位为什么被复位了呢?好了,我们给出答案和分析。
观察图3,我们知道在图3调用FC1时刻(network 1),Flag1是0。在调用FC1后,CPU运行FC1中的程序,当运行完FC1的network 1后(图4),Flag1是1。
然后CPU会结束FC1的运行返回OB1中。在结束FC1运行前,CPU会做一件事,这件事情就是将在FC1中的运行结果输出到FC1的输出引脚以及输入输出引脚。问题就出现在这里。在FC1中变量tag_INOUT始终是0,因为在FC1中从没对这个变量进行置位操作。在结束FC1运行返回OB1时刻,CPU将tag_INOUT(值是0)的值赋值给Flag1。此时Flag1的值就是0了。好了,完全能解释通了。
上一段介绍的就是函数引脚的值传输。值传输在类似上述的一些情况,容易出问题。所以博途引入了函数引脚的指针传输。下面我们看看指针传输是否能修正这个问题。我们首先对FC1的引脚进行改造。

图 5
我们不更改FC1的程序,只更改引脚。我们把gDB中的结构体复制到FC1的引脚中。

图 6
在OB1中把gDB中的结构体放到FC1中的引脚,其余都不改。然后下载程序,进行监控,我们发现这个错误已经得到修正,Flag1的值和编程者的意图一致了。
当输入输出引脚是结构体时,在调用程序块时引脚向程序块内部传输的是指针不是值。在结束FC1调用前,不会把FC1中的相关值赋值给引脚。所以也就不会复位Flag1了。这一段就是程序块引脚指针传输的原理。
另外,在程序块内对IN-OUT中的结构变量操作,是对引脚实际参数的直接操作。这种直接操作本质上就是利用指针对数据进行操作,所以称为引脚的指针传递。
好了,稍微有点绕。需要反复琢磨一下就明白了。
在IT的高级语言中同样存在着函数引脚的值传输和指针传输。比如在C#中,如果引脚增加关键字REF,那么这个引脚就是指针传输。但是在博途中,由于没有REF或者类似的关键字,所以很容易忽视程序块传输的到底是值还是指针。这也是为什么会给我带来这么大调试困扰的重要原因。因为虽然我知道这个知识点,但是出现问题时,并不会天然的想到是这个问题。如果博途日后的版本能把值传输还是指针传输显性化,那么碰到类似的问题就容易排查了。
总结:
这次调试的遭遇让我想起了一句老话,别拿豆包不当干粮。
伙伴们有啥问题,欢迎在此讨论。
关注我,会有更多技术博客在这里分享。