5.5 CRC 使用说明

本章说明 N32H49x 标准外设库中 CRC 驱动的文件组成与 16 个公开函数,以及 2 个示例。CRC 外设对 32 位或 16 位数据流计算校验值,并额外提供 8 位独立数据寄存器与 LRC 异或校验;初始值、字节序与清除方式的设置请配合用户手册的“循环冗余校验(CRC)”章节阅读。

5.5.1 相关文件

相对路径 作用
firmware/n32h49x_std_periph_driver/inc/n32h49x_crc.h 定义 32 位与 16 位校验的寄存器位与掩码,声明 16 个公开函数
firmware/n32h49x_std_periph_driver/src/n32h49x_crc.c 实现上述 16 个函数:32 位数据寄存器与结果读取、独立数据寄存器读写、CRC16 与 LRC 的格式配置、单字节与缓冲区计算及结果读取
projects/n32h49x_EVAL/examples/CRC/ CRC 示例根目录,含 CalCRC、CRC_DMA 2 个示例
projects/n32h49x_EVAL/examples/CRC/CalCRC/ 该示例的工程目录组成:MDK-ARM/、EWARM/、inc/、src/、readme.txt
projects/n32h49x_EVAL/bsp/ 该示例引用的公共板级文件:串口日志函数

5.5.2 API 参考

搜索 API共 16 个函数

以下 16 个公开函数按 n32h49x_crc.h 的声明顺序分为 4 组;每个条目给出功能摘要与 n32h49x_crc.c 中该函数正上方的注释原文。

32 位 CRC

CRC32_ResetCrc、CRC32_CalcCrc、CRC32_CalcBufCrc、CRC32_GetCrc

独立数据寄存器

CRC32_SetIDat、CRC32_GetIDat

16 位 CRC 与 LRC

__CRC16_SetLittleEndianFmt、__CRC16_SetBigEndianFmt、__CRC16_SetCleanEnable、__CRC16_SetCleanDisable、__CRC16_CalcCrc、__CRC16_SetCrc、__CRC16_GetCrc、__CRC16_SetLRC、__CRC16_GetLRC

16 位缓冲区

CRC16_CalcBufCrc

void CRC32_ResetCrc(void)复位 CRC 数据寄存器(DAT)。
源码注释
name    CRC32_ResetCrc.
fun     Resets the CRC Data register (DAT).
param   none
return  none
uint32_t CRC32_CalcCrc(uint32_t Data)计算给定 32 位数据字的 CRC 值。
源码注释
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)计算给定 32 位数据缓冲区(数据字)的 CRC 值。
源码注释
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)返回当前 CRC 值。
源码注释
name    CRC32_GetCrc.
fun     Returns the current CRC value.
param   none
return  32-bit CRC
void CRC32_SetIDat(uint8_t IDValue)向独立数据(ID)寄存器写入一个 8 位数据。
源码注释
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)返回独立数据(ID)寄存器中存储的 8 位数据。
源码注释
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)把参与计算的数据设为小端格式。
源码注释
name    __CRC16_SetLittleEndianFmt.
fun     Set the calculate data to little endian format.
param   none
return  none
void __CRC16_SetBigEndianFmt(void)把参与计算的数据设为大端格式。
源码注释
name    __CRC16_SetBigEndianFmt.
fun     Set the calculate data to big endian format.
param   none
return  none
void __CRC16_SetCleanEnable(void)使能 CRC16 值的清除。
源码注释
name    __CRC16_SetCleanEnable.
fun     Enable Clean CRC16 value.
param   none
return  none
void __CRC16_SetCleanDisable(void)关闭 CRC16 值的清除。
源码注释
name    __CRC16_SetCleanDisable.
fun     Disable Clean CRC16 value.
param   none
return  none
uint16_t __CRC16_CalcCrc(uint8_t Data)计算给定 8 位数据字的 16 位 CRC 值。
源码注释
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)向 16 位 CRC 数据寄存器写入一个 8 位数据。
源码注释
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)返回当前 CRC 值。
源码注释
name    __CRC16_GetCrc.
fun     Returns the current CRC value.
param   none
return  16-bit CRC
void __CRC16_SetLRC(uint8_t Data)写入 LRC 初始值。
源码注释
name    __CRC16_SetLRC.
fun     Write LRC initial value.
param   Data :
          - 8-bit data
