1. 项目概述
在嵌入式系统,尤其是像TMS320DM35x这类面向多媒体处理的数字媒体片上系统(DMSoC)中,数据搬移的效率直接决定了整个系统的性能上限。当CPU深陷于从外部SDRAM搬运一帧图像数据到内部缓存,或者忙于响应串口接收到的每一个字节时,它就无法专注于核心的信号处理或算法运算。这时,直接内存访问(DMA)技术就成为了解放CPU、提升系统并行处理能力的关键。而EDMA3(Enhanced Direct Memory Access 3),作为德州仪器(TI)在经典EDMA2基础上的重大演进,将DMA的能力提升到了一个全新的维度。它不再仅仅是一个简单的“搬运工”,而是一个拥有独立“大脑”(通道控制器)和高效“执行单元”(传输控制器)的智能数据调度系统。
本文旨在为你彻底拆解TMS320DM35x上的EDMA3控制器。我不会仅仅停留在手册的翻译层面,而是结合我多年在DSP平台上进行音视频编解码、高速数据采集等项目的实战经验,带你深入理解其双核架构(EDMA3CC与EDMA3TC)如何协同工作,如何通过参数RAM(PaRAM)实现极其灵活的三维传输定义,以及如何利用事件、队列和中断构建高效、可靠的数据流。无论你是正在为视频处理管线设计DMA数据流,还是试图优化音频接口的实时性,亦或是被复杂的传输链和中断配置所困扰,这篇文章都将提供从原理到寄存器操作,再到避坑经验的完整指南。
2. EDMA3控制器架构深度解析
理解EDMA3,首先要摒弃将其视为单一功能模块的旧观念。它是一个由EDMA3通道控制器(EDMA3CC)和EDMA3传输控制器(EDMA3TC)组成的协同系统。你可以把EDMA3CC想象成项目的“调度中心”或“指挥塔”,而EDMA3TC则是前线负责具体“搬运作业”的“工程队”。调度中心不直接搬东西,它只负责接收任务订单(事件)、规划任务细节(访问PaRAM)、并将派工单(传输请求TR)分发给合适的工程队。这种职责分离的架构,是实现高并发和多优先级调度的基础。
2.1 核心组件:通道控制器(EDMA3CC)详解
EDMA3CC是与程序员交互的主要界面,是整套系统的“大脑”。它的核心职责是事件处理和任务派发。
2.1.1 参数RAM(PaRAM):传输任务的蓝图库
PaRAM是EDMA3的灵魂所在,所有传输任务的“配方”都存储在这里。TMS320DM35x的EDMA3CC提供了128个PaRAM集合(PaRAM Set),每个集合包含8个32位字(共32字节),完整定义了一次传输的所有参数。
注意:PaRAM在物理上是一块专用的RAM,但其内容需要通过内存映射寄存器(MMR)的地址进行读写。编程时,你操作的是PaRAM对应的一组寄存器地址,而非直接操作一块内存。
一个完整的PaRAM Set包含以下关键字段(具体位域定义见第4章寄存器描述):
- OPT:通道选项。这是最重要的寄存器之一,定义了传输的同步类型(A/AB)、源/目的地址更新模式、中断使能、传输完成码(TCC)等。
- SRC/DST:源地址和目的地址。支持字节寻址。
- A_B_CNT:A维度和B维度的计数。ACNT定义了一次原子传输的字节数(通常是数据元素的大小,如一个32位整数就是4字节);BCNT定义了A维度传输需要重复的次数。
- SRC_DST_BIDX:源和目的地址的B维度索引。在完成一次ACNT传输后,源和目的地址根据此索引值进行偏移,以指向下一个数据块。
- LINK_BCNTRLD:链接地址和B计数重载值。用于实现传输链的自动重载。
- SRC_DST_CIDX:源和目的地址的C维度索引。在完成一组B维度的传输(即BCNT次ACNT传输)后,地址根据此索引值进行偏移。
- CCNT:C维度的计数。定义了整个三维传输需要重复的“帧”数。
2.1.2 事件处理与队列机制
EDMA3CC内部有复杂的事件处理逻辑,用于接收和排序各种传输请求:
- 事件源:事件可以来自外部外设(如串口接收完成)、软件手动写入事件置位寄存器(ESR)、或者由其他DMA传输完成触发的链式事件。
- 事件寄存器(ER)与使能寄存器(EER):发生的事件首先被记录在ER中,但只有相应位在EER中被使能的通道,其事件才会被进一步处理。这提供了事件过滤的能力。
- 事件队列:TMS320DM35x的EDMA3CC通常包含两个事件队列(Queue 0和Queue 1)。每个队列都有一个深度(例如16个条目)。被使能的事件会根据其通道映射到的队列编号,进入相应的队列等待处理。队列实现了事件的缓冲,防止在高事件率下丢失事件。
- 传输请求(TR)提交:EDMA3CC从事件队列中取出事件,根据事件对应的通道号找到其PaRAM Set,生成一个详细的传输请求(TR),然后提交给与该队列绑定的EDMA3TC去执行。
2.1.3 完成检测与中断逻辑
当EDMA3TC完成一次传输(或一组传输中的某个阶段),它会向EDMA3CC报告完成状态。EDMA3CC的完成检测模块负责:
- 更新PaRAM:对于非一次性传输(如使用链接或重载),EDMA3CC会根据OPT中的配置,自动更新当前PaRAM Set中的地址和计数,为下一次传输做好准备。
- 触发链式事件:如果OPT中设置了链式触发(Chaining),当传输完成码(TCC)与某个通道的链事件映射匹配时,EDMA3CC会自动为该通道生成一个事件,从而实现传输的自动串联。
- 触发中断:如果OPT中使能了传输完成中断,并且传输完成码(TCC)与中断映射寄存器匹配,EDMA3CC会向CPU发出中断信号(EDMA3CC_INT[0]),通知CPU一批数据已就绪。
2.2 核心组件:传输控制器(EDMA3TC)详解
如果说EDMA3CC是聪明的指挥者,那么EDMA3TC就是力大无穷且高效的执行者。TMS320DM35x通常配备两个EDMA3TC实例(TC0和TC1),它们可以并行工作。
2.2.1 工作模式与优先级
每个EDMA3TC从EDMA3CC接收TR。一个TR本质上描述了一个二维传输(ACNT * BCNT)。EDMA3TC会将其分解为一系列对系统互联网络的读/写命令。
- 并行处理:每个TC拥有独立的64位读端口和64位写端口,可以高效地进行数据搬移。系统设计时,可以将高带宽需求的外设(如DDR控制器)与特定的TC绑定,以优化数据通路。
- 优先级:TC本身具有可编程的系统优先级。此外,从CC发来的TR也带有优先级(由队列优先级和通道优先级决定)。TC内部会据此调度多个并发的传输请求。
2.2.2 传输的原子性
EDMA3TC保证单个ACNT传输的原子性。也就是说,一次ACNT字节的传输是不可分割的,这在对一致性有要求的场景(如操作FIFO或特定外设寄存器)中至关重要。
2.3 同步传输类型:A同步与AB同步的本质区别
这是EDMA3编程中最核心的概念之一,决定了“一个事件能搬多少数据”。
2.3.1 A同步传输(A-synchronized Transfer)
在这种模式下,一个触发事件只导致一次ACNT字节的传输。
- 过程:事件到来 -> EDMA3CC提交一个TR(包含ACNT, BCNT, CCNT)给TC -> TC执行一次ACNT字节的传输 -> 更新SRC/DST地址(+BIDX),BCNT减1。
- 何时完成:只有当BCNT减到0时,才认为这一“组”传输完成,此时会更新C维度(地址+CIDX,CCNT减1),并可能触发链式事件或中断。
- 适用场景:非常适合服务那些产生频繁、小数据量事件的外设。例如,一个音频串口(ASP)每收到一个左声道样本(比如32位)就产生一个事件。使用A同步,每个事件只传输一个样本(ACNT=4字节),BCNT设置为音频帧的样本数,CCNT设置为通道数或缓冲区块数。这样,CPU只需要在整帧音频传输完成(BCNT*CCNT个事件后)时被中断一次,极大地减轻了负担。
2.3.2 AB同步传输(AB-synchronized Transfer)
在这种模式下,一个触发事件会导致一整组B维度的传输(即BCNT次ACNT传输)被一次性提交和执行。
- 过程:事件到来 -> EDMA3CC提交一个TR给TC -> TC连续执行BCNT次ACNT字节的传输 -> 在一次事件处理中,BCNT就直接减到0,并更新C维度。
- 何时完成:一次事件就完成了B维度,所以完成检测和可能的链式触发/中断发生在每个事件之后(针对C维度的进度)。
- 适用场景:适合处理低频、但每次需要搬运一块数据的情况。例如,一个图像传感器(如CMOS)在完成一行像素的曝光和读出后,产生一个行有效事件。使用AB同步,可以将ACNT设置为一个像素的字节数,BCNT设置为一行的像素数。这样,一个事件就能搬完一整行图像数据,效率极高。
实操心得:选择A同步还是AB同步,是设计DMA数据流的第一步。一个常见的误区是认为AB同步一定比A同步“快”。实际上,对于高频小数据量事件,使用AB同步会导致EDMA3CC和TC长时间占用总线搬运大量数据,可能阻塞其他更紧急的传输请求,并增加事件响应延迟。而A同步虽然每个事件处理的数据量小,但响应更及时,总线上是细水长流式的传输,对系统实时性更友好。务必根据外设事件的特性和系统整体带宽来权衡。
3. 参数RAM(PaRAM)的配置艺术与传输链设计
仅仅理解架构和同步类型还不够,真正让EDMA3发挥威力的,是如何巧妙地配置PaRAM来描绘复杂的传输路径,以及如何通过“链接”(Linking)和“链式”(Chaining)让多个传输自动衔接,形成高效的数据流水线。
3.1 PaRAM Set字段的协同工作原理解析
让我们通过一个具体的例子,将PaRAM中的各个字段串联起来。假设我们需要将一块存储在外部DDR中的二维图像数据(假设为320x240的RGB565图像,每个像素2字节)搬运到内部SRAM中进行处理,并且希望按行连续存放。
- 目标:搬运一帧图像(240行 x 320列像素)。
- 数据视图:这是一个三维结构。
- A维度(ACNT):一个像素的大小 = 2字节。
- B维度(BCNT):一行的像素数 = 320。
- C维度(CCNT):总行数 = 240。
- PaRAM配置(假设使用AB同步):
OPT: 同步类型 = AB同步,目的地址模式 = 递增,传输完成中断使能,TCC=0。SRC: 源起始地址(DDR中图像数据的起始地址)。DST: 目的起始地址(SRAM中缓冲区的起始地址)。A_B_CNT: ACNT = 2, BCNT = 320。SRC_DST_BIDX: SRCBIDX = 2, DSTBIDX = 2。每传输一个像素(ACNT),源和目的地址都+2,指向下一个像素。LINK_BCNTRLD: 暂时设为0xFFFF(或一个无效链接地址),表示不链接。SRC_DST_CIDX: SRCCIDX = 3202 = 640, DSTCIDX = 3202 = 640。当完成一行(BCNT次传输)后,地址需要跳过这一行,指向下一行的开头。由于BIDX已经在每次传输后递增了地址,所以CIDX需要补偿一行中已递增的部分,即BCNT * (元素大小)。这里320 * 2 = 640。CCNT: 240。
传输过程模拟:
- 一个外部事件(或手动触发)到来。
- EDMA3CC读取该PaRAM Set,生成一个TR:传输
ACNT*BCNT = 2*320 = 640字节。 - EDMA3TC执行这个TR:从SRC开始,连续读取640字节(一行数据),并写入DST开始的地址。在这个过程中,每读/写2字节,地址自动按BIDX递增。
- 本次AB同步传输完成。EDMA3CC自动进行C维更新:CCNT减1(变为239),SRC和DST地址分别加上CIDX(640),指向下一行的起始位置。
- 由于CCNT不为0,PaRAM Set中的地址和CCNT已被更新,等待下一个事件来触发下一行的传输。
- 当第240个事件到来,完成最后一行传输后,CCNT减为0。此时,如果OPT中使能了中断且TCC匹配,则产生传输完成中断,通知CPU一整帧图像已就绪。
3.2 链接(Linking)机制:实现传输上下文的自动切换
在上面的例子中,我们完成一帧传输后,PaRAM Set中的SRC和DST地址已经指向了帧尾。如果要传输下一帧,我们需要CPU重新初始化PaRAM Set的地址和计数,这会产生软件开销和延迟。链接机制就是为了解决这个问题。
链接的原理:在PaRAM Set的LINK_BCNTRLD字段中,存放另一个PaRAM Set的地址(索引)。当当前传输完全完成(即CCNT也减为0)时,EDMA3CC会自动将LINK_BCNTRLD指向的那个PaRAM Set的内容(全部8个字)加载到当前通道的PaRAM Set中,覆盖旧参数。
应用场景——乒乓缓冲(Ping-Pong Buffering): 这是链接最经典的应用。在需要连续处理数据流(如音频播放、视频显示)时,我们准备两个缓冲区:Ping和Pong。
- 配置两个PaRAM Set:Set_Ping 和 Set_Pong。它们的参数基本相同,但目的地址分别指向Ping缓冲区和Pong缓冲区。
- 将Set_Ping的
LINK_BCNTRLD指向Set_Pong的地址。 - 将Set_Pong的
LINK_BCNTRLD指向Set_Ping的地址。 - 启动Set_Ping对应的DMA通道。
工作流程:
- 第一次传输:使用Set_Ping参数,数据写入Ping缓冲区。传输完成后,触发链接,Set_Pong的参数被加载到该通道。
- 第二次传输:使用Set_Pong参数(目的地址已变为Pong缓冲区),数据写入Pong缓冲区。传输完成后,再次链接,Set_Ping的参数被加载回来。
- 如此循环往复。
在这个过程中,CPU可以在DMA向Ping缓冲区写数据时,处理Pong缓冲区中已就绪的数据,实现了数据传输和处理的完美并行,消除了缓冲区切换的等待时间。
注意事项:链接操作发生在传输完全完成(即CCNT耗尽)之后。对于A同步传输,这意味着需要BCNT*CCNT个事件;对于AB同步,需要CCNT个事件。务必确保链接的PaRAM Set是预先配置好的有效集合,否则会导致不可预知的行为。
3.3 链式(Chaining)机制:实现传输内部的自动触发
链式与链接容易混淆,但作用层面不同。链接是“传输完成后替换整个参数集”,而链式是“在传输过程中的某个节点,自动触发另一个通道的传输”。
链式的原理:每个DMA通道在OPT字段中都可以指定一个传输完成码(TCC, 0-63)。同时,EDMA3CC有一个链式事件寄存器(CER),可以将某个TCC值映射到另一个DMA通道。当一次传输(或一次传输中的某个中间完成点)生成指定的TCC时,EDMA3CC会自动为映射到的通道置位一个事件,从而触发该通道的传输。
关键点——中间完成(Intermediate Completion): 这是链式的精髓。对于A同步传输,每完成一次ACNT传输(即BCNT减1),都可以被认为是一个“中间完成”点,并产生TCC。通过合理设置BCNTRLD(在LINK_BCNTRLD字段的低16位)和OPT中的TCC值,可以实现复杂的触发逻辑。
应用场景——多级数据处理流水线: 假设有一个数据处理流程:数据从外设A采集到缓冲区BUF1,然后需要经过一个硬件加速器C处理,结果存到BUF2,最后再通过外设D发送出去。
- 配置通道0:服务外设A,AB同步,BCNT=100,TCC=1。完成100个数据采集后,产生TCC=1。
- 配置通道1:服务加速器C,将BUF1的数据搬至加速器输入FIFO。在CER中设置TCC=1映射到通道1。这样,通道0完成时自动触发通道1。
- 配置通道2:由加速器C的完成事件触发,将处理结果从加速器输出FIFO搬至BUF2。
- 配置通道3:服务外设D,将BUF2的数据发送出去。可以通过通道2的完成TCC来链式触发通道3。
通过链式机制,我们构建了一个全自动的、由硬件事件驱动的数据处理流水线,CPU几乎不需要介入管理数据流,极大地提高了系统效率和实时性。
3.4 空传输(Null)与伪传输(Dummy)的陷阱
手册中特别区分了Null PaRAM Set和Dummy PaRAM Set,这在实际调试中是个大坑。
- Null传输:当一个PaRAM Set中ACNT、BCNT、CCNT全部为0时,如果该通道被触发,EDMA3CC会将其视为错误,并可能置位错误中断。这是一个需要避免的配置错误。
- 伪传输(Dummy):当一个PaRAM Set中至少有一个计数为0,但并非全为0时(例如ACNT=0, BCNT=10, CCNT=1),触发该通道会导致EDMA3CC提交一个传输请求,但实际传输字节数为0。这不是错误,但会消耗总线周期和TC资源。有时可以用于“占位”或产生纯事件(不搬数据)来触发链式操作,但需谨慎使用。
避坑指南:在初始化PaRAM Set时,务必确保所有计数字段被正确赋值。一个常见的错误是只配置了SRC/DST地址,但忘记写计数寄存器(默认为0),导致通道触发后产生Null传输错误,程序陷入调试困境。建议编写一个PaRAM初始化函数,对所有8个32位字进行显式写入。
4. DMA通道与QDMA通道的选用策略
EDMA3提供了两种触发机制的通道:64个DMA通道和8个QDMA通道。理解它们的区别是进行高效编程的关键。
4.1 DMA通道:事件驱动的标准模式
DMA通道是我们最常使用的类型。它的触发依赖于明确的事件源:
- 外部事件:由片内外设(如UART、SPI、Timer)产生的硬件信号。这是最常用的方式,用于服务外设的数据收发。
- 手动触发:CPU通过写事件置位寄存器(ESR)来软件触发一个DMA传输。适用于需要CPU发起的一次性大数据块搬运。
- 链式触发:由其他DMA通道的传输完成码(TCC)触发,如前所述。
DMA通道需要预先将通道号与特定事件源绑定(通过事件映射寄存器),并且需要使能该通道的事件(EER)和可能的中断。它的配置相对固定,适合规律性、周期性或外设驱动的数据流。
4.2 QDMA通道:写触发的高效“快捷方式”
QDMA通道的触发机制非常独特:当你向该QDMA通道对应的PaRAM Set中的特定“触发字”(Trigger Word)执行一次写操作时,传输立即被触发。
工作原理:
- 通过QDMA通道映射寄存器(QCHMAPn),你将一个QDMA通道(0-7)映射到128个PaRAM Set中某一个Set的特定条目(比如第3个字,即DST地址字段)。
- 你像配置普通DMA一样,完整地配置好这个PaRAM Set的所有参数。
- 当你的程序需要触发这次传输时,不需要去操作复杂的事件寄存器,只需要向这个被映射的“触发字”地址执行一次写操作(写任何值均可)。这次写操作本身就会作为触发事件,启动QDMA传输。
QDMA的优势:
- 极低的触发开销:触发一个QDMA只需要一次内存写操作,比配置和触发DMA通道(可能需要写多个寄存器)更快捷。
- 灵活的触发源:触发可以来自任何能执行内存写的master,包括另一个DMA通道(通过链式或链接后的传输来写触发字)、协处理器等。
- 适用于不规则请求:非常适合处理CPU非周期性的、零散的DMA传输需求,比如在算法中动态决定需要搬运哪块数据。
QDMA的局限性:
- 通道数量较少(通常8个)。
- 触发方式单一,只能由写操作触发,无法直接响应外设硬件事件。
4.3 DMA与QDMA的选择对比
| 特性 | DMA通道 | QDMA通道 |
|---|---|---|
| 触发方式 | 外部事件、软件手动、链式事件 | 对特定PaRAM条目的写操作 |
| 触发延迟 | 较低,但需事件检测和队列调度 | 极低,写操作即触发 |
| 适用场景 | 规律的、由外设硬件事件驱动的流式数据传输(音频、视频流) | 非周期的、由软件或其它DMA动态发起的块数据传输 |
| 配置复杂度 | 需配置事件映射、使能等 | 配置更集中(主要在PaRAM和QCHMAP) |
| 通道资源 | 较多(64个) | 较少(8个) |
实操心得:在实际项目中,我通常用DMA通道来处理所有与硬件外设相关的、有固定节奏的数据流(如摄像头采集、音频编解码器接口)。而QDMA通道则被预留出来,用于处理一些“后台”任务,比如:当图像处理算法识别出某个区域需要重点处理时,用QDMA快速将该区域数据从DDR搬移到更快的内部存储器;或者用于在复杂传输链的最后阶段,通过写触发字来启动一个清理或通知操作。将两者结合使用,能最大化EDMA3的灵活性。
5. 中断与错误处理:构建鲁棒的数据传输系统
配置好传输参数只是成功了一半,如何让CPU及时知道传输完成,以及如何应对传输过程中可能出现的错误,是保证系统稳定性的关键。
5.1 传输完成中断的精确定义
EDMA3的中断并非在“每次DMA请求被处理”时产生,其产生条件要严格得多。
- 使能配置:在PaRAM的OPT字段中,有一个
TCINTEN位。只有将此位置1,本次传输才具备产生完成中断的资格。 - 完成码(TCC):同样在OPT字段中,有一个
TCC字段(通常6位,0-63)。这个数字是本次传输的“标签”。 - 中断映射:在EDMA3CC的中断映射寄存器中,可以将特定的TCC值(0-63)映射到具体的硬件中断线(如EDMA3CC_INT[0]的某个位)。一个中断线可以接收多个TCC的完成事件。
- 中断产生条件:当一次传输完全完成(对于A同步,是CCNT减到0;对于AB同步,也是CCNT减到0),并且其
TCINTEN=1,EDMA3CC就会检查其TCC值。如果该TCC被映射到了某个已使能的中断线上,则相应的中断挂起位(IPR)被置位,进而向CPU发出中断请求。
重要概念:传输完成 vs. 传输提交务必区分:EDMA3CC向EDMA3TC提交一个传输请求(TR)很快,但这不产生中断。中断是在整个三维传输(ACNTBCNTCCNT字节)全部搬运完成后才产生的。对于A同步的长序列,中断频率很低,这正是其优势。
5.2 错误中断与调试
EDMA3具有完善的错误检测机制,主要错误类型包括:
- 空传输错误:触发了ACNT=BCNT=CCNT=0的通道。
- 事件丢失错误:某个通道的事件发生时,但其事件队列已满,导致事件被丢弃。
- 传输控制器错误:EDMA3TC在访问非法地址或遇到总线错误时报告。
错误中断的使能和状态查看有独立的寄存器组(如CCERR, ERRSTAT)。在调试阶段,务必使能错误中断,并在中断服务程序(ISR)中读取这些寄存器来定位问题。手册附录A的调试清单(Debug Checklist)非常有用,应作为开发流程的一部分。
5.3 中断服务程序(ISR)编写要点
在ISR中,你需要:
- 确定中断源:读取中断挂起寄存器(IPR)或传输完成状态寄存器,判断是哪个TCC或哪个通道完成了传输。
- 清除中断标志:向中断清除寄存器(ICR)的相应位写1,以清除挂起状态。注意:对于某些平台,可能需要先读取IPR,然后将读回的值写入ICR,这是一种安全的清除方式。
- 处理数据:对DMA传输完成的数据缓冲区进行操作(如处理、转发、释放)。
- 重新配置(如需要):对于循环缓冲或乒乓缓冲,可能需要在ISR中更新缓冲区指针或重新使能通道(如果传输完成后通道自动禁用)。对于链接机制,通常无需软件干预。
避坑指南:一个常见的错误是中断服务程序执行时间过长,导致错过了后续的DMA完成中断。对于高吞吐率应用,ISR应尽可能短小精悍,只做必要的标志设置和指针切换,将耗时的数据处理移到主循环或低优先级任务中。另外,确保在使能DMA通道和中断之前,所有的PaRAM配置和缓冲区指针都已正确初始化,否则第一个中断可能在你准备好之前就到来,导致访问非法数据。
6. 实战编程:从零构建一个EDMA3传输
理论说得再多,不如动手调一遍。下面我们以TMS320DM35x为例,概述配置一个完整EDMA3传输的软件流程。这里假设使用AB同步,从McASP(音频串口)接收数据到内部存储器。
6.1 硬件与软件准备
- 确认McASP的接收事件(如REVT)已正确路由到EDMA3的某个事件输入(例如,事件8)。
- 在内存中定义好接收缓冲区
audio_buffer[BUFFER_SIZE]。 - 确保EDMA3和McASP的外设时钟已使能。
6.2 PaRAM Set配置步骤
- 确定PaRAM Set索引:例如,我们使用PaRAM Set 0 对应 DMA 通道 8。
- 计算地址:获取
audio_buffer的物理地址(在DSP/ARM上需要注意虚拟地址到物理地址的转换)。 - 填充PaRAM结构体(以下为伪代码,具体寄存器地址请查手册):
// 假设 PaRAM 基地址为 PARAM_BASE volatile uint32_t *param_set = (uint32_t*)(PARAM_BASE + 0 * 32); // Set 0, 每个set 32字节 // OPT: AB同步,目的地址递增,使能传输完成中断,TCC=0 param_set[0] = (0x1 << 2) | (0x1 << 1) | (0x1 << 20) | (0x00 << 12); // 位域请参考手册OPT寄存器 // SRC: McASP数据接收寄存器地址 param_set[1] = MCASP_DATA_RCV_ADDR; // A_B_CNT: ACNT=4字节(32位音频样本), BCNT=128 (一个块包含128个样本) param_set[2] = (128 << 16) | 4; // DST: 内部存储器缓冲区地址 param_set[3] = (uint32_t)audio_buffer; // SRC_DST_BIDX: 源索引=4 (McASP FIFO地址固定),目的索引=4 param_set[4] = (4 << 16) | 4; // LINK_BCNTRLD: 链接到自身(实现乒乓缓冲需链接到另一个set),BCNTRLD=128 param_set[5] = (0xFFFF << 16) | 128; // 高16位为链接地址,0xFFFF表示无链接 // SRC_DST_CIDX: C维度索引,这里我们只做一维AB传输,CCNT=1,所以CIDX可设为0 param_set[6] = 0; // CCNT: 我们只传输一块数据,CCNT=1。如果需要双缓冲,可设为2并配合链接。 param_set[7] = 1;
6.3 EDMA3CC通道与事件配置
- 映射DMA通道到事件:将DMA通道8映射到事件输入8(假设硬件连接如此)。这通常通过事件映射寄存器完成,但某些平台事件与通道号固定对应,需查手册。
- 使能DMA通道事件:设置事件使能寄存器(EER)的第8位为1。
- 将通道关联到事件队列:通过DMAQNUM寄存器,设置通道8使用哪个事件队列(例如Queue 0)。
- 配置中断:将TCC=0映射到某个EDMA3CC中断线,并使能该中断线对应的CPU中断。
6.4 启动传输
- 使能McASP接收:配置McASP使其开始工作并产生接收事件。
- (可选)手动触发:如果需要立即启动,可以向事件置位寄存器(ESR)的第8位写1。
6.5 中断服务程序
void EDMA3_ISR(void) { // 1. 判断中断源,例如检查IPR寄存器 // 2. 清除EDMA3中断挂起位 (写ICR) // 3. 处理 audio_buffer 中的数据 process_audio_data(audio_buffer); // 4. 如果使用简单循环,无需其他操作。如果使用乒乓缓冲,可能需要切换缓冲区指针。 // 5. 清除CPU级中断标志 }7. 性能优化与高级调试技巧
7.1 队列优先级与传输控制器绑定
- 两个事件队列(Queue 0/1)可以设置不同的优先级。将高实时性要求的外设(如音频)通道分配到高优先级队列,将后台大数据搬运通道分配到低优先级队列。
- 每个队列可以绑定到特定的EDMA3TC(TC0/TC1)。如果系统有多个内存端口,可以将访问不同内存域的通道分配到不同的TC,以减少总线冲突。
7.2 利用“伪传输”进行流控在某些特殊场景下,可以配置一个Dummy传输(ACNT=0, BCNT>0)。当它被触发时,会消耗TC资源但不搬运数据,其完成TCC可用于链式触发其他操作。这可以作为一种简单的“延时”或“同步”机制,但需谨慎评估其对总线带宽的占用。
7.3 调试技巧
- 事件丢失监控:定期读取事件丢失寄存器(EMR/QEMR),这在调试初期排查事件是否成功产生或被EDMA3接收时非常有用。
- 队列状态监视:通过队列状态寄存器(QSTATn)可以查看队列的当前深度,判断事件是否积压。
- 传输控制器状态:在复杂传输卡住时,查看EDMA3TC的状态寄存器(TCSTAT)和错误寄存器,可以判断TC是否在忙碌或遇到了总线错误。
- 使用链接进行状态恢复:在复杂的传输链中,如果某个环节出错,可以通过在错误中断ISR中,修改链接地址,将PaRAM Set重载到一个已知的“安全”配置,实现传输链的软复位。
EDMA3是一个功能极其强大的DMA引擎,其学习曲线确实陡峭。我的经验是,不要试图一次性掌握所有高级功能。先从简单的、单一的A同步或AB同步传输开始,确保能够正确搬运数据并产生中断。然后逐步引入链接实现乒乓缓冲,再尝试链式构建简单流水线。每增加一个功能,都充分测试其行为是否符合预期。当你能够熟练运用这些基本组合时,就会发现EDMA3能够以极高的效率替你打理好系统中纷繁复杂的数据流,让CPU专注于真正的计算任务,这才是嵌入式系统性能优化的精髓所在。