2、SD卡基础知识:SD卡与SPI通信协议简介、SD卡模块接线(CS/SCK/MOSI/MISO)、SD卡初始化与卡类型检测、常见问题排查(电压不匹配、卡格式化)

好,咱们正式开始折腾SD卡。说实话,在嵌入式系统里做数据存储,SD卡绝对是最亲民的方案之一。便宜、容量大、随处都能买到。但很多新手一上来就被各种协议和接线搞懵了。别急,我带你一步步捋清楚。

2.1 SD卡与SPI通信协议简介

SD卡内部其实是个复杂的闪存芯片,但它对外提供了两种通信模式:SDIO模式SPI模式。咱们做单片机开发,绝大多数情况下用的是SPI模式。为什么?因为大部分单片机都自带SPI硬件外设,接线少,驱动也简单。

SPI,全称是Serial Peripheral Interface,串行外设接口。它是个主从通信协议,咱们的单片机是主机,SD卡是从机。通信需要四根线:

  • SCK(时钟线)—— 由主机产生,决定通信速率
  • MOSI(主机输出,从机输入)—— 主机发数据给SD卡
  • MISO(主机输入,从机输出)—— SD卡发数据给主机
  • CS(片选线)—— 选中SD卡,低电平有效

我个人习惯把SPI通信想象成两个人打电话:SCK是通话的节奏,MOSI是你说的话,MISO是对方回的话,CS就是“喂,你听着啊”这个信号。嗯,这样就好理解了。

关键点:SPI通信是同步的,所有数据交换都在时钟边沿完成。SD卡在SPI模式下,命令和响应都是字节流,每个命令固定6个字节。

2.2 SD卡模块接线(CS/SCK/MOSI/MISO)

接线这块,我踩过不少坑。先给你一个标准的接线表,以STM32F103为例:

SD卡模块引脚 STM32引脚 说明
CS PA4 片选,任意GPIO即可
SCK PA5 SPI时钟,必须用SPI硬件引脚
MOSI PA7 主机输出,接SD卡DI
MISO PA6 主机输入,接SD卡DO
VCC 3.3V 注意!不是5V!
GND GND 共地

这里我要特别强调一下:SD卡必须工作在3.3V。我曾经有一次手快,直接把5V接到了SD卡模块上,结果冒烟了……嗯,那模块直接报废。所以接线前一定用万用表量一下电压。

另外,有些SD卡模块自带电平转换芯片,可以兼容5V单片机。但如果你用的是3.3V的单片机(比如STM32),直接接就行,不用额外转换。

警告:MISO线是SD卡的输出,如果单片机引脚是5V容忍的,最好加个电阻分压或者电平转换。否则长期使用可能损坏SD卡或单片机。

2.3 SD卡初始化与卡类型检测

初始化SD卡,说白了就是让卡进入SPI模式,然后识别它是哪种卡。标准SD卡、SDHC卡、SDXC卡,它们的初始化流程略有不同。

我一般把初始化分成三步:

  1. 上电延时—— 给SD卡供电后,至少等待1ms,让它内部稳定
  2. 发送80个时钟脉冲—— 让SD卡进入SPI模式,这步不能省
  3. 发送CMD0 + CMD8 + CMD58—— 获取卡类型和电压范围

代码示例(伪代码,实际用C写):

// 1. 上电延时
delay_ms(10);

// 2. 发送80个时钟脉冲(CS拉高)
for(i=0; i<10; i++) {
    SPI_Write(0xFF);
}

// 3. 发送CMD0,让卡进入SPI模式
CS_LOW();
SPI_Write(0x40);  // CMD0命令索引
SPI_Write(0x00);
SPI_Write(0x00);
SPI_Write(0x00);
SPI_Write(0x00);
SPI_Write(0x95);  // CRC校验,CMD0必须带有效CRC
CS_HIGH();

// 4. 等待响应0x01(进入空闲状态)
// 如果一直收不到,说明卡没响应

// 5. 发送CMD8,检测卡是否支持SDHC
// CMD8参数:0x000001AA,CRC=0x87
// 如果响应0x01,说明是SDSC卡
// 如果响应0x05,说明是SDHC/SDXC卡

你想想看,为什么CMD0后面要跟CMD8?说白了,CMD8是用来探测卡是否支持1.8V或2.7V~3.6V电压范围的。如果卡不支持CMD8,那它就是老式的SDSC卡。如果支持,再看CMD58返回的OCR寄存器,就能区分SDHC和SDXC了。

小技巧:初始化时SPI时钟频率要慢,一般设到400kHz以下。等初始化完成后再切换到高速模式。我刚开始不知道这个,直接用10MHz去初始化,结果卡死活不响应。

2.4 常见问题排查(电压不匹配、卡格式化)

做SD卡驱动,十有八九会遇到问题。我把自己踩过的坑总结一下:

电压不匹配

这是最常见的问题。SD卡的工作电压是2.7V~3.6V,但很多开发板上的SPI引脚是5V电平。我曾经用一块5V的Arduino直接接SD卡模块,结果读出来的数据全是0xFF。后来用示波器一看,MISO线上的高电平只有2.8V,但单片机认为3V以下就是低电平……嗯,通信全乱了。

解决办法:用3.3V供电,或者加电平转换芯片(比如74LVC4245)。

卡格式化问题

很多SD卡出厂是FAT32格式,但有些卡是exFAT或者RAW格式。咱们做嵌入式,一般用FAT32或者裸扇区读写。如果卡没格式化,初始化时读MBR会失败。

我曾经遇到过一张64GB的卡,怎么初始化都不成功。折腾了半天,才发现它是exFAT格式,而我的FAT32驱动根本不认识。后来用电脑重新格式化成FAT32,问题就解决了。

其他常见问题

  • 卡没插好 —— 检查卡座弹片是否接触良好
  • 时钟太快 —— 初始化时降到100kHz试试
  • CRC校验错误 —— 有些卡对CRC要求严格,CMD0必须带有效CRC
  • 电源纹波太大 —— SD卡对电源质量敏感,加个10μF电容

避坑指南:如果你用的是淘宝上几块钱的SD卡模块,注意看上面的电平转换芯片。有些模块用的是3.3V LDO,但输入电压不能超过5.5V。我建议统一用3.3V供电,省心。

好了,SD卡的基础知识就这些。下一章咱们开始写真正的驱动代码,让卡读写起来。记住,硬件调试时别急,先确认电压、时钟、片选这三样都对,再谈软件问题。