4. MCU端SPI外设初始化:时钟、极性、相位配置详解
好,咱们进入正题。上一章我们把SD卡的物理层和协议层理清楚了,这一章要干的事很具体——把MCU的SPI外设配好,让它能和SD卡对上话。
SPI初始化,说白了就是三件事:时钟、极性、相位。这三样配不对,卡就认不到。我在项目里见过太多人卡在这一步,折腾半天发现是CPOL和CPHA搞反了。
4.1 SPI时钟树与分频配置
先看时钟。MCU的SPI外设挂在哪条总线上,决定了它的时钟源和最大频率。以STM32F4为例,SPI1挂在APB2上,SPI2和SPI3挂在APB1上。APB2最高84MHz,APB1最高42MHz。
SD卡在SPI模式下,初始化阶段时钟不能太快。规范里写得很清楚:初始化时SPI时钟不能超过400kHz。为什么?因为卡还没完成上电初始化,内部逻辑还不稳定,跑快了容易丢数据。
关键点:初始化阶段用低速(≤400kHz),初始化完成后切到高速(通常10-20MHz)。
我习惯的做法是:先配一个低速分频系数,等卡初始化成功后再动态调整SPI的波特率寄存器。代码里大概这样:
// 初始化阶段:低速模式
SPI1->CR1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0; // 256分频
// 假设APB2=84MHz,SPI时钟 = 84MHz / 256 ≈ 328kHz,满足≤400kHz
// 初始化完成后:切换到高速模式
SPI1->CR1 &= ~(SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0);
SPI1->CR1 |= SPI_CR1_BR_1; // 4分频,SPI时钟 = 84MHz / 4 = 21MHz
小技巧:分频系数不要一次切到底。我一般分两步走:先切到1MHz左右确认卡能响应,再切到目标高速。这样万一卡对高频敏感,还能降回来。
4.2 SPI极性(CPOL)与相位(CPHA)详解
这两个参数是SPI配置里最容易出错的。咱们一个一个说清楚。
4.2.1 CPOL:时钟极性
CPOL决定SCLK空闲时的电平状态:
- CPOL=0:空闲时SCLK为低电平
- CPOL=1:空闲时SCLK为高电平
SD卡在SPI模式下要求CPOL=0。也就是说,没有数据传输时,时钟线保持低电平。
4.2.2 CPHA:时钟相位
CPHA决定数据在哪个时钟沿被采样:
- CPHA=0:在第一个时钟沿采样数据
- CPHA=1:在第二个时钟沿采样数据
SD卡要求CPHA=0。结合CPOL=0,就是:空闲低电平,第一个时钟沿(上升沿)采样数据。
注意:有些MCU的SPI外设默认配置不是CPOL=0、CPHA=0。比如我遇到过某款国产MCU,复位后CPOL默认是1。如果你直接拿默认配置去读SD卡,卡永远不响应。所以初始化时一定要显式设置这两个位。
配置代码示例:
// SPI模式0:CPOL=0, CPHA=0
SPI1->CR1 &= ~(SPI_CR1_CPOL | SPI_CR1_CPHA); // 清零
// 或者直接写:
SPI1->CR1 |= (0 << SPI_CR1_CPOL_Pos) | (0 << SPI_CR1_CPHA_Pos);
4.3 数据格式与传输顺序
除了时钟,还有两个位要配:数据帧格式和MSB/LSB顺序。
| 参数 | SD卡要求 | 说明 |
|---|---|---|
| 数据帧格式 | 8位 | SD卡SPI命令和响应都是8位字节 |
| MSB/LSB | MSB first | 高位先传,这是SPI的常规做法 |
| 片选极性 | 低电平有效 | CS拉低选中卡,拉高释放 |
配置代码:
// 8位数据帧,MSB first
SPI1->CR1 &= ~SPI_CR1_DFF; // 0: 8位数据帧
SPI1->CR1 &= ~SPI_CR1_LSBFIRST; // 0: MSB first
// 软件控制片选(推荐)
// 配置CS引脚为GPIO输出,手动拉高拉低
GPIO_SetBits(GPIOA, GPIO_Pin_4); // CS初始高电平
为什么推荐软件控制CS?因为SD卡的SPI模式需要精确控制CS的拉高时机,尤其是在发送命令和读取响应之间。硬件自动CS往往时机不对,容易导致卡状态机紊乱。
4.4 完整的SPI初始化流程
把上面所有配置串起来,一个完整的SPI初始化函数大概长这样:
void SPI_SD_Init(void)
{
// 1. 使能SPI和GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
// 2. 配置GPIO复用功能
// SCK: PA5, MISO: PA6, MOSI: PA7, CS: PA4
GPIO_InitTypeDef gpio;
gpio.GPIO_Mode = GPIO_Mode_AF;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_PuPd = GPIO_PuPd_UP;
// SCK, MOSI, CS 推挽输出
gpio.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_4;
GPIO_Init(GPIOA, &gpio);
// MISO 浮空输入
gpio.GPIO_Pin = GPIO_Pin_6;
gpio.GPIO_Mode = GPIO_Mode_AF;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &gpio);
// 3. 配置SPI外设
SPI_InitTypeDef spi;
SPI_StructInit(&spi);
spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
spi.SPI_Mode = SPI_Mode_Master;
spi.SPI_DataSize = SPI_DataSize_8b;
spi.SPI_CPOL = SPI_CPOL_Low; // CPOL=0
spi.SPI_CPHA = SPI_CPHA_1Edge; // CPHA=0
spi.SPI_NSS = SPI_NSS_Soft; // 软件NSS
spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; // 低速
spi.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI1, &spi);
// 4. 使能SPI
SPI_Cmd(SPI1, ENABLE);
// 5. CS初始高电平
GPIO_SetBits(GPIOA, GPIO_Pin_4);
}
4.5 SPI配置与SD卡模式的对应关系
为了让你更直观地理解,我画了一张图,把SPI配置和SD卡SPI模式的关系串起来:
4.6 常见配置错误与排查方法
我在调试SD卡驱动时,SPI配置这块踩过不少坑。下面列几个最常见的:
我曾经遇到过一个案例:某款MCU的SPI外设,CPOL和CPHA的极性定义和标准SPI规范是反的。手册上写CPOL=0是空闲高电平,但实际测试发现CPOL=0才是空闲低电平。最后用示波器量了SCLK波形才确认。所以,不要完全相信手册,一定要用示波器实测。
排查步骤:
- 先看SCLK波形:空闲时是低电平还是高电平?确认CPOL对不对。
- 再看数据采样点:在SCLK的哪个沿有数据变化?确认CPHA对不对。
- 量一下时钟频率:初始化阶段是否≤400kHz?高速阶段是否在卡的支持范围内?
- 检查CS时序:发送命令前CS是否已拉低?命令发送完后CS是否保持低电平直到响应读完?
小技巧:如果你没有示波器,可以用一个简单的方法验证SPI配置——先发一个0xFF字节,看MISO上有没有回波。如果SPI配置正确,MISO应该能收到卡返回的数据(至少是0xFF或0x00)。如果MISO一直没反应,八成是CPOL/CPHA配错了。