搜索
bottom↓
回复: 16

开源PLC学习笔记07(再从51开始 PLC指令)——2013_11_12

[复制链接]

出0入0汤圆

发表于 2013-11-12 16:37:30 | 显示全部楼层 |阅读模式
本帖最后由 oldbeginner 于 2013-11-12 16:43 编辑

PLC的结构对我来说还是比较复杂的,前后逻辑联系非常紧密,幸亏提前有思想准备,否则早就半途而废了。


FX1NPrcosessing函数链接上位机和下位机之间的核心(因为IAP缘故,该函数像中间机)。

这节笔记将理解PLC指令,为了减小难度,只理解LD OUT SET RST END,元件只涉及x和y。然后理解这些指令是如何在单片机内部实现的。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-11-12 21:53:53 | 显示全部楼层
楼主加油呀,分享一下

出0入0汤圆

 楼主| 发表于 2013-11-13 13:08:18 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-11-13 14:54 编辑


首先是理解指令,参考资料是http://www.amobbs.com/thread-3303497-1-1.html

这个参考资料内容还是比较多,因为我暂时只想理解LD OUT SET RST END X Y,所以我只摘录相关的内容。
参考资料分了5部分来介绍,我只用前4部分。

一、标注说明:
注1:下列未特别标注者,均为十六进制数格式。
注2:p 为寄存器名称编号,pp 为32位寄存器名称编号,表示为十进制数,但在存储格式中以十六进制数 ppp 表示。
注7:X,Y为寄存器位(点)元件名称(包含地址)。

二、寄存器位(点)元件地址 ppp = 0-FFF,其分布如下:
  X0-377(八进制) =  (0-FF)   + 400  =  400-4FF
  Y0-377(八进制) =  (0-FF)   + 500  =  500-5FF

三、存储格式及报文发送格式说明:

存储格式为字型,低在先,高在后(如指令END,指令码为00 0F,存储为0F 00。指令LD  X002,指令码为24 02,存储为02 24)。
在报文发送时,以字为单位传送,低字节在先。在字节传送过程中,高位在先,低位在后,转换成 ASCII 码 后传送(即以存储格式传送)。
指令LD  X002,指令码为24 02,存储格式为02 24,转换成 ASCII 码传送为30 32 32 34。
注:报文发送方式未经上机验证!

四、三菱指令格式说明:

2、三菱指令命名: LD  = 2                 
                OUT = C (仅对Y,M有效)
                SET = D (仅对Y,M有效)
                RST = E (仅对Y,M有效)
3、三菱基本指令分类:
纯单字指令
                    END    000F
单字指令
                    LD     2000+ppp           ;(扩展 Mp除外)
                    OUT    C000+ppp           ;(仅对Y,M有效)
                    SET    D000+ppp           ;(仅对Y,M有效)
                    RST    E000+ppp           ;(仅对Y,M有效)
5、三菱应用指令中的数据/地址格式:

16数据/地址格式   = 8m00+xx ,8n00+yy

***************************************
确实有些抽象,这时候结合代码一起了解。

当FX1NProcessing函数把三菱上位机程序写到PLC程序空间后,运行正常后,就进入了
//   PLC 入口 函数  main_PLC();                                                        //
//------------------------------PLC51x.c----------------------------------------------//

void main_PLC (void)
{
  CODE_p = (unsigned char code *)CODE_START;
  Pi = 0x01;
  do{
      orderL = *CODE_p;
      CODE_p++;
      orderH = *CODE_p;
      CODE_p++;
      ppp = order & 0xfff;
      (*key_list[orderH >> 4])();
    } while((CODE_p < CODE_END) && (CODE_p != CODE_START));
}

就是上面这个函数把FLASH中PLC程序空间的数据翻译成相应的函数并执行。

这个函数虽然代码不长,但其实还是比较复杂的。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-11-13 14:39:18 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-11-13 15:07 编辑
oldbeginner 发表于 2013-11-13 13:08
首先是理解指令,参考资料是http://www.amobbs.com/thread-3303497-1-1.html

