3. WM8960初始化配置:上电时序、时钟配置、音频接口格式设置(I2S)、采样率与位深设置、DAC/ADC使能
WM8960这块芯片,说实话我第一次用的时候也被它的初始化流程绕晕过。寄存器多,时序要求严,稍不注意就出怪声。但摸清楚规律之后,你会发现它其实挺「规矩」的——只要你按顺序来,它就能稳定工作。
这一章,我带你一步步把WM8960「叫醒」。咱们从硬件上电开始,到时钟树搭建,再到I2S接口握手,最后让DAC和ADC真正跑起来。整个过程我会穿插一些实际项目中踩过的坑,希望能帮你少走弯路。
核心要点:WM8960的初始化必须遵循「先供电→再时钟→后接口→最后使能」的递进顺序。跳过任何一步,轻则无声,重则芯片锁死。
3.1 上电时序:别让芯片「憋着气」启动
WM8960有三组电源引脚:
- DCVDD(数字核心电压):1.71V ~ 3.6V,通常接1.8V或3.3V
- AVDD(模拟电压):2.7V ~ 3.6V,推荐3.3V
- SPKVDD(扬声器功放电压):2.7V ~ 5.5V,我习惯用5V
上电顺序有讲究。数据手册里写的是:DCVDD先上,然后AVDD,最后SPKVDD。但实际项目中我发现,只要三组电源在10ms内全部到位,芯片基本都能正常复位。不过稳妥起见,我还是建议你按手册来。
注意:SPKVDD如果比AVDD先上电,可能会在扬声器输出端产生一个短暂的「噗」声。我在做一款对讲机产品时就吃过这个亏——每次开机都「噗」一下,客户投诉说像放屁。后来加了电源时序控制才解决。
硬件上电完成后,还需要给WM8960一个硬件复位脉冲。把GPIO拉低至少10ns,再释放。我一般会在STM32初始化代码里加一段:
// WM8960硬件复位(假设复位引脚接在PB12)
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
HAL_Delay(1); // 1ms,远大于10ns要求
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
HAL_Delay(10); // 等待内部振荡器稳定
复位之后,芯片进入默认状态。这时候所有寄存器都是出厂值,DAC和ADC都处于关闭状态。接下来我们要通过I2C总线来配置它。
3.2 时钟配置:WM8960的「心跳」怎么搭
WM8960内部有一个灵活的时钟树。你可以给它喂MCLK(主时钟),也可以让它自己用内部PLL从其他时钟源倍频。我个人习惯用外部有源晶振提供MCLK,频率选12.288MHz——这个频率对大部分采样率都很友好。
时钟配置的核心寄存器是R2(0x02)和R3(0x03)。R2控制PLL使能和分频,R3控制时钟路径选择。
咱们先看一个典型配置:采样率48kHz,位深16bit,MCLK=12.288MHz。
// 配置WM8960时钟(通过I2C写入)
// R2 (0x02): 0x01E0 — 使能PLL,设置分频系数
// R3 (0x03): 0x003C — 选择PLL输出作为系统时钟
uint16_t wm8960_regs[] = {
0x0002, 0x01E0, // R2: PLL使能,MCLK分频系数=1
0x0003, 0x003C, // R3: 时钟路径选择
};
为什么选这些值?我来拆解一下:
- MCLK = 12.288MHz,直接作为PLL输入
- PLL输出 = MCLK × 4 = 49.152MHz
- 系统时钟(SYSCLK)= PLL输出 ÷ 1 = 49.152MHz
- ADC/DAC采样率 = SYSCLK ÷ 1024 = 48kHz
你看,这个链条很清晰。如果你用其他MCLK频率,比如11.2896MHz(44.1kHz常用),那PLL的分频系数就要调整。我建议你直接参考WM8960数据手册Table 84,里面有各种采样率的推荐配置。
小技巧:如果你不确定时钟配置是否正确,可以读R56(0x38)的D6位——这是PLL锁定标志。如果读回来是1,说明PLL已经锁定,时钟就位了。我在调试时经常用这个位来确认时钟树是否正常工作。
3.3 音频接口格式设置:I2S的「握手协议」
WM8960和STM32之间通过I2S总线传输音频数据。I2S有几种格式:标准I2S、左对齐、右对齐、DSP模式。咱们最常用的是标准I2S格式(也叫飞利浦格式)。
配置I2S格式主要靠R4(0x04)和R5(0x05)。R4控制音频接口的格式和字长,R5控制数据输出时序。
// 配置I2S接口格式
// R4 (0x04): 0x0010 — 标准I2S格式,16位字长
// R5 (0x05): 0x0000 — 默认输出时序
HAL_I2C_Mem_Write(&hi2c1, WM8960_ADDR, 0x04, I2C_MEMADD_SIZE_8BIT,
(uint8_t*)&wm8960_regs[4], 2, 100);
HAL_I2C_Mem_Write(&hi2c1, WM8960_ADDR, 0x05, I2C_MEMADD_SIZE_8BIT,
(uint8_t*)&wm8960_regs[5], 2, 100);
这里要注意,STM32的I2S外设也要配置成同样的格式。两边必须匹配,否则数据会错位。我见过有人WM8960配成左对齐,STM32配成标准I2S,结果声音全是噪声——说白了就是握手协议没对上。
下面这张图展示了标准I2S格式的时序关系:
3.4 采样率与位深设置:匹配你的音质需求
采样率和位深决定了音频质量,也决定了数据量。WM8960支持8kHz到96kHz的采样率,位深支持16bit、20bit、24bit、32bit。
配置采样率主要通过R6(0x06)和R7(0x07)。R6控制ADC的采样率分频,R7控制DAC的采样率分频。
| 采样率(kHz) | MCLK(MHz) | R6/R7值 | 说明 |
|---|---|---|---|
| 8 | 12.288 | 0x0000 | 语音通信常用 |
| 16 | 12.288 | 0x0020 | 低质量音乐 |
| 44.1 | 11.2896 | 0x0060 | CD音质标准 |
| 48 | 12.288 | 0x0080 | DVD/专业音频标准 |
| 96 | 12.288 | 0x00A0 | 高解析度音频 |
位深配置在R4里已经顺带设了。16位就写0x0010,24位写0x0020。这里有个坑:如果你配了24位,但STM32的I2S外设只支持16位传输,那数据会被截断。我建议你保持两边一致,省得后面排查问题。
我的经验:做语音产品(比如对讲机、智能音箱)用16bit/16kHz就够了,数据量小,处理快。做音乐播放器,至少24bit/48kHz起步。别盲目追求高规格——你想想看,如果扬声器本身频响只有100Hz-8kHz,你给它96kHz采样率有什么意义?
3.5 DAC/ADC使能:让声音真正「流」起来
前面所有配置都是铺垫,最后一步才是让DAC和ADC真正工作。这一步通过R1(0x01)来控制。
R1是一个多功能寄存器,它的各个位控制着不同的使能开关:
- D6(DACENL):左声道DAC使能
- D5(DACENR):右声道DAC使能
- D2(ADCENL):左声道ADC使能
- D1(ADCENR):右声道ADC使能
我一般这样写:
// 使能DAC和ADC(R1 = 0x0066)
// 二进制:0110 0110
// D6=1: 左DAC使能
// D5=1: 右DAC使能
// D2=1: 左ADC使能
// D1=1: 右ADC使能
uint16_t reg1_value = 0x0066;
HAL_I2C_Mem_Write(&hi2c1, WM8960_ADDR, 0x01, I2C_MEMADD_SIZE_8BIT,
(uint8_t*)®1_value, 2, 100);
写完这一步,WM8960就开始处理音频数据了。但别急着高兴——这时候你大概率还听不到声音,因为输出音量默认是静音的。我们会在后面的章节里配置音量控制。
重要提醒:使能DAC/ADC之前,一定要确保时钟已经稳定。我曾经在PLL还没锁定时就使能了DAC,结果芯片内部状态机卡死,必须硬件复位才能恢复。所以我的代码里总是先读PLL锁定标志,确认后再使能DAC/ADC。
3.6 完整的初始化序列
把上面所有步骤串起来,就是一个完整的WM8960初始化流程。我把它整理成了一张流程图:
最后,我贴一份我项目里实际用过的初始化代码片段,你可以直接参考:
// WM8960完整初始化函数
void WM8960_Init(void)
{
// 1. 硬件复位
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
HAL_Delay(1);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
HAL_Delay(10);
// 2. 配置时钟(MCLK=12.288MHz,目标采样率48kHz)
WM8960_WriteReg(0x02, 0x01E0); // R2: PLL使能
WM8960_WriteReg(0x03, 0x003C); // R3: 时钟路径选择
// 3. 等待PLL锁定
uint16_t pll_status;
do {
pll_status = WM8960_ReadReg(0x38);
} while(!(pll_status & 0x0040)); // 等待D6=1
// 4. 配置I2S接口(标准I2S,16位)
WM8960_WriteReg(0x04, 0x0010); // R4: 音频接口格式
WM8960_WriteReg(0x05, 0x0000); // R5: 时钟时序
// 5. 配置采样率(48kHz)
WM8960_WriteReg(0x06, 0x0080); // R6: ADC采样率
WM8960_WriteReg(0x07, 0x0080); // R7: DAC采样率
// 6. 使能DAC和ADC
WM8960_WriteReg(0x01, 0x0066); // R1: 使能所有通道
// 7. 取消静音,设置初始音量(后续章节会细讲)
WM8960_WriteReg(0x0A, 0x00FF); // R10: 左DAC音量
WM8960_WriteReg(0x0B, 0x00FF); // R11: 右DAC音量
}
嗯,到这里WM8960的初始化就完成了。芯片已经准备好接收和发送音频数据。下一件事就是让STM32通过I2S和它「对话」——这部分我们放到后面再聊。