您现在所在的是:

人机界面

回帖:5个,阅读:5731 [上一页] [1] [下一页]
827
semonping
文章数:4
年度积分:50
历史总积分:827
注册时间:2009/7/11
发站内信
发表于:2014/10/28 17:49:42
#0楼
       版权申明:本文章由逸创论坛(www.yeecon.com)原创,转载请标明出处:http://www.yeecon.com/forum.php?mod=viewthread&tid=74&fromuid=1
(出处: 逸控BBS)

由于本人知识有限,文中如有错误,请告知。
作者:Lanber
E-Mail: 13524637976@163.com
 最近手上有个威纶通MT6056I的HMI,需要与公司的一款板卡通讯,板卡遵循的是自由协议,但是采用的校验方式CRC-16/XMODEM.
这种校验方式是CRC16校验方式的一种,但是与MODBUS协议的CRC16的生成方式不同。威纶通的脚本语言库中有CRC校验函数,但是这个校验
函数是CRC-16/MODBUS版本的,不能为我所用,所以我计划自己设计一个CRC16/XMODEM的函数,然后将这个函数保存到函数库里,以备他用
。由于第一次使用这个屏幕,经验不做,遇到一些困难,但也努力解决了。
       首先不得不吐槽下,威纶通的宏指令说明书写的太简单了,在遇到问题的时候,可能无法从指令的说明手册上找到答案,更多的是自己摸索。比如
[此贴子已经被作者于2014/10/28 17:53:38编辑过]
827
semonping
文章数:4
年度积分:50
历史总积分:827
注册时间:2009/7/11
发站内信
发表于:2014/10/28 17:51:32
#1楼
比如
自己写子函数时就遇到狗血的问题
      1:数组不能作为函数参数
           比如
                       sub short function(char dat[],char len)
           其中 char dat[] 参数将出错。
           解决方法,后面有表述
      2: 在调用子函数的时候,在函数的参数中不能出现常量,只能是变量的方式
          例如我定义了子函数 function( short a,short b)
          调用方式  function( 1000,1000)  编译器将会告诉你参数类型错误
          当我改成如下掉用方式就可以了
           short i=1000
           short j=1000
           function(i,j)

     好进入正题吧,如何写个CRC校验函数,其实根本问题,是如何将一串数据传给子函数,子函数将传过来的数据根据特定算法,运算出计算结果,关于CRC算法,本文不做论述,只提供代码。
我首先想到的是这样的思路:定义 这样一个函数 short CRC16(short dat[],short len),用来计算CRC。
但是却遇到了上面1中的问题,数组参数无法作为函数的形参,官方也找不到解决方法。
最后想到的解决方法是在LW存储区开辟一块暂存区域,将要进行CRC计算的数据搬运到这块暂存区域上。再CRC校验函数中根据LW的地址将数据取出,进行CRC计算。
827
semonping
文章数:4
年度积分:50
历史总积分:827
注册时间:2009/7/11
发站内信
发表于:2014/10/28 17:52:35
#2楼
如此可解决无法传递数组参数的问题。操作如下。
       定义  sub short CRC_16(short dataddr, short len)
      参数说明 short dataddr,dataddr是位于LW暂存区的起始地址,short len len 数据长度。
        下面红色区域为重点区域,注意理解。

  sub short CRC_16(short dataddr, short len)
   short i, j
   unsigned short crc_reg = 0x0000
   unsigned short current
   unsigned char dattmp[128]    //申请一定长度的数组来保存要进行校验的数据。
   GetData(dattmp[0], "Local HMI", LW, dataddr, len)//将暂存区的数据复制到dattmp中
   len=len-1
   for i = 0 to len
       current = dattmp
       current=current<<8
       crc_reg=crc_reg^current

       for j = 1 to 8

           if (crc_reg & 0x8000) <> 0 then
               crc_reg = (crc_reg << 1) ^ 0x1021
           else
               crc_reg=crc_reg << 1
           end if
       next

   next
   return crc_reg;
end sub
827
semonping
文章数:4
年度积分:50
历史总积分:827
注册时间:2009/7/11
发站内信
发表于:2014/10/28 17:54:07
#3楼
上面绿色部分是进行CRC计算的,这里不做研究。下面来讲讲红色部分。红色部分就是申请数组空间,然后,将LW,暂存空间的数据,转移到所申请的数组中,交给下面计算。
 这里的疑惑是为何要申请数组,然后在拷贝数据,这么麻烦,而不是用下面的方式进行,下面的算法是每次循环开始先读取暂存空间数据,先不说牺牲时间什么的,最起码这
