筆者最近手上有個威綸通MT6056I的HMI,需要與公司的一款板卡通訊,板卡遵循的是自由協(xié)議,但是采用的校驗方式CRC-16/XMODEM. 這種校驗方式是CRC16校驗方式的一種,但是與MODBUS協(xié)議的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存儲區(qū)開辟一塊暫存區(qū)域,將要進行CRC計算的數據搬運到這塊暫存區(qū)域上。再CRC校驗函數中根據LW的地址將數據取出,進行CRC計算。 如此可解決無法傳遞數組參數的問題。操作如下。 定義 sub short CRC_16(short dataddr, short len) 參數說明 short dataddr,dataddr是位于LW暫存區(qū)的起始地址,short len len 數據長度。 下面紅色區(qū)域為重點區(qū)域,注意理解。
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)//將暫存區(qū)的數據復制到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,暫存空間的數據,轉移到所申請的數組中,交給下面計算。 這里的疑惑是為何要申請數組,然后在拷貝數據,這么麻煩,而不是用下面的方式進行,下面的算法是每次循環(huán)開始先讀取暫存空間數據,先不說犧牲時間什么的,最起碼這 中不用申請上面那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