return  none
uint8_t __CRC16_GetLRC(void)返回当前 LRC 值。
源码注释
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)计算给定 8 位数据缓冲区的 16 位 CRC 值。
源码注释
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 示例说明

本节说明 CRC 的 2 个示例:CalCRC 用软件实现交叉验证硬件 CRC32 与 CRC16 的计算结果,CRC_DMA 用 DMA 把数据搬到 CRC 数据寄存器完成校验。

5.5.3.1 CalCRC 硬件与软件校验结果对比

工程目录: projects/n32h49x_EVAL/examples/CRC/CalCRC/

使能 CRC 时钟并初始化日志→用硬件 API 计算 32 位缓冲区校验→用软件函数算同一缓冲区的参考值→比较两者并打印结论→用同样方式验证 16 位缓冲区校验

项目 源码解读
功能 对 114 个 32 位字的 DataBuffer 用 CRC32_CalcBufCrc 计算校验,并与软件实现 GetCRC32_Software(初值 0xffffffff)比较;
再对 8 字节的 Buffer 用 CRC16_CalcBufCrc 计算校验,并与 GetCRC16_Software(初值 0x00)比较;
两次比较结果通过串口打印。
源码组成 src/main.c:main、ReverseBits、GetCRC16_Software、GetCRC32_Software;
inc/main.h:包含 n32h49x_crc.h 等头文件;
src/n32h49x_it.c、inc/n32h49x_it.h:中断服务函数(仅异常处理);
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 时钟:RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_CRC, ENABLE);
数据:DataBuffer 为 114 个 32 位字,Buffer 为 8 个字节;
软件参考:CRC32 初值 0xffffffff、CRC16 初值 0x00,多项式 CRC32_DFE_POLY、CRC16_DFE_POLY 只用于软件实现。
程序与数据路径 main 使能 CRC 时钟并调用 log_init;
CRC32_CalcBufCrc 得到硬件 32 位校验,GetCRC32_Software 得到参考值,比较后打印结论;
随后 CRC16_CalcBufCrc 与 GetCRC16_Software 对 8 字节缓冲区重复同样的比较。
主要 API CRC32_CalcBufCrc、CRC16_CalcBufCrc
验证方法 串口打印 “CRC32 calculation is correct!” 与 “CRC16 calculation is correct!” 表示硬件结果与软件参考一致;
若不一致则打印对应的 error 提示。
使用提示 软件对照实现对同一缓冲区逐位计算,用于交叉验证硬件结果;
示例在主循环前完成两次比较,不依赖中断;
缓冲区长度以 BUFFER_SIZE 与字面量给出,移植时两处长度要保持一致。
核心代码解读与移植

示例用硬件 API 与软件实现分别计算同一缓冲区的校验值并比较,验证 CRC32 与 CRC16 两条计算通路。

数据路径: RCC_EnableAHBPeriphClk 使能 CRC 时钟 → log_init 初始化日志 → CRC32_CalcBufCrc 计算硬件 32 位校验 → GetCRC32_Software 计算参考值 → 比较并打印 → CRC16_CalcBufCrc 与 GetCRC16_Software 对 8 字节缓冲区重复比较

1. 使能 CRC 时钟。 main 先打开 CRC 外设时钟,随后初始化串口日志。

src/main.csrc/main.c · main
    /* LED3 port is default used for JTAG, must remap first */
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_CRC, ENABLE);

CRC 计算只用到外设时钟,不占用 GPIO,移植时保留这一行使能即可。

2. 计算并比较 32 位校验。 先调用 CRC32_CalcBufCrc 得到硬件结果,再用 GetCRC32_Software 以初值 0xffffffff 算出参考值,两者相等即打印正确。

