19 DMA

文章目录

  • 19.0 前言
  • 19.1 DMA 简介
  • 19.2 DMA 功能框图
    • 19.2.1 地址路径(Address path)
    • 19.2.2 数据路径(Data path)
    • 19.2.3 编程模型与通道仲裁(Program model/channel arbitration)
    • 19.2.4 控制模块(Control)
    • 19.2.5 存储阵列 (Memory array)
    • 19.2.6 存储控制器 (Memory controller)
  • 19.3 eDMA 传输基本概念
    • 19.3.1 数据宽度
    • 19.3.2 次循环与主循环
    • 19.3.3 源地址和目标地址的设置及地址偏移设置
    • 19.3.4 通道组优先级与通道优先级
    • 19.3.5 DMA 请求
    • 19.3.6 通道错误与中断
  • 19.4 eDMA 基本工作流程
    • 19.4.1 激活 eDMA 传输通道
      • 19.4.1.1 eDMA 外部传输请求
      • 19.4.1.2 进行优先级仲裁
      • 19.4.1.3 将 eDMA 请求通道编号转化为 TCD 地址
      • 19.4.1.4 读取并加载地址路径
    • 19.4.2 进行数据传输
      • 19.4.2.1 地址计算
      • 19.4.2.2 数据路径
      • 19.4.2.3 控制模块
      • 19.4.3 更新传输控制描述符 (TCD)
  • 19.5 DMAMUX 简介及使用方法
    • 19.5.1 **禁用模式 (Disabled Mode)**
    • 19.5.2 **周期触发模式 (Periodic Trigger Mode)**
    • 19.5.3 **始终运行模式 (Always On Mode)**
    • 19.5.4 **等待触发模式 (Always On TriggerMode)**
  • 19.6 DMA 初始化结构体详解
    • 19.6.1 DMA 配置结构体
      • 19.6.1.1 enableContinuousLinkMode
      • 19.6.1.2 enableHaltOnError
      • 19.6.1.3 enableRoundRobinArbitration
      • 19.6.1.4 enableDebugMode
    • 19.6.2 DMA 传输配置结构体
      • 19.6.2.1 srcAddr
      • 19.6.2.2 destAddr
      • 19.6.2.3 srcTransferSize
      • 19.6.2.4 destTransferSize
      • 19.6.2.5 srcOffset
      • 19.6.2.6 destOffset
      • 19.6.2.7 minorLoopBytes
      • 19.6.2.8 majorLoopCounts
    • 19.6.3 DMA 传输句柄 edma_handle_t
      • 19.6.3.1 Callback
      • 19.6.3.2 userData
      • 19.6.3.3 Base
      • 19.6.3.4 tcdPool
      • 19.6.3.5 channel
  • 19.7 DMA 存储器到存储器模式
    • 19.7.1 编程思路
    • 19.7.2 代码
      • 19.7.2.1 DMA 宏定义及相关变量定义
      • 19.7.2.2 DMAMUX 设置
      • 19.7.2.3 初始化 DMA
      • 19.7.2.4 初始化DMA传输句柄
      • 19.7.2.5 回调函数
      • 19.7.2.6 主函数
  • 19.8 DMA 存储器到外设模式
    • 19.8.1 编程思路
    • 19.8.2 代码分析
      • 19.8.2.1 DMA 相关宏定义
      • 19.8.2.2 初始化串口
      • 19.8.2.3 串口 DMA 传输句柄
      • 19.8.2.4 DMA 初始化
        • 19.8.2.4.1 定义 DMA 传输句柄和 LPUART eDMA 句柄
        • 19.8.4.2 初始化 DMAMUX
        • 19.8.4.3 初始化 eDMA
        • 19.8.4.4 初始化串口 DMA 传输句柄
      • 19.8.2.5 回调函数
      • 19.8.2.6 主函数
  • 19.9 DMA存储器到SPI
    • 19.9.1 DMAMUX初始化
    • 19.9.2 DMA初始化
    • 19.9.3 SPI初始化
    • 19.9.4 DMA配置
    • 19.9.5 DMA发送
    • 19.9.6DMA回调

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
      19 DMA_第1张图片
      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 DMA_第2张图片

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 DMA_第3张图片

19.4.2.1 地址计算

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

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

19.4.2.2 数据路径

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

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

19.4.2.3 控制模块

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

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

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

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

  • TCDn_SADDR、TCDn_DADDR、TCDn_CITER_ELINKNO等

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

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

19.5 DMAMUX 简介及使用方法

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

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

CHCFGa[ENBL]、CHCFGa[ENBL]、CHCFGa[A_ON] 寄存器用于设置 DMA 的工作方式
19 DMA_第7张图片

19.5.1 禁用模式 (Disabled Mode)

  • 禁用该 DMA 通道

正常模式 (Normal Mode)

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

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

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

19 DMA_第8张图片从图中可以看出,在周期触发模式下只有当有外部请求时周期性触发信号才能触发 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 配置选项决定使用固定优先级模式还是根据通道号从大到小依次执行。

  • enable

你可能感兴趣的:(NXP,单片机)