CC13x2/CC26x2 I2S模块配置与DMA操作实战指南
2026/7/26 21:23:18 网站建设 项目流程

1. 项目概述与I2S核心价值

在嵌入式音频开发领域,I2S(Inter-IC Sound)总线几乎是数字音频传输的“普通话”。它定义了音频数据(ADx)、位时钟(BCLK)和字时钟(WCLK,或称LRCLK)这三根线之间的时序关系,让不同厂商的编解码器、处理器和微控制器能够无缝对话。对于CC13x2/CC26x2这类集成了强大射频功能的无线MCU而言,一个设计精良的I2S模块不仅仅是锦上添花,更是解锁无线音频传输、语音前端处理、多设备同步等高端应用场景的钥匙。

我接触过不少音频项目,从简单的本地播放到复杂的多房间无线音频系统,深刻体会到I2S配置的“魔鬼在细节里”。TI在CC13x2/CC26x2中提供的I2S模块,其强大之处在于将复杂的时序控制、DMA数据搬运乃至无线同步的底层支持,都通过一组精心设计的寄存器暴露给了开发者。这意味着你拥有极高的灵活性,但也意味着你需要透彻理解每个比特位的含义,否则极易掉进坑里。本文的目标,就是结合手册中的寄存器描述和实际调试经验,为你拆解CC13x2/CC26x2 I2S模块的配置精髓与DMA操作要点,让你不仅能“配通”,更能“配优”,构建稳定可靠的音频数据链路。

2. I2S模块整体架构与设计思路拆解

CC13x2/CC26x2的I2S模块并非一个简单的串行移位寄存器,而是一个集成了时钟管理、格式转换、DMA控制和高级同步功能的完整音频子系统。理解其整体架构是进行正确配置的前提。

2.1 模块核心组件与数据流

模块的核心可以看作两条并行的数据流水线:输入流水线和输出流水线。每条流水线都包含以下关键部分:

  1. 引脚接口与方向控制:通过AIFDIRCFG寄存器配置AD0和AD1引脚为输入或输出。一个典型的立体声应用会配置一个引脚为输入(接麦克风或ADC),另一个为输出(接扬声器或DAC),但也支持双输入或双输出以扩展通道数。
  2. 串行接口引擎:负责根据AIFFMTCFG寄存器设定的格式(I2S、LJF、RJF、DSP),在BCLK和WCLK的节拍下,将串行数据流与内部的并行样本字进行转换。这里的关键是理解DATA_DELAYWORD_LENSMPL_EDGEDUAL_PHASE这几个字段如何协同工作,共同定义了数据在时序线上的精确位置。
  3. 样本字缓冲区:每个数据引脚(ADx)对应一个移位寄存器和一个样本字缓冲区。当输入时,串行数据移入移位寄存器,凑够一个样本字后存入缓冲区;当输出时,DMA预先将数据加载到缓冲区,再按位移出。这个缓冲区是串行引擎和DMA之间的关键桥梁。
  4. 集成DMA控制器:这是减轻CPU负担的核心。它独立工作,在后台将输入缓冲区的数据搬运到内存,或将内存中的数据搬运到输出缓冲区。DMA以“块”为单位操作,块的大小由AIFDMACFG.END_FRAME_IDX定义。你需要通过AIFINPTRNEXTAIFOUTPTRNEXT寄存器为DMA提供下一个内存块的地址,形成乒乓缓冲或环形缓冲。
  5. 采样时间戳生成器:这是实现高精度同步的“秘密武器”。它包含两个计数器:STMPWCNT对WCLK帧进行计数,STMPXCNT对内部48MHz时钟(XOSC)周期进行计数。通过STMPINTRIGSTMPOUTTRIG可以精确控制DMA在哪个WCLK帧开始工作。更重要的是,它可以捕获特定事件(如无线数据包同步字)发生时的计数器值,用于跨设备的音频同步,实现恒定的音频延迟。

2.2 时钟树与配置策略

