STM32 的硬件 CRC 外设可大幅提升校验运算速度,而 DMA(直接存储器访问)能彻底解放 CPU,二者结合可实现 “数据自动传输 + 硬件 CRC 计算” 的高效方案,核心价值在于 “无 CPU 干预的批量数据校验”,适用于通信数据校验、存储数据完整性验证等场景。本文基于 STM32G474,详解 DMA 配合 CRC 的实现原理、配置步骤与实战代码。
资料获取:如何通过DMA配合CRC功能
1. 核心优势:DMA+CRC 为何优于传统方案?
传统 CRC 计算依赖软件算法或单纯硬件 CRC,存在明显局限,而 DMA+CRC 组合实现双重优化:
| 方案类型 | 核心劣势 | DMA + 硬件 CRC 优势 |
|---|---|---|
| 软件 CRC | 运算速度慢,占用 CPU 资源 | 硬件 CRC 运算(最高 4 个 AHB 时钟 / 32 位数据),速度提升 10 倍 + |
| 硬件 CRC(无 DMA) | 需 CPU 手动传输数据,效率低 | DMA 自动完成数据从内存到 CRC 外设的传输,CPU 仅需配置和处理结果 |
| 适用场景 | 少量数据、低实时要求 | 批量数据(如数组、存储块)、高实时通信场景 |
2. 实现原理:3 大关键可行性分析
2.1 时间可行性:DMA 传输与 CRC 计算时序匹配
- 单次 DMA 传输(Mem-to-Mem 模式)包含 “AHB 读 + AHB 写”,至少需 4 个 AHB 时钟周期。
- 硬件 CRC 计算耗时:8 位数据 1 个时钟、16 位 2 个时钟、32 位 4 个时钟,完全适配 DMA 传输时序,无需额外等待,可背靠背连续传输。
2.2 总线连接可行性:DMA 可直接访问 CRC 外设
2.3 DMA 传输模式:Mem-to-Mem 模式适配 CRC 特性
- CRC 外设无触发 DMA 传输的信号,因此必须使用 DMA 的 “内存到内存(Mem-to-Mem)” 模式,由软件发起传输,传输完成后触发中断通知 CPU 读取结果。
- 注意:Mem-to-Mem 模式禁止使用循环模式(CIRC 位需清 0)。
3. 实战实现:STM32G474 完整步骤
以 NUCLEO-G474RE 为例,基于 STM32CubeMX+HAL 库实现,目标是对 114 个 32 位数据进行 CRC 校验。
3.1 第一步:STM32CubeMX 配置(核心步骤)
(1)激活 CRC 外设
- 进入 “Pinout & Configuration”→“Computing”→“CRC”,勾选 “Activated”。
- 基础参数配置:“Input Data Format” 设为 “Words”(32 位数据,可按需改为字节 / 半字),其余参数默认(多项式 0x04C11DB7,初始值 0xFFFFFFFF)。
(2)配置 DMA(Mem-to-Mem 模式)
- 进入 “System Core”→“DMA”,点击 “Add” 添加通道,选择 “MEMTOMEM”(内存到内存)。
- 配置参数:
- Mode:Normal(非循环模式,Mem-to-Mem 禁止循环)。
- Data Width:源 / 目标均设为 “Word”(与 CRC 输入格式匹配)。
- Src Memory:勾选 “Increment Address”(源数据地址递增)。
- Dst Memory:不勾选(CRC_DR 寄存器地址固定,无需递增)。
(3)使能 DMA 中断
- 进入 “NVIC”→“DMA1 channel1 global interrupt”,勾选 “Enabled”(传输完成后触发中断,读取 CRC 结果)。
(4)生成代码
- 点击 “Project Manager” 生成工程,CubeMX 会自动创建 CRC 和 DMA 句柄(如
hcrc、hdma_memtomem_dma1_channel1)。
3.2 第二步:软件代码实现(关键部分)
(1)定义数据与预期结果
#define BUFFER_SIZE 114 // 待校验数据长度
// 待校验32位数据数组
static const uint32_t aDataBuffer[BUFFER_SIZE] = {
0x00001021, 0x20423063, 0x408450a5, /* ... 其余111个数据省略,完整数组见文档 ... */
0x2e933eb2, 0x0ed11ef0
};
// 预期CRC结果(默认配置下)
const uint32_t uwExpectedCRCValue = 0x379E9F06;
uint32_t uwCRCValue = 0; // 存储实际CRC结果
(2)注册 DMA 传输完成回调函数
传输完成后通过中断读取 CRC 结果,需注册回调函数:
/**
* @brief DMA传输完成回调函数(读取CRC结果)
*/
static void DMA_CRC_TransferCpltCallback(DMA_HandleTypeDef *hdma) {
if (hdma->Instance == DMA1_Channel1) { // 匹配配置的DMA通道
uwCRCValue = hcrc.Instance->DR; // 读取CRC_DR寄存器的校验结果
}
}
(3)初始化并启动 DMA+CRC
在main函数的USER CODE BEGIN 2区域添加代码:
// 1. 注册DMA传输完成回调函数
HAL_DMA_RegisterCallback(&hdma_memtomem_dma1_channel1,
HAL_DMA_XFER_CPLT_CB_ID,
DMA_CRC_TransferCpltCallback);
// 2. 重置CRC计算单元(初始化CRC_DR为初始值0xFFFFFFFF)
__HAL_CRC_DR_RESET(&hcrc);
// 3. 启动DMA传输:从aDataBuffer传输到CRC_DR,共BUFFER_SIZE个数据
HAL_DMA_Start_IT(&hdma_memtomem_dma1_channel1,
(uint32_t)aDataBuffer, // 源地址:数据数组
(uint32_t)&(hcrc.Instance->DR), // 目标地址:CRC数据寄存器
BUFFER_SIZE); // 传输数据个数
3.3 第三步:结果验证
DMA 传输完成后,uwCRCValue会存储实际校验结果,可通过串口打印或调试器验证是否与uwExpectedCRCValue一致:
// (可选)在主循环中打印结果
while (1) {
if (uwCRCValue != 0) { // 若已获取CRC结果
printf("实际CRC结果: 0x%08Xn", uwCRCValue);
printf("预期CRC结果: 0x%08Xn", uwExpectedCRCValue);
printf("校验结果: %sn", (uwCRCValue == uwExpectedCRCValue) ? "成功" : "失败");
uwCRCValue = 0; // 重置,便于下次校验
HAL_Delay(1000);
}
HAL_Delay(100);
}
4. 关键避坑指南
- DMA 模式限制:Mem-to-Mem 模式必须禁用循环模式(CubeMX 中 “Mode” 设为 Normal),否则传输无法终止。
- 地址增量配置:CRC_DR 寄存器地址固定,DMA 目标内存(Dst Memory)的 “Increment Address” 必须取消勾选。
- 数据宽度匹配:CRC 输入数据格式(字节 / 半字 / 字)需与 DMA 的 “Data Width” 一致,否则会导致校验错误。
- CRC 重置:每次新校验前必须调用
__HAL_CRC_DR_RESET(&hcrc),确保初始值正确。
DMA + 硬件 CRC 的组合方案完美解决了传统校验 “速度慢、CPU 占用高” 的痛点,尤其适用于批量数据校验场景。通过 STM32CubeMX 的可视化配置 + 简洁的 HAL 库代码,可快速落地该方案。
2596