19 DMA

本文详细介绍了RT1052微控制器的DMA功能,包括eDMA简介、功能框图、传输基本概念、工作流程、DMAMUX的使用方法以及存储器到存储器、存储器到外设的DMA传输模式。内容涵盖了DMA通道的配置、地址路径、数据路径、控制模块、存储控制器的运作,以及DMA传输句柄的创建和回调函数的设置,旨在帮助开发者深入了解并高效利用RT1052的DMA特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

19.0 前言

配合《IMXRT1050RM》第 22 章 Enhanced Direct Memory Access (eDMA) 和第 21 章Direct Memory Access Multiplexer (DMAMUX) 一起食用

19.1 DMA 简介

RT1052 的 DMA 功能齐全,工作模式众多,配合 DMA 多路复用模块 (DMAMUX) 一起使用。

RT1052 的 DMA 支持三种传输模式:

  • 外设到存储器传输
  • 存储器到外设传输
  • 存储器到存储器传输

外设到存储器传输就是把外设数据寄存器内容转移到指定的内存空间。

  • 比如进行 ADC 采集时我们可以利用 DMA 传输把 AD 转换数据转移到我们定义的存储区中

存储区到外设传输就是把特定存储区内容转移至外设的数据寄存器中:

  • 多用于外设的发送通信。

存储器到存储器传输就是把一个指定的存储区内容拷贝到另一个存储区空间

  • 类似于 C 语言内存拷贝函数 memcpy,利用 DMA 传输可以达到更高的传输效率

19.2 DMA 功能框图

RT1052 的 DMA 模块分为两个主要模块

  • eDMA 驱动模块 (eDMA engine)
  • 传输控制描述符TCD(transfer-control descriptor)

eDMA 驱动模块和 TCD 的关系可以简单理解为执行任务者和任务清单之间的关系。

  • eDMA 驱动模块如同执行任务者,他根据任务清单选择需要执行的任务,并将任务的执行过程和执行结果记录到任务清单。
  • TCD 就是一块最多只能记录 32 个传输任务的存储单元
    • eDMA 驱动模块从 TCD 中读取传输任务
    • 在执行过程中更新传输进度到 TCD
    • 执行完成后将源地址、目的地址等一些信息写回 TCD
      在这里插入图片描述
      eDMA 驱动模块分为 4 部分:如图中标号 1 到 4
      传输控制描述符分为两部分,如图中标号 ⑤ 和 ⑥

19.2.1 地址路径(Address path)

该模块主要完成通道的仲裁和地址的计算。

19.2.2 数据路径(Data path)

该模块实现总线主读写数据路径。

19.2.3 编程模型与通道仲裁(Program model/channel arbitration)

此块实现 eDMA 编程模型的第一部分以及通道仲裁逻辑。

19.2.4 控制模块(Control)

此块提供 eDMA 引擎的所有控制功能。

19.2.5 存储阵列 (Memory array)

TCD 由存储阵列和存储控制器组成。

19.2.6 存储控制器 (Memory controller)

存储控制器用于管理来自 eDMA 引擎的访问以及来自内部外围总线的访问。

  • 在同时访问的情况下,eDMA 引擎优先级更高,外围总线的访问被停止。

19.3 eDMA 传输基本概念

19.3.1 数据宽度

DMA 的源数据宽度与目的数据宽度可以不同。

  • 数据宽度的设置是通过 TCDa_ATTR 寄存器设置的。
    • 当源数据宽度与目的数据宽度相同时,执行一次读取执行一次写入。
    • 当源数据宽度小于目的数据宽度,DMA 执行两次读取执行一次写入。

19.3.2 次循环与主循环

次循环和主循环可以用于控制传输的数据量

  • 次循环用于设置一次 DMA 传输请求传输的数据量,单位为字节
  • 主循环用于设置执行多少个次循环之后停止该 DMA 通道的传输。