src/main.csrc/main.c · main
    /* 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");
    }

软件初值要与硬件数据寄存器的默认值一致,否则两路结果不可直接比较。

3. 计算并比较 16 位校验。 8 字节缓冲区改用 CRC16_CalcBufCrc 计算,并以 GetCRC16_Software 从初值 0x00 算出参考值。

src/main.csrc/main.c · main
    /* 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");
    }

16 位与 32 位用不同的多项式与初值,比较前要确认两者对应同一组参数。

4. 软件对照实现。 GetCRC32_Software 按位处理缓冲区,用标准多项式逐位更新校验值,作为硬件结果的参照。

src/main.csrc/main.c · GetCRC32_Software
    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;
        }
    }

移植到其它校验标准时,多项式与移位方向都要随之调整,不能只改一个常量。

对应源码: src/main.c

5.5.3.2 CRC_DMA 通过 DMA 搬运数据计算校验

工程目录: projects/n32h49x_EVAL/examples/CRC/CRC_DMA/

使能 CRC 与 DMA1 时钟→复位 CRC 数据寄存器→用 DMA 把字缓冲区搬入 CRC32 数据寄存器并读回校验→用 DMA 搬入字节缓冲区完成 CRC16 与 LRC 计算→把 CRC16 与 LRC 结果读回内存并检查

项目 源码解读
功能 用 DMA 把数据搬到 CRC 数据寄存器完成校验:TestCrc32AndDma 搬入 114 个字得到 CRC32 结果 0x379E9F06;
TestCrc16AndDma 搬入 456 个字节得到 CRC16 结果 0x5268 与 LRC 结果 0x00;
每步结果都读回并打印。
源码组成 src/main.c:main;
src/crc_dma.c:TestCrc32AndDma、TestCrc16AndDma 与缓冲区 CRC_original_word_data、CRC_original_byte_data;
inc/main.h:包含 n32h49x_crc.h、n32h49x_dma.h 等头文件;
src/n32h49x_it.c、inc/n32h49x_it.h:中断服务函数;
readme.txt:功能、环境与使用说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 时钟:使能 CRC 与 DMA1 时钟;
DMA:DMA1_CH1,Mem2Mem 使能;
字搬运用 WORD 宽度、字节搬运用 BYTE 宽度,写数据寄存器方向为内存到外设、读结果方向为外设到内存;
突发长度由参数 burst_mode_len(2~7)选择 DMA_BURST_LEN_2 到 DMA_BURST_LEN_7。
程序与数据路径 main 使能时钟并调用 CRC32_ResetCrc 复位数据寄存器;
TestCrc32AndDma(0, 2) 把 CRC_original_word_data 搬入 CRC->CRC32DAT,等待 DMA_FLAG_TC1 后用 CRC32_GetCrc 读取结果,再把 CRC->CRC32D 搬回内存;
TestCrc16AndDma(0, 2) 先写初值,把 CRC_original_byte_data 搬入 CRC->CRC16DAT,读回 __CRC16_GetCrc 与 __CRC16_GetLRC,并把 CRC->CRC16D、CRC->LRC 搬回。
主要 API CRC32_ResetCrc、CRC32_GetCrc、__CRC16_SetCrc、__CRC16_SetLRC、__CRC16_SetCleanEnable、__CRC16_SetBigEndianFmt、__CRC16_GetCrc、__CRC16_GetLRC
验证方法 串口打印 DMA 传输后的 CRC32 结果(应为 0x379E9F06)、CRC16 结果(应为 0x5268)与 LRC 结果(应为 0x00);
任一结果不符时打印对应的 error 提示。
使用提示 CRC32 用 114 个字写入 CRC->CRC32DAT,CRC16 用 456 个字节写入 CRC->CRC16DAT;
CRC16 计算前先写初值 0x00,并开启清除与大端格式;
结果既可直接读取寄存器,也可用外设到内存的 DMA 搬回内存。
核心代码解读与移植

示例用 DMA 把数据搬到 CRC 数据寄存器,分别完成 CRC32、CRC16 与 LRC 的计算,再把结果读回内存校验。

数据路径: RCC_EnableAHBPeriphClk 使能 CRC 与 DMA1 时钟 → CRC32_ResetCrc 复位数据寄存器 → TestCrc32AndDma 用 DMA 搬入 CRC->CRC32DAT 并读回校验 → TestCrc16AndDma 写初值后用 DMA 搬入 CRC->CRC16DAT,读回 CRC16 与 LRC

1. 使能时钟并复位数据寄存器。 main 同时使能 CRC 与 DMA1 时钟,复位 CRC 数据寄存器后依次启动两个测试。

src/main.csrc/main.c · main
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);

复位数据寄存器让每次测试都从明确的初值开始;两个测试参数分别为突发模式和突发长度。

2. 配置 DMA 把字缓冲区搬到 CRC32 数据寄存器。 TestCrc32AndDma 把外设地址设为 CRC->CRC32DAT、存储器地址设为 CRC_original_word_data,方向为内存到外设,并使用 Mem2Mem 与 WORD 宽度。

src/crc_dma.csrc/crc_dma.c · TestCrc32AndDma
    /*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;

外设地址不自增,保证每个字都写到同一个数据寄存器;缓冲区长度要与实际数据长度一致。

3. 启动 DMA 并读回 CRC32 结果。 使能通道后等待 DMA_FLAG_TC1,再用 CRC32_GetCrc 读取校验值并打印。

src/crc_dma.csrc/crc_dma.c · TestCrc32AndDma
    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());

搬运完成后先确认传输完成标志再读结果,避免读到尚未更新的校验值。

4. 写入 CRC16 与 LRC 初值并配置字节搬运。 TestCrc16AndDma 先把 CRC16 与 LRC 初值写为 0x00、开启清除并设为大端,再把 456 个字节搬到 CRC->CRC16DAT。

src/crc_dma.csrc/crc_dma.c · TestCrc16AndDma
    /*CRC 16 and LRC big endian test wirh 0 initial value*/
    __CRC16_SetCrc(0x00);
    __CRC16_SetLRC(0x00);
    __CRC16_SetCleanEnable();
    __CRC16_SetBigEndianFmt();
