3. WM8960寄存器配置入门:I2C控制接口初始化,核心寄存器配置流程

好,这一章我们正式开始跟WM8960“对话”。

WM8960这颗芯片,说白了就是个音频编解码器。你要让它干活,就得先通过I2C总线给它写寄存器。我刚开始接触这颗芯片时,觉得寄存器手册密密麻麻的,头都大了。后来发现,其实核心的就那么几个——Reset、Power、Interface。把这几个搞定,音频通路基本就通了。

3.1 I2C控制接口初始化

WM8960的I2C地址是固定的:0x1A(7位地址)。如果你用HAL库,初始化代码其实很简单。我个人习惯把I2C的初始化封装成一个函数,方便复用。

// WM8960 I2C地址(7位)
#define WM8960_I2C_ADDR    0x1A

// I2C句柄,由CubeMX生成
extern I2C_HandleTypeDef hi2c1;

/**
 * @brief  向WM8960写入一个寄存器
 * @param  reg: 寄存器地址(8位)
 * @param  val: 寄存器值(8位)
 * @retval HAL_OK 表示成功
 */
HAL_StatusTypeDef WM8960_WriteReg(uint8_t reg, uint8_t val)
{
    uint8_t data[2] = {reg, val};
    return HAL_I2C_Master_Transmit(&hi2c1, 
                                   WM8960_I2C_ADDR << 1, 
                                   data, 2, 100);
}
小提示:WM8960的I2C地址左移1位是因为HAL库的地址参数需要8位格式。左移后是0x34,别搞混了。我刚开始就因为这个坑,调了半天没反应。

初始化I2C外设本身,CubeMX已经帮我们做了。你只需要确保:

  • I2C时钟频率不超过400kHz(WM8960支持标准模式和快速模式)
  • SCL和SDA引脚的上拉电阻已配置(内部上拉通常不够,建议外部4.7kΩ)
  • I2C外设已使能,且GPIO模式设置为开漏输出

3.2 核心寄存器配置流程

WM8960的寄存器地址范围是0x00~0x3F,共64个。但实际项目中,你不需要全部配置。我总结了一套“三步走”流程:Reset → Power → Interface。按这个顺序来,基本不会出错。

核心配置三步走:
  1. Reset(复位):让芯片回到已知状态
  2. Power(电源管理):打开需要的模块,关闭不用的
  3. Interface(音频接口):配置I2S格式、位宽、采样率

下面这张图,是我画的核心配置流程。你跟着走一遍,心里就有谱了。

WM8960 核心寄存器配置流程 ① 复位 (Reset) ② 电源管理 (Power) ③ 音频接口 (Interface) ✅ 配置完成 关键寄存器 • 0x0F: Reset • 0x19: Power 1 • 0x1A: Power 2 • 0x07: Audio Interface 配置顺序 1. 先复位,清空所有状态 2. 再开电源,让模块工作 3. 最后配接口,建立通路 顺序错了可能没声音 配置完成后,WM8960即可接收/发送I2S音频数据 后续再配置音量、EQ、ALC等高级功能

3.3 第一步:复位寄存器(0x0F)

复位寄存器地址是0x0F。写入0x00即可触发软件复位。为什么先做这一步?因为芯片上电后,内部状态是不确定的。复位一下,所有寄存器回到默认值,心里踏实。

// 复位WM8960
WM8960_WriteReg(0x0F, 0x00);
HAL_Delay(10);  // 等待复位完成
注意:复位后,所有寄存器都恢复默认值。默认值下,大部分电源模块是关闭的。所以复位后必须重新配置电源,不能直接开始传音频数据。

3.4 第二步:电源管理寄存器(0x19, 0x1A)

电源管理是WM8960配置里最灵活的部分。两个寄存器控制着所有模拟模块的开关:

寄存器 地址 关键位 功能
Power 1 0x19 BIT6 (MICB), BIT5 (VMID), BIT0 (DIGEN) 麦克风偏置、VMID缓冲、数字核心
Power 2 0x1A BIT7 (HPR), BIT6 (HPL), BIT5 (SPK), BIT0 (OUT3) 耳机输出、扬声器输出、辅助输出

对于全双工录放音项目,我建议这样配置:

// 电源管理配置(全双工模式)
// Power 1 (0x19): 开启数字核心、VMID、麦克风偏置
WM8960_WriteReg(0x19, 0xBF);  // 0b10111111

// Power 2 (0x1A): 开启左右耳机输出
WM8960_WriteReg(0x1A, 0xC0);  // 0b11000000

这里解释一下关键位的含义:

  • DIGEN (BIT0):数字核心电源。不开这个,芯片不工作。
  • VMID (BIT5):中间电压缓冲。模拟电路需要这个参考电压。
  • MICB (BIT6):麦克风偏置。如果你要用板载麦克风,必须开。
  • HPL/HPR (BIT6/BIT7):左右耳机通道。全双工模式下,耳机用于监听。
经验之谈:如果你只用扬声器不用耳机,可以把HPL/HPR关掉,省电。但调试阶段,我建议都开着,方便用耳机听声音有没有问题。

3.5 第三步:音频接口寄存器(0x07)

音频接口寄存器0x07,是WM8960与STM32之间通信的“协议约定”。你需要告诉芯片:我们用I2S格式、数据位宽是多少、主从模式怎么选。

寄存器0x07的位定义如下:

位 名称 说明
BIT7:6 Format 00=I2S, 01=Left-justified, 10=PCM, 11=Reserved
BIT5:4 WL 00=16bit, 01=20bit, 10=24bit, 11=32bit
BIT3 LRSWAP 0=正常, 1=左右声道交换
BIT2 LRP 左右声道极性(通常置0)
BIT1:0 MS 00=从模式, 11=主模式

我们的项目里,STM32做主设备,WM8960做从设备。I2S格式,16位数据。配置代码很简单:

// 音频接口配置:I2S格式,16位,从模式
// 0x07 = 0b00000010 = 0x02
WM8960_WriteReg(0x07, 0x02);

为什么是0x02?你看:

  • BIT7:6 = 00 → I2S格式
  • BIT5:4 = 00 → 16位
  • BIT3 = 0 → 正常声道
  • BIT2 = 0 → 正常极性
  • BIT1:0 = 10 → 从模式

嗯,这里要注意:从模式意味着WM8960的BCLK和LRCLK都由STM32提供。如果你配成主模式,WM8960会自己产生时钟,那STM32就得跟着它跑,反而麻烦。

3.6 完整的初始化序列

把上面三步串起来,就是WM8960的初始化函数。我个人习惯把它放在一个单独的文件里,比如wm8960.c。

/**
 * @brief  WM8960初始化(全双工模式)
 * @retval HAL_OK 表示成功
 */
HAL_StatusTypeDef WM8960_Init(void)
{
    HAL_StatusTypeDef ret;
    
    // 1. 复位
    ret = WM8960_WriteReg(0x0F, 0x00);
    if (ret != HAL_OK) return ret;
    HAL_Delay(10);
    
    // 2. 电源管理
    ret = WM8960_WriteReg(0x19, 0xBF);  // Power 1
    if (ret != HAL_OK) return ret;
    
    ret = WM8960_WriteReg(0x1A, 0xC0);  // Power 2
    if (ret != HAL_OK) return ret;
    
    // 3. 音频接口
    ret = WM8960_WriteReg(0x07, 0x02);  // I2S, 16bit, Slave
    if (ret != HAL_OK) return ret;
    
    return HAL_OK;
}
避坑提醒:我曾经在项目里犯过一个低级错误——复位后没有加延时,紧接着就去配置电源寄存器。结果写进去的值没生效,芯片一直没声音。后来查手册才发现,复位需要几个毫秒的稳定时间。所以,复位后务必加10ms左右的延时。

3.7 验证配置是否成功

配置写完了,怎么知道芯片有没有正确响应?我常用的方法是回读寄存器。WM8960支持读操作,你可以读一下刚写进去的寄存器,看看值对不对。

/**
 * @brief  从WM8960读取一个寄存器
 * @param  reg: 寄存器地址
 * @retval 寄存器值,0xFF表示读取失败
 */
uint8_t WM8960_ReadReg(uint8_t reg)
{
    uint8_t val = 0;
    HAL_I2C_Master_Transmit(&hi2c1, WM8960_I2C_ADDR << 1, &reg, 1, 100);
    HAL_I2C_Master_Receive(&hi2c1, WM8960_I2C_ADDR << 1, &val, 1, 100);
    return val;
}

// 验证:读取音频接口寄存器,应为0x02
uint8_t check = WM8960_ReadReg(0x07);
if (check == 0x02) {
    // 配置成功
} else {
    // 配置失败,检查I2C连接
}

如果读回来的值跟你写的不一样,先别急着怀疑芯片。检查一下:

  • I2C地址对不对?左移了没?
  • 上拉电阻焊了没?
  • WM8960的供电正常吗?(3.3V数字、1.5V模拟核心)