首页 / 资讯中心 / 文章详情

AC108多mic矩阵驱动:TDM配置与四路同步采集实战

AC108多mic矩阵驱动:TDM配置与四路同步采集实战 ★ FEATURED ARTICLE
简介AC108多mic矩阵驱动芯片设计资料包面向音频硬件工程师、嵌入式驱动开发者及语音算法从业者聚焦多通道麦克风阵列的电路设计与系统集成。内容覆盖芯片概述、硬件设计指南、用户手册、驱动说明、datasheet与参考原理图六大模块可支撑智能手机、智能音箱、物联网设备等场景下的语音识别、噪声抑制与回声消除方案落地。资源共23个文件以pdf技术文档、db与dsn原理图工程、sch原理图、opj工程文件为主另含h头文件、c源码及doc说明压缩包约11.97MB结构按brief、datasheet、硬件设计、参考原理图、驱动与说明分层组织。已有977人学习下载。读者可据此掌握AC108的电源与接地策略、信号路由、抗干扰设计、采样率与通道配置、I2C/SPI通信及驱动集成方法并借助8MIC EVB参考原理图与SPK_MIC回路说明快速完成产品音频链路评估与调试。1. AC108 多 mic 矩阵驱动芯片从数据手册到阵列落地的关键路径如果你正在做远场拾音、声源定位或者波束成形大概率绕不开一个核心器件选型问题多路麦克风同步采集到底用什么方案。AC108 这颗芯片在嵌入式音频圈子里被反复提及原因很直接——它用一根 I2S 总线就能把四路麦克风的数据同时抓回来省掉了外挂多路 ADC 的麻烦。但很多人拿到数据手册之后卡在同一个地方寄存器能看懂板子也画出来了数据却对不上通道串扰、时钟不同步、增益不一致这些问题一个接一个。这篇笔记不讲空泛的芯片概述而是把 AC108 从选型理由、寄存器配置、TDM 时序调试到阵列校准的完整链路拆开让你能在自己的板子上跑通四路同步采集并且知道每一步的参数为什么这么设、出了问题该看哪个寄存器。2. AC108 的 TDM 与 I2S 模式到底怎么选时序、通道与主从配置2.1 为什么多 mic 矩阵优先走 TDM 而不是多路 I2S先把这个选型问题说清楚。AC108 支持标准 I2S 模式和 TDM 模式两种输出方式。标准 I2S 模式下左右声道各占一个 slot最多传两路音频TDM 模式则是在同一个帧同步信号下把多个通道的数据依次排列在 DIN/DOUT 线上。对于四 mic 阵列来说如果走标准 I2S你需要两颗 AC108 或者一颗芯片分时复用前者增加成本和布线复杂度后者引入通道间的时间偏差。TDM 模式的核心优势在于所有通道共享同一个 WSWord Select和 BCLKBit Clock四个通道的数据在同一个帧内完成传输采样时刻严格对齐。这对波束成形和声源定位来说是硬性要求——通道间哪怕有半个采样周期的偏差相位差计算就会出错波束指向直接偏掉。常见做法是把 AC108 配成 TDM 主模式由它产生 BCLK 和 WS后级 SoC 或 DSP 作为从设备接收。这样时钟抖动由 AC108 的 PLL 决定比 SoC 分频出来的时钟更干净。如果你的主控有低抖动的 MCLK 输出也可以让 AC108 做从模式但需要确认 MCLK 和 BCLK 的相位关系满足建立保持时间。2.2 TDM 帧结构与 AC108 的 slot 分配逻辑AC108 的 TDM 输出帧结构由几个关键寄存器决定。每个音频帧包含一个 WS 周期WS 的占空比通常是 50%前半帧传左声道 slot后半帧传右声道 slot。在 TDM 模式下AC108 把四个 ADC 通道的数据映射到连续的 slot 中每个 slot 的位宽由 BCLK 频率和采样率共同决定。假设采样率 48kHzBCLK 设为 12.288MHz那么每帧的 BCLK 周期数是 12.288M / 48k 256 个。四个通道各占 32 bit实际有效 24 bit高位补零总共 128 bit剩下的 128 个 BCLK 周期用于帧间隔。这个配置下 WS 的高电平持续 128 个 BCLK低电平也是 128 个。寄存器层面需要关注这几个寄存器地址功能推荐值说明0x00芯片使能0x01bit0 置 1 开启全局0x01采样率配置根据实际48k 对应分频系数0x02模式选择0x03bit0-1 设为 TDM 主模式0x03slot 位宽0x2032bit per slot0x04通道使能0x0F开启 CH0-CH30x10-0x13CH0-CH3 增益0x000dB 初始值这些寄存器的具体地址和位定义需要对照你手上那颗 AC108 的版本手册不同批次可能有细微差异。我一般会先用 I2C 工具把 0x00 到 0x04 读一遍确认芯片 ID 和默认值再逐条写配置。2.3 主从模式下的时钟树配置与常见翻车点时钟配置是 AC108 调试里最容易翻车的地方。主模式下AC108 内部 PLL 从 MCLK通常是 24.576MHz 或 12.288MHz 晶振倍频出 BCLK。PLL 的锁定时间大约在 10ms 量级上电后不能立刻发 I2S 数据否则前几帧是垃圾数据。一个血泪经验如果你用 SoC 的 I2S 做从设备务必确认 SoC 端的 I2S 接收 FIFO 在 AC108 时钟稳定之前处于复位状态。我遇到过 SoC 在 BCLK 还没稳定时就开始采样结果通道 0 和通道 2 的数据错位查了两天才发现是时钟建立时间不够。从模式下外部 MCLK 必须满足 AC108 的抖动要求。一般要求 MCLK 的周期抖动小于 100ps RMS否则 ADC 的 SNR 会明显下降。如果你用 SoC 的 GPIO 分频出 MCLK先拿示波器看一下眼图别偷懒。3. 用 I2C 把 AC108 配起来寄存器写入序列与验证方法3.1 上电初始化序列从复位到第一帧数据AC108 的上电流程有严格的顺序要求。先给 AVDD 和 DVDD 上电等电源稳定后再拉高复位引脚复位保持至少 1ms。复位释放后I2C 接口就绪但此时芯片内部 PLL 还没锁定不能开始采集。下面是一段用 Python 通过 I2C 适配器配置 AC108 的示例代码。假设 I2C 地址是 0x3B7 位地址总线编号为 1。import smbus2 import time bus smbus2.SMBus(1) AC108_ADDR 0x3B def write_reg(reg, val): 写单个寄存器 bus.write_byte_data(AC108_ADDR, reg, val) time.sleep(0.001) # 每个寄存器写入后留 1ms 稳定时间 def read_reg(reg): 读单个寄存器 return bus.read_byte_data(AC108_ADDR, reg) # 步骤 1软复位 write_reg(0x00, 0x00) time.sleep(0.01) write_reg(0x00, 0x01) # 释放复位全局使能 time.sleep(0.05) # 等 PLL 锁定 # 步骤 2配置采样率和时钟 write_reg(0x01, 0x08) # 48kHzMCLK 12.288MHz write_reg(0x02, 0x03) # TDM 主模式BCLK 输出 write_reg(0x03, 0x20) # 32bit slot 位宽 # 步骤 3使能四个通道 write_reg(0x04, 0x0F) # CH0-CH3 全部开启 # 步骤 4设置初始增益 for ch in range(4): write_reg(0x10 ch, 0x00) # 0dB # 步骤 5回读验证 for reg in [0x00, 0x01, 0x02, 0x03, 0x04]: val read_reg(reg) print(fReg 0x{reg:02X} 0x{val:02X})这段代码的逻辑很直接先复位再释放等 PLL 锁定然后按顺序写模式、位宽、通道使能、增益。每个寄存器写入后留 1ms 是为了满足 I2C 时序和内部状态机的切换时间。最后回读一遍确认写入的值和读回的值一致。如果某个寄存器读回不对先检查 I2C 上拉电阻和总线速率AC108 的 I2C 最高支持 400kHz但初始化阶段建议降到 100kHz。3.2 增益与高通滤波器的寄存器级调优AC108 每个通道有独立的 PGA可编程增益放大器和数字高通滤波器。PGA 的增益范围通常是 -6dB 到 30dB步进 1dB。寄存器 0x10 到 0x13 分别对应 CH0 到 CH3 的增益值写入的值是增益编码不是直接的 dB 数。具体映射关系看手册里的增益表。高通滤波器用来去掉直流偏置和低频环境噪声。寄存器 0x20 控制 HPF 的截止频率一般设 20Hz 到 100Hz 之间。对于语音拾音截止频率设 80Hz 左右比较合适既能滤掉风噪和桌面振动又不会衰减人声的基频。调增益的时候有个实用技巧先把所有通道增益设成一样的值用同一个声源在相同距离下录一段粉红噪声看四个通道的 RMS 电平差异。如果差异超过 1dB说明麦克风灵敏度不一致或者 PCB 走线不对称需要在增益上做补偿。补偿值直接写进对应通道的增益寄存器。3.3 用示波器抓 TDM 波形验证通道映射寄存器配完之后别急着跑应用层代码。先拿示波器或者逻辑分析仪抓 BCLK、WS 和 DOUT 三根线。触发条件设在 WS 的下降沿看 DOUT 上的数据流。正常的 TDM 波形应该是WS 高电平期间DOUT 依次输出 CH0 和 CH1 的数据WS 低电平期间输出 CH2 和 CH3 的数据。每个 slot 32 个 BCLK数据 MSB 在前。如果你看到某个 slot 全是 0 或者全是 1说明对应通道没使能或者麦克风没接好。我一般会用一个简单的测试方法用手指依次轻触四个麦克风同时用逻辑分析仪抓 DOUT。正常的话每次触碰对应通道的 slot 数据会有明显变化。如果碰 CH0 但 CH2 的数据在变那就是 slot 映射反了需要调整寄存器 0x03 的通道顺序位。4. 四路同步采集的软件层实现从 I2S 读到 PCM 文件4.1 Linux 下的 I2S 采集ALSA 配置与多通道绑定在 Linux 系统上AC108 通常通过 SoC 的 I2S 控制器接入 ALSA 框架。你需要确认设备树里 I2S 节点的 compatible 属性匹配你的 SoC 驱动并且 dai-link 的 cpu-dai 和 codec-dai 正确绑定。一个常见的四通道采集命令是用 arecordarecord -D hw:0,0 -f S32_LE -r 48000 -c 4 -d 10 -t raw /tmp/mic_array.raw参数说明-D hw:0,0指定声卡和设备号-f S32_LE是 32 位小端格式AC108 输出 24 位有效数据高位补零-r 48000采样率-c 4四通道-d 10录 10 秒-t raw输出裸 PCM 数据。录完之后用 Python 读进来检查通道数据import numpy as np # 读取裸 PCM32 位小端四通道交织 data np.fromfile(/tmp/mic_array.raw, dtypenp.int32) data data.reshape(-1, 4) # 每行四个通道 # 检查各通道 RMS for ch in range(4): rms np.sqrt(np.mean(data[:, ch].astype(np.float64)**2)) print(fCH{ch} RMS {rms:.2f}) # 检查通道间相关性用第一通道做参考 ref data[:, 0].astype(np.float64) for ch in range(1, 4): corr np.corrcoef(ref, data[:, ch].astype(np.float64))[0, 1] print(fCH0 vs CH{ch} correlation {corr:.4f})这段代码做了两件事算每个通道的 RMS 看增益是否一致算通道间相关系数看是否存在串扰。正常情况下来自同一声源的信号通道间相关系数应该在 0.8 以上如果某个通道相关系数低于 0.5要么是麦克风坏了要么是 TDM slot 映射错了。4.2 通道对齐与相位差补偿的代码实现四路麦克风在 PCB 上的物理间距会导致声波到达时间不同这个时间差在波束成形里是有用的信号但在做通道一致性检查时需要先补偿掉。假设麦克风阵列是线性排列间距 4cm声速 343m/s那么相邻麦克风的最大时间差是 0.04 / 343 ≈ 116 微秒对应 48kHz 采样率下约 5.6 个采样点。def align_channels(data, ref_ch0, max_delay10): 用互相关对齐各通道到参考通道 aligned np.zeros_like(data) aligned[:, ref_ch] data[:, ref_ch] ref data[:, ref_ch].astype(np.float64) for ch in range(data.shape[1]): if ch ref_ch: continue sig data[:, ch].astype(np.float64) # 计算互相关 corr np.correlate(ref, sig, modefull) delay np.argmax(corr) - (len(sig) - 1) delay np.clip(delay, -max_delay, max_delay) # 按延迟移位对齐 if delay 0: aligned[delay:, ch] data[:-delay, ch] elif delay 0: aligned[:delay, ch] data[-delay:, ch] else: aligned[:, ch] data[:, ch] print(fCH{ch} delay {delay} samples) return aligned这个函数的逻辑是用互相关找每个通道相对于参考通道的延迟然后移位对齐。max_delay限制最大补偿范围防止把噪声的相关峰误判成信号。对齐之后再做通道间一致性检查结果才可靠。4.3 把四通道数据写成 WAV 文件做离线分析裸 PCM 不方便用音频工具打开写个简单的 WAV 封装函数import struct def write_wav(filename, data, sample_rate48000): 把多通道 int32 数据写成 32bit WAV n_channels data.shape[1] n_frames data.shape[0] bits_per_sample 32 byte_rate sample_rate * n_channels * bits_per_sample // 8 block_align n_channels * bits_per_sample // 8 with open(filename, wb) as f: # RIFF header f.write(bRIFF) f.write(struct.pack(I, 36 n_frames * block_align)) f.write(bWAVE) # fmt chunk f.write(bfmt ) f.write(struct.pack(I, 16)) f.write(struct.pack(H, 1)) # PCM f.write(struct.pack(H, n_channels)) f.write(struct.pack(I, sample_rate)) f.write(struct.pack(I, byte_rate)) f.write(struct.pack(H, block_align)) f.write(struct.pack(H, bits_per_sample)) # data chunk f.write(bdata) f.write(struct.pack(I, n_frames * block_align)) f.write(data.astype(np.int32).tobytes()) # 使用 write_wav(/tmp/mic_array.wav, data)写完之后用 Audacity 或者任何音频编辑器打开四个通道会分别显示。听一下每个通道的声音确认没有明显的噪声、断流或者通道串扰。这一步是最后一道人工检查很多寄存器层面的小问题在波形上不明显但一听就能听出来。5. AC108 阵列调试避坑从通道串扰到时钟失锁的排查记录5.1 通道数据错位现象、原因与解决现象录出来的四通道数据里CH0 和 CH2 的内容几乎一样CH1 和 CH3 也是。原因TDM slot 映射寄存器的通道顺序位设错了导致硬件把 CH0 的数据放到了两个 slot 里。解决检查寄存器 0x03 的 bit4-5确认通道映射模式是 0 到 3 顺序排列而不是交错排列。改完之后重新抓波形验证。5.2 采样率偏差导致的高频失真现象录 1kHz 正弦波频谱分析发现峰值在 1.02kHz 而不是 1kHz高频段有杂散。原因AC108 的 PLL 分频系数算错了实际采样率是 48960Hz 而不是 48000Hz。解决重新计算 MCLK 和 BCLK 的分频比确保 PLL 输出频率是采样率的整数倍。用示波器测 WS 的实际频率48kHz 对应周期 20.833 微秒误差超过 0.1% 就要调。5.3 I2C 写入成功但寄存器值不变现象I2C 工具显示写入成功但回读寄存器值还是默认值。原因AC108 的某些寄存器需要先解锁才能写入或者写入顺序有依赖关系。解决查手册里的寄存器保护机制先写解锁寄存器再写目标寄存器最后写锁定寄存器。另外确认 I2C 时钟速率没有超过芯片支持的上限。5.4 麦克风偏置电压不足导致灵敏度下降现象所有通道的 RMS 电平都比预期低 10dB 以上增益调到最大也上不去。原因驻极体麦克风的偏置电阻太大或者偏置电压被 AC108 的输入阻抗拉低。解决测一下麦克风正极的直流电压正常应该在 1.5V 到 3V 之间。如果偏低减小偏置电阻或者加一个电压跟随器。AC108 的输入阻抗在手册里有标注选偏置电阻的时候要算上这个负载。5.5 长时间采集出现周期性爆音现象录 10 分钟以上的音频每隔几十秒出现一次爆音。原因I2S 接收 FIFO 溢出通常是 SoC 端的中断响应延迟或者 DMA 缓冲区太小。解决增大 ALSA 的 period size 和 buffer size用--period-size1024 --buffer-size8192参数测试。如果还不行检查 SoC 的 I2S 驱动有没有用实时线程优先级。6. 从四 mic 到波束成形AC108 阵列的进阶用法与验证技巧四通道同步采集跑通之后下一步通常是做波束成形或者声源定位。这里给一个最小可用的延迟求和波束成形实现用来验证你的阵列数据是否真的可用。def delay_and_sum(data, mic_positions, steer_angle, sample_rate48000, c343.0): 延迟求和波束成形 data: (n_frames, n_mics) 多通道数据 mic_positions: 麦克风坐标列表单位米 steer_angle: 波束指向角度单位度 n_mics data.shape[1] theta np.radians(steer_angle) # 计算每个麦克风相对于参考点的延迟 delays [] for pos in mic_positions: # 投影到波束方向 proj pos[0] * np.cos(theta) pos[1] * np.sin(theta) delay proj / c * sample_rate delays.append(delay) # 对齐并求和 max_delay int(np.max(np.abs(delays))) 1 output np.zeros(data.shape[0] - 2 * max_delay) for i, delay in enumerate(delays): d int(round(delay)) start max_delay - d output data[start:start len(output), i] return output / n_mics这个函数的逻辑是把每个麦克风的信号按波束方向的投影延迟对齐然后求和。如果阵列数据是同步的波束成形后的信号在目标方向上的信噪比应该比单通道高 6dB 左右四通道理论增益。如果增益远低于这个值说明通道间还有未补偿的延迟或者增益不一致。验证方法用一个扬声器在 0 度方向播放白噪声分别用 0 度和 90 度波束成形比较两个输出的能量比。正常应该在 10dB 以上。如果不到 5dB回去检查通道对齐和增益校准。我自己的习惯是每换一块板子先跑一遍通道一致性检查再跑波束成形验证两个都过了才敢往上叠算法。这个流程帮我省了很多后悔药——有次偷懒跳过通道检查直接调波束成形结果调了一周才发现是 CH2 的麦克风焊反了。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站