☰
【stm32】DMA
2026/10/3 12:37:14 网站建设 项目流程

🔥铅笔小新z:个人主页
🎬博客专栏:stm32学习
💫滴水不绝,可穿石;步履不休,能至渊。

DMA 详解 —— 让数据"自动搬家"的搬运工

面向初学者的通俗解读,配合逐行注释的代码,以及每个知识点后的"一句话总结"。
本文基于 STM32F103(Cortex-M3)+ DMA1 实战讲解,帮你彻底搞懂 DMA 的原理、通道、寄存器与中断。


目录

  1. DMA 是什么
  2. 为什么需要 DMA
  3. DMA 的核心目标与 CPU 的职责
  4. 四方关系:DMA 到底在哪
  5. 宏观理解 DMA 工作过程
  6. 总线共享:CPU 和 DMA 会打架吗
  7. DMA 通道与仲裁
  8. 查文档:DMA 的寄存器与地址空间
  9. 通道核心配置寄存器
  10. DMA 中断:TC、HT、TE
  11. 区分中断类型:ISR 与 IFCR
  12. 配置案例

1. DMA 是什么

1.1 全称与定义

DMA(Direct Memory Access,直接存储器访问),用来提供外设和存储器之间、或者存储器和存储器之间的高速数据传输。无需 CPU 干预,数据就能通过 DMA 快速移动,从而节省 CPU 资源去做其他操作。

通俗地说,DMA 就是单片机内部的一个外设控制器,它拥有独立的地址总线和控制逻辑。在 CPU 授权之后,它可以接管总线控制权,在不占用 CPU 资源的情况下,自动把数据从一个地址(源)搬到另一个地址(目的)。

1.2 一个搬运货物的比喻

为了快速理解 DMA,先看一个搬运货物的例子:

角色对应
CPU老板
DMA专业搬运工
数据需要搬运的货物
外设(串口、I2C、SPI、ADC 等)货物的来源或目的地之一
存储器(内存)货物的来源或目的地之一

老板(CPU)不用亲自搬货,他把"从哪搬、搬到哪、搬多少"交代给搬运工(DMA),搬运工自己完成,老板去做更重要的决策工作。


📌 本知识点一句话总结

DMA(直接存储器访问)是单片机内部的一个独立硬件控制器,拥有自己的地址总线和控制逻辑。它的作用是:在 CPU 授权后接管总线,不占用 CPU 就自动把数据从"源地址"搬到"目的地址",实现外设↔存储器、存储器↔存储器之间的高速传输。可以把 CPU 比作老板,DMA 比作专业搬运工——老板只下指令,搬运工干体力活,从而把宝贵的 CPU 资源留给逻辑运算等更重要的工作。


2. 为什么需要 DMA

2.1 没有 DMA 时:CPU 亲自搬货

如果没有 DMA,把数据从外设搬到内存、从内存搬到外设,都是 CPU 自己做的:

外设 →(CPU 读取)→ CPU →(CPU 写入)→ 内存

CPU 不仅要执行用户代码、响应中断,还要亲自干"数据搬迁"这种体力活。结果是:CPU 被拖慢,同时数据搬迁的响应速度也被拖慢。

2.2 引入 DMA 后:CPU 只发指令

引入 DMA 模块后,相当于请了搬运工(DMA)。CPU 只需要告诉 DMA 三件事:

  1. 搬多少(数据数量);
  2. 从哪搬(源地址);
  3. 搬到哪(目的地址)。

CPU 不需要亲自参与搬运过程,只需发布搬运命令,最后处理搬运完的货物即可。

2.3 为什么这么设计

CPU 资源非常宝贵,应该专注于逻辑运算、业务处理这类重要工作;而"大批量数据的重复搬运"这类简单、重复的工作,应该交给 DMA 完成。

没有 DMA:CPU 既当老板又当搬运工 → 累死且低效 有 DMA: CPU 当老板,DMA 当搬运工 → 各司其职,高效


📌 本知识点一句话总结

引入 DMA 的根本原因是解放 CPU:没有 DMA 时,外设与内存之间的数据拷贝都要 CPU 亲自执行,既拖慢 CPU 又拖慢数据响应。引入 DMA 后,CPU 只需下发"源地址、目的地址、数据量"三个参数即可,搬运过程由 DMA 硬件完成。这样 CPU 专注于逻辑运算和业务处理,简单重复的搬运工作交给 DMA,实现"各司其职",整体效率大幅提升。


