【EDMA】DMA_TCDn_NBYTES寄存器的多种映射解析

本文详细解析了K64的EDMA中DMA_TCDn_NBYTES寄存器在小循环映射功能开启与关闭两种情况下的用法区别,介绍了小循环映射功能如何影响地址偏移及NBYTES字段的配置。

背景

K64的EDMA一章中,在DMA_CR寄存器的总体说明中介绍了小循环(Minor)的详细工作过程,同时还说明了打开和关闭小循环映射(在不同DMA_CR[EMLM]值)的情况下,对DMA_TCDn_NBYTE寄存器的不同用法。

  • DMA_TCDn_NBYTES_MLNO
  • DMA_TCDn_NBYTES_MLOFFYES
  • DMA_TCDn_NBYTES_MLOFFNO

解析

关于EDMA地址偏移功能(大小循环的OFFSET),源地址和目的地址的地址指针寄存器即使在没有任何OFFSET的情况下也会自动递增并回存,所谓的偏移,都是在自动递增之后再进行额外的地址计算。即,若没有指定偏移,则EDMA的地址指针寄存器是连续递增的,若有指定的偏移,则本次大小循环结束之后,在正常的递增之外再进行地址跳跃,在下一次大小循环之前重新设定地址指针。

在默认情况下,小循环映射功能是不启用的(DMA_CR[EMLP]=0),即,DMA_TCDn_NBYTES寄存器就当成一个基本的用来设定NBYTES的寄存器来使用,不能使用小循环偏移功能让源地址和目标地址进行大尺度跳跃。此时,所有TCD中的DMA_TCDn_NBYTES寄存器将用完整的32 bit描述一个小循环(响应一个传输请求)连续传输的字节数。传完这NBYTES个字节之后,源地址和目标地址不会自动回到最初的位置。在这种情况下,DMA_TCDn_NBYTES寄存器使用一个别名——DMA_TCDn_NBYTES_MLNO(Minor Loop NO)。

PS:即使不使用小循环偏移,用先天的大循环偏移也能搞定将地址回到最初位置的问题。

当启用了小循环映射功能(DMA_CR[EMLP]=1)时,所有TCD中的DMA_TCDn_NBYTES中字段设置发生了变化:

字段描述
SMLOESource Minor Loop Offset Enable
DMLOESource Minor Loop Offset Enable
MLOFFMinor Loop OFFset,有符号数,指定小循环结束后偏移的地址跨度
NBYTES小循环内部连续传输的字节数



一旦DMA_CR[EMLM]=1,那么SMLOE和DMLOE的位置就必须预留出来,剩下的30个bit的空间的分化再由对SMLOE和DMLOE的使用决定。当MLOFF或DMLOE任意一个被使能,即启用小循环偏移,MLOFF字段将被预留出来专门存放小循环的偏移跨度,最后剩下的10 bit留给NBYTES字段用于存放每次小循环连续传输的字节数,此时DMA_TCDn_NBYTES寄存器的使用别名DMA_TCDn_NBYTES_MLOFFYES(Minor Loop yes with OFFset YES)。否则,除去因为DMA_CR[EMLM]=1而预留的SMLOE和DMLOE空位,在不使用小循环偏移跳转功能的前提下,预留MLOFF字段也没有意义,索性将其余的位置全部作为NBYTES使用,此时DMA_TCDn_NBYTES寄存器的使用别名DMA_TCDn_NBYTES_MLOFFNO(Minor Loop yes with OFFset NO)。

实际上,这里分别创建SMLOE和DMLOE两个使能控制位意义不大,因为只要它们任何一个功能位被使能,源地址和目标地址指针的偏移功能就被同时都使能了,并且这两个地址指针只能使用同一个偏移跨度,即MLOFF字段中的值。

