CC27xx I2S模块深度解析:从协议原理到无线音频同步实战

发布时间:2026/7/26 5:07:15

CC27xx I2S模块深度解析:从协议原理到无线音频同步实战 1. 项目概述在嵌入式音频开发领域I2SInter-IC Sound接口几乎是连接微控制器与外部音频编解码器、数字麦克风或DAC/ADC的“标准答案”。它不像I2C或SPI那样需要复杂的协议栈其核心思想简单而优雅用几根专用的时钟线和数据线实现音频采样数据的同步、连续传输。对于需要处理实时音频流的应用比如无线耳机、智能音箱、对讲机或者任何需要高质量音频输入输出的物联网设备深入理解并掌握I2S是绕不开的一步。最近在基于德州仪器CC27xx系列无线MCU开发一个低功耗音频传输项目时我花了大量时间啃它的技术参考手册。手册内容详尽但更像一本字典缺乏一个从“为什么这么设计”到“具体怎么操作”的连贯视角。特别是其内置的I2S模块功能相当强大不仅支持标准的I2S、左对齐、右对齐格式还集成了DMA、采样戳生成器等高级功能用于实现无线网络下的恒定音频延迟。这恰恰是很多无线音频产品如真无线立体声耳机的刚需。因此我决定结合手册内容和实际调试经验写一篇深度解析。本文不会停留在简单的引脚定义和初始化代码上而是会深入到时钟树的配置逻辑、DMA双缓冲机制如何避免音频卡顿、采样戳如何用于无线同步以及那些手册里一笔带过但实际开发中极易踩坑的细节。无论你是刚开始接触I2S还是正在CC27xx平台上进行音频开发希望这篇近万字的“实战笔记”能帮你理清思路少走弯路。2. I2S协议核心原理与CC27xx模块架构2.1 I2S协议的三线制与核心时序要玩转CC27xx的I2S模块首先得吃透I2S协议本身。它本质上是一个同步串行通信协议主要包含三根信号线串行时钟SCK/BCLK每一位数据的变化都跟随这个时钟。其频率 采样率 × 采样位数 × 通道数。例如44.1kHz采样率、16位、立体声2通道的音频BCLK频率至少为 44.1kHz × 16 × 2 1.4112 MHz。字选择WS/WCLK/LRCK用于指示当前传输的是左声道还是右声道数据。通常WS为低电平时传输左声道高电平时传输右声道。其频率就是音频的采样率如44.1kHz。串行数据SD/ADx承载实际的音频采样数据最高位MSB先行。CC27xx的模块在此基础上进行了扩展支持第四根线——主时钟MCLK。MCLK为外部编解码器提供稳定的系统时钟参考频率通常是BCLK的整数倍如256倍或512倍这对于需要高精度时钟的音频Codec至关重要。协议的精髓在于时序对齐。标准I2S格式下数据在BCLK的下降沿变化在上升沿被采样。WS信号的变化即声道切换发生在BCLK下降沿之后的一个BCLK周期。这种设计确保了数据在WS边沿变化时是稳定的避免了亚稳态问题。CC27xx模块完全遵循这一时序并可通过寄存器灵活配置数据在哪个时钟沿采样和更新。2.2 CC27xx I2S模块的整体设计思路CC27xx的I2S模块不是一个简单的串行移位寄存器而是一个高度集成、为无线音频流优化的硬件引擎。它的设计目标很明确在极低的MCU干预下实现音频数据在内存和外部音频设备间的高效、稳定搬运并能与无线射频BLE子系统协同工作维持端到端的固定延迟。其核心组件可以拆解为以下几部分时钟生成单元可以内部生成由PRCM模块分频或外部输入MCLK、BCLK、WCLK。内部生成方便但可能引入抖动外部输入时钟质量更高常用于主设备模式。串行数据接口包含两个独立的数据引脚AD0, AD1每个都可配置为输入或输出。这为立体声或多通道如TDM模式应用提供了灵活性。格式控制器支持I2S、左对齐LJF、右对齐RJF和DSP单相四种格式。这是通过配置AIFFMTCFG寄存器中的DUAL_PHASE,SMPL_EDGE,DATA_DELAY等字段实现的。DMA与内存接口这是模块的“心脏”。它内置了DMA控制器可以自动将接收到的音频数据存入指定的内存缓冲区或从内存缓冲区读取数据发送出去。开发者只需提前设置好缓冲区指针DMA便会自动管理数据搬运极大减轻了CPU负担。采样戳生成器Samplestamp Generator这是实现无线音频同步的“秘密武器”。它内部有两个计数器一个以WCLK为基准计数音频帧STMPWCNT另一个以系统高速时钟96MHz为基准计数帧内的时钟周期STMPXCNT。通过捕获特定事件如射频包同步字时刻的计数器值可以精确计算出音频采样的时间戳用于对齐无线网络中不同节点的音频流。这种架构使得CC27xx的I2S模块特别适合构建主从式的无线音频系统例如一个作为音频源Master的设备和多个作为接收端Slave的耳机通过采样戳机制来同步播放消除声音的断续或延迟差异。3. 时钟配置详解内部生成与外部输入的权衡时钟是数字音频的“心跳”配置不当直接导致杂音、爆音或根本不出声。CC27xx给了我们两种选择内部生成或使用外部时钟源。这个选择没有绝对的对错只有是否适合你的应用场景。3.1 内部时钟生成便捷与局限内部时钟由MCU的96MHz主时钟MCUCLK通过可编程分频器产生。相关寄存器是MCLKDIV.MDIV,BCLKDIV.BDIV,WCLKDIV.WDIV以及CLKCTL。配置步骤与计算示例假设我们需要生成一个标准的44.1kHz立体声音频流采样位宽为16位采用I2S格式。计算BCLK频率BCLK 采样率 × 位宽 × 通道数 44.1kHz × 16 × 2 1.4112 MHz。计算BDIV分频系数BDIV MCUCLK / BCLK。假设MCUCLK 48MHz则BDIV 48MHz / 1.4112MHz ≈ 34.01。分频系数必须为2到1024之间的整数且不能为1。34不是96MHz的整数分频会产生误差。更常见的做法是让MCUCLK运行在96MHz这样BDIV 96MHz / 1.4112MHz ≈ 68.027依然不是整数。这就是内部时钟的第一个局限它只能生成96MHz的整数分频频率。对于44.1kHz系列采样率直接分频无法得到精确的BCLK会引入固定的频率误差可能导致长期运行时音频缓冲区上溢或下溢。计算WCLK与WDIVWCLK就是采样率44.1kHz。在双相模式I2S下WCLK MCUCLK / (BDIV * 2 * WDIV)。因此WDIV MCUCLK / (BDIV * 2 * WCLK) 96MHz / (68 * 2 * 44.1kHz) ≈ 16.0。这里WDIV恰好是整数16但前提是BDIV取68。然而68的BDIV产生的BCLK是1.4117647MHz有轻微误差。配置寄存器BCLKDIV.BDIV 68WCLKDIV.WDIV 16且CLKCTL.WCLKPHASE 1双相模式CLKCTL.WBEN 1使能BCLK/WCLK生成MCLKDIV.MDIV可根据外部Codec需求配置例如生成256倍采样率的MCLKMCLK 44.1kHz * 256 11.2896MHzMDIV 96MHz / 11.2896MHz ≈ 8.5同样无法精确生成。此时可能需要选择接近的整数如8或9或者放弃使用MCLK。注意内部时钟生成简单无需外部晶振适合对时钟精度要求不高的应用或采样率恰好是96MHz整数分频的情况如48kHz, 32kHz。但对于高保真音频其固有的频率误差和可能较差的时钟抖动Jitter性能是主要缺点。3.2 外部时钟输入高精度与灵活性当使用外部高质量的音频时钟源如专用音频晶振或主设备提供的时钟时你需要将CC27xx配置为时钟从模式。引脚配置将MCLK、BCLK、WCLK对应的MCU引脚配置为输入功能。源选择设置AIFWCLKSRC.WBCLKSRC 1选择外部pad输入作为时钟源。时钟域隔离手册特别警告一旦选择了时钟源并开始向BCLK域提供时钟就不能再动态更改AIFWCLKSRC.WBCLKSRC的配置否则会导致时钟域亚稳态。这意味着你必须在启动DMA和采样戳生成器之前就确定并固定时钟源。外部时钟的优势极高的精度和低抖动时钟质量由外部晶振保证音质更好。支持任意采样率只要外部时钟源能产生CC27xx就能接受不受96MHz分频限制。支持主从同步在无线音频系统中可以指定一个节点为“音频时钟主设备”其他节点同步到它的时钟这是实现恒定延迟的关键。外部时钟的挑战需要额外的时钟源增加了BOM成本和PCB空间。时钟丢失处理如果外部时钟意外停止模块会通过IRQFLAGS.WCLK_TIMEOUT中断通知你软件需要妥善处理如静音、进入安全状态。我的经验在追求音质的项目中我倾向于使用外部时钟。一个实用的技巧是即使使用外部时钟也可以同时使能内部时钟生成作为备份。在检测到WCLK_TIMEOUT错误后快速切换到内部时钟虽然质量下降但系统不至于哑火同时尝试恢复外部时钟或通知用户。4. 数据格式、DMA与内存管理实战配置好时钟接下来就是让数据正确地流动起来。CC27xx的I2S模块在数据格式处理和DMA搬运方面设计得非常周到。4.1 四种串行格式的寄存器配置对照模块支持I2S、左对齐LJF、右对齐RJF和DSP格式。选择哪种格式取决于你连接的外部音频设备。配置主要通过AIFFMTCFG寄存器完成。格式DUAL_PHASESMPL_EDGEDATA_DELAYWORD_LEN关键时序特征I2S111最大位数WS变化后延迟1个BCLK发送MSB。左对齐(LJF)110最大位数WS变化的同时发送MSB。右对齐(RJF)11Phase_Bits - WORD_LEN精确位数LSB对齐在WS变化边沿。DSP000 或 1精确位数单相模式WS是一个脉冲后面紧跟多个通道的数据。配置要点解析WORD_LEN在双相模式I2S/LJF/RJF下它表示最大支持的位数8-24。如果实际串行数据位数小于此值LSB部分补零如果大于则高位被截断。在单相DSP模式下它必须是精确的位数。DATA_DELAY对于RJF格式其值等于一个相位内的BCLK周期数减去WORD_LEN。例如如果相位长度是32个BCLK即16位立体声WORD_LEN16那么DATA_DELAY 32 - 16 16。这个计算必须由软件完成。SMPL_EDGE决定了数据在哪个BCLK边沿被模块采样输入或更新输出。1代表在上升沿采样下降沿更新这是I2S最常见的方式。4.2 DMA双缓冲机制与“乒乓”操作这是保证音频流连续不中断的核心。CC27xx的I2S DMA采用了典型的双缓冲或称“乒乓缓冲”机制。工作原理软件准备两个内存缓冲区Buffer A和Buffer B。启动时先将Buffer A的地址写入AIFINPTRNEXT输入或AIFOUTPTRNEXT输出然后配置AIFDMACFG.END_FRAME_IDX定义每个缓冲区包含多少帧音频数据来启动DMA。DMA立即将AIFINPTRNEXT的值加载到当前指针AIFINPTR并开始使用Buffer A。关键一步在DMA使用Buffer A期间软件必须立即将Buffer B的地址写入AIFINPTRNEXT。当DMA填满或发送完Buffer A后会自动将AIFINPTRNEXT此时是Buffer B的地址加载到AIFINPTR开始处理Buffer B同时将AIFINPTRNEXT寄存器清零并产生IRQFLAGS.AIF_DMA_IN中断。在中断服务程序ISR中软件处理已经满的Buffer A例如编码、发送并再次将下一个空闲缓冲区可能是A如果B还在用的地址写入AIFINPTRNEXT为DMA的下一次切换做好准备。如此循环往复就像两个人打乒乓球一个缓冲区在被DMA使用的同时另一个缓冲区可以被CPU处理实现了并行操作避免了音频数据的丢失或重复。配置与避坑指南缓冲区大小计算AIFDMACFG.END_FRAME_IDX设置的是缓冲区包含的帧数减一。一帧包含所有通道的一个样本。例如立体声2通道下一帧就是2个样本。如果你希望每个缓冲区存放10ms的音频采样率是16kHz那么总样本数为16000 Hz * 0.01s 160个样本。因为是立体声所以帧数为160 / 2 80帧。那么END_FRAME_IDX应设置为80 - 1 79。内存对齐如果AIFFMTCFG.MEM_LEN_32 016位内存访问DMA指针必须是半字2字节对齐的。如果MEM_LEN_32 1则无需特别对齐。务必确保你的缓冲区地址符合要求。指针更新时机必须在当前缓冲区用完之前提前设置好下一个缓冲区指针。如果DMA完成当前缓冲区时发现AIFINPTRNEXT还是0会触发IRQFLAGS.PTR_ERR错误并停止DMA。这是最常见的错误之一。我的做法是在DMA启动后立即预填两个缓冲区指针然后在每次DMA完成中断中迅速填充下一个指针。关闭顺序停止DMA时不能直接禁用模块。应先停止填充AIFINPTRNEXT等待当前DMA传输完成指针寄存器变为0或者等待PTR_ERR错误发生最后再将AIFDMACFG.END_FRAME_IDX写0。对于外部时钟源还需要先停止外部时钟等待WCLK_TIMEOUT后再执行上述操作防止DMA在时钟停止时访问内存出错。5. 采样戳生成器实现无线音频同步的关键对于TWS耳机、无线话筒这类对唇音同步要求极高的应用单纯的音频流传输不够必须解决无线传输带来的可变延迟问题。CC27xx的采样戳生成器就是为此而生。5.1 采样戳的工作原理你可以把它想象成一个高精度的、与音频帧严格绑定的定时器系统。STMPWCNT以WCLK即采样率为基准的计数器。每产生一个WCLK边沿通常是一帧开始它就加1并在达到STMPWPER设定的周期后归零。STMPWPER通常设置为DMA缓冲区大小的整数倍例如N * (END_FRAME_IDX 1)这样STMPWCNT的值就直接对应了当前音频数据在环形缓冲区中的位置索引。STMPXCNT以系统高速时钟96MHz为基准的计数器。它在每个WCLK周期开始时清零然后开始计数直到下一个WCLK边沿到来。因此STMPXCNT记录了一个WCLK周期内经过的“子时钟”数。读取STMPWCNT时会自动锁存当前的STMPXCNT值到STMPXCNTCAPT0。STMPXPER记录上一个完整的WCLK周期内包含了多少个96MHz时钟周期。这个值动态反映了WCLK的实际频率。通过捕获特定事件例如射频模块发送或接收到一个数据包的同步头瞬间的STMPWCNTCAPT0和STMPXCNTCAPT0我们可以计算出一个高精度的“采样戳”Samplestamp STMPWCNTCAPT0 (STMPXCNTCAPT0 / STMPXPER)。这个值是一个定点数整数部分是帧计数小数部分是该帧内的精确时间偏移。5.2 实现恒定音频延迟的步骤假设我们有一个音频发送设备Master和一个接收设备Slave。主从定义与时钟Master使用内部或高精度时钟作为音频时钟源。Slave配置为使用外部时钟源并且该时钟源的频率可以通过某些手段微调例如通过改变时钟生成器的分频系数。缓冲区与采样戳周期对齐Master和Slave都设置STMPWPER N * (END_FRAME_IDX 1)。这样整个音频缓冲区在采样戳计数空间里是一个完整的循环。时间戳交换Master在通过射频发送一个音频数据包时捕获发送同步字时刻的采样戳T_master_tx并将这个值放入射频包中一起发送。Slave在接收到这个射频包时捕获接收同步字时刻的采样戳T_slave_rx。延迟计算与补偿Slave计算时间差Delta T_slave_rx - T_master_tx。这个Delta包含了射频空中传输时间、处理延迟等。Slave的目标是让它的音频播放与Master的音频采集保持一个固定的、已知的延迟例如20ms。假设目标延迟是Target_Latency以帧数为单位。Slave在启动播放时需要设置DMA的启动触发点STMPOUTTRIG。这个值应该根据T_slave_rx、Delta和Target_Latency来计算使得Slave播放某一帧音频的时刻恰好比Master采集该帧的时刻晚Target_Latency。更高级的做法是Slave根据连续计算的Delta值动态微调自己的外部音频时钟频率如果支持使Delta趋向于一个稳定值从而对抗时钟源的微小漂移。配置流程配置好I2S基本功能时钟、格式、DMA缓冲区。设置STMPWPER。将STMPINTRIG和STMPOUTTRIG初始化为一个大于STMPWPER的值如0xFFFF避免意外触发。使能采样戳生成器STMPCTL.STMP_EN 1。在适当的时候如与射频同步后通过STMPWSET或STMPWADD调整STMPWCNT使其与主设备对齐。设置正确的STMPINTRIG和STMPOUTTRIG值启动DMA。注意采样戳的捕获是由外部事件来自RFCORE触发的需要正确配置事件路由。这部分涉及射频子系统配置需要查阅CC27xx的射频相关文档。6. 完整工程实践从初始化到错误处理纸上得来终觉浅绝知此事要躬行。下面我将结合代码片段以C语言为例梳理一个典型的I2S音频播放初始化序列。6.1 启动序列Start-Up Sequence必须严格按照手册规定的顺序进行配置否则模块可能无法正常工作。// 假设使用内部时钟播放44.1kHz 16-bit 立体声 I2S格式音频 void I2S_InitForPlayback(void) { // 1. 设置依赖项例如使能I2S模块的电源和时钟 PRCMPowerDomainOn(PRCM_DOMAIN_PERIPH); while(PRCMPowerDomainStatus(PRCM_DOMAIN_PERIPH) ! PRCM_DOMAIN_POWER_ON); PRCMPeripheralRunEnable(PRCM_PERIPH_I2S); PRCMLoadSet(); while(!PRCMLoadGet()); // 2. 配置引脚功能 (AD0输出 AD1未使用 BCLK, WCLK, MCLK输出) IOCPinTypeI2s(I2S_BASE, IOID_xx, IOID_yy, ...); // 使用TI驱动库函数或直接配置IOCFG寄存器 // 3. 配置串行格式: I2S, 双相, 上升沿采样 16位字长 HWREG(I2S_BASE I2S_O_FMTCFG) (1 I2S_FMTCFG_DUAL_PHASE_S) | // 双相模式 (1 I2S_FMTCFG_SMPL_EDGE_S) | // 上升沿采样 (0 I2S_FMTCFG_MEM_LEN_32_S) | // 内存使用16位访问 (1 I2S_FMTCFG_DATA_DELAY_S) | // I2S格式要求DATA_DELAY1 (16 I2S_FMTCFG_WORD_LEN_S); // 最大字长16位 // 4. 配置通道映射: AD0引脚传输立体声左和右通道 HWREG(I2S_BASE I2S_O_WMASK0) 0x03; // 位0和位1置1使能通道0和1 // 5. 配置内部时钟生成 (假设MCUCLK48MHz目标BCLK1.4112MHz, WCLK44.1kHz) // 计算分频系数: BDIV 48MHz / 1.4112MHz 34 (近似) // WDIV 48MHz / (34 * 2 * 44.1kHz) ≈ 16.0 HWREG(I2S_BASE I2S_O_BCLKDIV) 34; HWREG(I2S_BASE I2S_O_WCLKDIV) 16; HWREG(I2S_BASE I2S_O_MCLKDIV) 8; // 生成~6MHz MCLK示例值 HWREG(I2S_BASE I2S_O_CLKCTL) (1 I2S_CLKCTL_WBEN_S) | // 使能WCLK/BCLK生成 (1 I2S_CLKCTL_MEN_S); // 使能MCLK生成 // 选择内部时钟源 HWREG(I2S_BASE I2S_O_WCLKSRC) 0x2; // 内部PRCM时钟源 // 6. DMA启动序列 (双缓冲) // 假设有两个缓冲区: audioBuffer[0] 和 audioBuffer[1], 每个大小 80帧 * 2通道 * 2字节 320字节 uint16_t audioBuffer[2][160]; // 160个样本 80帧 * 2通道 uint32_t buffer_size_in_frames 80; // a. 预加载第一个DMA指针 HWREG(I2S_BASE I2S_O_OUTPTRNXT) (uint32_t)audioBuffer[0]; // b. 配置DMA缓冲区大小并启动DMA引擎 HWREG(I2S_BASE I2S_O_DMACFG) buffer_size_in_frames - 1; // END_FRAME_IDX // 此时DMA会将OUTPTRNXT加载到OUTPTR并开始预取第一个样本 // c. 立即预加载第二个DMA指针 HWREG(I2S_BASE I2S_O_OUTPTRNXT) (uint32_t)audioBuffer[1]; // 7. 配置并启动采样戳生成器如果不需要无线同步可简单配置 HWREG(I2S_BASE I2S_O_STMPWPER) buffer_size_in_frames; // 周期等于缓冲区大小 HWREG(I2S_BASE I2S_O_STMPOUTTRIG) 0xFFFF; // 初始值设大避免误触发 // 等待一段时间让WCLK计数器跑起来 DelayMs(1); // 设置触发点为当前计数器值2手册建议 uint16_t current_wcnt HWREG(I2S_BASE I2S_O_STMPWCNT) 0xFFFF; HWREG(I2S_BASE I2S_O_STMPOUTTRIG) (current_wcnt 2) % buffer_size_in_frames; // 使能采样戳生成器 HWREG(I2S_BASE I2S_O_STMPCTL) | I2S_STMPCTL_STMP_EN; // 8. 使能中断如果需要 HWREG(I2S_BASE I2S_O_IRQMASK) | I2S_IRQMASK_DMAOUT; // 使能DMA输出完成中断 // ... 注册中断服务程序到系统 }6.2 中断服务程序与缓冲区管理DMA完成中断是维持音频流连续性的生命线。// I2S中断服务程序 void I2S_IRQHandler(void) { uint32_t irqFlags HWREG(I2S_BASE I2S_O_IRQFLAGS); // 处理输出DMA完成中断 if(irqFlags I2S_IRQFLAGS_DMAOUT) { // 中断标志会在写入新的OUTPTRNXT后自动清除这里通常不需要手动清除 // 1. 处理刚刚播放完的缓冲区例如标记为空闲 currentPlayedBufferIndex (currentPlayedBufferIndex 1) % 2; // 2. 用新的音频数据填充即将被DMA使用的“下一个”缓冲区 // (注意此时DMA可能正在使用另一个缓冲区我们填充的是“下下一个”) uint32_t nextBufferIndex (currentPlayedBufferIndex 1) % 2; FillAudioBuffer(audioBuffer[nextBufferIndex]); // 3. 将填充好的缓冲区地址写入OUTPTRNEXT HWREG(I2S_BASE I2S_O_OUTPTRNXT) (uint32_t)audioBuffer[nextBufferIndex]; } // 处理错误中断必须软件清除 if(irqFlags I2S_IRQFLAGS_PTRERR) { // DMA指针未及时更新音频流已中断 HWREG(I2S_BASE I2S_O_IRQCLR) I2S_IRQCLR_PTRERR; // 写1清除 // 需要进行错误恢复例如重新初始化I2S流 HandleI2SError(); } if(irqFlags I2S_IRQFLAGS_WCLK_TIMEOUT) { // 外部时钟丢失 HWREG(I2S_BASE I2S_O_IRQCLR) I2S_IRQCLR_WCLKTIMEOUT; // 切换到内部时钟或进入安全状态 SwitchToInternalClock(); } // ... 处理其他错误 }6.3 常见问题排查与调试技巧在实际开发中你可能会遇到以下问题没有声音输出/输入检查时钟用示波器测量BCLK、WCLK、MCLK如果使用是否正常产生频率是否正确。确认CLKCTL.WBEN和MEN已使能。检查数据线测量ADx引脚是否有数据波形。如果没有检查DIRCFG寄存器配置是否正确输入/输出WMASKx寄存器是否使能了对应通道。检查DMA确认AIFDMACFG.END_FRAME_IDX已设置为非零值。检查AIFOUTPTR或AIFINPTR寄存器是否在变化这表示DMA正在工作。检查格式确认AIFFMTCFG寄存器配置DUAL_PHASE,SMPL_EDGE,DATA_DELAY,WORD_LEN与外部音频设备完全匹配。一个常见的错误是I2S格式的DATA_DELAY没有设置为1。音频有周期性“咔嗒”声或断音缓冲区欠载/过载这是最可能的原因。检查DMA中断服务程序是否执行时间过长导致未能及时提供下一个缓冲区地址。优化ISR代码或者增大DMA缓冲区大小增加END_FRAME_IDX。时钟不匹配如果使用内部时钟计算出的分频系数不是整数会导致长期漂移最终缓冲区上溢或下溢。考虑使用支持所需采样率的精确外部时钟。中断优先级确保I2S的DMA中断有足够高的优先级不能被其他长时间中断阻塞。采样戳同步不准确事件触发时机确保射频同步字的捕获事件与音频采样戳的捕获紧密关联并且两者之间的处理延迟是稳定且可测量的。STMPWPER设置确保STMPWPER是DMA缓冲区大小的整数倍否则STMPWCNT与缓冲区索引的对应关系会错乱。时钟漂移主从设备间的时钟微小的频率差异会逐渐累积成大的延迟偏差。需要实现闭环的时钟调整算法根据计算出的时间差Delta动态调整Slave的音频时钟频率如果硬件支持。利用调试总线DTBCC27xx的I2S模块提供了一个数字测试总线DTB可以通过DTB.SEL寄存器选择将内部一些信号如采样戳计数器、数据使能信号输出到特定GPIO上。这在没有逻辑分析仪的情况下是判断模块内部状态如DMA是否在搬运、采样戳是否在计数的宝贵手段。最后的建议开始调试时可以先从最简单的环回测试开始。将AD0输出直接短接到AD1输入配置一个为输出一个为输入然后让MCU发送一个固定的正弦波数据再通过DMA接收回来。如果环回数据正确说明I2S模块本身、时钟和DMA配置基本正确问题可能出在外设或后续处理链上。这种分步验证的方法能极大提高调试效率。

相关新闻