• STM32F10x 学习笔记4(CRC计算单元 续)


    上篇博客给出了 STM32F10X 系列单片机中CRC 单元的用法。还指出了这个CRC 单元计算的结果与常见的CRC32 算法得到的结果不相同。但是为什么不相同,是什么原因造成的却没有写出来。这里再补一篇,把这些都说清楚。

    下面先给个crc32的计算函数,这个函数计算的结果与STM32F 单片机上硬件单元的计算结果相同。

    uint32_t crc32(uint32_t *addr, int num, uint32_t crc)
    {
        int i;
        for (; num > 0; num--)              
        {
            crc = crc ^ (*addr++);     
            for (i = 0; i < 32; i++)             
            {
                if (crc & 0x80000000)            
                    crc = (crc << 1) ^ POLY;   
                else                          
                    crc <<= 1;                 
            }                             
            crc &= 0xFFFFFFFF;            
        }                               
        return(crc);                   
    }

    在我写的文章《写给嵌入式程序员的循环冗余校验(CRC)算法入门引导》(http://blog.csdn.net/liyuanbhu/article/details/7882789) 中给了个利用查表法计算crc 的程序。那个程序稍微修改一点就能计算CRC32。下面给出改动后的程序。

    //crc32.h
    
    #ifndef CRC32_H_INCLUDED
    #define CRC32_H_INCLUDED
    
    #ifdef __cplusplus
    #if __cplusplus
    extern "C"{
        #endif
        #endif /* __cplusplus */
    
    
    #include<stdint.h>
    
    /*
    * The CRC parameters. Currently configured for CRC32.
    * CRC32=X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X1+X0
    */
    
    #define POLYNOMIAL          0x04C11DB7
    #define INITIAL_REMAINDER   0xFFFFFFFF
    #define FINAL_XOR_VALUE     0x00000000
    
    /*
    * The width of the CRC calculation and result.
    * Modify the typedef for an 8 or 32-bit CRC standard.
    */
    typedef uint32_t width_t;
    #define WIDTH (8 * sizeof(width_t))
    #define TOPBIT (1 << (WIDTH - 1))
    
    /**
     * Initialize the CRC lookup table.
     * This table is used by crcCompute() to make CRC computation faster.
     */
    void crcInit(void);
    
    /**
     * Compute the CRC checksum of a binary message block.
     * @para message, 用来计算的数据
     * @para nBytes, 数据的长度
     * @note This function expects that crcInit() has been called
     *       first to initialize the CRC lookup table.
     */
    width_t crcCompute(unsigned char * message, unsigned int nBytes, width_t remainder);
    
    
    #ifdef __cplusplus
        #if __cplusplus
    }
    #endif
    #endif /* __cplusplus */
    
    #endif // CRC32_H_INCLUDED

    对应的C程序如下:

    #include "crc32.h"
    /*
    * An array containing the pre-computed intermediate result for each
    * possible byte of input. This is used to speed up the computation.
    */
    static width_t crcTable[256];
    
    /**
     * Initialize the CRC lookup table.
     * This table is used by crcCompute() to make CRC computation faster.
     */
    void crcInit(void)
    {
        width_t remainder;
        width_t dividend;
        int bit;
        /* Perform binary long division, a bit at a time. */
        for(dividend = 0; dividend < 256; dividend++)
        {
            /* Initialize the remainder.  */
            remainder = dividend << (WIDTH - 8);
            /* Shift and XOR with the polynomial.   */
            for(bit = 0; bit < 8; bit++)
            {
                /* Try to divide the current data bit.  */
                if(remainder & TOPBIT)
                {
                    remainder = (remainder << 1) ^ POLYNOMIAL;
                }
                else
                {
                    remainder = remainder << 1;
                }
            }
            /* Save the result in the table. */
            crcTable[dividend] = remainder;
        }
    } /* crcInit() */
    
    /**
     * Compute the CRC checksum of a binary message block.
     * @para message, 用来计算的数据
     * @para nBytes, 数据的长度
     * @note This function expects that crcInit() has been called
     *       first to initialize the CRC lookup table.
     */
    width_t crcCompute(unsigned char * message, unsigned int nBytes, width_t remainder)
    {
        unsigned int offset;
        unsigned char byte;
        //width_t remainder = INITIAL_REMAINDER;
        /* Divide the message by the polynomial, a byte at a time. */
        for( offset = 0; offset < nBytes; offset++)
        {
            byte = (remainder >> (WIDTH - 8)) ^ message[offset];
            remainder = crcTable[byte] ^ (remainder << 8);
        }
        /* The final remainder is the CRC result. */
        return (remainder ^ FINAL_XOR_VALUE);
    } /* crcCompute() */

    不过用这个程序直接计算得到的CRC 值与STM32 给出的并不相同。之所以会这样是因为字节序的原因。可以举个例子来说明这个问题。比如我们有一片内存区域要计算CRC值。这片内存区域的起始地址是 0x1000,共有8个字节。用 crcCompute() 函数计算时是按照地址顺序依次传入各个字节。也就是先计算0x1000 处的字节,再计算0x0001 处的字节,以此类推最后计算0x1007 地址处的字节。而 STM32 的硬件CRC单元是以32位的字为单位计算的。我们知道CRC 实际上是个多项式的除法运算,而除法运算是从高位算起的。也就是相当于它是按照 0x10030x10020x10010x1000 这个顺序计算第一个字,然后按照0x10070x10060x1005x1004 的顺序计算第二个字。因此。我们要是预先将字节序调换一下得到结果就没有问题了。这就有了下面的改造。其中 remainder 传入 0xffffffff。因为STM32 中的CRC余数初始值为0xffffffff

    uint32_t stm32crc32(uint32_t * message, unsigned int nWords, uint32_t remainder)
    {
        unsigned int offset;
        unsigned char byte;
        unsigned char *p = (unsigned char *)message;
        //width_t remainder = INITIAL_REMAINDER;
        /* Divide the message by the polynomial, a byte at a time. */
        for( offset = 0; offset < nWords; offset++)
        {
            byte = (remainder >> (WIDTH - 8)) ^ p[3];
            remainder = crcTable[byte] ^ (remainder << 8);
    
            byte = (remainder >> (WIDTH - 8)) ^ p[2];
            remainder = crcTable[byte] ^ (remainder << 8);
    
            byte = (remainder >> (WIDTH - 8)) ^ p[1];
            remainder = crcTable[byte] ^ (remainder << 8);
    
            byte = (remainder >> (WIDTH - 8)) ^ p[0];
            remainder = crcTable[byte] ^ (remainder << 8);
    
            p += 4;
        }
        /* The final remainder is the CRC result. */
        return (remainder);
    } /* crcCompute() */

    大家可以验证这个函数的计算结果与STM32上的结果完全一样。

    写到这里本该就结束了,不过我要多说一句,之所以要这么麻烦的调换字节序,都是小端(little endian)惹的祸。要是都采用大端格式就没这些麻烦的转换了。





  • 相关阅读:
    块级标签与预格式化文本标签----------大多数XHTML可以表示为两种类型的标签:块标签(block tag)和内联标签(inline tag)
    下拉框与下拉框之间的联动效果
    下拉框与文本框之间的转换
    设置密码是否为可见
    html表单
    HTML基础2——综合案例3——创建考试报名表格
    HTML基础2——综合案例2——复杂的嵌套列表
    java配置、IntelliJ IDEA Ultimate激活、
    字节流转字符流OutputStreamWriter、InputStreamReader,关闭流的方法
    文件字节流、字符流、缓冲字节流、缓冲字符流、数据流
  • 原文地址:https://www.cnblogs.com/jiangu66/p/2996684.html
Copyright © 2020-2023  润新知