stm32h747 DMA
DMA 提供高速、无 CPU 干预的数据搬运能力。
双 AHB 总线架构 + FIFO 优化,两个控制器(DMA1、DMA2)各有 8 个 stream。 每个 stream 通过 DMAMUX 灵活选择请求源,仲裁器负责优先级调度。
1 DMA 主要特性
架构:
- 双 AHB 主总线架构:一条专用于内存访问,一条专用于外设访问。由于需要实现内存间数据传输,因此AHB 外设端口还需具备访问内存的权限。
- AHB 从接口:仅支持 32 位访问。
通道与请求:
- 每个 DMA 控制器有 8 个 stream。
- 每个 stream 最多支持 115 个请求源(通过 DMAMUX1 配置),可服务 107 个外设。
FIFO 与传输模式:
- 每个 stream 有一个 4-word 深度的 32 位 FIFO。
- FIFO 模式:可选择阈值 (1/4, 1/2, 3/4)。
- 直接模式:每个请求立即触发一次传输;在 memory-to-peripheral 模式下,DMA 会预加载一个数据到内部 FIFO,保证外设请求到来时立即传输。
通道配置:
- 普通通道:支持外设→内存、内存→外设、内存→内存传输。
- 双缓冲通道:支持内存侧双缓冲,提高连续数据流处理能力(使用两个内存指针,DMA 在一个缓冲区工作时,应用程序可操作另一个缓冲区。)。
优先级:
- 软件可编程优先级:四级(非常高、高、中、低)。
- 硬件仲裁:当优先级相同,按请求号排序(如请求0优先于请求1)。
触发方式:
- 每个 stream 支持 软件触发(用于内存到内存传输)。
- 请求源由 DMAMUX1 软件配置。
流控模式:
- DMA流控:软件在 NDTR 中设定传输数量 (1~65535)。
- 外设流控:数据量未知,由外设在最后一次传输时发出结束信号(需要外设支持发送结束信号)。
数据宽度与打包:
- 源和目的数据宽度可独立设置(byte/half-word/word)。
- 当宽度不一致时,DMA 自动打包/解包以优化带宽(仅 FIFO 模式支持)。
地址模式:
- 源和目的地址可选择 递增或固定。
Burst 传输:
- 支持 4、8、16 beat 的 burst传输。
- burst 大小可软件配置,通常等于外设 FIFO 的一半。
缓冲与事件:
- 每个 stream 支持 循环缓冲。
- 提供 5 个事件标志:半传输完成、传输完成、传输错误、FIFO 错误、直接模式错误。
- 这些标志通过逻辑 OR 合并成一个中断请求。
2 DMA functional description