3. DMA 的核心目标与 CPU 的职责

3.1 核心目标

数据传输(搬运)过程需要一个主体来控制。在 CPU 不参与的情况下,就需要另一个主体来控制传输过程,这就是DMA 控制器。不同架构 CPU 的 DMA 控制器实现细节不同,但目标一致:代替 CPU 控制数据传输过程。

3.2 CPU 到底做什么

一个容易误解的点:说"DMA 不占用 CPU"不等于"CPU 什么都不用干"。CPU 需要做两件事:

  1. 启动 DMA 传输过程(初始化配置 + 使能);
  2. 在 DMA 传输结束时执行下一个业务逻辑(如启动新的传输、或处理已收到的数据,例如格式化打印)。

记住:CPU 不参与的是"搬运过程",而不是"启动"和"收尾"。启动和收尾这两头,还得 CPU 亲自来。


📌 本知识点一句话总结

DMA 的核心目标是"代替 CPU 控制数据传输过程",但 CPU 并非完全不参与:它要负责两件事——① 在传输前配置并启动 DMA;② 在传输结束后(收到中断)执行后续业务逻辑(如启动下一次传输、处理数据)。所谓"不占用 CPU",仅指搬运过程本身由 DMA 硬件完成,CPU 不介入;但启动和收尾仍需 CPU 参与。


4. 四方关系:DMA 到底在哪

明确 DMA 的定位——它和 Cortex-M3(CPU)、MCU、开发板(灵犀板)之间的四方关系:

  • DMA 在 MCU 内部,是 MCU 内部的一个独立硬件控制单元,属于一个独立的片上外设。
  • 但从更本质的角度看,DMA 是一个总线主设备(Bus Master),这一点和普通片上外设(如串口,是被动的从设备)有区分度。

关键词:DMA 是"总线主设备"——它能主动发起总线操作(读/写),而普通外设只是被动响应。这是 DMA 能"自主搬数据"的根本原因。

在 STM32F103 系列芯片内部,一般包含两个 DMA 控制器模块:

  • DMA1:有 7 个通道;
  • DMA2:有 5 个通道(只有大容量和互联型产品才有)。


后续研究时,把注意力放在DMA1上即可(DMA2 原理完全一样)。


📌 本知识点一句话总结

DMA 位于 MCU 内部,是一个独立的硬件控制单元(片上外设),但本质上是总线主设备(Bus Master)——它能主动发起总线读写操作,这正是它能自主搬运数据的根本原因(普通外设只是被动的从设备)。STM32F103 有两个 DMA 控制器:DMA1(7 通道)和 DMA2(5 通道,仅大容量/互联型有),两者原理相同,学习时以 DMA1 为例即可。


5. 宏观理解 DMA 工作过程

DMA 传输主要发生在外设和内存之间(存储器到存储器虽然也支持,但应用很少)。一个 DMA 数据传输过程有4 个核心参与主体:

  • CPU(Cortex-M3 内核):负责启动 DMA 数据传输。
  • 外设:负责向 DMA 控制器发起 DMA 请求。
  • DMA 控制器:负责响应 DMA 请求并控制数据传输过程。
  • 内存(SRAM):作为数据的源或目的缓冲区。

下面以串口(USART1)为例,分 4 步建立宏观认知:

5.1 第一步:CPU 启动 DMA

CPU 让 DMA 帮它搬迁数据,这个工作需要用户提前设置好(源地址、目的地址、数量等),并最终使能通道。

5.2 第二步:外设发起 DMA 请求

  • 当外设收到数据时(如串口收到一个字节),USART1 会向 DMA 发起"DMA 数据搬迁请求"。
  • 当外设要发数据时(如串口要往外发一个字节),USART1 也会向 DMA 发起请求。

5.3 关于 DMA 应答(硬件"握手")

外设在发生特定事件后,向 DMA 控制器发送一个请求信号;当 DMA 控制器开始访问该外设时,会先回一个应答信号。这个过程叫 DMA 的硬件"握手"。

  • 从电气角度,握手就是电脉冲(或高低电平)。
  • 这个握手过程不需要工程师配置,是 DMA 和对应片上外设自主完成的。

