首页 > 解决方案 > Tcl 中的 CRC16 计算

问题描述

我试图计算二进制文件的 CRC16。

我首先从二进制文件中读取 2 个字节,然后计算一个具有多项式 = 1021 和 0xFFFF 初始值的 CRC16。我使用了一个 C 代码并试图将它翻译成 TCL。我不能使用字节格式,因为我通过计算得到一个关于使用非数字字符串的错误。所以我将字节转换为字符串。

proc main {}{
    # open binary file
    set file_read [open "$input_file" rb]
    while {1} {
        if {! [eof $fr]} {
          append binary_data [read $file_read 2]  
        }

         binary scan [string range $binary_data 0 1] H4 str_bin_data
         set CRC_data [CRC_calculation $str_bin_data]
         puts " CRC_data := $CRC_data"
    }   
}

proc CRC_calculation {str_bin_data} { 

    set Polynome 0x1021
    set Highbit   0x8000
    set CRC_data 0xFFFF
    set byte 0
    set bit  0
    set data_ln [string length $str_bin_data]
    # puts " data_ln := $data_ln"

    for {set byte 0} {$byte < $data_ln} {incr byte} {
        set CRC_data [ expr {$CRC_data ^ ([lindex $str_bin_data $byte] << 8)} ]                 
        for {set bit 8} {$bit > 0} {incr bit -1} {
            if {($CRC_data && $Highbit)} {
                set CRC_data [expr {($CRC_data << 1) ^ $Polynome}] 
            } else { 
                set CRC_data [expr {$CRC_data << 1}] 
            }           
        }   
        puts " byte_index := $byte"
        puts " CRC_data := $CRC_data"
    }

    return $CRC_data
} 

在 C 中,当我定义一个字节数组示例(二进制文件中的前 8 个字节)时: unsigned char bytes[3]= {0x55,0x55,0x55,0x55}; 然后 CRC = 0x82b8 在 Tcl 中我没有得到正确的值,甚至没有 32 位 CRC 值。

这是我使用的 C 代码:

#include<stdio.h>

#define Polynom 0x1021
#define Highbit 0x8000

unsigned short getCRC(const unsigned char data[])
{
    unsigned short rem = 0xFFFF;
    unsigned long  byte = 0;
    int bit = 0;
    for (byte = 0; byte < 3; ++byte) 
    {
        rem ^= (data[byte]<< 8); 
        for (bit = 8; bit > 0; --bit) 
        {
            if (rem & Highbit) 
                rem = (rem << 1) ^ Polynom;
            else
                rem = (rem << 1);
        }
    }
    return (rem);
}

int main() {
    int rem ;
    unsigned char data[]= {0x55,0x55,0x55,0x55};
    rem = getCRC (data);
    printf("%x", rem);

}

标签: tclcrc16

解决方案


有几个问题。首先,也是最重要的,二进制数据的扫描是不正确的,因为我们希望得到无符号字节(用于与 C 并行操作)而不是十六进制字符。你会更好:

# I'm assuming you've got Tcl 8.6, this is how you read 2 bytes as unsigned chars
binary scan [read $file_read 2] "cu*" str_bin_data
# Process the list of parsed byte data here; I'm not sure if you want this in the loop or not

另一个大问题是您的 CRC 计算不正确。

proc CRC_calculation {str_bin_data} { 
    set Polynom 0x1021
    set Highbit 0x8000
    set MASK 0xFFFF;   # 16 bit mask; for clamping to C unsigned short range

    set rem 0xFFFF
    # Assume str_bin_data holds a list of unsigned char values
    foreach byte $str_bin_data {
        set rem [expr {$rem ^ ($byte << 8)}]
        foreach _ {7 6 5 4 3 2 1 0} {
            set rem [expr {
                (($rem << 1) ^ ($rem & $Highbit ? $Polynom : 0)) & $MASK
            }]
        }
    }
    return $rem
}

这里的主要观察?Tcl 的数字是任意精度整数(和 IEEE doubles,虽然在这里不相关)。这意味着您需要限制范围。0xFFFF至少,在任何可以增加使用的位数的操作之后,这将是一个带有(16 位掩码)的 AND ,这只是<<在这个算法中。加上首先转换二进制数据的问题,这就是为什么事情不适合你的原因。我也切换到使用foreach,因为它对于以“做每一个”为基本思想的操作来说更快更清晰,并将内部位合并为一个expr(是的,expr如果你愿意,表达式可以是多行的)。

最大的问题是您将完全错误的东西传递给CRC_calculation代码。改变binary scan是至关重要的。


推荐阅读