3. DMA 传输类型:单次传输、块传输、循环传输、链式传输(Scatter/Gather)

S32K3 的 eDMA 引擎支持四种核心传输类型,理解它们的差异是进行高效数据搬运和中断协同设计的基础。每种类型对应不同的应用场景,在中断触发时机、地址更新规则和链路管理上各有特点。

3.1 传输类型概览与对比

传输类型 单次传输量 自动重复 中断触发时机 典型应用
单次传输 1 次 Minor Loop 否 Major Loop 完成 单次 ADC 结果读取
块传输 N 次 Minor Loop 否 Major Loop 完成 SPI 数据块收发
循环传输 N 次 Minor Loop 是(自动重载) 每次 Major Loop 完成 定时器捕获、PWM 更新
链式传输 可变(跨通道) 是(通道链接) 每个链接点触发 Scatter/Gather 描述符表
关键概念:Minor Loop 是 DMA 传输的最小原子单位,通常等于源/目的数据宽度(如 32 位)。Major Loop 由多个 Minor Loop 组成,代表一次完整的传输请求完成。

3.2 单次传输(Single Transfer)

单次传输是最基本的模式:DMA 响应一次请求,完成一个 Minor Loop 的数据搬运后即停止,等待下一次软件触发或外设请求。该模式下,TCD.CITER(当前迭代计数)初始值为 1,传输完成后自动清零。

// 单次传输 TCD 配置示例(S32K3 RTD 驱动风格)
tcd.SADDR = (uint32_t)&src_buffer;
tcd.DADDR = (uint32_t)&dst_register;
tcd.NBYTES = 4;              // Minor Loop 大小:4 字节
tcd.CITER = 1;               // Major Loop 迭代次数 = 1
tcd.BITER = 1;
tcd.CSR = DMA_CSR_START_MASK; // 使能硬件请求或软件触发
中断协同要点:单次传输完成后,DMA 会置位对应通道的完成标志位(TCD.CSR.DONE),并可触发 INTC 中断。由于传输量小,中断延迟敏感度较低,适合分配给中等优先级。

3.3 块传输(Block Transfer)

块传输将多个 Minor Loop 组合为一个 Major Loop。例如,从 SPI 接收 FIFO 连续读取 16 个 32 位数据:每个 Minor Loop 搬运 4 字节,16 个 Minor Loop 构成一个 Major Loop。传输完成后才产生中断,中间不打断 CPU。

块传输(Block Transfer)时序结构 ML#1 ML#2 ML#3 ML#4 ML#5 ML#6 ML#N Major Loop(一次完整块传输) → 中断触发
// 块传输 TCD 配置(16 次 Minor Loop)
tcd.NBYTES = 4;               // 每次搬运 4 字节
tcd.CITER = 16;               // 共 16 次 Minor Loop
tcd.BITER = 16;
tcd.SLAST = 0;                // 源地址不调整(FIFO 固定地址)
tcd.DLAST_SGA = -64;          // 目的地址回绕(环形缓冲)
项目移植陷阱:块传输中,如果外设请求频率高于 DMA 处理速度,会导致溢出。务必计算外设峰值速率与 DMA 带宽的匹配关系。S32K3 eDMA 在 160 MHz 下,单次 Minor Loop 约需 4~6 个总线时钟周期。

3.4 循环传输(Circular Transfer)

循环传输在块传输基础上增加了自动重载机制:Major Loop 完成后,TCD 的 CITER 和地址指针自动恢复为初始值,无需 CPU 重新配置。适用于周期性数据采集或波形生成。

  • 地址回绕:通过 SLAST 或 DLAST_SGA 设置负偏移量,使地址指针回到缓冲区起点。
  • 中断频率:每次 Major Loop 完成都会触发中断,可用于数据同步或标志更新。
  • 缓冲区大小:必须为 2 的幂次方,且与 NBYTES × CITER 对齐。
循环传输(Circular Transfer)地址回绕机制 Buf[0] Buf[1] Buf[2] Buf[3] Buf[4] DLAST_SGA = - (NBYTES × CITER) 回绕至 Buf[0] 每次 Major Loop 完成 → 中断
中断优先级建议:循环传输的中断频率固定且可预测,建议分配中等优先级(如 INTC 优先级 8~12),避免阻塞高优先级实时任务,同时确保数据不丢失。

3.5 链式传输(Scatter/Gather)

链式传输是 S32K3 eDMA 最强大的特性,允许通过硬件自动加载多个 TCD 描述符,实现复杂的数据搬运序列。每个 TCD 完成后,DMA 自动从内存中加载下一个 TCD,无需 CPU 干预。