中不用申请上面那128大的数组。理论可行,但是实际上确实错误的。其主要GetData和SetData 函数实现原理,以及数据在LW中存储方式不清楚造成的。
sub short CRC_16_2(short dataddr, short len)
   short i, j
   unsigned short crc_reg = 0x0000
   unsigned short current
   unsigned char dattmp
   short addr
   addr=dataddr
   len=len-1

   for i = 0 to len

      GetData(dattmp, "Local HMI", LW, addr, 1)
       addr=addr+1
       current = dattmp

       current=current<<8
       crc_reg=crc_reg^current

       for j = 1 to 8

           if (crc_reg & 0x8000) <> 0 then
               crc_reg = (crc_reg << 1) ^ 0x1021
           else
               crc_reg=crc_reg << 1
           end if
       next

   next
   return crc_reg;
end sub



GetData和SetData 函数实现原理,以及数据在LW中存储方式
先来说说LW中的数据存储 (吐槽:为何网上关于这方面的资料很少,几乎没有LW存储空间的详细说明)
目前 笔者使用 软件 EB8000 V4.65

LW 可理解为计算机的RAM ,掉电数据不保存,但是存取速度快。每个存储单元是16bit。分为高字节 (bit15-bit8)和低字节(bit7 -bit 0)
bit15 bit0


例如 0x1234 存储方式为高字节 0x12 ,低字节0x34.

SetData 当用SetData 来写LW中的数据的时候,会根据第一个参数的类型来指导操作

                   如下程序,这是正常的操作程序。a的类型是short
unsigned short a=0x1234
unsigned char b[2]
unsigned char c
SetData(a, "Local HMI", LW, 0, 1)
GetData(c, "Local HMI", LW, 0, 1)
GetData(b[0], "Local HMI", LW, 0, 2)
TRACE("C = %d", c)                    //c=0x34
TRACE("b[0] = %d", b[0])          //b[0]=0x34
TRACE("b[1] = %d", b[1])         //b[1]=0x12

但是SetData的第一个参数是第一个char类型的数组如下
unsigned short a
unsigned char b[2]
b[1]=0x12
b[0]=0x34
SetData(b[0], "Local HMI", LW, 0, 2)
GetData(a, "Local HMI", LW, 0, 1)
TRACE("a = %d", a) //a=0x1234

可以看出,在保存char 型数据的时候,为了节省空间进行了特别处理
理论上b[0]应该保存在LW0000,b[1]保存在LW0001.但是实际上确实b[0]保存在 LW0000的低字节,b[1]保存在LW0000的高字节。

同样的道理GetData 也遵循相同的操作。

现在能回答为何不能采用第二种子函数写法.主要原因是 每次循环都是读取一个字的底字节。高字节数据被丢弃了。
假如写入内存的是b[0]到b[10],则通过下面循环读取的是b[0],b[2],b[4].....,请好好体会。
   for i = 0 to len

      GetData(dattmp, "Local HMI", LW, addr, 1)
       addr=addr+1
       current = dattmp
   next

再需要调用CRC函数的时候,可用如下的方法操作
       char sendbus[26]

       short lwadd=7000 //lw 暂存区开始地址
       short len=26
       SetData(sendbuf[1], "Local HMI", LW, lwadd, len)   //将得计算的数阻搬到LW7000开始的空间,具体在什么地址,可自己安排,只要注意LW 范                                                                                        //0-9000
       tmp = CRC_16(lwadd, len)                                //CRC计算
-148
2781794948
文章数:-1
年度积分:-148
历史总积分:-148
注册时间:2014/10/30
发站内信
发表于:2014/10/30 21:06:22
#4楼
此楼内容不符合板块规定,不予显示! 查看原帖内容>>
18266
ZHTOK
文章数:11606
年度积分:-16
历史总积分:18266
注册时间:2004/3/11
发站内信
2014相约国庆
发表于:2014/11/1 8:09:58
#5楼
     CRC校验有点复杂。

关于我们 | 联系我们 | 广告服务 | 本站动态 | 友情链接 | 法律声明 | 非法和不良信息举报

工控网客服热线:0755-86369299
版权所有 工控网 Copyright©2024 Gkong.com, All Rights Reserved

78.0005