DMA的应用

文章目录

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__);
相关推荐
学嵌入式的小杨同学3 小时前
STM32 进阶封神之路(二十五):ESP8266 深度解析 —— 从 WiFi 通信原理到 AT 指令开发(底层逻辑 + 实战基础)
c++·vscode·stm32·单片机·嵌入式硬件·mcu·智能硬件
树爷只认钱3 小时前
ESP01S模块+串口底座 AT指令连接中移Onenet物联网全过程(第1篇)
单片机·嵌入式硬件·物联网·esp8266
学嵌入式的小杨同学3 小时前
STM32 进阶封神之路(二十六):ESP8266 实战全攻略 ——TCP 通信 + 数据上传 + 远程控制 + 透传模式(库函数 + 代码落地)
stm32·单片机·嵌入式硬件·mcu·硬件架构·硬件工程·智能硬件
Nice__J3 小时前
Mcu架构以及原理——7.寄存器编程与抽象
stm32·单片机·架构
嵌入式学习和实践3 小时前
当MCU遇上大模型:在单片机上实现AI对话的硬核玩法
人工智能·单片机·大模型
我不是程序猿儿4 小时前
【嵌入式】适合 STM32 初学者BootLoader 入门学习心得
linux·stm32·单片机·嵌入式硬件·学习
Suifqwu4 小时前
stm32进阶-启动文件
stm32·单片机·嵌入式硬件
火龙果里的芝麻5 小时前
STM32 FreeModbus 移植(最详细)
stm32·单片机·嵌入式硬件
weiyvyy6 小时前
嵌入式硬件接口开发的流程
人工智能·驱动开发·单片机·嵌入式硬件·硬件架构·硬件工程