3. STM32CubeMX 项目配置:时钟树、I2C、I2S、DMA 与 GPIO
好,咱们正式开始动手配置。这一章是整个系统的骨架——时钟、外设、DMA、GPIO,全在这里定下来。我个人习惯是先把时钟树理清楚,因为所有外设的时序都依赖它。WM8960 这颗 codec 对 MCLK 的要求比较特殊,搞不好后面 I2S 就罢工了。
3.1 时钟树配置:MCLK 生成
WM8960 需要外部提供 MCLK(主时钟),典型频率是 12.288 MHz 或 11.2896 MHz。我一般选 12.288 MHz,因为它能整除 48 kHz、96 kHz 这些常用采样率。你想想看,如果 MCLK 频率不对,codec 内部 PLL 锁不住,音频直接变调。
STM32F4 系列通常用 HSE(外部高速晶振)作为时钟源。我习惯用 8 MHz 晶振,然后通过 PLL 倍频到 168 MHz(系统主频),再通过 I2S 专用的 PLLI2S 分频出 12.288 MHz。
具体步骤:
- 选择 HSE:RCC 配置中选 Crystal/Ceramic Resonator。
- 配置 PLL:HSE 8 MHz → PLLM=8 → PLLN=336 → PLLP=2 → 得到 168 MHz。
- 使能 PLLI2S:PLLI2S 的 N=258,R=3 → 8 MHz / 8 * 258 / 3 = 86 MHz?不对,这里要算清楚。
嗯,这里要注意。PLLI2S 的输出频率公式是:VCO = HSE / PLLM * PLLI2SN,然后 I2S_CLK = VCO / PLLI2SR。我们要的是 12.288 MHz,所以:
VCO = 8 MHz / 8 * 258 = 258 MHz
I2S_CLK = 258 MHz / 21 = 12.2857 MHz ≈ 12.288 MHz
误差在 0.02% 以内,完全可用。我在项目中遇到过用 11.2896 MHz 的情况(针对 44.1 kHz 采样率),但 48 kHz 系更通用,所以这次就用 12.288 MHz。
配置完成后,I2S 外设的时钟源选 PLLI2S R,这样 MCLK 就能从 I2S_CKIN 引脚输出了。WM8960 的 MCLK 引脚接在 STM32 的 I2S_CKIN 上,频率就是 12.288 MHz。
3.2 I2C 外设配置:WM8960 寄存器控制
WM8960 的寄存器读写走 I2C 总线。它的 I2C 地址是 0x1A(7 位地址),数据格式是先发寄存器地址(7 位 + R/W),再发 9 位数据。嗯,这里有个坑——WM8960 的寄存器是 9 位数据,但 I2C 一次只能传 8 位,所以需要分两次发送。
CubeMX 里配置 I2C1:
- Mode:I2C(标准模式或快速模式,我选 100 kHz 标准模式,稳定第一)
- I2C Speed Mode:Standard Mode(100 kHz)
- DMA:暂时不使能,寄存器配置量小,轮询就够了
引脚分配:PB6(SCL)、PB7(SDA)。记得外部上拉电阻,4.7 kΩ 就行。我曾经因为没加上拉,调试了一下午才发现波形全是乱的。
3.3 I2S 外设配置:音频数据传输
I2S 是音频数据的通道。WM8960 支持 I2S、左对齐、右对齐、DSP 等多种格式。我习惯用标准 I2S 格式(飞利浦协议),数据位 16 位或 24 位。这次用 16 位,因为 48 kHz 16 位立体声已经能满足大部分场景。
CubeMX 里配置 I2S3(全双工模式):
- Mode:Master Transmit / Master Receive(全双工)
- Standard:I2S Phillips
- Data Format:16-bit
- Audio Frequency:48 kHz
- Clock Polarity:Low(数据在上升沿采样)
- MCLK Output:Enable(输出 12.288 MHz 给 WM8960)
引脚分配:PC10(I2S3_CK)、PC12(I2S3_SD)、PA4(I2S3_WS)、PC7(I2S3_MCK)。注意 MCK 引脚必须使能,否则 WM8960 没有主时钟。
3.4 DMA 配置:音频数据搬运
音频数据量大,用 CPU 一个一个搬不现实。DMA 就是干这个的。我配置两个 DMA 通道:一个用于 I2S3 发送(TX),一个用于接收(RX)。
CubeMX 里在 DMA Settings 中添加:
- I2S3_TX:DMA2 Stream 1,通道 0,优先级 High,方向 Memory-to-Peripheral
- I2S3_RX:DMA2 Stream 0,通道 0,优先级 High,方向 Peripheral-to-Memory
DMA 参数:
- Mode:Circular(循环模式,音频流是连续的)
- Data Width:Half Word(16 位)
- Burst:Single
- FIFO:Disable(直接模式,减少延迟)
为什么用 Circular 模式?因为音频数据是环形缓冲,DMA 自动循环搬运,CPU 只需要在缓冲区满或空时处理。我曾经用 Normal 模式,结果每次传输完都要手动重配,音频流卡顿得没法听。
3.5 GPIO 配置:复位与使能引脚
WM8960 有两个控制引脚:复位(RESET)和使能(EN)。复位引脚低电平有效,使能引脚高电平有效。我习惯把它们接到 STM32 的普通 GPIO 上,方便软件控制上电时序。
配置:
- RESET:PA0,推挽输出,默认高电平(不复位)
- EN:PA1,推挽输出,默认低电平(不使能)
上电时序很重要:先拉低 RESET 至少 10 ms,然后释放,再拉高 EN 使能 codec。我见过有人把顺序搞反,结果 codec 初始化失败,I2C 通信一直 NACK。
// 伪代码示例
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // 复位
HAL_Delay(10);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // 释放复位
HAL_Delay(1);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // 使能 codec
HAL_Delay(5);
3.6 整体配置一览
为了方便你对照检查,我把所有配置整理成表格:
| 外设 | 功能 | 关键参数 | 引脚 |
|---|---|---|---|
| RCC | 时钟源 | HSE 8 MHz → PLL 168 MHz | PH0/PH1 |
| PLLI2S | MCLK 生成 | N=258, R=21 → 12.288 MHz | — |
| I2C1 | 寄存器控制 | 100 kHz, 7-bit 地址 0x1A | PB6/PB7 |
| I2S3 | 音频数据传输 | Master, 16-bit, 48 kHz, MCLK 使能 | PC10/PC12/PA4/PC7 |
| DMA2 Stream 0 | I2S3 RX | Circular, Half Word, High | — |
| DMA2 Stream 1 | I2S3 TX | Circular, Half Word, High | — |
| GPIO PA0 | WM8960 RESET | 推挽输出, 默认高 | PA0 |
| GPIO PA1 | WM8960 EN | 推挽输出, 默认低 | PA1 |
3.7 知识体系结构图
下面这张 SVG 图把本章的配置逻辑串起来了。你可以看到时钟怎么分叉到 I2S 和 I2C,DMA 怎么接在 I2S 后面,GPIO 怎么控制 codec。