增加文件系统
This commit is contained in:
@@ -3,43 +3,57 @@
|
||||
|
||||
/*
|
||||
* 模块名称:FAL 配置
|
||||
* 模å<EFBFBD>—功能:定ä¹?FAL Flash 设备表和分区è¡? * 适用平å<C2B3>°ï¼šSTM32F407ZGT6
|
||||
* 作者:王建� * 创建日期�026-07-16
|
||||
* 修改记录ï¼? * 2026-07-16 王建é”? 创建åˆ<C3A5>始版本
|
||||
* 模块功能:定义 Flash 物理分区布局和 FAL 设备/分区表
|
||||
* 适用平台:STM32F407ZGT6
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-16
|
||||
* 修改记录:
|
||||
* 2026-07-20 王建锋 统一分区配置,添加 FTL 分区宏供 nand_ftl.c 使用
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* FAL 调试开关:0-å…³é—ï¼?-å¼€å<E282AC>?*/
|
||||
/* ================== 物理分区布局(256MB GD5F2GQ5UE) ================== */
|
||||
/*
|
||||
* 分区名 偏移 大小 块范围 用途
|
||||
* fdb_kvdb1 0 64MB Block 0~511 FlashDB KVDB
|
||||
* fdb_tsdb1 64MB 64MB Block 512~1023 FlashDB TSDB
|
||||
* ftl_fatfs 128MB 128MB Block 1024~2047 dhara FTL + FatFS
|
||||
*/
|
||||
#define FDB_KVDB1_OFFSET 0
|
||||
#define FDB_KVDB1_SIZE (64 * 1024 * 1024)
|
||||
#define FDB_TSDB1_OFFSET (FDB_KVDB1_OFFSET + FDB_KVDB1_SIZE)
|
||||
#define FDB_TSDB1_SIZE (64 * 1024 * 1024)
|
||||
#define FTL_FATFS_OFFSET (FDB_TSDB1_OFFSET + FDB_TSDB1_SIZE)
|
||||
#define FTL_FATFS_SIZE (128 * 1024 * 1024)
|
||||
|
||||
/* ====================== FAL 配置(需 FAL 头文件) ====================== */
|
||||
|
||||
#define FAL_DEBUG 0
|
||||
|
||||
/* å<EFBFBD>¯ç”¨åˆ†åŒºè¡¨é…<EFBFBD>ç½?*/
|
||||
/* 启用分区表配置(FAL 库要求此宏) */
|
||||
#define FAL_PART_HAS_TABLE_CFG
|
||||
|
||||
/* ======================== Flash 设备�======================== */
|
||||
/* ======================== Flash 设备表 ======================== */
|
||||
|
||||
extern const struct fal_flash_dev g_gd5f2gq5ue_flash;
|
||||
|
||||
#define FAL_FLASH_DEV_TABLE \
|
||||
{ \
|
||||
&g_gd5f2gq5ue_flash, \
|
||||
&g_gd5f2gq5ue_flash, \
|
||||
}
|
||||
|
||||
/* ======================== 分区�======================== */
|
||||
/*
|
||||
* 分区å<C2BA><C3A5>ç§° 设备å<E280A1><C3A5>ç§° å<><C3A5>ç§» 大å°<C3A5>
|
||||
* KVDB: 64MB @ 0
|
||||
* TSDB: 64MB @ 64MB
|
||||
*/
|
||||
/* ======================== 分区表 ======================== */
|
||||
|
||||
#ifdef FAL_PART_HAS_TABLE_CFG
|
||||
#define FAL_PART_TABLE \
|
||||
{ \
|
||||
{FAL_PART_MAGIC_WORD, "fdb_kvdb1", "gd5f2gq5ue", 0, 64*1024*1024, 0}, \
|
||||
{FAL_PART_MAGIC_WORD, "fdb_tsdb1", "gd5f2gq5ue", 64*1024*1024, 64*1024*1024, 0}, \
|
||||
#define FAL_PART_TABLE \
|
||||
{ \
|
||||
{FAL_PART_MAGIC_WORD, "fdb_kvdb1", "gd5f2gq5ue", FDB_KVDB1_OFFSET, FDB_KVDB1_SIZE, 0}, \
|
||||
{FAL_PART_MAGIC_WORD, "fdb_tsdb1", "gd5f2gq5ue", FDB_TSDB1_OFFSET, FDB_TSDB1_SIZE, 0}, \
|
||||
}
|
||||
#endif
|
||||
#endif /* FAL_PART_HAS_TABLE_CFG */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* 模块名称:GD5F2GQ5UE SPI NAND Flash 驱动
|
||||
* 妯″潡鍔熻兘锛氭彁锟?GD5F2GQ5UE SPI NAND Flash 鐨勫垵濮嬪寲銆佽<EFBFBD>銆佸啓銆佹摝闄ゆ帴锟? * 閫傜敤骞冲彴锛歋TM32F407ZGT6 + SPI1 纭<EFBFBD>欢 SPI
|
||||
* 浣滆€咃細鐜嬪缓锟? * 鍒涘缓鏃ユ湡锟?026-07-16
|
||||
* 淇<EFBFBD>敼璁板綍锟? * 2026-07-16 鐜嬪缓锟? 鍒涘缓鍒濆<E98D92>鐗堟湰
|
||||
* 2026-07-17 鐜嬪缓锟? 鍒囨崲纭<E5B4B2>欢 SPI锛屼慨姝f摝闄ゅ湴鍧€锛屽弬锟?NuttX 椹卞姩
|
||||
* 模块功能:提<EFBFBD>?GD5F2GQ5UE SPI NAND Flash 的初始化、读、写、擦除接<EFBFBD>? * 适用平台:STM32F407ZGT6 + SPI1 硬件 SPI
|
||||
* 作者:王建<EFBFBD>? * 创建日期<E697A5>?026-07-16
|
||||
* 修改记录<EFBFBD>? * 2026-07-16 王建<EFBFBD>? 创建初始版本
|
||||
* 2026-07-17 王建<EFBFBD>? 切换硬件 SPI,修正擦除地址,参<EFBC8C>?NuttX 驱动
|
||||
*/
|
||||
|
||||
/* 头文件包含区 */
|
||||
@@ -17,34 +17,58 @@
|
||||
#include "dbg_log.h"
|
||||
|
||||
/* 私有宏定义区 */
|
||||
#define GD5F_SPI_TIMEOUT 100
|
||||
#define GD5F_SPI_TIMEOUT 100
|
||||
#define GD5F_BBT_SIZE (GD5F_TOTAL_BLOCKS / 8)
|
||||
|
||||
/* BBT 静态数组(1 bit 表示一个块,0=好块 1=坏块) */
|
||||
static uint8_t s_bbt[GD5F_BBT_SIZE];
|
||||
|
||||
/* 外部 SPI 句柄声明 */
|
||||
extern SPI_HandleTypeDef hspi1;
|
||||
|
||||
/* DMA 完成标志(ch395f.c 中的 HAL_SPI_TxRxCpltCallback 会设置此标志) */
|
||||
volatile uint8_t g_spi1_dma_done;
|
||||
|
||||
/* DMA 阈值(字节数超过此值使用 DMA 传输) */
|
||||
#define GD5F_DMA_THRESHOLD 32
|
||||
|
||||
/*
|
||||
* 函数功能:SPI1 TX DMA 完成回调(PROGRAM_LOAD 数据发送用)
|
||||
*/
|
||||
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
|
||||
if (hspi->Instance == SPI1) {
|
||||
g_spi1_dma_done = 1U;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:SPI1 RX DMA 完成回调(READ_FROM_CACHE 数据接收用)
|
||||
* 函数说明:HAL_SPI_Receive_DMA() 在 2 线 master 模式下
|
||||
* 内部走 HAL_SPI_TransmitReceive_DMA 但 state=BUSY_RX,
|
||||
* 完成回调是 HAL_SPI_RxCpltCallback 而非 TxRxCpltCallback
|
||||
*/
|
||||
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
|
||||
if (hspi->Instance == SPI1) {
|
||||
g_spi1_dma_done = 1U;
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================== 私有函数声明 ======================== */
|
||||
|
||||
static int gd5f_wait_busy(uint32_t timeout_ms);
|
||||
static int gd5f_write_enable(void);
|
||||
static int gd5f_read_status(uint8_t *p_status);
|
||||
static int gd5f_page_read(uint32_t page_addr);
|
||||
static int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
|
||||
size_t size);
|
||||
static int gd5f_page_program(uint32_t page_addr, uint16_t column,
|
||||
const uint8_t *p_buf, size_t size);
|
||||
static int gd5f_set_feature(uint8_t addr, uint8_t data);
|
||||
static int gd5f_block_erase(uint32_t block_addr);
|
||||
|
||||
/* ======================== 绉佹湁鍑芥暟瀹氫箟 ======================== */
|
||||
/* ======================== SPI 原语定义 ======================== */
|
||||
|
||||
/*
|
||||
* 函数功能:等待芯片操作完成(轮询 OIP 位)
|
||||
* 入口参数:timeout_ms - 超时时间 uint32_t > 0
|
||||
* 杩斿洖鍊硷細0 - 鎿嶄綔瀹屾垚锟?2 - 瓒呮椂
|
||||
* 返回值:0 - 操作完成,-2 - 超时
|
||||
* 限定条件:SPI 已初始化
|
||||
* 函数说明:循环读取状态寄存器直到 OIP 位清零或超时
|
||||
*/
|
||||
static int gd5f_wait_busy(uint32_t timeout_ms) {
|
||||
int gd5f_wait_busy(uint32_t timeout_ms) {
|
||||
uint8_t cmd = GD5F_CMD_GET_FEATURE;
|
||||
uint8_t addr = GD5F_REG_STATUS;
|
||||
uint8_t status = 0;
|
||||
@@ -71,8 +95,8 @@ static int gd5f_wait_busy(uint32_t timeout_ms) {
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功
|
||||
* 限定条件:SPI 已初始化
|
||||
* 鍑芥暟璇存槑锛欳S# 鎷変綆鍚庡彂锟?06h 鍛戒护鍐嶆媺锟? */
|
||||
static int gd5f_write_enable(void) {
|
||||
* 函数说明:CS# 拉低后发<E5908E>?06h 命令再拉<E5868D>? */
|
||||
int gd5f_write_enable(void) {
|
||||
uint8_t cmd = GD5F_CMD_WRITE_ENABLE;
|
||||
|
||||
GD5F_CS_LOW();
|
||||
@@ -84,11 +108,11 @@ static int gd5f_write_enable(void) {
|
||||
|
||||
/*
|
||||
* 函数功能:读取状态寄存器
|
||||
* 鍏ュ彛鍙傛暟锛歱_status - 鐘舵€佸€艰緭鍑烘寚锟? uint8_t* 涓嶄负 NULL
|
||||
* 入口参数:p_status - 状态值输出指<EFBFBD>? uint8_t* 不为 NULL
|
||||
* 返回值:0 - 成功
|
||||
* 限定条件:SPI 已初始化
|
||||
* 鍑芥暟璇存槑锛氬彂锟?0Fh + C0h 鍦板潃鍚庤<EFBFBD>锟?瀛楄妭鐘舵€侊拷? */
|
||||
static int gd5f_read_status(uint8_t *p_status) {
|
||||
* 函数说明:发<EFBFBD>?0Fh + C0h 地址后读<EFBFBD>?字节状态<E78AB6>? */
|
||||
int gd5f_read_status(uint8_t *p_status) {
|
||||
uint8_t cmd = GD5F_CMD_GET_FEATURE;
|
||||
uint8_t addr = GD5F_REG_STATUS;
|
||||
|
||||
@@ -102,11 +126,11 @@ static int gd5f_read_status(uint8_t *p_status) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 鍑芥暟鍔熻兘锛氶〉璇诲彇锛堝皢鏁版嵁浠庡瓨鍌ㄩ樀鍒楀姞杞藉埌鍐呴儴缂撳瓨锟? * 鍏ュ彛鍙傛暟锛歱age_addr - 椤靛湴鍧€ uint32_t
|
||||
* 杩斿洖鍊硷細0 - 鎴愬姛锛屽叾锟?- 閿欒<E996BF>锟? * 闄愬畾鏉′欢锛歋PI 宸插垵濮嬪寲
|
||||
* 鍑芥暟璇存槑锛氬彂锟?13h + 3瀛楄妭琛屽湴鍧€锛岀瓑锟?OIP 娓呴浂
|
||||
* 函数功能:页读取(将数据从存储阵列加载到内部缓存<EFBFBD>? * 入口参数:page_addr - 页地址 uint32_t
|
||||
* 返回值:0 - 成功,其<EFBC8C>?- 错误<E99499>? * 限定条件:SPI 已初始化
|
||||
* 函数说明:发<EFBFBD>?13h + 3字节行地址,等<EFBC8C>?OIP 清零
|
||||
*/
|
||||
static int gd5f_page_read(uint32_t page_addr) {
|
||||
int gd5f_page_read(uint32_t page_addr) {
|
||||
uint8_t cmd[4] = {0};
|
||||
|
||||
cmd[0] = GD5F_CMD_PAGE_READ;
|
||||
@@ -124,12 +148,12 @@ static int gd5f_page_read(uint32_t page_addr) {
|
||||
/*
|
||||
* 函数功能:从内部缓存读取数据
|
||||
* 入口参数:column - 列地址(页内偏移) uint16_t 0 - 2047
|
||||
* p_buf - 鏁版嵁杈撳嚭缂撳啿锟? uint8_t* 涓嶄负 NULL
|
||||
* size - 璇诲彇瀛楄妭锟? size_t > 0
|
||||
* p_buf - 数据输出缓冲<EFBFBD>? uint8_t* 不为 NULL
|
||||
* size - 读取字节<EFBFBD>? size_t > 0
|
||||
* 返回值:0 - 成功
|
||||
* 限定条件:必须先调用 gd5f_page_read 完成数据加载
|
||||
* 鍑芥暟璇存槑锛氬彂锟?0Bh + 2瀛楄妭鍒楀湴鍧€ +1瀛楄妭 dummy 鍚庤<E98D9A>鍙栨暟锟? */
|
||||
static int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
|
||||
* 函数说明:发<EFBFBD>?0Bh + 2字节列地址 +1字节 dummy 后读取数<E58F96>? */
|
||||
int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
|
||||
size_t size) {
|
||||
uint8_t cmd[4] = {0};
|
||||
|
||||
@@ -140,7 +164,24 @@ static int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
|
||||
|
||||
GD5F_CS_LOW();
|
||||
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
|
||||
HAL_SPI_Receive(&hspi1, p_buf, size, GD5F_SPI_TIMEOUT);
|
||||
|
||||
if (size > GD5F_DMA_THRESHOLD) {
|
||||
uint32_t tick_start = HAL_GetTick();
|
||||
g_spi1_dma_done = 0U;
|
||||
if (HAL_SPI_Receive_DMA(&hspi1, p_buf, (uint16_t)size) != HAL_OK) {
|
||||
GD5F_CS_HIGH();
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
while (g_spi1_dma_done == 0U) {
|
||||
if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) {
|
||||
HAL_SPI_DMAStop(&hspi1);
|
||||
GD5F_CS_HIGH();
|
||||
return GD5F_BUSY_TIMEOUT;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
HAL_SPI_Receive(&hspi1, p_buf, size, GD5F_SPI_TIMEOUT);
|
||||
}
|
||||
GD5F_CS_HIGH();
|
||||
|
||||
return GD5F_OK;
|
||||
@@ -150,29 +191,57 @@ static int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
|
||||
* 函数功能:页编程(将数据写入指定页)
|
||||
* 入口参数:page_addr - 页地址 uint32_t
|
||||
* column - 列地址 uint16_t 0 - 2047
|
||||
* p_buf - 鏁版嵁缂撳啿锟? const uint8_t*
|
||||
* size - 鍐欏叆瀛楄妭锟? size_t > 0
|
||||
* 杩斿洖鍊硷細0 - 鎴愬姛锛屽叾锟?- 閿欒<E996BF>锟? * 闄愬畾鏉′欢锛氱洰鏍囧尯鍩熷凡鎿﹂櫎
|
||||
* 鍑芥暟璇存槑锟?. 鍐欎娇锟?- 02h 鍔犺浇鏁版嵁 - 10h 鎵ц<EFBFBD>缂栫▼ - 绛夊緟瀹屾垚
|
||||
* 2. 缂栫▼瀹屾垚鍚庢<EFBFBD>锟?P_FAIL 锟? */
|
||||
* p_buf - 数据缓冲<EFBFBD>? const uint8_t*
|
||||
* size - 写入字节<EFBFBD>? size_t > 0
|
||||
* 返回值:0 - 成功,其<EFBC8C>?- 错误<E99499>? * 限定条件:目标区域已擦除
|
||||
* 函数说明<EFBFBD>?. 写使<E58699>?- 02h 加载数据 - 10h 执行编程 - 等待完成
|
||||
* 2. 编程完成后检<EFBFBD>?P_FAIL <EFBFBD>? */
|
||||
static int gd5f_page_program(uint32_t page_addr, uint16_t column,
|
||||
const uint8_t *p_buf, size_t size) {
|
||||
int ret = GD5F_OK;
|
||||
uint8_t cmd[4];
|
||||
uint8_t status = 0;
|
||||
|
||||
int ret;
|
||||
gd5f_write_enable();
|
||||
ret = gd5f_program_load(column, p_buf, size);
|
||||
if (ret != GD5F_OK) return ret;
|
||||
return gd5f_program_exec(page_addr);
|
||||
}
|
||||
|
||||
int gd5f_program_load(uint16_t column, const uint8_t *p_buf, size_t size) {
|
||||
uint8_t cmd[3];
|
||||
cmd[0] = GD5F_CMD_PROGRAM_LOAD;
|
||||
cmd[1] = (column >> 8) & 0xFF;
|
||||
cmd[2] = column & 0xFF;
|
||||
cmd[1] = (uint8_t)(column >> 8);
|
||||
cmd[2] = (uint8_t)(column);
|
||||
|
||||
GD5F_CS_LOW();
|
||||
HAL_SPI_Transmit(&hspi1, cmd, 3, GD5F_SPI_TIMEOUT);
|
||||
HAL_SPI_Transmit(&hspi1, (uint8_t *)p_buf, size,
|
||||
GD5F_SPI_TIMEOUT);
|
||||
|
||||
if (size > GD5F_DMA_THRESHOLD) {
|
||||
uint32_t tick_start = HAL_GetTick();
|
||||
g_spi1_dma_done = 0U;
|
||||
if (HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)p_buf, (uint16_t)size)
|
||||
!= HAL_OK) {
|
||||
GD5F_CS_HIGH();
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
while (g_spi1_dma_done == 0U) {
|
||||
if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) {
|
||||
HAL_SPI_DMAStop(&hspi1);
|
||||
GD5F_CS_HIGH();
|
||||
return GD5F_BUSY_TIMEOUT;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
HAL_SPI_Transmit(&hspi1, (uint8_t *)p_buf, size, GD5F_SPI_TIMEOUT);
|
||||
}
|
||||
GD5F_CS_HIGH();
|
||||
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
int gd5f_program_exec(uint32_t page_addr) {
|
||||
int ret;
|
||||
uint8_t cmd[4];
|
||||
uint8_t status;
|
||||
|
||||
cmd[0] = GD5F_CMD_PROGRAM_EXEC;
|
||||
cmd[1] = (page_addr >> 16) & 0xFF;
|
||||
cmd[2] = (page_addr >> 8) & 0xFF;
|
||||
@@ -183,24 +252,28 @@ static int gd5f_page_program(uint32_t page_addr, uint16_t column,
|
||||
GD5F_CS_HIGH();
|
||||
|
||||
ret = gd5f_wait_busy(1000);
|
||||
if (ret != GD5F_OK) {
|
||||
return ret;
|
||||
}
|
||||
if (ret != GD5F_OK) return ret;
|
||||
|
||||
gd5f_read_status(&status);
|
||||
if (status & GD5F_STATUS_P_FAIL) {
|
||||
return GD5F_PROGRAM_FAIL;
|
||||
}
|
||||
if (status & GD5F_STATUS_P_FAIL) return GD5F_PROGRAM_FAIL;
|
||||
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
int gd5f_check_ecc(void) {
|
||||
uint8_t status;
|
||||
gd5f_read_status(&status);
|
||||
uint8_t ecc = (status >> 4) & 0x03;
|
||||
if (ecc == 0x02) return GD5F_ECC_ERROR;
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 鍑芥暟鍔熻兘锛氳<EFBFBD>锟?Feature 瀵勫瓨锟? * 鍏ュ彛鍙傛暟锛歛ddr - 瀵勫瓨鍣ㄥ湴鍧€ uint8_t
|
||||
* 函数功能:设<EFBFBD>?Feature 寄存<E5AF84>? * 入口参数:addr - 寄存器地址 uint8_t
|
||||
* data - 写入数据 uint8_t
|
||||
* 返回值:0 - 成功
|
||||
* 限定条件:SPI 已初始化
|
||||
* 鍑芥暟璇存槑锛氬厛鍐欎娇鑳斤紝鍙戯拷?1Fh + 鍦板潃 + 鏁版嵁锛岀瓑寰呮搷浣滃畬锟? */
|
||||
* 函数说明:先写使能,发<EFBFBD>?1Fh + 地址 + 数据,等待操作完<E4BD9C>? */
|
||||
static int gd5f_set_feature(uint8_t addr, uint8_t data) {
|
||||
uint8_t cmd[3];
|
||||
|
||||
@@ -220,11 +293,11 @@ static int gd5f_set_feature(uint8_t addr, uint8_t data) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 鍑芥暟鍔熻兘锛氬潡鎿﹂櫎锛堟摝锟?28KB鍧楋級
|
||||
* 鍏ュ彛鍙傛暟锛歜lock_addr - 鍧楃紪锟? uint32_t 0 - 2047
|
||||
* 杩斿洖鍊硷細0 - 鎴愬姛锛屽叾锟?- 閿欒<E996BF>锟? * 闄愬畾鏉′欢锛歡d5f2gq5ue_init() 宸茶皟锟? * 鍑芥暟璇存槑锟?. 鍐欎娇锟?- D8h + 3瀛楄妭瀛楄妭鍦板潃 - 绛夊緟瀹屾垚
|
||||
* 2. 鎿﹂櫎瀹屾垚鍚庢<EFBFBD>锟?E_FAIL 锟? */
|
||||
static int gd5f_block_erase(uint32_t block_addr) {
|
||||
* 函数功能:块擦除(擦<EFBFBD>?28KB块)
|
||||
* 入口参数:block_addr - 块编<EFBFBD>? uint32_t 0 - 2047
|
||||
* 返回值:0 - 成功,其<EFBC8C>?- 错误<E99499>? * 限定条件:gd5f2gq5ue_init() 已调<E5B7B2>? * 函数说明<E8AFB4>?. 写使<E58699>?- D8h + 3字节字节地址 - 等待完成
|
||||
* 2. 擦除完成后检<EFBFBD>?E_FAIL <EFBFBD>? */
|
||||
int gd5f_block_erase(uint32_t block_addr) {
|
||||
int ret = GD5F_OK;
|
||||
uint8_t cmd[4];
|
||||
uint8_t status = 0;
|
||||
@@ -254,14 +327,49 @@ static int gd5f_block_erase(uint32_t block_addr) {
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:扫描所有块构建 BBT(在 ECC 使能前调用)
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功
|
||||
* 限定条件:SPI 已初始化,ECC 尚未使能
|
||||
* 函数说明:读取每个块最后一页(page 63)的 spare byte 0,
|
||||
* 若不为 0xFF 则为出厂坏块
|
||||
*/
|
||||
static int gd5f_bbt_scan(void) {
|
||||
uint32_t block;
|
||||
uint8_t spare[8];
|
||||
uint32_t bad_count = 0;
|
||||
|
||||
memset(s_bbt, 0, GD5F_BBT_SIZE);
|
||||
|
||||
for (block = 0; block < GD5F_TOTAL_BLOCKS; block++) {
|
||||
uint32_t page = (block << 6) | 63;
|
||||
|
||||
gd5f_page_read(page);
|
||||
gd5f_read_from_cache(GD5F_PAGE_SIZE, spare, 1);
|
||||
|
||||
if (spare[0] != 0xFF) {
|
||||
s_bbt[block >> 3] |= (1 << (block & 7));
|
||||
bad_count++;
|
||||
}
|
||||
}
|
||||
|
||||
DBG_INFO("BBT scan: %lu bad blocks found", bad_count);
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/* ======================== 公共函数定义 ======================== */
|
||||
|
||||
/*
|
||||
* 函数功能:初始化 GD5F2GQ5UE
|
||||
* 入口参数:无
|
||||
* 杩斿洖鍊硷細0 - 鎴愬姛锛屽叾锟?- 閿欒<E996BF>锟? * 闄愬畾鏉′欢锛歋PI1 鍜岀浉锟?GPIO 宸茬敱 CubeMX 鍒濆<E98D92>鍖栧畬锟? * 鍑芥暟璇存槑锟?. 鍙戦€佸<E282AC>浣嶅懡浠ゅ苟绛夊緟瀹屾垚
|
||||
* 2. 璇诲彇鑺<E5BD87>墖 ID 骞舵牎锟? * 3. 浣胯兘鍐呴儴 ECC (B0h bit4)
|
||||
* 4. 瑙i櫎鎵€鏈夊潡淇濇姢 (A0h = 0x00)
|
||||
* 返回值:0 - 成功,其他 - 错误码
|
||||
* 限定条件:SPI1 和相关 GPIO 已由 CubeMX 初始化完成
|
||||
* 函数说明:1. 发送复位命令并等待完成
|
||||
* 2. 读取芯片 ID 并校验
|
||||
* 3. 扫描出厂坏块构建 BBT(ECC 使能前)
|
||||
* 4. 使能内部 ECC (B0h bit4)
|
||||
* 5. 解除所有块保护 (A0h = 0x00)
|
||||
*/
|
||||
int gd5f2gq5ue_init(void) {
|
||||
int ret = GD5F_OK;
|
||||
@@ -298,6 +406,9 @@ int gd5f2gq5ue_init(void) {
|
||||
return GD5F_ID_MISMATCH;
|
||||
}
|
||||
|
||||
DBG_INFO("Scanning bad blocks...");
|
||||
gd5f_bbt_scan();
|
||||
|
||||
DBG_INFO("Enabling ECC...");
|
||||
gd5f_set_feature(0xB0, 0x10);
|
||||
gd5f_wait_busy(100);
|
||||
@@ -311,11 +422,11 @@ int gd5f2gq5ue_init(void) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 鍑芥暟鍔熻兘锛氳<EFBFBD>鍙栬姱锟?ID锛圡ID + DID锟? * 鍏ュ彛鍙傛暟锛歮id - 鍒堕€犲晢 ID 杈撳嚭鎸囬拡 uint8_t* 涓嶄负 NULL
|
||||
* 函数功能:读取芯<EFBFBD>?ID(MID + DID<EFBFBD>? * 入口参数:mid - 制造商 ID 输出指针 uint8_t* 不为 NULL
|
||||
* did - 设备 ID 输出指针 uint8_t* 不为 NULL
|
||||
* 返回值:0 - 成功
|
||||
* 限定条件:SPI 已初始化
|
||||
* 鍑芥暟璇存槑锛氬彂锟?9Fh 鍛戒护鍚庢帴锟?锟?dummy + MID + DID
|
||||
* 函数说明:发<EFBFBD>?9Fh 命令后接<E5908E>?<3F>?dummy + MID + DID
|
||||
*/
|
||||
int gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did) {
|
||||
uint8_t cmd = GD5F_CMD_READ_ID;
|
||||
@@ -334,10 +445,10 @@ int gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did) {
|
||||
|
||||
/*
|
||||
* 函数功能:从 NAND 读取数据(支持跨页)
|
||||
* 鍏ュ彛鍙傛暟锛歰ffset - 璧峰<E792A7>瀛楄妭鍋忕Щ long 0 ~ 鎬诲<EFBFBD>锟?1
|
||||
* p_buf - 鏁版嵁缂撳啿锟? uint8_t* 涓嶄负 NULL
|
||||
* size - 璇诲彇瀛楄妭锟? size_t > 0
|
||||
* 杩斿洖鍊硷細0 - 鎴愬姛锛屽叾锟?- 閿欒<E996BF>锟? * 闄愬畾鏉′欢锛歡d5f2gq5ue_init() 宸叉垚鍔熻皟锟? * 鍑芥暟璇存槑锛氳嚜鍔ㄥ<E98D94>鐞嗚法椤佃<E6A4A4>锟? */
|
||||
* 入口参数:offset - 起始字节偏移 long 0 ~ 总容<EFBFBD>?1
|
||||
* p_buf - 数据缓冲<EFBFBD>? uint8_t* 不为 NULL
|
||||
* size - 读取字节<EFBFBD>? size_t > 0
|
||||
* 返回值:0 - 成功,其<EFBC8C>?- 错误<E99499>? * 限定条件:gd5f2gq5ue_init() 已成功调<E58A9F>? * 函数说明:自动处理跨页读<E9A1B5>? */
|
||||
int gd5f2gq5ue_read(long offset, uint8_t *p_buf, size_t size) {
|
||||
int ret = GD5F_OK;
|
||||
|
||||
@@ -370,10 +481,10 @@ int gd5f2gq5ue_read(long offset, uint8_t *p_buf, size_t size) {
|
||||
|
||||
/*
|
||||
* 函数功能:向 NAND 写入数据(支持跨页)
|
||||
* 鍏ュ彛鍙傛暟锛歰ffset - 璧峰<E792A7>瀛楄妭鍋忕Щ long 0 ~ 鎬诲<EFBFBD>锟?1
|
||||
* p_buf - 鏁版嵁缂撳啿锟? const uint8_t* 涓嶄负 NULL
|
||||
* size - 鍐欏叆瀛楄妭锟? size_t > 0
|
||||
* 杩斿洖鍊硷細0 - 鎴愬姛锛屽叾锟?- 閿欒<E996BF>锟? * 闄愬畾鏉′欢锛歡d5f2gq5ue_init() 宸叉垚鍔熻皟鐢<E79A9F>紝鐩<E7B49D>爣鍖哄煙宸叉摝锟? * 鍑芥暟璇存槑锛氳嚜鍔ㄥ<E98D94>鐞嗚法椤靛啓锟? */
|
||||
* 入口参数:offset - 起始字节偏移 long 0 ~ 总容<EFBFBD>?1
|
||||
* p_buf - 数据缓冲<EFBFBD>? const uint8_t* 不为 NULL
|
||||
* size - 写入字节<EFBFBD>? size_t > 0
|
||||
* 返回值:0 - 成功,其<EFBC8C>?- 错误<E99499>? * 限定条件:gd5f2gq5ue_init() 已成功调用,目标区域已擦<E5B7B2>? * 函数说明:自动处理跨页写<E9A1B5>? */
|
||||
int gd5f2gq5ue_write(long offset, const uint8_t *p_buf, size_t size) {
|
||||
int ret = GD5F_OK;
|
||||
|
||||
@@ -400,7 +511,7 @@ int gd5f2gq5ue_write(long offset, const uint8_t *p_buf, size_t size) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 鍑芥暟鍔熻兘锛氭摝闄ゅ潡锛堟寜鍧楁摝闄わ紝鏈€灏忓崟锟?128KB锟? * 鍏ュ彛鍙傛暟锛歰ffset - 璧峰<E792A7>瀛楄妭鍋忕Щ long 蹇呴』鍧楀<EFBFBD>锟? * size - 鎿﹂櫎瀛楄妭锟? size_t 蹇呴』鍧楀ぇ灏忔暣鏁帮拷? * 杩斿洖鍊硷細0 - 鎴愬姛锛屽叾锟?- 閿欒<E996BF>锟? * 闄愬畾鏉′欢锛歡d5f2gq5ue_init() 宸叉垚鍔熻皟锟? * 鍑芥暟璇存槑锛氭摝闄ゆ搷浣滀互鍧椾负鍗曚綅
|
||||
* 函数功能:擦除块(按块擦除,最小单<EFBFBD>?128KB<EFBFBD>? * 入口参数:offset - 起始字节偏移 long 必须块对<EFBFBD>? * size - 擦除字节<EFBFBD>? size_t 必须块大小整数<EFBFBD>? * 返回值:0 - 成功,其<EFBC8C>?- 错误<E99499>? * 限定条件:gd5f2gq5ue_init() 已成功调<E58A9F>? * 函数说明:擦除操作以块为单位
|
||||
*/
|
||||
int gd5f2gq5ue_erase(long offset, size_t size) {
|
||||
int ret = GD5F_OK;
|
||||
@@ -428,10 +539,10 @@ int gd5f2gq5ue_erase(long offset, size_t size) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 鍑芥暟鍔熻兘锛氬<EFBFBD>浣嶈姱锟? * 鍏ュ彛鍙傛暟锛氭棤
|
||||
* 函数功能:复位芯<EFBFBD>? * 入口参数:无
|
||||
* 返回值:0 - 成功
|
||||
* 限定条件:SPI 已初始化
|
||||
* 鍑芥暟璇存槑锛氬彂锟?FFh 澶嶄綅鍛戒护鍚庣瓑锟?5ms
|
||||
* 函数说明:发<EFBFBD>?FFh 复位命令后等<E5908E>?5ms
|
||||
*/
|
||||
int gd5f2gq5ue_reset(void) {
|
||||
uint8_t cmd = GD5F_CMD_RESET;
|
||||
@@ -445,3 +556,31 @@ int gd5f2gq5ue_reset(void) {
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:查询块是否坏块
|
||||
* 入口参数:block - 块编号 uint32_t 0 ~ GD5F_TOTAL_BLOCKS-1
|
||||
* 返回值:0 - 好块,1 - 坏块
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:通过 BBT 查询
|
||||
*/
|
||||
int gd5f2gq5ue_is_block_bad(uint32_t block) {
|
||||
if (block >= GD5F_TOTAL_BLOCKS) {
|
||||
return 1;
|
||||
}
|
||||
return (s_bbt[block >> 3] >> (block & 7)) & 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:标记块为坏块
|
||||
* 入口参数:block - 块编号 uint32_t 0 ~ GD5F_TOTAL_BLOCKS-1
|
||||
* 返回值:无
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:更新 BBT
|
||||
*/
|
||||
void gd5f2gq5ue_mark_block_bad(uint32_t block) {
|
||||
if (block >= GD5F_TOTAL_BLOCKS) {
|
||||
return;
|
||||
}
|
||||
s_bbt[block >> 3] |= (1 << (block & 7));
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,36 @@ int gd5f2gq5ue_erase(long offset, size_t size);
|
||||
*/
|
||||
int gd5f2gq5ue_reset(void);
|
||||
|
||||
/*
|
||||
* 函数功能:查询块是否坏块
|
||||
* 入口参数:block - 块编号 uint32_t 0 ~ GD5F_TOTAL_BLOCKS-1
|
||||
* 返回值:0 - 好块,1 - 坏块
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:通过 BBT(Bad Block Table)查询,BBT 在 init 阶段 ECC 使能前扫描构建
|
||||
*/
|
||||
int gd5f2gq5ue_is_block_bad(uint32_t block);
|
||||
|
||||
/*
|
||||
* 函数功能:标记块为坏块
|
||||
* 入口参数:block - 块编号 uint32_t 0 ~ GD5F_TOTAL_BLOCKS-1
|
||||
* 返回值:无
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:更新 BBT,并尝试物理标记该块最后一页 spare byte 0 为 0x00
|
||||
*/
|
||||
void gd5f2gq5ue_mark_block_bad(uint32_t block);
|
||||
|
||||
/* ==================== SPI 原语(FTL 共享) ==================== */
|
||||
|
||||
int gd5f_wait_busy(uint32_t timeout_ms);
|
||||
int gd5f_write_enable(void);
|
||||
int gd5f_read_status(uint8_t *p_status);
|
||||
int gd5f_page_read(uint32_t page_addr);
|
||||
int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf, size_t size);
|
||||
int gd5f_program_load(uint16_t column, const uint8_t *p_buf, size_t size);
|
||||
int gd5f_program_exec(uint32_t page_addr);
|
||||
int gd5f_block_erase(uint32_t block_addr);
|
||||
int gd5f_check_ecc(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
386
Drivers/BSP/GD5F2GQ5UE/nand_ftl.c
Normal file
386
Drivers/BSP/GD5F2GQ5UE/nand_ftl.c
Normal file
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* 模块名称:NAND FTL 适配层(dhara + FatFS diskio 胶水)
|
||||
* 模块功能:实现 dhara NAND HAL 和 FatFS 磁盘 I/O,连接 GD5F2GQ5UE 底层驱动
|
||||
* 适用平台:STM32F407ZGT6
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-20
|
||||
* 修改记录:
|
||||
* 2026-07-20 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
/* 头文件包含区 */
|
||||
#include <string.h>
|
||||
#include "gd5f2gq5ue.h"
|
||||
#include "ff.h"
|
||||
#include "diskio.h"
|
||||
#include "nand.h"
|
||||
#include "map.h"
|
||||
|
||||
/* 统一分区配置 */
|
||||
#include "fal_cfg.h"
|
||||
|
||||
/* 调试输出配置 */
|
||||
#define DBG_TAG "[NAND_FTL]"
|
||||
#include "dbg_log.h"
|
||||
|
||||
/* 私有宏定义区 */
|
||||
#define FTL_PAGE_SIZE GD5F_PAGE_SIZE
|
||||
#define FTL_SECTORS_PER_PAGE (FTL_PAGE_SIZE / 512)
|
||||
#define FTL_START_BLOCK (FTL_FATFS_OFFSET / GD5F_BLOCK_SIZE)
|
||||
#define FTL_NUM_BLOCKS (GD5F_TOTAL_BLOCKS - FTL_START_BLOCK)
|
||||
#define FTL_START_PAGE (FTL_START_BLOCK * GD5F_PAGES_PER_BLOCK)
|
||||
|
||||
/* dhara 逻辑地址到物理地址转换 */
|
||||
static inline uint32_t ftl_phy_block(dhara_block_t b) { return b + FTL_START_BLOCK; }
|
||||
static inline uint32_t ftl_phy_page(dhara_page_t p) { return p + FTL_START_PAGE; }
|
||||
|
||||
/* ======================== 私有变量 ======================== */
|
||||
|
||||
/* dhara 核心数据结构 */
|
||||
static struct dhara_nand s_nand;
|
||||
static struct dhara_map s_map;
|
||||
static uint8_t s_page_buf[FTL_PAGE_SIZE]; /* dhara 内部页缓冲区 */
|
||||
|
||||
/* 页面缓存(FatFS 读写缓存) */
|
||||
static uint8_t s_cache_buf[FTL_PAGE_SIZE];
|
||||
static dhara_sector_t s_cached_lpn;
|
||||
static uint8_t s_cache_dirty;
|
||||
|
||||
/* 拷贝临时缓冲区 */
|
||||
static uint8_t s_copy_buf[FTL_PAGE_SIZE];
|
||||
|
||||
/* 初始化状态 */
|
||||
static uint8_t s_initialized;
|
||||
|
||||
/* ======================== 私有函数声明 ======================== */
|
||||
|
||||
static int ftl_flush_cache(void);
|
||||
static int ftl_read_page(dhara_sector_t lpn);
|
||||
|
||||
/* SPI 原语从 gd5f2gq5ue.h 获取 */
|
||||
|
||||
/* ======================== 页面缓存管理 ======================== */
|
||||
|
||||
static int ftl_flush_cache(void) {
|
||||
if (!s_cache_dirty) {
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
dhara_error_t err;
|
||||
if (dhara_map_write(&s_map, s_cached_lpn, s_cache_buf, &err) < 0) {
|
||||
DBG_ERROR("Cache flush failed: LPN=%lu err=%d", s_cached_lpn, err);
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
|
||||
s_cache_dirty = 0;
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
static int ftl_read_page(dhara_sector_t lpn) {
|
||||
dhara_error_t err;
|
||||
|
||||
if (dhara_map_read(&s_map, lpn, s_cache_buf, &err) < 0) {
|
||||
DBG_ERROR("Page read failed: LPN=%lu err=%d", lpn, err);
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
|
||||
s_cached_lpn = lpn;
|
||||
s_cache_dirty = 0;
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/* ======================== dhara NAND HAL ======================== */
|
||||
|
||||
int dhara_nand_is_bad(const struct dhara_nand *n, dhara_block_t b) {
|
||||
(void)n;
|
||||
return gd5f2gq5ue_is_block_bad(ftl_phy_block(b));
|
||||
}
|
||||
|
||||
void dhara_nand_mark_bad(const struct dhara_nand *n, dhara_block_t b) {
|
||||
(void)n;
|
||||
gd5f2gq5ue_mark_block_bad(ftl_phy_block(b));
|
||||
}
|
||||
|
||||
int dhara_nand_erase(const struct dhara_nand *n, dhara_block_t b, dhara_error_t *err) {
|
||||
(void)n;
|
||||
int ret = gd5f_block_erase(ftl_phy_block(b));
|
||||
if (ret == GD5F_ERASE_FAIL || ret != GD5F_OK) {
|
||||
dhara_nand_mark_bad(n, b);
|
||||
dhara_set_error(err, DHARA_E_BAD_BLOCK);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhara_nand_prog(const struct dhara_nand *n, dhara_page_t p, const uint8_t *data, dhara_error_t *err) {
|
||||
int ret;
|
||||
uint32_t phy_pg = ftl_phy_page(p);
|
||||
|
||||
gd5f_write_enable();
|
||||
ret = gd5f_program_load(0, data, FTL_PAGE_SIZE);
|
||||
if (ret != GD5F_OK) {
|
||||
dhara_nand_mark_bad(n, p >> n->log2_ppb);
|
||||
dhara_set_error(err, DHARA_E_BAD_BLOCK);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = gd5f_program_exec(phy_pg);
|
||||
if (ret == GD5F_PROGRAM_FAIL || ret != GD5F_OK) {
|
||||
dhara_nand_mark_bad(n, p >> n->log2_ppb);
|
||||
dhara_set_error(err, DHARA_E_BAD_BLOCK);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhara_nand_is_free(const struct dhara_nand *n, dhara_page_t p) {
|
||||
(void)n;
|
||||
uint32_t i;
|
||||
uint8_t buf[64];
|
||||
uint32_t phy_pg = ftl_phy_page(p);
|
||||
|
||||
if (gd5f_page_read(phy_pg) != GD5F_OK) {
|
||||
return 0;
|
||||
}
|
||||
gd5f_read_from_cache(0, buf, sizeof(buf));
|
||||
|
||||
for (i = 0; i < sizeof(buf); i++) {
|
||||
if (buf[i] != 0xFF) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p, size_t offset, size_t length, uint8_t *data, dhara_error_t *err) {
|
||||
(void)n;
|
||||
uint32_t phy_pg = ftl_phy_page(p);
|
||||
|
||||
if (gd5f_page_read(phy_pg) != GD5F_OK) {
|
||||
dhara_set_error(err, DHARA_E_ECC);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gd5f_check_ecc() != GD5F_OK) {
|
||||
dhara_set_error(err, DHARA_E_ECC);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gd5f_read_from_cache((uint16_t)offset, data, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhara_nand_copy(const struct dhara_nand *n, dhara_page_t src, dhara_page_t dst, dhara_error_t *err) {
|
||||
(void)n;
|
||||
|
||||
if (dhara_nand_read(n, src, 0, FTL_PAGE_SIZE, s_copy_buf, err) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dhara_nand_prog(n, dst, s_copy_buf, err) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ======================== FatFS 磁盘 I/O ======================== */
|
||||
|
||||
DSTATUS disk_initialize(BYTE pdrv) {
|
||||
if (pdrv != 0) {
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
if (s_initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DBG_INFO("Initializing NAND FTL...");
|
||||
|
||||
s_nand.log2_page_size = 11;
|
||||
s_nand.log2_ppb = 6;
|
||||
s_nand.num_blocks = FTL_NUM_BLOCKS;
|
||||
|
||||
dhara_map_init(&s_map, &s_nand, s_page_buf, 4);
|
||||
|
||||
memset(s_cache_buf, 0, FTL_PAGE_SIZE);
|
||||
s_cached_lpn = 0;
|
||||
s_cache_dirty = 0;
|
||||
|
||||
{
|
||||
dhara_error_t err;
|
||||
if (dhara_map_resume(&s_map, &err) < 0) {
|
||||
DBG_INFO("No valid map found, creating fresh (err=%d)", err);
|
||||
dhara_map_clear(&s_map);
|
||||
} else {
|
||||
DBG_INFO("Map resumed: %lu/%lu sectors used",
|
||||
dhara_map_size(&s_map), dhara_map_capacity(&s_map));
|
||||
}
|
||||
}
|
||||
|
||||
s_initialized = 1;
|
||||
DBG_INFO("NAND FTL ready (capacity: %lu pages = %lu MB)",
|
||||
dhara_map_capacity(&s_map),
|
||||
(dhara_map_capacity(&s_map) * FTL_PAGE_SIZE) / (1024 * 1024));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DSTATUS disk_status(BYTE pdrv) {
|
||||
if (pdrv != 0) {
|
||||
return STA_NOINIT;
|
||||
}
|
||||
if (!s_initialized) {
|
||||
return STA_NOINIT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
|
||||
if (pdrv != 0 || !buff) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
if (!s_initialized && disk_initialize(pdrv) != 0) {
|
||||
return RES_NOTRDY;
|
||||
}
|
||||
|
||||
while (count > 0) {
|
||||
dhara_sector_t lpn = (dhara_sector_t)(sector / FTL_SECTORS_PER_PAGE);
|
||||
uint32_t offset = (uint32_t)(sector % FTL_SECTORS_PER_PAGE) * 512;
|
||||
uint32_t batch = FTL_SECTORS_PER_PAGE - (sector % FTL_SECTORS_PER_PAGE);
|
||||
|
||||
if (batch > count) {
|
||||
batch = count;
|
||||
}
|
||||
|
||||
if (lpn != s_cached_lpn) {
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
if (ftl_read_page(lpn) != GD5F_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(buff, s_cache_buf + offset, batch * 512);
|
||||
buff += batch * 512;
|
||||
sector += batch;
|
||||
count -= (UINT)batch;
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) {
|
||||
if (pdrv != 0 || !buff) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
if (!s_initialized && disk_initialize(pdrv) != 0) {
|
||||
return RES_NOTRDY;
|
||||
}
|
||||
|
||||
while (count > 0) {
|
||||
dhara_sector_t lpn = (dhara_sector_t)(sector / FTL_SECTORS_PER_PAGE);
|
||||
uint32_t offset = (uint32_t)(sector % FTL_SECTORS_PER_PAGE) * 512;
|
||||
uint32_t batch = FTL_SECTORS_PER_PAGE - (sector % FTL_SECTORS_PER_PAGE);
|
||||
|
||||
if (batch > count) {
|
||||
batch = count;
|
||||
}
|
||||
|
||||
if (lpn != s_cached_lpn) {
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
if (ftl_read_page(lpn) != GD5F_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(s_cache_buf + offset, buff, batch * 512);
|
||||
s_cache_dirty = 1;
|
||||
|
||||
if (offset == 0 && batch == FTL_SECTORS_PER_PAGE) {
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
buff += batch * 512;
|
||||
sector += batch;
|
||||
count -= (UINT)batch;
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) {
|
||||
if (pdrv != 0) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
if (!s_initialized) {
|
||||
return RES_NOTRDY;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case CTRL_SYNC:
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
{
|
||||
dhara_error_t err;
|
||||
if (dhara_map_sync(&s_map, &err) < 0) {
|
||||
DBG_ERROR("Map sync failed: %d", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
return RES_OK;
|
||||
|
||||
case GET_SECTOR_COUNT: {
|
||||
LBA_t *p_sectors = (LBA_t *)buff;
|
||||
dhara_sector_t pages = dhara_map_capacity(&s_map);
|
||||
*p_sectors = pages * FTL_SECTORS_PER_PAGE;
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
case GET_SECTOR_SIZE:
|
||||
*(WORD *)buff = 512;
|
||||
return RES_OK;
|
||||
|
||||
case GET_BLOCK_SIZE:
|
||||
*(DWORD *)buff = 1;
|
||||
return RES_OK;
|
||||
|
||||
default:
|
||||
return RES_PARERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================== 公共函数定义 ======================== */
|
||||
|
||||
int nand_ftl_format(void) {
|
||||
dhara_error_t err;
|
||||
|
||||
if (!s_initialized) {
|
||||
if (disk_initialize(0) != 0) {
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
|
||||
dhara_map_clear(&s_map);
|
||||
|
||||
if (dhara_map_sync(&s_map, &err) < 0) {
|
||||
DBG_ERROR("Map sync after clear failed: %d", err);
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
|
||||
s_cache_dirty = 0;
|
||||
|
||||
DBG_INFO("NAND FTL formatted, capacity: %lu pages", dhara_map_capacity(&s_map));
|
||||
return GD5F_OK;
|
||||
}
|
||||
17
Drivers/BSP/GD5F2GQ5UE/nand_ftl.h
Normal file
17
Drivers/BSP/GD5F2GQ5UE/nand_ftl.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef __NAND_FTL_H
|
||||
#define __NAND_FTL_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int nand_ftl_init(void);
|
||||
int nand_ftl_format(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user