根据CRC’32模型,编写M4特有的硬件CRC计算函数软件版以下:算法
<span style="font-size:14px;">unsigned long crc32_m4( const unsigned char *buf, unsigned long len ) { unsigned long rem = 0xFFFFFFFF; unsigned long byte_p = 0; int bit_p = 0; if (buf != 0) { for (byte_p = 0; byte_p < len; byte_p++) { for (bit_p = 7; bit_p >= 0; bit_p--) { if (((rem >> 31U) ^ (buf[byte_p] >> bit_p)) & 1U) { rem = (rem << 1U) ^ 0x04C11DB7U; } else { rem = rem << 1U; } } } } return rem; }</span>CRC算法有一个很奇妙的性质,以32位的CRC来说,那就是,当数据的位数小于等于32的时候,不一样的数据获得的校验和必定不重复。1~4字节的数据,长度相同且内容不一样的数据,获得的CRC32必定不一样,固然CRC’32也必定不一样。进一步说,对于32位的数据,其等效的32位整型值与32位CRC校验和是一一对应的,稍微花点时间就能够穷举出咱们须要的“魔术字节”了。
接下来讨论前补七字节的方法。前四字节的32位进行穷举,后三字节固定为零,即要寻找数组
CRC’32(?? ?? ?? ?? 00 00 00) == 0xFFFFFFFF 的状况。函数
<span style="font-size:14px;">#include <stdio.h> #define LENGTH 7 int main() { unsigned long i[2] = { 0, 0 }; while (1) { if (crc32_m4 ((unsigned char *)i, LENGTH) == 0xFFFFFFFF) { printf ("ans: %08X \n", i[0]); break; } i[0]++; } return 0; }</span>算出这七个字节是:6A A5 9E 9D 00 00 00。
<span style="font-size:14px;">#include <stdint.h> #include "stm32f4xx_crc.h" #include "stm32f4xx_rcc.h" uint32_t crc32_std( const uint8_t *buf, uint32_t len ) { uint32_t ans = 0; if (buf > 0 && buf + (len - 1) > buf) { uint32_t i = 0, v = 0; RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_CRC, ENABLE); CRC_ResetDR (); switch (len % 4) { while (1) { v = ((v >> 0x01) & 0x55555555) | ((v & 0x55555555) << 0x01); v = ((v >> 0x02) & 0x33333333) | ((v & 0x33333333) << 0x02); v = ((v >> 0x04) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 0x04); CRC->DR = v; // <-- CRC_CalcCRC v; if (i >= len) { break; } case 0: // 全部的字节能够按照4字节一组构成32位的组, 故没必要前补额外的字节 v = buf[i] << 24 | buf[i + 1] << 16 | buf[i + 2] << 8 | buf[i + 3]; i += 4; continue; case 1: // 因多出1个字节没法构成32位, 故前补7字节以将之补齐 CRC->DR = 0x6AA59E9D; // <-- CRC_CalcCRC 0x6AA59E9D; v = buf[i]; i += 1; continue; case 2: // 因多出2个字节没法构成32位, 故前补6字节以将之补齐 CRC->DR = 0x9746CD0A; // <-- CRC_CalcCRC 0x9746CD0A; v = buf[i] << 8 | buf[i + 1]; i += 2; continue; case 3: // 因多出3个字节没法构成32位, 故前补5字节以将之补齐 CRC->DR = 0xCC6021D0; // <-- CRC_CalcCRC 0xCC6021D0; v = buf[i] << 16 | buf[i + 1] << 8 | buf[i + 2]; i += 3; continue; } default: break; } v = CRC->DR; // <-- CRC_GetCRC; v = ((v >> 0x01) & 0x55555555) | ((v & 0x55555555) << 0x01); v = ((v >> 0x02) & 0x33333333) | ((v & 0x33333333) << 0x02); v = ((v >> 0x04) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 0x04); v = ((v >> 0x08) & 0x00FF00FF) | ((v & 0x00FF00FF) << 0x08); v = ((v >> 0x10) & 0x0000FFFF) | ((v & 0x0000FFFF) << 0x10); ans = ~v; RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_CRC, DISABLE); } return ans; }</span>代码中的CRC->DR是硬件CRC的寄存器,向其写值即是输入数据,从之读数即是获取校验和。代码中对变量v进行的大量位运算,是为了进行高低位的逆序。虽然可使用查找表法来进行逆序,可是,要使用查找表的话,倒不如直接用查找表来软件计算CRC了。