5.4 第三步:DMA 响应请求并控制传输

  • 外设 → 内存:DMA 把外设数据寄存器里的数据搬到内存缓冲区。
  • 内存 → 外设:DMA 把内存缓冲区的数据搬到外设数据寄存器。

5.5 第四步:通知 CPU,传输完成

数据全部搬完后,DMA 通过中断通知 CPU,CPU 收到后介入处理后续数据。

完整流程一句话:CPU 启动 → 外设请求 → DMA 握手并搬运 → DMA 中断通知 CPU 收尾。


📌 本知识点一句话总结

一个完整的 DMA 传输包含四个主体和四步流程:四个主体是 CPU、外设、DMA 控制器、内存。四步流程是:① CPU 配置并启动 DMA;② 外设因"收到数据/要发数据"向 DMA 发请求;③ DMA 与外设硬件握手后响应请求,把数据从外设寄存器搬到内存(或反向);④ 搬完产生中断通知 CPU 收尾。其中"握手"(请求/应答)是 DMA 与外设硬件自动完成的,无需软件配置。


6. 总线共享:CPU 和 DMA 会打架吗

6.1 提出问题

CPU 本来也要访问外设和内存(读 Flash 代码、访问 USART、写内存等),DMA 也要访问。两者都要通过AHB 总线和总线矩阵进行,那岂不是会冲突?

6.2 官方说明

意法官方文档(参考手册)给出的结论是:

DMA 控制器和 Cortex-M3 核心共享系统数据总线。当 CPU 和 DMA 同时访问相同目标(RAM 或外设)时,DMA 请求会暂停 CPU 访问系统总线达若干个周期,以保证 CPU 至少可以得到一半的系统总线(存储器或外设)带宽。总线仲裁器执行循环调度。

6.3 结论

  • 官方没有披露 DMA 和总线的详细内部细节,但对应用层来说足够了。
  • 最终结论:逻辑上 CPU 和 DMA 互不影响。总线仲裁器通过"循环调度",保证两边都能获得至少一半带宽,不会死锁或互相饿死。

📌 本知识点一句话总结

CPU 和 DMA 都通过 AHB 总线和总线矩阵访问外设/内存,存在潜在的资源竞争。解决方式是总线仲裁器的循环调度:当 CPU 和 DMA 同时访问相同目标时,DMA 请求会暂停 CPU 访问总线若干周期,从而保证 CPU 至少能获得一半总线带宽。因此逻辑上 CPU 和 DMA 互不影响,能安全地共享系统数据总线。


7. DMA 通道与仲裁

7.1 通道初识:为什么要多个通道

MCU 里的片上外设非常多(USART、SPI、I2C、ADC、TIM……)。如果 DMA 控制器只有一个通道,一次只能处理一个外设的请求,效率太低。

为了支持多个外设的数据搬迁"并发"进行,DMA 提供了多个通道,每个通道独立工作:

每一个通道,其实就是一个"子 DMA",主要负责进行数据搬迁。

7.2 一个疑问:总线不是共享的吗,怎么能并发?

多通道用的是同一套总线,所以严格说同一时刻只能有一个通道真正占有总线。但要注意:

  • 外设数据"需要搬迁" ≠ “一直在搬迁”。多个外设往往在不同时刻才需要搬数据(空闲时总线就空着)。
  • DMA 的"并发"本质是分时复用总线——多个通道交替使用总线空闲时间完成各自搬迁。
  • 多通道的终极目标是"不让总线闲着",充分复用总线资源。

打个比方:一条单车道马路,不同的车(通道)轮流过,虽然同一时刻只能过一辆,但整体看起来是"并发"运行的。

7.3 仲裁器与通道优先级

既然同一时刻只能让一个通道拥有总线,那到底让谁先?这就需要优先级和仲裁器。

关键认知(两个"细节"):

  • 优先级高 ≠ 一直霸占总线:因为外设不一定满足搬迁条件(还没收到数据、还没发完等),高优先级通道暂时不工作时,低优先级通道照样能干活。
  • 仲裁器:用来判断优先级、决定谁拥有总线资源的模块。

