1. ホーム
  2. c

[解決済み] CRC16チェックサムを計算する関数

2022-03-07 12:22:50

質問内容

RS232またはRS485接続でシンプルで信頼性の高い通信を行うためのライブラリに取り組んでいます。 このコードの一部には、回線ノイズによる破損を検出するために、データのCRC16チェックサムを使用することが含まれています。 CRC16チェックサムを計算する関数を作成しましたが、正しい値が出力されないようです。

私が書いた関連するコードは以下の通りです(以下も参照できます)。 こちら ).

#include <stdint.h>

#define CRC16 0x8005

uint16_t gen_crc16(const uint8_t *data, uint16_t size)
{
    uint16_t out = 0;
    int bits_read = 0, bit_flag;

    /* Sanity check: */
    if(data == NULL)
        return 0;

    while(size > 0)
    {
        bit_flag = out >> 15;

        /* Get next bit: */
        out <<= 1;
        out |= (*data >> (7 - bits_read)) & 1;

        /* Increment bit counter: */
        bits_read++;
        if(bits_read > 7)
        {
            bits_read = 0;
            data++;
            size--;
        }

        /* Cycle check: */
        if(bit_flag)
            out ^= CRC16;
    }

    return out;
}

出力したものを照合している このオンラインCRC計算機 .

CRC16の計算方法についての私の理解が誤っているか、オンラインの計算機が誤っているかのどちらかであるという結論に達しました(前者の方が可能性が高そうです)。 どなたか、私がどこで間違っているのか、教えていただけませんか?

解決方法は?

同じ多項式を使用しても、データビットの処理方法、CRCの特定の初期値(0であったり、0xffffであったり)の使用、CRCのビットの反転などの細かい違いによって結果が異なる場合があります。例えば、ある実装ではデータバイトの下位ビットから順に処理し、ある実装では上位ビットから順に処理します(現在のあなたの実装と同じです)。

また、すべてのデータビットを通過させた後、CRCの最後のビットを「プッシュアウト」する必要があります。

CRCアルゴリズムはハードウェアで実装されるように設計されているため、ビット順序の処理方法はソフトウェアの観点からはあまり意味をなさない場合があることを覚えておいてください。

lammertbies.nl CRC計算機のページにあるようにCRC16を多項式0x8005で照合したい場合、CRC関数に以下の変更をする必要があります。

  • a) データビットを最上位ビットからではなく、最下位ビットからCRCループに通す。
  • b) 入力データ終了後、CRCの最後の16ビットをCRCレジスタから押し出す。
  • c) CRCビットを反転させる(このビットはハードウェア実装からのキャリーオーバーだと思われます)

つまり、関数は次のようになります。

#define CRC16 0x8005

uint16_t gen_crc16(const uint8_t *data, uint16_t size)
{
    uint16_t out = 0;
    int bits_read = 0, bit_flag;

    /* Sanity check: */
    if(data == NULL)
        return 0;

    while(size > 0)
    {
        bit_flag = out >> 15;

        /* Get next bit: */
        out <<= 1;
        out |= (*data >> bits_read) & 1; // item a) work from the least significant bits

        /* Increment bit counter: */
        bits_read++;
        if(bits_read > 7)
        {
            bits_read = 0;
            data++;
            size--;
        }

        /* Cycle check: */
        if(bit_flag)
            out ^= CRC16;

    }

    // item b) "push out" the last 16 bits
    int i;
    for (i = 0; i < 16; ++i) {
        bit_flag = out >> 15;
        out <<= 1;
        if(bit_flag)
            out ^= CRC16;
    }

    // item c) reverse the bits
    uint16_t crc = 0;
    i = 0x8000;
    int j = 0x0001;
    for (; i != 0; i >>=1, j <<= 1) {
        if (i & out) crc |= j;
    }

    return crc;
}

その関数は 0xbb3d を渡すと、私の場合は "123456789" .