搜索
bottom↓
回复: 9

开源PLC学习笔记21(入门PLC简单算法在51上的实现)——2014_1_1

[复制链接]

出0入0汤圆

发表于 2014-1-1 12:47:02 | 显示全部楼层 |阅读模式
本帖最后由 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周年了!感谢大家的支持与爱护!!

如果想吃一顿饺子,就得从冰箱里取出肉,剁馅儿,倒面粉、揉面、醒面,擀成皮儿,下锅……
一整个繁琐流程,就是为了出锅时那一嘴滚烫流油的热饺子。

如果这个过程,禁不住饿,零食下肚了,饺子出锅时也就不香了……《非诚勿扰3》

出0入0汤圆

发表于 2014-1-1 13:31:31 | 显示全部楼层
谢谢分享。支持!!!                                                                                                                              

出0入0汤圆

发表于 2014-1-1 15:04:55 | 显示全部楼层
板凳  学习

出0入4汤圆

发表于 2014-3-6 19:13:47 | 显示全部楼层
谢谢老初的详细讲解!
自称老初,你谦虚了  

出0入0汤圆

发表于 2014-3-17 17:27:23 | 显示全部楼层
值得学习

出0入0汤圆

发表于 2014-3-26 11:16:46 | 显示全部楼层
收藏下 谢谢

出0入0汤圆

发表于 2015-6-17 08:31:58 | 显示全部楼层
有兴趣开发PLC的,请加我QQ:611017112,相互交流

出0入4汤圆

发表于 2015-7-5 17:35:56 | 显示全部楼层
mark                  

出95入8汤圆

发表于 2015-7-6 14:14:40 | 显示全部楼层
这个楼非常好

出0入0汤圆

发表于 2016-2-28 23:47:43 | 显示全部楼层
学习,有空慢慢看
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-28 22:28

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表