比喻:你是航空公司 VIP 乘客,优先级高,但你不是每天坐飞机、也不是时刻在排队。当你排队时,需要"工作人员"(仲裁器)确认你的 VIP 身份让你先走;你不排队时,普通乘客照常通行。

仲裁器根据通道请求的优先级来启动外设/存储器的访问,优先级管理分2 个阶段:

阶段一:软件优先级(4 个等级)

在DMA_CCRx寄存器中设置:

DMA_PRIORITY_LOW// 低DMA_PRIORITY_MEDIUM// 中DMA_PRIORITY_HIGH// 高DMA_PRIORITY_VERY_HIGH// 最高
阶段二:硬件优先级

如果两个请求的软件优先级相同,则编号较小的通道优先级更高。例如通道 2 优先于通道 4。


这个"硬件优先级"由芯片内部走线决定,通道编号固定,用户无需也无法设置。

7.4 细节 1:外设请求信号(硬映射)

哪种片上外设用哪个 DMA 通道,在芯片设计(走线)时就已经固定了。这种"外设↔通道"的固定对应关系,叫硬映射(Hard Mapping)。


以 USART1 为例(DMA1):

  • USART1_TX → 通道 4
  • USART1_RX → 通道 5

收发映射到不同通道,是为了让收发"几乎同时"用 DMA 搬迁,从而在 DMA 场景下支持 USART1 的全双工。

同时,任意一个通道只能被一种外设使用(排他性)。虽然一个通道理论上能接到多个外设的请求,但用户必须提前配置好让哪个外设用这个通道,不能同时让两个以上外设使用同一个通道。

外设要使用 DMA,对应外设控制寄存器里必然有"开启 DMA"的配置位。以 USART1 为例,它的CR3 寄存器里的DMAT(发送 DMA 使能)和DMAR(接收 DMA 使能)就是开关。任何要用 DMA 的外设,都必然存在类似配置位。

7.5 细节 2:优先级聚合与仲裁过程

把"打开一个通道"想象成"选中一个开关"。多个通道的请求信号汇聚到仲裁器,仲裁器根据(软件优先级 → 硬件优先级)的顺序,选出当前应该占有总线的通道,执行该通道的数据搬迁。

7.6 核心结论:通道的内部构成

逻辑上,一个通道内部一定包含一套核心配置寄存器,n 个通道就包含 n 份核心寄存器。每个通道都要独立配置:优先级、传输方向、数据量、地址自增等,所以这些配置寄存器每个通道都有一份。


📌 本知识点一句话总结

DMA 通过"多通道"实现多外设的并发搬迁,每个通道是一个独立的"子 DMA"。多通道本质是分时复用总线(同一时刻只有一个通道真正占有总线),终极目标是"不让总线闲着"。当多通道同时请求时,由仲裁器决定谁先,优先级分两级:先看软件优先级(CCR 寄存器的 PL 位,4 级),相同再看硬件优先级(通道编号小的优先)。外设与通道的对应关系是芯片设计时固定好的硬映射(如 USART1_TX→通道4、RX→通道5),且一个通道具有"排他性"(同一时刻只能服务一个外设)。


8. 查文档:DMA 的寄存器与地址空间

8.1 寄存器被映射到地址空间

STM32 的片上外设寄存器是统一编址、直接映射到 CPU 地址空间的,所以可以直接用 C 语言指针访问 DMA 模块。

8.2 DMA1 的地址范围

  • DMA1 地址范围:0x4002 0000~0x4002 03FF
  • 大小计算:0x400203FF - 0x40020000 + 1 = 0x400 = 1024 字节 = 1KB
  • 注意:CPU 虽为 DMA1 分配了 1KB,但实际只用到开头很少一部分,后续地址是保留的,访问可能产生不可预知行为。

8.3 DMA1 的寄存器数量

DMA1 共有2 + 4 × 7 = 30个寄存器:

  • “2”:DMA1 整体关于通道中断管理的两个寄存器(即 ISR 和 IFCR,稍后讲)。
  • “4”:每个通道都有 4 个管理/配置类寄存器。
  • “7”:DMA1 有 7 个通道。

访问方式:0x40020000 + 寄存器偏移。

