最近在幫客戶編寫基于S32K144編寫TPS929120的軟件驅動時,需要在發送數據的末端增加8bit的CRC校驗碼。因為之前使用E522.49時,其數據手冊直接提供了自身CRC校驗算法的代碼,而TPS929120的手冊只告知了算法的CRC多項式為 ,初始值為0xFF,所以代碼部分需要自己實現。
我查閱了TLD7002,BD18331,MPQ7225等LED Driver的數據手冊,都是采用的CRC校驗算法,其中MPQ7225的CRC校驗算法和TPS929120是一樣的。因為之前接觸CRC校驗不多,特定將相關的學習過程記錄下來,方便后面有需要時快速完成其他廠家的LED Driver軟件驅動。
CRC基礎知識
CRC概念
CRC(Cyclic Redundancy Checksum)是一種糾錯技術,代表循環冗余校驗和。
數據通信領域中最常用的一種差錯校驗碼,其信息字段和校驗字段長度可以任意指定,但要求通信雙方定義的CRC標準一致。主要用來檢測或校驗數據傳輸或者保存后可能出現的錯誤。它的使用方式可以說明如下圖所示:
在數據傳輸過程中,無論傳輸系統的設計再怎么完美,差錯總會存在,這種差錯可能會導致在鏈路上傳輸的一個或者多個幀被破壞(出現比特差錯,0變為1,或者1變為0),從而接受方接收到錯誤的數據。
為盡量提高接受方收到數據的正確率,在接收方接收數據之前需要對數據進行差錯檢測,當且僅當檢測的結果為正確時接收方才真正收下數據。檢測的方式有多種,常見的有 奇偶校驗 、因特網校驗和循環冗余校驗等。
CRC參數模型
一個完整的CRC參數模型應該包含以下信息: WIDTH,POLY,INIT,REFIN,REFOUT,XOROUT 。
- NAME :參數模型名稱。
- WIDTH :寬度,即生成的CRC數據位寬,如CRC-8,生成的CRC為8位
- POLY :十六進制多項式,省略最高位1,如 ,二進制為 1 0000 0111 ,省略最高位1,轉換為十六進制為0x07。
- INIT :CRC初始值,和WIDTH位寬一致。
- REFIN :true或false,在進行計算之前,原始數據是否翻轉,如原始數據:0x34 = 0011 0100 ,如果REFIN為true,進行翻轉之后為0010 1100 = 0x2c
- REFOUT :true或false,運算完成之后,得到的CRC值是否進行翻轉,如計算得到的CRC值:0x97 = 1001 0111 ,如果REFOUT為true,進行翻轉之后為11101001 = 0xE9。
- XOROUT :計算結果與此參數進行異或運算后得到最終的CRC值,和WIDTH位寬一致。
通常如果只給了一個多項式,其他的沒有說明則: **INIT=0x00,REFIN=false,REFOUT=false,XOROUT=0x00** 。
TPS929120的CRC值計算方式
CRC參數:
根據數據手冊,TPS929120選擇的是CRC多項式為 ,初始值為0xFF;所以POLY為0x31,INIT為FF。由于UART的數據傳輸是LSB(先傳輸低位),所以REFIN為True。綜上所述,TPS929120的CRC參數表為:
NAME = CRC_TPS929120
WIDTH = 8
POLY = 0x31 = 0011 0001(最高位1已經省略)
INIT = 0xFF
XOROUT = 0x00
REFIN = TRUE
REFOUT = FALSE
算法圖:
TPS929120數據手冊提供了自身CRC校驗的算法圖,如下所示:
因為TPS929120要求第一個發送的數據0x55(用于同步)不參與計算,所以下面介紹的幾種計算方式都不會將0x55納入CRC計算范圍。
計算方法一:
方法一按照TPS929120提供的算法圖進行實現。
計算步驟:
結合TPS929120數據手冊提供的算法圖,方法一的計算步驟如下所示:
1. 將0xFF作為CRC初始值。
2. 當第一個輸入字節的Bit0到來時,
2.1 將初始值的Bit7和輸入值的Bit0進行異或運算,得到的值保留,記作Temp_Bit;
2.2 將初始值的Bit6代替原本的Bit7;
2.3 將初始值的Bit5代替原本的Bit6;
2.4 將初始值的Bit4和Temp_Bit進行異或運算,使用得到的值代替原本的Bit5;
2.5 將初始值的Bit3和Temp_Bit進行異或運算,使用得到的值代替原本的Bit4;
2.6 將初始值的Bit2代替原本的Bit3;
2.7 將初始值的Bit1代替原本的Bit2;
2.8 將初始值的Bit0代替原本的Bit1;
2.9 使用Temp_Bit代替原本的Bit0;
3. 第一個輸入字節的剩余7bit也是同樣的操作,然后得到第一個輸入字節的CRC值。
4. 第二個輸入字節到來,使用上一次計算得到的CRC值作為CRC初始值,重復2,3步驟。
5. 當最后一個輸入字節計算完畢之后,就得到了最終的CRC校驗值。
實現代碼為:
- 單個字節的CRC值計算
#define BIT0 (0x01)
#define BIT1 (0x02)
#define BIT2 (0x04)
#define BIT3 (0x08)
#define BIT4 (0x10)
#define BIT5 (0x20)
#define BIT6 (0x40)
#define BIT7 (0x80)
unsigned int CRC_Calculation(unsigned int CRC_Initial, unsigned int Input_Data)
{
unsigned int Temp_Bit, Input_Bit;
/* store every bit value of Input_Data */
unsigned int bit0, bit1, bit2, bit3, bit4, bit5, bit6, bit7;
/* store the Input_Data (byte) 's CRC */
unsigned int CRC=0;
/* get every bit of CRC initial value */
unsigned int i=0; bit0 = CRC_Initial & BIT0;
bit1 = (CRC_Initial & BIT1)>>1;
bit2 = (CRC_Initial & BIT2)>>2;
bit3 = (CRC_Initial & BIT3)>>3;
bit4 = (CRC_Initial & BIT4)>>4;
bit5 = (CRC_Initial & BIT5)>>5;
bit6 = (CRC_Initial & BIT6)>>6;
bit7 = (CRC_Initial & BIT7)>>7;
for(i=0; i<8; i++)
{
/* extract one bit of Input Data (from LSB to MSB) */
Input_Bit = (Input_Data >> i) & 0x01;
/* Do Input_Bit XOR bit7 */
Temp_Bit = Input_Bit ^ bit7;
bit7 = bit6;
bit6 = bit5;
/* Do bit4 XOR Temp_Bit */
bit4 = bit4 ^ Temp_Bit;
bit5 = bit4;
/* Do bit3 XOR Tmep_Bit */
bit3 = bit3 ^ Temp_Bit;
bit4 = bit3;
bit3 = bit2;
bit2 = bit1;
bit1 = bit0;
bit0 = Temp_Bit;
}
CRC = (bit7<<7)|(bit6<<6)|(bit5<<5)|(bit4<<4)|(bit3<<3)|(bit2<<2)|(bit1<<1)|bit0;
return CRC;
}
- 命令幀的CRC值計算
unsigned int CRC(unsigned int commandFrame_withoutCRC[], unsigned int byteLength)
{
unsigned int j;
unsigned int CRCtemp;
for(j=0; j
計算方法二:
Github上面有一套成熟的開源CRC算法,如下,
GitHub - whik/crc-lib-c: 基于C語言的CRC校驗庫,包括常用的21個CRC參數模型實現^[1]^
參考其中的 CRC-8/MAXIM ,并根據TPS929120的CRC參數進行微調,得到的算法如下:
/* as UART transmit from LSB to MSB,
* invert the polynomial 0x31 (0011 0001) to 0x8C (1000 1100)
*/
#define polynomialINV 0x8C
#define LSB 0x01
/* calculate CRC of command frame */
unsigned int CRC_LUT(unsigned int commandFrame_withoutCRC[], unsigned int byteLength)
{
unsigned int remainder, k, j;
/* assign the initial value 0xFF */
remainder = 0xFF;
/* the first SYNC byte not engage CRC calculation */
for(k=1;k
計算方法三:
方法三在方法二的基礎上進行優化,將CRC的計算部分(如下圖)提前做成數組,涵蓋0-255的CRC計算結果,然后用到的時候直接查表。
for(i=0; i<8; i++)
{
if(remainder & LSB)
{
/* right shift 1 bit and do the XOR operation with 0x8C */
remainder = (remainder>>1) ^ polynomialINV;
}
else
{
remainder = remainder>>1;
}
}
最終的實現形式如下:
/* as UART transmit from LSB to MSB,
* invert the polynomial 0x31 (0011 0001) to 0x8C (1000 1100)
*/
#define polynomialINV 0x8C
#define LSB 0x01
unsigned int crcArray[256];
/* calculate and store the CRC of data from 0x00 to 0xFF */
void crcInitial()
{
unsigned int k, j, remainder;
for(k=0;k<256;k++)
{
remainder=k;
for(j=8;j>0;j--)
{
if(remainder & LSB)
{
/* right shift 1 bit and do the XOR operation with 0x8C */
remainder = (remainder>>1) ^ polynomialINV;
}
else
{
remainder = remainder>>1;
}
}
crcArray[k]=remainder;
}
}
/* calculate CRC of command frame */
unsigned int CRC_LUT(unsigned int commandFrame_withoutCRC[], unsigned int byteLength)
{
unsigned int remainder, tempData, k;
/* assign the initial value 0xFF */
remainder = 0xFF;
/* the first SYNC byte not engage CRC calculation */
for(k=1;k
驗證:
使用一個數組{0x55,0x80,0x61,0x00}進行驗證,其中0x55不參與CRC計算,以計算方法一為例,添加打印的mian函數如下:
#include
int main()
{
unsigned int data[4] = {0x55,0x80,0x61,0x00};
unsigned int crc;
for(int i = 0; i < 4; i++)
{
printf("%02x ", data[i]);
}
printf("\\n");
crc = CRC(data,4);
printf("Method 1:CRC result of TPS929120 is %02x\\n", crc);
return 0;
}
使用在線C語言編譯器(菜鳥教程在線編輯器 (runoob.com) ^[2]^ )進行驗證,計算結果如下圖,為0x74:
使用在線CRC計算工具進行驗證,結果也是0x74:
使用TPS929120自帶的Excel形式的CRC計算工具進行驗證,結果也是0x74:
其他兩種方式經過驗證也是一樣的結果,有興趣的讀者可以使用在線C語言編輯器試一下。
-
C語言
+關注
關注
180文章
7599瀏覽量
136213 -
CRC校驗
+關注
關注
0文章
84瀏覽量
15188 -
UART接口
+關注
關注
0文章
124瀏覽量
15268 -
S32k144
+關注
關注
1文章
7瀏覽量
1884
發布評論請先 登錄
相關推薦
評論