关键芯片都驱动,且测试成功

This commit is contained in:
2026-07-18 16:09:06 +08:00
parent 3466c4c062
commit fe107ea9af
42 changed files with 16193 additions and 501 deletions

View File

@@ -1,35 +1,51 @@
/**
******************************************************************************
* @file fal_cfg.h
* @brief FAL 配置Flash 设备表 + 分区表
******************************************************************************
*/
#ifndef __FAL_CFG_H
#define __FAL_CFG_H
#ifndef _FAL_CFG_H_
#define _FAL_CFG_H_
/*
* 模块名称FAL 配置
* 模块功能:定义 FAL Flash 设备表和分区表
* 适用平台STM32F407ZGT6
* 作者:王建锋
* 创建日期2026-07-16
* 修改记录:
* 2026-07-16 王建锋 创建初始版本
*/
#ifdef __cplusplus
extern "C" {
#endif
/* FAL 调试开关0-关闭1-开启 */
#define FAL_DEBUG 0
/* 启用分区表配置 */
#define FAL_PART_HAS_TABLE_CFG
/* ===================== Flash 设备表 ===================== */
/* ======================== Flash 设备表 ======================== */
extern const struct fal_flash_dev gd5f2gq5ue_flash;
#define FAL_FLASH_DEV_TABLE \
{ \
&gd5f2gq5ue_flash, \
#define FAL_FLASH_DEV_TABLE \
{ \
&gd5f2gq5ue_flash, \
}
/* ===================== 分区表 ===================== */
/* 分区名称 设备名称 偏移 大小 */
/* KVDB: 64MB @ 0 */
/* TSDB: 64MB @ 64MB */
/* ======================== 分区表 ======================== */
/*
* 分区名称 设备名称 偏移 大小
* 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", 0, 64*1024*1024, 0}, \
{FAL_PART_MAGIC_WORD, "fdb_tsdb1", "gd5f2gq5ue", 64*1024*1024, 64*1024*1024, 0}, \
}
#endif
#endif /* _FAL_CFG_H_ */
#ifdef __cplusplus
}
#endif
#endif /* __FAL_CFG_H */

View File

@@ -1,42 +1,80 @@
/**
******************************************************************************
* @file fal_flash_gd5f2gq5ue.c
* @brief FAL 端口GD5F2GQ5UE SPI NAND Flash 适配层
******************************************************************************
*/
/*
* 模块名称FAL Flash 设备适配
* 模块功能:将 GD5F2GQ5UE 驱动接口适配到 FAL 框架
* 适用平台STM32F407ZGT6
* 作者:王建锋
* 创建日期2026-07-16
* 修改记录:
* 2026-07-16 王建锋 创建初始版本
*/
/* 头文件包含区 */
#include "fal_def.h"
#include "gd5f2gq5ue.h"
/* ===================== FAL Flash 设备操作函数 ===================== */
/* ======================== FAL 操作函数适配 ======================== */
/*
* 函数功能Flash 设备初始化适配
* 入口参数:无
* 返回值0 - 成功,其他 - 错误码
* 限定条件SPI 和 GPIO 已由 CubeMX 初始化完成
* 函数说明:调用底层驱动的初始化函数
*/
static int gd5f_fal_init(void)
{
return gd5f2gq5ue_init();
}
static int gd5f_fal_read(long offset, uint8_t *buf, size_t size)
/*
* 函数功能Flash 读取适配
* 入口参数offset - 起始字节偏移 long
* p_buf - 数据缓冲区 uint8_t*
* size - 读取字节数 size_t
* 返回值0 - 成功,其他 - 错误码
* 限定条件gd5f_fal_init() 已成功调用
* 函数说明:直接转发到底层驱动的读取函数
*/
static int gd5f_fal_read(long offset, uint8_t *p_buf, size_t size)
{
return gd5f2gq5ue_read(offset, buf, size);
return gd5f2gq5ue_read(offset, p_buf, size);
}
static int gd5f_fal_write(long offset, const uint8_t *buf, size_t size)
/*
* 函数功能Flash 写入适配
* 入口参数offset - 起始字节偏移 long
* p_buf - 数据缓冲区 const uint8_t*
* size - 写入字节数 size_t
* 返回值0 - 成功,其他 - 错误码
* 限定条件:目标区域已擦除
* 函数说明:直接转发到底层驱动的写入函数
*/
static int gd5f_fal_write(long offset, const uint8_t *p_buf, size_t size)
{
return gd5f2gq5ue_write(offset, buf, size);
return gd5f2gq5ue_write(offset, p_buf, size);
}
/*
* 函数功能Flash 擦除适配
* 入口参数offset - 起始字节偏移 long
* size - 擦除字节数 size_t
* 返回值0 - 成功,其他 - 错误码
* 限定条件gd5f_fal_init() 已成功调用
* 函数说明:直接转发到底层驱动的擦除函数
*/
static int gd5f_fal_erase(long offset, size_t size)
{
return gd5f2gq5ue_erase(offset, size);
}
/* ===================== FAL Flash 设备定义 ===================== */
/* ======================== FAL Flash 设备定义 ======================== */
/* GD5F2GQ5UE FAL 设备实例总容量256MB块大小128KB */
const struct fal_flash_dev gd5f2gq5ue_flash = {
.name = "gd5f2gq5ue",
.addr = 0,
.len = GD5F_TOTAL_SIZE, /* 256MB */
.blk_size = GD5F_BLOCK_SIZE, /* 128KB */
.len = GD5F_TOTAL_SIZE,
.blk_size = GD5F_BLOCK_SIZE,
.ops = {
.init = gd5f_fal_init,
.read = gd5f_fal_read,

View File

@@ -1,20 +1,25 @@
/**
******************************************************************************
* @file fdb_cfg.h
* @brief FlashDB 配置文件
******************************************************************************
*/
#ifndef __FDB_CFG_H
#define __FDB_CFG_H
#ifndef _FDB_CFG_H_
#define _FDB_CFG_H_
/*
* 模块名称FlashDB 配置
* 模块功能:配置 FlashDB 数据库功能开关和参数
* 适用平台STM32F407ZGT6
* 作者:王建锋
* 创建日期2026-07-16
* 修改记录:
* 2026-07-16 王建锋 创建初始版本
*/
#ifdef __cplusplus
extern "C" {
#endif
/* 启用 KVDB键值数据库 */
#define FDB_USING_KVDB
#ifdef FDB_USING_KVDB
/* 当 KVDB 版本号变化时自动更新到最新默认值 */
/* KVDB 版本号变化时自动更新到最新默认值(默认关闭) */
/* #define FDB_KV_AUTO_UPDATE */
#endif
/* 启用 TSDB时序数据库 */
#define FDB_USING_TSDB
@@ -22,11 +27,14 @@
/* 使用 FAL 存储模式(非文件模式) */
#define FDB_USING_FAL_MODE
/* Flash 写入粒度单位bit
* STM32F4 = 8字节可编程 */
#define FDB_WRITE_GRAN 8
/* Flash 写入粒度单位bitSTM32F4 字节可编程 = 8 */
#define FDB_WRITE_GRAN 8
/* 调试输出 */
#define FDB_DEBUG_ENABLE
/* 调试输出使能 */
/*#define FDB_DEBUG_ENABLE*/
#endif /* _FDB_CFG_H_ */
#ifdef __cplusplus
}
#endif
#endif /* __FDB_CFG_H */

View File

@@ -1,350 +1,450 @@
/**
******************************************************************************
* @file gd5f2gq5ue.c
* @brief GD5F2GQ5UE SPI NAND Flash 驱动实现
******************************************************************************
*/
/*
* 模块名称GD5F2GQ5UE SPI NAND Flash 驱动
* 模块功能:提供 GD5F2GQ5UE SPI NAND Flash 的初始化、读、写、擦除接口
* 适用平台STM32F407ZGT6 + SPI1 硬件 SPI
* 作者:王建锋
* 创建日期2026-07-16
* 修改记录:
* 2026-07-16 王建锋 创建初始版本
* 2026-07-17 王建锋 切换硬件 SPI修正擦除地址参考 NuttX 驱动
*/
/* 头文件包含区 */
#include "gd5f2gq5ue.h"
#include <string.h>
/* 私有宏定义区 */
#define GD5F_SPI_TIMEOUT 100
/* 外部 SPI 句柄声明 */
extern SPI_HandleTypeDef hspi1;
/* ===================== 内部辅助函数 ===================== */
/* ======================== 私有函数声明 ======================== */
/**
* @brief SPI 发送/接收单字节
*/
static uint8_t gd5f_spi_xfer(uint8_t tx)
{
uint8_t rx;
HAL_SPI_TransmitReceive(&hspi1, &tx, &rx, 1, 100);
return rx;
}
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);
/**
* @brief SPI 发送缓冲区
*/
static void gd5f_spi_tx(const uint8_t *buf, size_t len)
{
HAL_SPI_Transmit(&hspi1, (uint8_t *)buf, len, 100);
}
/* ======================== 私有函数定义 ======================== */
/**
* @brief SPI 接收缓冲区
*/
static void gd5f_spi_rx(uint8_t *buf, size_t len)
{
HAL_SPI_Receive(&hspi1, buf, len, 100);
}
/**
* @brief 等待操作完成OIP 位清零)
* @param timeout_ms: 超时时间(毫秒)
* @retval GD5F_OK 成功, GD5F_BUSY_TIMEOUT 超时
/*
* 函数功能:等待芯片操作完成(轮询 OIP 位)
* 入口参数timeout_ms - 超时时间 uint32_t > 0
* 返回值0 - 操作完成,-2 - 超时
* 限定条件SPI 已初始化
* 函数说明:循环读取状态寄存器直到 OIP 位清零或超时
*/
static int gd5f_wait_busy(uint32_t timeout_ms)
{
uint8_t cmd = GD5F_CMD_GET_FEATURE;
uint8_t addr = GD5F_REG_STATUS;
uint8_t status;
uint32_t tickstart = HAL_GetTick();
uint8_t status = 0;
uint32_t tick_start = HAL_GetTick();
while (1) {
GD5F_CS_LOW();
gd5f_spi_tx(&cmd, 1);
gd5f_spi_tx(&addr, 1);
gd5f_spi_rx(&status, 1);
HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
HAL_SPI_Transmit(&hspi1, &addr, 1, GD5F_SPI_TIMEOUT);
HAL_SPI_Receive(&hspi1, &status, 1, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
if ((status & GD5F_STATUS_OIP) == 0) {
return GD5F_OK;
}
if ((HAL_GetTick() - tickstart) >= timeout_ms) {
if ((HAL_GetTick() - tick_start) >= timeout_ms) {
return GD5F_BUSY_TIMEOUT;
}
}
}
/**
* @brief 写使能
/*
* 函数功能:发送写使能命令
* 入口参数:无
* 返回值0 - 成功
* 限定条件SPI 已初始化
* 函数说明CS# 拉低后发送 06h 命令再拉高
*/
static int gd5f_write_enable(void)
{
uint8_t cmd = GD5F_CMD_WRITE_ENABLE;
GD5F_CS_LOW();
gd5f_spi_tx(&cmd, 1);
HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
return GD5F_OK;
}
/**
* @brief 读取状态寄存器
/*
* 函数功能:读取状态寄存器
* 入口参数p_status - 状态值输出指针 uint8_t* 不为 NULL
* 返回值0 - 成功
* 限定条件SPI 已初始化
* 函数说明:发送 0Fh + C0h 地址后读取1字节状态值
*/
static int gd5f_read_status(uint8_t *status)
static int gd5f_read_status(uint8_t *p_status)
{
uint8_t cmd = GD5F_CMD_GET_FEATURE;
uint8_t addr = GD5F_REG_STATUS;
GD5F_CS_LOW();
gd5f_spi_tx(&cmd, 1);
gd5f_spi_tx(&addr, 1);
gd5f_spi_rx(status, 1);
HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
HAL_SPI_Transmit(&hspi1, &addr, 1, GD5F_SPI_TIMEOUT);
HAL_SPI_Receive(&hspi1, p_status, 1, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
return GD5F_OK;
}
/**
* @brief 页读取到缓存Page Read to Cache
* @param page_addr: 页地址(行地址,包含 block + page
/*
* 函数功能:页读取(将数据从存储阵列加载到内部缓存
* 入口参数:page_addr - 页地址 uint32_t
* 返回值0 - 成功,其他 - 错误码
* 限定条件SPI 已初始化
* 函数说明:发送 13h + 3字节行地址等待 OIP 清零
*/
static int gd5f_page_read(uint32_t page_addr)
{
uint8_t cmd[4];
cmd[0] = GD5F_CMD_PAGE_READ;
cmd[1] = (page_addr >> 16) & 0xFF;
cmd[2] = (page_addr >> 8) & 0xFF;
cmd[3] = page_addr & 0xFF;
GD5F_CS_LOW();
gd5f_spi_tx(cmd, 4);
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
return gd5f_wait_busy(100);
}
/**
* @brief 从缓存读取数据Read from Cache
* @param column: 列地址(页内偏移)
* @param buf: 数据缓冲区
* @param size: 读取字节数
/*
* 函数功能:从内部缓存读取数据
* 入口参数:column - 列地址(页内偏移) uint16_t 0 - 2047
* p_buf - 数据输出缓冲区 uint8_t* 不为 NULL
* size - 读取字节数 size_t > 0
* 返回值0 - 成功
* 限定条件:必须先调用 gd5f_page_read 完成数据加载
* 函数说明:发送 0Bh + 2字节列地址 +1字节 dummy 后读取数据
*/
static int gd5f_read_from_cache(uint16_t column, uint8_t *buf, size_t size)
static int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
size_t size)
{
uint8_t cmd[5];
uint8_t cmd[4];
cmd[0] = GD5F_CMD_READ_FROM_CACHE;
cmd[1] = (column >> 8) & 0xFF;
cmd[2] = column & 0xFF;
cmd[3] = 0x00; /* dummy byte */
cmd[3] = 0x00;
GD5F_CS_LOW();
gd5f_spi_tx(cmd, 4);
gd5f_spi_rx(buf, size);
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
HAL_SPI_Receive(&hspi1, p_buf, size, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
return GD5F_OK;
}
/**
* @brief 页编程(从缓存写入阵列
* @param page_addr: 页地址
* @param column: 列地址
* @param buf: 数据缓冲区
* @param size: 写入字节数
/*
* 函数功能:页编程(将数据写入指定页
* 入口参数:page_addr - 页地址 uint32_t
* column - 列地址 uint16_t 0 - 2047
* p_buf - 数据缓冲区 const uint8_t*
* size - 写入字节数 size_t > 0
* 返回值0 - 成功,其他 - 错误码
* 限定条件:目标区域已擦除
* 函数说明1. 写使能 - 02h 加载数据 - 10h 执行编程 - 等待完成
* 2. 编程完成后检查 P_FAIL 位
*/
static int gd5f_page_program(uint32_t page_addr, uint16_t column, const uint8_t *buf, size_t size)
static int gd5f_page_program(uint32_t page_addr, uint16_t column,
const uint8_t *p_buf, size_t size)
{
int ret;
int ret = GD5F_OK;
uint8_t cmd[4];
uint8_t status = 0;
/* 1. 写使能 */
gd5f_write_enable();
/* 2. 数据加载到缓存 (Program Load) */
cmd[0] = GD5F_CMD_PROGRAM_LOAD;
cmd[1] = (column >> 8) & 0xFF;
cmd[2] = column & 0xFF;
GD5F_CS_LOW();
gd5f_spi_tx(cmd, 3);
gd5f_spi_tx(buf, size);
HAL_SPI_Transmit(&hspi1, cmd, 3, GD5F_SPI_TIMEOUT);
HAL_SPI_Transmit(&hspi1, (uint8_t *)p_buf, size,
GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
/* 3. 编程执行 (Program Execute) */
cmd[0] = GD5F_CMD_PROGRAM_EXEC;
cmd[1] = (page_addr >> 16) & 0xFF;
cmd[2] = (page_addr >> 8) & 0xFF;
cmd[3] = page_addr & 0xFF;
GD5F_CS_LOW();
gd5f_spi_tx(cmd, 4);
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
/* 4. 等待完成 */
ret = gd5f_wait_busy(1000);
if (ret != GD5F_OK) return ret;
if (ret != GD5F_OK) {
return ret;
}
/* 5. 检查编程结果 */
uint8_t status;
gd5f_read_status(&status);
if (status & GD5F_STATUS_P_FAIL) {
/* 清除 P_FAIL写使能 + 读状态 */
gd5f_write_enable();
return GD5F_PROGRAM_FAIL;
}
return GD5F_OK;
}
/**
* @brief 块擦除
* @param block_addr: 块地址 (0 ~ 2047)
/*
* 函数功能:设置 Feature 寄存器
* 入口参数addr - 寄存器地址 uint8_t
* data - 写入数据 uint8_t
* 返回值0 - 成功
* 限定条件SPI 已初始化
* 函数说明:先写使能,发送 1Fh + 地址 + 数据,等待操作完成
*/
static int gd5f_set_feature(uint8_t addr, uint8_t data)
{
uint8_t cmd[3];
gd5f_write_enable();
cmd[0] = GD5F_CMD_SET_FEATURE;
cmd[1] = addr;
cmd[2] = data;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, cmd, 3, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
gd5f_wait_busy(100);
return GD5F_OK;
}
/*
* 函数功能块擦除擦除128KB块
* 入口参数block_addr - 块编号 uint32_t 0 - 2047
* 返回值0 - 成功,其他 - 错误码
* 限定条件gd5f2gq5ue_init() 已调用
* 函数说明1. 写使能 - D8h + 3字节字节地址 - 等待完成
* 2. 擦除完成后检查 E_FAIL 位
*/
static int gd5f_block_erase(uint32_t block_addr)
{
int ret;
int ret = GD5F_OK;
uint8_t cmd[4];
uint8_t status = 0;
uint32_t byte_addr = block_addr * GD5F_BLOCK_SIZE;
/* 1. 写使能 */
gd5f_write_enable();
/* 2. 块擦除命令 */
cmd[0] = GD5F_CMD_BLOCK_ERASE;
cmd[1] = (block_addr >> 8) & 0xFF;
cmd[2] = block_addr & 0xFF;
cmd[3] = 0x00;
cmd[1] = (byte_addr >> 16) & 0xFF;
cmd[2] = (byte_addr >> 8) & 0xFF;
cmd[3] = byte_addr & 0xFF;
GD5F_CS_LOW();
gd5f_spi_tx(cmd, 4);
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
/* 3. 等待完成(擦除较慢) */
ret = gd5f_wait_busy(5000);
if (ret != GD5F_OK) return ret;
if (ret != GD5F_OK) {
return ret;
}
/* 4. 检查擦除结果 */
uint8_t status;
gd5f_read_status(&status);
if (status & GD5F_STATUS_E_FAIL) {
gd5f_write_enable();
return GD5F_ERASE_FAIL;
}
return GD5F_OK;
}
/* ===================== 公共接口实现 ===================== */
/* ======================== 公共函数定义 ======================== */
/*
* 函数功能:初始化 GD5F2GQ5UE
* 入口参数:无
* 返回值0 - 成功,其他 - 错误码
* 限定条件SPI1 和相关 GPIO 已由 CubeMX 初始化完成
* 函数说明1. 发送复位命令并等待完成
* 2. 读取芯片 ID 并校验
* 3. 使能内部 ECC (B0h bit4)
* 4. 解除所有块保护 (A0h = 0x00)
*/
int gd5f2gq5ue_init(void)
{
int ret;
uint8_t mid, did;
int ret = GD5F_OK;
uint8_t mid = 0;
uint8_t did = 0;
/* 确保控制引脚处于非激活状态 */
GD5F_CS_HIGH();
GD5F_WP_HIGH();
GD5F_HOLD_HIGH();
/* 小延时等待上电稳定 */
HAL_Delay(10);
/* 复位芯片 */
ret = gd5f2gq5ue_reset();
if (ret != GD5F_OK) return ret;
if (ret != GD5F_OK) {
return ret;
}
HAL_Delay(5);
/* 读取 ID 验证通信 */
ret = gd5f2gq5ue_read_id(&mid, &did);
if (ret != GD5F_OK) return ret;
if (ret != GD5F_OK) {
return ret;
}
if (mid != GD5F_MANUFACTURER_ID || did != GD5F_DEVICE_ID) {
return GD5F_ID_MISMATCH;
}
gd5f_set_feature(0xB0, 0x10);
gd5f_wait_busy(100);
gd5f_set_feature(GD5F_REG_PROTECT, 0x00);
gd5f_wait_busy(100);
return GD5F_OK;
}
/*
* 函数功能:读取芯片 IDMID + DID
* 入口参数mid - 制造商 ID 输出指针 uint8_t* 不为 NULL
* did - 设备 ID 输出指针 uint8_t* 不为 NULL
* 返回值0 - 成功
* 限定条件SPI 已初始化
* 函数说明:发送 9Fh 命令后接收1个 dummy + MID + DID
*/
int gd5f2gq5ue_read_id(uint8_t *mid, uint8_t *did)
{
uint8_t cmd = GD5F_CMD_READ_ID;
uint8_t id_buf[3];
uint8_t id_buf[3] = {0};
GD5F_CS_LOW();
gd5f_spi_tx(&cmd, 1);
gd5f_spi_rx(id_buf, 3); /* dummy + MID + DID */
HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
HAL_SPI_Receive(&hspi1, id_buf, 3, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
*mid = id_buf[1]; /* id_buf[0] = dummy */
*mid = id_buf[1];
*did = id_buf[2];
return GD5F_OK;
}
int gd5f2gq5ue_read(long offset, uint8_t *buf, size_t size)
/*
* 函数功能:从 NAND 读取数据(支持跨页)
* 入口参数offset - 起始字节偏移 long 0 ~ 总容量-1
* p_buf - 数据缓冲区 uint8_t* 不为 NULL
* size - 读取字节数 size_t > 0
* 返回值0 - 成功,其他 - 错误码
* 限定条件gd5f2gq5ue_init() 已成功调用
* 函数说明:自动处理跨页读取
*/
int gd5f2gq5ue_read(long offset, uint8_t *p_buf, size_t size)
{
int ret;
int ret = GD5F_OK;
while (size > 0) {
/* 计算页地址和列偏移 */
uint32_t page_addr = offset / GD5F_PAGE_SIZE;
uint16_t column = offset % GD5F_PAGE_SIZE;
/* 本次读取字节数(不超过当前页剩余空间) */
size_t bytes = GD5F_PAGE_SIZE - column;
if (bytes > size) bytes = size;
/* Page Read to Cache */
if (bytes > size) {
bytes = size;
}
ret = gd5f_page_read(page_addr);
if (ret != GD5F_OK) return ret;
if (ret != GD5F_OK) {
return ret;
}
/* Read from Cache */
ret = gd5f_read_from_cache(column, buf, bytes);
if (ret != GD5F_OK) return ret;
ret = gd5f_read_from_cache(column, p_buf, bytes);
if (ret != GD5F_OK) {
return ret;
}
offset += bytes;
buf += bytes;
p_buf += bytes;
size -= bytes;
}
return GD5F_OK;
}
int gd5f2gq5ue_write(long offset, const uint8_t *buf, size_t size)
/*
* 函数功能:向 NAND 写入数据(支持跨页)
* 入口参数offset - 起始字节偏移 long 0 ~ 总容量-1
* p_buf - 数据缓冲区 const uint8_t* 不为 NULL
* size - 写入字节数 size_t > 0
* 返回值0 - 成功,其他 - 错误码
* 限定条件gd5f2gq5ue_init() 已成功调用,目标区域已擦除
* 函数说明:自动处理跨页写入
*/
int gd5f2gq5ue_write(long offset, const uint8_t *p_buf, size_t size)
{
int ret;
int ret = GD5F_OK;
while (size > 0) {
/* 计算页地址和列偏移 */
uint32_t page_addr = offset / GD5F_PAGE_SIZE;
uint16_t column = offset % GD5F_PAGE_SIZE;
/* 本次写入字节数(不超过当前页剩余空间) */
size_t bytes = GD5F_PAGE_SIZE - column;
if (bytes > size) bytes = size;
/* 页编程 */
ret = gd5f_page_program(page_addr, column, buf, bytes);
if (ret != GD5F_OK) return ret;
if (bytes > size) {
bytes = size;
}
ret = gd5f_page_program(page_addr, column, p_buf, bytes);
if (ret != GD5F_OK) {
return ret;
}
offset += bytes;
buf += bytes;
p_buf += bytes;
size -= bytes;
}
return GD5F_OK;
}
/*
* 函数功能:擦除块(按块擦除,最小单位 128KB
* 入口参数offset - 起始字节偏移 long 必须块对齐
* size - 擦除字节数 size_t 必须块大小整数倍
* 返回值0 - 成功,其他 - 错误码
* 限定条件gd5f2gq5ue_init() 已成功调用
* 函数说明:擦除操作以块为单位
*/
int gd5f2gq5ue_erase(long offset, size_t size)
{
int ret;
int ret = GD5F_OK;
/* 擦除起始地址必须块对齐 */
if (offset % GD5F_BLOCK_SIZE != 0) {
return GD5F_ERROR;
}
/* 擦除大小必须为块大小的整数倍 */
if (size % GD5F_BLOCK_SIZE != 0) {
return GD5F_ERROR;
}
/* 逐块擦除 */
while (size > 0) {
uint32_t block_addr = offset / GD5F_BLOCK_SIZE;
ret = gd5f_block_erase(block_addr);
if (ret != GD5F_OK) return ret;
if (ret != GD5F_OK) {
return ret;
}
offset += GD5F_BLOCK_SIZE;
size -= GD5F_BLOCK_SIZE;
@@ -353,12 +453,19 @@ int gd5f2gq5ue_erase(long offset, size_t size)
return GD5F_OK;
}
/*
* 函数功能:复位芯片
* 入口参数:无
* 返回值0 - 成功
* 限定条件SPI 已初始化
* 函数说明:发送 FFh 复位命令后等待 5ms
*/
int gd5f2gq5ue_reset(void)
{
uint8_t cmd = GD5F_CMD_RESET;
GD5F_CS_LOW();
gd5f_spi_tx(&cmd, 1);
HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
HAL_Delay(5);

View File

@@ -1,75 +1,70 @@
/**
******************************************************************************
* @file gd5f2gq5ue.h
* @brief GD5F2GQ5UE SPI NAND Flash 驱动头文件
******************************************************************************
*/
#ifndef __GD5F2GQ5UE_H
#define __GD5F2GQ5UE_H
/*
* 模块名称GD5F2GQ5UE SPI NAND Flash 驱动
* 模块功能:提供 GD5F2GQ5UE SPI NAND Flash 的初始化、读、写、擦除接口
* 适用平台STM32F407ZGT6 + SPI1 硬件 SPI
* 作者:王建锋
* 创建日期2026-07-16
* 修改记录:
* 2026-07-16 王建锋 创建初始版本
* 2026-07-17 王建锋 切换为硬件 SPI修正擦除地址参考 NuttX 驱动
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#include "spi.h"
/* ===================== 硬件引脚定义 ===================== */
#define GD5F_CS_LOW() HAL_GPIO_WritePin(GD_CS_GPIO_Port, GD_CS_Pin, GPIO_PIN_RESET)
#define GD5F_CS_HIGH() HAL_GPIO_WritePin(GD_CS_GPIO_Port, GD_CS_Pin, GPIO_PIN_SET)
#define GD5F_WP_HIGH() HAL_GPIO_WritePin(GD_WP_GPIO_Port, GD_WP_Pin, GPIO_PIN_SET)
#define GD5F_HOLD_HIGH() HAL_GPIO_WritePin(GD_HOLD_GPIO_Port, GD_HOLD_Pin, GPIO_PIN_SET)
/* ======================== 宏定义 ======================== */
/* ===================== SPI 命令码 ===================== */
/* SPI 命令码 */
#define GD5F_CMD_WRITE_ENABLE 0x06
#define GD5F_CMD_WRITE_DISABLE 0x04
#define GD5F_CMD_GET_FEATURE 0x0F
#define GD5F_CMD_SET_FEATURE 0x1F
#define GD5F_CMD_READ_ID 0x9F
#define GD5F_CMD_PAGE_READ 0x13
#define GD5F_CMD_READ_FROM_CACHE 0x0B /* Fast Read, 1 dummy byte */
#define GD5F_CMD_READ_FROM_CACHE 0x0B
#define GD5F_CMD_PROGRAM_LOAD 0x02
#define GD5F_CMD_PROGRAM_EXEC 0x10
#define GD5F_CMD_BLOCK_ERASE 0xD8
#define GD5F_CMD_RESET 0xFF
/* ===================== 寄存器地址 ===================== */
#define GD5F_REG_STATUS 0xC0
/* 寄存器地址 */
#define GD5F_REG_PROTECT 0xA0
#define GD5F_REG_FEATURE 0xB0
#define GD5F_REG_STATUS 0xC0
#define GD5F_REG_DRIVER 0xD0
/* ===================== 状态位定义 ===================== */
#define GD5F_STATUS_OIP (1 << 0) /* 操作进行中 */
#define GD5F_STATUS_WEL (1 << 1) /* 写使能锁存 */
#define GD5F_STATUS_E_FAIL (1 << 2) /* 擦除失败 */
#define GD5F_STATUS_P_FAIL (1 << 3) /* 编程失败 */
#define GD5F_STATUS_BIT4 (1 << 4)
#define GD5F_STATUS_ECCS0 (1 << 5)
#define GD5F_STATUS_ECCS1 (1 << 6)
#define GD5F_STATUS_ECCSE0 (1 << 4) /* ECC 单 bit 错误低位 */
#define GD5F_STATUS_ECCSE1 (1 << 5) /* ECC 单 bit 错误高位 */
#define GD5F_STATUS_ECCSE2 (1 << 6) /* ECC 单 bit 错误高位 */
/* 状态位定义 */
#define GD5F_STATUS_OIP (1 << 0)
#define GD5F_STATUS_WEL (1 << 1)
#define GD5F_STATUS_E_FAIL (1 << 2)
#define GD5F_STATUS_P_FAIL (1 << 3)
#define GD5F_STATUS_ECCS0 (1 << 4)
#define GD5F_STATUS_ECCS1 (1 << 5)
/* ===================== Feature 位定义 ===================== */
/* Feature 位定义 */
#define GD5F_FEATURE_ECC_EN (1 << 4)
#define GD5F_FEATURE_QE (1 << 0) /* Quad Enable, B0[0] */
#define GD5F_FEATURE_QE (1 << 0)
/* ===================== 芯片参数 ===================== */
#define GD5F_PAGE_SIZE 2048 /* 主数据区字节数 */
#define GD5F_SPARE_SIZE 64 /* ECC 启用时 spare 区 */
#define GD5F_TOTAL_PAGE_SIZE 2112 /* 2048 + 64 */
/* 芯片参数 */
#define GD5F_PAGE_SIZE 2048
#define GD5F_SPARE_SIZE 64
#define GD5F_TOTAL_PAGE_SIZE 2112
#define GD5F_PAGES_PER_BLOCK 64
#define GD5F_BLOCK_SIZE (GD5F_PAGES_PER_BLOCK * GD5F_PAGE_SIZE) /* 128KB */
#define GD5F_BLOCK_SIZE (GD5F_PAGES_PER_BLOCK * GD5F_PAGE_SIZE)
#define GD5F_TOTAL_BLOCKS 2048
#define GD5F_TOTAL_SIZE (GD5F_TOTAL_BLOCKS * GD5F_BLOCK_SIZE) /* 256MB */
#define GD5F_TOTAL_SIZE (GD5F_TOTAL_BLOCKS * GD5F_BLOCK_SIZE)
/* 制造商 ID 和设备 ID */
#define GD5F_MANUFACTURER_ID 0xC8
#define GD5F_DEVICE_ID 0x52
/* ===================== 返回值定义 ===================== */
/* 返回值定义 */
#define GD5F_OK 0
#define GD5F_ERROR -1
#define GD5F_BUSY_TIMEOUT -2
@@ -78,51 +73,74 @@ extern "C" {
#define GD5F_ERASE_FAIL -5
#define GD5F_ID_MISMATCH -6
/* ===================== 公共接口函数 ===================== */
/* 控制引脚宏 */
#define GD5F_CS_LOW() HAL_GPIO_WritePin(GD_CS_GPIO_Port, GD_CS_Pin, GPIO_PIN_RESET)
#define GD5F_CS_HIGH() HAL_GPIO_WritePin(GD_CS_GPIO_Port, GD_CS_Pin, GPIO_PIN_SET)
#define GD5F_WP_HIGH() HAL_GPIO_WritePin(GD_WP_GPIO_Port, GD_WP_Pin, GPIO_PIN_SET)
#define GD5F_HOLD_HIGH() HAL_GPIO_WritePin(GD_HOLD_GPIO_Port, GD_HOLD_Pin, GPIO_PIN_SET)
/**
* @brief 初始化 GD5F2GQ5UESPI + GPIO + 读 ID 验证)
* @retval GD5F_OK 成功, 其他为错误码
/* ======================== 函数声明 ======================== */
/*
* 函数功能:初始化 GD5F2GQ5UE读 ID + 使能 ECC + 解除块保护)
* 入口参数:无
* 返回值0 - 成功,其他 - 错误码
* 限定条件SPI1 和相关 GPIO 已由 CubeMX 初始化完成
* 函数说明1. 发送复位命令并等待完成
* 2. 读取芯片 ID 并校验
* 3. 使能内部 ECC (B0h bit4)
* 4. 解除所有块保护 (A0h = 0x00)
*/
int gd5f2gq5ue_init(void);
/**
* @brief 读取芯片 IDMID + DID
* @param mid: 制造商 ID 输出指针
* @param did: 设备 ID 输出指针
* @retval GD5F_OK 成功
/*
* 函数功能:读取芯片 IDMID + DID
* 入口参数:mid - 制造商 ID 输出指针 uint8_t* 不为 NULL
* did - 设备 ID 输出指针 uint8_t* 不为 NULL
* 返回值0 - 成功
* 限定条件SPI 已初始化
* 函数说明:发送 9Fh 命令后接收1个 dummy + MID + DID
*/
int gd5f2gq5ue_read_id(uint8_t *mid, uint8_t *did);
/**
* @brief 从 NAND 读取数据(支持跨页)
* @param offset: 起始字节偏移(相对于 Flash 起始地址)
* @param buf: 数据缓冲区
* @param size: 读取字节数
* @retval GD5F_OK 成功
/*
* 函数功能:从 NAND 读取数据(支持跨页)
* 入口参数:offset - 起始字节偏移 long 0 - GD5F_TOTAL_SIZE-1
* buf - 数据缓冲区 uint8_t* 不为 NULL
* size - 读取字节数 size_t > 0
* 返回值0 - 成功,其他 - 错误码
* 限定条件gd5f2gq5ue_init() 已成功调用
* 函数说明:自动处理跨页读取,每次读取不超过当前页剩余空间
*/
int gd5f2gq5ue_read(long offset, uint8_t *buf, size_t size);
/**
* @brief 向 NAND 写入数据(支持跨页)
* @param offset: 起始字节偏移
* @param buf: 数据缓冲区
* @param size: 写入字节数
* @retval GD5F_OK 成功
/*
* 函数功能:向 NAND 写入数据(支持跨页)
* 入口参数:offset - 起始字节偏移 long 0 - GD5F_TOTAL_SIZE-1
* buf - 数据缓冲区 uint8_t* 不为 NULL
* size - 写入字节数 size_t > 0
* 返回值0 - 成功,其他 - 错误码
* 限定条件gd5f2gq5ue_init() 已成功调用,目标区域已擦除
* 函数说明:自动处理跨页写入,每次写入不超过当前页剩余空间
*/
int gd5f2gq5ue_write(long offset, const uint8_t *buf, size_t size);
/**
* @brief 擦除块(按块擦除,最小单位 128KB
* @param offset: 起始字节偏移(必须块对齐
* @param size: 擦除字节数(必须为块大小的整数倍
* @retval GD5F_OK 成功
/*
* 函数功能:擦除块(按块擦除,最小单位 128KB
* 入口参数:offset - 起始字节偏移 long 必须 GD5F_BLOCK_SIZE 对齐
* size - 擦除字节数 size_t 必须 GD5F_BLOCK_SIZE 整数倍
* 返回值0 - 成功,其他 - 错误码
* 限定条件gd5f2gq5ue_init() 已成功调用
* 函数说明擦除操作以块为单位offset 和 size 必须块对齐
*/
int gd5f2gq5ue_erase(long offset, size_t size);
/**
* @brief 复位芯片
* @retval GD5F_OK 成功
/*
* 函数功能:复位芯片
* 入口参数:无
* 返回值0 - 成功
* 限定条件SPI 已初始化
* 函数说明:发送 FFh 复位命令后等待 5ms
*/
int gd5f2gq5ue_reset(void);