STM32 如何驱动 瀚海微SD NAND

电子说

1.4w人已加入

描述

一、工程说明与接口定义

适用芯片:​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​ 文件读写。

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分