MM32SPIN2x 電機專用MCU功能特色——CRC計算單元
上一章節(jié)中已經(jīng)教大家如何使用MM32SPIN2x的UARTBit9模式,本章節(jié)將與大家一起使用CRC模塊進行數(shù)據(jù)校驗。在數(shù)據(jù)傳輸過程中,無論傳輸系統(tǒng)的設計再怎么完美,差錯總會存在,這種差錯可能會導致在鏈路上傳輸?shù)囊粋或者多個幀被破壞(出現(xiàn)比特差錯,0變?yōu)?,或者1變?yōu)?),從而接受方接收到錯誤的數(shù)據(jù)。為盡量提高接受方收到數(shù)據(jù)的正確率,在接受方接收數(shù)據(jù)之前需要對數(shù)據(jù)進行差錯檢測,當且僅當檢測的結果為正確時接收方才真正收下數(shù)據(jù)。檢測的方式有多種,常見的有奇偶校驗、因特網(wǎng)校驗和循環(huán)冗余校驗等。
其中循環(huán)冗余校驗(CRC)原理實際上就是在一個p位二進制數(shù)據(jù)序列之后附加一個r位二進制檢驗碼(序列),從而構成一個總長為n=p+r位的二進制序列;附加在數(shù)據(jù)序列之后的這個檢驗碼與數(shù)據(jù)序列的內(nèi)容之間存在著某種特定的關系。如果因干擾等原因使數(shù)據(jù)序列中的某一位或某些位發(fā)生錯誤,這種特定關系就會被破壞。因此,通過檢查這一關系,就可以實現(xiàn)對數(shù)據(jù)正確性的檢驗。只要經(jīng)過嚴格的挑選,并使用位數(shù)足夠多的除數(shù) P,那么出現(xiàn)檢測不到的差錯的概率就很小很小。CRC是一種常用的檢錯碼,無法檢測出錯誤在哪里,因此并不能用于自動糾錯,一般的做法是丟棄接收的數(shù)據(jù)。
從上面的程序框圖,我們可以發(fā)現(xiàn),多項式的冪次越高,校驗效果越好,但所花費的時間也越長。因此常用查表法或?qū)S玫挠布﨏RC模塊來提高效率。
其中查表法,是事先根據(jù)特定的校驗多項式,算出1字節(jié)數(shù)據(jù)范圍所對應的256個余數(shù),將其作為表格,編程寫到程序存儲器中查詢而避免在線運算,已是非常通用的做法。但如果是CRC16校驗,存儲表格要占512字節(jié)(CRC32則需要1 KB),對于有限的單片機ROM資源來說所占比例不小,往往只因為多裝了此表,就不得不升級單片機的型號。
在SPIN2x系列MCU中,加入了一個CRC計算單元,使用1個32位數(shù)據(jù)寄存器作為輸入和輸出,在執(zhí)行寫操作時輸入CRC計算的新數(shù)據(jù),在執(zhí)行讀操作時返回上一次CRC計算的結果。每一次寫入數(shù)據(jù)寄存器,都會對整個32位字進行CRC計算,而不是逐字節(jié)地計算,從而節(jié)省大量的時間。
• 使用CRC-32(以太網(wǎng))多項式: 0x4C11DB7
• 每次CRC計算需要4個AHB時鐘周期
• 在 CRC 計算期間會暫停寫操作,因此可以對寄存器 CRC_DR 進行背靠背寫入或者連續(xù)地寫-讀操作。
• 可以通過設置寄存器CRC_CTRL的RESET位來重置寄存器 CRC_DR 為 0xFFFFFFFF,該操作不影響8位獨立數(shù)據(jù)寄存器CRC_IDR內(nèi)的數(shù)據(jù)。
圖2 CRC計算單元框圖
下面我們來看一下在程序中軟件CRC與硬件CRC的配置。
CRC模塊的配置步驟如下:
CRC模塊時鐘使能
CRC_CR的第一位RESET位復位(可選)
將數(shù)據(jù)寫入CRC_DR寄存器
從CRC_DR寄存器中讀出計算結果
程序中配置如下:
uint32_t Hardware_CRC(u32*addr, int num)
{
CRC->CR|=1; //復位
for (; num > 0; num--)
CRC->DR = (*addr++);
return CRC->DR;
}
我們可以使用軟件算法來檢驗計算結果,對比兩種方式花費的時間。
軟件算法如下:
u32 Software_CRC (u32 *ptr,u32 len)
{
u32 xbit;
u32 data;
u32 CRC32 = 0xFFFFFFFF;
u32 bits;
const u32 dwPolynomial =0x04C11DB7 ;
u32 i;
for(i = 0;i < len;i++)
{
xbit = (unsigned)1 <<31;
data = ptr[i];
for (bits = 0; bits < 32;bits++)
{
if (CRC32 & 0x80000000)
{
CRC32 <<= 1;
CRC32 ^= dwPolynomial;
}
else
CRC32 <<= 1;
if (data & xbit)
CRC32 ^= dwPolynomial;
xbit >>= 1;
}
}
return CRC32;
}
下面我們用兩個簡單的數(shù)組,一個數(shù)組從0x00遞增到0x7F,另一個數(shù)組從0x7F遞減到0x00,分別使用硬件CRC和軟件CRC計算,同時使用TIM1進行計時,最后通過UART輸出得到的校驗碼和花費的時間。
計算和輸出程序:
void CRCTest()
{
unsigned int i;
u32 CRCtime,CRCresault;
u32 crc1[128];
for(i=0;i<128;i++)
crc1[i] = i;
printf("CRC_test\r\n");
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC,ENABLE);//使能CRC時鐘
CRC->CR|=1; //復位
printf("\r\nCRC_DR=%x\t\r\n",CRC->DR);//輸出復位值
TIM1->CNT &= 0;
CRCresault=Hardware_CRC(crc1,128);
CRCtime =TIM1->CNT;
printf("Hardware_CRC1:resault=%08x\ttime=%d\r\n",CRCresault,CRCtime);
TIM1->CNT &= 0;
CRCresault=Software_CRC(crc1,128);
CRCtime =TIM1->CNT;
printf("Software_CRC1:resault=%08x\ttime=%d\r\n",CRCresault,CRCtime);
for(i=0;i<256;i++)
crc1[i] = 0xFF-i;
TIM1->CNT &= 0;
CRCresault=Hardware_CRC(crc1,128);
CRCtime =TIM1->CNT;
printf("Hardware_CRC2:resault=%08x\ttime=%d\r\n",CRCresault,CRCtime);
TIM1->CNT &= 0;
CRCresault=Software_CRC(crc1,128);
CRCtime =TIM1->CNT;
printf("Software_CRC2:resault=%08x\ttime=%d\r\n",CRCresault,CRCtime);
}
定時器配置程序:
void Tim1_UPCount_test(u16Prescaler,u16 Period)
{
TIM_TimeBaseInitTypeDefTIM_StructInit;
/*使能TIM1時鐘,默認時鐘源為PCLK2(PCLK2未分頻時不倍頻,否則由PCLK2倍頻輸出),可選其它時鐘源*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
TIM_StructInit.TIM_Period=Period; //ARR寄存器值
TIM_StructInit.TIM_Prescaler=Prescaler; //預分頻值
/*數(shù)字濾波器采樣頻率,不影響定時器時鐘*/
TIM_StructInit.TIM_ClockDivision=TIM_CKD_DIV1; //采樣分頻值
TIM_StructInit.TIM_CounterMode=TIM_CounterMode_Up; //計數(shù)模式
TIM_StructInit.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM1,&TIM_StructInit);
TIM_Cmd(TIM1, ENABLE);
/*更新定時器時會產(chǎn)生更新時間,清除標志位*/
TIM_ClearFlag(TIM1,TIM_FLAG_Update);
}
Main函數(shù):
int main(void)
{
Tim1_UPCount_test(48-1,0xffff); //APB2時鐘為48M,48分頻后TIM1時鐘為1MHz
delay_init();
uart_initwBaudRate(9600); //初始化UART
CRCTest();
while(1) { }
}
圖3 軟件CRC與硬件CRC計算結果
從結果中,我們可以看到,對于這兩個數(shù)組,硬件CRC與軟件CRC所得到的結果一致,但是硬件CRC每個數(shù)組只花費了52us,遠小于軟件CRC的2.5ms,由此可見,在進行大量數(shù)據(jù)處理的時候,使用硬件CRC模塊可以節(jié)省大量的時間,同時保證了計算結果的正確。
編輯:admin 最后修改時間:2019-06-15