DMA2 结构几乎完全一样,只是:起始地址0x40020400,只有 5 个通道,所以是2 + 4 × 5 = 22个寄存器。

核心结论:用 C 指针通过"基地址 + 偏移量"就能直接访问 DMA 内部寄存器,从而配置通道、控制 DMA 行为。


📌 本知识点一句话总结

STM32 的 DMA 寄存器是统一编址、可直接用 C 指针访问的。DMA1 基地址为0x40020000,地址空间 1KB,共 30 个寄存器(= 2 个中断管理寄存器 + 每通道 4 个配置寄存器 × 7 个通道)。DMA2 基地址0x40020400,5 个通道,共 22 个寄存器。访问任意寄存器都是"基地址 + 偏移量",配置 DMA 的本质就是 CPU 写这些寄存器。


9. 通道核心配置寄存器

9.1 概览:每个通道 4 个寄存器

每个 DMA 通道有 4 个独立寄存器:

寄存器缩写展开命名英文全称核心功能
DMA_CCRxDMA1_CCR1 ~ CCR7DMA Channel x Configuration Register配置与优先级:软件优先级、传输方向、模式、数据宽度、自增等
DMA_CNDTRxDMA1_CNDTR1 ~ CNDTR7DMA Channel x Number of Data to Transfer Register传输数量:设定并记录剩余待搬运数据总量
DMA_CPARxDMA1_CPAR1 ~ CPAR7DMA Channel x Peripheral Address Register外设地址:存储外设端起始物理地址
DMA_CMARxDMA1_CMAR1 ~ CMAR7DMA Channel x Memory Address Register存储器地址:存储内存端起始物理地址

每个通道都对应一套这 4 个寄存器,n 个通道就有 n 份。配置 DMA 的本质就是 CPU 设置这些寄存器。

9.2 CCR 寄存器详解

DMA_CCRx是特定通道的核心寄存器。下面逐个解释其中的关键位。



9.2.1 通道使能 EN 与优先级 PL
  • EN(通道使能位):决定是否开启对应通道。因为外设和通道是固定关联的,使能通道就允许对应外设使用该通道搬运数据。
    • ⚠️细节:使能了 ≠ 仲裁成功。使能只表示"这个通道可以工作、可以参与申请仲裁",但当前是否真正搬运,取决于仲裁结果。
  • PL(通道优先级):设置该通道的软件优先级(低/中/高/最高)。当多个通道同时申请时,仲裁器比较各通道 PL 值决定谁先。
9.2.2 传输方向 DIR

外设和存储器的 DMA 传输方向有两种:

  • DIR=0:外设 → 内存(从外设读)。例如从串口、I2C、SPI 接收数据。
  • DIR=1:内存 → 外设(从内存读,即写外设)。例如向串口、I2C、SPI 发送数据。

记忆技巧:永远站在内存角度。

  • 1像一支笔,用来写字,DIR=1 表示从内存"写"数据到外设。
  • 0像一张嘴,读书用嘴"读",DIR=0 表示把数据从外设"读"到内存。
9.2.3 数据宽度 PSIZE 和 MSIZE

数据宽度指定每次传输的数据大小,可配置 8/16/32 位。外设和内存是独立设备,可分别设置宽度。

  • 例如:外设 16 位、内存 16 位,则每次传输 2 字节。

⚠️注意:外设和内存宽度可以不同,但需匹配以避免数据错误:

  • 源窄目的宽 → 数据扩展(类似int b = a;里a是 char 会扩展);
  • 源宽目的窄 → 数据截断丢失(类似char a = b;里b是 int 会截断)。

多数情况把源端和目的端宽度配置相同即可避免截断错误。

9.2.4 地址自增 PINC 和 MINC

DMA 需要设置参与传输的双方是否会地址自增。

模式PINC/MINC步长逻辑典型应用说明
单增模式(常用)PINC=0, MINC=1外设固定,内存 +MSIZE串口收发外设守着固定数据寄存器(如 USART_DR),内存按顺序存储
双增模式PINC=1, MINC=1外设 +PSIZE,内存 +MSIZE数据拷贝源地址和目的地址同步移动,整块迁移
双不增模式(不常用)PINC=0, MINC=0外设固定,内存固定引脚镜像两个地址原地踏步,硬件寄存器↔内存变量实时映射

