1. 项目概述:DMA控制器在嵌入式系统中的核心价值
在嵌入式系统开发中,尤其是涉及高速数据流处理的应用场景,CPU常常被大量、重复的内存拷贝任务所拖累。想象一下,你的CPU就像一个忙碌的快递分拣员,它不仅要处理复杂的计算(比如规划路线、处理客户投诉),还要亲自去仓库里一件件地搬运包裹(数据)。这种模式效率低下,且严重浪费了CPU这个“高薪员工”的宝贵算力。这时,DMA(Direct Memory Access,直接内存访问)控制器就扮演了那个专业的“搬运工”角色。它是一块独立的硬件模块,能够在外设(如ADC、SPI、UART)和内存之间,或者内存与内存之间,直接建立数据传输通道,无需CPU的持续干预。CPU只需要告诉DMA“从哪里搬、搬到哪里、搬多少”,然后就可以去处理其他更重要的任务,等DMA搬完了再通知它一声即可。
然而,用好这个“搬运工”并非易事。它功能强大,但也相对复杂。很多开发者可能只停留在配置源地址、目的地址和传输长度这些基础操作上,却忽略了DMA控制器内部更深层次的机制,这些机制恰恰是优化系统性能、降低功耗和高效调试的关键。比如,当你在用仿真器单步调试程序时,DMA是否应该停止工作?如何在不影响实时性的前提下,让DMA在空闲时进入低功耗状态?当多个外设同时请求DMA服务时,数据会不会在内部“堵车”?这些问题都指向了DMA的三个高级特性:调试模式、电源管理和FIFO缓冲与仲裁机制。
本文将基于一份典型的DMA控制器技术手册(以TI的某款DMA为例),深入解析这三个核心机制。我不会仅仅翻译手册,而是结合我多年在嵌入式实时系统开发中的实际踩坑经验,为你拆解这些功能的设计意图、配置方法以及那些手册上不会写的“实战技巧”。无论你是正在调试一个棘手的DMA数据错位问题,还是在为电池供电的设备苦苦优化功耗,亦或是想榨干总线带宽以提升数据吞吐率,相信接下来的内容都能给你带来直接的帮助。
2. 调试模式:让DMA在关键时刻“听话”
调试是嵌入式开发中不可或缺的一环。当程序运行不如预期,尤其是涉及DMA这种后台异步操作的数据流时,传统的断点调试可能会失效——你暂停了CPU,但DMA可能还在疯狂地搬运数据,导致内存状态被意外修改,问题无从查起。因此,一个可控的调试模式至关重要。
2.1 挂起模式:四种行为,四种策略
DMA控制器通常支持一种称为“挂起”的调试状态。当调试器(如JTAG/SWD)请求系统进入调试模式时,内核可以通知DMA进入挂起状态。关键在于,DMA进入挂起状态后具体怎么做,是可以由软件预先配置的。这通常有四种行为模式:
- 立即停止:在下一个DMA通道仲裁边界立刻停止所有操作。你可以把它理解为“急刹车”。仲裁边界是什么?简单说,就是DMA完成当前最小调度单元(可能是一个数据块或一帧)的传输,准备切换去服务下一个通道的那个时间点。这种模式最“干净”,能瞬间冻结DMA状态,适合检查精确的瞬时状态。
- 完成当前帧后停止:DMA会继续完成当前正在传输的整个“帧”(Frame),然后再挂起。帧是比块更大的一个逻辑单元,通常由多个块(Block)组成。这适用于你不希望打断一个完整数据包传输的场景,比如正在通过DMA接收一帧完整的网络数据包。
- 完成当前块后停止:DMA会继续完成当前正在传输的“块”(Block),然后再挂起。块是比元素(Element)更大的传输单元。这比“完成帧”更精细一些,平衡了调试的即时性和数据完整性。
- 忽略挂起:DMA完全不管调试信号,继续像正常运行时一样工作。这在调试与DMA无关的其他模块时非常有用,可以避免DMA传输被意外打断,影响其他外设的正常工作。
实操心得:如何选择挂起模式?我的经验是,在前期驱动开发和集成阶段,可以设置为“完成当前块后停止”。这既能保证单次传输的完整性,又不会让调试等待太久。当定位一个非常精确的、与时序相关的数据损坏问题时,可以切换到“立即停止”,配合观察点寄存器(后面会讲)来抓现场。而在系统联调,确保DMA数据流不影响其他功能时,可以短暂设为“忽略挂起”。切忌在最终产品代码中保留“忽略挂起”模式,这会让在线调试变得困难。
2.2 观察点与观察掩码寄存器:给DMA装上“监控探头”
如果说挂起模式是让DMA“停下来”,那么观察点寄存器就是告诉它“在什么情况下停下来”。这是DMA调试中最强大的武器之一。
观察点寄存器通常包含一个地址值和一个掩码值。其工作原理类似于一个硬件断点:
- 观察点寄存器:设置一个你关心的目标地址。
- 观察掩码寄存器:决定哪些地址位参与比较。掩码位为1表示需要精确匹配该位,为0则表示“不关心”。
例如,你怀疑数据在写入内存地址0x2000_1000时出错。你可以将观察点寄存器设置为0x2000_1000,并将观察掩码寄存器设置为0xFFFF_FFFF(全匹配)。这样,当DMA的传输地址(无论是源地址还是目的地址,取决于具体实现)匹配到这个精确地址时,DMA就会自动冻结其内部状态(停止传输),并向CPU发出一个调试请求信号。
更强大的用法是利用掩码来监控一个地址范围。比如,你想监控0x2000_1000到0x2000_1FFF这4KB的区域。你可以设置观察点为0x2000_1000,掩码为0xFFFF_F000。这意味着高20位(0x2000_1)必须精确匹配,低12位(0x000)不关心。这样,任何访问该区域的行为都会触发调试事件。
避坑指南:观察点的使用限制首先,不是所有DMA控制器都支持观察点功能,需要查证手册。其次,观察点通常是对地址的监控,而不是数据值。最后,也是最容易忽略的一点:观察点触发并挂起DMA后,来自外部的DMA请求并不会丢失。手册中提到,在挂起期间,DMA控制器会持续采样硬件请求信号,但不会更新“通道挂起寄存器”。一旦挂起结束,所有在挂起期间收到的请求都会反映出来。这意味着你的调试行为不会造成请求遗漏,但可能会改变多个请求之间的相对时序,在分析严格的实时性问题时需要考虑这一点。
3. 电源管理:让DMA在空闲时“打盹”
在电池供电的物联网或便携式设备中,每一微瓦的功耗都至关重要。DMA作为活跃的硬件模块,其功耗管理不容忽视。
3.1 运行模式与睡眠模式
典型的DMA电源管理包含两种模式:
- 运行模式:全功能模式,DMA模块完全上电,随时可以响应请求并执行传输。
- 睡眠模式:一种低功耗状态。当DMA检测到没有任何通道在等待服务(即通道挂起寄存器为空),且当前没有活跃传输时,它可以自动或由软件控制进入睡眠模式,关闭部分内部时钟和电路以节省功耗。
睡眠模式的退出是自动且快速的。一旦有新的DMA请求(硬件或软件触发)到来,DMA会立即唤醒并进入运行模式,开始处理请求。这种设计非常适合间歇性工作的外设,比如周期性采集数据的传感器,DMA可以在数据采集的间隙进入睡眠,从而降低系统平均功耗。
3.2 全局低功耗模式下的协同
嵌入式系统往往有更上层的电源管理策略,比如进入“待机”或“停止”模式。此时,系统电源管理模块会向所有外设(包括DMA)发出一个“全局低功耗模式请求”。
DMA的响应逻辑是:如果当前没有任何未决的DMA请求,它会立即回复一个“确认”信号,表示自己可以安全进入低功耗状态。一旦系统进入该模式,DMA的时钟会被停止。这里有一个关键点:时钟停止意味着DMA无法检测任何外部的硬件DMA请求。因此,如果某个外设希望在系统深度睡眠时还能通过DMA搬运数据,这个设计是不支持的。必须先通过其他方式(如外部中断)唤醒整个设备,恢复时钟,然后DMA才能正常工作。
配置要点与风险使能DMA的睡眠模式通常是通过一个全局控制寄存器位。你需要评估你的应用场景:如果DMA服务的数据流是连续或高频率的,进入和退出睡眠模式带来的开销可能得不偿失。反之,如果DMA长时间空闲,开启睡眠模式的收益非常明显。
特别注意:在调试时,如果开启了睡眠模式,当CPU因断点暂停时,DMA可能因为空闲而进入睡眠。此时通过调试器读取DMA寄存器可能会唤醒它,甚至导致状态异常。建议在深度调试DMA相关代码时,暂时关闭其睡眠功能。
4. FIFO缓冲区与通道仲裁机制:数据流的高速立交桥
DMA控制器内部通常有一个或多个FIFO缓冲区。它的作用类似于一个迷你仓库,临时存放从源端读出的数据,然后再写入目的端。这个设计主要为了解决两个问题:数据打包/解包和总线带宽优化。
4.1 FIFO的基本工作状态与通道切换
以手册中描述的4级深度、64位宽的FIFO为例。它可以暂存最多4个64位的数据。它有两个核心状态:
- 空:FIFO里没有数据。这是通道仲裁发生的时刻。
- 满:FIFO已填满,或者需要读取的元素计数已为零。此时必须停止读操作,防止溢出。
一个关键规则是:DMA通道的切换(仲裁)只能发生在FIFO为空的时候。这很好理解,你不能在搬运一个通道的数据到一半时(FIFO里还有它的数据),突然去处理另一个通道,那会导致数据混乱。因此,仲裁器总是在当前活跃通道的FIFO清空后,才决定下一个服务哪个通道。
4.2 旁路模式:用延迟换取灵活性
FIFO虽好,但引入了一级缓存,也带来了延迟。对于某些对延迟极其敏感的场景,比如需要在两个通道间极速切换,FIFO的排空等待时间可能是不可接受的。为此,DMA提供了旁路模式。
在旁路模式下,FIFO的深度被限制为1个元素。也就是说,DMA从源端读1个元素后,会立即启动向目的端的写入,几乎不缓冲。这使得通道仲裁可以发生在单个源元素粒度上,切换延迟大大降低。
然而,天下没有免费的午餐。旁路模式牺牲了总线利用效率。我们来看手册中给出的对比表格(非旁路模式 vs 旁路模式):
| 读元素大小 | 写元素大小 | 非旁路模式 (读/写事务数) | 旁路模式 (读/写事务数) |
|---|---|---|---|
| 8位 | 8位 | 4 / 4 | 1 / 1 |
| 32位 | 8位 | 1 / 4 | 1 / 4 |
| 64位 | 8位 | 1 / 8 | 1 / 8 |
| 32位 | 32位 | 4 / 4 | 1 / 1 |
分析一下:当读写元素大小相同时(如32位->32位),非旁路模式可以攒够4个元素(填满FIFO)后,发起一次连续的写突发传输,效率很高。而旁路模式读一个写一个,会产生更多次、但更短的总线事务,增加了总线仲裁开销。
当读写元素大小不同时(如64位读,8位写),情况更明显。非旁路模式读1次64位数据(相当于8字节),需要拆成8次8位的写操作。但由于有FIFO,它可以在一次读事务后,慢慢处理这8次写。而在旁路模式下,它读1次64位,就必须立刻开始1次8位写,然后才能进行下一次读,总线被频繁占用,整体吞吐率可能下降。
性能权衡实战如何选择?我的经验法则是:
- 高吞吐、大数据量传输:优先使用非旁路模式。让FIFO发挥缓冲和打包作用,最大化总线突发传输能力,适合内存到内存的大块拷贝或高速ADC数据流。
- 低延迟、多通道快速切换:选择旁路模式。例如,你需要同时服务一个高速SPI发送和一个低速UART接收,且对SPI的数据流中断延迟要求极高,旁路模式可以减少SPI通道的等待时间。
- 读写尺寸不匹配:如果读大、写小(如32位->8位),使用非旁路模式通常更优,因为FIFO缓解了速度不匹配。如果读小、写大,则需要具体分析总线压力。
配置旁路模式通常是通过端口控制寄存器中的一个特定位来实现。开启后,务必重新评估系统的整体带宽和实时性是否仍在可接受范围内。
5. 通道仲裁与链式触发:调度DMA的交通规则
当多个外设同时请求DMA服务时,就需要一套仲裁规则来决定谁先谁后。此外,DMA还支持一种高级功能——通道链式触发,可以实现复杂的传输序列。
5.1 优先级与仲裁边界
DMA控制器通常支持为每个通道设置优先级(高/低)。仲裁器会优先服务所有高优先级通道,然后再服务低优先级通道。在同优先级内,可能是固定优先级(如通道号小的优先)或轮询调度。
如前所述,仲裁只发生在“仲裁边界”。对于支持FIFO的DMA,这个边界就是FIFO为空时。对于旁路模式,边界是每个元素传输完成后。理解这一点对分析多通道DMA的实时性至关重要。一个低优先级但正在传输大块数据的通道,可能会阻塞高优先级通道,直到它当前的数据块传输完成并清空FIFO。
5.2 通道链式触发:让DMA“自助”工作
这是DMA自动化程度很高的一个功能。它允许一个通道传输完成后,自动触发另一个(或一组)通道开始传输,而无需CPU或外部硬件再次发起请求。
实现原理是:在通道的控制寄存器中,有一个“链”字段。你可以将通道B的编号写入通道A的链字段。当通道A完成其传输(帧或块,取决于配置)后,DMA硬件会自动在挂起寄存器中置位通道B的等待位,仿佛有一个内部请求触发了它。
手册中的例子很说明问题:假设通道1、2、4、5被同时触发,而通道3被链式配置到通道1之后。尽管通道3最初未被触发,但当通道1完成后,通道3会被自动加入待处理队列。最终的服务顺序仍然是CH1 -> CH2 -> CH3 -> CH4 -> CH5。这里的关键是,链式触发只是“插入”了一个请求,这个请求依然要服从全局的优先级和仲裁规则。
配置链式触发的注意事项
- 顺序配置:必须在触发第一个DMA请求之前,就完成所有相关通道(包括被链入的通道)的寄存器配置和使能。否则链式触发可能不会生效。
- 避免循环链:严禁配置A链B,B又链A这样的死循环,这会导致DMA逻辑锁死。
- 应用场景:非常适合需要连续进行多段不同数据传输的场景。例如,先从传感器读取一批原始数据到缓冲区A(通道1),处理完成后,再将结果从缓冲区A搬运到发送缓冲区B(通道2)。通过链式触发,CPU只需启动通道1,后续动作全部由DMA自动完成,极大提高了效率。
6. 内存保护与奇偶校验:DMA的“安全卫士”
对于可靠性要求高的系统,DMA不能成为一个“破坏王”。它需要被约束,并且其自身操作的正确性也需要被检查。
6.1 内存保护机制
DMA控制器通常能访问设备的全部地址空间。内存保护机制允许你定义最多数个(例如4个)受保护的内存区域。你可以为每个区域设置访问权限:完全访问、只读、只写、禁止访问。
当DMA试图以违反权限的方式访问受保护区域时(例如向一个“只读”区域写入数据),硬件会触发一个保护错误。DMA会立即停止当前违规的通道,并产生一个中断通知CPU。同时,下一个可用的DMA通道会被继续服务,避免一个通道的错误导致整个DMA控制器瘫痪。
配置技巧与重叠区域处理你需要为关键的系统数据(如操作系统内核数据、安全密钥)或未使用的内存区域设置保护。手册中特别强调了一个细节:如果配置的保护区域地址有重叠,那么编号小的区域(如Region 0)的权限优先级更高。这在配置多个复杂区域时需要仔细规划,避免出现意料之外的权限漏洞。
6.2 奇偶校验与测试
DMA的控制数据包通常存储在内部的RAM中。为了确保这些关键配置信息在存储过程中不发生位翻转,许多DMA控制器支持对控制包RAM进行奇偶校验。
- 工作原理:写入RAM时,硬件会根据写入的数据(通常按字节)自动计算并存储一个奇偶校验位。读取时,重新计算数据的奇偶性,并与存储的校验位比较。如果不匹配,则产生奇偶错误中断,并记录出错地址。
- 错误处理:处理方式取决于读取者。如果是DMA自己在读取控制包时出错,则它发起的传输不会执行。如果是CPU在读取时出错,CPU仍然能拿到数据,但会同时收到错误中断。这有助于区分是瞬时干扰还是存储介质问题。
- 测试模式:更专业的是,硬件提供了“测试模式”。在此模式下,奇偶校验位存储区会被映射到特定的可访问地址。开发者可以手动写入错误的校验位,来模拟一个奇偶错误,从而完整地测试整个错误检测和上报通路是否正常工作。这是功能安全(Functional Safety)相关开发中验证诊断覆盖率的重要手段。
6.3 上电初始化
一个容易被忽视的要点是:芯片上电后,RAM(包括奇偶校验位)的内容是随机的、不确定的。如果你在初始化RAM内容之前就使能了奇偶校验,那么第一次读取RAM时,几乎必然会发生奇偶校验错误。
因此,标准的初始化流程是:先向控制包RAM写入已知的、确定的数据(通常是全0或特定模式),然后再使能奇偶校验功能。在写入数据的过程中,硬件会自动计算并更新对应的奇偶校验位,从而为后续的正确校验打下基础。有些芯片的系统模块还提供SRAM自动初始化功能,可以批量完成这个操作。
7. 核心控制寄存器详解与编程模型
理解了原理,最终要落实到寄存器配置上。DMA的寄存器数量繁多,但可以分类理解。以下是对关键寄存器组的解析和编程思路。
7.1 全局控制与状态寄存器组
这是配置DMA大脑的寄存器。
- GCTRL:全局控制寄存器。包含DMA总使能位、软件复位位,以及前面提到的调试模式选择位。上电后,第一步就是打开DMA总使能。
- PEND:通道挂起寄存器。这是一个状态寄存器,每一位代表一个通道是否有待处理的传输请求(无论是硬件还是软件触发)。只读,用于查询DMA当前的工作负载。
- DMASTAT:通道状态寄存器。指示哪些通道正在活跃传输(即在DMA的执行队列中)。注意,即使DMA被全局禁用或进入调试模式,正在传输的通道状态位依然保持活跃,直到其传输自然完成。
7.2 通道使能与触发控制
这是配置DMA手脚的寄存器,通常成对出现(SET和RESET/CLEAR)。
- HWCHENAS / HWCHENAR:硬件通道使能置位/复位寄存器。某位置1,则允许对应的通道响应硬件外部的DMA请求信号。这是外设使用DMA的前提。
- SWCHENAS / SWCHENAR:软件通道使能置位/复位寄存器。向某位写1,相当于用软件手动触发一次该通道的DMA传输。这在测试或手动启动传输时非常有用。
- DREQASIx:DMA请求分配寄存器。这是将物理的DMA请求线映射到逻辑DMA通道的关键。一个微控制器可能有几十个DMA请求源(如UART0_RX, TIMER1_CH1等),但它们可能共享有限的几条物理请求线输入到DMA控制器。这些寄存器定义了哪条物理请求线触发哪个DMA通道。配置错误会导致外设无法正确触发DMA。
7.3 中断与优先级管理
这是配置DMA如何汇报工作的寄存器。
- GCHIENAS / GCHIENAR:全局通道中断使能寄存器。使能后,对应通道在传输完成(或出错)时会产生中断。
- CHPRIOS / CHPRIOR:通道优先级设置寄存器。将通道分配到高优先级或低优先级队列。
- 各种中断映射与标志寄存器:如
FTCFLAG(帧传输完成标志)、BTCFLAG(块传输完成标志)、BERFLAG(总线错误标志)等。这些寄存器用于查询具体的中断事件来源。通常需要先清除标志位,再处理中断。
7.4 高级功能配置寄存器
- PTCRL:端口控制寄存器。其中就包含了FIFO旁路模式的使能位。
- DCTRL, WPR, WMR:调试控制、观察点、观察掩码寄存器。用于配置前面所述的调试功能。
- DMAMPCTRL, DMAMPRxS/E:内存保护控制、区域起始/结束地址寄存器。用于配置内存保护区域。
- DMAPCR, DMAPAR:奇偶校验控制、奇偶错误地址寄存器。用于管理和诊断奇偶校验错误。
7.5 编程流程与示例
一个典型的DMA通道初始化流程如下:
- 全局使能:配置
GCTRL,开启DMA,设置调试模式(如需)。 - 配置通道控制包:这不是通过上述内存映射寄存器,而是通过写入DMA内部专用的控制包RAM。一个控制包通常包含:源地址、目的地址、传输数量、传输宽度、地址递增模式、传输类型(块/帧)等。这是DMA传输的核心参数。
- 配置通道特性:通过
DREQASIx寄存器,将该通道与特定的外设请求线绑定。 - 使能通道:通过
HWCHENAS寄存器,使能该通道的硬件触发。 - 配置中断:通过
GCHIENAS使能该通道的传输完成中断(如果需要)。 - 配置优先级:通过
CHPRIOS/CHPRIOR设置通道优先级。 - 触发传输:外设产生事件(或软件写
SWCHENAS),启动传输。
一个常见的坑:控制包配置时机务必注意,必须在通道被触发(Pending)之前,完成其控制包的配置。如果在通道已经挂起等待仲裁时,去修改它的控制包,可能会导致不可预知的行为,甚至清空其挂起状态。安全的做法是,在初始化阶段配置好所有控制包,并使能通道,然后再去激活外设产生请求。
8. 实战问题排查与性能优化经验
理论最终服务于实践。下面分享几个我在项目中实际遇到的DMA相关问题和解决思路。
8.1 数据错位或损坏
- 症状:通过DMA搬运的数据,在目的地址出现字节顺序错乱、数据丢失或重复。
- 排查思路:
- 检查对齐:源地址、目的地址、传输数据的宽度(8/16/32/64位)是否满足处理器的对齐要求?不满足对齐的访问在某些架构上会导致硬件异常或拆分多次低效访问。
- 检查控制包:重点检查源/目的地址的递增模式。你是希望地址在每次传输后递增、递减还是不变?配置错误会导致数据被重复写入同一个地址,或者写入错误的地址区间。
- 检查FIFO与传输尺寸:结合前面讲的FIFO和旁路模式。如果你的读元素是32位,写元素是8位,在非旁路模式下,FIFO会帮你做拆包。但如果配置或理解有误,可能会以为是一次性写入。
- 使用观察点:在怀疑出错的源地址或目的地址设置观察点,让DMA在访问该地址时暂停,然后检查DMA的内部状态寄存器(如活跃通道地址寄存器
PAACSADDR/PBACSADDR),看传输是否如预期进行。
8.2 DMA传输导致系统卡顿
- 症状:当DMA进行大规模传输时,CPU访问总线(执行指令、存取数据)变慢,系统响应延迟增加。
- 排查与优化:
- 总线仲裁:DMA和CPU共享系统总线(如AHB)。高带宽的DMA传输会占用总线,导致CPU等待。查看芯片手册的总线架构,确认DMA和CPU的访问路径。
- 优化DMA策略:
- 使用突发传输:确保DMA配置为使用最大允许的突发长度(Burst),减少总线仲裁开销。这通常与FIFO深度和非旁路模式相关。
- 调整优先级:如果CPU的实时任务更重要,可以降低DMA通道的优先级(如果总线仲裁器支持),或使用带QoS(服务质量)的总线。
- 分片传输:将一次巨大的DMA传输拆分成多个较小的块传输,并在块传输完成中断中重新提交下一个块。这样可以在块之间释放总线给CPU使用,虽然整体传输时间可能略长,但系统响应性更好。
- 检查内存访问冲突:确保DMA的源/目的区域与CPU频繁访问的代码区、堆栈区不在同一块物理RAM bank内,避免bank冲突。
8.3 低功耗模式下DMA不工作
- 症状:系统进入低功耗模式后,期望由DMA配合外设(如RTC唤醒)搬运数据,但DMA无反应。
- 排查:
- 时钟门控:确认在目标低功耗模式下,DMA控制器及其总线时钟(如AHB时钟)是否仍然开启。许多芯片的低功耗模式会关闭大部分外设时钟。
- DMA自身睡眠模式:如果DMA处于睡眠模式,它能被外设请求唤醒吗?根据手册,通常可以。但需确认唤醒路径和时序。
- 全局低功耗模式:如前面电源管理章节所述,在全局低功耗模式下,DMA时钟可能被停止,根本无法检测请求。这种场景下,必须设计为先由外设中断唤醒系统到运行模式,恢复时钟,然后再启动DMA。
8.4 链式触发未按预期执行
- 症状:配置了通道A链式触发通道B,但通道A完成后,通道B没有启动。
- 排查:
- 配置顺序:确认是在触发通道A之前,就已经配置并使能了通道B的所有参数(包括控制包和通道使能位)。
- 链字段:仔细核对通道A控制包中的“链”字段,写入的数值是否正确对应通道B的编号。
- 传输类型:链式触发发生在通道A的“一次传输”完成时。这个“一次传输”是帧传输完成还是块传输完成?这由通道A控制包中的传输类型位决定。确保你的理解与配置一致。
- 中断干扰:检查是否在通道A的中断服务程序中进行了不当操作,意外修改了通道B的状态或链式配置。
调试DMA问题,尤其是复杂的多通道、链式触发场景,善用其调试功能是关键。结合观察点、灵活的挂起模式,以及仔细分析各个状态寄存器,可以像外科手术一样精准地定位问题所在。最后,所有的优化(性能、功耗)都建立在对其内部机制(如FIFO、仲裁、旁路模式)的深刻理解之上,盲目调整参数往往事倍功半。