1. 项目概述与DMA核心价值
在嵌入式系统,尤其是像TMS320VC5401这类面向实时信号处理的DSP芯片开发中,我们常常面临一个核心矛盾:CPU的计算能力是宝贵的,但大量、频繁的数据搬运(比如从串口接收数据到内存,或从内存搬数据到DAC输出)却会无情地占用CPU周期。想象一下,你正在用DSP全速运行一个复杂的音频滤波算法,但每处理完一个采样点,CPU都得停下来,亲自去串口寄存器里把下一个数据“拿”进来,这效率简直无法忍受。这时,直接内存访问(DMA)技术就如同一位不知疲倦的专职搬运工,它的核心价值就是让CPU专注于计算,把繁琐的数据搬运工作交给专门的硬件控制器去并行完成。
TMS320VC5401集成的DMA控制器,正是为解决这一问题而生的利器。它不是一个简单的数据通道,而是一个拥有六个独立通道、可编程、可同步、能自动初始化的智能数据搬运引擎。在实际项目中,无论是实现McBSP(多通道缓冲串行口)的连续音频流收发,还是完成片内存储器与外部存储器之间的大块数据交换,抑或是构建高效的环形缓冲区(Circular Buffer)进行实时数据处理,DMA都是提升系统性能和实时性的关键。理解并熟练配置它,是从嵌入式新手迈向资深开发者的必经之路。本文将带你深入VC5401的DMA控制器,不仅解读手册上的原理,更结合我多年的实际调试经验,拆解其特性、配置流程以及那些手册上不会写的“坑”与技巧。
2. TMS320VC5401 DMA控制器架构深度解析
要驾驭好DMA,首先得把它当成一个独立的、有自己“想法”的微型处理器来看待,而不仅仅是CPU的一个附属外设。
2.1 独立性与多通道设计
VC5401的DMA控制器完全独立于CPU内核运行。这意味着,当DMA在后台默默地搬运数据时,CPU可以继续执行其主程序代码,两者在硬件层面是并行的。这种并行性是其高性能的基石。
它提供了六个完全独立的可编程通道(Channel 0 ~ Channel 5)。你可以把这六个通道想象成六个独立的搬运工,每个搬运工都可以被分配不同的任务。例如,通道0专门负责从McBSP0接收数据到DARAM,通道1负责将处理好的数据从DARAM发送到McBSP1,通道2则负责在片内不同DARAM块之间搬运中间计算结果。每个通道都维护着自己完整的“工作上下文”,包括源地址、目的地址、传输数量、传输模式等,互不干扰。
注意:虽然六个通道是独立的,但它们共享同一个内存总线。当多个通道同时请求传输时,就需要仲裁机制,这就是优先级发挥作用的地方。
2.2 内存映射与访问权限
DMA控制器有自己的“视野”,即DMA内存映射。如图3-10所示,这个映射是固定的,且不受CPU内存映射寄存器(PMST)中MP/MC、DROM和OVLY位状态的影响。这是一个极其重要的特性,它保证了DMA操作的确定性和可靠性。
例如,无论CPU将片内DARAM映射到程序空间还是数据空间,DMA控制器访问0x1000-0x1FFF和0x2000-0x2FFF这两个4K字DARAM区块的路径都是固定的。同样,访问McBSP寄存器(0x20-0x23)和HPI控制寄存器(0x2C)的路径也是确定的。这种设计避免了因CPU改变内存映射而导致DMA访问错误地址的风险。
关键内存区域说明:
- Scratch-Pad RAM (0x60-0x7F):这是一个特殊的区域,物理上属于起始于0x1000的DARAM块。虽然容量小,但因其访问特性,在需要避免访问冲突的特定高性能算法中很有用。DMA也可以访问此区域。
- 外设寄存器区:DMA可以像CPU一样读写McBSP的数据接收/发送寄存器(DRR, DXR)、定时器寄存器等,这是实现外设与内存自动数据交换的基础。
2.3 优先级与仲裁机制
每个DMA通道都可以被独立地设置为高优先级(High Priority)或低优先级(Low Priority)。这个优先级是通道间的相对优先级。
- 高优先级 vs 低优先级:在任何时刻,高优先级通道的传输请求都会优先于低优先级通道得到服务。这确保了关键数据流(如实时音频采集)的延迟最小化。
- 轮询调度(Round-Robin):如果有多个通道被设置为相同的优先级(比如都是高优先级),那么DMA控制器会以轮询的方式为它们服务。这保证了公平性,避免某个通道饿死其他同优先级通道。
配置心得:在实际系统中,通常会将与实时性要求最高的外设(如主音频码流的McBSP)关联的DMA通道设置为高优先级,而将一些后台的、非实时的数据搬运任务(如日志存储、非关键参数更新)设置为低优先级。
3. DMA核心特性与工作模式详解
理解了架构,我们再来深入看看VC5401 DMA控制器的几项核心“技能”,这些技能决定了它的灵活性和强大功能。
3.1 灵活的地址修改模式
这是DMA最实用的特性之一。它允许在每次传输(读或写)后,自动更新源地址和目的地址。模式通过DMMCRx寄存器中的SIND(源地址索引模式)和DIND(目的地址索引模式)字段配置。
- 固定地址(No Modify):地址保持不变。适用于从固定外设寄存器(如ADC结果寄存器)读取数据到内存的连续区域,或者从内存固定区域发送数据到外设寄存器(如DAC)。
- 后递增(Post-increment):每次传输后,地址自动增加1(对于16位字传输)。这是最常用的模式,用于顺序读写连续的内存块。
- 后递减(Post-decrement):每次传输后,地址自动减少1。可用于反向填充缓冲区等场景。
- 带偏移量的后递增(Indexed):这是实现复杂数据管理的核心。地址不是简单地加1或减1,而是增加一个可编程的偏移量(正值或负值)。这个偏移量存储在
DMIDX0或DMIDX1寄存器中,并通过DMMCRx寄存器选择。
地址修改的进阶应用:帧传输与帧索引DMA支持“多帧(Multi-Frame)”传输概念。一个“块(Block)”由多个“帧(Frame)”组成,一帧又由多个“元素(Element)”组成。通常,一个元素就是一个16位字。
- 元素索引(Element Index):在每帧内,除最后一个元素外的每次传输后,地址按
DMIDX0/1(元素索引)调整。 - 帧索引(Frame Index):在一帧的最后一个元素传输完成后,地址按
DMFRI0/1(帧索引)调整。这实现了二维数据搬运。
生活化类比:想象你在搬运一堆书,这些书按“行”(帧)和“列”(元素)摆放在书架上。DMIDX告诉你搬完一本书后,在同一行内向右移动几列(通常为1)。DMFRI则告诉你在搬完一行的最后一本书后,移动到下一行的第几列(可能是第0列,即行首)。这样,你可以轻松地搬运一个矩形区域的数据,或者实现“隔行”搬运。
3.2 自动初始化模式:实现连续无缝传输
自动初始化(Autoinitialization)是DMA实现“永动”或循环缓冲的关键。当一次块传输完成后,DMA能自动从一组“全局重载寄存器”中加载参数,立即开始下一次传输,而无需CPU干预。
相关寄存器:
DMGSA:全局源地址重载寄存器DMGDA:全局目的地址重载寄存器DMGCR:全局元素计数重载寄存器DMGFR:全局帧计数重载寄存器
工作流程:
- CPU初始化一个DMA通道(如通道0),设置好
DMSRC0,DMDST0,DMCTR0,DMSFC0等,并同时将下一次传输所需的起始参数写入DMGSA,DMGDA,DMGCR,DMGFR。 - 启动DMA传输。
- 当本次块传输完成(
DMCTR0和DMSFC0中的帧计数减到0),如果DMMCR0中的AUTOINIT位被置1,则DMA控制器自动将DMGSA、DMGDA、DMGCR、DMGFR的值分别加载到DMSRC0、DMDST0、DMCTR0、DMSFC0中。 - DMA通道立即使用这些新参数开始下一次块传输。
应用场景:
- 双缓冲(Ping-Pong Buffer):设置两个缓冲区A和B。首次传输目标为A,同时将全局重载寄存器指向B。当A满(传输完成),DMA自动开始向B传输,同时触发中断通知CPU处理A的数据。CPU在处理A的同时,DMA在填充B,如此循环,实现数据连续处理无间隙。
- 循环缓冲区:通过巧妙设置源/目的地址和索引值,配合自动初始化,可以构建一个首尾相连的环形缓冲区,用于流式数据的实时生产与消费。
实操要点:务必在启动DMA传输之前就配置好全局重载寄存器。因为一旦传输开始,DMA可能在任何时刻完成并触发重载,如果那时全局寄存器还未准备好,将导致错误的数据传输。
3.3 传输计数与双字模式
传输计数:
- 元素计数(Element Count):由
DMCTRx寄存器的低16位定义,表示每帧要传输的元素个数。最大值65535(0xFFFF)。每传输一个元素,该计数器减1。 - 帧计数(Frame Count):由
DMSFCx寄存器的低8位(DMSEFCn[7:0])定义,表示一个块中有多少帧。最大值128(0xFF)。每传输完一帧(即元素计数减到0时),帧计数减1。 - 总传输量:总传输数据量(单位:元素,通常是16位字) = 帧计数 × 元素计数。
双字模式(Double-Word Mode): 通过设置DMMCRx寄存器中的DBLM位,可以启用双字模式。在此模式下,DMA每次传输会连续搬运两个连续的16位字(共32位),并将其视为一个元素。地址索引的更新也以双字(2个地址单位)为单位进行。
- 优势:在需要32位数据对齐或搬运效率更高的场景下,双字模式可以将传输效率理论上提升一倍(因为每次传输事务处理的数据量翻倍)。
- 注意:源地址和目的地址都应对齐到偶地址边界(即地址最低位为0),否则可能导致非对齐访问错误或性能下降。
4. DMA配置与编程实战指南
理论说得再多,不如一行代码。下面我们以一个典型场景为例:使用DMA通道0,将McBSP0接收到的数据自动搬运到片内DARAM的一个缓冲区中,并设置自动初始化实现双缓冲。
4.1 硬件连接与需求分析
假设我们有一个音频编解码器通过McBSP0与VC5401连接。McBSP0配置为每帧1个16位字,接收数据就绪后会产生BRINT0(接收中断)事件。我们需要:
- 将接收到的数据存入缓冲区A(假设地址0x2000,大小256字)。
- 缓冲区A存满后,自动切换至缓冲区B(地址0x2100,大小256字)继续存储,并通知CPU处理A的数据。
- 如此循环往复。
4.2 寄存器配置详解
我们将使用DMA通道0。首先,需要了解访问DMA寄存器的方式。除了DMPREC(优先级使能控制寄存器)是标准内存映射寄存器(地址0x54)外,其他所有通道专用寄存器(如DMSRC0)和全局寄存器(如DMGSA)都通过子库寻址方式访问。
子库寻址机制:
- 向
DMSA(地址0x55)写入目标寄存器的子地址(Sub-address,见表3-14,例如DMSRC0的子地址是0x00)。 - 然后,对
DMSDN(地址0x57)进行读写,即可访问刚刚选中的寄存器。若使用DMSDI(地址0x56),则在访问后DMSA中的子地址会自动加1,便于连续配置多个寄存器。
步骤1:配置子库地址并初始化源/目的地址
// 假设使用C语言和TI的编译器,指针指向内存映射寄存器 volatile unsigned int *DMSA = (volatile unsigned int *)0x55; volatile unsigned int *DMSDI = (volatile unsigned int *)0x56; // 使用自动递增模式 // 通过DMSDI连续配置通道0的主要寄存器 *DMSA = 0x00; // 指向DMSRC0的子地址 *DMSDI = 0x0020; // DMSRC0: 源地址 = 0x0020 (McBSP0数据接收寄存器DRR20的地址) *DMSDI = 0x2000; // DMDST0: 目的地址 = 0x2000 (缓冲区A起始地址) *DMSDI = 256; // DMCTR0: 元素计数 = 256 (缓冲区大小) // DMSFC0: 同步选择和帧计数寄存器 // 假设每帧1个元素,帧计数为1(即单帧传输)。同步事件选择McBSP0接收事件(DSYN=0001b) // 寄存器格式:[15-8: FRAME COUNT] [7-6: Reserved] [5-0: DSYN] *DMSDI = (0 << 8) | (0x01); // 帧计数=0(代表1帧),DSYN=0001b步骤2:配置传输模式控制寄存器(DMMCR0)DMMCR0控制着传输的核心行为。
// 继续通过DMSDI配置,当前DMSA已自动指向DMMCR0(子地址0x04) // DMMCR0 寄存器位域假设(具体需查手册bit定义): // DBLM = 0: 单字模式 // IMOD = 1: 中断模式为半块和全块传输完成时产生中断 // CTMOD = 0: 多帧模式 // SIND = 01b: 源地址固定(从McBSP寄存器读,地址不变) // DIND = 01b: 目的地址后递增(存入内存后地址+1) // DINM = 1: 使能DMA中断 // AUTONIT = 1: 使能自动初始化 // 假设一个示例值,实际需按位组合 unsigned int dmmcr0_value = 0; dmmcr0_value |= (0 << 14); // DBLM = 0 dmmcr0_value |= (1 << 10); // IMOD = 1 dmmcr0_value |= (0 << 8); // CTMOD = 0 dmmcr0_value |= (0x1 << 4); // SIND = 01b (固定地址) dmmcr0_value |= (0x1 << 2); // DIND = 01b (后递增) dmmcr0_value |= (1 << 1); // DINM = 1 dmmcr0_value |= (1 << 0); // AUTOINIT = 1 *DMSDI = dmmcr0_value; // 写入DMMCR0步骤3:配置全局重载寄存器以实现双缓冲
// 配置全局重载寄存器,为下一次传输(缓冲区B)做准备 // 首先,将DMSA指向全局重载寄存器的起始子地址 *DMSA = 0x24; // 指向DMGSA的子地址 *DMSDI = 0x0020; // DMGSA: 下次传输的源地址仍是McBSP0 DRR20 *DMSDI = 0x2100; // DMGDA: 下次传输的目的地址是缓冲区B起始地址 *DMSDI = 256; // DMGCR: 下次传输的元素计数 *DMSDI = (0 << 8) | (0x01); // DMGFR: 下次传输的帧计数和同步事件步骤4:配置DMA通道优先级并使能最后,需要配置DMPREC寄存器来使能通道并设置优先级。
volatile unsigned int *DMPREC = (volatile unsigned int *)0x54; // DMPREC 寄存器位域假设: // [15-12]: 保留 // [11-10]: INTSEL (中断选择,用于通道0-3,我们先不管,用默认) // [9-8]: 保留 // [7-6]: PRI5-4 (通道5/4优先级,00=低,01=高) // [5-4]: PRI3-2 // [3-2]: PRI1-0 // [1]: DE5 (通道5使能) // [0]: DE4 ... 依此类推,[5]: DE0 // 假设我们使能通道0,并设置为高优先级,其他通道禁用。 // 需要仔细查阅手册确定bit位。这里假设DE0在bit5,PRI0在bit2-3。 unsigned int dmprec_value = 0; dmprec_value |= (1 << 5); // 使能通道0 (DE0=1) dmprec_value |= (0x1 << 2); // 设置通道0为高优先级 (PRI0=01b) // 注意:还需要根据表3-10,通过INTSEL位选择DMA通道0的中断源(如果要用中断) // 假设INTSEL[1:0] = 10b,将IMR/IFR[6]分配给DMAC0 dmprec_value |= (0x2 << 10); // INTSEL = 10b *DMPREC = dmprec_value;步骤5:使能CPU中断如果配置了DMA中断(DINM=1,IMOD=1),还需要在CPU端使能相应的中断。
// 假设DMAC0中断对应IMR的bit6(根据INTSEL=10b配置) volatile unsigned int *IMR = (volatile unsigned int *)0x0; *IMR |= (1 << 6); // 使能DMA通道0中断 // 同时需要确保全局中断使能(INTM = 0),这通常通过汇编指令或设置ST1寄存器完成。4.3 启动与停止传输
- 启动传输:一旦完成上述配置并使能了通道(
DMPREC中DEx=1),DMA通道就会等待其配置的同步事件(本例中是McBSP0接收事件)发生。一旦McBSP0接收到一个数据,DMA传输便会自动开始。 - 停止传输:将
DMPREC寄存器中对应通道的使能位(DEx)清零,即可禁用该通道,停止其传输。也可以在DMMCRx中清除AUTOINIT位,让当前块传输完成后不再自动重载。
5. 同步事件与中断机制实战
DMA的强大之处在于它能与系统内外的事件同步,并在特定时刻通知CPU。
5.1 同步事件选择
每个DMA通道的传输可以与一个特定事件同步,这通过DMSFCx寄存器中的DSYN字段选择。例如:
DSYN=0000b:无同步。DMA会以尽可能快的速度(取决于优先级和总线带宽)连续传输数据,直到完成指定的元素和帧计数。适用于内存到内存的拷贝。DSYN=0001b/0010b:与McBSP0的接收/发送事件同步。这是最常用的场景之一,确保每个串行数据到来或发送时,DMA才搬运一次,完美匹配串行数据流的速度。DSYN=1101b/1111b:与定时器0/1中断同步。可以用于产生精确周期性的数据搬运,例如生成固定采样率的波形。DSYN=1110b:与外部中断3同步。可以由外部硬件信号触发DMA传输。
配置示例:如前所述,在DMSFC0中设置DSYN=0001b,即让通道0与McBSP0的接收事件同步。
5.2 中断产生与处理
DMA可以在传输过程中的特定点向CPU发出中断请求,这是CPU与DMA协同工作的关键。
中断行为由DMMCRx中的DINM和IMOD位控制:
DINM=0:禁止该通道产生中断。DINM=1且IMOD=0:仅在整个块传输完成时产生一次中断。适用于需要处理整块数据的场景。DINM=1且IMOD=1:在半块传输完成和整个块传输完成时各产生一次中断。这是实现双缓冲(Ping-Pong Buffer)的理想模式。- 第一次中断(半块):表示前半部分缓冲区(例如缓冲区A的前128字)已满,CPU可以开始处理这前半部分数据,同时DMA继续向后半部分填充。
- 第二次中断(全块):表示整个缓冲区(256字)已满,且DMA已通过自动初始化切换到下一个缓冲区(如缓冲区B)。CPU处理完前半部分后,可以接着处理后半部分,此时DMA正在填充另一个缓冲区,实现并行。
中断服务程序(ISR)编写要点:
- 判断中断源:在中断服务程序中,首先读取
IFR(中断标志寄存器)确定是哪个DMA通道产生的中断(例如,检查IFR的bit6是否为1,对应DMAC0)。 - 清除中断标志:向
IFR的对应位写1以清除中断标志。非常重要,否则会持续触发中断。 - 处理数据:根据是半块中断还是全块中断,确定当前可安全读/写的数据缓冲区范围。
- 操作缓冲区指针:在双缓冲模式下,通常需要维护一组软件指针来跟踪当前正在被DMA填充的缓冲区(
DMA_CurrentBuffer)和当前正在被CPU处理的缓冲区(CPU_CurrentBuffer)。在中断中切换这些指针。 - 注意重入和竞争:确保对共享缓冲区(介于DMA和CPU之间)的访问是安全的。通常,DMA和CPU操作不同的缓冲区(双缓冲),或者通过标志位进行同步。
5.3 中断选择与冲突解决
如表3-10和3-15所示,VC5401的DMA通道0-3的中断线与其它外设中断是复用的,需要通过DMPREC寄存器的INTSEL字段进行选择。
- 默认情况(复位后):
INTSEL=00b。此时,IMR/IFR[6]为保留,[7]对应TINT1(定时器1),[10]对应BRINT1(McBSP1接收),[11]对应BXINT1(McBSP1发送)。 - 选择DMA中断:若想使用DMA通道0-3的中断,需设置
INTSEL=10b。此时,IMR/IFR[6]对应DMAC0,[7]对应DMAC1,[10]对应DMAC2,[11]对应DMAC3。
这意味着:如果你同时需要使用Timer1中断和DMA通道1中断,它们共享同一个中断向量(INT7)。你无法同时使能它们,必须根据INTSEL的配置二选一。在设计系统时,必须仔细规划中断资源的使用。
6. 常见问题、调试技巧与经验实录
即使理解了所有寄存器,实际调试DMA时依然会遇到各种问题。下面分享一些我踩过的“坑”和总结的技巧。
6.1 DMA传输不启动或数据错误
这是最常见的问题。请按以下清单排查:
- 时钟与复位:确认DSP核心时钟和外围设备(如McBSP)时钟已正确配置并运行。确保芯片已脱离复位状态。
- 外设使能:DMA的同步事件依赖于外设。例如,如果同步事件是McBSP接收,必须确保McBSP本身已正确配置并使能(SPCR寄存器中的
XRST和RRST位)。 - DMA通道使能:检查
DMPREC寄存器中对应通道的使能位(DEx)是否已设置为1。 - 寄存器访问顺序:务必遵循正确的子库寻址流程:先写
DMSA选择子地址,再读写DMSDN/DMSDI。在连续配置时,使用DMSDI可以简化操作,但要注意其自动递增特性,避免跳错寄存器。 - 地址对齐:特别是在双字模式下,确保源地址和目的地址是偶地址(最低位为0)。非对齐访问可能导致不可预知的行为。
- 传输计数不为零:检查
DMCTRx和DMSFCx中的元素计数和帧计数是否已设置为非零值。复位后它们通常是0。 - 同步事件是否发生:如果配置了同步事件(如McBSP接收),请确认该事件确实发生了。可以通过查询外设的状态寄存器来验证。
6.2 中断无法进入或进入过于频繁
- 全局中断使能:确认CPU的全局中断屏蔽位
INTM(位于ST1寄存器)为0(使能)。INTM在复位后为1(禁止)。 - 中断屏蔽寄存器(IMR):确认
IMR中对应DMA通道的中断位已被置1(使能)。 - 中断选择(INTSEL):如果使用的是DMA通道0-3,务必检查
DMPREC中的INTSEL位是否已正确配置,将中断线分配给DMA而非其他外设。 - 中断标志清除:在中断服务程序(ISR)中,必须向
IFR的对应位写1以清除中断标志。忘记清除会导致中断持续触发,CPU不断跳入ISR,仿佛“卡死”在中断里。 - 中断向量表:确保中断向量表中对应位置(例如DMAC0在
INT6,地址0x58)的指令是一条跳转到你ISR的BD或B指令。
6.3 性能优化与注意事项
- 总线冲突与优先级:DMA与CPU共享片内存储器和外设总线。高优先级的DMA通道会抢占CPU的访问。如果CPU正在执行对性能敏感的循环(如FIR滤波内核),且该循环位于片内RAM中,被DMA频繁抢占会导致CPU性能下降。此时,可以考虑将CPU的关键代码段放在DARAM中,而将DMA传输的源/目的地址设置在SARAM或外部存储器(如果速度允许),或者调整DMA通道的优先级。
- 双字模式的使用:当搬运连续对齐的32位数据时,务必启用双字模式。这不仅能减少总线事务数量,还能避免因两次独立的16位访问造成的潜在数据一致性问题。
- 自动初始化的时机:全局重载寄存器(
DMGSA,DMGDA等)必须在当前块传输开始前就写入正确的值。因为DMA完成当前块传输和进行自动初始化之间的时间极短,CPU可能没有机会在中间插入配置。安全的做法是在启动DMA前,一次性配置好所有通道寄存器和对应的全局重载寄存器。 - 使用仿真器调试:CCS(Code Composer Studio)的调试器是强大的工具。你可以:
- 查看DMA寄存器:在Memory Browser中直接查看0x55-0x57地址区域(
DMSA,DMSDI/DMSDN)以及0x54(DMPREC)的值,但注意子库寻址的间接性。 - 设置数据断点:在DMA的目的缓冲区地址设置写断点,可以观察DMA是否在正确写入数据。
- 使用事件分析器:高级版本的CCS支持DMA事件跟踪,可以图形化看到DMA传输的启动、停止和中断触发时间点。
- 查看DMA寄存器:在Memory Browser中直接查看0x55-0x57地址区域(
6.4 一个典型的双缓冲音频处理流程总结
- 初始化阶段:
- 配置McBSP0的采样率、字长等。
- 分配两个音频缓冲区
BufferPing[256]和BufferPong[256]。 - 配置DMA通道0:
- 源地址:McBSP0数据接收寄存器地址。
- 目的地址:
BufferPing首地址。 - 元素计数:256,帧计数:1。
- 同步事件:McBSP0接收。
- 地址模式:源固定,目的后递增。
- 中断模式:
IMOD=1(半块和全块中断)。 - 自动初始化:使能。
- 配置全局重载寄存器:源地址不变,目的地址重载为
BufferPong首地址。 - 使能DMA通道0(高优先级)。
- 使能CPU的DMA通道0中断(
IMR和INTM)。
- 运行阶段:
- 启动McBSP0接收。
- DMA开始工作。当
BufferPing填满前128个样本(半块)时,产生第一次中断。 - ISR处理半块中断:设置一个标志,通知主程序
BufferPing的前半部分已就绪可处理。不在此处进行耗时处理。 - DMA继续填充
BufferPing的后半部分。 - 当
BufferPing完全填满(全块)时,产生第二次中断,同时DMA自动将目的地址切换到BufferPong并开始填充。 - ISR处理全块中断:切换软件指针,将“可处理缓冲区”指向
BufferPing,将“正在填充缓冲区”指向BufferPong。通知主程序BufferPing全部就绪。 - 主程序在后台(非中断上下文)处理
BufferPing中的256个音频样本(如进行滤波、增益调整等)。 - 当
BufferPong的前半部分填满时,流程重复,但角色在Ping和Pong之间切换。
通过这样的设计,DMA负责不间断的数据采集,CPU负责批处理数据,两者高效并行,实现了实时音频流处理,且CPU有充足的时间(至少半缓冲区的时间)完成处理,避免了数据丢失。掌握TMS320VC5401的DMA控制器,本质上就是掌握了让数据在芯片内“自动、智能、高效”流动的艺术,它能将你的DSP系统性能提升一个数量级。