音频时钟的稳定性和准确性直接决定了音质。模块支持内部和外部两种时钟源,选择时需要权衡。

  • 内部时钟源:由PRCM模块从48MHz系统时钟分频产生MCLK、BCLK和WCLK。优点是无需外部晶振,节省成本和PCB空间。但缺点也很明显:首先,所有音频时钟频率必须是48MHz的整数分频,灵活性受限;其次,如果系统时钟源在RCOSC_HF和XOSC_HF之间切换,可能会引入时钟毛刺或频率跳变,导致音频失真。因此手册强烈建议,当使用内部音频时钟时,必须确保系统时钟(SCLK_HF)始终来自外部高频晶振(XOSC_HF)。
  • 外部时钟源:由外部音频编解码器或时钟发生器提供BCLK和WCLK。这是追求高保真音质的首选方案,因为外部专用时钟源的抖动性能通常远优于内部分频时钟。配置时,需要将I2S:AIFWCLKSRCPRCM:I2SBCLKSEL.SRC都设置为使用外部源。

实操心得:在电池供电的无线音频设备中,我通常的折衷方案是:让低功耗、低抖动的外部音频编解码器作为主时钟(Master),向CC26x2提供BCLK和WCLK。这样MCU可以作为从设备(Slave),既能获得好时钟,又能在音频不活动时让编解码器进入低功耗模式,而MCU的射频部分仍可独立工作。

2.3 内存中的数据布局

DMA搬运数据时,样本字在内存中的存储方式必须了然于胸。这由AIFFMTCFG.MEM_LEN_24AIFWMASKx寄存器共同决定。

  • 样本字长MEM_LEN_24决定内存中每个样本用16位还是24位存储。注意,这与串行线上的WORD_LEN(8-24位)是独立的。如果串行字长与内存字长不一致,模块会自动进行截断或零填充。例如,接收24位I2S数据但内存设置为16位,则低8位会被丢弃。
  • 字节序:所有数据均采用小端字节序(Little-Endian)存储。即一个16位样本0xABCD,在内存中地址低位存0xCD,地址高位存0xAB
  • 通道映射与存储顺序AIFWMASKx的位掩码决定了哪些通道的数据会被存储或读取。在双相位模式(I2S/LJF/RJF)下,通常只使用低2位:0x01为单声道(仅左通道),0x03为立体声。数据在内存中的排列顺序是:帧0左通道 -> 帧0右通道 -> 帧1左通道 -> 帧1右通道...如果启用两个ADx引脚,则顺序是:AD0帧0左 -> AD0帧0右 -> AD1帧0左 -> AD1帧0右 -> AD0帧1左...

理解这个布局对于正确设置DMA缓冲区指针和后续的音频数据处理算法至关重要。错误的理解会导致左右声道颠倒或数据错位。

3. 关键寄存器配置详解与实操要点

手册列出了大量寄存器,但实际开发中,我们需要聚焦于最核心的几个,理解其联动关系。下面我将以配置一个典型的、从外部编解码器接收立体声音频并存入内存的场景为例,详解关键步骤。

3.1 串行格式配置:AIFFMTCFG寄存器

这是配置的基石,决定了数据线上的时序。假设我们连接一个标准的I2S格式编解码器(如TI的TLV320AIC系列)。

  1. DUAL_PHASE:设置为1,选择双相位模式,对应I2S、LJF、RJF格式。
  2. WORD_LEN:设置为每个样本字的实际位数减1。例如,对于24位音频,设置为23(0x17)。注意,在双相位模式下,此字段代表的是“最大位数”,但通常我们将其设为实际位数。
  3. SMPL_EDGE:对于I2S格式,数据在BCLK的下降沿采样,上升沿更新。因此,根据手册对SMPL_EDGE的描述(1=在BCLK上升沿采样),我们需要设置为1。
  4. DATA_DELAY:这是I2S格式的关键。标准I2S要求在WCLK边沿变化后,延迟1个BCLK周期才开始传输MSB。因此,此字段应设置为1。
  5. MEM_LEN_24:根据你的应用需求选择。如果后续的音频处理算法希望使用24位精度,则设为1;如果为了节省内存和带宽,可以设为0,存储为16位(低8位丢失)。

