Cómo configurar STM32 para generar CRC32 estándar

14 minutos de lectura

Avatar de usuario de David Molnar
David Molnar

Estoy tratando de generar CRC con el módulo de hardware STM32L4. Me gustaría validar los archivos fatfs, así que básicamente tengo matrices de bytes. Estoy usando este CRC generador.

Desafortunadamente, no puedo entender cómo configurar STM32L4 para generar el mismo resultado. Necesito CRC32 y tengo

configuración:

hcrc.Instance = CRC;

/* The default polynomial is not used. It is required to defined it in CrcHandle.Init.GeneratingPolynomial*/
hcrc.Init.DefaultPolynomialUse    = DEFAULT_POLYNOMIAL_DISABLE;
/* Set the value of the polynomial */
hcrc.Init.GeneratingPolynomial    = 0x4C11DB7;
//hcrc.Init.GeneratingPolynomial    = 0xFB3EE248;
hcrc.Init.CRCLength= CRC_POLYLENGTH_32B;
/* The default init value is used */
/* The default init value is not used */
hcrc.Init.DefaultInitValueUse     = DEFAULT_INIT_VALUE_ENABLE;

/* User init value is used instead */
//hcrc.Init.InitValue               = 0;
hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE;
//hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_BYTE;
/* The input data are inverted by word */
//hcrc.Init.InputDataInversionMode  = CRC_INPUTDATA_INVERSION_WORD;

//hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_ENABLE;
hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE;
hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
HAL_CRC_Init(&hcrc);

pruebas:

uint8_t test[] = {49,50,51,52};
uint32_t uwCRCValue = HAL_CRC_Calculate(&hcrc,(uint32_t *) test, 4);

resultado: A695C4AA

Se me acabaron las ideas. Hay una forma en que tengo éxito con eso para tener uint32_t test[] y la entrada se establece en hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
Lamentablemente tengo uint8_t

  • ¿Te acordaste de habilitar el reloj periférico CRC? Tu código no lo muestra.

    – rost0031

    22/09/2016 a las 21:55


  • ¿Ha leído el manual de referencia adecuado? El módulo CRC tiene configuraciones avanzadas para diferentes casos de endianness. Si tiene el resultado correcto con la prueba uint32_t[]por lo que el problema solo en la configuración endianness.

    – imbearr

    23 de septiembre de 2016 a las 8:16

  • Gracias por su respuesta. Sí, los he comprobado (utilicé el generador de código CubeMX). He leído el manual de referencia. Desafortunadamente, ninguna combinación de configuraciones me da ninguno de los CRC32 calculados por algoritmos estándar.

    – David Molnar

    4 de octubre de 2016 a las 7:51

  • Tienes que mencionar a las personas si quieres que vean tu comentario. Eres el que abre, por lo que recibes los comentarios de tus publicaciones sin que te mencionen. Para mencionar personas, agregue @ antes de su nombre.

    – Mohammad Kholghi

    31 de julio de 2021 a las 6:58

Usando CubeMX, generé con estas configuraciones:

hcrc.Instance = CRC;
hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_ENABLE;
hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE;
hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_BYTE;
hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_ENABLE;
hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;

Calcular el CRC así:

uint32_t crc = HAL_CRC_Calculate(&hcrc, (uint32_t *)address, length);

Y finalmente invertir:

crc = ~crc;

  • ¡Hola! Gracias por tus sugerencias. Desafortunadamente, no puedo probar su solución, el proyecto ya no está en mi escritorio. Por lo que puedo recordar, mi problema fue que no puedo reproducir el mismo resultado (CRC) que STM para la misma entrada en el lado de la PC. Intenté calcular el resultado, por ejemplo, con este sitio. sol2k.de/codificación/javascript/crc/crc_js.html pero no puede obtener el mismo resultado.

    – David Molnar

    20 de febrero de 2018 a las 15:04


  • Y una cosa más vino a mi mente. Traté de usar la matriz uint8_t como entrada con la opción CRC_INPUTDATA_FORMAT_BYTES. No logro obtener el mismo resultado que en PC. Luego usé la matriz uint32_t con los mismos datos de 8 bits y evalué el resultado correcto. Así que terminé usando una matriz más grande uint32_t[] en lugar de uint8_t[] y podría usar el algoritmo de cálculo estándar en el lado de la PC.

    – David Molnar

    20 de febrero de 2018 a las 15:12

  • @DavidMolnar ¿Hiciste la prueba? uint32_t uwCRCValue = HAL_CRC_Calculate(&hcrc,(uint32_t *) test, 1);? 1 = longitud de la matriz de datos de 8 bits (4) / 4

    – Le Moussel

    20 dic 2018 a las 17:40

  • Confirmo que esto funciona y emite CRC32 estándar, esta inversión final es crítica. Además, no necesita dividir la longitud de los datos entre 4 si configura CRC_INPUTDATA_FORMAT_BYTES como HAL hace eso por ti, más o menos. Usé este código uint32_t uwCRCValue = ~HAL_CRC_Calculate(&hcrc, (uint32_t *) msg, strlen(msg));

    –Tomislav Darlic

    3 mayo 2019 a las 23:45


Esto funciona para mí.

static CRC_HandleTypeDef hcrc = { 
    .Instance = CRC, 
    .Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_ENABLE,
    .Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE,
    .Init.CRCLength = CRC_POLYLENGTH_32B,
    .Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE,
    .Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE,
    .InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES,
};

y el metodo manual

static const uint32_t crc_table[0x100] = {
  0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, 
  0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75, 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD, 
  0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D, 
  0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95, 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 
  0x34867077, 0x30476DC0, 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072, 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA, 
  0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA, 
  0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A, 
  0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A, 
  0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, 0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53, 
  0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF, 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 
  0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B, 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3, 
  0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71, 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3, 
  0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C, 0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, 
  0x119B4BE9, 0x155A565E, 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC, 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654, 
  0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C, 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, 
  0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668, 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4, 
};

uint32_t CalcCRC(uint8_t * pData, uint32_t DataLength)
{
    uint32_t Checksum = 0xFFFFFFFF;
    for(unsigned int i=0; i < DataLength; i++)
    {
        uint8_t top = (uint8_t)(Checksum >> 24);
        top ^= pData[i];
        Checksum = (Checksum << 8) ^ crc_table[top];
    }
    return Checksum;
}

  • Tenga en cuenta que cuando se usa el cálculo de CRC de hardware, hay un aumento significativo del rendimiento al usar palabras de 32 bits en lugar de bytes.

    – usuario1139455

    6 sep 2020 a las 18:38

Use el siguiente código para calcular cc32. CRC32 calc by STM32 CRC unit no es lo mismo que nuestro CRC32 estándar, usó big endian y no XOR con 0xFFFFFFFF.

u32 CRC32_ForBytes(u8 *pData, u32 uLen);

#define UNUSED(x) ((void)(x))

/**
 * @brief  CRC functions
 */
#define __HAL_RCC_CRC_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg; \
                                        SET_BIT(RCC->AHBENR, RCC_AHBENR_CRCEN);\
                                        /* Delay after an RCC peripheral clock enabling */\
                                        tmpreg = READ_BIT(RCC->AHBENR, RCC_AHBENR_CRCEN);\
                                        UNUSED(tmpreg); \
                                      } while(0)

#define __HAL_RCC_CRC_CLK_DISABLE()       (RCC->AHBENR &= ~(RCC_AHBENR_CRCEN))

#define CRC32_POLYNOMIAL                        ((u32)0xEDB88320)  
#define RCC_CRC_BIT                             ((u32)0x00001000)


/**
 * @brief  Calc CRC32 for data in bytes
 * @param  pData Buffer pointer
 * @param  uLen  Buffer Length
 * @retval CRC32 Checksum
 */
