首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >STM32 如何驱动 瀚海微SD NAND

STM32 如何驱动 瀚海微SD NAND

原创
作者头像
杭州瀚海微
发布2025-10-27 10:35:20
发布2025-10-27 10:35:20
1680
举报

一、工程说明与接口定义

  • 适用芯片:​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 <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 对接与使用示例

  • 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)\r\n", &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​ 文件读写。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档