对应的C代码配置可能如下:

// 假设 I2S_BASE 为 I2S 模块基地址 HWREG(I2S_BASE + I2S_O_AIFFMTCFG) = (1 << 5) | // DUAL_PHASE = 1, 双相位 (I2S) (1 << 6) | // SMPL_EDGE = 1, 上升沿采样 (23 << 0) | // WORD_LEN = 23 (24位) (1 << 8) | // DATA_DELAY = 1 (I2S格式) (1 << 7); // MEM_LEN_24 = 1, 内存中存24位

3.2 引脚与通道配置:AIFDIRCFG 和 AIFWMASKx

  1. AIFDIRCFG:配置AD0引脚为输入模式(假设音频数据从编解码器通过AD0引脚输入MCU)。将AD0字段设置为0x1(输入模式)。
  2. AIFWMASK0:因为我们只使用AD0引脚,且是立体声输入,所以将MASK字段设置为0x03,表示左(bit 0)右(bit 1)通道均有效。如果只接了一个麦克风(单声道),则设为0x01

3.3 时钟源配置:AIFWCLKSRC 与 PRCM 模块

由于我们假设使用外部编解码器作为主时钟,因此需要配置为外部时钟模式。

  1. I2S模块配置:将I2S:AIFWCLKSRC.WCLK_SRC设置为0x1,选择来自引脚的外部WCLK生成器。WCLK_INV根据实际需要设置,通常为0(不反相)。
  2. PRCM模块配置:将PRCM:I2SBCLKSEL.SRC设置为0x0,选择外部BCLK源。同时,确保PRCM:I2SCLKCTL.EN在适当的时机被使能(通常在DMA启动前)。

3.4 DMA缓冲区配置:AIFDMACFG、AIFINPTRNEXT/AIFOUTPTRNEXT

这是实现流畅音频流的核心。我们的目标是建立一个双缓冲(Ping-Pong)或环形缓冲机制。

  1. 计算缓冲区大小AIFDMACFG.END_FRAME_IDX定义了每个DMA缓冲区包含的帧数减一。一帧对应一个WCLK周期(立体声下包含左、右两个样本)。例如,如果你想设置一个包含256帧(即512个立体声样本)的缓冲区,则END_FRAME_IDX = 255

    • 缓冲区大小(字节)= (帧数) × (每帧样本数) × (每个样本在内存中的字节数)
    • 对于24位立体声:256帧 × 2通道/帧 × 3字节/样本 = 1536字节。
    • 对于16位立体声:256帧 × 2通道/帧 × 2字节/样本 = 1024字节。
  2. 准备内存缓冲区:在内存中(通常是RAM)分配至少两个上述大小的缓冲区,我们称之为bufferAbufferB。确保它们的地址对齐符合要求(16位存储时需半字对齐)。

  3. 启动序列:这是最容易出错的地方,必须严格按照手册顺序: a.首次写入指针:将bufferA的起始地址写入AIFINPTRNEXT。 b.配置DMA并初始化:将计算好的END_FRAME_IDX值写入AIFDMACFG这个写操作是使能DMA的关键动作。写入后,AIFINPTRNEXT的值会自动加载到AIFINPTR,DMA准备就绪。 c.预加载下一个指针:立即将bufferB的起始地址写入AIFINPTRNEXT。 d.配置采样时间戳触发器:设置STMPINTRIG等(见下文),然后使能采样时间戳生成器(STMPCTL.EN = 1)。 e.等待DMA开始:当STMPWCNT计数到与STMPINTRIG匹配时,DMA正式开始将数据存入bufferA

  4. 运行中维护:DMA在填充完一个缓冲区(例如bufferA)后,会触发AIF_DMA_IN中断(如果使能),并将AIFINPTRNEXT清零。你必须在中断服务程序(或轮询检查AIFINPTRNEXT == 0)中,及时将下一个缓冲区的地址(例如bufferA的地址,如果使用双缓冲)再次写入AIFINPTRNEXT。如果写入不及时,会导致PTR_ERR错误,DMA停止。

