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