
一、工程说明与接口定义
二、CubeMX 与初始化配置

三、核心驱动代码
#ifndef __SDIO_SD_H
#define __SDIO_SD_H
#include "stm32f4xx_hal.h"
#include "ff_gen_drv.h"
// 返回值定义(与 HAL 一致)
#define RES_OK 0U
#define RES_ERROR 1U
#define RES_NOTRDY 2U
#define RES_PARERR 3U
// SD 状态
typedef enum {
SD_CARD_UNINIT = 0,
SD_CARD_READY,
SD_CARD_IDENT,
SD_CARD_STBY,
SD_CARD_TRAN,
SD_CARD_DATA,
SD_CARD_RCV,
SD_CARD_PRG,
SD_CARD_DIS,
SD_CARD_ERROR
} sd_card_state_t;
// 公共接口(供 diskio.c 调用)
DSTATUS sd_disk_initialize (BYTE pdrv);
DSTATUS sd_disk_status (BYTE pdrv);
DRESULT sd_disk_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
#if _USE_WRITE == 1
DRESULT sd_disk_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
#endif
#if _USE_IOCTL == 1
DRESULT sd_disk_ioctl (BYTE pdrv, BYTE cmd, void *buff);
#endif
// 内部接口(可选)
HAL_StatusTypeDef sd_init_card(void);
HAL_StatusTypeDef sd_switch_4bit(void);
sd_card_state_t sd_get_state(void);
uint32_t sd_get_sector_count(void);
uint32_t sd_get_block_size(void);
#endif
#include "sdio_sd.h"
#include <string.h>
SD_HandleTypeDef hsd;
HAL_SD_CardInfoTypeDef SDCardInfo;
static DSTATUS disk_status_ = STA_NOINIT;
// 私有:等待卡退出空闲(CMD0 -> CMD1 循环)
static HAL_StatusTypeDef sd_cmd0_cmd1(void)
{
uint32_t resp;
uint32_t timeout = 1000000;
// CMD0: GO_IDLE_STATE
if (HAL_SD_CmdGoIdleState(&hsd, &resp) != HAL_OK) return HAL_ERROR;
if ((resp & 0xFF) != 0x01) return HAL_ERROR;
// CMD1: SEND_OP_COND(SD 2.0)
do {
if (HAL_SD_CmdAppOpCond(&hsd, 0x00000000, &resp) != HAL_OK) return HAL_ERROR;
if (timeout-- == 0) return HAL_TIMEOUT;
} while ((resp & 0x80000000) != 0); // CCS 位未置位
return HAL_OK;
}
// 私有:ACMD41(带 HCS 标志,识别 SDHC/SDXC)
static HAL_StatusTypeDef sd_acmd41(void)
{
uint32_t resp;
uint32_t timeout = 1000000;
do {
if (HAL_SD_CmdAppCommand(&hsd, 0x00000000, &resp) != HAL_OK) return HAL_ERROR; // CMD55
if ((resp & 0xFF) != 0x01) return HAL_ERROR;
if (HAL_SD_CmdAppOpCond(&hsd, 0x40000000, &resp) != HAL_OK) return HAL_ERROR; // ACMD41 with HCS
if (timeout-- == 0) return HAL_TIMEOUT;
} while ((resp & 0x80000000) == 0);
return HAL_OK;
}
// 私有:读取 OCR(可选)
static HAL_StatusTypeDef sd_read_ocr(void)
{
uint32_t resp;
return HAL_SD_CmdReadOCR(&hsd, &resp);
}
// 私有:获取 CSD/CID(可选)
static HAL_StatusTypeDef sd_get_cid_csd(void)
{
if (HAL_SD_GetCardCID(&hsd, (HAL_SD_CardCIDTypeDef*)&SDCardInfo.CID) != HAL_OK)
return HAL_ERROR;
if (HAL_SD_GetCardCSD(&hsd, (HAL_SD_CardCSDTypeDef*)&SDCardInfo.CSD) != HAL_OK)
return HAL_ERROR;
return HAL_OK;
}
// 初始化卡(SD 2.0)
HAL_StatusTypeDef sd_init_card(void)
{
HAL_StatusTypeDef status;
// 1) CMD0
if ((status = sd_cmd0_cmd1()) != HAL_OK) return status;
// 2) CMD1 或 ACMD41(SD 2.0 走 ACMD41)
if ((status = sd_acmd41()) != HAL_OK) return status;
// 3) CMD2: ALL_SEND_CID
if (HAL_SD_CmdSendCID(&hsd, (uint32_t*)&SDCardInfo.CID) != HAL_OK) return HAL_ERROR;
// 4) CMD3: SEND_RELATIVE_ADDR(获取 RCA)
if (HAL_SD_CmdSetRelAdd(&hsd, (uint32_t*)&SDCardInfo.RCA) != HAL_OK) return HAL_ERROR;
// 5) CMD9: SEND_CSD
if (HAL_SD_CmdSendCSD(&hsd, (uint32_t*)&SDCardInfo.CSD) != HAL_OK) return HAL_ERROR;
// 6) CMD7: SELECT/DESELECT_CARD(选中卡)
if (HAL_SD_CmdSelDesel(&hsd, (uint32_t)(SDCardInfo.RCA << 16)) != HAL_OK) return HAL_ERROR;
// 7) CMD13: SEND_STATUS
if (HAL_SD_CmdSendStatus(&hsd, (uint32_t)(SDCardInfo.RCA << 16), &resp) != HAL_OK) return HAL_ERROR;
// 8) 读取 OCR(可选)
if ((status = sd_read_ocr()) != HAL_OK) return status;
// 9) 读取 CSD/CID(可选)
if ((status = sd_get_cid_csd()) != HAL_OK) return status;
// 10) 设置块长度为 512(SDSC 必要;SDHC/SDXC 固定 512B,但某些主机仍要求)
if (HAL_SD_CmdBlockLength(&hsd, 512) != HAL_OK) return HAL_ERROR;
return HAL_OK;
}
// 切换 4-bit 总线(初始化后调用)
HAL_StatusTypeDef sd_switch_4bit(void)
{
uint32_t resp;
// CMD55 + ACMD6(bus width = 2 => 4-bit)
if (HAL_SD_CmdAppCommand(&hsd, 0x00000000, &resp) != HAL_OK) return HAL_ERROR;
if (HAL_SD_CmdAppSetBusWidth(&hsd, 2, &resp) != HAL_OK) return HAL_ERROR;
hsd.Instance->CLKCR |= SDIO_CLKCR_WIDBUS_0; // 4-bit
return HAL_OK;
}
// 读取扇区
DRESULT sd_disk_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count)
{
(void)pdrv;
if (disk_status_ != RES_OK) return RES_NOTRDY;
if (HAL_SD_ReadBlocks(&hsd, (uint32_t*)buff, sector, count, 1000000) == HAL_OK) {
// 等待传输完成
while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER) {}
return RES_OK;
}
return RES_ERROR;
}
// 写入扇区
#if _USE_WRITE == 1
DRESULT sd_disk_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count)
{
(void)pdrv;
if (disk_status_ != RES_OK) return RES_NOTRDY;
if (HAL_SD_WriteBlocks(&hsd, (uint32_t*)buff, sector, count, 1000000) == HAL_OK) {
while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER) {}
return RES_OK;
}
return RES_ERROR;
}
#endif
// IOCTL
#if _USE_IOCTL == 1
DRESULT sd_disk_ioctl (BYTE pdrv, BYTE cmd, void *buff)
{
(void)pdrv;
DRESULT res = RES_PARERR;
switch (cmd) {
case CTRL_SYNC:
while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER) {}
res = RES_OK;
break;
case GET_SECTOR_COUNT:
*(uint32_t*)buff = SDCardInfo.BlockNbr;
res = RES_OK;
break;
case GET_SECTOR_SIZE:
*(uint32_t*)buff = SDCardInfo.BlockSize;
res = RES_OK;
break;
case GET_BLOCK_SIZE:
*(uint32_t*)buff = SDCardInfo.BlockSize; // SD 2.0 通常为 512
res = RES_OK;
break;
default:
res = RES_PARERR;
}
return res;
}
#endif
// Disk Status
DSTATUS sd_disk_status (BYTE pdrv)
{
(void)pdrv;
if (disk_status_ == RES_OK) {
if (HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_TRANSFER)
return RES_OK;
else
return STA_NOINIT;
}
return disk_status_;
}
// Disk Initialize
DSTATUS sd_disk_initialize (BYTE pdrv)
{
(void)pdrv;
if (disk_status_ == RES_OK) return RES_OK;
// 上电复位:拉低 DAT3 片选(若硬件未处理)
// __HAL_RCC_SDIO_CLK_ENABLE();
// SDIO power on/off sequence per reference manual
if (sd_init_card() == HAL_OK) {
if (sd_switch_4bit() == HAL_OK) {
disk_status_ = RES_OK;
return RES_OK;
}
}
disk_status_ = RES_ERROR;
return RES_ERROR;
}
四、FatFS 对接与使用示例
#include "diskio.h"
#include "ff_gen_drv.h"
#include "sdio_sd.h"
#define SD_DISK_NUM 0
DSTATUS disk_status (BYTE pdrv) { return sd_disk_status(pdrv); }
DSTATUS disk_initialize (BYTE pdrv) { return sd_disk_initialize(pdrv); }
DRESULT disk_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count) { return sd_disk_read(pdrv, buff, sector, count); }
#if _USE_WRITE
DRESULT disk_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count) { return sd_disk_write(pdrv, buff, sector, count); }
#endif
#if _USE_IOCTL
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void *buff) { return sd_disk_ioctl(pdrv, cmd, buff); }
#endif
#include "ff.h"
#include "sdio_sd.h"
FATFS fs;
FIL f;
FRESULT fr;
UINT bw;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SDIO_SD_Init();
// 挂载文件系统
fr = f_mount(&fs, "", 1);
if (fr != FR_OK) {
// 格式化(首次烧录或异常时)
fr = f_mkfs("", FM_ANY, 0, buff, sizeof(buff));
if (fr == FR_OK) fr = f_mount(&fs, "", 1);
}
if (fr == FR_OK) {
fr = f_open(&f, "test.txt", FA_WRITE | FA_CREATE_ALWAYS);
if (fr == FR_OK) {
f_puts("Hello, SD NAND (SDIO 4-bit, SD 2.0)\r\n", &f);
f_close(&f);
}
}
while (1) {
// 主循环
}
}
五、常见问题与优化建议
以上文件可直接集成到现有 STM32 工程,配合 CubeMX 生成的初始化代码即可完成 SD 2.0 瀚海微SD NAND 的稳定驱动与 FATFS 文件读写。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。