一、工程說(shuō)明與接口定義?
適用芯片:?STM32F4xx / STM32F7xx / STM32H7xx?(其他系列可按 HAL調(diào)整)
接口方式:?SDIO 4-bit總線?(默認(rèn))
協(xié)議標(biāo)準(zhǔn):?SD 2.0?(含 SDHC/SDXC),通過(guò) CMD8/ACMD41完成電壓與容量識(shí)別
文件系統(tǒng):?FatFS?(diskio接口對(duì)接,扇區(qū)大小固定為512B)
硬件要點(diǎn):
?CD/DAT3作為片選?(硬件拉高,SDIO自動(dòng)控制)
?電源上電順序?:VCC先上電并穩(wěn)定,再拉低 CMD/CLK/DAT0~3,初始化完成后再釋放 DAT3片選
建議在 ?3.3V?電源域,SDIO接口附近放置 ?0.1μF + 10μF?去耦
本歷程默認(rèn)使用 ?HAL庫(kù),時(shí)鐘與引腳在 CubeMX中配置后生成工程再集成
?二、CubeMX與初始化配置?
RCC:HSE/PLL正常輸出,系統(tǒng)時(shí)鐘按芯片手冊(cè)設(shè)置
SDIO:
Mode:?SDIO?
Prescaler:初始分頻使 ?SDIO_CK ≤ 400 kHz?(識(shí)別階段)
Bus Wide:?1-bit?(初始化),初始化成功后切換 ?4-bit?
Hardware Flow Control:?Enable?
GPIO:CMD、CLK、DAT0~3設(shè)為 ?SDIO復(fù)用推挽;CD/DAT3設(shè)為 ?輸入上拉?(硬件片選)
NVIC:開(kāi)啟 ?SDIO中斷?與 ?DMA中斷?(優(yōu)先級(jí)略高于 SDIO)
生成工程后,將以下 sdio_sd.c/h加入工程,并在 main中調(diào)用初始化與測(cè)試接口
三、核心驅(qū)動(dòng)代碼?
頭文件: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狀態(tài)
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調(diào)用)
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
//內(nèi)部接口(可選)
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循環(huán))
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標(biāo)志,識(shí)別 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)設(shè)置塊長(zhǎng)度為 512(SDSC必要;SDHC/SDXC固定 512B,但某些主機(jī)仍要求)
if (HAL_SD_CmdBlockLength(&hsd, 512) != HAL_OK) return HAL_ERROR;
return HAL_OK;
}
//切換 4-bit總線(初始化后調(diào)用)
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;
}
//讀取扇區(qū)
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;
}
//寫(xiě)入扇區(qū)
#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;
//上電復(fù)位:拉低 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對(duì)接與使用示例?
diskio.c(最小化對(duì)接)
#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();
//掛載文件系統(tǒng)
fr = f_mount(&fs, "", 1);
if (fr != FR_OK) {
//格式化(首次燒錄或異常時(shí))
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) {
//主循環(huán)
}
}
五、常見(jiàn)問(wèn)題與優(yōu)化建議?
初始化失敗
檢查?電源 3.3V穩(wěn)定、去耦充分
確認(rèn)?SDIO引腳復(fù)用與上拉? 正確,DAT3片選邏輯符合
識(shí)別階段?SDIO_CK ≤ 400 kHz,初始化完成后再提速
若卡不支持?ACMD41,可回退到?CMD1?
速度優(yōu)化
初始化成功后調(diào)用?sd_switch_4bit()?,并將?ClockDiv? 調(diào)至芯片手冊(cè)允許的最大值
啟用?SDIO DMA? 與合適的中斷優(yōu)先級(jí)
文件系統(tǒng)
?SDHC? 容量上限?32GB,?SDXC? 為 64GB+;FatFS對(duì)大容量支持良好
若需?中文長(zhǎng)文件名,在 ffconf.h啟用?_USE_LFN=2? 并配置?FF_CODE_PAGE=936?
穩(wěn)定性
長(zhǎng)時(shí)間寫(xiě)入建議加入?寫(xiě)緩存/隊(duì)列? 與?掉電檢測(cè)?(GPIO中斷 +緩存落盤(pán))
工業(yè)環(huán)境注意?ESD防護(hù)? 與?TVS,貼片 SD NAND比 TF卡更抗震動(dòng)
以上文件可直接集成到現(xiàn)有 STM32工程,配合 CubeMX生成的初始化代碼即可完成?SD 2.0瀚海微SD NAND? 的穩(wěn)定驅(qū)動(dòng)與?FATFS? 文件讀寫(xiě)。
審核編輯 黃宇
-
STM32
+關(guān)注
關(guān)注
2309文章
11162瀏覽量
373404 -
SD NAND
+關(guān)注
關(guān)注
0文章
112瀏覽量
1826
發(fā)布評(píng)論請(qǐng)先 登錄
STM32+SD NAND(貼片SD卡)完成FATFS文件系統(tǒng)移植與測(cè)試
關(guān)于SD NAND 的概述
STM32系列與SD NAND 成功的經(jīng)驗(yàn)分享
瀚海微SD NAND之SD 協(xié)議(29)硬件接口
瀚海微SD NAND之SD 協(xié)議(32)1.8V信令的驅(qū)動(dòng)強(qiáng)度和總線時(shí)序
瀚海微SD NAND之SD 協(xié)議(36)SPI模式
CS創(chuàng)世SD NAND在北京君正平臺(tái)和瑞芯微RK平臺(tái)的應(yīng)用
瀚海微SD NAND/TF卡——數(shù)據(jù)世界的全能搭檔
瀚海微SD NAND/TF卡:賦能全場(chǎng)景數(shù)據(jù)存儲(chǔ),定義高效安全新基準(zhǔn)
瀚海微SD NAND/TF卡數(shù)據(jù)讀寫(xiě)超時(shí)(Data Transfer Timeout)問(wèn)題深度解析
瀚海微SD NAND TF卡硬件識(shí)別與初始化類(lèi)問(wèn)題探討
解決SD NAND CRC校驗(yàn)失敗的綜合指南:瀚海微存儲(chǔ)產(chǎn)品的可靠性保障
瀚海微SD NAND/TF卡數(shù)據(jù)損壞與校驗(yàn)錯(cuò)誤(含CRC錯(cuò)誤、數(shù)據(jù)比對(duì)失敗)問(wèn)題解析
ESP32 驅(qū)動(dòng)瀚海微SD NAND 完整方案 + FAT/FAT32 驅(qū)動(dòng)核心區(qū)別
STM32 如何驅(qū)動(dòng) 瀚海微SD NAND
評(píng)論