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)。
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位时,需要合理设置传输计数单位。
4.3 源地址寄存器(CHx_SAR)与目的地址寄存器(CHx_DAR)
源地址寄存器和目的地址寄存器分别存储DMA传输的源端和目的端起始地址。这两个寄存器在传输过程中会根据地址递增模式自动更新。
- 源地址寄存器(CHx_SAR): 存放数据源地址。若为内存到外设传输,源地址通常指向内存缓冲区;若为外设到内存传输,源地址通常指向外设数据寄存器地址。
- 目的地址寄存器(CHx_DAR): 存放数据目的地址。若为内存到外设传输,目的地址指向外设数据寄存器;若为外设到内存传输,目的地址指向内存缓冲区。
编程要点: 在配置地址寄存器时,必须确保地址对齐到数据宽度边界。例如,若数据宽度为32位,则源地址和目的地址必须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未按预期变化,通常表明地址递增模式或数据宽度配置有误。
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)清除对应标志位,最后执行数据处理逻辑。顺序错误可能导致中断丢失或重复触发。
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