避坑指南:务必在写入AIFDMACFG.END_FRAME_IDX之前之后各写入一次AIFINPTRNEXT。手册中这个“两次写入”的要求非常关键,第一次是设置初始指针,第二次是在DMA初始化后预加载下一个指针。遗漏任何一次都会导致DMA无法正常启动或立即触发指针错误。

3.5 采样时间戳生成器配置:实现精准启动与同步

采样时间戳生成器用于精确控制DMA的启动时机,是实现低延迟和无线同步的基础。

  1. 设置周期STMPWPER应设置为DMA缓冲区大小的整数倍(N倍),即STMPWPER = N * (END_FRAME_IDX + 1)STMPWCNT会从0计数到STMPWPER-1然后归零,形成一个循环。这个循环的周期代表了你的“全局音频时间线”。
  2. 设置触发点STMPINTRIGSTMPOUTTRIG定义了输入和输出DMA在STMPWCNT等于何值时启动。为了避免误触发,初始时应将它们设置为大于等于STMPWPER的值。在准备启动时,再将其设置为目标触发值。例如,如果你想在STMPWCNT计数到100时开始输入,就将其设为100。
  3. 启动DMA:手册提供了一个简单的启动方法:在完成所有其他配置后,将STMPINTRIGSTMPOUTTRIG设置为(STMPWCNT + 2) % STMPWPER。这确保了DMA会在不久的将来(2个WCLK帧后)启动,给了系统足够的准备时间。
  4. 状态检查STMPCTL.IN_RDYOUT_RDY位会指示输入/输出DMA是否已准备好被触发。当触发发生后,这些位会清零。

4. 完整配置流程与代码实现参考

下面我将以一个具体的场景为例,展示从零开始配置CC26x2的I2S模块作为从设备,接收24位、48kHz立体声音频,并使用双缓冲DMA存入内存的完整流程和代码框架。

4.1 场景定义与参数计算

  • 主设备:外部音频编解码器,生成BCLK和WCLK。
  • 格式:标准I2S,24位样本深度,48kHz采样率(WCLK频率)。
  • BCLK频率:对于24位数据,每帧(左右声道)需要传输2 * 24 = 48位。I2S格式下,每个BCLK周期传输1位,因此BCLK频率 = 采样率 * 位数/帧 * 2(相位)?这里需要澄清:在双相位(立体声)模式下,一个WCLK周期包含两个相位(左声道和右声道)。标准I2S的BCLK频率计算公式为:BCLK = 采样率 * 位数/通道 * 通道数。对于48kHz, 24-bit, 立体声,BCLK = 48000 * 24 * 2 = 2.304 MHz
  • DMA缓冲区:我们希望缓冲区能容纳10ms的音频数据,以平衡延迟和中断频率。
    • 10ms的帧数 = 48000 Hz * 0.01 s = 480帧。
    • 为方便计算,取256帧(约5.33ms)。END_FRAME_IDX = 255
    • 每个缓冲区大小(24位存储) = 256帧 * 2通道/帧 * 3字节/样本 = 1536字节。

4.2 初始化步骤与代码框架

