通常PLC指令较多,各种不同型号的PLC指令也各不相同。但用于逻辑量处理的指令并不多,各种型号的PLC此类指令也是大同小异。表1列出了某型PLC常用的指令及其含义。
表1 梯形图指令及其功能描述<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> | |||
指令助记符 | 功 能 描 述 | 指令助记符 | 功 能 描 述 |
LD | 使常开触点与左母线相连 | OUT | 线圈驱动指令 |
LDI | 使常闭触点与左母线相连 | SET | 线圈动作保持指令 |
LDP | 上升沿检出运算开始 | RST | 解除线圈动作保持指令 |
LDF | 下降沿检出运算开始 | PLS | 线圈上升沿输出指令 |
AND | 继电器常开触点与其他继电器触点串联 | PLF | 线圈下降沿输出指令 |
ANI | 继电器常闭触点与其他继电器触点串联 | MC | 公共串联接点用线圈指令 |
ANDP | 继电器常开触点闭合瞬间与前面的触点串联一个扫描周期 | MCR | 公共串联接点解除指令 |
ANDF | 继电器常开触点断开瞬间与前面的触点串联一个扫描周期 | MPS | 运算存储 |
OR | 继电器常开触点与其他继电器触点并联 | MRD | 存储读出 |
ORI | 继电器常闭触点与其他继电器触点并联 | MPP | 存储读出和复位 |
ORP | 继电器常开触点闭合瞬间与前面的触点并联一个扫描周期 | INV | 运算结果取反 |
ORF | 继电器常开触点断开瞬间与前面的触点并联一个扫描周期 | NOP | 无动作 |
ANB | 电路块之间串联 | END | 程序结束 |
ORB | 电路块之间并联 |
|
|
本书不对PLC指令详细说明,读者如对这些指令的用法有疑问,可以找PLC教材阅读。
要使用梯形图的方式来编写单片机程序,关键是将梯形图转化为单片机可以识读的HEX格式文件。通常梯形图是用一些专用软件在PC机上绘制,如图1所示是一个简单的梯形图。
<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />
图1 梯形图例子
如果希望采用这种图形化方式来编程,就需要自行开发可以绘制梯形图的软件,限于条件,这种方案不考虑。
自行开发图形编程软件不考虑,那么就要对梯形图进一步进行分析。对应于梯形图,最终会形成一个指令表,如图1的梯形图,可以转换为如下所示的指令表:
LD X000
OUT C1 K5
LD C1
SET Y002
LD X001
RST C1
LD X002
RST Y002
END
这是一种文本格式,文本处理较之图形要容易一些。因此,可以考虑编写一段程序,读入每一条指令,然后对其进行解释以直接生成HEX格式文件或者生成C语言源程序,最后编译、链接生成HEX格式文件。这种方式较之上述直接编写图形化软件要方便一些,但仍不是最佳选择。
绘制好的梯形图最终必须保存为文件,对文件分析可以得到诸多有用的信息。因此接下来对各条指令在文件中的保存情况进行分析,以找到每条指令与其保存代码之间的关系。
LD类指令功能是使常开触点与左母线连接。LD指令的操作元件可以是输入继电器X、输出继电器Y、辅助继电器M、状态继电器S、定时器T和计数器C中的任何一个。下面首先分析LD S类指令。
ld s* (*的取值从000~999)
这是取状态继电器s触点的指令,s元件的个数最多为1000个,当PLC编程指令为:
Ld s0
时,其对应的二进制代码为:
00 20
而当PLC指令为:
LD S999
时,其对应的代码为:
0xE7 0x23
不难看出,,代码中第一个数是元件号,第二个数是命令码,可以用来区分是哪一条指令。
接下来再分析LD X类指令
指令:ld x* (*的取值从0~255,用8进制表示)
经过分析,其代码为:* 24,即当指令为:LD X0时,其对应的代码为:00 24,而当指令为:LD X377(377为8进制数,相当于十进制的255)时,其对应的代码为:00 FF。
分析了这两条指令,下面的指令都是类似的,就不再一一分析了,表2给出了指令与其代码之间的对应关系。
表2 Ld类指令与代码的关系 | ||||||
指令 | Ld s* | Ld X* | Ld Y* | Ld T* | Ld M* | Ld C* |
代码 | 00 20 ~ e7 23 | 00 24~ff 24 | 00 25~ff 25 | 00 26~ff 26 | 00 28~ff 2d | 00 2e~ff 2e |
说明:S*中的*取值为000~999,M*中的*取值为 0000~1536,其余*的取值为0~255。在书写指令时,X和Y元件中的*用八进制书写,其他元件中的*均用十进制书写。如用指令格式书写PLC程序时,不会出现LD X8或LD Y9之类的指令,因为对X或Y操作时,其后数字必须是八进制。
可以出现
LD S8或
LD M9
之类的指令,因为这些操作数均用十进制表示。表格中代码所在行中的数均为16进制数,为简单起见,未加前缀0x。
LDI类指令称之为“取反指令”,其功能是使常闭触点与左母线连接。LDI指令的操作元件与LD类指令相同。其指令与代码关系如表3所示。
表3 LdI类指令与代码的关系 | ||||||
指令 | Ld s* | Ld X* | Ld Y* | Ld T* | Ld M* | Ld C* |
代码 | 00 30 ~ e7 33 | 00 34~ff 34 | 00 35~ff 35 | 00 36~ff 36 | 00 38~ff 3d | 00 3e~ff 3e |
说明:S*中的*取值为000~999,M*中的*取值为 0000~1536,其余*的取值为0~255。在写指令时,X*,Y*中的*用八进制写法,其他均用十进制写法。表格中代码所在行中的数均为16进制数,为简单起见,未加前缀0x。
......更多的指令可以根据这里的提示自行分析
PLC的工作过程是一个不断循环扫描的过程。每一次扫描过程包括:输入采样、程序执行和输出刷新三个阶段,如图2所示。
图2 PLC程序的工作过程示意图
(1)输入采样阶段: PLC在输入采样阶段,首先扫描所有输入端,并将各输入端的状态存入对应的输入映像寄存器中。当输入映像寄存器被刷新后,进入程序执行阶段。
(2)程序执行阶段:不论梯形图如何画,是否有分支、块等,最终得到的指令序列是一个一维的指令序列。PLC按顺序逐句扫描执行程序。当指令中涉及输入状态时,CPU从输入映像寄存器中读取输入状态,而不是直接去读输入端的状态。当指令需要输出时,CPU将待输出的数据送到输出映像寄存器,而不是直接进行输出。当指行到结束指令END时,结束程序执行阶段,进入输出刷新阶段。
(3)输出刷新阶段:当用户程序执行结束后,输出映像寄存器中所有输出继电器的状态,在输出刷新阶段转存到输出锁存器中,并最终驱动执行机构(晶体管、继电器等动作)。
输出刷新阶段完成后,转到输入采样又开始下一轮循环,只要PLC不断电,这个循环就会一直不停地工作下去。
使用单片机来处理PLC程序时,也必须按照这一工作过程来进行,如图3所示是这个处理程序的总流程示意图。
图3 主程序流程图
程序开始运行后,首先对内存、定时器、I/O口等进行初始化;然后读入X元件值,即输入采样;随后进入逐条取指令-执行指令的阶段,每取一条指令先判断该指令是否是END指令,如果不是则要对每一条指令进行分析判断并执行,执行完一条指令后转去取下一条指令并分析执行,如此循环不断;当取到END指令时,进入输出刷新阶段,将Y值通过I/O口输出。
从前面的分析可以看出,每一条指令都有其特定的操作码,因此,对操作码进行判断就能知道这条指令要做的工作;除了诸如NOP、END等少数指令外,大部分指令都用操作数表示了操作对象、参数等特性。因此,编程时先找出操作码,然后通过一个switch语句来区分,根据指令的用途查找这条指令的操码,并且最终完成这条指令的操作。
根据这样的思路,写出了程序,下面是指令处理的部分程序:
for(;;)
{ InPut(); //读输入数据,即输入采样。
/*从这里开始一次读指令-处理指令的过程,直到所有指令执行完毕,遇到结束指令END,才能退出这个无限循环*/
for(i=0;;) //i的增加由循环内部控制
{
pCode[0]=Code;
i++;
pCode[1]=Code; //从指令数组中读出2字节的指令
i++;
if((pCode[0]==0x0f)&&(pCode[1]==0x00)) //结束指令
break;
switch (pCode[1]) //对指令进行分析和执行
{
……
}
OutPut(); //输出
}
程序分析:InPut()函数用于输入采样,这一阶段完成以后,接下来就是程序执行阶段,这实际上是一个“读取指令-执行指令”的过程,这里采用了一个无限循环来完成这一过程,而退出这个无限循环则通过在循环体内通过判断是否遇到结束指令来实现。
本段程序用于测试梯形图指令,因此用了一种简单的方法,直接将有关梯形图指令代码放在数组中。例如有这样的一段PLC程序:
指令 | 代码 | 说明 |
ld x0 | 0x00 0x24 | 取x0的状态 |
out c0 k10 | 0x00 0x0e 0x0a 0x80 0x00 0x80 | 设置计数器C0,计数值为10 |
ld c0 | 0x00 0x2e | 取计数器c0的输出触点 |
out y0 | 0x00 0xc5 | 驱动Y0输出 |
ld x1 | 0x01 0x24 | 取X1的状态 |
rst c0 | 0x0c 0x00 0x00 0x00 0x8e | 复位C0 |
end | 0x0f 0x0 | 结束 |
要将这段程序让单片机处理,那么就在程序中定义了这样一个数组:
uchar Code[]=
{0x00,0x24,0x00,0x0e,0x0a,0x80,0x00,0x80,0x00,0x2e,0x00,0xc5,0x01,0x24,0x0c,0x00,0x00,0x8e,0x0f,0x00};
这样就描述了这段PLC程序。
读取指令时,将指令代码读入两个变量pCode[0]和pCode[1]中,同时将指针加1,指向下一条指令。如第1条指令代码00 24其中24就是操作码,而00就是操作数。
随后就要判断这条指令是否是结束指令了,程序通过下面的程序行来判断:
if((pCode[0]==0x0f)&&(pCode[1]==0x00)) //结束指令
break;
一段PLC程序的所有有效指令执行结束时,将遇到“结束”指令,在for(i=0;;)这个循环中,判断出现了“结束”指令,就将执行break指令,结束循环。接下来执行OutPut函数,将本段指令执行过程中得到的结果集中输出,并再次回到for(;;)大循环中去,进行下一轮循环,这种循环将一直持续不断地进行直到断电为止。
文章评论(0条评论)
登录后参与讨论