STM32硬件CRC计算CRC8

最重要收获:了解到同一个寄存器按字节,半字和字访问的区别。同一个内存寄存器地址,强转为volitale uint8_t *类型,volitale uint16_t *类型和volitale uint32_t *类型时,若其支持按字节,半字和字访问时,这三个类型写入的结果对CPU来说是不一致的。感觉支持多类型访问的寄存器,写入低字节时,CPU记录低字节有数据更新,然后只进行低字节数据运算。低2字节有数据更新,CPU记录低2字节有变化,然后只进行低2字节数据计算。当写入4字节数据时,cpu记录4个字节均有变化,然后进行4字节数据运算。感觉一个寄存器,在CPU测有多个写入记录。

1.STM32L071单片机的硬件CRC使用,单片机手册见CRC章节介绍;

2.需注意的点:CRC输入数据按字节,半字和字反转,反转的含义即数据bit位置互换,即原数据从高位开始计算bit,则反转后从低位开始计算bit。如0x51的bit如图,其二级制为0b0101 0001,则反转即原来的0-7bit位置,依次用7-0bit位置替换,反转后结果为0x8A=0b1000 1010。如图:原来的bit0反转后变为bit7,bit1反转后为bit6,依次bit7反转后为bit0。图示的是按字节反转的结果。按半字反转即原始数据bit15变为反转后bit0,原始数据bit14变为反转后bit1,....,原始数据bit1变为反转后bit14,0原始数据bit1变为反转后bit15;按字反转即31原始数据bit变为反转后bit0,原始数据bit30变为反转后bit1,....,原始数据bit1变为反转后bit30,0原始数据bit1变为反转后bit31。如0x5128,按半字反转后结果为0x148A;数据0x51286983按字反转后结果为0xC196148A。

STM32硬件CRC支持对输入数据按字节,半字和字反转,支持对输出结果进行反转。

3.重点在于STM32硬件CRC的DR寄存器理解,手册上指出该寄存器可以按字访问,右对齐的按半字和右对齐按字节访问,需理解该访问含义。

手册上的给出DR寄存器如下描述,最后红线部分描述数据写入时的操作,可动态调整数据大小,从而减少写入的访问次数。此处的动态调整数据大小指动态调整写入DR寄存器数据的大小,DR寄存器可以按字节、半字和字访问,动态调整即为可以在写入时采用按字节,半字或字的访问方式把数据写入DR寄存器,写入访问大小在计算不同多项式因子的结果时尤为重要。如用该硬件CRC计算CRC8结果时,使用CRC8/ROHC,多项式因子为0x07,初始值0XFF,结果异或值0x00,输入数据反转,输出数据反转。

用于初始化和计算的数据代码如下

void MX_CRC_Init(void)
{

  hcrc.Instance = CRC;
  hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_DISABLE;   //不使用默认的多项式因子
  hcrc.Init.GeneratingPolynomial = 0x07;                         //自定义多项式因子0x07
  hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_DISABLE;    //不使用默认初始化值
  hcrc.Init.InitValue           = 0xFF;                          //自定义初始值0xFF
  hcrc.Init.CRCLength           = CRC_POLYLENGTH_8B;             //CRC多项式因子宽度为8
  hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_BYTE; //使能输入数据反转
  hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_ENABLE; //使能输出数据反转
  hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;                  //输入数据格式为字节序
  if (HAL_CRC_Init(&hcrc) != HAL_OK)
  {
    Error_Handler();
  }

}

uint8_t crc_buf[] = {0x55,0xaa,0x99,0x88,0x66,0x88,0x77,0x44,0x55,0x33};   //CRC结果为0xFC,正确
uint32_t crc_buf[] = {0x55,0xaa,0x99,0x88,0x66,0x88,0x77,0x44,0x55,0x33};  //CRC结果为0x8B,相当于如下数组数据,结果与每个字节后都增加了3个0x00数据
//{0x55,0x00,0x00,0x00,0xaa,0x00,0x00,0x00,0x99,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x77,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x55,0x00,0x00,0x00,0x33,0x00,0x00,0x00}
//是由于函数在写入时,数组的每个数据都会左移24位后写到DR寄存器中,此时写入DR寄存器中的数据宽度为32bit,进行CRC计算时会把DR寄存器中的所有数据均进行计算,故得出的结果与实际想要的结果不同
uint32_t crc_result = 0;
int main(void)
{
  MX_CRC_Init();       //初始化CRC寄存器,见上述第1行函数定义
  /* USER CODE BEGIN 2 */
  crc_result = HAL_CRC_Calculate(&hcrc,(uint32_t *)crc_buf,sizeof(crc_buf));      //CRC计算,见第32行
}