次循环设置寄存器有三个选择:

  • CR[EMLM] = 0,禁用次循环映射。
  • CR[EMLM] = 1, 启 用 次 循 环 映射
    • TCDa_NBYTES_MLOFFNO[SMLOE] = 0,TCDa_NBYTES_MLOFFNO[DMLOE] = 0,禁用源地址和目的地址次循环偏移。
    • 这种情况下使用 TCDa_NBYTES_MLOFFNO[NBYTES] 寄存器指定一个次循环传输的数据量。
  • CR[EMLM] = 1, 启 用 次 循 环 映 射
    • TCDa_NBYTES_MLOFFYES[SMLOE] = 1 或者 TCDa_NBYTES_MLOFFYES[DMLOE] = 1,启用源地址或目的地址次循环偏移。
    • 偏移地址由偏移寄存器TCDa_NBYTES_MLOFFYES[MLOFF] 设定的原地址或目的地址偏移值决定
    • TCDa_NBYTES_MLOFFYES[NBYTES] 寄存器指定一个次循环传输的数据量

根据是否使用通道连接,主计数值得设置不同的寄存器,分为如下两种情况

  • TCDa_BITER_ELINKNO[ELINK] = 0 表示禁止通道连接
    • 此时 TCDa_BITER_ELINKNO[BITER] 用于指定主循环计数值
  • TCDa_BITER_ELINKYES[ELINK] = 1 表 示 启 用 通 道连 接
    • TCDa_BITER_ELINKYES[BITER] 用 于 指 定 次 循 环 执 行 次 数
      • 当一个次循环执行结束后 DMA 自动设置 TCDn_CSR[START] 寄存器触发一次通道 n 的 DMA 传输
    • TCDa_BITER_ELINKYES[LINKCH] = n 表示当前通道与通道 n 连接

初始化时 TCDa_BITER_ELINKNO寄存器与 TCDa_BITER_ELINKYES 寄存器的设置要相同。这两个寄存器分别表示启始主循环次数和当前主循环次数.

19.3.3 源地址和目标地址的设置及地址偏移设置

TCDa_SADDR 寄存器与 TCDa_DADDR 寄存器分别用于设置 DMA 源始地址与目的启始地址。

  • TCDa_SOFF 与 TCDa_DOFF 寄存器分别用于设置 DMA 执行一次读写操作之后原地址和目的地址的偏移值。
  • 当完成一个次循环之后 DMA 重新使用源起始地址寄存器(TCDa_SADDR)和目的启始寄存器(TCDa_DADDR)初始化 DMA 当前读写地址。
  • 如果启用了次循环映射还会添加次循环映射指定的偏移值。

19.3.4 通道组优先级与通道优先级

T1052 拥有 32 个 DMA 通道,这 32 个通道被分为两个通道组,通道组 0 包含通道 0 到 15,通道组 1 包含通道 16 到 31。

CR[ERCA] 寄存控制是否使用固定优先级模式

  • CR[ERCA] = 0 时优先级的设置才有效

CR[GRP0PRI] 寄存器与 CR[GRP1PRI] 寄存器分别用于设置通道组 0 与通道组 1 的优先级

  • 初始状态下 CR[GRP0PRI] = 0,CR[GRP1PRI] = 1
  • 优先级数值越大对应的优先级越高。
  • 默认情况下,DMA 通道组 1 的所有通道的优先级高于 DMA 通道组 0 的所有通道的优先级。

DCHPRIn(n 取 0 到 31),每一个 DMA 通道有各自的通道优先级设置寄存器

  • DCHPRIn[CHPRI]寄存器用于设置通道优先级。
  • 对于通道组 0,默认情况下通道优先级与通道号减 1。
  • 对于通道 1,默认情况下通道优先级等于通道号减 16。
  • DCHPRIn[ECP] 寄存器配置该通道是否允许被更改优先级的通道打断,默认情况下是允许。
  • DCHPRIn[DPA] 寄存器配置该通道是否能够打断较低优先级的通道,默认情况下是能够打断的。

如果我们使用通道固定优先级(CR[ERCA] = 0)并且优先级设置保持默认则通道的优先与通道编对应,通道编号越大优先级越高。

