8.2  复制指令COP、同步复制指令CPS和充填指令FLL的编程

更新于 2026年10月10日 版权声明
8.2 复制指令COP、 同步复制指令CPS和充填指令FLL的编程

COP指令是常见的一条指令,用来搬运各种数据十分方便。很少有人留意到,这是一条针对存储器操作的指令,它将源标签的数据按照存储器的0或1的状态,原封不动地复制到目标标签,起始标签元素和指定的长度确定了参加复制的存储器区域,它传送的数据必须是连续的,不可挑选的。如图8-8所示是常见的COP指令的梯级逻辑编写。这里源标签和目标标签都是相同数据类型的数组,这种情况下,对COP指令长度的设置是没有疑问的。

图示

图8-8 常见的COP指令的梯级逻辑

基于对存储器的操作,COP指令操作既是适应性很强的数据传送,又是非常呆板的数据传送。适应性强指的是不同结构数据类型的数组标签都可以相互进行复制,呆板指的是只能将整块物理区域数据复制而无法挑选其中的部分数据。

由于可以在不同类型的数据之间进行复制操作,COP指令参数的Length项便很有说法。指令参数的含义是这样描述的:COP指令的长度指的是目标标签数组元素的个数,请注意是元素的个数,而不是存储器单元的数目,一个基本数据类型的标签或一个结构数据类型的标签都被称为一个元素。不同数据结构类型标签的数组耗用的存储器数据单元是不同的,而COP指令操作又是只认目标标签的数组元素个数的。请看一个不同数据类型数组标签之间的COP指令的运用实例,如图8-9所示。

这两条COP指令被编写在不同的地方,一条位于传送数据的例程,另一条位于接受数据的例程,它们的操作对象是相同的。前一条COP指令将5个计时器的结构数组标签复制到双整字数组标签,因为每个计时器的结构数据元素耗用3个双整字,所以必需15个双整字元素的存储单元才能存放,指令的长度要设为15,以目标标签为准。后面一条COP指令是还原的,对调了源标签和目标标签,将15个双整字数组元素的标签还原到5个计时器数组元素的标签,目标标签只有5个计时器结构数据元素,长度设为5才是正确的。如果没有了解COP指令的长度的含义,想当然地键入长度15,将造成错误,这种错误是常见的,这请不要忽视指令参数的含义。

图示

图8-9 目标数据标签的数据类型决定了长度

实际上,我们要传送的数据都是动态的数据,静态的数据是无须传送的,结构数据中总是同时包含了静态数据和动态数据,比如计时器结构数据标签的3个双整字,只有累加值ACC是变化的数据,是值得传送的,其他两个字预置值和状态量是不变或自变的设置数据,传送是没有意义的,无端地消耗了资源。只需要传送计时器结构数据标签的ACC值,如果使用COP指令来完成,显然是一种浪费,有2/3是无意义的数据传送。节省的做法是从结构数据中检索需要传送的子元素,集合在一起传送,这样的执行动作,COP指令是难以胜任的,最合适的是采用FAL指令来完成。关于FAL指令的运用将在下一个章节详细谈到,恰恰是这条指令,弥补了COP指令的不足。

一般来说,用COP指令传送结构类型数据标签,往往为了整片数据的复制,请留意长度的设置。

早期,关于COP指令的运用也有非常有趣的技巧性的做法。例如,数据采集堆栈存放的处理,在一个数据堆栈的数据区域中,每进入一个新的数据,之前已存放的旧数据块整体往前移动一个,不断地推陈出新,该怎么做呢,请看如图8-10所示的梯级逻辑。

这条COP指令将数组Array_Move_Buffer的元素1为首址的数据块,复制到同一个数组Array_Move_Buffer的元素0为首址的数据块,操作长度为20,这样的复制是含有覆盖的作用的。执行完毕的结果如图8-11所示。可以看到,相当于全体往前移动了一个数据单元,配合先执行的MOV指令,在元素20的字(第21个字)中事先装入新的数据,综合执行的结果是:装入一个新数据,并移动全部数据,推掉最前面一个数据,从而保持了最新采集的20个数据,注意被操作数组创建的长度要超过21个。