地址自增时,按对应设备自己设置的数据宽度自增:

宽度设置数据类型物理步长
00(8 位)uint8_t+1 字节
01(16 位)uint16_t+2 字节
10(32 位)uint32_t+4 字节
9.2.5 循环模式 CIRC

DMA 有时只需做一次,有时需要重复执行:

单次模式(Normal)循环模式(Circular)
核心逻辑搬完预设数量即停止,进入空闲搬完立即重头开始,无限循环
CNDTR 归零后保持为 0自动重装为初始总数
地址指针停留在末尾地址自动跳回起始地址
EN 使能位硬件自动清零(通道关闭)始终保持 1(持续开启)
再次触发需软件重新配置并开启 EN只需启动一次,硬件自动触发
典型应用串口发字符串、内存拷贝ADC 连续采样、音频播放、引脚镜像
数据覆盖风险无(搬完即停)高(CPU 处理慢时第二圈覆盖第一圈)

9.3 传输数量寄存器 CNDTR

数据传输数量 NDT 表示"传输数据项的个数",最大可配置65535。几点含义:

  • NDT不是字节数,实际总字节数 = NDT × 数据宽度。
  • 示例:NDT=32,宽度 32bit(4 字节),总字节 = 32 × 4 = 128 字节。
  • 数据宽度是"基本大小",传输数量是"多少个基本大小"。
  • 从外设到内存:表示从外设读取的数据项个数;从内存到外设:表示向外设发送的数据项个数。
  • NDT 决定了单次 DMA 传输何时结束(传输完成)。

9.4 外设地址 CPAR 与内存地址 CMAR

严格说,搬迁数据不是"从外设到内存",而是:

  • 从外设数据寄存器(DR)到内存地址指向的空间,或
  • 从内存地址指向的空间到外设数据寄存器。

所以通道配置里必然要有描述"源/目的"的寄存器:

  • DMA_CPARx:保存外设数据寄存器地址;
  • DMA_CMARx:保存内存地址;
  • 再由DMA_CCRx的 DIR 位表示传输方向。

对齐(了解即可):

  • 半字对齐:地址能被 2 整除(bit0 = 0);
  • 字对齐:地址能被 4 整除(bit0、bit1 = 0)。
  • 现代 CPU 按"块"读内存(如一次 4/8 字节),若数据跨越两个读取周期则需读两次拼接,耗时翻倍。设置 2/4 字节读写单位时,DMA 会自动处理对齐。

9.5 通道配置小结

对单个 DMA 通道的配置参数,逻辑上分3 类:

  1. 外设端:外设地址(CPAR)、外设数据宽度(PSIZE)、外设自增(PINC);
  2. 存储器端:内存地址(CMAR)、内存数据宽度(MSIZE)、内存自增(MINC);
  3. 传输过程参数:传输方向(DIR)、传输数量(CNDTR/NDT)、优先级(PL)、循环模式(CIRC)、中断使能(TCIE/HTIE/TEIE)等。

此时还有个未解决的问题:搬完了,CPU 怎么知道?——答案是中断,下一节讲。


📌 本知识点一句话总结

每个 DMA 通道由 4 个寄存器构成:CCR(配置:方向/宽度/自增/循环/优先级/中断使能)、CNDTR(传输数据项个数 NDT,最大 65535,总字节 = NDT×宽度)、CPAR(外设数据寄存器地址)、CMAR(内存地址)。核心配置可归纳为三类:外设端、存储器端、传输过程参数。关键点:DIR 决定方向(0 外设→内存,1 内存→外设);宽度建议源/目相同避免截断;PINC/MINC 控制地址自增;CIRC 决定单次还是循环;NDT 决定何时结束。


10. DMA 中断:TC、HT、TE

10.1 直接回答问题

Q:数据搬迁完毕后,CPU 怎么知道?DMA 怎么告诉 CPU?

A:DMA(核心是通道)本质也是一个 MCU 片上外设,和串口、SPI、I2C 一样,也能通过中断通知 CPU。当 DMA 搬迁完数据,会通过中断通知 CPU,CPU 在中断处理函数里做后续处理。

