将Fletcher校验和从32位重新编码为8位

时间:2012-01-19 11:08:36

标签: c microcontroller pic crc microchip

此转换是否正确?

uint8_t fletcher8( uint8_t *data, uint8_t len )
{
    uint8_t sum1 = 0xff, sum2 = 0xff;

    while (len) {
            unsigned tlen = len > 360 ? 360 : len;
            len -= tlen;
            do {
                    sum1 += *data++;
                    sum2 += sum1;
                    tlen -= sizeof( uint8_t );
            } while (tlen);
            sum1 = (sum1 & 0xff) + (sum1 >> 4);
            sum2 = (sum2 & 0xff) + (sum2 >> 4);
    }
    /* Second reduction step to reduce sums to 4 bits */
    sum1 = (sum1 & 0xff) + (sum1 >> 4);
    sum2 = (sum2 & 0xff) + (sum2 >> 4);
    return sum2 << 4 | sum1;
    }

原件:

uint32_t fletcher32( uint16_t *data, size_t len )
{
    uint32_t sum1 = 0xffff, sum2 = 0xffff;

    while (len) {
            unsigned tlen = len > 360 ? 360 : len;
            len -= tlen;
            do {
                    sum1 += *data++;
                    sum2 += sum1;
                    tlen -= sizeof( uint16_t );
            } while (tlen);
            sum1 = (sum1 & 0xffff) + (sum1 >> 16);
            sum2 = (sum2 & 0xffff) + (sum2 >> 16);
    }
    /* Second reduction step to reduce sums to 16 bits */
    sum1 = (sum1 & 0xffff) + (sum1 >> 16);
    sum2 = (sum2 & 0xffff) + (sum2 >> 16);
    return sum2 << 16 | sum1;
    }

len将是8。

数据将输入数据[](1 - 8)

其实我不知道怎么处理这行:unsigned tlen = len&gt; 360? 360:len;

也许 - &gt; int8_t tlen = len&gt; 255? 255:len;

3 个答案:

答案 0 :(得分:2)

如何计算此tlen

  

其实我不知道怎么处理这行:unsigned tlen = len&gt; 360? 360:len;

该行似乎来自an old versionthis Wikipedia section。它现在已经改为359,理由在talk page上解释。该数字仅适用于求和16位实体,因为它是满足 n 的最大数

  

n n +5)/ 2×(2 16 -1)&lt; 2 32

换句话说,这是您可以在不执行模数减少的情况下添加块的大量次数,并且仍然可以避免溢出uint32_t。对于4位数据字和8位累加器,相应的值将为4,使用

计算
  

n n +5)/ 2×(2 4 -1)&lt; 2 8

因此,如果更改数据大小,则必须修改该行。您还可以更改代码以使用更大的数据类型来保持其总和,从而在减少之前汇总更多块。但在这种情况下,您可能需要在循环内部执行多个缩减步骤。

例如,如果您使用uint32_t作为sum1sum2,那么您可以在出现溢出危险之前将23927个半字节相加,但之后您需要最多减少7个sum1 = (sum1 & 0xf) + (sum1 >> 4)形式将其缩小到10x1e的范围,就像你原来的算法一样。将其写为(sum1 - 1)%0xf + 1可能更有效。在这种情况下,您甚至可以将范围从1到15更改为0到14,将总和初始化为0并将缩减写为sum1 %= 0xf。除非您要求与使用其他范围的实现兼容。

答案 1 :(得分:1)

我认为你需要0xF掩码而不是0xFF。 32位使用16位掩码,32位的一半,8位使用8位掩码,不是8位的一半,4位是8位的一半。

uint8_t fletcher8( uint8_t *data, uint8_t len )
{
    uint8_t sum1 = 0xf, sum2 = 0xf;

    while (len) {
        unsigned tlen = len > 360 ? 360 : len;
        len -= tlen;
        do {
                sum1 += *data++;
                sum2 += sum1;
                tlen -= sizeof( uint8_t );
        } while (tlen);
        sum1 = (sum1 & 0xf) + (sum1 >> 4);
        sum2 = (sum2 & 0xf) + (sum2 >> 4);
    }
    /* Second reduction step to reduce sums to 4 bits */
    sum1 = (sum1 & 0xf) + (sum1 >> 4);
    sum2 = (sum2 & 0xf) + (sum2 >> 4);
    return sum2 << 4 | sum1;
}

否则,您创建的是不同的校验和,而不是Fletcher。 sum1例如执行我认为称为补码校验和的东西。基本上它是先前的16位和你的情况下的4位,校验和,其中进位位被重新加入。用于互联网协议使得很容易修改数据包而无需计算整个数据包的校验和,你可以仅根据现有校验和添加和减去更改。

额外的减少步骤是针对一个极端情况,如果sum1 + = * data = 0x1F的结果使用四位方案,那么进位的加法是0x01 + 0x0F = 0x10,你需要添加那个进位也回到位,因此在循环0x01 + 0x00 = 0x01之外。否则,循环和之外的加法为零。根据您的体系结构,您可以使用if(sum1&amp; 0x10)sum1 = 0x01等更快的速度执行;而不是那些可能需要更多指示的狡猾的添加物。

当它加入进位时,它不仅仅是一个校验和,而是两者组合时的最后一步。例如,如果你只使用32位fletcher作为16位校验和,你浪费了你的时间,结果的低16位只是一个库存校验和,并且进位位重新加入,没什么特别的。 sum2是有趣的数字,因为它是sum1校验和的累积(sum1是数据的累积,sum2是校验和的累积)。

答案 2 :(得分:0)

在原始版本sum1中,sum2是32位。这就是之后比特转移的原因。在你的情况下,你声明sum1,sum2为8位,所以位移是没有意义的。