2. DMA基础:DMA控制器工作原理、DMA传输模式与请求响应
DMA(Direct Memory Access,直接存储器访问)是嵌入式系统中实现高速数据传输的核心技术。在UART驱动开发中,DMA能够在不占用CPU的情况下完成串口数据的批量收发,显著降低CPU负载并提升吞吐量。本节将深入剖析DMA控制器的工作原理、三种主流传输模式以及DMA请求与响应的完整流程。
学习目标:理解DMA控制器内部架构与工作流程;掌握单次、循环、散聚三种传输模式的适用场景与配置差异;理清DMA请求从外设到控制器的信号路径与响应机制。
2.1 DMA控制器工作原理
DMA控制器是独立于CPU的总线主控设备,负责在外设与存储器之间、或存储器与存储器之间搬运数据。其核心组件包括:
- 通道(Channel):每个DMA控制器包含多个独立通道,每个通道可服务于一个外设请求。
- 源地址寄存器(SAR):存储数据源地址,支持递增、递减或固定模式。
- 目的地址寄存器(DAR):存储数据目的地址,同样支持地址模式配置。
- 传输计数寄存器(CTR):记录剩余待传输的数据量,每完成一次传输自动递减。
- 控制寄存器(CR):配置传输方向、数据宽度、突发大小、中断使能等参数。
- 状态寄存器(SR):反映通道忙闲状态、传输完成标志、错误标志等。
关键概念:DMA控制器作为总线主控,在传输期间会占用系统总线。当DMA与CPU同时请求总线时,由总线仲裁器决定优先级。在UART应用中,通常将DMA优先级设置为低于CPU,避免影响关键中断响应。
下图展示了DMA控制器内部架构及其与系统总线的连接关系:
2.2 DMA传输模式
根据应用场景的不同,DMA控制器支持三种基本传输模式。下表对比了各模式的核心差异:
| 传输模式 | 传输终止条件 | 地址更新方式 | 中断触发时机 | 典型UART应用 |
|---|---|---|---|---|
| 单次传输(Single/Basic) | 传输计数CTR减至0 | 源/目的地址按数据宽度递增 | 传输完成中断(单次) | 发送固定长度数据帧 |
| 循环传输(Circular) | 永不停止,CTR溢出后自动重载 | 地址回绕至缓冲区起始 | 半传输完成 + 传输完成(循环) | 连续数据流接收(如GPS模块) |
| 散聚传输(Scatter-Gather) | 链表中的描述符全部完成 | 由链表描述符指定,支持非连续内存 | 每个描述符完成或全部完成 | 大数据包收发、协议栈零拷贝 |
2.2.1 单次传输模式
单次传输是最基础的DMA模式。配置完成后,DMA控制器从源地址搬运指定数量的数据到目的地址,传输完成后自动停止并产生中断。该模式适用于已知数据长度的场景,如发送一个固定大小的UART数据包。
注意事项:单次传输完成后,必须重新配置传输计数寄存器(CTR)才能启动下一次传输。在UART接收中,如果数据长度不确定,单次模式可能导致接收不完整或缓冲区溢出。
2.2.2 循环传输模式
循环传输模式下,DMA控制器在传输计数归零后自动将地址和计数值重载为初始值,形成环形缓冲区。该模式特别适合UART接收连续数据流,配合半传输完成中断和传输完成中断,可以实现双缓冲机制。
循环传输的关键配置参数:
- 缓冲区大小:必须为2的幂次方,便于地址回绕计算。
- 半传输中断:当传输计数达到初始值的一半时触发,用于处理前半缓冲区数据。
- 传输完成中断:当传输计数回绕到初始值时触发,用于处理后半缓冲区数据。
2.2.3 散聚传输模式
散聚传输(也称为链表传输)通过内存中的描述符链表管理多个不连续的传输块。每个描述符包含源地址、目的地址、传输计数和下一个描述符指针。DMA控制器自动遍历链表,完成所有描述符指定的传输任务。
散聚传输描述符结构示例:
struct dma_descriptor {
uint32_t src_addr; // 源地址
uint32_t dst_addr; // 目的地址
uint32_t transfer_size; // 传输字节数
uint32_t next_desc; // 下一个描述符地址
uint32_t ctrl; // 控制标志(中断使能、完成标志等)
};
下图展示了散聚传输的链表执行流程:
2.3 DMA请求与响应
DMA传输的启动依赖于外设发出的DMA请求信号。完整的请求与响应流程涉及外设、DMA控制器和总线仲裁器三方协作。
2.3.1 DMA请求类型
| 请求类型 | 触发源 | 信号特征 | 典型应用 |
|---|---|---|---|
| 硬件请求(HW Request) | 外设硬件信号(如UART RXNE标志) | 电平触发或脉冲触发,可配置极性 | UART接收/发送、SPI传输、ADC转换 |
| 软件请求(SW Request) | CPU写DMA软件触发寄存器 | 一次性触发,无需外设信号 | 存储器到存储器拷贝、测试模式 |
| 外设事件请求(Event Request) | 定时器事件、外部中断等 | 与定时器PWM或比较事件同步 | 定时触发ADC采样、PWM同步传输 |
2.3.2 请求响应流程
以UART接收为例,DMA请求与响应的完整时序如下:
- 外设就绪:UART接收FIFO中数据达到阈值(如1字节),硬件自动置位DMA请求信号(DMA_REQ)。
- DMA仲裁:DMA控制器检测到请求信号,根据通道优先级进行仲裁。若多个通道同时请求,高优先级通道优先获得总线控制权。
- 总线请求:DMA控制器向总线仲裁器发出总线请求信号(HOLD/BREQ)。
- 总线授权:CPU完成当前总线周期后,释放总线控制权。总线仲裁器向DMA控制器发送总线授权信号(HLDA/BGNT)。
- 数据传输:DMA控制器获得总线控制权,执行一次或多次突发传输,将UART FIFO中的数据搬运至内存缓冲区。
- 请求释放:传输完成后,DMA控制器释放总线,外设DMA请求信号自动清除。若传输计数未归零,DMA控制器继续等待下一次请求。
性能要点:突发传输(Burst Transfer)允许DMA在一次总线仲裁后连续传输多个数据单元,减少总线仲裁开销。在UART应用中,建议将突发大小设置为与FIFO深度匹配(如4字节或8字节),避免FIFO溢出或空置。
下图展示了UART DMA请求与响应的完整时序流程:
2.4 关键配置参数总结
在UART驱动开发中配置DMA时,以下参数需要根据具体硬件和应用场景仔细设定:
| 参数 | 可选值 | UART推荐配置 | 说明 |
|---|---|---|---|
| 数据宽度 | 8位 / 16位 / 32位 | 8位(与UART数据位匹配) | 必须与外设数据寄存器宽度一致 |
| 突发大小 | 1 / 4 / 8 / 16 次传输 | 4 或 8(匹配UART FIFO深度) | 过大可能导致CPU响应延迟 |
| 地址增量 | 递增 / 递减 / 固定 | 外设地址固定,内存地址递增 | UART数据寄存器地址固定不变 |
| 传输方向 | 外设→内存 / 内存→外设 | 接收:外设→内存;发送:内存→外设 | 由DMA控制寄存器DIR位决定 |
| 中断使能 | 传输完成 / 半传输 / 错误 | 传输完成 + 错误中断必须使能 | 错误中断用于检测总线错误或FIFO溢出 |
| 通道优先级 | 低 / 中 / 高 / 最高 | UART接收优先级高于发送 | 避免接收FIFO溢出导致数据丢失 |
实践建议:在初始化DMA时,建议先配置通道参数,再使能DMA控制器,最后使能外设的DMA请求。关闭顺序相反:先禁能外设DMA请求,再禁能DMA通道。这种顺序可以避免在配置过程中产生意外的DMA请求。