10.2 三种中断(在 CCR 里配置)

DMA_CCRx 里与中断有关的配置位(每个通道都有):

  • TCIE(传输完成中断使能):所有数据传输完成时触发,最常用。作用就是通知 CPU"我搬完了,你可以处理了"。

  • HTIE(半传输中断使能):数据传输量达到预设总额一半时触发。

  • TEIE(传输错误中断使能):传输中发生总线错误或配置冲突时触发,用于异常捕获和处理,避免程序卡死。

10.3 为什么要有"半传输中断"

原因:实现数据传输与数据处理的并行,消除等待时间。

  • 若只有 TC 中断,CPU 必须等整块数据搬完才能开始处理。
  • 有了 HT 中断,可以先搬一半 → 触发 HT 中断 → CPU 处理前半段,同时 DMA 继续搬后半段,逻辑上"数据搬运"和"数据处理"并行进行。

双缓冲(Ping-Pong Buffering)是 HT 的经典应用。假设 1024 字节缓冲区:

  • 前半 512 字节填满 → 触发 HT 中断 → CPU 处理前半段,DMA 继续搬后半段;
  • 后半 512 字节填满 → 触发 TC 中断 → 循环往复,像"接力赛"。

典型场景:音频播放。如果"读完再播放、再读再放",会卡顿;用 HT 中断让"播放音频"和"拷贝音频"同时进行,就能解决卡顿。


📌 本知识点一句话总结

DMA 通过中断通知 CPU 传输状态,在 CCR 寄存器里有三个中断使能位:TCIE(传输完成,最常用,通知 CPU 搬完可处理)、HTIE(半传输,搬满一半时触发)、TEIE(传输错误,捕获异常防卡死)。"半传输中断"的核心价值是实现搬运与处理的并行:先搬一半触发 HT,CPU 处理前半段时 DMA 继续搬后半段,即双缓冲(Ping-Pong)机制,典型用于音频播放等对实时性要求高的场景,消除"等全部搬完再处理"的等待时间。


11. 区分中断类型:ISR 与 IFCR

11.1 问题:一个通道就一根中断线,怎么区分是哪类中断

一个通道只有一根中断线连到 NVIC,最多传递"有中断了"这个信号,无法传递"是传输完成、传输一半、还是出错"这么多信息。

11.2 解决方案:DMA_ISR 与 DMA_IFCR


为了区分 DMA1 中 7 个通道、每个通道的不同中断类型,DMA 内部提供了两个中断管理寄存器(就是前面说的那"2"个寄存器)。

每个通道的中断情况由4 个标志位表示:

  • GIFx:是否产生了上述任意事件(全局中断标志);
  • TCIFx:是否发生了传输完成;
  • HTIFx:是否发生了半传输完成;
  • TEIFx:是否发生了传输错误。

(x 表示通道号,1~7)

关键机制:

  1. 一旦特定通道产生中断,DMA 硬件除了向 CPU 发中断,还会自动设置DMA_ISR寄存器中对应的位。
    • 例如通道 5 发生 TC:TCIF5=1, HTIF5=0, TEIF5=0, GIF5=1。
  2. CPU 收到中断后,回头读DMA_ISR寄存器,就能明确是哪个通道、发生了什么中断,再做相应处理。至此 DMA 逻辑闭环。

标志位清除:这些中断标志位是只读的,不能直接写 0 清除。要清除,必须写DMA_IFCR寄存器的对应位(写 1 清零)。

比喻:你把村里的小朋友打了,小朋友不直接找你(打不过),而是去找你妈,让你妈收拾你。这里的"ISR 只读、IFCR 负责清除"就是类似的"间接操作"逻辑。

这些寄存器的设置和清除,后续有 HAL 库自动完成,我们读源码时再回来看文档即可。


📌 本知识点一句话总结

因为一个通道只有一根中断线,无法区分中断类型,DMA 用DMA_ISR(中断状态寄存器)和DMA_IFCR(中断标志清除寄存器)来解决。每个通道有 4 个标志:GIF(任意事件)、TCIF(传输完成)、HTIF(半传输)、TEIF(传输错误)。硬件触发中断时会自动置位 ISR 对应位,CPU 在中断里读 ISR 即可判断是哪个通道、哪类中断。ISR 只读,清除标志要通过写 IFCR 的对应位完成。这两个寄存器就是 DMA1 那"2 个"中断管理寄存器。


