笔者最近手上有个威纶通MT6056I的HMI,需要与公司的一款板卡通讯,板卡遵循的是自由协议,但是采用的校验方式CRC-16/XMODEM. 这种校验方式是CRC16校验方式的一种,但是与MODBUS协议的CRC16的生成方式不同。威纶通的脚本语言库中有CRC校验函数,但是这个校验 函数是CRC-16/MODBUS版本的,不能为我所用,所以我计划自己设计一个CRC16/XMODEM的函数,然后将这个函数保存到函数库里,以备他用 。由于第一次使用这个屏幕,经验不做,遇到一些困难,但也努力解决了。 首先不得不吐槽下,威纶通的宏指令说明书写的太简单了,在遇到问题的时候,可能无法从指令的说明手册上找到答案,更多的是自己摸索。比如 自己写子函数时就遇到狗血的问题 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计算。 如此可解决无法传递数组参数的问题。操作如下。 定义 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
上面绿色部分是进行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