电子说
一、工程说明与接口定义
适用芯片:STM32F4xx / STM32F7xx / STM32H7xx(其他系列可按 HAL 调整)
接口方式:SDIO 4-bit 总线(默认)
协议标准:SD 2.0(含 SDHC/SDXC),通过 CMD8/ACMD41 完成电压与容量识别
文件系统:FatFS(diskio 接口对接,扇区大小固定为512B)
硬件要点:
CD/DAT3 作为片选(硬件拉高,SDIO 自动控制)
电源上电顺序:VCC 先上电并稳定,再拉低 CMD/CLK/DAT0~3,初始化完成后再释放 DAT3 片选
建议在 3.3V 电源域,SDIO 接口附近放置 0.1µF + 10µF 去耦
本历程默认使用 HAL 库,时钟与引脚在 CubeMX 中配置后生成工程再集成
二、CubeMX 与初始化配置
RCC:HSE/PLL 正常输出,系统时钟按芯片手册设置
SDIO:
Mode:SDIO
Prescaler:初始分频使 SDIO_CK ≤ 400 kHz(识别阶段)
Bus Wide:1-bit(初始化),初始化成功后切换 4-bit
Hardware Flow Control:Enable
GPIO:CMD、CLK、DAT0~3 设为 SDIO 复用推挽;CD/DAT3 设为 输入上拉(硬件片选)
NVIC:开启 SDIO 中断 与 DMA 中断(优先级略高于 SDIO)
生成工程后,将以下 sdio_sd.c/h 加入工程,并在 main 中调用初始化与测试接口
三、核心驱动代码
头文件:sdio_sd.h
#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
源文件:sdio_sd.c
#include "sdio_sd.h"
#include
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 对接与使用示例
diskio.c(最小化对接)
#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
main.c 最小示例
#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)rn", &f);
f_close(&f);
}
}
while (1) {
// 主循环
}
}
五、常见问题与优化建议
初始化失败
检查 电源 3.3V 稳定、去耦充分
确认 SDIO 引脚复用与上拉 正确,DAT3 片选逻辑符合
识别阶段 SDIO_CK ≤ 400 kHz,初始化完成后再提速
若卡不支持 ACMD41,可回退到 CMD1
速度优化
初始化成功后调用 sd_switch_4bit(),并将 ClockDiv 调至芯片手册允许的最大值
启用 SDIO DMA 与合适的中断优先级
文件系统
SDHC 容量上限 32GB,SDXC 为 64GB+;FatFS 对大容量支持良好
若需 中文长文件名,在 ffconf.h 启用 _USE_LFN=2 并配置 FF_CODE_PAGE=936
稳定性
长时间写入建议加入 写缓存/队列 与 掉电检测(GPIO 中断 + 缓存落盘)
工业环境注意 ESD 防护 与 TVS,贴片 SD NAND 比 TF 卡更抗震动
以上文件可直接集成到现有 STM32 工程,配合 CubeMX 生成的初始化代码即可完成 SD 2.0 瀚海微SD NAND 的稳定驱动与 FATFS 文件读写。
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !