4. DMA控制器硬件描述

DMA(Direct Memory Access)控制器是硬件加速数据传输的核心组件,它允许外设或内存之间直接交换数据,无需CPU逐字节干预。理解DMA控制器的寄存器布局与各寄存器的功能,是编写高效DMA驱动的基础。本章将详细剖析DMA控制器的硬件寄存器结构,包括通道控制、地址管理、传输计数及中断状态等关键寄存器。

核心概念: DMA控制器内部通常包含多个独立的DMA通道,每个通道对应一组寄存器,用于配置传输方向、数据宽度、地址递增模式及传输长度。CPU通过编程这些寄存器来启动和控制DMA传输。

4.1 DMA控制器寄存器布局概览

一个典型的DMA控制器寄存器布局遵循“全局控制 + 通道独立寄存器”的架构。全局寄存器负责DMA控制器的使能、总线优先级仲裁及公共中断控制;每个通道则拥有自己独立的寄存器组,用于配置该通道的传输参数。

下表列出了DMA控制器中常见的寄存器分类及其功能描述:

寄存器类别 寄存器名称 功能描述
全局控制 DMA_CR (Control Register) 全局使能、总线优先级、通道仲裁模式
全局状态 DMA_SR (Status Register) 各通道传输完成/错误标志位
通道控制 CHx_CR (Channel x Control Register) 通道使能、传输方向、数据宽度、地址递增模式
源地址 CHx_SAR (Source Address Register) 源数据起始地址(内存或外设地址)
目的地址 CHx_DAR (Destination Address Register) 目的数据起始地址(内存或外设地址)
传输计数 CHx_NDTR (Number of Data Transfer Register) 待传输的数据项数量(通常以字节、半字或字为单位)
中断状态 CHx_ISR (Interrupt Status Register) 传输完成、半传输、传输错误等中断标志
设计提示: 在硬件抽象层(HAL)设计中,通常将每个通道的寄存器基址定义为宏或结构体指针,便于驱动代码通过偏移量快速访问。例如:#define DMA_CH0_BASE (DMA_BASE + 0x10)。
DMA控制器寄存器布局架构图 全局寄存器 DMA_CR (控制) DMA_SR (状态) DMA_IFCR (中断清除) 通道0寄存器组 CH0_CR (通道控制) CH0_SAR (源地址) CH0_DAR (目的地址) CH0_NDTR (传输计数) CH0_ISR (中断状态) 通道1寄存器组 CH1_CR (通道控制) CH1_SAR (源地址) CH1_DAR (目的地址) CH1_NDTR (传输计数) CH1_ISR (中断状态) 说明:每个通道拥有独立的寄存器组,支持并行配置与独立传输。全局寄存器控制所有通道的公共行为。

4.2 通道控制寄存器(CHx_CR)

通道控制寄存器是每个DMA通道的核心配置寄存器,它决定了该通道的传输行为。驱动开发者需要根据具体场景设置以下关键位域:

  • EN (Enable): 通道使能位。置1启动DMA传输,传输完成后硬件自动清零。
  • DIR (Direction): 传输方向。0表示从外设到内存(读),1表示从内存到外设(写)。
  • MSIZE / PSIZE (Memory/Peripheral Size): 数据宽度。可选字节(8-bit)、半字(16-bit)、字(32-bit)。
  • MINC / PINC (Memory/Peripheral Increment): 地址递增模式。置1表示每次传输后地址自动增加数据宽度。
  • CIRC (Circular Mode): 循环模式。置1使能循环传输,适用于连续数据流场景(如音频ADC)。
  • TCIE / HTIE / TEIE (Interrupt Enable): 传输完成/半传输/传输错误中断使能。
常见陷阱: 在配置MSIZE和PSIZE时,必须确保源和目的的数据宽度匹配,否则可能导致数据错位或总线错误。例如,外设数据宽度为8位,而内存数据宽度为32位时,需要合理设置传输计数单位。
通道控制寄存器 (CHx_CR) 位域结构 31:24 保留 | 23:20 保留 | 19:16 保留 | 15:14 MSIZE | 13:12 PSIZE | 11:10 保留 | 9 MINC | 8 PINC | 7 CIRC | 6 DIR | 5:4 保留 | 3 TEIE | 2 HTIE | 1 TCIE | 0 EN EN (位0) DIR (位6) MSIZE (位15:14) PSIZE (位13:12) MINC/PINC (位9:8) CIRC (位7) TCIE/HTIE/TEIE (位3:1) 说明:位域布局因芯片厂商而异,上图以STM32 DMA控制器为参考模型。实际开发时请查阅对应芯片参考手册。

4.3 源地址寄存器(CHx_SAR)与目的地址寄存器(CHx_DAR)