// 1. 引脚复用配置 (使用TI DriverLib或直接操作寄存器) // 将对应的GPIO引脚配置为I2S功能(AD0, BCLK, WCLK)。具体引脚请参考数据手册。 PRCMPeripheralRunEnable(PRCM_PERIPH_I2S); // 使能I2S外设时钟 PRCMLoadSet(); // ... 具体的PINMUX配置代码,此处省略 ... // 2. 配置I2S串行格式 (AIFFMTCFG) HWREG(I2S_BASE + I2S_O_AIFFMTCFG) = (1 << 5) | // DUAL_PHASE = 1 (I2S) (1 << 6) | // SMPL_EDGE = 1 (上升沿采样) (23 << 0) | // WORD_LEN = 23 (24-bit) (1 << 8) | // DATA_DELAY = 1 (I2S) (1 << 7); // MEM_LEN_24 = 1 (内存存24位) // 3. 配置引脚方向与通道掩码 HWREG(I2S_BASE + I2S_O_AIFDIRCFG) = 0x01; // AD0 配置为输入 HWREG(I2S_BASE + I2S_O_AIFWMASK0) = 0x03; // AD0 使能立体声(通道0和1) // 4. 配置时钟源为外部 HWREG(I2S_BASE + I2S_O_AIFWCLKSRC) = 0x01; // WCLK_SRC = 1 (外部), WCLK_INV = 0 // 注意:还需要配置PRCM模块的I2SBCLKSEL,选择外部BCLK源。 // 5. 分配DMA缓冲区 #define DMA_BUF_FRAMES 256 #define SAMPLES_PER_FRAME 2 // 立体声 #define BYTES_PER_SAMPLE 3 // 24-bit in memory uint8_t audioInputBufferA[DMA_BUF_FRAMES * SAMPLES_PER_FRAME * BYTES_PER_SAMPLE]; uint8_t audioInputBufferB[DMA_BUF_FRAMES * SAMPLES_PER_FRAME * BYTES_PER_SAMPLE]; volatile uint8_t *currentDmaReadBuffer = audioInputBufferA; // 用于应用程序读取的指针 // 6. DMA启动序列 // 6.1 写入第一个缓冲区地址到NEXT指针 HWREG(I2S_BASE + I2S_O_AIFINPTRNEXT) = (uint32_t)audioInputBufferA; // 6.2 配置DMA缓冲区大小,此操作会初始化DMA并加载NEXT指针到CURR指针 HWREG(I2S_BASE + I2S_O_AIFDMACFG) = (DMA_BUF_FRAMES - 1); // END_FRAME_IDX // 6.3 立即写入第二个缓冲区地址到NEXT指针 HWREG(I2S_BASE + I2S_O_AIFINPTRNEXT) = (uint32_t)audioInputBufferB; // 7. 配置采样时间戳生成器 #define STMPWPER_VALUE (DMA_BUF_FRAMES * 2) // 示例:设置为缓冲区大小的2倍 HWREG(I2S_BASE + I2S_O_STMPWPER) = STMPWPER_VALUE; // 初始设置触发值大于周期,避免误触发 HWREG(I2S_BASE + I2S_O_STMPINTRIG) = STMPWPER_VALUE; // 使能采样时间戳生成器 HWREG(I2S_BASE + I2S_O_STMPCTL) |= 0x01; // STMP_EN = 1 // 8. 设置实际触发点并等待启动 // 读取当前WCNT,计算未来的一个触发点 uint16_t currentWcnt = HWREG(I2S_BASE + I2S_O_STMPWCNT) & 0xFFFF; uint16_t triggerValue = (currentWcnt + 2) % STMPWPER_VALUE; HWREG(I2S_BASE + I2S_O_STMPINTRIG) = triggerValue; // 9. 使能中断(可选但推荐) HWREG(I2S_BASE + I2S_O_IRQMASK) |= (1 << 5); // 使能 AIF_DMA_IN 中断 // 在NVIC中使能I2S中断 IntEnable(INT_I2S); // 此时,DMA会在STMPWCNT等于triggerValue时开始工作。

4.3 DMA中断服务程序(ISR)示例

