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)惹的祸。要是都采用大端格式就没这些麻烦的转换了。

 


 




 

posted @ 2013-04-02 22:29  坚固66  阅读(404)  评论(0编辑  收藏  举报