u32 CRC32_ForBytes(u8 *pData,u32 uLen)  
{  
    u32 uIndex= 0,uData = 0,i;  
    uIndex = uLen >> 2;  

    __HAL_RCC_CRC_CLK_ENABLE();

    /* Reset CRC generator */  
    CRC_ResetDR();

    while(uIndex--)  
    {  
#ifdef USED_BIG_ENDIAN    
        uData = __REV((u32*)pData);  
#else
        ((u8 *)&uData)[0] = pData[0];
        ((u8 *)&uData)[1] = pData[1];
        ((u8 *)&uData)[2] = pData[2];
        ((u8 *)&uData)[3] = pData[3];
#endif        
        pData += 4;  
        uData = revbit(uData);  
        CRC->DR = uData;  
    }  
    uData = revbit(CRC->DR);  
    uIndex = uLen & 0x03;  
    while(uIndex--)  
    {  
        uData ^= (u32)*pData++;  
        for(i = 0;i < 8;i++)  
          if (uData & 0x1)  
            uData = (uData >> 1) ^ CRC32_POLYNOMIAL;  
          else  
            uData >>= 1;  
    }

    __HAL_RCC_CRC_CLK_DISABLE();

    return uData^0xFFFFFFFF;  
}

static u32 revbit(u32 uData)
{  
    u32 uRevData = 0,uIndex = 0;  
    uRevData |= ((uData >> uIndex) & 0x01);  
    for(uIndex = 1;uIndex < 32;uIndex++)  
    {  
        uRevData <<= 1;  
        uRevData |= ((uData >> uIndex) & 0x01);  
    }  
    return uRevData;  
}

Calcule su CRC32 así:

u32 uwCRCValue = CRC32_ForBytes(&test, 4);

  • Gracias por el codigo. Mi objetivo principal era utilizar la calculadora de hardware. Generé con éxito la misma salida con este algoritmo en el lado de la PC Código CRC32 C# En el sitio de hardware funciona con: ‘uint32_t test[] = {49,50,51,52};’ Pero si cambio a: ‘uint8_t test[] = {49,50,51,52};’ el resultado cambió y todavía no puedo encontrar el algoritmo correcto. Podría ser por un problema de relleno, pero usé 4 bytes para probar. He terminado usando una suma de comprobación.

    – David Molnar

    4 de octubre de 2016 a las 8:14

  • CRC es esencialmente el resto después de una división larga muy larga, por lo que el orden de los bytes de datos de entrada sí importa. Los bytes de datos de entrada son como un número extremadamente largo en lo que respecta al algoritmo CRC.

    – usuario50619

    5 de noviembre de 2018 a las 14:31

  • Al menos el STM32L4 admite la inversión de los datos de entrada. Por lo tanto, puede elegir si el hardware procesa los datos de forma big endian o little endian. Como es, esta respuesta es incorrecta.

    – Sven

    3 de junio de 2020 a las 14:58

  • Este algoritmo sugerido es en realidad 100 % correcto y coincide con cualquier CRC32 estándar para cualquier dato de longitud arbitraria (incluidos los casos en que (uLen % 4) > 0). Además, recomendaría usar __RBIT(uData) operación, que permite invertir el orden de bits en la palabra completa de 32 bits en 1 marca en ARM MCU;

    – Nik

    8 sep 2021 a las 18:46

  • Esto parece mucho código teniendo en cuenta que se supone que el periférico CRC hace la mayor parte del trabajo necesario. ¿Quizás esto podría modificarse para evitar tener que copiar los datos en la memoria temporal?

    – jacobq

    12 abr a las 12:19

avatar de usuario de jacobq
jacobq

La implementación debería ser muy simple cuando se usa el periférico, básicamente un bucle que se ejecuta:

*(uint8_t*)&CRC->DR = buffer[i];

Las partes difíciles son configurar correctamente las opciones (cambio de bit, valor inicial, polinomio a usar, resultado de XOR, etc.) y asegurarse de acceder al registro de datos en el ancho de datos apropiado (es decir, para indicarle que opere en 1, 2 o 4 bytes a la vez). https://crccalc.com/ muestra de manera útil las variaciones que obtendrá dependiendo de si el orden de bits está invertido, la salida tiene bits XOR, etc. Sugiero probar su implementación con unos pocos bytes arbitrarios y comparar el resultado con la tabla producida por ese sitio para el mismo datos.

Aquí está mi implementación como referencia:

