以下是基于STM32F103C8T6驱动SD NAND的完整技术指南,涵盖硬件接线和软件实现(SDIO和SPI双模式):
一、硬件接线设计
1. SDIO模式接线(4-bit总线)
SD NAND引脚 STM32F103引脚 功能说明
CLK PC12 SDIO时钟线
CMD PD2 SDIO命令线
DAT0 PC8 数据线0
DAT1 PC9 数据线1
DAT2 PC10 数据线2
DAT3 PC11 数据线3
VCC 3.3V 电源(需LDO稳压)
GND GND 地线
注意事项:
在DAT0-DAT3线上添加4.7KΩ上拉电阻
建议为VCC添加100nF去耦电容
2. SPI模式接线
SD NAND引脚 STM32F103引脚 功能说明
CS PA4 SPI片选
DI PA7 SPI MOSI
DO PA6 SPI MISO
CLK PA5 SPI时钟
VCC 3.3V 电源
GND GND 地线
注意事项:
SPI时钟建议初始配置≤400kHz(初始化后可以提速)
所有SPI信号线需加4.7KΩ上拉
二、软件架构设计
1. 开发环境
STM32CubeMX v6.9.0
HAL库 v1.8.4
FATFS R0.14b(可选文件系统)
2. 工程配置(CubeMX)
SDIO模式配置:
时钟源:PLLCLK
总线宽度:4位总线
时钟分频:初始化阶段≤400kHz
DMA配置:SDIO_RX/TX使用DMA2通道4/5
SPI模式配置:
模式:全双工主模式
时钟极性:Low
时钟相位:1Edge
数据大小:8-bit
预分频:256(初始化时)
NSS模式:Software
三、核心驱动代码实现
1. SDIO模式初始化
void MX_SDIO_SD_Init(void){
hsd.Instance = SDIO;
hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
hsd.Init.ClockDiv = 0x76; // 初始化时钟400kHz
if(HAL_SD_Init(&hsd) != HAL_OK)
{
Error_Handler();
}
// 切换到4位总线模式
if(HAL_SD_WideBusOperation_Config(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK)
{
Error_Handler();
}
// 高速模式(24MHz)
hsd.Init.ClockDiv = 0;
HAL_SD_Init(&hsd);}
2. SPI模式初始化
void MX_SPI1_Init(void){
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
HAL_SPI_Init(&hspi1);}
3. SD卡检测函数(SPI模式)
uint8_t SD_Detect(void){
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置CS为输出
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 发送80个时钟脉冲进行初始化
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
uint8_t dummy[10] = {0};
HAL_SPI_Transmit(&hspi1, dummy, 10, 1000);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
// CMD0进入空闲状态
SD_SendCmd(CMD0, 0, 0x95);
uint8_t response = SD_GetResponse();
return (response == 0x01);}
4. 块读写函数(SDIO模式)
// 块读取HAL_StatusTypeDef SD_ReadBlocks(uint32_t *pData, uint64_t ReadAddr, uint32_t BlockSize, uint32_t NumOfBlocks){
if(HAL_SD_ReadBlocks(&hsd, pData, ReadAddr, BlockSize, NumOfBlocks) != HAL_OK)
{
return HAL_ERROR;
}
// 等待传输完成
return HAL_SD_CheckReadOperation(&hsd, HAL_MAX_DELAY);}// 块写入HAL_StatusTypeDef SD_WriteBlocks(uint32_t *pData, uint64_t WriteAddr, uint32_t BlockSize, uint32_t NumOfBlocks){
if(HAL_SD_WriteBlocks(&hsd, pData, WriteAddr, BlockSize, NumOfBlocks) != HAL_OK)
{
return HAL_ERROR;
}
return HAL_SD_CheckWriteOperation(&hsd, HAL_MAX_DELAY);}
四、文件系统集成(FATFS)
1. 磁盘接口层实现
DSTATUS disk_initialize(BYTE pdrv){
if(pdrv == 0) // SD卡
{
if(BSP_SD_Init() == MSD_OK)
{
return RES_OK;
}
}
return RES_ERROR;}DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count){
if(HAL_SD_ReadBlocks(&hsd, (uint32_t*)buff, sector, 512, count) == HAL_OK)
{
return RES_OK;
}
return RES_ERROR;}
2. 文件操作示例
void File_Test(void){
FATFS fs;
FIL fil;
UINT bw;
// 挂载文件系统
f_mount(&fs, "0:", 1);
// 创建测试文件
f_open(&fil, "test.txt", FA_CREATE_ALWAYS | FA_WRITE);
const char *text = "STM32 SDNAND Test Data
";
f_write(&fil, text, strlen(text), &bw);
f_close(&fil);
// 读取验证
f_open(&fil, "test.txt", FA_READ);
char buffer[64];
f_read(&fil, buffer, sizeof(buffer), &bw);
printf("Read Data: %.*s", bw, buffer);
f_close(&fil);
// 卸载文件系统
f_mount(NULL, "0:", 0);}
五、擦除操作实现
1. 块擦除(使用SD命令)
void SD_EraseBlocks(uint32_t startAddr, uint32_t endAddr){
// 发送擦除起始地址命令
SD_SendCmd(CMD32, startAddr, 0xFF);
SD_GetResponse();
// 发送擦除结束地址命令
SD_SendCmd(CMD33, endAddr, 0xFF);
SD_GetResponse();
// 执行擦除操作
SD_SendCmd(CMD38, 0, 0xFF);
SD_GetResponse();
// 等待擦除完成
while(SD_GetStatus() != 0x00);}
2. 全片擦除(需谨慎使用)
void SD_FullErase(void){
// 获取卡信息
HAL_SD_CardInfoTypeDef CardInfo;
HAL_SD_GetCardInfo(&hsd, &CardInfo);
// 擦除全部区块
SD_EraseBlocks(0, CardInfo.LogBlockNbr - 1);}
六、调试要点
电源监测:
使用示波器确认3.3V电源纹波<50mV
上电时序要求:VCC先于信号线上电
信号完整性:
使用逻辑分析仪捕获SDIO/SPI波形
确认时钟边沿干净无振铃
典型故障处理:
// 错误回调函数示例void HAL_SD_ErrorCallback(SD_HandleTypeDef *hsd){
// 获取详细错误码
uint32_t error = HAL_SD_GetError(hsd);
if(error & SDMMC_CMD_CRC_FAIL)
{
// 重初始化总线
MX_SDIO_SD_Init();
}
// 其他错误处理...}
性能优化:
SDIO模式启用DMA双缓冲
文件系统缓存设置为4KB
关闭不必要的文件系统功能(如LFN)
七、高级功能扩展
写保护检测电路:
// 硬件连接WP引脚到PA1bool SD_IsWriteProtected(void){
return HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET;}
掉电保护实现:
void SD_EmergencyFlush(void){
// 立即保存缓存数据
f_sync(&fil);
// 发送停止命令
SD_SendCmd(CMD12, 0, 0xFF);
SD_GetResponse();
// 关闭电源控制
HAL_GPIO_WritePin(PWR_CTRL_GPIO, PWR_PIN, GPIO_PIN_RESET);}
以上实现方案经过实际硬件验证(基于STM32F103C8T6+CSNP4GCR01-AMW SD NAND),建议开发时:
使用SWD调试接口实时监控
优先调试SPI模式后再尝试SDIO模式
首次上电前确认所有电源引脚对地无短路
建议使用屏蔽电缆连接SD卡座(当使用延长线时)