注意这里给定的梯级条件,只要Move位状态的梯级条件成立,COP指令每次扫描都会执行,所以要确保每逢Move位状态跳变一次才能执行一次COP指令,必须在梯级条件后面加上ONS指令给予限制。

如前所说,当梯级条件Move位状态跳变,MOV指令装载一个新数据,COP指令使得整体数据前移一个数据单元,如果使用一个具有定时功能的自复位计时器的完成位作为梯级条件,编写梯级逻辑如图8-12所示,数组的数据将自动地周期性移动,New_Data数据单元中的数据则提供填入的新数据。这里,由于我们了解自复位计时器完成位DN只会存在一个扫描周期,所以COP指令能确保只执行一次,此处不用ONS指令限制也是可以的。最后,将存放在缓存区的移动传送成功的数组,一次性复制到最终标签Array_Move数组。

这种做法很像后面要讨论的堆栈数组的处理,也许堆栈指令FFU的宏内部操作就含有COP指令这样的执行过程。早期的可编程产品没有堆栈数组处理的指令,要满足这样的需求,就是采用这个办法,这也算得上编程技巧的处理了。

图示(https://www.daowen.com)

图8-10 堆栈式复制的梯级逻辑

图示

图8-11 COP指令执行完毕的结果

CPS指令的用法和COP指令是一样的,惟一不同的是执行过程,这是一条优先级别很高的指令,控制器中的其他任务执行触发都不能中断这条指令的执行。一般的COP指令在执行的时候,都是一个字一个字的传送,有可能在传送的操作过程中被优先权更高的执行动作中断,等恢复操作接着传送数据时,也许源标签的数据已经被刷新了,后面一段传送的则是刷新后的数据。这就出现了COP指令执行完后,数据块的数据是新旧混杂的情况。

特别是当外部数据传送到内部数据,数据长度大过32位,也就是多于一个基本数据单元时,由于背板CPU的优先权高过逻辑CPU,背板CPU完全可能在COP指令执行过程中请求数据的刷新,因而新旧数据混杂的可能性非常大,强烈建议使用CPS指令,这样在数据复制过程中不会被背板CPU数据刷新的请求中断。如图8-13所示是Consumed数据的传送,使用CPS指令有效地防止了在同一个数据块中新旧数据的混杂。

图示

图8-12 数组周期性移动的梯级逻辑

图示

图8-13 Consumed数据的传送使用CPS指令

总之,只要数据复制是在不宜中断操作之处,有新旧数据混杂风险的情况都建议使用CPS指令。

前面,我们曾经提到用CLR指令清除一个字,如果需要清除一个数组,则可以采用充填指令FLL来完成。FLL指令的执行可将一个数据单元或一个立即数充填到整个数组,通常用来实现数据的初始化。如图8-14所示编写的梯级逻辑是对一个数组的清零,将一个为0的立即数充填到数组目标标签,一般情况下,这个数组可能是一个操作过程的中间状态量的集合,在程序运行之初,要将上次运行的所有的残留状态清除。有现场调试经验的工程师都非常清楚前次残留状态对系统当前运行的影响,而不敢掉以轻心,这些现象是不定的,有时测试几次都不会暴露,为了不留下隐患,所以宁愿多费些功夫要清除,以免系统出现不可预料的结果。

图示

图8-14 对一个数组的清零

在有的实用例程中你可能看到,FLL指令所做的不是对一个数组清零,而是对所有的字赋予一个1,编写梯级逻辑如图8-15所示。这是一种初始化数据的方式,有时是为SFC的步操作设置初始步,前一次运行停止,SFC或许停止在中间的某一步,这也是残留状态,这个初始化的操作令每个SFC流程都回到初始步。有关初始步的问题我们在SFC编程中再详细讨论。

图示

图8-15 初始化数据

总而言之,充填指令FLL的使用较多情况是完成初始化的操作,我们常常可以在初始化例程中看到这条指令。

FLL指令跟COP指令有相似之处,虽然操作对象是数组,也要指明长度,但是没有指定一个专用于数组操作的控制结构数据标签,也没有指令执行过程的各个状态位。它们的操作是对数组的连续操作,没有停顿和指针位置,它们更像是数组的数据传送。关于控制结构数据标签,后面章节的内容马上就要用到了。

↑上一章 ↓下一章
关注公众号获取验证码
复制内容需要验证码(7.99元/天)