根據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了。