19.3.5 DMA 请求

RT1052 大多数外设能够申请 DMA 传输请求,在 RT1052 官方的 SDK 库中定义了 114 个 DMA请求源

typedef enum _dma_request_source
2 {
   
3 kDmaRequestMuxFlexIO1Request0Request1 = 0|0x100U, /**< FlexIO1 */
4 kDmaRequestMuxFlexIO2Request0Request1 = 1|0x100U, /**< FlexIO2 */
5 kDmaRequestMuxLPUART1Tx = 2|0x100U, /**< LPUART1 Transmit */
6 kDmaRequestMuxLPUART1Rx = 3|0x100U, /**< LPUART1 Receive */
7 kDmaRequestMuxLPUART3Tx = 4|0x100U, /**< LPUART3 Transmit */
8 kDmaRequestMuxLPUART3Rx = 5|0x100U, /**< LPUART3 Receive */
9 kDmaRequestMuxLPUART5Tx = 6|0x100U, /**< LPUART5 Transmit */
10 kDmaRequestMuxLPUART5Rx = 7|0x100U, /**< LPUART5 Receive */
11 kDmaRequestMuxLPUART7Tx = 8|0x100U, /**< LPUART7 Transmit */
12 kDmaRequestMuxLPUART7Rx = 9|0x100U, /**< LPUART7 Receive */
13 kDmaRequestMuxCSI = 12|0x100U, /**< CSI */
14 . . .
15 . . .
16 . . .
17 } dma_request_source_t;

每一个 DMA 通道可以选择任意一个 DMA 触发源作为DMA 的触发信号。

19.3.6 通道错误与中断

错误状态寄存器(ES)列出了所有可能的错误状态。

当发生通道错误时每个通道可以独立配置处理方式,可以选择忽略错误也可以选择产生错误中断。

错误中断使能寄存器(EEI)是一个 32 位寄存器,每一位控制一个通道

  • 我们可以直接修改该寄存设置通道发生错误时是否产生中断
  • 也可以通过清除错误中断使能寄存器(SEEI)禁止错误中断寄存器(CEEI)设置单个通道

19.4 eDMA 基本工作流程

在这里插入图片描述

19.4.1 激活 eDMA 传输通道

19.4.1.1 eDMA 外部传输请求

eDMA 传输通道被激活的前提是有 eDMA 传输请求或者寄存器 TCDn_CSR[START] 被置 1。两种方式 eDMA 的激活流程是相同的。

19.4.1.2 进行优先级仲裁

eDMA 请求通过控制模块之后,进入程序模型和通道总裁模块

  • 根据 CR[ERCA] 寄存器的配置,仲裁使用使用固定优先级或循环算法
    • 如果使用固定优先级则高优先级的通道可以打断低优先级优先得到处理,低优先级只能等待高优先级执行结束。
    • 使用循环算法情况下 eDMA 会根据通道号从大到小依次执行,但是如果一个传输通道正在执行此时另外一个通道号更大的请求不会打断当前的传输。

19.4.1.3 将 eDMA 请求通道编号转化为 TCD 地址

仲裁通过后,eDMA 通道编号经过地址路径模块转化为地址,用于访问 TCDn 的本地内存

19.4.1.4 读取并加载地址路径

第 ③ 部分已经得到 eDMA 传输通道的传输描述符的地址,该部分的作用是将传输描述符加载到eDMA 引擎。

19.4.2 进行数据传输

eDMA 从 TCD 获取传输信息之后即可开始 DMA 传输,传输工作由硬件自动完成。

DMA传输过程:
在这里插入图片描述

19.4.2.1 地址计算

DMA 传输过程中大多数情况下需要不断的移动读写地址,这部分工作由地址路径模块完成

  • 它根据从 TCD 读取得到的配置参数完成地址的计算

19.4.2.2 数据路径

数据路径模块根据地址路径模块提供的地址信息执行源读取

  • 读取的数据被暂存在数据路径模块中,当达到目标写数据宽度后再执行写入操作。

