STM32F 系列的单片机内部带了CRC32 计算单元。这个内置CRC模块的方法使用非常简单。其操作如下图所示。
图 1 CRC计算单元框图
归纳起来有如下几步操作:
1. 开启CRC单元的时钟。RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE)
2. 复位CRC模块(设置CRC_CR=0x01),这个操作把CRC余数初始化为0xFFFFFFFF
3. 把要计算的数据按逐个地写入CRC_DR寄存器
4. 写完所有的数据字后,从CRC_DR寄存器读出计算的结果
STM32F10x StdPeriph Driver 中提供了几个函数。
CRC_ResetDR(void)
用来复位CRC模块。
uint32_t CRC_CalcCRC(uint32_t Data)
将一个数据写入CRC_DR寄存器,返回值为计算结果。
uint32_t CRC_CalcBlockCRC(uint32_t pBuffer[], uint32_t BufferLength)
计算一个数组的CRC 值。
uint32_t CRC_GetCRC(void)
读取CRC_DR寄存器的结果。
另外,CRC 模块中还有个独立数据寄存器(CRC_IDR)。这是个单字节的寄存器,用于临时存放1字节的数据,不受复位操作影响。相应的操作函数有两个。
void CRC_SetIDRegister(uint8_t IDValue)
uint8_t CRC_GetIDRegister(void)
分别是写CRC_IDR和读 CRC_IDR 寄存器。
虽然STM32F 上的CRC 单元用起来很简单,但是似乎它计算出来的结果与传统的CRC32算法得到的结果有些不同。
下面是个简单的例子。
#include "stm32f10x.h"
int main(void)
{
uint32_t j;
uint32_t str[11] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', ' '};
SystemInit();
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);
CRC_ResetDR();
str[9] = CRC_CalcBlockCRC(str, 1);
CRC_ResetDR();
CRC_CalcCRC(0xA5A5A5A5);
j = CRC_GetCRC();
CRC_CalcCRC(j);
for(;;)
{
}
}
。还指出了这个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 /* * 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 实际上是个多项式的除法运算,而除法运算是从高位算起的。也就是相当于它是按照 0x1003、0x1002、0x1001、0x1000 这个顺序计算第一个字,然后按照0x1007、0x1006、0x1005、x1004 的顺序计算第二个字。因此。我们要是预先将字节序调换一下得到结果就没有问题了。这就有了下面的改造。其中 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);
上一篇:STM32F10x 学习笔记之SysTick 定时器
下一篇:STM32F10x 学习笔记之独立看门狗IWDG 模块
推荐阅读最新更新时间:2024-11-10 22:02