// I2S中断服务程序 void I2S_IRQHandler(void) { uint32_t irqFlags = HWREG(I2S_BASE + I2S_O_IRQFLAGS); // 处理输入DMA完成中断 if (irqFlags & (1 << 5)) { // AIF_DMA_IN 标志位 // 中断标志会在写入新指针后自动清除,但最好显式读取确认 // 1. 当前填充完成的缓冲区已就绪,可供应用程序处理 // currentDmaReadBuffer 指向刚满的缓冲区(例如bufferA) processAudioData(currentDmaReadBuffer, DMA_BUF_FRAMES * SAMPLES_PER_FRAME); // 2. 检查并更新NEXT指针,为DMA提供下一个空缓冲区 uint32_t nextPtr = HWREG(I2S_BASE + I2S_O_AIFINPTRNEXT); if (nextPtr == 0) { // 确定下一个空闲缓冲区是哪个 if (currentDmaReadBuffer == audioInputBufferA) { HWREG(I2S_BASE + I2S_O_AIFINPTRNEXT) = (uint32_t)audioInputBufferA; currentDmaReadBuffer = audioInputBufferB; // 下次读取B } else { HWREG(I2S_BASE + I2S_O_AIFINPTRNEXT) = (uint32_t)audioInputBufferB; currentDmaReadBuffer = audioInputBufferA; // 下次读取A } } else { // 指针未清零,说明上次提供的指针未被使用,可能发生指针错误 // 应进行错误处理,可能需要重启DMA handleDmaError(); } } // 处理其他错误中断(PTR_ERR, BUS_ERR, WCLK_ERR等) if (irqFlags & 0x07) { // 检查低3位错误标志 handleI2SError(irqFlags); // 错误标志是“粘性”的,需要软件清除 HWREG(I2S_BASE + I2S_O_IRQCLR) = (irqFlags & 0x07); } }

5. 高级话题:利用采样时间戳实现无线音频同步

CC13x2/CC26x2 I2S模块最强大的功能之一,就是通过采样时间戳生成器支持跨无线网络的音频同步。这对于实现多扬声器同步播放或无线麦克风低延迟传输至关重要。

5.1 同步原理

其核心思想是建立一个基于WCLK帧计数的“全局音频时间轴”。两个设备(一个主,一个从)的STMPWCNT计数器应该尽可能对齐,这样它们播放或录制的音频样本在时间上就是对应的。

  1. 主从设定:指定一个设备为音频时钟主设备(Master),它使用内部或稳定的外部时钟。另一个为从设备(Slave),其音频时钟(通常是VCXO)频率可以被微调。
  2. 无线事件捕获:当主设备通过射频发送一个包含同步信息的音频数据包时,它利用EVENT:I2SSTMPSEL0将一个射频事件(如同步字发送完成)路由到I2S模块,触发一次采样时间戳捕获。此时,主设备的STMPWCNTCAPT0STMPXCNTCAPT0被冻结,这个时间戳被打包进无线数据包。
  3. 从设备校准:从设备在收到该数据包时,也用同样的射频事件触发一次本地的时间戳捕获。通过比较主从设备的时间戳差值,从设备可以计算出自己的音频时钟是偏快还是偏慢。
  4. 时钟调整与计数器偏移:从设备通过两种方式调整:
    • 粗调:直接修改STMPWCNT(通过STMPWSETSTMPWADD),使其与主设备对齐。
    • 细调:微调外部VCXO的频率,使从设备的WCLK周期(STMPXPER)趋近于主设备,从而让两个计数器在长期运行中保持同步。

5.2 配置步骤概要

  1. 设置STMPWPER:主从设备设置为相同的值,且应为DMA缓冲区大小的整数倍。STMPWPER定义了时间轴的周期,也代表了音频延迟的帧数。
  2. 配置事件路由:使用EVENT:I2SSTMPSEL0寄存器,将射频核心(RFCORE)的某个事件(如RX_SYNCTX_SYNC)连接到I2S的采样时间戳捕获通道0。
  3. 实现同步算法:在射频协议栈的应用层,实现时间戳的交换、差值计算和时钟调整逻辑。这通常需要与射频协议(如专有RF协议或蓝牙LE Audio)深度集成。

经验之谈:实现稳健的无线音频同步是一个系统工程,除了I2S模块的精确计时,射频链路的稳定性、网络延迟的抖动、时钟源的温漂都会影响最终效果。在实际项目中,我们往往会在应用层增加一个自适应的缓冲区和丢包补偿算法,以应对网络波动,而将I2S的时间戳同步作为基础的时钟校准手段。

6. 常见问题排查与调试技巧

即使按照手册配置,在实际硬件调试中仍会遇到各种问题。以下是我总结的一些常见故障现象和排查思路。

