4、音频硬件接口:I2S、PDM、TDM接口原理与R329上的配置

做音频处理,绕不开硬件接口。说白了,音频数据怎么从麦克风进到芯片,又怎么从芯片送到喇叭,全靠这些接口在干活。

R329这颗芯片,我用了好几年。它的音频接口挺全的——I2S、PDM、TDM都有。今天咱们就一个一个聊清楚。

4.1 I2S接口:最常用的音频总线

I2S,全称是Inter-IC Sound。这玩意儿是飞利浦在80年代搞出来的,专门用来传数字音频数据。到现在,几乎所有音频芯片都支持它。

4.1.1 I2S的信号线

I2S总线就4根线,不多不少:

  • BCLK(位时钟):每个时钟周期传一个bit的数据。采样率越高,BCLK频率也越高。
  • LRCK(左右声道时钟):也叫帧时钟。高电平传右声道,低电平传左声道。频率等于采样率。
  • SDI/SDO(数据输入/输出):传音频数据的线。
  • MCLK(主时钟):有些系统需要,有些不需要。R329内部可以自己生成。
我的经验: 刚开始做I2S时,我总搞混BCLK和LRCK的关系。记住一句话:BCLK是节拍,LRCK是左右手。一个节拍传一个bit,左右手轮流上场。

4.1.2 I2S的数据格式

I2S标准格式有个特点:数据比LRCK晚一个BCLK周期。为什么?为了兼容性。早期的DAC芯片需要这个延迟来准备数据。

不过现在很多芯片也支持左对齐、右对齐格式。R329的I2S控制器支持三种模式:

  • 标准I2S格式(飞利浦格式)
  • 左对齐格式(MSB对齐)
  • 右对齐格式(LSB对齐)
重要: 选格式时,一定要看你的Codec芯片支持什么。我踩过坑——Codec只支持左对齐,我配成了标准I2S,结果声音全是噪声。

4.1.3 R329上的I2S配置

R329有3个I2S控制器。每个控制器可以独立配置采样率、位深、通道数。

下面是一个典型的配置代码,初始化I2S0用于录音:

#include <sunxi_hal_i2s.h>

i2s_config_t i2s_cfg = {
    .sample_rate = 16000,      // 16kHz采样率
    .channel_num = 2,          // 双声道
    .data_width = I2S_DATA_WIDTH_16BIT,  // 16位精度
    .mode = I2S_MODE_MASTER,   // R329做主设备
    .format = I2S_FORMAT_STANDARD,  // 标准I2S格式
    .mclk_freq = 0,            // 使用内部MCLK
};

hal_i2s_init(I2S_DEVICE_0, &i2s_cfg);
hal_i2s_start(I2S_DEVICE_0);
注意: 采样率、位深、MCLK三者有严格的倍数关系。比如16kHz、16bit、双声道,BCLK = 16k × 16 × 2 = 512kHz。MCLK一般是BCLK的整数倍,常见的是256倍或512倍。

4.2 PDM接口:数字麦克风的专属通道

PDM,脉冲密度调制。这玩意儿跟I2S不一样,它只传1bit的数据,但采样率极高。

4.2.1 PDM的原理

你想想看,传统的ADC是把模拟信号转成16bit的数字值。PDM呢?它用1bit来表示信号的大小——1代表正,0代表负。但它的采样率特别高,通常是2.4MHz到3.2MHz。

为什么用这么高的频率?因为后面要经过一个叫「抽取滤波器」的东西,把高采样率的1bit数据转成低采样率的多bit数据。这个过程叫降采样。

我个人的习惯: 用PDM麦克风时,我会先确认R329的PDM控制器内部有没有集成抽取滤波器。R329是有的,所以可以直接拿到16kHz或48kHz的PCM数据,省了软件滤波的麻烦。

4.2.2 PDM的信号线

PDM接口更简单,就3根线:

  • PDM_CLK:时钟线,由主设备提供
  • PDM_DATA:数据线,可以同时传两个麦克风的数据(左右声道)
  • PDM_SYNC:同步信号,有些芯片需要

4.2.3 R329上的PDM配置

R329的PDM控制器支持最多4个数字麦克风。配置时主要设置采样率和抽取倍数。

pdm_config_t pdm_cfg = {
    .sample_rate = 16000,       // 目标采样率16kHz
    .decimation_ratio = 200,    // 抽取倍数,输入3.2MHz / 200 = 16kHz
    .channel_num = 2,           // 双麦克风
    .data_width = PDM_DATA_WIDTH_16BIT,
};

hal_pdm_init(PDM_DEVICE_0, &pdm_cfg);
hal_pdm_start(PDM_DEVICE_0);
我曾经踩过的坑: PDM麦克风的时钟频率不能太高,否则信号完整性会出问题。R329的PDM_CLK最高支持3.2MHz,但实际布线时如果走线太长,建议降到2.4MHz。我有一块板子就是因为时钟线走了15cm,高频噪声串进来了,折腾了两天才找到原因。

4.3 TDM接口:多通道音频的利器

TDM,时分复用。说白了,就是把多个音频通道的数据塞到同一根数据线上,分时传输。

4.3.1 TDM的工作原理

I2S只能传两个声道(左+右)。但TDM可以传4个、8个、甚至16个声道。怎么做?

每个声道分配一个固定的时隙(slot)。比如8声道TDM,一个帧周期内就有8个slot,每个slot传一个声道的数据。BCLK频率要足够高,才能把所有slot的数据传完。

应用场景: 智能音箱的麦克风阵列。一个阵列有4个或6个麦克风,如果用I2S,需要多个I2S控制器。用TDM,一根数据线就搞定了。R329的TDM控制器最多支持16个时隙。

4.3.2 TDM的配置要点

配置TDM时,有几个参数必须搞清楚:

  • 时隙数:等于麦克风数量
  • 时隙宽度:每个时隙占几个BCLK周期,一般等于位深
  • 帧同步:TDM的帧同步信号可以是高电平有效,也可以是低电平有效
  • 数据延迟:数据相对于帧同步的延迟,可以是0或1个BCLK

4.3.3 R329上的TDM配置示例

下面是一个4通道TDM的配置,用于麦克风阵列:

tdm_config_t tdm_cfg = {
    .sample_rate = 16000,
    .slot_num = 4,              // 4个时隙
    .slot_width = TDM_SLOT_WIDTH_16BIT,
    .frame_sync = TDM_FRAME_SYNC_HIGH,
    .data_delay = TDM_DATA_DELAY_1BCLK,
    .mode = TDM_MODE_MASTER,
};

hal_tdm_init(TDM_DEVICE_0, &tdm_cfg);
hal_tdm_start(TDM_DEVICE_0);
我建议: 用TDM时,一定要确认所有麦克风的数据在同一个时刻采样。有些Codec芯片的TDM实现有延迟,不同时隙的数据可能不是同一时刻采的。这对波束成形算法影响很大。

4.4 三种接口的对比与选型

做项目时,怎么选?我列个表,一目了然:

接口类型 通道数 典型应用 优点 缺点
I2S 2(立体声) Codec芯片、蓝牙音频 通用性强,几乎所有音频芯片都支持 通道数少
PDM 2~4 数字麦克风 线少,抗干扰强 需要抽取滤波器,延迟稍大
TDM 4~16 麦克风阵列 通道数多,一根线搞定 配置复杂,时钟要求高
选型建议: 如果只是做双声道录音或播放,I2S就够了。如果是做智能音箱的麦克风阵列,建议用PDM麦克风+PDM接口,或者用TDM连接多个数字麦克风。R329的PDM和TDM都支持,看你的麦克风类型。

4.5 调试经验分享

最后,分享几个我调试音频接口时的心得:

  • 先看时钟: 用示波器量BCLK和LRCK,确认频率对不对。我遇到过晶振虚焊,BCLK频率只有一半的情况。
  • 再看数据: 用逻辑分析仪抓数据,看数据格式对不对。特别是TDM的时隙对齐,很容易搞错。
  • 最后听声音: 如果数据格式对了但声音不对,检查位深和字节序。R329默认是小端,有些Codec是大端。
我的小技巧: 调试时先发一个1kHz的正弦波,用示波器看DAC输出。如果波形干净,说明接口没问题。如果波形有毛刺或失真,大概率是时钟抖动或数据对齐问题。

嗯,音频接口这块内容不少,但掌握了I2S、PDM、TDM这三个,R329上的音频项目基本都能搞定。下一章咱们聊聊音频Codec的配置和调试,到时候会用到今天讲的这些接口知识。