19.4.2.3 控制模块

如果源地址与目的地址数据宽度不同,则控制模块根据数据宽度的差异控制数据路径模块。

  • 例如源数据宽度为 16 位目的数据宽度为 32 位,则数据路径模块会执行两次读操作之后执行一次写操作。
  • 当传输完成后控制模块向外发出传输完成标志。

19.4.3 更新传输控制描述符 (TCD)

一个次循环传输完成后,执行数据传输的最后阶段,更新传输控制描述符 (TCD),如下图所示:
在这里插入图片描述一个次循环完成后需要更新 TCD 中的某些寄存器

  • TCDn_SADDR、TCDn_DADDR、TCDn_CITER_ELINKNO等

如果主循环计数完成则还要处理其他任务

  • 例如可选的中断请求、最终调整源地址和目标地址、重新加载 TCDn_BITER_ELINKNO 和 TCDn_CITER_ELINKNO 寄存器等

19.5 DMAMUX 简介及使用方法

DMA 多路复用器 (DMAMUX) 将 DMA 源 (称为槽) 路由到 32 个 DMA 通道中的任何一个。
在这里插入图片描述
DMAMUX 为每个 DMA 通道提供了一个通道配置寄存器 CHCFGa(a 取 0 到 31)
通过这些寄存器可以独立的设置每个通道的 DMA 触发源、工作模式等。

  • CHCFGa[SOURCE] 用于指定通道的 DMA 触发入源
  • RT1052 的 SDK 库中列出了 114 个 DMA 输入源

CHCFGa[ENBL]、CHCFGa[ENBL]、CHCFGa[A_ON] 寄存器用于设置 DMA 的工作方式
在这里插入图片描述

19.5.1 禁用模式 (Disabled Mode)

  • 禁用该 DMA 通道

正常模式 (Normal Mode)

  • 正常模式是最常用的一种工作模式,适合于所有 DMA 通道
  • DMA 通道每收到一传输请求信号执行一次传输,待传输完成之后(次循环与主循环执行结束)自动停止。

19.5.2 周期触发模式 (Periodic Trigger Mode)

  • 该模式只适合 DMA 的前 4 个通道(0 到 3)

在这里插入图片描述从图中可以看出,在周期触发模式下只有当有外部请求时周期性触发信号才能触发 DMA 请求

19.5.3 始终运行模式 (Always On Mode)

  • 在始终运行模式下 DMA 通道不断的执行从源地址传输数据到目的地址,一次传输执行完成之后不会停止,循环执行。

19.5.4 等待触发模式 (Always On TriggerMode)

  • 与周期触发模式对比,该模式相当于外部请求信号一直存在,只要产生周期触发信号就会产生 DMA 请求。

19.6 DMA 初始化结构体详解

RT1052 的 SDK 库为 DMA 的初始化建立了两个初始化结构体

  • edma_config_t 用于配置 DMA 的工作方式
  • edma_transfer_config_t 用于配置 DMA 传输设置

19.6.1 DMA 配置结构体

1 typedef struct _edma_config
2 {
   
3 bool enableContinuousLinkMode; /* 是否开启次循环连接模式 */
4 bool enableHaltOnError; /* 是否允许错误停止模式 */
5 bool enableRoundRobinArbitration;/* 选择使用固定优先级模式或轮询通道仲裁模式 */
6 bool enableDebugMode; /* 是否使能 Debug 模式 */
7 } edma_config_t;

19.6.1.1 enableContinuousLinkMode

次循环连接的作用是当该通道的一个次循环执行结束后自动切换到连接的通道执行。

  • 可以配置连接到自身,这样该通道一个次循环执行结束之后自动开启下一次循环。
  • enableContin-uousLinkMode = 1, 开启次循环通道连接

19.6.1.2 enableHaltOnError

通过 enableHaltOnError 配置项设置如何处理DMA 错误

  • enableHaltOnError = 1 如果一个通道发生则忽略所有通道的 DMA 传输请求,直到错误标志位被清除。
  • enableHaltOnError = 0 忽略错误。

