文章目录
-
- UART的DMA
- [UART的DMA + IDLE中断](#UART的DMA + IDLE中断)
- GPDMA的链表应用
-
DMA,全称为:Direct Memory Access (直接存储器访问)
-
DMA传输是将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输,而无需 CPU 介入
-
这样,既不用CPU不断查询是否需要进行数据传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使 CPU 的效率大为提高

GPDMA
- 基于stm32u575为例
- 前缀GP:General purpose 和 另一个使用前缀LP(Low Power)的LPDMA区分

GPDMA的通道
- 16个通道,可以最多同时为16个外设、内存之间完成无CPU介入的DMA传输
- 16个通道,分别由各自的一组寄存器控制,通道彼此独立
- 可配置的传输形式:
- 外设到内存
- 内存到外设
- 内存到内存
- 外设到外设
- 16个通道可并行使用
- 16个通道分为2组
| 通道 | FIFO 尺寸 | 特性 |
|---|---|---|
| 0~11 | 2 字共 8 字节 | 支持固定或连续递增的地址必须用于 APB 或 AHB 的外设和 SRAM 之间 |
| 12~15 | 8 字共 32 字节 | 支持二维地址二者均适用于源和目标,可设定两个地址跳转 / 偏移量可用于要求更高的 APB 或 AHB 的外设和 SRAM 之间或者从 / 至外部 RAM |
对总线的端口
- 双向的AHB主端口
- 2个端口
- Port 0:通常用于连接外设到内存或内存到外设的传输
- Port 1:通常用于内存到内存的传输或特定外设连接
传输的优先级
- 可配置为 4 个优先级,其中,优先级3 最高,0 最低
- 优先级3 为时间敏感的传输
- 优先级0~2通过各自的分组,循环占用总线

DMA触发源
- 与外设相关的
- 一共126个DMA请求源,编号 :0 ~ 125,包含了几乎所有的对外接口、定时器、ADC/DAC等等
- 通过GPDMA_CxTR2寄存器REQSEL[6:0] 配置
- 片上一些特定事件
- 一共68个触发源,编号:0 ~ 67,主要为EXTI中断,防拆,定时器,比较器以及DMA内部传输结束等
- 通过 GPDMA_CxTR2 寄存器的 TRIGSEL[6:0] 位进行选择
- TRIGPOL[1:0] 控制上升沿或下降沿触发
突发(burst)传输和块(block)传输
- 突发是一系列节拍(n = 1 至 64)。每一拍是一次数据传输,具有相同数据宽度
- 建议将 GPDMA 突发的大小设定为所分配 GPDMA 通道的 FIFO 大小的一半
- 通道 0 至 11 通常为 1 字突发
- 通道12 至 15 通常为 4 字突发
| 特性 | Burst 模式 | Block 模式 |
|---|---|---|
| 传输方式 | 单事件触发多数据连续传输 | 分块传输,可中断 / 链式触发 |
| 效率 | 更高(减少总线仲裁开销) | 适中(适合非连续数据) |
| 适用外设 | TIM、HRTIM、高速 ADC/DAC | SDIO、USB、SPI/I2C |
| 配置复杂度 | 需注意数据对齐(如 GPDMA 处理) | 更简单,块大小可灵活设定 |
两种应用方式
普通方式
- 直接向寄存器配置GPDMA相关参数,启动后,在触发源的控制下,完成设定次数的DMA传输
链表模式
- 在内存中建立一个链表,链表的每个节点都存储着控制GPDMA传输所需要的所有寄存器内容

- 传输开始,先将链表的头节点内容加载到DMA控制相关寄存器中,待传输完成,可以自动加载下一个节点的内容,直到完成所有节点中要求的DMA传输。
- 每个节点的配置参数可以相互独立,但通常用于拓展不同的参与DMA传输的内存空间地址。例如,可以通过链表模式,让一个外设对应3个不同的内存地址,依次使用,避免DMA传输完成,处理内存数据时,DMA的停顿
普通模式的应用流程
- 传输配置
- 传输模式(参与传输的模块角色)
- 优先级
- 源地址及是否需要自增
- 目标地址及是否需要自增
- 传输宽度(源和目标可配置不同的宽度,通过使用FIFO拆组包匹配)
- DMA传输次数
- 绑定外设
- 设置传输完成一半中断(可选)和全部传输完成中断
- 传输过程
- 使能DMA,开始在触发源的控制下进行数据的传输
- 完成一半次数的传输,可以触发中断
- 完成所有的传输,触发传输完成中断
- 其他:
- 在传输过程中,可以对传输进行挂起和恢复
- 在传输过程中,可以对传输进行中断和复位
常规模式和循环模式
- 每次完成预设的DMA传输次数,就停止DMA传输,为常规模式
- 每次完成一轮预设的DMA传输次数后,自动更新下一参与传输的内存地址并将传输次数重置为之前的次数。开始新一轮的DMA传输。循环模式需要使用链表
通道可产生的事件及中断
- 传输完成中断(TC: transfer complete) & 完成一半传输中断(HTF: half transfer)
- 一共有3种DMA完成事件产生时机,受TCEM[1:0]控制,可配置成:
- 00,块传输级:当传输项全部完成(GPDMA_CxBR1.BNDT[15:0] = 0)时:全部(或一半)传输事件在块结束时(或半块结束时)生成
- 01:对于通道 0至11,与00相同;通道12至15,在2D/重复块级别,当是最末一个数据快(GPDMA_CxBR1.BRC[10:0] = 0)且传输完成(GPDMA_CxBR1.BNDT[15:0] = 0)时。完整(及半)传输事件在2D/重复块结束时(或半块结束时)生成
- 10:链表节点传输级:每完成一个链表节点控制的一半或所有DMA传输项,产生半传输或传输完成中断。
- 11,通道级别:完整传输事件在最后一个LLI传输结束时生成。半传输事件在最后一个LLI数据传输的中途生成。全部传输事件在最末一个LLI全部传输完成时产生。若通道传输为连续/无限模式,则不会生成任何事件
- 错误相关的事件和中断
- 用户设置错误(USE: user setting error)
- 更新链表传输错误(ULE: update link transfer error)
- 数据传输错误(DTE: data transfer error )
- 其他的事件和中断
- 触发溢出中断(TO: trigger overrun)
- 挂起中断(SUSP: completed suspension)
低功耗模式下的数据自主传输模式
- 在睡眠Sleep和停止Stop模式下自主完成数据传输
线性地址和二维地址
- 线性地址:每次DMA传输的地址都是连续的
- 二维地址:DMA传输的数据可以到不连续的地址中,例如可以在采样立体声音频时,把左右声道的数据分别存储到两个不连续的内存数据块中
GPDMA基本应用
UART的DMA
- 开辟一个8字节的缓冲区,接收定长数据,并转发回来
- 收发皆使用GPDMA的模式
CubeMX配置
- 在GPDMA菜单,选择未使用的通道

- 为每个通道配置相应的触发源

- 单通道详细配置


UART的DMA + IDLE中断
- 使用DMA+UART Idle中断的方式接收不定长数据,并通过DMA方式转发回来
- 相关函数
c
//串口发送-DMA方式
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t
*pData, uint16_t Size);
//串口发送DMA中断回调函数
//完成一半
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);
//全部完成(需开启串口全局中断)
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
//串口接收-DMA方式
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t
*pData, uint16_t Size);
//串口接收DMA中断回调函数
//完成一半
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
//全部完成
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
//串口接收-DMA + Idle中断
HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart,
uint8_t *pData, uint16_t Size);
//接收回调函数(完成一半,全部完成,Idle中断均会触发)
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size);
GPDMA的链表应用
- 定义两个DMA链表,分别用于串口的接收和发送
- 每个链表2个节点,完成接收后,将接收的内容,通过发送链表转发回来
CubeMX配置
- 在GPDMA选项卡,申请2个通道,使能为 Link List 模式

- CH1作为发送用链表

- CH2作为接收链表

- 在 Utilities 菜单下 ,操作 LINKEDLIST,通过 Add List 添加链表


- 通过 Add Node 添加链表中的节点:
- 接收用节点的详细配置界面

- 发送用节点的详细配置界面

主要函数
c
//CubeMX生成的链表配置函数,会因链表名称不同而不同
MX_uart1_rx_queue_Config();
//转换成动态链表,减少更新寄存器的数量,提高效率
HAL_StatusTypeDef HAL_DMAEx_List_ConvertQToDynamic(DMA_QListTypeDef *const
pQList);
//将链表与DMA通道关联
HAL_StatusTypeDef HAL_DMAEx_List_LinkQ(DMA_HandleTypeDef *const hdma,
DMA_QListTypeDef *const pQList);
//宏函数,用于将链表和具体的外设DMA源关联
__HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD__, __DMA_HANDLE__);