2. 开发环境搭建:STM32CubeMX配置I2C与I2S外设
好,咱们正式开始动手。这一章我会带你走一遍STM32CubeMX的配置流程。说白了,就是先把硬件外设的“骨架”搭好,后面写驱动才能跑得起来。
我个人习惯,拿到一个新项目,第一件事不是写代码,而是先把时钟树和外设引脚理清楚。你想想看,如果时钟配错了,后面调I2S音频采样率,调死你都调不对。我在项目中就吃过这个亏,当时折腾了一整天,最后发现是PLL配置多了一个分频系数……嗯,从那以后我再也不敢跳过时钟树检查了。
2.1 新建工程与芯片选择
打开STM32CubeMX,点击“New Project”。在Board Selector里,我建议你直接按芯片型号搜索。咱们课程用的是STM32F407VET6,当然你用F4系列其他型号也行,只要带I2S外设就好。
- Series: STM32F4
- Lines: STM32F407
- Package: LQFP100(或者你手头开发板的封装)
- MCU: STM32F407VET6
选好后双击,工程就创建了。别急着点“Generate Code”,咱们先把外设配好。
2.2 I2C外设配置(控制通道)
WM8960有两个接口:控制接口用I2C,音频数据接口用I2S。先配I2C。
在Pinout & Configuration界面,找到Connectivity → I2C1。为什么选I2C1?因为我习惯把I2C1留给音频编解码器,I2C2留给其他传感器。当然你也可以用I2C2或I2C3,只要后面驱动里改一下句柄就行。
I2C1的模式选I2C,不要选SMBus。参数配置如下:
| 参数项 | 配置值 | 说明 |
|---|---|---|
| I2C Speed Mode | Standard Mode (100kHz) | WM8960支持100kHz和400kHz,100kHz更稳定 |
| I2C Clock Speed | 100000 Hz | 对应100kHz |
| I2C Peripheral | Enabled | 使能I2C外设 |
| I2C Address | 7-bit | WM8960默认地址0x1A(7位) |
引脚分配会自动生成。默认PB6是SCL,PB7是SDA。如果你的板子有硬件上拉电阻,那就不用额外加;如果没有,记得在PCB上加上4.7kΩ上拉电阻到3.3V。
2.3 I2S外设配置(音频数据通道)
接下来配I2S。WM8960支持I2S、左对齐、右对齐等多种格式。咱们用最通用的I2S标准格式,16位数据,立体声。
在Pinout & Configuration里,找到Connectivity → SPI2。为什么是SPI2?因为STM32F4的I2S功能是复用SPI引脚的。SPI2对应的I2S2就是咱们要用的。
模式选择I2S2,然后配置参数:
| 参数项 | 配置值 | 说明 |
|---|---|---|
| Mode | Master Transmit | STM32做主设备,发送音频数据 |
| Standard | I2S Standard (Philips) | 标准I2S格式 |
| Data Format | 16-bit | 16位数据,WM8960也支持24位 |
| Audio Frequency | 48 kHz | CD音质采样率 |
| Clock Polarity | Low | CK信号低电平空闲 |
| Master Clock Output | Disabled | MCLK由外部晶振提供 |
这里有个关键点:Audio Frequency选48kHz后,CubeMX会自动计算分频系数。但有时候它算出来的值不对,尤其是当你改了系统时钟之后。我建议你先用默认值,等时钟树配完再回来看这个参数。
I2S2的引脚分配:
- CK (SCK): PB13
- SD (MOSI): PB15
- WS (NSS): PB12
- MCK: PC6(如果使能了Master Clock Output)
2.4 时钟树配置
好,重头戏来了。时钟树配不好,I2S采样率全是错的。咱们的目标是让I2S外设得到精确的时钟源,这样才能生成48kHz的采样率。
切换到Clock Configuration标签页。我习惯的配置路径:
- HSE:8MHz外部晶振(根据你板子实际晶振频率设置)
- PLL:HSE作为PLL输入,PLLM=8,PLLN=336,PLLP=2,得到168MHz主频
- SYSCLK:168MHz
- AHB Prescaler:/1,HCLK=168MHz
- APB1 Prescaler:/4,APB1=42MHz(I2S2挂在APB1上)
- APB2 Prescaler:/2,APB2=84MHz
为什么要这么配?因为I2S2的时钟源来自APB1(42MHz),而42MHz经过I2S分频器后,正好能精确得到48kHz的采样率。具体分频公式:
I2S采样率 = I2S时钟源 / (分频系数 * 8 * 2)
48kHz = 42MHz / (54.6875 * 8 * 2)
// 实际分频系数取整后为55,得到47.7kHz,误差约0.6%
// 这个误差人耳基本听不出来
如果你用其他主频,比如180MHz,APB1可能是45MHz,分频后误差会更大。所以我个人建议,做音频项目就用168MHz这个经典配置,省心。
2.5 生成工程与代码结构
配完所有外设,点Project → Generate Code。生成前检查一下:
- Project Name: wm8960_audio_driver
- Project Location: 你自己选
- Toolchain / IDE: MDK-ARM V5(或者你用的IDE)
- Generate peripheral initialization as a pair of .c/.h files: 勾选
生成后,打开工程,你会看到HAL库自动生成了以下文件:
| 文件 | 内容 |
|---|---|
| main.c | 主函数,外设初始化调用 |
| i2c.c / i2c.h | I2C1初始化函数MX_I2C1_Init() |
| spi.c / spi.h | I2S2初始化函数MX_I2S2_Init() |
| stm32f4xx_hal_msp.c | 引脚GPIO配置、时钟使能、DMA配置(如果用了) |
打开main.c,你会看到类似这样的初始化顺序:
int main(void)
{
HAL_Init();
SystemClock_Config(); // 时钟树配置
MX_GPIO_Init(); // GPIO初始化
MX_I2C1_Init(); // I2C初始化
MX_I2S2_Init(); // I2S初始化
// 接下来就是咱们的WM8960驱动初始化
while (1)
{
// 主循环
}
}
嗯,到这里,硬件外设的“骨架”就搭好了。I2C负责控制,I2S负责传音频数据,时钟树保证采样率准确。下一章咱们就开始写WM8960的寄存器读写函数,让这个骨架真正动起来。
哦对了,差点忘了。生成工程后,记得检查一下GPIO引脚配置。有时候CubeMX会把I2C的PB6、PB7配置成开漏输出,但忘了使能内部上拉。如果你板子上没有外部上拉电阻,记得在MX_GPIO_Init()里加上:
GPIO_InitStruct.Pull = GPIO_PULLUP;
这个细节,我曾经在调试I2C通信时卡了半小时,最后发现是上拉没配。你注意一下就好。