1. 课程导入

做音频驱动适配,第一件事不是看代码,而是把芯片的音频数据路径摸清楚。R-Car 的音频子系统,说白了就是一条从外部数字音频接口到最终扬声器/耳机的流水线。这条流水线上有四个关键模块:SSI、SCU、SRC、DVC。我当年第一次接触 R-Car 时,光看数据手册就被这些缩写绕晕了。今天咱们一个一个拆开讲。

1.1 整体数据流:从录音到放音

先看一张整体框图,把四个模块的位置和关系搞清楚。

R-Car 音频子系统数据流架构 外部 CODEC I2S / TDM SSI 串行音频接口 SRC 采样率转换 DVC 数字音量控制 SCU 音频路由/混音 DDR 内存 数据流方向(放音为例): DDR → SCU(路由/混音)→ SRC(重采样)→ SSI(串行输出)→ 外部 CODEC → 扬声器 录音方向相反:外部 CODEC → SSI → DVC(增益)→ SCU → DDR

从图上你能看到,数据从外部 CODEC 进来,先经过 SSI 变成内部数字信号,然后可以选择走 SRC 做采样率转换,或者走 DVC 做音量调节,最后统一由 SCU 路由到内存。反过来放音就是逆过程。嗯,这里要注意:SCU 是整个子系统的中枢,所有音频数据流都要经过它。

1.2 SSI:串行音频接口

SSI(Synchronous Serial Interface)是 R-Car 与外部 CODEC 通信的物理层。说白了,它就是一组 I2S 或 TDM 接口的控制器。

特性 说明
接口标准 I2S、左对齐、右对齐、TDM(最多 16 通道)
数据位宽 8/16/18/20/24/32 bit
时钟源 内部 PLL 或外部 MCLK
DMA 支持 每个 SSI 通道独立 DMA 请求
多路复用 支持多 SSI 通道同步

我在项目中遇到过一个问题:某个平台用 8 通道 TDM 录音,结果 SSI 的时钟配置错了,导致只有左声道有数据。后来查了三天才发现是 SSI 的时隙掩码没设对。你想想看,8 个通道的 TDM 帧,每个通道的位时钟周期必须精确对齐,差一个 bit 都不行。

我的习惯:调试 SSI 时,先用示波器抓 BCLK 和 LRCLK,确认时钟频率和占空比正确。然后再看数据线上的 bit 排列是否符合预期。这一步能省掉后面 80% 的排查时间。

1.3 SRC:采样率转换器

SRC(Sampling Rate Converter)解决的是采样率不匹配的问题。比如你的录音设备是 48kHz,但系统需要 44.1kHz 的音频流,这时候 SRC 就派上用场了。

R-Car 的 SRC 模块支持以下转换比例:

  • 整数倍转换:1:2、1:4、2:1、4:1
  • 分数倍转换:任意比例,内部通过多相滤波器实现
  • 直通模式:输入输出采样率相同,旁路滤波器

我曾经踩过一个坑:SRC 的滤波器系数加载顺序搞反了,导致转换后的音频有高频噪声。后来我仔细看了数据手册,发现 SRC 的系数表是按 相位索引 排列的,不是按频率索引。这个细节很容易被忽略。

注意:SRC 的延迟大约在 1-3ms 左右,具体取决于转换比例和滤波器阶数。如果做实时对讲应用,这个延迟必须考虑进去。我建议在驱动初始化时,通过 SRC 的状态寄存器读取当前延迟值,而不是硬编码。

1.4 DVC:数字音量控制器

DVC(Digital Volume Control)负责音频信号的增益调节。它位于数据路径上,可以独立控制每个通道的音量。

参数 范围
增益范围 -119.5 dB 到 +24 dB
步进 0.5 dB
通道数 最多 8 通道独立控制
渐变支持 硬件自动渐变(防止爆音)

DVC 有一个很实用的功能:零交叉检测。当音量变化时,DVC 会等到信号过零点才切换增益,避免产生咔嗒声。我建议你在驱动中默认开启这个功能,除非有特殊需求。

另外,DVC 的渐变速率是可以配置的。我记得有一次客户反馈音量调节时有明显的阶梯感,后来发现是渐变速率设得太快了,每步只用了 1ms。改成 10ms 后,问题就解决了。

1.5 SCU:音频路由与混音核心

SCU(Sound Control Unit)是整个音频子系统的交通枢纽。它负责:

  • 数据路由:将 SSI、SRC、DVC 的数据连接到内存或彼此之间
  • 混音:支持多路音频流的叠加
  • 格式转换:比如 16bit 转 24bit,或者左右声道交换
  • DMA 管理:生成 DMA 请求,将数据搬运到 DDR

SCU 内部有多个 音频通道,每个通道可以独立配置数据源和目的地。我习惯把 SCU 想象成一个 音频交换机,你只需要告诉它从哪个端口收数据,发到哪个端口去。

关键点:SCU 的混音是在数字域完成的,不会引入额外的噪声。但要注意混音后的数据位宽可能会增加,比如两个 16bit 信号混音后变成 17bit,需要做饱和处理。R-Car 的 SCU 硬件会自动处理这个,但你要确保 DMA 缓冲区的大小配置正确。

1.6 典型数据流路径配置

下面是一个实际的放音路径配置示例,我在 R-Car H3 上验证过:

// 放音路径:DDR → SCU → SRC → SSI → CODEC
// 1. 配置 SCU 通道 0 从 DDR 读取 48kHz 16bit 立体声数据
scu_channel_config(0, SCU_SRC_DDR, 48000, 16, 2);

// 2. 配置 SRC 将 48kHz 转换为 44.1kHz
src_config(SRC_UNIT_0, 48000, 44100, SRC_QUALITY_HIGH);

// 3. 配置 SSI 以 I2S 格式输出 44.1kHz 数据
ssi_config(SSI_0, SSI_MODE_I2S, 44100, 16, 2);

// 4. 连接 SCU 输出到 SRC 输入,SRC 输出到 SSI 输入
scu_connect(SCU_CH_0, SRC_UNIT_0);
src_connect(SRC_UNIT_0, SSI_0);

// 5. 启动 DMA 传输
dma_start(SCU_CH_0);

这个配置看起来简单,但实际调试时要注意每个模块的 时钟域。SSI 的时钟来自外部 CODEC 或内部 PLL,SRC 有自己的内部时钟,SCU 则挂在系统总线上。如果时钟不同步,数据就会出错。

1.7 避坑指南

最后分享几个我在项目中踩过的坑:

  • SSI 的 FIFO 深度:R-Car 的 SSI FIFO 只有 8 级,如果 DMA 响应不及时,很容易溢出。我建议把 DMA 的 burst 长度设为 4,并且开启 FIFO 中断作为备份。
  • SRC 的初始化顺序:必须先配置 SRC 的输入采样率,再配置输出采样率,最后使能。顺序反了会导致 SRC 锁相环无法锁定。
  • DVC 的渐变中断:DVC 渐变完成后会触发中断,但中断标志位需要软件清除。我曾经忘了清标志,导致中断一直触发,CPU 占用率飙升。
  • SCU 的通道数限制:不同 R-Car 型号的 SCU 通道数不同,H3 有 16 个,M3 只有 8 个。写驱动时要做平台适配,不要硬编码。

好了,这一章的内容就是这些。音频硬件架构是驱动开发的地基,把 SSI、SRC、DVC、SCU 这四个模块的关系理清楚,后面写驱动就会顺畅很多。