- dma_hclk:Digital input, DMA AHB clock
- dma_it[0:7]:Digital outputs, DMA stream [0:7] global interrupts
- dma_tcif[0:7]:Digital outputs, MDMA triggers
- dma_str[0:7]:Digital input, DMA stream [0:7] requests
2.1 DMA 事务 (Transactions)
事务定义:一个 DMA 事务由一系列数据传输组成。
数据项数量与宽度:可由软件配置(8/16/32 位)。
每次传输包含三步:
- 从外设数据寄存器或内存地址加载数据(通过 DMA_SxPAR 或 DMA_SxM0AR)。
- 将数据存储到目标外设寄存器或内存地址。
- DMA_SxNDTR 递减,表示剩余传输数减少。
握手过程:
- 外设发出请求信号 → DMA 根据优先级仲裁 → DMA 发出 Acknowledge → 外设释放请求 → DMA 释放 Acknowledge。
- 外设可继续发起下一次请求。
2.2 源、目的与传输模式
源和目的地址可覆盖整个 4GB 地址空间 (0x0000_0000 ~ 0xFFFF_FFFF)。
方向配置:通过 DMA_SxCR.DIR[1:0] 位:
- 00 外设→内存:源=PAR,目的=M0AR。
- 01 内存→外设:源=M0AR,目的=PAR。
- 10 内存→内存:源=PAR,目的=M0AR。
- 11 保留。
地址对齐要求:
- 半字传输 → 地址必须半字对齐。
- 字传输 → 地址必须字对齐。
2.3 数据传输
外设→内存模式:
- EN=1 后,每次外设请求触发, DMA 将数据搬入 FIFO。
- FIFO 达到阈值后,批量写入内存。
- 停止条件:NDTR=0、外设结束信号、或软件清除 EN。
- 直接模式 (DMDIS=0):不使用阈值,每次请求立即搬运数据到内存。
- 仲裁:DMA 只有在赢得仲裁后才能访问总线。
内存→外设模式:
- EN=1 后,DMA 立即填满 FIFO。
- 外设请求到来时,FIFO 数据被写入外设。
- 当 FIFO 低于阈值时,DMA 再次填充。
- 停止条件同上。
- 直接模式:DMA 预加载一个数据到内部 FIFO,外设请求时立即传输,再加载下一个。
内存→内存模式:
- 不依赖外设请求,EN=1 后立即开始传输。
- FIFO 达到阈值后批量写入目的地址。
- 停止条件:NDTR=0 或 EN=0。
- 限制:此模式下不能使用循环模式和直接模式。
2.4 地址递增 (Pointer Incrementation)
PINC/MINC 控制源/目的地址是否递增。
- 禁用递增:适合访问固定寄存器。
- 启用递增:地址按数据宽度递增(byte=+1,half-word=+2,word=+4)。
PINCOS 位:控制外设地址递增方式:
- 默认:按数据宽度递增。
- PINCOS=1:强制按 32 位递增(+4),无论 PSIZE 设置。
- 注意:只影响外设端,不影响内存端。
2.5 循环模式 (Circular Mode)
作用:用于处理循环缓冲区和连续数据流(如 ADC 扫描模式)。
启用方式:设置 DMA_SxCR.CIRC=1。
行为:当传输完成后,NDTR 会自动重新加载为初始值,DMA 请求继续被服务,实现无限循环搬运。
注意,当内存端配置为突发模式时:
- 必须满足公式:DMA_SxNDTR = Multiple of ((Mburst beat) × (Msize)/(Psize))
- Mburst beat = 4, 8, 16 (由 MBURST 配置)
- Msize/Psize = 1, 2, 4, 1/2, 1/4
举例:MBURST=8,MSIZE=byte,PSIZE=half-word → NDTR 必须是 4 的倍数。
如果不满足公式,DMA 行为和数据完整性无法保证。
同时 NDTR 也必须是 外设突发大小 × 外设数据宽度 的倍数。
2.6 双缓冲模式 (Double Buffer Mode)
启用方式:设置 DMA_SxCR.DBM=1。
行为:
- 有两个内存指针 (M0AR 和 M1AR)。
- 每次事务结束后,DMA 自动切换到另一个缓冲区。
- 软件可以在一个缓冲区处理数据,同时 DMA 在另一个缓冲区继续搬运。
- 自动启用循环模式(DMA_SxCR中的CIRC位无关紧要)。
方向支持:
- 外设→内存:源=PAR,目的=M0AR/M1AR。
- 内存→外设:源=M0AR/M1AR,目的=PAR。
- 内存→内存:不允许(因为双缓冲强制循环,而内存到内存不支持循环)。
动态更新地址的规则:
- 当 CT=0 (in DMA_SxCR) → 可以写 M1AR。
- 当 CT=1 → 可以写 M0AR。
- 如果违反规则 → 设置 TEIF 错误并自动关闭 DMA。
- 建议在 TCIF=1(传输完成标志置位) 时更新地址,因为此时 DMA 已经切换到另一缓冲区。
2.7 可编程数据宽度、打包/解包、大小端
NDTR:必须在使能前配置好(除非外设流控)。
PSIZE/MSIZE:可分别设置源和目的数据宽度(8/16/32 位)。
PSIZE≠MSIZE 时:
- NDTR 的单位由 PSIZE 决定。
- DMA 自动进行打包/解包,但只支持 小端序。
若在数据完全打包/解包前中断该操作,此打包/解包流程可能导致数据损坏风险。为确保数据一致性,可配置数据流生成突发传输:此时,属于同一突发的传输组不可分割(不会被高优先级传输打断)。
Direct Mode (DMDIS=0):不支持打包/解包,必须保证 PSIZE=MSIZE。
约束:为了避免最后一次传输不完整,NDTR 必须满足以下条件:
- PSIZE=8bit,MSIZE=16bit → NDTR 必须是 2 的倍数。
- PSIZE=8bit,MSIZE=32bit → NDTR 必须是 4 的倍数。
- PSIZE=16bit,MSIZE=32bit → NDTR 必须是 2 的倍数。
2.8 单次传输与突发传输 (Single vs Burst Transfers)
单次传输:每个 DMA 请求搬运一个数据项(byte/half-word/word)。
突发传输:每个 DMA 请求搬运一组数据(4、8 或 16 beat)。
配置:
- 外设端突发大小由 PBURST + PSIZE 决定。
- 内存端突发大小由 MBURST + MSIZE 决定。
- 备注:burst size 以psize or msize为单位
特性:
- 突发传输是不可分割的,保证数据一致性。
- 仲裁器在突发期间不会打断 DMA。
Direct Mode:只能做单次传输。
地址对齐要求:突发传输必须对齐到数据宽度边界。
AHB 协议约束:突发传输不能跨越 1KB 边界,否则会产生 AHB 错误(DMA 寄存器不会报告)。
3 DMA FIFO 机制
3.1 FIFO 结构
- 每个 DMA stream 都有一个独立的 4-word 深度 FIFO。
- FIFO 用来暂存源数据,再传输到目的地。
- 阈值 (Threshold) 可配置为 1/4、1/2、3/4 或满。
- 要启用阈值功能,必须关闭直通模式 (DMDIS=1)。
3.2 FIFO 阈值与突发配置
- 约束:FIFO 阈值必须与内存突发传输数据量整数匹配,否则会报错 (FEIF)。限制条件:突发大小(MBURST) × 数据宽度(MSIZE) ≤ FIFO 阈值大小。
不完整突发情况:
- 如果 NDTR 不是突发大小 × 数据宽度的倍数 → 最后剩余数据会用单次传输完成。
- 如果 FIFO 剩余数据不是突发大小 × 数据宽度的倍数 → 也会退化为单次传输。
特别规则:
- 若(PBURST × PSIZE)=FIFO_SIZE(4个字),则当 PSIZE =1、2或4且 PBURST =4、8或16时,禁止使用FIFO_Threshold=3/4。该规则确保每次都有足够的FIFO空间可用,以响应外围设备的请求。
3.3 FIFO 刷新 (Flush)
当 stream 被禁用 (EN=0) 时,FIFO 中可能还有残留数据。
DMA 会继续把这些数据搬运到目的地,直到 FIFO 清空。
完成后置位 TCIF 标志。
注意事项:
- 如果 FIFO 剩余数据 < 内存数据宽度 (例如 FIFO 只有 2 字节,而 MSIZE=word),DMA 仍按 MSIZE 写入 → 可能导致内存写入无效数据。
- 软件可通过 NDTR 判断哪些内存区域是有效数据。
- 如果 FIFO 剩余数据 < 突发大小 → DMA 会用单次传输完成刷新。
3.4 直通模式 (Direct Mode)
默认情况下 FIFO 工作在直通模式 (DMDIS=0)。
在直通模式下,阈值功能不可用。
行为:
- 每次 DMA 请求立即触发一次传输。
- 在内存→外设模式下,DMA 会预加载一个数据到内部 FIFO,保证外设请求到来时立即传输。
限制:
- 源和目的数据宽度必须相同 (由 PSIZE 定义)。
- 不支持突发传输。
- 不允许用于内存→内存传输。
建议:为了避免 FIFO 饱和,直通模式下应给该 stream 配置较高优先级。
4 DMA 传输完成 (Transfer Completion)
DMA 的传输完成事件会置位 TCIFx 标志。不同模式下触发条件不同:
DMA 流控模式 (PFCTRL=0)
- 内存→外设:当 NDTR=0 时传输完成。
- 外设→内存 / 内存→内存:如果在传输过程中软件清除了 EN 位禁用 stream,DMA 会把 FIFO 中剩余数据搬完到内存后才算完成。
外设流控模式 (PFCTRL=1)
外设→内存:
- 外设发出最后一次请求,DMA 把 FIFO 中剩余数据搬完到内存 → 完成。
- 软件禁用 stream 时,DMA 也会把 FIFO 中剩余数据搬完到内存 → 完成。
内存→外设:完成条件仅依赖外设的最后一次请求,不涉及 FIFO。
非循环模式下
- 当 NDTR=0 → DMA 自动停止 (EN 位被硬件清零)。
- 后续不再响应请求,除非软件重新配置并重新使能。
5 DMA 传输挂起 (Suspension)
DMA 可以在传输过程中被挂起,分两种情况:
永久停止
- 软件清除 EN 位 → stream 停止。
- DMA 会先完成正在进行的传输,然后置位 TCIF。
- NDTR 保留剩余数据项数,软件可用来计算已传输的数据量。
暂停后恢复
- 软件清除 EN 位 → stream 暂停。
- 软件读取 NDTR,得知已传输数量。
- 软件更新地址寄存器 (PAR/MAR) 和 NDTR 为剩余数量。
- 重新设置 EN 位 → DMA 从暂停点继续传输。
6 流控机制 (Flow Controller)
决定传输数据量的实体称为 流控器,由 PFCTRL 位配置:
DMA 控制器作为流控器 (PFCTRL=0)
- 软件在 NDTR 中设定传输数量。
- DMA 按照设定执行,直到 NDTR=0。
外设作为流控器 (PFCTRL=1)
- 外设通过硬件信号告诉 DMA 什么时候是最后一次传输。
- NDTR 在使能时被硬件强制为 0xFFFF。
- 数据量未知,由外设决定结束。
三种结束情况:
提前停止:软件清除 EN 位 → DMA 停止并 flush FIFO。
- 已传输数量 = 0xFFFF – NDTR。
正常结束:外设发出最后一次请求 → DMA 完成并置位 TCIF。
- 已传输数量同上公式。
NDTR=0:DMA 强制结束,即使外设未发出结束信号。
- 最多支持 65535 项数据。
⚠️ 限制:
- 在 内存→内存模式 下,DMA 始终是流控器 (PFCTRL=0)。
- 在 外设流控模式 下,禁止使用循环模式。
7 DMA Stream 配置步骤
要配置一个 DMA stream(编号为 x),必须按照以下顺序进行:
1 禁用当前 stream
- 如果 stream 已经启用,先清除 DMA_SxCR.EN 位。
- 然后读取 EN 位确认 stream 已经真正停止。
- 注意:写 EN=0 并不会立即生效,硬件会等到当前传输完成后才清零。
- 当 EN=0 时,stream 才能安全重新配置。
- 同时必须清除上一次传输遗留的状态标志位 (DMA_LISR / DMA_HISR)。
2 设置外设地址
- 在 DMA_SxPAR 中写入外设端口寄存器地址。
- DMA 会从这个地址读/写数据。
3 设置内存地址
- 在 DMA_SxM0AR 中写入内存地址。
- 如果是双缓冲模式,还需要设置 DMA_SxM1AR。
4 配置传输数据量
- 在 DMA_SxNDTR 中写入要传输的数据项数量。
- 每次外设事件或突发传输都会使该值递减。
5 配置 DMAMUX
- 使用 DMAMUX1 将外设的 DMA 请求路由到对应的 DMA channel。
6 选择流控器
- 如果外设支持作为流控器,则设置 DMA_SxCR.PFCTRL=1。
- 否则由 DMA 控制传输数量。
7 配置优先级
- 使用 DMA_SxCR.PL[1:0] 设置 stream 的优先级。
8 配置 FIFO
- 决定是否启用 FIFO,以及阈值大小。
9 配置传输模式,包括:
- 数据方向 (内存↔外设/内存)
- 地址是否递增 (MINC/PINC)
- 单次或突发传输
- 外设/内存数据宽度
- 是否启用循环模式
- 是否启用双缓冲模式
- 中断配置(半传输、完成、错误)
10 启用 stream
- 设置 DMA_SxCR.EN=1。
- 一旦启用,DMA 就会响应外设的请求。
中断与标志
- 半传输 (HTIF):当一半数据传输完成时置位,如果启用了 HTIE 中断则触发。
- 传输完成 (TCIF):当全部数据传输完成时置位,如果启用了 TCIE 中断则触发。
⚠️ 警告
如果要关闭一个外设,必须 先关闭与之连接的 DMA stream,并等待 EN=0。只有在 DMA stream 完全停止后,才能安全关闭外设。
否则可能导致数据丢失或 DMA 异常。
参考
[1] STM32H7xx Reference Manual, RM0399