1. DMA控制器概述
1.1 DMA工作原理
直接存储器访问(Direct Memory Access, DMA)是一种允许外设或存储器之间直接传输数据而无需CPU干预的硬件机制。DMA控制器作为系统总线上的独立主设备,在获得总线控制权后,可以自主完成数据搬运任务。
核心思想:CPU只需初始化传输参数(源地址、目的地址、传输长度),DMA控制器即可独立完成后续所有数据传输操作,传输完成后通过中断通知CPU。
1.2 DMA在嵌入式系统中的作用
在嵌入式系统中,DMA控制器扮演着数据搬运引擎的角色,其核心价值体现在以下几个方面:
| 应用场景 | DMA作用 | 典型示例 |
|---|---|---|
| 高速数据采集 | ADC采样数据直接存入内存,不占用CPU | 音频采集、传感器数据流 |
| 显示刷新 | 帧缓冲区数据自动传输到显示控制器 | LCD/OLED屏幕刷新 |
| 存储设备访问 | SD卡/eMMC数据块搬运 | 文件系统读写 |
| 网络数据包处理 | 以太网MAC与内存之间的数据帧传输 | TCP/IP协议栈 |
| 存储器间拷贝 | 内存到内存的大块数据复制 | 图像处理、数据缓冲 |
关键指标:DMA传输速率通常以字节/秒或传输次数/秒衡量。在嵌入式Linux系统中,DMA带宽可达数百MB/s至数GB/s,具体取决于总线架构(如AXI、AHB、PCIe)和DMA引擎设计。
1.3 DMA与CPU的数据搬运对比
理解DMA与CPU在数据搬运上的差异,是驱动开发的基础。以下从多个维度进行对比:
| 对比维度 | CPU搬运 | DMA搬运 |
|---|---|---|
| 执行主体 | CPU核心(如ARM Cortex-A7) | DMA控制器硬件状态机 |
| 并行能力 | 搬运期间CPU无法执行其他任务 | CPU可并行执行计算任务 |
| 传输速率 | 受限于CPU指令执行速度 | 接近总线理论带宽 |
| 适用场景 | 小数据量(< 64字节)、频繁随机访问 | 大数据块(≥ 64字节)、流式传输 |
| 编程复杂度 | 简单,直接使用memcpy/循环 | 需配置描述符、处理中断、管理缓冲区 |
| 功耗 | 较高(CPU持续活跃) | 较低(专用硬件,CPU可休眠) |
1.4 DMA传输的基本要素
一个完整的DMA传输涉及以下核心要素:
- 传输方向:内存→外设(写)、外设→内存(读)、内存→内存(拷贝)
- 源地址与目的地址:物理地址,支持递增/固定模式
- 传输宽度:字节(8-bit)、半字(16-bit)、字(32-bit)或双字(64-bit)
- 传输计数:一次DMA请求传输的数据单元数量
- 触发方式:软件触发、硬件外设请求触发、链式描述符触发
- 中断条件:传输完成、传输错误、半传输完成等
驱动开发注意:DMA操作涉及物理地址,用户空间虚拟地址必须经过内核API(如dma_map_single)转换为总线地址后才能使用。错误的地址映射会导致数据损坏或系统崩溃。
1.5 典型DMA控制器架构
以ARM PrimeCell DMA控制器(PL080/PL081)为例,其内部结构包含以下关键模块:
各模块功能说明:
- 总线接口:负责与系统总线(AHB/AXI)交互,发起读写传输
- 控制逻辑:解析描述符、管理传输状态、处理错误
- 通道仲裁器:多通道并发时决定优先级,支持轮询或固定优先级
- 中断控制:根据传输状态产生中断信号给中断控制器(GIC)
- 描述符通道:每个通道维护一个描述符链表,支持链式传输
1.6 描述符接口简介
现代DMA控制器普遍采用描述符(Descriptor)接口,替代传统的寄存器直接编程方式。描述符是一个内存中的数据结构,包含传输所需的所有参数:
struct dma_descriptor {
uint32_t src_addr; // 源物理地址
uint32_t dst_addr; // 目的物理地址
uint32_t transfer_size; // 传输字节数
uint32_t ctrl; // 控制标志(中断使能、传输类型等)
uint32_t next; // 下一个描述符指针(链表)
};
描述符接口优势:
- 支持链式传输:多个描述符组成链表,DMA自动依次执行
- 支持循环缓冲:描述符链表首尾相连,适用于连续数据流
- 支持分散-聚集:不连续的内存块可以合并传输
- 减少CPU干预:一次配置可完成大量传输任务