6.1 无声或数据全为零

  • 检查时钟:这是最常见的问题。用示波器测量BCLK和WCLK引脚,确认有时钟信号,且频率符合预期。检查AIFWCLKSRC和PRCM的时钟源配置是否正确。
  • 检查数据线:测量ADx引脚,确认有数据变化。如果数据线一直是低电平或高电平,检查编解码器是否已正确初始化并进入工作模式。
  • 检查DMA指针:确认AIFINPTRNEXTAIFOUTPTRNEXT已被正确写入非零地址,并且AIFDMACFG.END_FRAME_IDX已写入非零值。通过调试器读取AIFINPTRAIFOUTPTR寄存器,看其是否在递增。如果不递增,说明DMA未启动。
  • 检查中断:使能AIF_DMA_IN/OUT中断,并在中断服务程序中设置断点或翻转一个GPIO。如果从未进入中断,可能是DMA未触发,或者中断未正确使能(检查IRQMASK和NVIC)。

6.2 音频失真、噪声或噼啪声

  • 检查缓冲区指针管理:这是导致周期性噪声或爆音的元凶。确保在DMA中断中及时更新了AIFINPTRNEXT/AIFOUTPTRNEXT指针。如果更新慢了,DMA会停止,导致数据丢失或重复。使用逻辑分析仪或调试器监控指针更新是否及时。
  • 检查采样时间戳触发:如果STMPINTRIG/STMPOUTTRIG设置不当,DMA可能在错误的WCLK周期开始存取数据,导致样本错位。确保在启动前将其设置为一个未来的、确定的值。
  • 检查WORD_LENDATA_DELAY:这两个寄存器配置错误会导致采样点错位,表现为严重失真。仔细对照编解码器数据手册的时序图,确认与I2S模块的配置匹配。特别是DATA_DELAY,对于I2S格式必须是1。
  • 检查内存对齐和缓冲区大小:如果MEM_LEN_24=0(16位存储),DMA指针必须是半字对齐(地址最低位为0)。缓冲区大小计算错误可能导致DMA访问越界,破坏其他内存数据。

6.3 DMA错误中断(PTR_ERR, BUS_ERR)

  • PTR_ERRAIFINPTRNEXTAIFOUTPTRNEXT在DMA需要下一个缓冲区时仍为0。确保你的指针更新代码路径执行时间足够短,不会错过DMA的请求。可以考虑使用双缓冲甚至多缓冲环形队列来提供更宽松的时间窗口。
  • BUS_ERR:DMA传输未能及时完成。这通常是因为系统总线(AHB)被更高优先级的主设备(如射频核心)长时间占用,导致DMA无法访问内存。可以尝试:
    • 将音频缓冲区放在CC26x2的SRAM中,而不是外部存储器。
    • 优化系统总线仲裁优先级(如果可能)。
    • 增加DMA缓冲区大小,降低DMA请求的频率。

6.4 利用调试功能

  • PWM调试输出AIFPWMVALUE寄存器可以生成一个与DMA缓冲区开始同步的PWM脉冲。你可以将这个内部信号映射到一个GPIO上,用示波器观察。这个脉冲的上升沿对应每个DMA缓冲区第一个样本MSB的开始时刻,是验证DMA启动和缓冲区边界是否正确的绝佳工具。
  • 监控计数器:在调试时,可以定期读取STMPWCNTSTMPXCNT,观察它们是否在规律地递增。这有助于判断采样时间戳生成器是否正常工作,WCLK信号是否稳定。

配置CC13x2/CC26x2的I2S模块就像在指挥一个精密的交响乐团,每个寄存器都是乐手,时序是指挥棒。最初的调试可能会让人感到繁琐,但一旦你理解了数据流、时钟域和缓冲区管理的内在逻辑,它就会变得非常直观和强大。这个模块的灵活性允许你实现从简单的语音采集到复杂的多设备无线音频系统等各种应用。最关键的是,要养成严谨的配置顺序习惯,充分利用中断和状态寄存器进行错误处理,并在硬件调试中善用示波器观察关键信号。纸上得来终觉浅,绝知此事要躬行,动手在开发板上实现一遍,很多抽象的概念会立刻变得具体起来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询