4. DMA控制器基础:DMA工作原理、传输类型与通道请求

直接存储器访问(Direct Memory Access, DMA)是嵌入式系统中实现高速数据传输的关键硬件机制。在SPI等外设驱动中,DMA能够在不占用CPU的情况下完成大量数据的搬移,是提升系统吞吐量的核心手段。本节将深入剖析DMA控制器的工作原理、三种传输类型以及通道与请求的映射关系。

4.1 DMA工作原理

DMA控制器本质上是一个专用的硬件状态机,能够在系统总线(如AHB/AXI)上扮演总线主控角色,直接控制存储器与外设之间的数据流动。其核心工作流程如下:

  1. CPU配置DMA:软件设置源地址、目的地址、传输宽度、传输计数、触发源等参数。
  2. 外设发起请求:当SPI接收FIFO非空或发送FIFO空时,向DMA控制器发送硬件请求信号。
  3. DMA仲裁与响应:DMA控制器根据优先级仲裁,获取总线控制权,向外设返回应答信号。
  4. 数据传输:DMA在源与目的之间执行单次或突发传输,每次传输后更新地址和计数。
  5. 传输完成:当计数归零时,DMA产生完成中断,通知CPU数据已就绪。
关键概念:DMA传输的最小单位是节拍(Beat),通常为1、2、4字节。多个节拍组成突发(Burst),突发长度由硬件设计决定,常见为4、8、16节拍。
DMA控制器工作流程 CPU配置DMA 外设发起请求 DMA仲裁与响应 数据传输执行 传输完成中断 循环传输(自动重载) DMA传输期间CPU可执行其他任务,仅在配置和完成中断时被短暂占用

4.2 DMA传输类型

根据数据流动的方向和参与方,DMA传输分为三种基本类型。在SPI驱动联调中,理解这三种类型是正确配置DMA通道的前提。

传输类型 源地址 目的地址 典型应用场景 SPI联调示例
内存→内存 内存缓冲区A 内存缓冲区B 数据拷贝、内存搬运、帧缓冲处理 SPI接收数据从临时buf搬移到应用buf
内存→外设 内存缓冲区 外设数据寄存器 外设发送数据(如SPI发送、UART发送) SPI发送:DMA将tx_buf数据写入SPI_DR
外设→内存 外设数据寄存器 内存缓冲区 外设接收数据(如SPI接收、ADC采集) SPI接收:DMA将SPI_DR数据读入rx_buf
实战要点:在SPI驱动中,内存→外设和外设→内存是最常用的两种类型。全双工SPI传输需要同时配置两个DMA通道(一个发送、一个接收),且必须确保两个通道的同步启动。
三种DMA传输类型对比 类型1:内存→内存 源内存 目的内存 CPU配置后自动搬运 类型2:内存→外设 内存 外设寄存器 外设请求触发传输 类型3:外设→内存 外设寄存器 内存 外设数据到达即触发 传输类型关键差异 触发方式:内存→内存由软件启动;内存→外设/外设→内存由硬件请求触发 地址模式:内存→内存源/目的地址均递增;外设端地址固定(外设寄存器地址不变) 数据宽度:外设端宽度必须与外设寄存器宽度匹配(如SPI_DR为16位则必须配置半字传输) 流控方式:内存→内存由DMA流控;外设相关传输由外设流控(外设决定何时传输) 中断时机:内存→内存传输完成中断;外设相关传输可配置半传输/完成/错误中断

4.3 DMA通道与请求

DMA控制器内部包含多个独立的通道(Channel),每个通道可以独立配置源地址、目的地址、传输参数和触发源。通道与硬件请求之间的映射关系是驱动配置的核心。

4.3.1 通道结构

每个DMA通道通常包含以下寄存器组:

  • 源地址寄存器(SAR):存放数据源地址,支持递增/递减/固定模式。
  • 目的地址寄存器(DAR):存放数据目的地址,支持递增/递减/固定模式。
  • 控制寄存器(CR):配置传输宽度、突发长度、优先级、中断使能等。
  • 传输计数寄存器(NDTR):记录剩余传输节拍数,每传输一次自动减1。
  • 请求选择寄存器(RSR):选择该通道绑定的硬件外设请求信号。