src/crc_dma.csrc/crc_dma.c · TestCrc16AndDma
    /*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;

字节搬运改用 BYTE 宽度,初值与清除设置在搬运前完成,顺序不能颠倒。

5. 读回 CRC16 与 LRC 结果。 搬运完成后读取 __CRC16_GetCrc 与 __CRC16_GetLRC 并与期望值比较。

src/crc_dma.csrc/crc_dma.c · TestCrc16AndDma
    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");
    }

期望值由数据内容与初值共同决定,换用其它数据集时这两个常量要一并更新。

对应源码: src/main.c、src/crc_dma.c

5.5.4 注意与排障

场景 检查与建议 来源
CRC32 复位 向 RESET 位写 1 会把 CRC32R 复位为 0xFFFFFFFF,该位由硬件自动清除,无需手动清零。 用户手册
CRC16 字节序 ENDIAN 位配置 CRC16 数据的解析顺序,决定输入数据按大端还是小端解析。 用户手册
清除方式 CLEAR 位自清除,把 CRC 值从 0xFFFF_FFFF 置为默认值 0x0000。 用户手册
独立数据寄存器 CRC32IDAT 是通用数据寄存器,不受 RESET 位影响。 用户手册
计算初值 需要指定初值时,应先向 CRC_CRC16D、CRC_CRC32D 写入初始值,否则本次计算会沿用上一次的结果。 用户手册
LRC 校验 LRC 为异或校验,软件应在下一次计算前清除 LRC 初值。 用户手册
数据宽度 CRC16DAT 支持 8 位、16 位与 32 位写操作。 用户手册
国民技术开发套件文档中心

按芯片系列在线查阅国民技术 MCU 开发套件的芯片资料、SDK、驱动 API 与 Demo 示例说明。

© 2026 国民技术股份有限公司 版权所有粤ICP备11069485号本站使用不含 Cookie 的匿名访问统计