源地址寄存器和目的地址寄存器分别存储DMA传输的源端和目的端起始地址。这两个寄存器在传输过程中会根据地址递增模式自动更新。

  • 源地址寄存器(CHx_SAR): 存放数据源地址。若为内存到外设传输,源地址通常指向内存缓冲区;若为外设到内存传输,源地址通常指向外设数据寄存器地址。
  • 目的地址寄存器(CHx_DAR): 存放数据目的地址。若为内存到外设传输,目的地址指向外设数据寄存器;若为外设到内存传输,目的地址指向内存缓冲区。
编程要点: 在配置地址寄存器时,必须确保地址对齐到数据宽度边界。例如,若数据宽度为32位,则源地址和目的地址必须4字节对齐。非对齐访问可能导致硬件异常或性能下降。
源地址/目的地址寄存器与传输方向 内存缓冲区 地址: 0x2000_0000 外设数据寄存器 地址: 0x4000_0000 内存→外设 (DIR=1) SAR=0x2000_0000, DAR=0x4000_0000 外设→内存 (DIR=0) SAR=0x4000_0000, DAR=0x2000_0000 说明:源地址和目的地址寄存器在传输过程中自动递增(若使能递增模式)。 递增步长由数据宽度决定:字节+1,半字+2,字+4。

4.4 传输计数寄存器(CHx_NDTR)

传输计数寄存器(NDTR,Number of Data Transfer Register)记录了当前DMA通道还需要传输的数据项数量。该寄存器在每次传输完成后自动递减,当减至0时触发传输完成事件。

  • 初始值: 在启动DMA传输前,需要将待传输的数据项总数写入NDTR。数据项的单位由数据宽度决定(字节、半字或字)。
  • 自动递减: 每次成功传输一个数据项后,硬件自动将NDTR减1。递减过程对CPU透明,无需软件干预。
  • 读取当前值: 在传输过程中,CPU可以随时读取NDTR的值,以了解剩余传输进度。这在实现进度条或超时检测时非常有用。
  • 循环模式下的行为: 若使能循环模式(CIRC=1),NDTR减至0后会自动重载初始值,实现连续传输。
调试技巧: 在驱动开发初期,可以在每次DMA传输完成后打印NDTR的值,验证传输计数是否正确递减。若NDTR未按预期变化,通常表明地址递增模式或数据宽度配置有误。
传输计数寄存器 (CHx_NDTR) 工作流程 初始状态 NDTR = N (总项数) 传输进行中 NDTR = N - k (剩余项数) 传输完成 NDTR = 0 循环模式 (CIRC=1):自动重载 NDTR = N 说明:NDTR寄存器在每次传输后自动递减,递减步长固定为1(数据项计数)。 实际传输的字节数 = NDTR初始值 × 数据宽度(字节/半字/字)。 例如:NDTR=100,数据宽度=32位(4字节),则总传输字节数=400字节。

4.5 中断状态寄存器(CHx_ISR)

中断状态寄存器用于指示DMA通道的各类事件状态。当特定事件发生时,硬件将对应的标志位置1。驱动开发者可以通过查询该寄存器或使能中断来响应这些事件。

常见的中断标志位包括:

标志位 名称 触发条件 典型用途
TCIF (Transfer Complete) 传输完成标志 NDTR减至0,所有数据项传输完毕 通知CPU数据已就绪,可进行后续处理
HTIF (Half Transfer) 半传输标志 NDTR减至初始值的一半 双缓冲切换、流水线处理
TEIF (Transfer Error) 传输错误标志 总线错误、地址对齐错误、FIFO溢出等 错误恢复、重试机制
DMEIF (Direct Mode Error) 直接模式错误标志 直接模式下FIFO下溢/上溢 调试与异常处理
中断处理注意事项: 在中断服务函数(ISR)中,必须先读取中断状态寄存器确认中断源,然后通过写中断清除寄存器(CHx_IFCR)清除对应标志位,最后执行数据处理逻辑。顺序错误可能导致中断丢失或重复触发。
中断状态寄存器与中断处理流程 CHx_ISR 寄存器 位3: TEIF (传输错误) 位2: HTIF (半传输) 位1: TCIF (传输完成) 位0: 保留 步骤1: 读取ISR 识别中断源 步骤2: 清除标志 写IFCR对应位 步骤3: 数据处理 拷贝/处理数据 说明:中断标志位必须通过写中断清除寄存器(CHx_IFCR)的对应位来清零, 直接写ISR寄存器无效。清除操作不会影响其他通道的状态。

4.6 寄存器操作代码示例

以下代码展示了如何在Linux内核驱动中操作DMA控制器的寄存器。该示例基于内存映射I/O(MMIO)方式,通过ioremap获取寄存器虚拟地址,并使用readl/writel进行32位寄存器读写。

/* DMA控制器寄存器基址结构体定义 */
struct dma_regs {
    volatile uint32_t cr;      /* 控制寄存器,偏移0x00 */
    volatile uint32_t sr;      /* 状态寄存器,偏移0x