我应该如何为 HDLC 实施 CRC? (CRC16-CCITT)

How should I implement CRC for HDLC? (CRC16-CCITT)

我需要为 HDLC 实施 CRC16-CCITT,作为开发某些功能以控制外部硬件(某种类型的 LTE 天线)的一部分。 问题是,尽管我已经找到了许多 CRC16-CCITT 的在线实现,但输出值与硬件供应商发送给我们的 CRC 值不匹配。

例如,这是应该发送到设备的第一个 HDLC 帧(名为 "Device Scan"),采用十六进制格式: 7EFFBF81F02A011300000000000000000000000000000000000000003130000000000000000000000000000000000000007F0F7E

可以看出,开始和结束的7E是分界符,最后4个16进制字符是CRC——也就是0x7F0F是CRC结果。

然而,许多在线 CRC 计算器产生的结果是 0x6CC3 (例如看这里:http://www.zorc.breitbandkatze.de/crc.html , https://www.lammertbies.nl/comm/info/crc-calculation

另一个命令也是如此,它是上面匹配的 "receive" 命令。 RCV 命令是: 7E00BF81F018010F4B4D4B3032313941303035335A585806024B4D040130F1ED7E

这意味着 0xF1ED 是 CRC,这又不是我在其他计算器中看到的,它产生 0xD814。

我们已要求供应商进行澄清,他向我们发送了此查询 table 他们正在使用:

crc table: { 0x0000、0x1189、0x2312、0x329b、0x4624、0x57ad、0x6536、0x74bf、 0x8c48、0x9dc1、0xaf5a、0xbed3、0xca6c、0xdbe5、0xe97e、0xf8f7、 0x1081、0x0108、0x3393、0x221a、0x56a5、0x472c、0x75b7、0x643e、 0x9cc9、0x8d40、0xbfdb、0xae52、0xdaed、0xcb64、0xf9ff、0xe876、 0x2102、0x308b、0x0210、0x1399、0x6726、0x76af、0x4434、0x55bd、 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183、0x200a、0x1291、0x0318、0x77a7、0x662e、0x54b5、0x453c、 0xbdcb、0xac42、0x9ed9、0x8f50、0xfbef、0xea66、0xd8fd、0xc974、 0x4204、0x538d、0x6116、0x709f、0x0420、0x15a9、0x2732、0x36bb、 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285、0x430c、0x7197、0x601e、0x14a1、0x0528、0x37b3、0x263a、 0xdecd、0xcf44、0xfddf、0xec56、0x98e9、0x8960、0xbbfb、0xaa72、 0x6306、0x728f、0x4014、0x519d、0x2522、0x34ab、0x0630、0x17b9、 0xef4e、0xfec7、0xcc5c、0xddd5、0xa96a、0xb8e3、0x8a78、0x9bf1、 0x7387、0x620e、0x5095、0x411c、0x35a3、0x242a、0x16b1、0x0738、 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408、0x9581、0xa71a、0xb693、0xc22c、0xd3a5、0xe13e、0xf0b7、 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489、0x8500、0xb79b、0xa612、0xd2ad、0xc324、0xf1bf、0xe036、 0x18c1、0x0948、0x3bd3、0x2a5a、0x5ee5、0x4f6c、0x7df7、0x6c7e、 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942、0x38cb、0x0a50、0x1bd9、0x6f66、0x7eef、0x4c74、0x5dfd、 0xb58b、0xa402、0x9699、0x8710、0xf3af、0xe226、0xd0bd、0xc134、 0x39c3、0x284a、0x1ad1、0x0b58、0x7fe7、0x6e6e、0x5cf5、0x4d7c、 0xc60c、0xd785、0xe51e、0xf497、0x8028、0x91a1、0xa33a、0xb2b3、 0x4a44、0x5bcd、0x6956、0x78df、0x0c60、0x1de9、0x2f72、0x3efb、 0xd68d、0xc704、0xf59f、0xe416、0x90a9、0x8120、0xb3bb、0xa232、 0x5ac5、0x4b4c、0x79d7、0x685e、0x1ce1、0x0d68、0x3ff3、0x2e7a、 0xe70e、0xf687、0xc41c、0xd595、0xa12a、0xb0a3、0x8238、0x93b1、 0x6b46、0x7acf、0x4854、0x59dd、0x2d62、0x3ceb、0x0e70、0x1ff9、 0xf78f、0xe606、0xd49d、0xc514、0xb1ab、0xa022、0x92b9、0x8330、 0x7bc7、0x6a4e、0x58d5、0x495c、0x3de3、0x2c6a、0x1ef1、0x0f78 }

这看起来不像我在网上找到的CRC16-CCITT的常规table,例如:

static const unsigned short CRC_CCITT_TABLE[256] = { 0x0000、0x1021、0x2042、0x3063、0x4084、0x50A5、0x60C6、0x70E7、 0x8108、0x9129、0xA14A、0xB16B、0xC18C、0xD1AD、0xE1CE、0xF1EF、 0x1231、0x0210、0x3273、0x2252、0x52B5、0x4294、0x72F7、0x62D6、 0x9339、0x8318、0xB37B、0xA35A、0xD3BD、0xC39C、0xF3FF、0xE3DE、 0x2462、0x3443、0x0420、0x1401、0x64E6、0x74C7、0x44A4、0x5485、 0xA56A、0xB54B、0x8528、0x9509、0xE5EE、0xF5CF、0xC5AC、0xD58D、 0x3653、0x2672、0x1611、0x0630、0x76D7、0x66F6、0x5695、0x46B4、 0xB75B、0xA77A、0x9719、0x8738、0xF7DF、0xE7FE、0xD79D、0xC7BC、 0x48C4、0x58E5、0x6886、0x78A7、0x0840、0x1861、0x2802、0x3823、 0xC9CC、0xD9ED、0xE98E、0xF9AF、0x8948、0x9969、0xA90A、0xB92B、 0x5AF5、0x4AD4、0x7AB7、0x6A96、0x1A71、0x0A50、0x3A33、0x2A12、 0xDBFD、0xCBDC、0xFBBF、0xEB9E、0x9B79、0x8B58、0xBB3B、0xAB1A、 0x6CA6、0x7C87、0x4CE4、0x5CC5、0x2C22、0x3C03、0x0C60、0x1C41、 0xEDAE、0xFD8F、0xCDEC、0xDDCD、0xAD2A、0xBD0B、0x8D68、0x9D49、 0x7E97、0x6EB6、0x5ED5、0x4EF4、0x3E13、0x2E32、0x1E51、0x0E70、 0xFF9F、0xEFBE、0xDFDD、0xCFFC、0xBF1B、0xAF3A、0x9F59、0x8F78、 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080、0x00A1、0x30C2、0x20E3、0x5004、0x4025、0x7046、0x6067、 0x83B9、0x9398、0xA3FB、0xB3DA、0xC33D、0xD31C、0xE37F、0xF35E、 0x02B1、0x1290、0x22F3、0x32D2、0x4235、0x5214、0x6277、0x7256、 0xB5EA、0xA5CB、0x95A8、0x8589、0xF56E、0xE54F、0xD52C、0xC50D、 0x34E2、0x24C3、0x14A0、0x0481、0x7466、0x6447、0x5424、0x4405、 0xA7DB、0xB7FA、0x8799、0x97B8、0xE75F、0xF77E、0xC71D、0xD73C、 0x26D3、0x36F2、0x0691、0x16B0、0x6657、0x7676、0x4615、0x5634、 0xD94C、0xC96D、0xF90E、0xE92F、0x99C8、0x89E9、0xB98A、0xA9AB、 0x5844、0x4865、0x7806、0x6827、0x18C0、0x08E1、0x3882、0x28A3、 0xCB7D、0xDB5C、0xEB3F、0xFB1E、0x8BF9、0x9BD8、0xABBB、0xBB9A、 0x4A75、0x5A54、0x6A37、0x7A16、0x0AF1、0x1AD0、0x2AB3、0x3A92、 0xFD2E、0xED0F、0xDD6C、0xCD4D、0xBDAA、0xAD8B、0x9DE8、0x8DC9、 0x7C26、0x6C07、0x5C64、0x4C45、0x3CA2、0x2C83、0x1CE0、0x0CC1、 0xEF1F、0xFF3E、0xCF5D、0xDF7C、0xAF9B、0xBFBA、0x8FD9、0x9FF8、 0x6E17、0x7E36、0x4E55、0x5E74、0x2E93、0x3EB2、0x0ED1、0x1EF0 };

我尝试在我们的 CRC 实现中使用他们的 table 而不是我的,但我们仍然得到与他们不同的结果。

有人知道这里发生了什么吗? 哪个 CRC 是实际正确的 HDLC CRC16-CCITT? 上述帧的 CRC16-CCITT 的正确结果是什么?

非常感谢大家!

我最初认为多边形是 0x1189,但它是 0x1021 = 0x8408 的一点反转,在 table[0x80] 处看到。使用下面链接的在线 CRC 计算器,它是预定义的 CRC_X_25,poly = 0x1021,输入反映,结果反映,初始值 0xFFFF,最终 xor 0xFFFF。点击"show reflected lookup table",使用计算器计算CRC时,会得到与供应商table相同的table。

将CRC附加到数据时,CRC附加为低字节,高字节。因此,0xEDF1 的 CRC 附加为:0xF1、0xED,而 0x0F7F 的 CRC 附加为:0x7F、0x0F。

因为CRC是post补码的(xorout = 0xFFFF),如果通过数据+CRC重新计算来检查CRC,如果没有错误,CRC将是一个非零常数,0x0F47 .

http://www.sunshine2k.de/coding/javascript/crc/crc_js.html


示例代码:

#include <iostream>
#include <iomanip>

uint16_t crctbl[256];

void gentbl()
{
uint16_t crc, i, j;
    for(j = 0; j < 0x100; j++){
        crc = j;
        for(i = 0; i < 8; i++)
            crc = (crc>>1)^((0-(crc&1))&0x8408);
        crctbl[j] = crc;
    }
}

uint16_t crc16(uint8_t * bfr, size_t size)
{
uint16_t crc = 0xffff;
    while (size--)
        crc = (crc>>8)^crctbl[(crc^*bfr++)&0xff];
    return(crc ^ 0xffff);
}

int main(int argc, char**argv)
{
uint16_t crc;
// bfr has 2 trailing zeroes to store crc into
uint8_t bfr[] = {0x00,0xBF,0x81,0xF0,0x18,0x01,0x0F,0x4B,
                 0x4D,0x4B,0x30,0x32,0x31,0x39,0x41,0x30,
                 0x30,0x35,0x33,0x5A,0x58,0x58,0x06,0x02,
                 0x4B,0x4D,0x04,0x01,0x30,0x00,0x00};
    gentbl();
    crc = crc16(bfr, sizeof(bfr)-2);
    std::cout << std::hex << crc << std::endl;
    bfr[sizeof(bfr)-2] = crc&0xff;
    bfr[sizeof(bfr)-1] = crc>>8;
    crc = crc16(bfr, sizeof(bfr));
    std::cout << std::hex << crc << std::endl;
    if(crc != 0xf47)
        std::cout << "error" << std::endl;
    return(0);
}