|
本帖最后由 oldbeginner 于 2014-1-1 22:02 编辑
在前面的学习中,都是本着有什么指令就看相应代码的思路,作为入门,效果是不错的。
其实,随着对代码的熟悉,就不经好奇他们是怎样组织的,逻辑关系是怎样建立的。
网上搜一下,软PLC编译系统的开发与实现
http://www.eepw.com.cn/article/163110_3.htm
虽然不够详细,但是已经可以入门了(我直接跳过了编译这些内容,编译原理对我来说太抽象了,虽然词法分析学了一些,非常不喜欢编译原理的表达方式)
这篇文章介绍了如何组织下面指令的实现
LD X000
AND X001
OR X002
ANI X003
OR X004
OUT Y000
在开源PLC中,执行顺序
void LD (void)
{ ACC_BIT <<= 1;
ACC_BIT |= RD_ppp(0x400);
}
void AND (void)
{ ACC_BIT &= (RD_ppp(0x401) | 0xfe);
}
void OR (void)
{ ACC_BIT |= RD_ppp(0x402);
}
void ANI (void)
{ ACC_BIT &= (~RD_ppp(0x403) | 0xfe);
}
void OR (void)
{ ACC_BIT |= RD_ppp(0x404);
}
void OUTYM (void)
{ WR_YM(0x500 , ACC_BIT);
}
直接把要理解的内容摘录如下:
*******************************************
PLC指令表程序的分析是通过对指令表程序的解释而获得程序的逻辑,并以对话框形式演示程序的逻辑状态。在解释过程中,构造2个变量,1个用于存储分支块的逻辑值,另1个用于存储分支块前面语句的逻辑值。同时构造1个堆栈用来存储解释过程中的结果,分支块前面的值保存在堆栈中,整个分支块的值保存在1个临时变量中。
PLC指令表程序的解释过程为:
1)当解释程序 发现LD或LDI指令时,将临时变量值压入堆栈,临时变量赋值为1,临时变量与指令后面的元素进行逻辑与操作,将结果保存在临时变量中;
2)当解释程序发 现AND或ANI指令时,临时变量与指令后的元素进行与操作,将结果保存到临时变量中;
3)当解释程序发现OR或ORI指令时,l临时变量与指令后的元素 进行或操作,将结果保存到临时变量中;
4)当解释程序发现ANB指令时,临时变量与栈顶的值进行与操作,将结果保存到临时变量中,同时堆栈将栈顶元素弹 出;
5)当解释程序发现ORB指令时,临时变量与栈顶的值进行或操作,将结果保存到临时变量中,同时堆栈将栈顶元素弹出;
6)当解释程序发现OUT指令 时,将临时变量与栈顶的值进行与操作,将结果保存在临时变量中。同时清空堆栈。
***********************************************
然后和开源PLC的代码进行对比理解,
临时变量就是ACC_BIT。
指令后面的元素就是一个函数,RD_ppp(unsigned int a)
************************************************
2)当解释程序发 现AND或ANI指令时,临时变量与指令后的元素进行与操作,将结果保存到临时变量中;
void AND (void) // 4 (AND指令, 4000+ppp, 扩展 Mp除外)
{
ACC_BIT &= (RD_ppp(ppp) | 0xfe);
}
***************************************************
3)当解释程序发现OR或ORI指令时,l临时变量与指令后的元素 进行或操作,将结果保存到临时变量中;
void OR (void) // 6 (OR指令, 6000+ppp, 扩展 Mp除外)
{
ACC_BIT |= RD_ppp(ppp);
}
****************************************************
1)当解释程序 发现LD或LDI指令时,将临时变量值压入堆栈,临时变量赋值为1,临时变量与指令后面的元素进行逻辑与操作,将结果保存在临时变量中;
void LD (void) // 2 (LD指令, 2000+ppp, 扩展 Mp除外)
{
ACC_BIT <<= 1;
ACC_BIT |= RD_ppp(ppp);
}
这里实现方式不太一致,但是结果是一样的。
*****************************************************
6)当解释程序发现OUT指令 时,将临时变量与栈顶的值进行与操作,将结果保存在临时变量中。同时清空堆栈。
void OUTYM (void) // C (OUT指令, C000+ppp, 仅对Y,M有效)
{
WR_YM(ppp,ACC_BIT);
}
*****************************************************
上面的结构理解后,再来具体理解如何实现
指令后面的元素就是一个函数,RD_ppp(unsigned int a)
unsigned char RD_ppp(unsigned int a) // (读入点内容)
{
unsigned char n;
unsigned char *p;
p = ADDR_int_ppp(a);
n = *p >> (a % 8);
return(n & 0x01);
}
char* ADDR_int_ppp(unsigned int a) // (读入int点内容,返回地址绝对指针)
{ unsigned char *p;
a &= 0xfff;
if (a<0x400)
{ if (a < _S_num)
{ p = (unsigned char*)rS + (a / 8);
}
}
else if (a<0x500)
{ a -= 0x400;
if (a < _X_num)
{ p = (unsigned char*)rX + (a / 8);
}
}
else if (a<0x600)
{ a -= 0x500;
if (a < _Y_num)
{ p = (unsigned char*)rY + (a / 8);
}
}
else if (a<0x800)
{ a -= 0x600;
if (a < _T_num)
{ p = (unsigned char*)rT + (a / 8);
}
}
else if (a<0xe00)
{ a -= 0x800;
if (a < _M_num)
{ p = (unsigned char*)rM + (a / 8);
}
}
else if (a<0xf00)
{ a -= 0xe00;
if (a < _C_num)
{ p = (unsigned char*)rC + (a / 8);
}
}
else
{ a -= 0xf00;
if (a < _M8xxx_num)
{ p = (unsigned char*)rM8xxx + (a / 8);
}
}
return(p);
}
上面的两个函数在笔记19中理解过一次,复习一下。
********************************************
最后,就是理解输出
6)当解释程序发现OUT指令 时,将临时变量与栈顶的值进行与操作,将结果保存在临时变量中。同时清空堆栈。
void WR_YM(unsigned int a,unsigned char i) // (写入Y,M点内容)
{ unsigned char *p;
a &= 0xfff;
i &= 0x01;
if ((a>=0x500) && (a<0x600))
{
a -= 0x500;
if (a < _Y_num)
{ p = (unsigned char*)rY + (a / 8);
if (i == 0) *p &= ~(1 << (a % 8));
else *p |= 1 << (a % 8);
}
}
else if ((a>=0x800) && (a<0xe00))
{
a -= 0x800;
if (a < _M_num)
{ p = (unsigned char*)rM + (a / 8);
if (i == 0) *p &= ~(1 << (a % 8));
else *p |= 1 << (a % 8);
}
}
else ;
}
开源PLC中,OUT指令没有清空堆栈,这样也可以理解为为什么
在LD指令中,有ACC_BIT <<= 1;
ACC_BIT <<= 1;执行效果,低位为0,相当于清空。
所以开源PLC中设计 LDx指令时,第一句都是ACC_BIT <<= 1,即清空堆栈。
这样OUT命令就不需要再清空堆栈了。
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
阿莫论坛20周年了!感谢大家的支持与爱护!!
曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……
|