uint32_t HAL_CRC_Calculate(CRC_HandleTypeDef *hcrc, uint32_t pBuffer[], uint32_t BufferLength)
{
  uint32_t index;      
  uint32_t temp = 0U;  
  hcrc->State = HAL_CRC_STATE_BUSY; 
  __HAL_CRC_DR_RESET(hcrc);   //重新复位CRC寄存器,不会累积上次的CRC计算结果
  switch (hcrc->InputDataFormat)
  {
    case CRC_INPUTDATA_FORMAT_WORDS:
      /* Enter 32-bit input data to the CRC calculator */
      for (index = 0U; index < BufferLength; index++)
      {
        hcrc->Instance->DR = pBuffer[index];
      }
      temp = hcrc->Instance->DR;
      break;
    case CRC_INPUTDATA_FORMAT_BYTES:   //使用该逻辑部分计算CRC结果,按字节序格式写入数据
      /* Specific 8-bit input data handling  */
      temp = CRC_Handle_8(hcrc, (uint8_t *)pBuffer, BufferLength); //见第63行原始库代码和100行自己修改后代码
      break;
    case CRC_INPUTDATA_FORMAT_HALFWORDS:
      /* Specific 16-bit input data handling  */
      temp = CRC_Handle_16(hcrc, (uint16_t *)(void *)pBuffer, BufferLength);    /* Derogation MisraC2012 R.11.5 */
      break;
    default:
      break;
  }
  hcrc->State = HAL_CRC_STATE_READY;
  return temp;
}

static uint32_t CRC_Handle_8(CRC_HandleTypeDef *hcrc, uint8_t pBuffer[], uint32_t BufferLength)//原始库代码
{
  uint32_t i; /* input data buffer index */
  uint16_t data;
  __IO uint16_t *pReg;
  uint32_t temp = 0;
  for (i = 0U; i < (BufferLength / 4U); i++)
  {
   temp =               ((uint32_t)pBuffer[4U * i] << 24U) | \
                         ((uint32_t)pBuffer[(4U * i) + 1U] << 16U) | \
                         ((uint32_t)pBuffer[(4U * i) + 2U] << 8U)  | \
                         (uint32_t)pBuffer[(4U * i) + 3U];
    hcrc->Instance->DR = temp;  //首先按字写入数据,所有写入DR寄存器的数据均进行CRC计算
  }
  if ((BufferLength % 4U) != 0U) //数据长度不是4字节对齐
  {
    if ((BufferLength % 4U) == 1U)  //余1个字节数据
    {
      *(__IO uint8_t *)(__IO void *)(&hcrc->Instance->DR) = pBuffer[4U * i]; //按字节写入,此时仅对单字节进行CRC运算,高3字节不参与CRC运算
    }
    if ((BufferLength % 4U) == 2U)
    {
      data = ((uint16_t)(pBuffer[4U * i]) << 8U) | (uint16_t)pBuffer[(4U * i) + 1U];
      pReg = (__IO uint16_t *)(__IO void *)(&hcrc->Instance->DR);         //按半字写入,此时仅对低半字进行CRC运算,高2字节不参与CRC运算
      *pReg = data;
    }
    if ((BufferLength % 4U) == 3U)
    {
      data = ((uint16_t)(pBuffer[4U * i]) << 8U) | (uint16_t)pBuffer[(4U * i) + 1U];
      pReg = (__IO uint16_t *)(__IO void *)(&hcrc->Instance->DR);                    /* Derogation MisraC2012 R.11.5 */
      *pReg = data;

      *(__IO uint8_t *)(__IO void *)(&hcrc->Instance->DR) = pBuffer[(4U * i) + 2U];  /* Derogation MisraC2012 R.11.5 */
    }
  }
  return hcrc->Instance->DR;
}
static uint32_t CRC_Handle_8(CRC_HandleTypeDef *hcrc, uint8_t pBuffer[], uint32_t BufferLength)//自己修改的代码
{
  uint32_t i; /* input data buffer index */

  for (i = 0U; i < BufferLength; i++)
  {
    *(__IO uint8_t *)(__IO void *)(&hcrc->Instance->DR) = pBuffer[i]; //按字节写入,此时仅对单字节进行CRC运算,高3字节不参与CRC运算
  }
  return hcrc->Instance->DR;
}

