讲解 STM32 的 DMA 内存到内存传输
函数名作用示例启动一次 DMA 传输等待传输完成(阻塞)中断处理函数在中调用搬运完成后的回调用户自己写。
·
代码实战 + 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 示例)
-
打开 CubeMX
-
找到 DMA → DMA1 Channel1
-
选择:
-
Direction: Memory to Memory
-
Priority: Medium 或 High
-
Enable Circular Mode: 一般关闭(除非需要循环传)
-
勾选 “Use DMA interrupt”(建议)
-
-
点生成代码
🔢 五、实际内存搬运实验(拷贝数组)
👇 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 或中断 |
| 推荐场景 | 大块数据复制、高速缓存刷新、图像/音频处理 |
更多推荐



所有评论(0)