你给出的这行代码: ```c EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA, EDMA3_CHA_UART2_TX, EDMA3_CHA_UART2_TX, EVT_QUEUE_NUM); ``` 是一个典型的 **TI EDMA3 驱动中请求 DMA 通道的函数调用**,我们来详细分析它的含义、参数、使用场景以及注意事项。 --- ## ✅ 函数原型 ```c Int32 EDMA3RequestChannel( volatile EDMA3_CCRL_Regs *pEdmaRegs, EDMA3ChannelType chType, Uint8 *pChNum, Uint8 *pTccNum, Uint16 evtNum ); ``` | 参数 | 说明 | |------|------| | `pEdmaRegs` | EDMA 控制器寄存器基地址 | | `chType` | 通道类型:DMA、QDMA、LINK 等 | | `pChNum` | 输出参数:分配的通道号 | | `pTccNum` | 输出参数:分配的 TCC 号 | | `evtNum` | 事件号,用于事件触发模式;如果是手动触发,可以设为 0 | --- ## 📌 你给出的代码解析 ```c EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA, EDMA3_CHA_UART2_TX, EDMA3_CHA_UART2_TX, EVT_QUEUE_NUM); ``` ### ❗问题:你传入的是 **值** 而不是 **指针** ```c Uint8 *pChNum, Uint8 *pTccNum ``` 这两个参数是**输出参数(指针)**,用于接收分配的通道号和 TCC 号。你传入的是常量值 `EDMA3_CHA_UART2_TX`,而不是指向 `Uint8` 的指针,这样会导致**未定义行为或编译错误**。 --- ## ✅ 正确的使用方式 ```c Uint8 chNum, tccNum; EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA, &chNum, // 正确:传入地址 &tccNum, // 正确:传入地址 EVT_QUEUE_NUM); // 事件号 ``` - `&chNum`:分配的通道号(如 4、5、6 等) - `&tccNum`:分配的 TCC 号(用于中断) - `EVT_QUEUE_NUM`:事件号,如 `SPI_EVT_RX_FULL` 或 UART 的 `UART_EVT_TX_DONE` --- ## ✅ 强制使用特定通道号(不推荐) 如果你**确定某个通道未被使用**,并且想**强制绑定特定通道号**,可以这样写: ```c Uint8 chNum = EDMA3_CHA_UART2_TX; Uint8 tccNum = EDMA3_CHA_UART2_TX; // 手动映射事件号到通道 EDMA3MapChannel(SOC_EDMA30CC_0_REGS, chNum, EDMA3_MAP_2_EVT, EVT_QUEUE_NUM); // 设置 PaRAM EDMA3SetPaRAM(...); // 启动传输 EDMA3EnableTransfer(...); ``` 但这种方式需要你自己确保通道没有被占用,**不推荐在通用代码中使用**。 --- ## 🧠 使用场景分析 | 场景 | 是否推荐使用 `EDMA3RequestChannel()` | 原因 | |------|--------------------------------------|------| | 通用驱动开发 | ✅ 是 | 自动分配空闲通道,避免冲突 | | 固定通道绑定 | ❌ 否 | 易冲突,需手动管理 | | 多模块共享 EDMA | ✅ 是 | 防止通道冲突 | | 调试/测试 | ✅ 可以先用自动分配,再锁定通道号 | --- ## 🧪 示例:UART2 TX 使用 EDMA 传输 ```c Uint8 chNum, tccNum; // 请求一个 EDMA 通道并绑定 UART2 TX 事件 EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA, &chNum, &tccNum, UART_EVT_TX_DONE); // 配置搬运参数 EDMA3CCPaRAMEntry paramSet; paramSet.srcAddr = (unsigned int)(UART2_BASE + UART_O_DR); // UART 数据寄存器 paramSet.destAddr = (unsigned int)txBuffer; // 发送缓冲区 paramSet.aCnt = 1; // 每次传输 1 字节 paramSet.bCnt = strlen(txBuffer); // 总共搬运多少次 aCnt paramSet.cCnt = 1; paramSet.opt = (EDMA3_OPT_TCINTEN_YES << EDMA3_OPT_TCINTEN_SHIFT) | (tccNum << EDMA3_OPT_TCC_SHIFT) | (EDMA3_OPT_SYNCDIM_A << EDMA3_OPT_SYNCDIM_SHIFT); EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, chNum, &paramSet); // 启动手动触发传输 EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, chNum, EDMA3_TRIG_MODE_MANUAL); ``` --- ## ✅ 总结 | 问题 | 回答 | |------|------| | `EDMA3RequestChannel()` 的参数是值还是指针? | ✅ 是指针,用于返回分配的通道号和 TCC 号 | | 传入 `EDMA3_CHA_UART2_TX` 是否正确? | ❌ 不正确,应传地址 `&chNum` | | 是否可以强制使用特定通道? | ✅ 可以,但需调用 `EDMA3MapChannel()` | | 事件号是什么? | ✅ 外设发出的信号,用于触发 EDMA 传输 | | `EVT_QUEUE_NUM` 是什么? | ✅ 事件队列编号,决定事件优先级 | --- ##
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值