最初把crc_buf类型设置为uint32_t类型,最后计算得出的CRC结果不正确结果为0x8B,实际与如下图结果一致,把类型设置为uint8_t类型后结果正确结果为0xFC。

使用CRC_Handle_8原始库代码和自己修改的代码,计算结果都是0xFC。从中可以清楚的看到DR寄存器按字节,按半字和按字访问的区别。按字访问时,写入DR寄存器中的所有数据均参与运算,即32bit数据均参与运算。按半字访问时,DR寄存器的低2字节参与CRC运算;按字访问时,DR寄存器的低字节参与CRC运算。按字节访问即把hcrc->Instance->DR地址强转为volitale uint8_t *类型,按半字访问即把hcrc->Instance->DR地址强转为volitale uint16_t *类型.

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
crc8校验的原理,程序和检验软件 CRC即循环冗余校验码(Cyclic Redundancy Check):是数据通信领域中最常用的一种差错校验码,其特征是信息字段和校验字段的长度可以任意选定。 CRC校验可以简单地描述为:例如我们要发送一些数据(信息字段),为了避免一些干扰以及在接收端的对读取的数据进行判断是否接受的是真实的数据,这时我们就要加上校验数据(即CRC校验码),来判断接收的数据是否正确。在发送端,根据要传送的k位二进制码序列,以一定的规则(CRC校验有不同的规则。这个规则,在差错控制理论中称为“生成多项式”。)产生一个校验用的r位校验码(CRC码),附在原始信息后边,构成一个新的二进制码序列数共k+r位,然后发送出去。在接收端,根据信息码和CRC码之间所遵循的规则(即与发送时生成CRC校验码相同的规则)进行检验,校验采用计算机的模二除法,即除数和被除数(即生成多项式)做异或运算,进行异或运算时除数和被除数最高位对齐,进行按位异或运算,若最终的数据能被除尽,则传输正确;否则,传输错误。 CRC8即最终生成的CRC校验码为1字节,其生成多项式,生成多项式为g(x)=x8+x5+x4+1,相当于g(x)=1•x8+0•x7+0•x6+1•x5+1•x4+0•x3+0•x2+0•x1+1•x0,即对应的二进制数为100110001。 CRC8校验算法: 1.CRC8校验的一般性算法: 例如: 信息字段代码为: 00000001 00000010 ———— 对应m(x)=x8+x 生成多项式为:g(x)=x8+x5+x4+1 ———— 对应g(x)的二进制代码为:100110001 现在我们将要对2字节数据0x0102生成CRC8校验码,并最终将生成的1字节CRC校验码跟在0x0102的后面,即 0x01 02 ##,(##即8为CRC码),最终生成的3字节数据就是经CRC8校验生成的数据。 先计算x8m(x)=x16+x9,对应的2进制数为:100000010 00000000 。可以看到这样运算所得到的结果其实就是将信息字段代码的数左移8位。因为最终要将生成的8位CRC8校验码附在信息字段的后面,所以要将信息字段的数左移8位。最后用x8m(x)得到的二进制数对生成多项式g(x)进行模二运算,最终的余数(其二进制数的位数一定比生成多项式g(x)的位数小)就是所要的CRC8校验码。 100000010 00000000 ^ 100110001 --------------------------- 000110011 00000000 ^ 100110 001 --------------------------- 010101 00100000 ^ 10011 0001 --------------------------- 00110 00110000 ^ 100 110001 --------------------------- 010 11110100 ^ 10 0110001 --------------------------- 00 10010110 对x8m(x)做模二运算取余得10010110(0x96),这个8位的二进制数就是CRC8校验码。所以,经CRC8校验后研发送的数据就是0x010296。 2.CRC8校验在DS18B20中的应用: 以上分析的是常规的CRC8校验方法。在DS18B20中,有两处用到CRC。一是DS18B20的8字节的序列号,最后一字节是前面七个字节的CRC码,这是为了保证序列号的唯一性与正确性;另一个是在DS18B20内部9字节的高速温度存储器,其第9字节是前面8个字节的CRC校验码,这是为了温度数据传输的正确性。而在DS18B20中生成CRC码所用到的方法不同于常规生成算法,它采用的是逆序CRC信息单元编码算法,该CRC的生成是由DS18B20中的多项式寄存器通过其中所包含的移位寄存器以及异或门对输入该多项式寄存器的每一位二进制数做一定的运算所得到的CRC码(可以查看Maxim官网上DS18B20的应用笔记Note27,专门介绍DS18B20CRC详细生成过程)。在此列举两种DS18B20CRC校验的C程序。
### 回答1: 通用的CRC32校验程序可以用于检验数据的完整性,它可以完美匹配STM32硬件CRC算法。 CRC32是一种循环冗余校验算法,它使用32位的多项式来生成校验值。通用的CRC32校验程序可以按照CRC32算法进行计算,以确保数据的准确性。 在STM32芯片中的硬件CRC算法也是基于CRC32标准的,但它是通过硬件指令进行加速计算的。使用通用的CRC32校验程序可以实现与STM32硬件CRC算法相同的计算结果。 使用通用的CRC32校验程序,首先需要确定待校验数据的多项式以及初始值。以CRC32标准为例,多项式为0x04C11DB7,初始值为0xFFFFFFFF。 接下来,将待校验的数据逐位进行异或和移位操作,并与多项式进行多次异或操作,直到数据处理完毕。最后,取最终的校验值进行比较。 通用的CRC32校验程序与STM32硬件CRC算法相比,虽然执行效率会稍低一些,但其结果可以完全匹配。因此,在没有硬件CRC加速指令可用的情况下,使用通用的CRC32校验程序可以有效地实现数据的校验。 总之,通用的CRC32校验程序能够完美匹配STM32硬件CRC算法,通过按照CRC32标准进行计算,并与多项式和初始值进行异或和移位操作,最终得到校验值。虽然执行效率稍低,但可以保证数据的完整性。 ### 回答2: 通用CRC32校验程序是一种用于数据校验的算法,可以完美匹配STM32硬件CRC算法。CRC32(Cyclic Redundancy Check)是一种循环冗余校验算法,用于检测和校验数据传输中的错误。 通用CRC32校验程序的实现方式如下: 1. 创建一个CRC32表,用于加快CRC计算的速度。这个表的大小为256个32位无符号整数。 2. 初始化CRC寄存器的值为0xFFFFFFFF。 3. 对待校验的数据进行逐字节的计算。 4. 将当前CRC寄存器的值与当前数据字节进行异或运算。 5. 取CRC寄存器当前值的低8位,作为索引查表,得到一个新的32位无符号整数。 6. 将CRC寄存器的值右移8位。 7. 将查表得到的32位无符号整数与CRC寄存器的值进行异或运算。 8. 重复步骤5到步骤7,直到处理完所有的数据字节。 9. 对最终得到的CRC寄存器的值进行取反操作,得到最终的CRC32校验值。 通用CRC32校验程序能够完美匹配STM32硬件CRC算法的原因是它采用了相同的计算方法和相同的CRC32表。因此,通过使用这个通用的校验程序可以在软件层面上实现与STM32硬件CRC算法相同的校验结果,从而保证数据传输的可靠性。 ### 回答3: 通用的CRC32校验程序可以完美匹配STM32硬件CRC算法。CRC32(循环冗余校验)是一种常用的数据校验方法,能够有效检测数据传输或存储中的错误。STM32是一系列32位微控制器,其中的硬件CRC模块可以直接进行CRC32计算,使得校验过程更加高效。 通用的CRC32校验程序基本思路是利用位移和异或操作,在数据上逐位计算并更新CRC校验值。为了完美匹配STM32硬件CRC算法,首先需要确定CRC的生成多项式和初始值。在STM32中,多项式是0x04C11DB7,初始值是0xFFFFFFFF。 在编写通用的CRC32校验程序时,我们可以先定义一个数组,包含了从0到255的所有可能的8位二进制数。然后,通过一个循环,将每个字节与CRC校验值进行异或操作,并根据某种规则进行位移处理。最后,返回CRC校验值即可。 为了与STM32硬件CRC算法完美匹配,我们需要在校验前对输入数据进行字节反转操作,以保证数据的高字节在前,低字节在后。这是因为STM32硬件CRC模块对输入数据是按字节反转的。 总的来说,通用的CRC32校验程序可以与STM32硬件CRC算法完美匹配,但在使用时需要注意数据的字节顺序。这样,我们就可以使用通用的CRC32校验程序来进行数据的校验,而无需依赖STM32硬件CRC模块。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值