4.3.2 请求映射机制

在典型SoC中,DMA控制器通过请求多路选择器(Request MUX)将多个外设的请求信号映射到有限的DMA通道上。映射关系通常在芯片参考手册中以表格形式给出。

DMA通道 默认外设请求 可选外设请求 SPI典型映射
CH0 SPI1_TX UART1_RX, I2C1_TX SPI1发送通道
CH1 SPI1_RX UART1_TX, I2C1_RX SPI1接收通道
CH2 SPI2_TX UART2_RX, TIM1_UP SPI2发送通道
CH3 SPI2_RX UART2_TX, TIM2_UP SPI2接收通道
CH4 ADC1 SPI3_TX, UART3_RX 备用SPI发送
CH5 ADC2 SPI3_RX, UART3_TX 备用SPI接收
联调陷阱:多个外设共享同一个DMA通道时,必须确保外设请求不会同时触发。在Android HAL层,如果SPI和UART同时使用同一通道,需要在驱动中实现互斥访问或使用DMA控制器支持的通道优先级抢占机制。
DMA通道与请求映射架构 外设层 SPI1 SPI2 UART1 UART2 I2C1 ADC1 请求多路选择器(Request MUX) 根据RSR寄存器选择每个通道绑定的外设请求 DMA通道层 CH0 SPI1_TX CH1 SPI1_RX CH2 SPI2_TX CH3 SPI2_RX CH4 ADC1 CH5 ADC2 系统总线(AHB/AXI) 内存(DDR/SRAM)

4.4 通道配置实战要点

在Android HAL层配置DMA通道时,需要关注以下关键参数:

SPI DMA通道配置示例(伪代码):
/* 配置SPI发送DMA通道 */
dma_channel_config_t tx_cfg = {
    .channel_id = DMA_CH0,           /* 选择通道0 */
    .src_addr   = (uint32_t)tx_buf,  /* 内存发送缓冲区 */
    .dst_addr   = (uint32_t)&SPI1->DR, /* SPI数据寄存器地址 */
    .src_inc    = DMA_ADDR_INC,      /* 源地址递增 */
    .dst_inc    = DMA_ADDR_FIXED,    /* 目的地址固定(外设寄存器) */
    .data_width = DMA_WIDTH_16BIT,   /* SPI_DR为16位 */
    .burst_len  = DMA_BURST_4,       /* 4节拍突发 */
    .transfer_cnt = len / 2,         /* 总传输节拍数 */
    .request_src = DMA_REQ_SPI1_TX,  /* 绑定SPI1发送请求 */
    .priority   = DMA_PRIO_HIGH,
    .irq_enable = true               /* 使能传输完成中断 */
};
dma_channel_init(&tx_cfg);

4.4.1 通道优先级与仲裁

当多个DMA通道同时请求总线时,DMA控制器根据优先级进行仲裁。常见策略包括:

  • 固定优先级:通道编号越小优先级越高(CH0 > CH1 > ...)。
  • 可编程优先级:每个通道可独立配置为低/中/高/最高四级。
  • 轮询仲裁:所有通道轮流获得总线使用权,防止低优先级通道饿死。

4.4.2 通道状态监控

驱动开发中常用的通道状态寄存器包括:

状态位 含义 驱动处理方式
TCIF(传输完成) 当前通道传输计数归零 触发完成回调,启动下一批传输
HTIF(半传输) 传输计数达到一半 可用于双缓冲切换(Ping-Pong)
TEIF(传输错误) 总线错误或FIFO溢出 停止DMA,复位通道,上报错误
DMEIF(直接模式错误) FIFO模式下数据丢失 检查FIFO阈值配置
BUSY(通道忙) 通道正在传输中 等待或返回EBUSY
联调建议:在Android HAL层实现DMA驱动时,建议在dma_channel_init()之后立即读取通道状态寄存器确认配置生效,并在每次传输完成后检查TEIF位。很多SPI通信异常的根本原因是DMA通道配置错误或请求映射冲突。