这个参考资料内容还是比较 ...


映射表有4种,
code (*key_list[16])();
code (*key_list_1[12][2])();
code (*key_list_2[14][2])();
code (*key_list_3[256])();

因为我打算实现的功能简单,所以只接触第一种最简单的映射,而且16个元素只会用到4个。
code (*key_list[16])()={
          CMDFNC ,   // 0 (FNC应用指令)   
          CMDP   ,   // 1 (P 应用指令)  
          LD     ,   // 2 (LD指令, 5000+ppp, 扩展 Mp除外)  
          LDI    ,   // 3 (LDI指令, 5000+ppp, 扩展 Mp除外)   
          AND    ,   // 4 (AND指令, 5000+ppp, 扩展 Mp除外)   
          ANI    ,   // 5 (ANI指令, 5000+ppp, 扩展 Mp除外)   
          OR     ,   // 6 (OR指令, 5000+ppp, 扩展 Mp除外)   
          ORI    ,   // 7 (ORI指令, 5000+ppp, 扩展 Mp除外)   
          CMDER  ,   // 8 (多字指令,第二字及以后有效)
          CMDER  ,   // 9  
          CMDER  ,   // A (多字指令,第二字及以后有效, 仅对M1536-M3071有效,需加偏移量200)  
          CMDER  ,   // B (Pn指令, 仅对CJ,CALL有效)
          OUTYM  ,   // C (OUT指令, 仅对Y,M有效)
          SETYM  ,   // D (SET指令, 仅对Y,M有效)
          RSTYM  ,   // E (RST指令, 仅对Y,M有效)

          CMDCH };   // F (纯单字指令)  

为什么用code,
1,把段码放在code里,是为了节省RAM。如果放在前256字节内,查表时只要八位地址即可,所以会快些。ROM读取不会慢。单片机执行的每一条指令都是从ROM区读取的。
ROM区的内容是只读的,所以你不能将改变(程序运行中改变)的数组放进去。http://zhidao.baidu.com/question ... l=relate_question_1

不理解为什么没有unsigned int或unsigned char搭配code。先继续。
*****************************************************************
先看一下代码中的定义
#define   CODE_START   0x8000         // PLC执行代码缓冲区首地址

但是另外还有一个
  unsigned char code CODE_START[PLCSTEP*4] = {0x0,0x24,0x8,0x0,0x0,0x88,0x0,0x28,0x0,0xd5,0x1,0x24,0x9,
  0x0,0x1,0x88,0x1,0x28,0x0,0xe5,0xca,0x1,0x2,0x84,0x2,0xc8,0x2,0x28,0x1,0xd5,0xcb,0x1,0x2,0x84,0x3,
  0xc8,0x3,0x28,0x1,0xe5,0x0f,0x0
                            };       
两者是什么关系?怎样联系?先继续

volatile unsigned char code *CODE_p;
1、然后理解第1句代码,
CODE_p = (unsigned char code *)CODE_START;
就是让CODE_p指向PLC执行代码缓冲区

2、第2句,  Pi = 0x01;
volatile unsigned int  data  Pi;   
因为Pi是16位的数,所以Pi=0x0001,作用应该在下面了解。

3、最后是do while循环。先看循环体,最后看循环条件。
循环体
      orderL = *CODE_p;
          CODE_p++;
      orderH = *CODE_p;
          CODE_p++;
      ppp = order & 0xfff;
      (*key_list[orderH >> 4])();

4、看定义
#define   orderL    order0.BYTES.BYTEL    // 命令位地址缓冲区低位
#define   orderH    order0.BYTES.BYTEH    // 命令位地址缓冲区高位

还要再看order0的定义
volatile TYPE_BYTES_WORD  xdata order0;       