3.5.1 链式传输工作原理

  • 描述符表:在系统内存中预定义一组 TCD 结构体数组,每个 TCD 描述一次传输的源、目的、计数和链接信息。
  • 链接字段:TCD 的 DLAST_SGA 字段在链式模式下存储下一个 TCD 的内存地址。
  • 自动加载:当前 TCD 的 Major Loop 完成后,eDMA 引擎自动从 DLAST_SGA 指向的地址加载新 TCD,并开始下一次传输。
链式传输(Scatter/Gather)描述符链接结构 TCD #0 Src: ADC_Buf0 Dst: RAM_A TCD #1 Src: SPI_FIFO Dst: RAM_B TCD #2 Src: Timer_Cap Dst: RAM_C DLAST_SGA DLAST_SGA TCD #2 完成 → 中断 内存描述符表(Descriptor Table) TCD[0] → TCD[1] → TCD[2] → ... → TCD[N](最后一个 TCD 的 DLAST_SGA = 0)

3.5.2 链式传输配置步骤

  1. 定义描述符数组:在非缓存内存区域(如 SRAM)中分配 tcd_t chain_tcd[3]。
  2. 填充 TCD 字段:为每个 TCD 设置源地址、目的地址、传输计数、数据宽度。
  3. 设置链接指针:将 tcd[0].DLAST_SGA 指向 &tcd[1],tcd[1].DLAST_SGA 指向 &tcd[2],最后一个 TCD 的 DLAST_SGA 置 0。
  4. 使能通道:将第一个 TCD 加载到 DMA 通道寄存器,设置 CSR.START 位。
// 链式传输描述符配置示例
#define CHAIN_LEN 3
tcd_t chain_tcd[CHAIN_LEN] __attribute__((section(".sram_data")));

// 初始化 TCD[0]
chain_tcd[0].SADDR = (uint32_t)&adc_result[0];
chain_tcd[0].DADDR = (uint32_t)&ram_pool_a;
chain_tcd[0].NBYTES = 4;
chain_tcd[0].CITER = 8;
chain_tcd[0].BITER = 8;
chain_tcd[0].DLAST_SGA = (uint32_t)&chain_tcd[1];  // 链接到下一个 TCD

// 初始化 TCD[1]
chain_tcd[1].SADDR = (uint32_t)&spi_rx_fifo;
chain_tcd[1].DADDR = (uint32_t)&ram_pool_b;
chain_tcd[1].NBYTES = 4;
chain_tcd[1].CITER = 16;
chain_tcd[1].BITER = 16;
chain_tcd[1].DLAST_SGA = (uint32_t)&chain_tcd[2];

// 初始化 TCD[2](最后一个)
chain_tcd[2].SADDR = (uint32_t)&timer_capture;
chain_tcd[2].DADDR = (uint32_t)&ram_pool_c;
chain_tcd[2].NBYTES = 2;
chain_tcd[2].CITER = 4;
chain_tcd[2].BITER = 4;
chain_tcd[2].DLAST_SGA = 0;  // 终止链
中断协同设计:链式传输通常只在最后一个 TCD 完成后触发中断,前序 TCD 不使能中断(CSR.INTMAJOR = 0)。这样可以减少中断次数,CPU 只需在整条链完成后一次性处理所有数据。中断优先级建议设置为中高(如优先级 4~6),确保链式传输的实时性。

3.6 传输类型选择决策树

在实际项目移植中,可根据以下条件快速选择传输类型:

DMA 传输类型选择决策树 传输需求 仅需 1 次搬运? 单次传输 需要周期性重复? 循环传输 多段不同配置? 链式传输 否则 → 块传输 提示:链式传输可组合单次、块和循环模式,实现任意复杂传输序列

3.7 项目移植中的常见问题

问题现象 可能原因 解决方案
循环传输地址不回绕 DLAST_SGA 偏移量计算错误 确保偏移量 = - (NBYTES × CITER)
链式传输卡在第一个 TCD DLAST_SGA 指向无效地址或对齐错误 检查描述符地址 32 位对齐,且位于可访问内存区域
中断触发频率异常 循环传输中 CSR.INTMAJOR 误配置为每个 Minor Loop 触发 确认 CSR.INTHALF 和 CSR.INTMAJOR 位设置正确
块传输数据错位 源/目的地址递增方向与缓冲区布局不匹配 检查 SOFF、DOFF、SLAST、DLAST_SGA 的符号和数值
关键提醒:在 S32K3 项目移植中,如果从 S32K1 或 Kinetis 系列迁移,注意 eDMA 的 TCD 结构体字段布局与旧版 DMA 不完全兼容。特别是 DLAST_SGA 在链式模式下的语义变化,务必参考 S32K3 RM(参考手册)第 18 章 eDMA 描述符表定义。