19.6.1.3 enableRoundRobinArbitration

DMA 拥有 32 个通道,同一时间只能有一个通道传输数据

当多个通道请求传输数据时根据 enableRoundRobinArbitration 配置选项决定使用固定优先级模式还是根据通道号从大到小依次执行。

  • enableRoundRobinArbitration = 0,根据预先设定的通道优先级选择当前执行的通道
  • enableRoundRobinArbitration = 1, 忽略通达优先级,根据通道编号从大到小依次执行。

19.6.1.4 enableDebugMode

设置是否使能Debug模式。

  • enableDebugMode=0,在Debug模式下,DMA正常运行不受影响。
  • enableDebugMode = 1,DMA 延时新通道的启动,允许当前执行通道执行完成,当退出 Debug 模式后通道恢复正常运行。

19.6.2 DMA 传输配置结构体

edma_transfer_config_t

typedef struct _edma_transfer_config
2 {
   
3 uint32_t srcAddr; /* 源数据地址 */
4 uint32_t destAddr; /* 目的数据地址 */
5 edma_transfer_size_t srcTransferSize; /* 源数据宽度 */
6 edma_transfer_size_t destTransferSize; /* 目的数据宽度 */
7 int16_t srcOffset; /* 源地址偏移 */
8 int16_t destOffset; /* 目的地址偏移 */
9 uint32_t minorLoopBytes; /* 次循环,传输字节数 */
10 uint32_t majorLoopCounts; /* 主循环,循环计数值(循环次数) */
11 } edma_transfer_config_t;

19.6.2.1 srcAddr

源数据地址。用于设置源地址的起始地址。

19.6.2.2 destAddr

目的数据地址。用于设置目的地址的起始地址。

19.6.2.3 srcTransferSize

源数据的宽度。

typedef enum _edma_transfer_size
2 {
   
3 kEDMA_TransferSize1Bytes = 0x0U, /* 一次传输 1 个字节 */
4 kEDMA_TransferSize2Bytes = 0x1U, /* 一次传输 2 个字节 */
5 kEDMA_TransferSize4Bytes = 0x2U, /* 一次传输 4 个字节 */
6 kEDMA_TransferSize8Bytes = 0x3U, /* 一次传输 8 个字节 */
7 kEDMA_TransferSize16Bytes = 0x4U, /* 一次传输 16 个字节 */
8 kEDMA_TransferSize32Bytes = 0x5U, /* 一次传输 32 个字节 */
} edma_transfer_size_t;

如果选择数据宽度为 32 字节则 DMA 数据总线要传输 8次(32 位总线每次最多传输 4 字节)才能完成一次传输

  • 在此期间即使使用了固定通道优先级,高优先级通道也不能打断该通道的传输。一次传输是原子性的,不能被打断

19.6.2.4 destTransferSize

目的地址宽度。类似于源数据宽度。

19.6.2.5 srcOffset

源地址偏移。当 DMA 完成一次传输,该寄存器用于设置下一个读地址与当前读地址的偏移。

  • 该寄存器是有符号的,设置位正值,表示地址增加。为负值,表示地址减少。单位为字节。

19.6.2.6 destOffset

目的地址偏移。类似于源地址偏移。

19.6.2.7 minorLoopBytes

设置一个次循环传输的字节数。

19.6.2.8 majorLoopCounts

设置主循环计数值。用于设置次循环执行次数。

  • 当主循环执行完成,如果开启了中断则会触发 DMA 传输完成中断。

19.6.3 DMA 传输句柄 edma_handle_t

typedef struct _edma_handle
2 {
   
3 edma_callback callback; /* 主循环完成回调函数 */
4 void *userData; /* 回调函数参数 */
5 DMA_Type *base; /*eDMA 基地址 */
6 edma_tcd_t *tcdPool; /* 指向 TCDs 的指针 */
7 uint8_t channel; 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万码无虫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值