如果你正在尝试将SD NAND接入一个资源受限的嵌入式系统,SPI模式确实是个很实用的选择——它接口简单,占用引脚少,但编程过程中有几个关键点需要特别注意。以下是我结合SD协议规范与实际工程经验总结出的SPI模式编程设置方法,帮你避开一些常见“坑点”:
SPI模式仅需4根信号线,比SD模式的6线或4线并行精简很多:
SD NAND引脚 | MCU/主控引脚 | 作用 |
---|---|---|
CS | GPIO | 片选(低电平有效) |
CLK | SPI_SCK | 时钟信号 |
DI (MOSI) | SPI_MOSI | 主设备输出,从设备输入 |
DO (MISO) | SPI_MISO | 主设备输入,从设备输出 |
VCC/GND | 电源/地 | 供电(通常2.7V~3.6V) |
硬件注意点:
上拉电阻:建议在CLK、DI、DO线上加1-10kΩ上拉电阻,避免悬空导致的不稳定;
CS引脚:务必在初始化期间保持低电平,否则无法进入SPI模式。
初始化是SPI模式能否正常工作的关键,必须严格按顺序操作:
初始化SPI外设:
配置SPI时钟为低速模式(≤400 kHz);
发送≥74个空时钟周期(实际建议发80~100个),用于稳定内部电路
2.发送CMD0(复位命令):
spi_send_cmd(0x40, 0x00000000, 0x95); // CMD0格式: 0x40 + 32位参数 + CRC7
3.发送CMD8(电压检查):
spi_send_cmd(0x48, 0x000001AA, 0x87); // 参数0x1AA表示支持2.7~3.6V
若卡返回R7响应(如0x01 0x00 0x00 0x01AA
),说明是SD2.0+卡且电压兼容
4.循环发送CMD55+ACMD41:
do { spi_send_cmd(0x77, 0x00000000, 0x65); // CMD55 spi_send_cmd(0x69, 0x40000000, 0x77); // ACMD41 (带HCS位) response = spi_read_byte();} while (response != 0x00); // 等待返回0x00(初始化完成)
若ACMD41返回值的bit31=1,说明是SDHC/SDXC卡(块寻址)
5.发送CMD58(读取OCR):
确认供电状态和卡类型(如CCS位指示块寻址)
6.发送CMD16(设置块大小):
spi_send_cmd(0x50, 0x00000200, 0x15); // 设置512字节/块
即使SDHC默认为512字节,也建议显式设置
7.切换至高速模式:
初始化成功后,提高SPI时钟至最高25 MHz(根据卡性能调整)
spi_send_cmd(0x51, block_address, 0x00); // 块地址(SDHC用块号)while (spi_read_byte() != 0xFE); // 等待数据起始令牌0xFEfor (int i=0; i<512; i++) { buffer[i] = spi_read_byte(); // 读取数据}spi_read_byte(); spi_read_byte(); // 丢弃2字节CRC
spi_send_cmd(0x58, block_address, 0x00); spi_send_byte(0xFE); // 发送数据起始令牌for (int i=0; i<512; i++) { spi_send_byte(buffer[i]); // 发送数据}spi_send_byte(0xFF); spi_send_byte(0xFF); // 发送伪CRCstatus = spi_read_byte(); // 读取写状态if ((status & 0x1F) != 0x05) { /* 错误处理 */ }
关键点:
块地址:SDSC卡用字节地址,SDHC/SDXC必须用块号(512字节为块);
忙状态检测:写操作后需轮询spi_read_byte()
直到返回非0xFF(卡忙结束)
无响应:检查硬件连接、CS电平、时钟频率(初始必须≤400 kHz);
CMD8返回错误:电压不匹配或卡版本旧(SD1.1不支持CMD8);
ACMD41超时:尝试发送CMD1(仅限MMC卡)或检查HCS位设置;
数据错误:确保每次读写前发送正确的起始令牌(0xFE)
DMA传输:使用DMA搬运SPI数据,减少CPU占用;
多块读写:支持CMD18/25连续读写时,避免单块多次寻址;
双缓冲区:读写时交替填充缓冲区,隐藏等待时间
#include <SPI.h>#define CS_PIN 4void setup() { SPI.begin(); pinMode(CS_PIN, OUTPUT); digitalWrite(CS_PIN, HIGH); // 初始化SD卡(SPI模式) sd_init();}void sd_init() { digitalWrite(CS_PIN, LOW); for (int i=0; i<80; i++) SPI.transfer(0xFF); // 发送80个空时钟 send_cmd(0x40, 0, 0x95); // CMD0 send_cmd(0x48, 0x1AA, 0x87); // CMD8 // ...后续参考上文流程}void send_cmd(uint8_t cmd, uint32_t arg, uint8_t crc) { SPI.transfer(cmd); SPI.transfer(arg >> 24); SPI.transfer(arg >> 16); SPI.transfer(arg >> 8); SPI.transfer(arg); SPI.transfer(crc);}
SPI模式的核心在于严格遵循初始化序列和正确处理响应令牌。虽然速度不如SD模式,但其硬件兼容性极强,适合STM32、ESP32或Arduino等MCU。实际开发中建议:
使用逻辑分析仪抓取SPI波形,对照SD协议检查时序;
为关键函数(如send_cmd()
)添加超时重试机制;
工业场景下,对写操作做冗余校验(如写入后立即回读验证)