还要再看TYPE_BYTES_WORD的定义
typedef union {     //重点注意C编辑器的多字节变量类型的高低字节前后排列次序
  struct { unsigned char BYTEH;   //可以按字节寻址
           unsigned char BYTEL;   //可以按字节寻址
         }BYTES;
  unsigned int  WORD;       //可以按字寻址
}TYPE_BYTES_WORD;  //定义一个既能按字节寻址也可按字寻址的新变量类型

类似笔记02中的联合体,幸亏当时理解了,现在难度就减小了。

用同样的方法来理解,
unsigned char   orderL
unsigned char   orderH
将orderL和orderH都理解位8位无符号数,他俩又可以组成一个字。

5、   orderL = *CODE_p;
          CODE_p++;
      orderH = *CODE_p;
          CODE_p++;
很好理解,利用CODE_p把PLC缓冲区的内容赋值给orderL和orderH。

6、      ppp = order & 0xfff;
先看定义
volatile unsigned int ppp;
#define   order     order0.WORD           // 命令位地址缓冲区

order就是由orderL和orderH合并而成的,根据一、标注说明:注释2pp 为32位寄存器名称编号,表示为十进制数,但在存储格式中以十六进制数 ppp 表示。
所以把order和0xfff与一下,然后赋值给ppp。

实例,指令LD  X002,指令码为24 02,存储格式为02 24,转换成 ASCII 码传送为30 32 32 34。
则orderL=0x30
  orderH=0x32
利用联合体概念, order=0x3230,则ppp=0x0230。


7、 (*key_list[orderH >> 4])();
利用实例来理解,orderH向右移4位,则orderH=0x03,这里不是很理解。如果向左移,才有意义。这样orderH=0x02
然后*key_list[orderH >> 4]=*key_list[2]=LD。

此时调用了LD函数,下一步再理解LD函数,目前只需了解通过映射表,LD函数被调用了。
void LD     (void)    // 2  (LD指令, 2000+ppp, 扩展 Mp除外)
{ ACC_BIT <<= 1;
  ACC_BIT |= RD_ppp(ppp);
}

8、最后是循环条件while((CODE_p < CODE_END) && (CODE_p != CODE_START));
根据字面意思就很好理解,不是开头和并且未到末尾就待在循环体中。

***************************************************


出0入0汤圆

发表于 2013-11-13 15:00:01 | 显示全部楼层
支持楼主!

出0入0汤圆

发表于 2013-11-13 15:07:42 | 显示全部楼层
楼主会因为PLC学习笔记一炮而红。

出0入0汤圆

 楼主| 发表于 2013-11-13 16:08:47 | 显示全部楼层
oldbeginner 发表于 2013-11-13 14:39
映射表有4种,
code (*key_list[16])();
code (*key_list_1[12][2])();

再回到指令函数中
void LD     (void)    // 2  (LD指令, 2000+ppp, 扩展 Mp除外)
{
ACC_BIT <<= 1;
  ACC_BIT |= RD_ppp(ppp);
}
和笔记01中的不同了。

多了一个RD_ppp(ppp);

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);
}

又调用了一个很长的函数
//    函数名称:   ADDR_int_ppp                                                             //
//    函数类型:   char* ADDR_int_ppp(unsigned int a)                                       //
//                                                                                                                           //
//    功能描述:  读入并列的n位软元件点的起始地址,返回地址绝对指针                        //
//                                                                                                                           //
//    入口参数:  unsigned int  a ...... 读入软元件点的起始地址                            //
//                                                                                                                           //
//    出口参数:  返回地址绝对指针  

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);
        }
    }
。。。。。。。
  return(p);
}

查看定义
#define   _X_num      48    // 48个输入端口,    编号:X0-X57
#define   _Y_num      48    // 48个输出端口,    编号:Y0-Y57

volatile TYPE_BIT_BYTE   data  rX[_X_BYTE]
#define   _X_BYTE     (_X_num + 7) / 8    // 48个输入端口,所占内存字节数

rX就是笔记01中rX,在笔记01中把rX理解为结构体
现在同样 结构体 rX[6]

