5.5 CRC Guide
This chapter describes the file composition and 16 public functions of the CRC driver in the N32H49x standard peripheral library, together with 2 examples. The CRC peripheral computes a checksum over a 32-bit or 16-bit data stream, and additionally provides an 8-bit independent data register and an LRC exclusive-OR check; for the initial value, endianness and clean-method settings, see the “Cyclic redundancy check (CRC)” chapter of the user manual.
5.5.1 Related files
| Relative path | Purpose |
|---|---|
firmware/n32h49x_std_periph_driver/inc/n32h49x_crc.h |
Defines the register bits and masks for the 32-bit and 16-bit checksums, and declares the 16 public functions |
firmware/n32h49x_std_periph_driver/src/n32h49x_crc.c |
Implements the 16 functions above: 32-bit data-register handling and result readback, independent data-register access, CRC16 and LRC format configuration, single-word and buffer computation, and result readback |
projects/n32h49x_EVAL/examples/CRC/ |
CRC example root directory, containing the CalCRC and CRC_DMA examples |
projects/n32h49x_EVAL/examples/CRC/CalCRC/ |
Project directory composition of this example: MDK-ARM/, EWARM/, inc/, src/, readme.txt |
projects/n32h49x_EVAL/bsp/ |
Common board files referenced by this example: the UART log function |
5.5.2 API reference
The following 16 public functions are listed in the declaration order of n32h49x_crc.h in 4 groups; each entry gives a summary and the original comment above the function in n32h49x_crc.c.
32-bit CRC
CRC32_ResetCrc, CRC32_CalcCrc, CRC32_CalcBufCrc, CRC32_GetCrc
Independent data register
CRC32_SetIDat, CRC32_GetIDat
16-bit CRC and LRC
__CRC16_SetLittleEndianFmt, __CRC16_SetBigEndianFmt, __CRC16_SetCleanEnable, __CRC16_SetCleanDisable, __CRC16_CalcCrc, __CRC16_SetCrc, __CRC16_GetCrc, __CRC16_SetLRC, __CRC16_GetLRC
16-bit buffer
CRC16_CalcBufCrc
void CRC32_ResetCrc(void)Resets the CRC Data register (DAT).
name CRC32_ResetCrc.
fun Resets the CRC Data register (DAT).
param none
return none
uint32_t CRC32_CalcCrc(uint32_t Data)Computes the 32-bit CRC of a given data word(32-bit).
name CRC32_CalcCrc.
fun Computes the 32-bit CRC of a given data word(32-bit).
param Data :
- data word(32-bit) to compute its CRC
return 32-bit CRC
uint32_t CRC32_CalcBufCrc(const uint32_t pBuffer[], uint32_t BufferLength)Computes the 32-bit CRC of a given buffer of data word(32-bit).
name CRC32_CalcBufCrc.
fun 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
return 32-bit CRC
uint32_t CRC32_GetCrc(void)Returns the current CRC value.
name CRC32_GetCrc.
fun Returns the current CRC value.
param none
return 32-bit CRC
void CRC32_SetIDat(uint8_t IDValue)Stores a 8-bit data in the Independent Data(ID) register.
name CRC32_SetIDat.
fun Stores a 8-bit data in the Independent Data(ID) register.
param IDValue :
- 8-bit value to be stored in the ID register
return none
uint8_t CRC32_GetIDat(void)Returns the 8-bit data stored in the Independent Data(ID) register.
name CRC32_GetIDat.
fun Returns the 8-bit data stored in the Independent Data(ID) register.
param none
return 8-bit value of the ID register
void __CRC16_SetLittleEndianFmt(void)Set the calculate data to little endian format.
name __CRC16_SetLittleEndianFmt.
fun Set the calculate data to little endian format.
param none
return none
void __CRC16_SetBigEndianFmt(void)Set the calculate data to big endian format.
name __CRC16_SetBigEndianFmt.
fun Set the calculate data to big endian format.
param none
return none
void __CRC16_SetCleanEnable(void)Enable Clean CRC16 value.
name __CRC16_SetCleanEnable.
fun Enable Clean CRC16 value.
param none
return none
void __CRC16_SetCleanDisable(void)Disable Clean CRC16 value.
name __CRC16_SetCleanDisable.
fun Disable Clean CRC16 value.
param none
return none
uint16_t __CRC16_CalcCrc(uint8_t Data)Computes the 16-bit CRC of a given data word(8-bit).
name __CRC16_CalcCrc.
fun Computes the 16-bit CRC of a given data word(8-bit).
param Data :
- data word(8-bit) to compute its CRC
return 16-bit CRC
void __CRC16_SetCrc(uint8_t Data)write a 8-bit data in the 16-bit CRC Data register.
name __CRC16_SetCrc.
fun write a 8-bit data in the 16-bit CRC Data register.
param Data :
- 8-bit data value to be writed in the 16-bit CRC Data register
return none
uint16_t __CRC16_GetCrc(void)Returns the current CRC value.
name __CRC16_GetCrc.
fun Returns the current CRC value.
param none
return 16-bit CRC
void __CRC16_SetLRC(uint8_t Data)Write LRC initial value.
name __CRC16_SetLRC.
fun Write LRC initial value.
param Data :
- 8-bit data
return none
uint8_t __CRC16_GetLRC(void)Returns the current LRC value.
name __CRC16_GetLRC.
fun Returns the current LRC value.
param none
return 8-bit LRC
uint16_t CRC16_CalcBufCrc(const uint8_t pBuffer[], uint32_t BufferLength)Computes the 16-bit CRC of a given buffer of data word(8-bit).
name CRC16_CalcBufCrc.
fun Computes the 16-bit CRC of a given buffer of data word(8-bit).
param pBuffer :
- pointer to the buffer containing the data to be computed
param BufferLength :
- length of the buffer to be computed
return 16-bit CRC
5.5.3 Demo descriptions
This section describes the 2 CRC examples: CalCRC cross-checks the hardware CRC32 and CRC16 results against a software implementation, and CRC_DMA uses DMA to move data into the CRC data registers.
5.5.3.1 CalCRC hardware and software checksum comparison
Example directory: projects/n32h49x_EVAL/examples/CRC/CalCRC/
Enable the CRC clock and initialize the log→Compute the 32-bit buffer CRC with the hardware API→Compute the reference value of the same buffer in software→Compare the two and print the conclusion→Verify the 16-bit buffer CRC the same way
| Item | Source analysis |
|---|---|
| Function | Computes the checksum of the 114-word DataBuffer with CRC32_CalcBufCrc and compares it with the software implementation GetCRC32_Software (initial value 0xffffffff);then computes the checksum of the 8-byte Buffer with CRC16_CalcBufCrc and compares it with GetCRC16_Software (initial value 0x00);both comparison results are printed over UART. |
| Source files | src/main.c: main, ReverseBits, GetCRC16_Software, GetCRC32_Software;inc/main.h: includes n32h49x_crc.h and other headers;src/n32h49x_it.c, inc/n32h49x_it.h: interrupt service routines (exception handlers only);readme.txt: function, environment and usage notes;MDK-ARM/, EWARM/: Keil and IAR projects. |
| Key configuration | Clock: RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_CRC, ENABLE);Data: DataBuffer is 114 32-bit words and Buffer is 8 bytes;Software counterpart: CRC32 initial value 0xffffffff and CRC16 initial value 0x00, with the polynomials CRC32_DFE_POLY and CRC16_DFE_POLY used only by the software implementation. |
| Program and data path | main enables the CRC clock and calls log_init;CRC32_CalcBufCrc produces the hardware 32-bit checksum and GetCRC32_Software the reference value, which are compared before printing the conclusion;then CRC16_CalcBufCrc and GetCRC16_Software repeat the same comparison for the 8-byte buffer. |
| Main APIs | CRC32_CalcBufCrc, CRC16_CalcBufCrc |
| Verification | The UART prints “CRC32 calculation is correct!” and “CRC16 calculation is correct!” when the hardware result matches the software value; otherwise it prints the corresponding error message. |
| Use note | The software counterpart processes the same buffer bit by bit and is used to cross-check the hardware result; the example finishes both comparisons before the main loop and does not rely on interrupts; the buffer lengths come from BUFFER_SIZE and a literal, so keep both lengths consistent when porting. |
Core-code walkthrough and porting
The example computes the checksum of the same buffer with both the hardware API and a software implementation and compares them, verifying the CRC32 and CRC16 paths.
Data path: RCC_EnableAHBPeriphClk enables the CRC clock → log_init initializes the log → CRC32_CalcBufCrc computes the hardware 32-bit checksum → GetCRC32_Software computes the reference value → the two are compared and printed → CRC16_CalcBufCrc and GetCRC16_Software repeat the comparison for the 8-byte buffer
1. Enable the CRC clock. main first turns on the CRC peripheral clock and then initializes the UART log.
/* LED3 port is default used for JTAG, must remap first */
RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_CRC, ENABLE);
The CRC computation only needs the peripheral clock and uses no GPIO, so keep this line when porting.
2. Compute and compare the 32-bit checksum. It first calls CRC32_CalcBufCrc for the hardware result, then uses GetCRC32_Software with initial value 0xffffffff for the reference value, and prints success when the two are equal.
/* Compute the 32bit CRC of "DataBuffer" */
CRC32Value = CRC32_CalcBufCrc((uint32_t*)DataBuffer, BUFFER_SIZE);
/* Compute the 32bit CRC of "DataBuffer" by software*/
CRCValue = GetCRC32_Software((uint32_t*)DataBuffer, BUFFER_SIZE, 0xffffffff);
if( CRC32Value == CRCValue )
{
printf(" CRC32 calculation is correct! \r\n");
}
else
{
printf(" CRC32 calculation error! \r\n");
}
The software initial value must match the hardware data-register default, otherwise the two results cannot be compared directly.
3. Compute and compare the 16-bit checksum. The 8-byte buffer uses CRC16_CalcBufCrc, and GetCRC16_Software computes the reference value from initial value 0x00.
/* Compute the 16bit CRC of an array */
CRC16Value = CRC16_CalcBufCrc((uint8_t*)Buffer, 8);
/* Compute the 16bit CRC of an array by software*/
CRCValue = GetCRC16_Software((uint8_t*)Buffer, 8, 0x00);
if( CRC16Value == CRCValue )
{
printf("CRC16 calculation is correct! \r\n");
}
else
{
printf("CRC16 calculation error! \r\n");
}
The 16-bit and 32-bit paths use different polynomials and initial values, so confirm that both sides use the same set of parameters before comparing.
4. Software counterpart. GetCRC32_Software processes the buffer bit by bit and updates the checksum with the standard polynomial, serving as the reference for the hardware result.
for(i = 0; i < len; i ++)
{
xbit = 0x80000000;
for (bits = 0; bits < 32; bits++)
{
if (crc32 & 0x80000000)
{
crc32 <<= 1;
crc32 ^= CRC32_DFE_POLY;
}
else
{
crc32 <<= 1;
}
if (buf[i] & xbit)
{
crc32 ^= CRC32_DFE_POLY;
}
xbit >>= 1;
}
}
When porting to another checksum standard, both the polynomial and the shift direction must change together, not just the constant.
Corresponding source files: src/main.c
5.5.3.2 CRC_DMA checksum computation by DMA transfer
Example directory: projects/n32h49x_EVAL/examples/CRC/CRC_DMA/
Enable the CRC and DMA1 clocks→Reset the CRC data register→Use DMA to move the word buffer into the CRC32 data register and read the result back→Use DMA to move the byte buffer for the CRC16 and LRC computation→Read the CRC16 and LRC results back into memory and check them
| Item | Source analysis |
|---|---|
| Function | Uses DMA to move data into the CRC data registers and complete the checks: TestCrc32AndDma transfers 114 words and yields a CRC32 result of 0x379E9F06;TestCrc16AndDma transfers 456 bytes and yields a CRC16 result of 0x5268 and an LRC result of 0x00;each result is read back and printed. |
| Source files | src/main.c: main;src/crc_dma.c: TestCrc32AndDma, TestCrc16AndDma and the buffers CRC_original_word_data, CRC_original_byte_data;inc/main.h: includes n32h49x_crc.h, n32h49x_dma.h and other headers;src/n32h49x_it.c, inc/n32h49x_it.h: interrupt service routines;readme.txt: function, environment and usage notes;MDK-ARM/, EWARM/: Keil and IAR projects. |
| Key configuration | Clock: enables the CRC and DMA1 clocks; DMA: DMA1_CH1 with Mem2Mem enabled;WORD width is used for the word transfer and BYTE width for the byte transfer, with the memory-to-peripheral direction for writing the data register and the peripheral-to-memory direction for reading results; the burst length is selected from the burst_mode_len parameter (2 to 7) as DMA_BURST_LEN_2 through DMA_BURST_LEN_7. |
| Program and data path | main enables the clocks and calls CRC32_ResetCrc to reset the data register;TestCrc32AndDma(0, 2) moves CRC_original_word_data into CRC->CRC32DAT, waits for DMA_FLAG_TC1 and reads the result with CRC32_GetCrc, then moves CRC->CRC32D back to memory;TestCrc16AndDma(0, 2) writes the initial values, moves CRC_original_byte_data into CRC->CRC16DAT, reads back __CRC16_GetCrc and __CRC16_GetLRC, and moves CRC->CRC16D and CRC->LRC back to memory. |
| Main APIs | CRC32_ResetCrc, CRC32_GetCrc, __CRC16_SetCrc, __CRC16_SetLRC, __CRC16_SetCleanEnable, __CRC16_SetBigEndianFmt, __CRC16_GetCrc, __CRC16_GetLRC |
| Verification | The UART prints the CRC32 result after the DMA transfer (expected 0x379E9F06), the CRC16 result (expected 0x5268) and the LRC result (expected 0x00); any mismatch prints the corresponding error message. |
| Use note | CRC32 writes 114 words into CRC->CRC32DAT and CRC16 writes 456 bytes into CRC->CRC16DAT;the CRC16 initial value 0x00 is written before the transfer, with clean enabled and big-endian format selected; results can be read from the registers directly or moved back to memory by a peripheral-to-memory DMA transfer. |
Core-code walkthrough and porting
The example uses DMA to move data into the CRC data registers and completes the CRC32, CRC16 and LRC computations, then reads the results back into memory for checking.
Data path: RCC_EnableAHBPeriphClk enables the CRC and DMA1 clocks → CRC32_ResetCrc resets the data register → TestCrc32AndDma uses DMA to move data into CRC->CRC32DAT and reads the result back → TestCrc16AndDma writes the initial values, moves data into CRC->CRC16DAT and reads back CRC16 and LRC
1. Enable the clocks and reset the data register. main enables both the CRC and DMA1 clocks, resets the CRC data register and then runs the two tests.
int main(void)
{
/* Enable CRC and DMA clock */
RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_CRC | RCC_AHB_PERIPHEN_DMA1, ENABLE);
log_init();
printf(" CRC DMA test start.\r\n");
CRC32_ResetCrc();
TestCrc32AndDma(0, 2);
TestCrc16AndDma(0, 2);
Resetting the data register makes each test start from a defined initial value; the two arguments are the burst mode and the burst length.
2. Configure DMA to move the word buffer into the CRC32 data register. TestCrc32AndDma sets the peripheral address to CRC->CRC32DAT and the memory address to CRC_original_word_data, with the memory-to-peripheral direction and Mem2Mem and WORD width.
/*Memory->CRC32DAT*/
DMA_InitStructure.PeriphAddr = (uint32_t)&CRC->CRC32DAT;
DMA_InitStructure.MemAddr = (uint32_t)CRC_original_word_data;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_DST;
DMA_InitStructure.Mem2Mem = DMA_M2M_ENABLE;
DMA_InitStructure.MemDataSize = DMA_MEM_DATA_WIDTH_WORD;
DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_WORD;
DMA_InitStructure.MemoryInc = DMA_MEM_INC_ENABLE;
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
DMA_InitStructure.CircularMode = DMA_MODE_NORMAL;
DMA_InitStructure.Priority = DMA_PRIORITY_VERY_HIGH;
DMA_InitStructure.BufSize = 114;
DMA_InitStructure.BurstCmd = DMA_BURST_DISABLE;
The peripheral address does not increment, so every word is written to the same data register; the buffer length must match the real data length.
3. Start the DMA and read the CRC32 result back. After enabling the channel it waits for DMA_FLAG_TC1 and then reads the checksum with CRC32_GetCrc and prints it.
DMA_Init(DMA1_CH1, &DMA_InitStructure);
DMA_EnableChannel(DMA1_CH1, ENABLE);
while(!DMA_GetFlagStatus(DMA_FLAG_TC1, DMA1));
DMA_DeInit(DMA1_CH1);
CRC32_Value = CRC32_GetCrc();
printf("The CRC 32 code after Memory->CRC32DAT DMA transmission is %08x.\r\n", CRC32_GetCrc());
Wait for the transfer-complete flag before reading the result, otherwise the checksum may not be updated yet.
4. Write the CRC16 and LRC initial values and configure the byte transfer. TestCrc16AndDma first writes the CRC16 and LRC initial values 0x00, enables clean and selects big endian, then moves 456 bytes into CRC->CRC16DAT.
/*CRC 16 and LRC big endian test wirh 0 initial value*/
__CRC16_SetCrc(0x00);
__CRC16_SetLRC(0x00);
__CRC16_SetCleanEnable();
__CRC16_SetBigEndianFmt();
/*Memory->CRC.CRC16DAT*/
DMA_InitStructure.PeriphAddr = (uint32_t)&CRC->CRC16DAT;
DMA_InitStructure.MemAddr = (uint32_t)CRC_original_byte_data;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_DST;
DMA_InitStructure.Mem2Mem = DMA_M2M_ENABLE;
DMA_InitStructure.MemDataSize = DMA_MEM_DATA_WIDTH_BYTE;
DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_BYTE;
DMA_InitStructure.MemoryInc = DMA_MEM_INC_ENABLE;
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
DMA_InitStructure.CircularMode = DMA_MODE_NORMAL;
DMA_InitStructure.Priority = DMA_PRIORITY_VERY_HIGH;
DMA_InitStructure.BufSize = 456;
The byte transfer switches to BYTE width; the initial values and clean setting must be done before the transfer and the order cannot be reversed.
5. Read the CRC16 and LRC results back. After the transfer it reads __CRC16_GetCrc and __CRC16_GetLRC and compares them with the expected values.
CRC16_Value = __CRC16_GetCrc();
LRC_Value = __CRC16_GetLRC();
printf("The CRC 16 code after Memory->CRC.CRC16DAT DMA transmission is %04x.\r\n", __CRC16_GetCrc());
if(CRC16_Value != 0x5268)
{
printf("The CRC 16 code computed through DMA transmission is error,it should be 0x5268.\r\n");
}
printf("The LRC code after Memory->CRC.CRC16DAT DMA transmission is %02x.\r\n", __CRC16_GetLRC());
if(LRC_Value != 0x00)
{
printf("The LRC code computed through DMA transmission is error,it should be 0x00.\r\n");
}
The expected values depend on both the data contents and the initial values, so these two constants must be updated together when another data set is used.
Corresponding source files: src/main.c, src/crc_dma.c
5.5.4 Notes and troubleshooting
| Scenario | Check & action | Source |
|---|---|---|
| CRC32 reset | Writing 1 to the RESET bit resets CRC32R to 0xFFFFFFFF, and the bit is cleared by hardware automatically without manual clearing. | User manual |
| CRC16 endianness | The ENDIAN bit configures the parsing order of the CRC16 data, deciding whether the input is parsed as big endian or little endian. |
User manual |
| Clean method | The CLEAR bit is self-clearing and sets the CRC value from 0xFFFF_FFFF to the default 0x0000. |
User manual |
| Independent data register | CRC32IDAT is a general-purpose data register and is not affected by the RESET bit. |
User manual |
| Initial value | When an initial value is required, write it to CRC_CRC16D or CRC_CRC32D first, otherwise the computation continues from the previous result. |
User manual |
| LRC check | LRC is an exclusive-OR check, and software should clear the LRC initial value before the next computation. | User manual |
| Data width | CRC16DAT supports 8-bit, 16-bit and 32-bit write operations. |
User manual |