SD 卡 SPI 模式通信详解:与主控芯片的大容量数据交互(附源码)
本文介绍了SD卡在嵌入式系统中的SPI模式应用,主要内容包括:SPI模式的硬件连接方式(4线接口,3.3V电平转换),支持的SD卡类型(SD/SDHC/SDXC)及兼容性差异;详细阐述了SPI通信协议特点(主从同步、命令格式)和初始化流程;提供了单块读写及多块读写的代码实现示例;最后介绍了FAT文件系统集成方案,推荐使用FatFs等开源库进行简化开发。整体涵盖从底层硬件到上层文件系统的完整技术链路
文章目录
概要
SD 卡作为一种通用的大容量存储介质,在嵌入式系统中被广泛应用于数据日志、固件存储、多媒体文件管理等场景。多数 SD 卡模块支持 SPI 模式通信,通过简单的 4 线接口即可与 STM32、Arduino 等主控芯片实现高速数据交互,相比 SDIO 模式具有硬件设计简单、兼容性强的优势。
一、SD 卡 SPI 模式硬件架构
1. 基本连接方式
SD 卡与主控芯片的 SPI 模式连接仅需 4 根信号线,典型电路如下:
关键连接注意事项:
SD 卡工作电压为 3.3V,需使用电平转换电路(如 74LVC245)连接 5V 主控芯片
信号线建议串联 100Ω 限流电阻,保护 SD 卡接口
电源端需添加 100nF 和 10μF 电容滤波,减少电源噪声
布线应短且直,尤其在高速通信时(>10MHz)
2. 支持的 SD 卡类型
SPI 模式兼容多种 SD 卡标准,但需注意不同类型的差异:
SD 卡:容量≤2GB,支持 SPI 模式
SDHC 卡:容量 2GB~32GB,采用 FAT32 文件系统,支持 SPI 模式
SDXC 卡:容量 32GB~2TB,采用 exFAT 文件系统,部分模块可能不支持
microSD 卡:通过适配器可与标准 SD 卡接口兼容,是嵌入式系统首选
二、SD 卡 SPI 模式通信协议
1. 通信基础
SD 卡 SPI 模式采用主从同步通信方式,具有以下特点:
由主控芯片产生 SCK 时钟信号,频率可在 100kHz~25MHz 范围内调整(初始化阶段需≤400kHz)
数据在 SCK 的上升沿从主控发送到 SD 卡,在 SCK 的下降沿从 SD 卡接收数据
所有命令和数据传输均以 CS 线拉低为前提,CS 线拉高时 SD 卡忽略 SPI 总线上的所有信号
2. 命令格式
SD 卡通过特定命令集进行操作,SPI 模式下的命令格式如下:
常用基础命令:
CMD0:复位卡,进入 IDLE 状态(0x40 00 00 00 00 95)
CMD1:初始化卡,检查卡是否准备好(0x41 00 00 00 00 FF)
CMD8:发送接口条件,用于 SDHC 卡识别(0x48 00 00 01 AA 87)
CMD16:设置块长度(默认 512 字节)(0x50 00 00 02 00 FF)
CMD17:读单块数据(0x51 + 32 位地址 + CRC)
CMD24:写单块数据(0x58 + 32 位地址 + CRC)
CMD55:应用命令前缀(0x77 00 00 00 00 FF)
ACMD41:SD 卡初始化(需先发送 CMD55)
3. 初始化流程
SD 卡在使用前必须经过初始化流程,特别是 SPI 模式:
三、数据读写操作实现
1. 单块读取操作
读取 SD 卡数据以块(默认 512 字节)为单位,流程如下:
// 从SD卡指定地址读取一个块(512字节)
// addr: 块地址(SDHC为扇区地址,单位512字节)
// buf: 接收缓冲区(需至少512字节)
// 返回值: 0=成功,非0=失败
uint8_t sd_read_block(uint32_t addr, uint8_t *buf) {
uint8_t response;
uint16_t timeout;
// 1. 拉低CS,选中SD卡
SD_CS_LOW();
// 2. 发送读块命令CMD17 (0x51)
spi_send_byte(0x51); // 命令字节
spi_send_byte((addr >> 24) & 0xFF); // 地址高8位
spi_send_byte((addr >> 16) & 0xFF); // 地址中8位
spi_send_byte((addr >> 8) & 0xFF); // 地址低8位
spi_send_byte(addr & 0xFF);
spi_send_byte(0xFF); // CRC(SPI模式可忽略)
// 3. 等待响应(0x00表示成功)
timeout = 0;
do {
response = spi_receive_byte();
if (timeout++ > 0xFFFF) {
SD_CS_HIGH();
return 1; // 超时错误
}
} while (response != 0x00);
// 4. 等待数据起始令牌(0xFE)
timeout = 0;
do {
response = spi_receive_byte();
if (timeout++ > 0xFFFF) {
SD_CS_HIGH();
return 2; // 无数据令牌错误
}
} while (response != 0xFE);
// 5. 读取512字节数据
for (uint16_t i = 0; i < 512; i++) {
buf[i] = spi_receive_byte();
}
// 6. 读取2字节CRC(SPI模式可忽略)
spi_receive_byte();
spi_receive_byte();
// 7. 拉高CS,结束读取
SD_CS_HIGH();
return 0; // 成功
}
2. 单块写入操作
写入操作同样以块为单位,且需要严格遵循时序:
// 向SD卡指定地址写入一个块(512字节)
// addr: 块地址
// buf: 待写入数据(必须512字节)
// 返回值: 0=成功,非0=失败
uint8_t sd_write_block(uint32_t addr, const uint8_t *buf) {
uint8_t response;
uint16_t timeout;
// 1. 拉低CS,选中SD卡
SD_CS_LOW();
// 2. 发送写块命令CMD24 (0x58)
spi_send_byte(0x58); // 命令字节
spi_send_byte((addr >> 24) & 0xFF); // 地址高8位
spi_send_byte((addr >> 16) & 0xFF); // 地址中8位
spi_send_byte((addr >> 8) & 0xFF); // 地址低8位
spi_send_byte(addr & 0xFF);
spi_send_byte(0xFF); // CRC
// 3. 等待响应(0x00表示成功)
timeout = 0;
do {
response = spi_receive_byte();
if (timeout++ > 0xFFFF) {
SD_CS_HIGH();
return 1; // 超时错误
}
} while (response != 0x00);
// 4. 发送数据起始令牌(0xFE)
spi_send_byte(0xFE);
// 5. 发送512字节数据
for (uint16_t i = 0; i < 512; i++) {
spi_send_byte(buf[i]);
}
// 6. 发送2字节伪CRC(SPI模式可忽略)
spi_send_byte(0xFF);
spi_send_byte(0xFF);
// 7. 检查数据响应
response = spi_receive_byte();
if ((response & 0x1F) != 0x05) { // 0x05表示数据接受成功
SD_CS_HIGH();
return 2; // 写入失败
}
// 8. 等待写入完成
timeout = 0;
while (spi_receive_byte() != 0xFF) {
if (timeout++ > 0xFFFF) {
SD_CS_HIGH();
return 3; // 写入超时
}
}
// 9. 拉高CS,结束写入
SD_CS_HIGH();
return 0; // 成功
}
3. 多块读写操作
对于大数据量传输,可使用多块读写命令提高效率:
多块读:使用 CMD18 命令,持续接收数据直到发送 CMD12 停止命令
多块写:使用 CMD25 命令,发送数据直到发送停止令牌 (0xFD)
四、文件系统与上层应用
SD 卡通常采用 FAT 文件系统(FAT16/FAT32/exFAT),嵌入式系统中可通过以下库简化开发:
1. 常用文件系统库
FatFs:开源的 FAT 文件系统模块,支持 SPI 模式 SD 卡,体积小、移植性好
SD 库:Arduino 平台专用,封装了底层 SPI 通信和 FatFs 文件系统
STM32Cube 库:ST 官方提供的 SD 卡驱动,支持 SPI 和 SDIO 模式
2. 文件操作示例(基于 FatFs)
#include "ff.h"
#include "diskio.h"
FATFS fs; // 文件系统对象
FIL file; // 文件对象
FRESULT fr; // 操作结果
UINT bw, br; // 读写字节数
// 初始化文件系统并创建文件
void sd_file_demo(void) {
// 1. 挂载文件系统
fr = f_mount(&fs, "", 1);
if (fr != FR_OK) {
// 挂载失败处理
return;
}
// 2. 创建并打开文件(如果不存在则创建)
fr = f_open(&file, "data.log", FA_WRITE | FA_CREATE_ALWAYS);
if (fr != FR_OK) {
// 打开失败处理
f_mount(NULL, "", 1);
return;
}
// 3. 向文件写入数据
const char *text = "Hello, SD Card!";
fr = f_write(&file, text, strlen(text), &bw);
if (fr != FR_OK || bw != strlen(text)) {
// 写入失败处理
}
// 4. 关闭文件
f_close(&file);
// 5. 打开文件读取内容
fr = f_open(&file, "data.log", FA_READ);
if (fr == FR_OK) {
char buffer[100];
fr = f_read(&file, buffer, sizeof(buffer)-1, &br);
if (fr == FR_OK) {
buffer[br] = '\0'; // 添加字符串结束符
// 处理读取到的数据
}
f_close(&file);
}
// 6. 卸载文件系统
f_mount(NULL, "", 1);
}
五、性能优化与可靠性设计
1. 速率优化
初始化完成后可提高 SPI 时钟频率(最高 25MHz),提高传输速度
使用 DMA 方式传输数据,减少 CPU 占用(尤其在 STM32 等高端 MCU 上)
采用多块读写命令,减少命令交互开销
2. 可靠性保障
电源管理:确保 3.3V 电源稳定,波动不超过 ±0.3V
数据校验:关键数据可添加校验和,检测传输错误
异常处理:实现命令超时、卡拔出检测和自动重连机制
磨损均衡:避免频繁写入同一区块,延长 SD 卡使用寿命
3. 低功耗设计
空闲时通过 CMD50 命令使 SD 卡进入休眠模式
降低 SPI 时钟频率或关闭 SPI 外设,减少功耗
采用中断方式而非轮询,减少 CPU 活动时间
六、常见问题与解决方案

总结
SD 卡的 SPI 模式通信为嵌入式系统提供了便捷的大容量存储解决方案,其核心优势在于:
硬件接口简单,仅需 4 根线即可实现通信
兼容性强,支持多种 SD 卡类型和主控芯片
传输速度适中,满足大多数嵌入式应用需求
配合文件系统库,可实现复杂的文件管理功能
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)