取一段来理解,a就是ppp,寄存器名称,16进制表示
。。。。。。
else if (a<0x500)
    {
       a -= 0x400;
      if (a < 48)
        {
           p = (unsigned char*)rX + (a / 8);
        }
    }
。。。。。。
  return(p);

1、a=a-0x400,暂时不理解,可能和下列注释有关
                //        编写读写代码须知
                //    1.8000 以上地址与 PLC程序相关;
                //    2.4000以上地址与 PLC寄存器[数据寄存器0x4000~0x7e40,31.125K (HEX).特殊寄存器0x0e00~0x0ec0,384字节 (HEX)]相关

2、如果a<48,因为设定的x端口只有48个。

3、假设a=40,则 p为 rX[5]的地址,a/8是用来分组的。

4、返回该地址。

******************************
再进入
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);
}

此时 p指向rX[5]
然后,
n=rX[5]>>0,即n=rX[5]
然后返回 rX[5] & 0000 0001。

*********************************
再进入
void LD     (void)    // 2  (LD指令, 2000+ppp, 扩展 Mp除外)
{
  ACC_BIT <<= 1;
  ACC_BIT |= RD_ppp(ppp);
}

ACC_BIT<<=1确保最低位为0,
然后
ACC_BIT=rX[5] & 0000 0001

***********************************


继续采用笔记01中的方法,联合LD和OUT一起理解


void OUTYM  (void)    // C (OUT指令, C000+ppp, 仅对Y,M有效)
{
    WR_YM(ppp,ACC_BIT);
}
相当于
WR_YM(ppp, rX[5] & 0000 0001);

还需要了解
//    函数名称:   WR_YM                                                                    //
//    函数类型:   void WR_YM(unsigned int a,unsigned char i)                               //
//                                                                                           //
//    功能描述:  将1点软元件的内容写入Y,M存储器                                           //
//                                                                                           //
//    入口参数:  unsigned int  a ...... 读入软元件点的起始地址   
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 ;
}

判断条件暂时都忽略,只看
             if (i == 0)   
                    *p &= ~(1 << (a % 8));
               else  
                     *p |= 1 << (a % 8);
在这里,i就是ACC_BIT,而a是输出Y的端口号,例如0。

如果ACC_BIT=1,即x40闭合,此时 *p的最低位为1,即端口y0是1。