12. 配置案例

12.1 案例 1:USART1 用 DMA 接收数据的配置

一个使用 USART1 的 DMA 模式进行接收的配置示例:

  1. 寄存器地址0x40013804:就是 USART1 的数据寄存器(DR)地址。
  2. 数据宽度 8 位:因为串口有效数据是 8 位,收发端宽度都配 8 位。
  3. 外设端不自增:因为只有一个数据寄存器,外设地址固定。
  4. 存储器端自增:因为要连续保存串口数据,内存地址要自增。

12.2 案例 2:按图说话

需求:

  1. 传输方向:外设 → 存储器(DIR=0)。
  2. 外设只有一个 8 位数据寄存器,地址0x40013804,记作REG(0x40013804)。
  3. 内存缓冲区 256 字节,起始地址0x200000e0,记作BUF(0x200000e0)。
  4. 数据宽度 8 位,外设地址不自增,一次 DMA 传输读 256 字节,内存地址自增。

一次 DMA 传输过程:

  1. CPU 启动 DMA 数据传输。
  2. 外设寄存器数据非空,外设向 DMA 发出请求。
  3. DMA 应答,启动传输:REG(0x40013804) → BUF(0x200000e0)。
  4. 传输完成,寄存器"数据非空"事件自动清除。
  5. 外设又来数据(非空),再次请求。
  6. DMA 应答,传输:REG(0x40013804) → BUF(0x200000e0+1)。
  7. 传输完成,事件自动清除。
  8. ……(以此类推)
  9. 外设再来数据,请求。
  10. DMA 应答,传输:REG(0x40013804) → BUF(0x200000e0+255)。
  11. 传输完成,事件自动清除。
  12. 256 字节搬完,产生传输完成(TC)中断。

这个案例完整串起了前面所有知识点:外设地址固定(PINC=0)、内存地址自增(MINC=1)、每搬一个字节外设请求一次、最后产生 TC 中断。


📌 本知识点一句话总结

配置一个串口 DMA 接收,核心是:外设地址填数据寄存器地址(如 USART1 的0x40013804)且不自增,内存地址填缓冲区首地址(如0x200000e0)且自增,方向 DIR=0(外设→内存),宽度按外设数据位宽(串口 8 位)。工作过程是"外设每来一个数据就请求一次,DMA 搬一个字节并让内存地址 +1",搬满预设数量(如 256 字节)后产生 TC 中断。整个过程 CPU 不参与搬运,只在最后的中断里介入处理。


全文总结(速记卡)

  1. DMA 是什么:直接存储器访问,独立硬件控制器 + 总线主设备,代替 CPU 自动搬运数据。
  2. 为什么用:解放 CPU,把重复的搬运工作交给 DMA,CPU 专注业务逻辑。
  3. 四方关系:CPU(启动+收尾)、外设(发请求)、DMA(控制搬运)、内存(源/目的)。
  4. 工作流程:CPU 启动 → 外设请求 → DMA 握手搬运 → 中断通知 CPU。
  5. 总线共享:CPU 和 DMA 共享 AHB 总线,靠总线仲裁器循环调度,各占一半带宽,逻辑上互不影响。
  6. 通道与仲裁:多通道分时复用总线,仲裁器按"软件优先级(4 级)→硬件优先级(编号小优先)"仲裁;外设↔通道是硬映射(USART1_TX→通道4、RX→通道5),且排他。
  7. 寄存器:DMA1 基地址0x40020000,30 个寄存器;每通道 4 个(CCR/CNDTR/CPAR/CMAR)。
  8. CCR 关键位:EN(使能)、PL(优先级)、DIR(方向)、PSIZE/MSIZE(宽度)、PINC/MINC(自增)、CIRC(循环)、TCIE/HTIE/TEIE(中断)。
  9. 中断:TC(完成)、HT(半传输,双缓冲并行)、TE(错误);用 ISR 读状态、IFCR 清标志。
  10. 使用流程:配置外设 DMA 使能位 → 配置通道 4 寄存器 → 使能通道 → 中断收尾。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询