#include "stm32g4xx_hal.h" // change, as appropriate, for your MCU
#include <stdlib.h>

uint32_t crc32(uint8_t *buffer, size_t size) {
  // If the clock was turned on previously and kept on then it isn't necessary to do that here each time.
  // Did it here to match ST's example in AN4187 (page 8)
  // https://www.st.com/resource/en/application_note/dm00068118-using-the-crc-peripheral-in-the-stm32-family-stmicroelectronics.pdf)
  __HAL_RCC_CRC_CLK_ENABLE();
  // For standard (Ethernet) CRC-32 bit order is reversed on input and output
  CRC->CR = CRC_CR_REV_IN_0 | CRC_CR_REV_IN_1 | CRC_CR_REV_OUT;
  // The initial value and polynomial are set up during initialization.
  // Conveniently, in my case, the reset values are already correct:
  //CRC->INIT = 0xFFFFFFFF;
  //CRC->POL = 0x04C11DB7;
  CRC->CR |= CRC_CR_RESET;
  uint32_t i = 0;
  // First work on as many full 32-bit words as we can
  uint32_t full_word_bytes = size & 0b11;
  while (i < full_word_bytes) {
    CRC->DR = *(uint32_t*)&buffer[i];
    i += 4;
  }
  // Now handle any additional bytes one at a time
  while (i < size) {
    // Here we are using 8-bit access to the CRC peripheral's data register
    // so it does not introduce padding into the computation.
    // (e.g. the CRC of 4 zeros is different than for only 1)
    *(uint8_t*)&CRC->DR = buffer[i];
    i++;
  }
  // For standard (Ethernet) CRC-32 output bits need to all flip
  i = CRC->DR ^ 0xFFFFFFFF;
  __HAL_RCC_CRC_CLK_DISABLE();
  return i;
}

// Can test like this:
uint8_t crc_test_in[] = { 0x12, 0x34, 0x56, 0x78, 0x90 };
uint32_t expected_crc = 0xDC936EB1;
uint32_t crc = crc32(crc_test_in, CRC_TEST_LENGTH);
// ... print / compare ...

Esta es una pequeña nota si te preguntas qué polinomio hcrc.Init.GeneratingPolynomial y hcrc.Init.CRCLength medio. En su ejemplo inicial, la configuración de su polinomio dará:

> polyviz(0x4C11DB7, 32)
x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1

Si tiene node.js instalado, puede usar la siguiente función que escribí para convertir del polinomio del generador stm32 crc a la fórmula del polinomio crc ...+x^2+x^1+1 forma.

function polyviz(Pol, PolyLength)
{
  var msb = 31;

  process.stdout.write(" x^"+(PolyLength));
  while (msb-- > 0)
  {
    if ((Pol & (1 << msb)))
    {
      if (msb == 0)
      process.stdout.write(" + 1");
      else
      process.stdout.write(" + x^"+(msb));
    }
  }
  process.stdout.write("\r\n");
}

// Examples from HAL_CRCEx_Polynomial_Set():
// * for a polynomial of degree 16, X^16 + X^12 + X^5 + 1 is written 0x1021 (Bin: 0001 0000 0010 0001 )
polyviz(0x1021, 16)
// * for a polynomial of degree 7, X^7 + X^6 + X^5 + X^2 + 1 is written 0x65 (Bin: 0110 0101)
polyviz(0x65, 7)

Usando este método, puedes confirmar si configuraste tu polinomio correctamente. (Dado que muchos estándares de CRC usan representación polinomial)

Encontré este tutorial (para STM32F746) y lo uso con STM32F407VGT6,

Hay muchas configuraciones de IDE y probablemente será mejor acceder a ellas directamente, lo siento, no inserté todo el contenido directamente aquí:

Práctico: generación de suma de comprobación CRC

Nota: En este caso, el archivo a escribir es ROM.hex (necesitará configurar el STM32CubeIDE para poder hacer esta operación automáticamenteel IDE usa el archivo *.elf, vea cómo hacerlo en los consejos a continuación):

Algunos consejos y soluciones sobre este uso de CRC (Windows/Linux)

¿Ha sido útil esta solución?