还是有些绕,暂时对这个复杂过程有个初步理解。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-11-13 20:36:03 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-11-13 20:54 编辑
oldbeginner 发表于 2013-11-13 16:08
再回到指令函数中
void LD     (void)    // 2  (LD指令, 2000+ppp, 扩展 Mp除外)
{

ADDR_int_ppp  函数和WR_YM 都和寄存器位置相关,

现在需要理解一下再继续,

在参考资料,http://www.amobbs.com/thread-3303497-1-1.html
二、寄存器位(点)元件地址 ppp = 0-FFF,其分布如下:

    寄存器名称   =  位地址 + 偏移量 = 实际地址ppp
  S0-1023        =  (0-3FF)        =   0-3FF
  X0-377(八进制) =  (0-FF)   + 400  =  400-4FF
  Y0-377(八进制) =  (0-FF)   + 500  =  500-5FF

x输入端最多有256个,用x0~x255表示,用16进制的地址映射就是, 400-4FF。
所以在计算具体端口时,要先减去0x400。下面的计数中,a是形参,实际的x寄存器名代入,
a -= 0x400;      现在可以理解,减去偏移量,得到位地址。
  然后,    if (a < 48)
        {
           p = (unsigned char*)rX + (a / 8);
        }
也可以理解了,因为48是定义时已经制定了的最大端口数,所以a必须小于48。
把rX理解为8位bit组成的结构数组,利用a/8+rX找到该位的具体地址。

y输出端也是同理理解,所以在WR_YM中,会有
a -= 0x500;

四、三菱指令格式说明:
单字指令
                    LD     2000+ppp           ;(扩展 Mp除外)
例如计算 LD X02的命令格式
首先,X02的地址=0x02+0x400=0x402

然后 LD X02就是    0x2000+0x402=0x2402


最后把16进制转换成ascii码,
16进制的命令格式        0x    2    4    0     2
转换为ascii命令格式             ‘2’   ‘4’   ‘0’   ‘2’

上述的计算和转换都是在PC上位机中进行的,然后上位机通过UART发送到单片机,
存储格式为字型(搜了一下,16位),低在先,高在后 ‘0’   ‘2’     ‘2’   ‘4’
                                           即存储为     0x0224
                                             
在字型传送过程中,字型高位在先,低位在后  ‘0’   ‘2’     ‘2’   ‘4’
                                                         
                                                     
****************************************************************

再复习一下报文格式,下载格式,
PC机发送   字节数: 0025, 数据: STX,"E11","805C","06",'0','2','2','4','0','3','C','5','0','F','0','0',ETX,"69"
                                                              // PC机发出写PLC 805CH地址处连续06H字节(3步程序)数据指令

单片机接收到。。。'0','2','2','4'。。。。后,在指定的地址805C处
开始写入
这一功能是由FX1NProcessing函数中的Write Code函数中的这一句来实现的,WriteFlash(WriteAddr,(unsigned char *)(Buffer+10),(unsigned char)WriteLen);

上位机程序下载结束后,单片机运营交由main_PLC函数掌控,其再通过key_list把 '0','2','2','4'。。,转换成 LD()函数,并把寄存器地址代入。相当于执行了 LD X02。



出0入0汤圆

 楼主| 发表于 2013-11-14 15:33:27 | 显示全部楼层
oldbeginner 发表于 2013-11-13 20:36
在ADDR_int_ppp  函数和WR_YM 都和寄存器位置相关,

现在需要理解一下再继续,

在上位机中,OUT SET RST适用所有寄存器,但是在单片机中需要分类对待,因为刚开始只处理Y输出,所以只会用到以下三个函数,
void OUTYM  (void)    // C (OUT指令, C000+ppp, 仅对Y,M有效)
{
          WR_YM(ppp,ACC_BIT);
}

void SETYM  (void)    // D (SET指令, D000+ppp, 仅对Y,M有效)
{
   if ((ACC_BIT & 0x01) != 0)
               WR_YM(ppp,1);
}

void RSTYM  (void)    // E (RST指令, E000+ppp, 仅对Y,M有效)
{
   if ((ACC_BIT & 0x01) != 0)  
             WR_YM(ppp,0);
}
*******************************************************
三个函数中都含有WR_YM函数,根据字面意思就是write y或m寄存器。功能好理解,是怎样实现的,

WR_YM函数需要两个输入变量:
1、ppp,寄存器的偏移地址(16进制),该地址是如何计算的必须了解,例如我们写的是y寄存器,则
寄存器名称   =  位地址 + 偏移量 = 实际地址ppp

Y0-377(八进制) =  (0-FF)   + 500  =  500-5FF

2、另外一个输入变量是ACC_BIT,在SETYM函数中只有ppp和1两个输入,实际上是把ACC_BIT=1代入了,相当于一个特例。同理RSTYM中是把ACC_BIT=0代入了。

ACC_BIT是怎样确定的?因为目前只学习LD函数,ACC_BIT是由LD函数决定的,而且物理意义非常明确,LD函数是用来检测输入点(触点)是导通还是断开,导通,则把ACC_BIT置1,反之复位。

知道了这两个输入,就可以开始逐步了解WR_YM函数了,
1、void WR_YM(unsigned int a,unsigned char i)    // (写入Y,M点内容)
该函数用了两个形参,a表示ppp,i表示ACC_BIT

2、 unsigned char *p;
定义了一个指针,是什么作用?继续看

3、a &= 0xfff;
    a就是命令和ppp的组合体,它的值是在main_PLC函数中赋予的,赋值过程有点复杂,会在第2遍理解main_PLC函数时详细展开。
    这里就是对a就是过滤作用,去掉命令,只要所需的ppp,
    例如
     OUT Y05就是    0xC000+0x505=0xC505
     a= 0xC505 & 0x0fff = 0x 0505,即得到所需的ppp,好理解。

4、i &= 0x01;
    i就是ACC_BIT,它的值是由前面的LD函数来确认的,因为只需要最后1位,所以过滤前7位,也好理解。

5、 if ((a>=0x500) && (a<0x600));
   根据实际地址大小确定是y寄存器还是m寄存器,谁让这个函数要同时处理y寄存器和m寄存器呢?Y0-377(八进制) =  (0-FF)   + 500  =  500-5FF

6、a -= 0x500;
    寄存器名称   =  位地址 + 偏移量 = 实际地址ppp,得到偏移量

7、if (a < _Y_num) 即if (a < 48)
    因为本程序只定义最多48个输出端口,所以偏移量不能超出。

8、p = (unsigned char*)rY + (a / 8);
   rY结构和rX一致,在笔记01中详细理解过了。因为rY[ ]有8个位元素(端口),所以利用a/8来选择具体的rY[ ](8个位元素)。

9、if (i == 0)   *p &= ~(1 << (a % 8));
   如果对应的输入x是断开的,则相应的输出端口是~1=0,
   1 << (a % 8),是把1赋值到相应的位置,利用a%8求出具体的端口(8个位元素中的某一个)。
   然后再取反,即0。

10、else  *p |= 1 << (a % 8);
   反之,则相应端口输出1。

因为目前只考虑输出y,所以这个函数理解到此就可以了。


出0入0汤圆

 楼主| 发表于 2013-11-14 16:15:39 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-11-14 16:20 编辑
oldbeginner 发表于 2013-11-14 15:33
在上位机中,OUT SET RST适用所有寄存器,但是在单片机中需要分类对待,因为刚开始只处理Y输出,所以只会 ...

再回到main_PLC函数中,

假设在FX1XProcessing中写入了
PC机发送   字节数: 0025, 数据: STX,"E11","805C","06",'0','2','2','4','0','5','C','5','0','F','0','0',ETX,"69"
                                                              // PC机发出写PLC 805CH地址处连续06H字节(3步程序)数据指令


则在main_PLC执行时,是如下情况
1、main_PLC是从0x8000开始执行的;
2、执行到0x805C后如下图,





此时,通过(*key_list[])()调用了LD函数,具体输入端口x由ppp确定。
等价于 LD X02

同理分析 '0' '5' 'C' '5'
即 OUT Y05

功能上就是,X2决定Y5。

第1次理解时,把16进制数和ASCII搞混了,在这个程序里如何区分我还是不太了解,在后面的复习中要专门理解一下。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-11-14 17:14:33 | 显示全部楼层
mark一下 一直关注了

出0入0汤圆

发表于 2013-11-25 16:14:04 | 显示全部楼层
楼主都可以出书了.

出0入0汤圆

发表于 2014-10-2 14:25:07 | 显示全部楼层
好东西,真不错

出0入0汤圆

发表于 2014-10-5 08:49:28 | 显示全部楼层
楼主 您太强大的   那个报文你是怎么知道的呢

出0入0汤圆

发表于 2015-10-16 13:33:20 | 显示全部楼层
感谢楼主 把自己的东西分享出来

出0入0汤圆

发表于 2016-6-8 14:32:53 | 显示全部楼层
实例,指令LD  X002,指令码为24 02,存储格式为02 24,转换成 ASCII 码传送为30 32 32 34。
则orderL=0x30
  orderH=0x32
利用联合体概念, order=0x3230,则ppp=0x0230。


这里
orderL=0x30
  orderH=0x32
有错吧,应该取的是二进制 不是ASCII啊

出0入0汤圆

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

本版积分规则

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

GMT+8, 2024-4-19 00:32

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

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