代码实战 + HAL库函数详解系统讲解 STM32 的 DMA 内存到内存传输


🧠 一、DMA内存到内存传输是什么?

一句话解释:

DMA内存到内存传输就是不通过CPU,让DMA控制器把一块内存的数据自动搬运到另一块内存中。

类比理解(费曼式):

就像你有两个仓库 A[]B[],你不想亲自一件一件地去搬货,于是请了一个搬运机器人(DMA)

  • 你告诉它:

    • 要搬什么(源地址 A)

    • 搬到哪儿(目的地 B)

    • 一共搬多少件(传输长度)

  • 它就自动完成任务!你只要喝茶等通知即可 🍵。


📦 二、DMA 内存到内存的特点

特性 说明
不需要外设参与 只涉及内存之间的数据传输
非常快 比 CPU memcpy 更高效,尤其是大数据量
支持中断通知 可以配置搬完后中断,提示我们处理结果
支持多种数据宽度 8位 / 16位 / 32位
支持一次性或循环 可以搬一次也可以无限搬(常用于缓存机制)

🔩 三、DMA配置条件(重要前提)

只有支持“内存到内存”功能的 DMA 通道,才能完成此操作。

在 STM32 中:

  • 需要开启 Memory-to-Memory Mode

  • 一般是 DMA1 通道 1 支持该功能(以 STM32F103 为例)


🛠️ 四、CubeMX 配置步骤(STM32F103 示例)

  1. 打开 CubeMX

  2. 找到 DMA → DMA1 Channel1

  3. 选择:

    • Direction: Memory to Memory

    • Priority: Medium 或 High

    • Enable Circular Mode: 一般关闭(除非需要循环传)

    • 勾选 “Use DMA interrupt”(建议)

  4. 点生成代码


🔢 五、实际内存搬运实验(拷贝数组)

👇 1. 准备数据

#define SIZE 10
uint32_t src[SIZE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
uint32_t dst[SIZE] = {0};  // 目标数组

👇 2. 启动 DMA 传输

HAL_DMA_Start(&hdma_memtomem_dma1_channel1,
              (uint32_t)src,
              (uint32_t)dst,
              SIZE);

👇 3. 等待完成或使用中断

主动等待方式

while (HAL_DMA_PollForTransfer(&hdma_memtomem_dma1_channel1,
                               HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY) != HAL_OK);

中断方式

void HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma) {
  HAL_DMA_IRQHandler(&hdma_memtomem_dma1_channel1);
}

void HAL_DMA_XferCpltCallback(DMA_HandleTypeDef *hdma)
{
  if(hdma->Instance == DMA1_Channel1)
  {
    printf("DMA memory copy complete!\r\n");
  }
}

🔧 六、HAL函数讲解总结表格

函数名 作用 示例
HAL_DMA_Start() 启动一次 DMA 传输 HAL_DMA_Start(&hdma, src, dst, len);
HAL_DMA_PollForTransfer() 等待传输完成(阻塞) HAL_DMA_PollForTransfer(...);
HAL_DMA_IRQHandler() 中断处理函数 stm32f1xx_it.c 中调用
HAL_DMA_XferCpltCallback() 搬运完成后的回调 用户自己写

🧪 七、完整代码示例

#include "main.h"

#define SIZE 10
uint32_t src[SIZE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
uint32_t dst[SIZE] = {0};

extern DMA_HandleTypeDef hdma_memtomem_dma1_channel1;

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_DMA_Init(); // 自动生成的初始化函数

  HAL_DMA_Start(&hdma_memtomem_dma1_channel1, (uint32_t)src, (uint32_t)dst, SIZE);
  HAL_DMA_PollForTransfer(&hdma_memtomem_dma1_channel1, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);

  // 验证数据
  for(int i = 0; i < SIZE; i++)
  {
    printf("dst[%d] = %ld\r\n", i, dst[i]);
  }

  while (1);
}

📌 八、常见问题排查

问题 可能原因
搬运无效,dst数组无变化 DMA 没有启动 / 地址类型转换错误
程序卡住 没有 HAL_Init() / 没有 Poll 完成
HAL_BUSY 返回 DMA 正在搬,未完成上一次
中断不触发 NVIC 没开 / 没有写回调函数

✅ 九、小结

知识点 内容
目的 在不使用 CPU 的情况下将内存 A → B
应用 快速初始化数据、图像处理、缓存同步
实现方式 CubeMX + HAL_DMA_Start + HAL_DMA_Poll 或中断
推荐场景 大块数据复制、高速缓存刷新、图像/音频处理

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