SDNAND 是一种采用 SD 接口的 NAND 闪存芯片,相比传统的 TF 卡,它具有更高的可靠性和更低的功耗,特别适合嵌入式系统应用。下面详细介绍如何在 STM32F407ZET6 上驱动 SDNAND。
STM32F407ZET6 与 SDNAND 的典型连接方式:
STM32F407ZET6 SDNAND
-------------------------------
PC8 (SDIO_D0) <--> D0
PC9 (SDIO_D1) <--> D1
PC10 (SDIO_D2) <--> D2
PC11 (SDIO_D3) <--> D3
PC12 (SDIO_CK) <--> CLK
PD2 (SDIO_CMD) <--> CMD
VCC <--> VCC (通常3.3V)
GND <--> GND
首先需要初始化 STM32 的 SDIO 接口,配置时钟、总线宽度等参数:
#include "main.h"
#include "sdmmc.h"
SD_HandleTypeDef hsd1;
void MX_SDMMC1_SD_Init(void)
{
hsd1.Instance = SDMMC1;
hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
hsd1.Init.BusWide = SDMMC_BUS_WIDE_4B; // 使用4位宽总线
hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
hsd1.Init.ClockDiv = 2; // 初始时钟分频,后续可提高
if (HAL_SD_Init(&hsd1) != HAL_OK)
{
Error_Handler();
}
// 配置宽总线模式
if (HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B) != HAL_OK)
{
Error_Handler();
}
}
void HAL_SD_MspInit(SD_HandleTypeDef* sdHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(sdHandle->Instance == SDMMC1)
{
// 使能时钟
__HAL_RCC_SDMMC1_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
// 配置GPIO引脚
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_SDIO1;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_2;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
// 配置NVIC
HAL_NVIC_SetPriority(SDMMC1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SDMMC1_IRQn);
}
}
SDNAND 的初始化流程与标准 SD 卡有所不同,需要考虑 NAND 闪存的特性:
#include "sd_nand.h"
SDNAND_HandleTypeDef SDNAND_Handle;
HAL_StatusTypeDef SDNAND_Init(void)
{
HAL_StatusTypeDef status;
// 初始化SDIO接口
MX_SDMMC1_SD_Init();
// 发送CMD0: 复位SD卡
status = HAL_SD_SendCmd(&hsd1, SD_CMD_GO_IDLE_STATE, 0, 0, SD_MAX_TIMEOUT);
if(status != HAL_OK)
{
return status;
}
// 发送ACMD41: 初始化SD卡
uint32_t timeout = 0;
uint32_t ocr = 0;
do
{
status = HAL_SD_SendCmd(&hsd1, SD_CMD_SEND_OP_COND, 0x40000000, 0, SD_MAX_TIMEOUT);
if(status != HAL_OK)
{
return status;
}
// 读取OCR寄存器
status = HAL_SD_Read_OCR(&hsd1, &ocr);
if(status != HAL_OK)
{
return status;
}
timeout++;
if(timeout > SD_INIT_TIMEOUT)
{
return HAL_TIMEOUT;
}
} while(!(ocr & 0x40000000));
// 发送CMD2: 获取CID
status = HAL_SD_SendCmd(&hsd1, SD_CMD_ALL_SEND_CID, 0, 0, SD_MAX_TIMEOUT);
if(status != HAL_OK)
{
return status;
}
// 发送CMD3: 获取RCA
status = HAL_SD_SendCmd(&hsd1, SD_CMD_SET_REL_ADDR, 0, 0, SD_MAX_TIMEOUT);
if(status != HAL_OK)
{
return status;
}
// 发送CMD9: 获取CSD
status = HAL_SD_SendCmd(&hsd1, SD_CMD_SEND_CSD, SDNAND_Handle.RCA << 16, 0, SD_MAX_TIMEOUT);
if(status != HAL_OK)
{
return status;
}
// 配置SDNAND特定参数
SDNAND_Handle.BlkSize = 512; // 通常为512字节
SDNAND_Handle.CardType = SDIO_STD_CAPACITY_SD_CARD_V1_1; // 根据实际SDNAND类型调整
return HAL_OK;
}
SDNAND 的读写操作与标准 SD 卡类似,但需要考虑 NAND 闪存的特性:
#include "sd_nand.h"
/* 读取单个块 */
HAL_StatusTypeDef SDNAND_ReadBlock(uint8_t *pData, uint32_t BlockAddr, uint32_t Timeout)
{
return HAL_SD_ReadBlocks(&hsd1, pData, BlockAddr, 1, Timeout);
}
/* 读取多个块 */
HAL_StatusTypeDef SDNAND_ReadBlocks(uint8_t *pData, uint32_t BlockAddr, uint32_t NumberOfBlocks, uint32_t Timeout)
{
return HAL_SD_ReadBlocks(&hsd1, pData, BlockAddr, NumberOfBlocks, Timeout);
}
/* 写入单个块 */
HAL_StatusTypeDef SDNAND_WriteBlock(uint8_t *pData, uint32_t BlockAddr, uint32_t Timeout)
{
return HAL_SD_WriteBlocks(&hsd1, pData, BlockAddr, 1, Timeout);
}
/* 写入多个块 */
HAL_StatusTypeDef SDNAND_WriteBlocks(uint8_t *pData, uint32_t BlockAddr, uint32_t NumberOfBlocks, uint32_t Timeout)
{
return HAL_SD_WriteBlocks(&hsd1, pData, BlockAddr, NumberOfBlocks, Timeout);
}
可以在 SDNAND 驱动之上挂载文件系统,如 FatFS:
#include "ff.h"
#include "sd_nand.h"
FATFS SDNAND_FatFs; // 文件系统对象
FIL SDNAND_File; // 文件对象
/* 挂载文件系统 */
FRESULT SDNAND_MountFS(void)
{
FRESULT res;
// 挂载文件系统
res = f_mount(&SDNAND_FatFs, "", 1);
return res;
}
/* 创建文件并写入数据 */
FRESULT SDNAND_WriteFile(const TCHAR* path, const uint8_t* buff, UINT len, UINT* written)
{
FRESULT res;
// 打开文件
res = f_open(&SDNAND_File, path, FA_CREATE_ALWAYS | FA_WRITE);
if(res != FR_OK)
{
return res;
}
// 写入数据
res = f_write(&SDNAND_File, buff, len, written);
// 关闭文件
f_close(&SDNAND_File);
return res;
}
/* 读取文件内容 */
FRESULT SDNAND_ReadFile(const TCHAR* path, uint8_t* buff, UINT len, UINT* read)
{
FRESULT res;
// 打开文件
res = f_open(&SDNAND_File, path, FA_READ);
if(res != FR_OK)
{
return res;
}
// 读取数据
res = f_read(&SDNAND_File, buff, len, read);
// 关闭文件
f_close(&SDNAND_File);
return res;
}
SDNAND 特性:
时序要求:
电源考虑:
确保供电稳定,建议添加滤波电容
注意 SDNAND 的工作电压范围
通过以上方法,你可以在 STM32F407ZET6 上成功驱动 SDNAND。实际应用中,建议根据具体的 SDNAND 型号和应用需求进行适当调整。
热门标签:SD NAND FLASH 贴片式TF卡 贴片式SD卡 SD FLASH NAND FLASH