4. WM8960 初始化流程:芯片复位、电源管理寄存器配置、时钟使能、音频接口配置

好,咱们进入正题。WM8960 这芯片,说复杂也复杂,说简单也简单。但初始化这一步要是搞错了,后面全是白搭。我见过不少朋友,焊好板子一上电,没声音,就开始怀疑硬件。其实多半是初始化顺序没搞对。

WM8960 的初始化,我习惯把它拆成四个步骤:芯片复位 → 电源管理 → 时钟使能 → 音频接口配置。这个顺序不能乱,尤其是电源和时钟,必须先让芯片“吃饱喝足”,它才肯干活。

核心原则:WM8960 的寄存器是掉电易失的,每次上电都必须完整走一遍初始化流程。别指望它像 STM32 那样能记住上次的状态。

WM8960 初始化四步流程图 WM8960 初始化四步流程 ① 芯片复位 R15 = 0x0000 ② 电源管理 R25/R26/R27 ③ 时钟使能 R4/R5/R6 ④ 音频接口 R7/R8/R9 ⚠ 关键提醒 • 步骤②和③之间有依赖关系:必须先供电,再给时钟 • 步骤④的格式必须与 STM32 I²S 配置严格一致 • 每一步之间建议加 5-10ms 延时,让内部稳压器稳定

4.1 芯片复位——让一切归零

WM8960 的复位有两种方式:硬件复位(拉低 RST 引脚)和软件复位(写寄存器)。我个人更推荐软件复位,省一个 GPIO 引脚。

软件复位很简单,往 R15 (Software Reset) 寄存器写 0x0000 就行。芯片内部会自动加载默认值。

/* WM8960 软件复位 */
uint8_t ret = WM8960_WriteReg(WM8960_R15_SOFTWARE_RESET, 0x0000);
if (ret != HAL_OK) {
    Error_Handler();  // 复位失败,基本可以判断 I2C 不通
}
HAL_Delay(10);  // 等内部复位完成

我曾经踩过的坑:复位后不要立刻配置其他寄存器。WM8960 内部有多个稳压器和偏置电路,复位后需要几毫秒才能稳定。我早期做项目时没加这个延时,结果 10 片板子有 3 片初始化失败,查了两天才发现是时序问题。

4.2 电源管理寄存器配置——先让芯片“吃饱”

复位之后,WM8960 大部分模块是关着的。你得手动打开需要用到的电源域。相关的寄存器有三个:

寄存器 地址 作用 典型值
R25 (PWR1) 0x19 MIC、ADC、DAC 电源 0x01C0
R26 (PWR2) 0x1A 输出混音器、HP 功放 0x01FD
R27 (PWR3) 0x1B 输入混音器、LINEOUT 0x01EF

如果你只是做音频播放(不录音),可以只开 DAC 和耳机输出相关的电源域。我一般这样配:

/* 电源管理配置 - 仅播放模式 */
WM8960_WriteReg(WM8960_R25_PWR1, 0x01C0);  // 开启 DACL/DACR, VMID
WM8960_WriteReg(WM8960_R26_PWR2, 0x01FD);  // 开启 HP_OUT 左右声道
WM8960_WriteReg(WM8960_R27_PWR3, 0x01EF);  // 开启输入混音器备用
HAL_Delay(5);  // 等电源稳定

小技巧:VMID 是内部参考电压,必须开启。如果你发现输出有咔咔声,多半是 VMID 没开或者电容没焊好。我遇到过两次,都是 VMID 引脚对地电容虚焊导致的。

4.3 时钟使能——给芯片一个“心跳”

WM8960 的时钟系统有点绕,但说白了就三件事:选时钟源、配分频比、决定采样率。

相关的寄存器是 R4 (CLOCK1)、R5 (CLOCK2) 和 R6 (CLOCK3)。其中 R4 最重要,它决定了 MCLK 和 SYSCLK 的关系。

我常用的配置是:MCLK = 12.288MHz,采样率 48kHz。为什么选这个频率?因为 12.288MHz 是 48kHz 的整数倍(256 倍),分频最简单,抖动最小。

/* 时钟配置 - MCLK=12.288MHz, 采样率=48kHz */
WM8960_WriteReg(WM8960_R4_CLOCK1, 0x0160);  // SYSCLK = MCLK, 不分频
WM8960_WriteReg(WM8960_R5_CLOCK2, 0x0000);  // 不使用 PLL
WM8960_WriteReg(WM8960_R6_CLOCK3, 0x0000);  // 默认

如果你用的是其他 MCLK 频率(比如 11.2896MHz 对应 44.1kHz),需要调整分频系数。具体怎么算?看数据手册 Table 36,里面有详细的对照表。

注意:MCLK 和采样率的比例必须是整数倍。常见的有 256x、384x、512x。如果比例不对,WM8960 内部锁相环会失锁,出来的声音就是变调的。我刚开始用 16kHz 采样率时,忘了改 MCLK,结果放出来的声音像磁带卡住了一样。

4.4 音频接口配置——约定通信“语言”

这一步是让 WM8960 和 STM32 的 I²S 接口“对上话”。说白了,两边得用同一种格式说话。

主要配置 R7 (IFACE1) 和 R8 (IFACE2)。R7 决定数据格式、字长、位时钟极性;R8 决定左右声道时钟的极性。

参数 选项 推荐值 说明
数据格式 I²S / Left-justified / DSP I²S 最通用,STM32 默认支持
字长 16 / 20 / 24 / 32 bit 16 bit 兼容性好,省带宽
BCLK 极性 上升沿/下降沿采样 上升沿 与 STM32 默认一致
LRCLK 极性 高电平左声道/右声道 低电平左声道 标准 I²S 协议
/* 音频接口配置 - I²S, 16bit, 标准模式 */
WM8960_WriteReg(WM8960_R7_IFACE1, 0x0002);  // I²S 格式, 16bit
WM8960_WriteReg(WM8960_R8_IFACE2, 0x0000);  // 默认 LRCLK 极性

关键检查点:配置完 R7 后,务必确认 STM32 的 I²S 初始化参数与 WM8960 完全一致。尤其是数据格式和字长,两边差一点都不行。我习惯在代码里用宏定义统一管理这些参数,避免手写不一致。

4.5 完整的初始化函数

把上面四步串起来,就是一个完整的初始化函数。我一般这样写:

/**
  * @brief  WM8960 完整初始化
  * @retval HAL_OK 成功,其他 失败
  */
HAL_StatusTypeDef WM8960_Init(void)
{
    HAL_StatusTypeDef ret;

    /* 第一步:软件复位 */
    ret = WM8960_WriteReg(WM8960_R15_SOFTWARE_RESET, 0x0000);
    if (ret != HAL_OK) return ret;
    HAL_Delay(10);

    /* 第二步:电源管理 */
    ret = WM8960_WriteReg(WM8960_R25_PWR1, 0x01C0);
    if (ret != HAL_OK) return ret;
    ret = WM8960_WriteReg(WM8960_R26_PWR2, 0x01FD);
    if (ret != HAL_OK) return ret;
    ret = WM8960_WriteReg(WM8960_R27_PWR3, 0x01EF);
    if (ret != HAL_OK) return ret;
    HAL_Delay(5);

    /* 第三步:时钟使能 */
    ret = WM8960_WriteReg(WM8960_R4_CLOCK1, 0x0160);
    if (ret != HAL_OK) return ret;

    /* 第四步:音频接口 */
    ret = WM8960_WriteReg(WM8960_R7_IFACE1, 0x0002);
    if (ret != HAL_OK) return ret;
    ret = WM8960_WriteReg(WM8960_R8_IFACE2, 0x0000);
    if (ret != HAL_OK) return ret;

    return HAL_OK;
}

我的习惯:每个寄存器写完后都检查返回值。虽然 I²C 通信出错的概率不高,但一旦出错,你至少能立刻定位到是哪一步出了问题。别问我为什么这么谨慎——有一次 I²C 上拉电阻焊错了,就是靠这个检查才快速找到问题的。