STM32开发中CRC寄存器的作用

(一)STM32中CRC循环冗余校验基本介绍

  循环冗余校验(CRC)计算单元是根据固定的生成多项式得到任一32位全字的CRC计算结果。在其他的应用中, CRC技术主要应用于核实数据传输或者数据存储的正确性和完整性。标准EN/IEC 60335-1即提供了一种核实闪存存储器完整性的方法。 CRC计算单元可以在程序运行时计算出软件的标识,之后与在连接时生成的参考标识比较,然后存放在指定的存储器空间。STM32中的CRC功能,用硬件代替软件,可以提高CRC计算的速度,不占用CPU的内存,IAP或者OTA做固件更新时,可对固件完整性做校验。

IAP(In Application Programming)是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。

空中下载(OTA)是通过移动通信的空中接口实现对移动终端设备及SIM卡数据进行远程管理的技术。经过公网多年的应用与发展,已十分成熟,网络运营商通过OTA技术实现SIM卡远程管理,还能提供移动化的新业务下载功能(来源于百度百科)


(二)CRC算法分析

1、发送端 
1.1、在发送端先将数据分组,每组k个数据。假定要传送的数据是M。 
1.2、在数据M后面添加供差错检测的n位冗余码,然后构成一帧发送出去,一共发送(k+n)位。 
虽然添加n位冗余码增大了数据传送的开销,但是可以进行差错检测,当传输可能出现差错时,付出这种代价是值得的。

1.3、冗余码可以用下面的方法得出: 
1.3.1、用二进制模2运算进行2^n*M(相当于M左移n位)的运算。意思就是在M后面补n个0。现在M就变成了k+n位。 
1.3.2、用M除以收发双方事先商定的长度为n+1的除数P。 
1.3.3、得到的余数R,这个R就是FCS(帧检验序列)。将这个FCS序列加到M上然后发出去。

 

2、接受端 
2.1、在接受端把接受到的数据以帧为单位进行CRC校验 
2.2、把收到的每一个帧都除以同样的除数P,然后检查余数R。 
2.3、如果余数R为0,如果在传输过程中没有差错。 
2.4、如果出现误码,那么余数R为零的概率是非常小的。

总结:在接收端对接收到的每一帧进行CRC检验后,若余数R为,则表示这个帧没有错,就接受。若R不为0,则判定这个帧出错,就丢弃。

 

例:M=101001,P=1101,n=3。 
在发送端: 
1、M=(2^n*M); 
则:M=101001000 
2、用M除以P

 

3、得到余数R也就是FCS,将FCS加到M上,就得到了要发送的帧。 
M=101001000+FCS=101001001

在接收端: 
接收到的每一帧都要进行差错检验,假设收到101001001,P=1101。

最后余数R=0,则判定这个帧没有出错。

CRC原理介绍(专业讲):
      在K位信息码后再拼接R位的校验码,整个编码长度为N位,因此,这种编码也叫(N,K)码。对于一个给定的(N,K)码,可以证明存在一个最高次幂为N-K=R的多项式G(x)。根据G(x)可以生成K位信息的校验码,而G(x)叫做这个CRC码的生成多项式。校验码的具体生成过程为:假设要发送的信息用多项式C(X)表示,将C(x)左移R位(可表示成C(x)*2R),这样C(x)的右边就会空出R位,这就是校验码的位置。用 C(x)*2R 除以生成多项式G(x)得到的余数就是校验码。任意一个由二进制位串组成的代码都可以和一个系数仅为‘0’和‘1’取值的多项式一一对应。例如:代码1010111对应的多项式为x6+x4+x2+x+1,而多项式为x5+x3+x2+x+1对应的代码101111。

      这里还有个问题,如果被除数比除数小,那么余数就是被除数本身,比如说只要传一个字节,那么他的CRC就是他自己,为避免这种情况发生,在做除法之前先将他移位,使他大鱼除数,那么移位多少位呢?这是所选的固定除数有关,左移位数比除数的位数少1,下面是常用标准中的除数:

CRC8:多项式是X8+X5+X4+1,对应的数字是0x131,左移8位
CRC12:多项式是X12+X11+X3+X2+1,对应的数字是0x180D,左移12位
CCITT CRC16:多项式是X16+X12+X5+1,对应的数字是0x11021,左移16位
ANSI CRC16:多项式是X16+X15+X2+1,对应的数字是0x18005,左移16位
CRC32:多项式是X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X1+1,对应数字是0x104C11DB7,左移32

  因此,在得到字节串对应的数字后,再将数字左移M位(比如ANSI-CRC16是左移16位),就得到了被除数。

  好了,现在被除数和除数都有了,那么就要开始做除法求CRC校验码了。CRC除法的计算过程与我们笔算除法类似,首先是被除数与除数高位对齐后,被除数减去除数,得到了差,除数再与差的最高位对齐,进行减法,然后再对齐再减,直到差比除数小,这个差就是余数。不过和普通减法有差别的是,CRC的加(减)法是不进(借)位的,比如10减01,它的结果是11,而不是借位减法得到的01,因此,实际上CRC的加法和减法所得的结果是一样的,比如10加01的结果是11,10减01的结果也是11,这其实就是异或操作。

(三)CRC计算单元源码解析

CRC计算单元含有132位数据寄存器:

● 对该寄存器进行写操作时,作为输入寄存器,可以输入要进行CRC计算的新数据。

● 对该寄存器进行读操作时,返回上一次CRC计算的结果。

每一次写入数据寄存器,其计算结果是前一次CRC计算结果和新计算结果的组合(对整个32位字
进行CRC计算,而不是逐字节地计算)

CRC计算期间会暂停CPU的写操作,因此可以对寄存器CRC_DR进行背靠背写入或者连续地

-读操作。
可以通过设置寄存器CRC_CRRESET位来重置寄存器CRC_DR0xFFFF FFFF。该操作不
影响寄存器CRC_IDR
内的数据。
CRC计算单元包括2个数据寄存器和1个控制寄存器

 

#include "stm32f10x_crc.h"
void CRC_ResetDR(void)
{
  /* Reset CRC generator */
  CRC->CR = CRC_CR_RESET;/*将CR控制寄存器的第0位置1,复位CRC计算单元,设置数据寄存器为0xFFFF FFFF,只能对该位写’1’,它由硬件自动清’0’*/
}

/**
  * @brief  Computes the 32-bit CRC of a given data word(32-bit).
  * @param  Data: data word(32-bit) to compute its CRC
  * @retval 32-bit CRC
  */
uint32_t CRC_CalcCRC(uint32_t Data)
{
  CRC->DR = Data;
  
  return (CRC->DR);/*写入CRC计算器的新数据时,作为输入寄存器读取时返回CRC计算的结果*/
}

/**
  * @brief  Computes the 32-bit CRC of a given buffer of data word(32-bit).
  * @param  pBuffer: pointer to the buffer containing the data to be computed
  * @param  BufferLength: length of the buffer to be computed                    
  * @retval 32-bit CRC
  */
uint32_t CRC_CalcBlockCRC(uint32_t pBuffer[], uint32_t BufferLength)
{
  uint32_t index = 0;
  
  for(index = 0; index < BufferLength; index++)
  {
    CRC->DR = pBuffer[index];/*在DR数据寄存器中输入缓冲区的数据,并计算数据CRC校验的结果*/
  }
  return (CRC->DR);
}

/**
  * @brief  Returns the current CRC value.
  * @param  None
  * @retval 32-bit CRC
  */
uint32_t CRC_GetCRC(void)
{
  return (CRC->DR);/*返回CRC运算的结果*/
}

/**
  * @brief  Stores a 8-bit data in the Independent Data(ID) register.
  * @param  IDValue: 8-bit value to be stored in the ID register                     
  * @retval None
  */
void CRC_SetIDRegister(uint8_t IDValue)
{
  CRC->IDR = IDValue;
}

/**
  * @brief  Returns the 8-bit data stored in the Independent Data(ID) register
  * @param  None
  * @retval 8-bit value of the ID register 
  */
uint8_t CRC_GetIDRegister(void)
{
  return (CRC->IDR);
}

 

 

 

posted @ 2020-10-18 16:59  banglezyl  阅读(2289)  评论(0)    收藏  举报