本文所选用的flash为兆易创新的GD5F2GM7 NAND flash采用标准SPI接口。

spi的驱动

/**
 ******************************************************************************
 * @file    gd32f407_nand_spi.c
 * @brief   GD32F407 SPI platform implementation for NAND Flash driver
 * @author  Driver for GD32F407 Platform
 * @date    2026-02-09
 ******************************************************************************
 * @attention
 * 
 * This file implements the low-level SPI functions required by the NAND driver
 * Configure your SPI peripheral before using these functions
 * 
 * Example pin configuration:
 * - SPI0_SCK:  PA5 or PB3
 * - SPI0_MISO: PA6 or PB4
 * - SPI0_MOSI: PA7 or PB5
 * - CS#:       PA4 (GPIO output)
 * 
 ******************************************************************************
 */

#include "gd5f2gm7_nand.h"
#include "gd32f4xx.h"

/* Configuration ------------------------------------------------------------- */
#define NAND_SPI_PERIPH         SPI0
#define NAND_SPI_CLK            RCU_SPI0

#define NAND_CS_PORT            GPIOA
#define NAND_CS_PIN             GPIO_PIN_4
#define NAND_CS_CLK             RCU_GPIOA

#define NAND_SCK_PORT           GPIOA
#define NAND_SCK_PIN            GPIO_PIN_5
#define NAND_SCK_AF             GPIO_AF_5

#define NAND_MISO_PORT          GPIOA
#define NAND_MISO_PIN           GPIO_PIN_6
#define NAND_MISO_AF            GPIO_AF_5

#define NAND_MOSI_PORT          GPIOA
#define NAND_MOSI_PIN           GPIO_PIN_7
#define NAND_MOSI_AF            GPIO_AF_5

/* Private variables --------------------------------------------------------- */
static volatile uint32_t systick_count = 0;

/**
 * @brief  SysTick interrupt handler (must be called from SysTick_Handler)
 */
void NAND_SysTick_Handler(void)
{
    systick_count++;
}

/**
 * @brief  Initialize SPI peripheral for NAND Flash
 * @param  spi_handle: Not used (can be NULL)
 */
void NAND_SPI_Init(void *spi_handle)
{
    spi_parameter_struct spi_init_struct;
    
    /* Enable peripheral clocks */
    rcu_periph_clock_enable(NAND_SPI_CLK);
    rcu_periph_clock_enable(NAND_CS_CLK);
    rcu_periph_clock_enable(RCU_GPIOA);
    
    /* Configure CS pin as output */
    gpio_mode_set(NAND_CS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, NAND_CS_PIN);
    gpio_output_options_set(NAND_CS_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, NAND_CS_PIN);
    gpio_bit_set(NAND_CS_PORT, NAND_CS_PIN);  // CS# high (inactive)
    
    /* Configure SPI pins */
    gpio_af_set(NAND_SCK_PORT, NAND_SCK_AF, NAND_SCK_PIN);
    gpio_af_set(NAND_MISO_PORT, NAND_MISO_AF, NAND_MISO_PIN);
    gpio_af_set(NAND_MOSI_PORT, NAND_MOSI_AF, NAND_MOSI_PIN);
    
    gpio_mode_set(NAND_SCK_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, NAND_SCK_PIN);
    gpio_output_options_set(NAND_SCK_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, NAND_SCK_PIN);
    
    gpio_mode_set(NAND_MISO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, NAND_MISO_PIN);
    
    gpio_mode_set(NAND_MOSI_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, NAND_MOSI_PIN);
    gpio_output_options_set(NAND_MOSI_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, NAND_MOSI_PIN);
    
    /* SPI configuration */
    spi_disable(NAND_SPI_PERIPH);
    
    spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode = SPI_MASTER;
    spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;  // Mode 0
    spi_init_struct.nss = SPI_NSS_SOFT;
    spi_init_struct.prescale = SPI_PSC_4;  // APB2 clock / 4 (adjust for desired frequency)
    spi_init_struct.endian = SPI_ENDIAN_MSB;
    
    spi_init(NAND_SPI_PERIPH, &spi_init_struct);
    
    /* Enable SPI */
    spi_enable(NAND_SPI_PERIPH);
    
    /* Configure SysTick for 1ms interrupts */
    systick_config();
}

/**
 * @brief  Set CS# low (activate chip select)
 */
void NAND_SPI_CS_Low(void)
{
    gpio_bit_reset(NAND_CS_PORT, NAND_CS_PIN);
    /* Small delay for CS# setup time */
    for (volatile int i = 0; i < 10; i++);
}

/**
 * @brief  Set CS# high (deactivate chip select)
 */
void NAND_SPI_CS_High(void)
{
    /* Wait for SPI to complete */
    while (RESET != spi_i2s_flag_get(NAND_SPI_PERIPH, SPI_FLAG_TRANS));
    
    /* Small delay before CS# high */
    for (volatile int i = 0; i < 10; i++);
    
    gpio_bit_set(NAND_CS_PORT, NAND_CS_PIN);
    
    /* CS# high time */
    for (volatile int i = 0; i < 20; i++);
}

/**
 * @brief  Transmit and receive one byte via SPI
 * @param  data: Byte to transmit
 * @retval Received byte
 */
uint8_t NAND_SPI_TransmitReceive(uint8_t data)
{
    /* Wait until transmit buffer is empty */
    while (RESET == spi_i2s_flag_get(NAND_SPI_PERIPH, SPI_FLAG_TBE));
    
    /* Send data */
    spi_i2s_data_transmit(NAND_SPI_PERIPH, data);
    
    /* Wait until receive buffer is not empty */
    while (RESET == spi_i2s_flag_get(NAND_SPI_PERIPH, SPI_FLAG_RBNE));
    
    /* Return received data */
    return spi_i2s_data_receive(NAND_SPI_PERIPH);
}

/**
 * @brief  Transmit multiple bytes via SPI
 * @param  buffer: Pointer to data buffer
 * @param  size: Number of bytes to transmit
 */
void NAND_SPI_Transmit(const uint8_t *buffer, uint32_t size)
{
    for (uint32_t i = 0; i < size; i++) {
        NAND_SPI_TransmitReceive(buffer[i]);
    }
}

/**
 * @brief  Receive multiple bytes via SPI
 * @param  buffer: Pointer to receive buffer
 * @param  size: Number of bytes to receive
 */
void NAND_SPI_Receive(uint8_t *buffer, uint32_t size)
{
    for (uint32_t i = 0; i < size; i++) {
        buffer[i] = NAND_SPI_TransmitReceive(0xFF);
    }
}

/**
 * @brief  Delay in microseconds
 * @param  us: Delay time in microseconds
 * @note   This is a simple delay, implement more accurate timing if needed
 */
void NAND_Delay_us(uint32_t us)
{
    /* Assuming system clock is 168MHz */
    /* Each loop iteration takes approximately 6 cycles */
    uint32_t loops = (SystemCoreClock / 1000000) * us / 6;
    
    for (volatile uint32_t i = 0; i < loops; i++) {
        __NOP();
    }
}

/**
 * @brief  Delay in milliseconds
 * @param  ms: Delay time in milliseconds
 */
void NAND_Delay_ms(uint32_t ms)
{
    uint32_t start = systick_count;
    while ((systick_count - start) < ms);
}

/**
 * @brief  Get current tick count
 * @retval Current tick count in milliseconds
 */
uint32_t NAND_GetTick(void)
{
    return systick_count;
}

NAND flash的驱动,包括.c.h结合使用手册发指令即可

flash.h

/**
 ******************************************************************************
 * @file    gd5f2gm7_nand.h
 * @brief   Header file for GD5F2GM7UEYIGR SPI-NAND Flash driver
 * @author  Driver for GD32F407 Platform
 * @date    2026-02-09
 ******************************************************************************
 * @attention
 * 
 * This driver supports GD5F2GM7UEYIGR 2Gbit SPI-NAND Flash
 * - 2048 blocks × 64 pages/block × (2KB + 128B)/page
 * - SPI modes: Standard, Dual, Quad (up to 133MHz)
 * - Internal ECC enabled by default (8-bit/528-byte)
 * 
 ******************************************************************************
 */

#ifndef __GD5F2GM7_NAND_H
#define __GD5F2GM7_NAND_H

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>

/* Device Parameters ---------------------------------------------------------*/
#define NAND_PAGE_SIZE              2048        // Main area: 2KB
#define NAND_SPARE_SIZE_ECC_ON      64          // Spare area when ECC enabled
#define NAND_SPARE_SIZE_ECC_OFF     128         // Spare area when ECC disabled
#define NAND_PAGES_PER_BLOCK        64          // Pages per block
#define NAND_TOTAL_BLOCKS           2048        // Total blocks
#define NAND_BLOCK_SIZE             (NAND_PAGE_SIZE * NAND_PAGES_PER_BLOCK)

/* Command Set ---------------------------------------------------------------*/
#define CMD_WRITE_ENABLE            0x06
#define CMD_WRITE_DISABLE           0x04
#define CMD_GET_FEATURES            0x0F
#define CMD_SET_FEATURES            0x1F
#define CMD_PAGE_READ               0x13
#define CMD_READ_CACHE              0x03
#define CMD_READ_CACHE_FAST         0x0B
#define CMD_READ_CACHE_X2           0x3B
#define CMD_READ_CACHE_X4           0x6B
#define CMD_READ_CACHE_DUAL_IO      0xBB
#define CMD_READ_CACHE_QUAD_IO      0xEB
#define CMD_READ_CACHE_QUAD_DTR     0xEE
#define CMD_READ_ID                 0x9F
#define CMD_PROGRAM_LOAD            0x02
#define CMD_PROGRAM_LOAD_X4         0x32
#define CMD_PROGRAM_EXECUTE         0x10
#define CMD_PROGRAM_LOAD_RANDOM     0x84
#define CMD_PROGRAM_LOAD_RANDOM_X4  0x34
#define CMD_BLOCK_ERASE             0xD8
#define CMD_RESET                   0xFF
#define CMD_ENABLE_POWER_ON_RESET   0x66
#define CMD_POWER_ON_RESET          0x99

/* Feature Register Addresses ------------------------------------------------*/
#define REG_PROTECTION              0xA0
#define REG_FEATURE                 0xB0
#define REG_STATUS                  0xC0
#define REG_DRIVER                  0xD0
#define REG_STATUS_EXT              0xF0

/* Status Register Bits ------------------------------------------------------*/
#define STATUS_OIP                  (1 << 0)    // Operation in progress
#define STATUS_WEL                  (1 << 1)    // Write enable latch
#define STATUS_E_FAIL               (1 << 2)    // Erase failure
#define STATUS_P_FAIL               (1 << 3)    // Program failure
#define STATUS_ECCS0                (1 << 4)    // ECC status bit 0
#define STATUS_ECCS1                (1 << 5)    // ECC status bit 1

/* Feature Register Bits -----------------------------------------------------*/
#define FEATURE_QE                  (1 << 0)    // Quad enable
#define FEATURE_ECC_EN              (1 << 4)    // ECC enable
#define FEATURE_OTP_EN              (1 << 6)    // OTP enable
#define FEATURE_OTP_PRT             (1 << 7)    // OTP protect

/* Protection Register Bits --------------------------------------------------*/
#define PROT_CMP                    (1 << 1)    // Complement protect
#define PROT_INV                    (1 << 2)    // Invert protect
#define PROT_BP0                    (1 << 3)    // Block protect bit 0
#define PROT_BP1                    (1 << 4)    // Block protect bit 1
#define PROT_BP2                    (1 << 5)    // Block protect bit 2
#define PROT_BRWD                   (1 << 7)    // Block register write disable

/* ECC Status ----------------------------------------------------------------*/
#define ECC_NO_ERROR                0x00
#define ECC_CORRECTED_1_4           0x01
#define ECC_CORRECTED_5             0x05
#define ECC_CORRECTED_6             0x06
#define ECC_CORRECTED_7             0x07
#define ECC_CORRECTED_8             0x03
#define ECC_UNCORRECTABLE           0x02

/* Timing Parameters (microseconds) ------------------------------------------*/
#define TIMING_RESET                500         // Reset time
#define TIMING_PAGE_READ            120         // Page read time (max)
#define TIMING_PAGE_PROGRAM         600         // Page program time (max)
#define TIMING_BLOCK_ERASE          10000       // Block erase time (max)
#define TIMING_POWER_ON             2000        // Power on delay

/* Error Codes ---------------------------------------------------------------*/
typedef enum {
    NAND_OK = 0,
    NAND_ERROR,
    NAND_TIMEOUT,
    NAND_BUSY,
    NAND_ECC_ERROR,
    NAND_PROGRAM_FAIL,
    NAND_ERASE_FAIL,
    NAND_BAD_BLOCK,
    NAND_INVALID_PARAM
} NAND_StatusTypeDef;

/* ECC Status ----------------------------------------------------------------*/
typedef enum {
    ECC_STATUS_NO_ERROR = 0,
    ECC_STATUS_CORRECTED,
    ECC_STATUS_UNCORRECTABLE
} NAND_ECC_StatusTypeDef;

/* NAND Device Info ----------------------------------------------------------*/
typedef struct {
    uint8_t manufacturer_id;        // Manufacturer ID (0xC8 for GigaDevice)
    uint8_t device_id;              // Device ID (0x92 for GD5F2GM7UE)
    bool ecc_enabled;               // Internal ECC status
    bool quad_enabled;              // Quad SPI status
} NAND_DeviceInfoTypeDef;

/* NAND Handle ---------------------------------------------------------------*/
typedef struct {
    /* SPI Hardware Interface */
    void *spi_handle;               // Pointer to SPI handle (platform specific)
    
    /* Device Information */
    NAND_DeviceInfoTypeDef device_info;
    
    /* Configuration */
    bool initialized;
    uint32_t timeout_ms;
    
    /* Statistics */
    uint32_t read_count;
    uint32_t write_count;
    uint32_t erase_count;
    uint32_t ecc_error_count;
} NAND_HandleTypeDef;

/* Function Prototypes -------------------------------------------------------*/

/* Initialization and Configuration */
NAND_StatusTypeDef NAND_Init(NAND_HandleTypeDef *hnand);
NAND_StatusTypeDef NAND_DeInit(NAND_HandleTypeDef *hnand);
NAND_StatusTypeDef NAND_Reset(NAND_HandleTypeDef *hnand);
NAND_StatusTypeDef NAND_ReadID(NAND_HandleTypeDef *hnand, uint8_t *manufacturer_id, uint8_t *device_id);

/* Feature and Status Operations */
NAND_StatusTypeDef NAND_GetFeature(NAND_HandleTypeDef *hnand, uint8_t reg_addr, uint8_t *value);
NAND_StatusTypeDef NAND_SetFeature(NAND_HandleTypeDef *hnand, uint8_t reg_addr, uint8_t value);
NAND_StatusTypeDef NAND_GetStatus(NAND_HandleTypeDef *hnand, uint8_t *status);
NAND_StatusTypeDef NAND_WaitReady(NAND_HandleTypeDef *hnand, uint32_t timeout_ms);

/* ECC Operations */
NAND_StatusTypeDef NAND_EnableECC(NAND_HandleTypeDef *hnand);
NAND_StatusTypeDef NAND_DisableECC(NAND_HandleTypeDef *hnand);
NAND_ECC_StatusTypeDef NAND_GetECCStatus(NAND_HandleTypeDef *hnand, uint8_t *error_bits);

/* Quad Mode Operations */
NAND_StatusTypeDef NAND_EnableQuad(NAND_HandleTypeDef *hnand);
NAND_StatusTypeDef NAND_DisableQuad(NAND_HandleTypeDef *hnand);

/* Block Protection */
NAND_StatusTypeDef NAND_UnlockAllBlocks(NAND_HandleTypeDef *hnand);
NAND_StatusTypeDef NAND_LockAllBlocks(NAND_HandleTypeDef *hnand);

/* Read Operations */
NAND_StatusTypeDef NAND_ReadPage(NAND_HandleTypeDef *hnand, uint32_t page_addr, 
                                 uint8_t *buffer, uint32_t size);
NAND_StatusTypeDef NAND_ReadPageSpare(NAND_HandleTypeDef *hnand, uint32_t page_addr, 
                                      uint8_t *main_buffer, uint8_t *spare_buffer);

/* Write Operations */
NAND_StatusTypeDef NAND_WritePage(NAND_HandleTypeDef *hnand, uint32_t page_addr, 
                                  const uint8_t *buffer, uint32_t size);
NAND_StatusTypeDef NAND_WritePageSpare(NAND_HandleTypeDef *hnand, uint32_t page_addr, 
                                       const uint8_t *main_buffer, const uint8_t *spare_buffer);

/* Erase Operations */
NAND_StatusTypeDef NAND_EraseBlock(NAND_HandleTypeDef *hnand, uint32_t block_addr);

/* Bad Block Management */
NAND_StatusTypeDef NAND_MarkBadBlock(NAND_HandleTypeDef *hnand, uint32_t block_addr);
bool NAND_IsBadBlock(NAND_HandleTypeDef *hnand, uint32_t block_addr);

/* Utility Functions */
uint32_t NAND_GetBlockAddress(uint32_t page_addr);
uint32_t NAND_GetPageAddress(uint32_t block_addr, uint32_t page_in_block);

/* Low-Level SPI Functions (Platform Specific - User Must Implement) */
void NAND_SPI_Init(void *spi_handle);
void NAND_SPI_CS_Low(void);
void NAND_SPI_CS_High(void);
uint8_t NAND_SPI_TransmitReceive(uint8_t data);
void NAND_SPI_Transmit(const uint8_t *buffer, uint32_t size);
void NAND_SPI_Receive(uint8_t *buffer, uint32_t size);
void NAND_Delay_us(uint32_t us);
void NAND_Delay_ms(uint32_t ms);

#ifdef __cplusplus
}
#endif

#endif /* __GD5F2GM7_NAND_H */

flash.c

/**
 ******************************************************************************
 * @file    gd5f2gm7_nand.c
 * @brief   Implementation of GD5F2GM7UEYIGR SPI-NAND Flash driver
 * @author  Driver for GD32F407 Platform
 * @date    2026-02-09
 ******************************************************************************
 */

#include "gd5f2gm7_nand.h"
#include <string.h>

/* Private Functions ---------------------------------------------------------*/

/**
 * @brief  Send command to NAND Flash
 * @param  cmd: Command byte
 */
static void NAND_SendCommand(uint8_t cmd)
{
    NAND_SPI_CS_Low();
    NAND_SPI_TransmitReceive(cmd);
}

/**
 * @brief  Send command with 24-bit address
 * @param  cmd: Command byte
 * @param  addr: 24-bit address (row address)
 */
static void NAND_SendCommandWithRowAddr(uint8_t cmd, uint32_t addr)
{
    NAND_SPI_CS_Low();
    NAND_SPI_TransmitReceive(cmd);
    NAND_SPI_TransmitReceive((addr >> 16) & 0xFF);  // A23-A16
    NAND_SPI_TransmitReceive((addr >> 8) & 0xFF);   // A15-A8
    NAND_SPI_TransmitReceive(addr & 0xFF);          // A7-A0
}

/**
 * @brief  Send command with 16-bit column address
 * @param  cmd: Command byte
 * @param  addr: 16-bit column address
 */
static void NAND_SendCommandWithColAddr(uint8_t cmd, uint16_t addr)
{
    NAND_SPI_CS_Low();
    NAND_SPI_TransmitReceive(cmd);
    NAND_SPI_TransmitReceive((addr >> 8) & 0xFF);   // A15-A8
    NAND_SPI_TransmitReceive(addr & 0xFF);          // A7-A0
}

/**
 * @brief  Write Enable command
 * @retval NAND status
 */
static NAND_StatusTypeDef NAND_WriteEnable(NAND_HandleTypeDef *hnand)
{
    NAND_SendCommand(CMD_WRITE_ENABLE);
    NAND_SPI_CS_High();
    
    /* Verify WEL bit is set */
    uint8_t status;
    NAND_StatusTypeDef ret = NAND_GetStatus(hnand, &status);
    if (ret != NAND_OK) {
        return ret;
    }
    
    if (!(status & STATUS_WEL)) {
        return NAND_ERROR;
    }
    
    return NAND_OK;
}

/* Public Functions ----------------------------------------------------------*/

/**
 * @brief  Initialize NAND Flash device
 * @param  hnand: Pointer to NAND handle
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_Init(NAND_HandleTypeDef *hnand)
{
    if (hnand == NULL) {
        return NAND_INVALID_PARAM;
    }
    
    /* Initialize SPI interface */
    NAND_SPI_Init(hnand->spi_handle);
    
    /* Default timeout */
    if (hnand->timeout_ms == 0) {
        hnand->timeout_ms = 1000;
    }
    
    /* Power-on delay */
    NAND_Delay_ms(TIMING_POWER_ON / 1000);
    
    /* Reset device */
    NAND_StatusTypeDef status = NAND_Reset(hnand);
    if (status != NAND_OK) {
        return status;
    }
    
    /* Read device ID */
    uint8_t mfr_id, dev_id;
    status = NAND_ReadID(hnand, &mfr_id, &dev_id);
    if (status != NAND_OK) {
        return status;
    }
    
    /* Verify device ID */
    if (mfr_id != 0xC8 || dev_id != 0x92) {
        return NAND_ERROR;
    }
    
    hnand->device_info.manufacturer_id = mfr_id;
    hnand->device_info.device_id = dev_id;
    
    /* Check ECC status */
    uint8_t feature;
    status = NAND_GetFeature(hnand, REG_FEATURE, &feature);
    if (status != NAND_OK) {
        return status;
    }
    
    hnand->device_info.ecc_enabled = (feature & FEATURE_ECC_EN) ? true : false;
    hnand->device_info.quad_enabled = (feature & FEATURE_QE) ? true : false;
    
    /* Unlock all blocks */
    status = NAND_UnlockAllBlocks(hnand);
    if (status != NAND_OK) {
        return status;
    }
    
    /* Initialize statistics */
    hnand->read_count = 0;
    hnand->write_count = 0;
    hnand->erase_count = 0;
    hnand->ecc_error_count = 0;
    
    hnand->initialized = true;
    
    return NAND_OK;
}

/**
 * @brief  De-initialize NAND Flash device
 * @param  hnand: Pointer to NAND handle
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_DeInit(NAND_HandleTypeDef *hnand)
{
    if (hnand == NULL || !hnand->initialized) {
        return NAND_INVALID_PARAM;
    }
    
    hnand->initialized = false;
    return NAND_OK;
}

/**
 * @brief  Reset NAND Flash device
 * @param  hnand: Pointer to NAND handle
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_Reset(NAND_HandleTypeDef *hnand)
{
    NAND_SendCommand(CMD_RESET);
    NAND_SPI_CS_High();
    
    /* Wait for reset to complete */
    NAND_Delay_us(TIMING_RESET);
    
    return NAND_WaitReady(hnand, hnand->timeout_ms);
}

/**
 * @brief  Read device ID
 * @param  hnand: Pointer to NAND handle
 * @param  manufacturer_id: Pointer to store manufacturer ID
 * @param  device_id: Pointer to store device ID
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_ReadID(NAND_HandleTypeDef *hnand, uint8_t *manufacturer_id, uint8_t *device_id)
{
    NAND_SendCommand(CMD_READ_ID);
    
    /* Dummy byte */
    NAND_SPI_TransmitReceive(0x00);
    
    /* Read manufacturer ID and device ID */
    *manufacturer_id = NAND_SPI_TransmitReceive(0x00);
    *device_id = NAND_SPI_TransmitReceive(0x00);
    
    NAND_SPI_CS_High();
    
    return NAND_OK;
}

/**
 * @brief  Get feature register value
 * @param  hnand: Pointer to NAND handle
 * @param  reg_addr: Register address
 * @param  value: Pointer to store register value
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_GetFeature(NAND_HandleTypeDef *hnand, uint8_t reg_addr, uint8_t *value)
{
    NAND_SendCommand(CMD_GET_FEATURES);
    NAND_SPI_TransmitReceive(reg_addr);
    *value = NAND_SPI_TransmitReceive(0x00);
    NAND_SPI_CS_High();
    
    return NAND_OK;
}

/**
 * @brief  Set feature register value
 * @param  hnand: Pointer to NAND handle
 * @param  reg_addr: Register address
 * @param  value: Register value to set
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_SetFeature(NAND_HandleTypeDef *hnand, uint8_t reg_addr, uint8_t value)
{
    NAND_SendCommand(CMD_SET_FEATURES);
    NAND_SPI_TransmitReceive(reg_addr);
    NAND_SPI_TransmitReceive(value);
    NAND_SPI_CS_High();
    
    return NAND_OK;
}

/**
 * @brief  Get status register value
 * @param  hnand: Pointer to NAND handle
 * @param  status: Pointer to store status value
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_GetStatus(NAND_HandleTypeDef *hnand, uint8_t *status)
{
    return NAND_GetFeature(hnand, REG_STATUS, status);
}

/**
 * @brief  Wait for device ready
 * @param  hnand: Pointer to NAND handle
 * @param  timeout_ms: Timeout in milliseconds
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_WaitReady(NAND_HandleTypeDef *hnand, uint32_t timeout_ms)
{
    uint32_t start_time = 0;  // Platform specific tick counter
    uint8_t status;
    
    while (1) {
        NAND_StatusTypeDef ret = NAND_GetStatus(hnand, &status);
        if (ret != NAND_OK) {
            return ret;
        }
        
        if (!(status & STATUS_OIP)) {
            return NAND_OK;  // Device ready
        }
        
        /* Timeout check */
        NAND_Delay_ms(1);
        if (++start_time >= timeout_ms) {
            return NAND_TIMEOUT;
        }
    }
}

/**
 * @brief  Enable internal ECC
 * @param  hnand: Pointer to NAND handle
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_EnableECC(NAND_HandleTypeDef *hnand)
{
    uint8_t feature;
    NAND_StatusTypeDef status = NAND_GetFeature(hnand, REG_FEATURE, &feature);
    if (status != NAND_OK) {
        return status;
    }
    
    feature |= FEATURE_ECC_EN;
    status = NAND_SetFeature(hnand, REG_FEATURE, feature);
    if (status == NAND_OK) {
        hnand->device_info.ecc_enabled = true;
    }
    
    return status;
}

/**
 * @brief  Disable internal ECC
 * @param  hnand: Pointer to NAND handle
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_DisableECC(NAND_HandleTypeDef *hnand)
{
    uint8_t feature;
    NAND_StatusTypeDef status = NAND_GetFeature(hnand, REG_FEATURE, &feature);
    if (status != NAND_OK) {
        return status;
    }
    
    feature &= ~FEATURE_ECC_EN;
    status = NAND_SetFeature(hnand, REG_FEATURE, feature);
    if (status == NAND_OK) {
        hnand->device_info.ecc_enabled = false;
    }
    
    return status;
}

/**
 * @brief  Get ECC status after read operation
 * @param  hnand: Pointer to NAND handle
 * @param  error_bits: Pointer to store number of corrected bits
 * @retval ECC status
 */
NAND_ECC_StatusTypeDef NAND_GetECCStatus(NAND_HandleTypeDef *hnand, uint8_t *error_bits)
{
    uint8_t status, status_ext;
    
    NAND_GetFeature(hnand, REG_STATUS, &status);
    NAND_GetFeature(hnand, REG_STATUS_EXT, &status_ext);
    
    uint8_t eccs = (status >> 4) & 0x03;    // ECCS1:ECCS0
    uint8_t eccse = (status_ext >> 4) & 0x03; // ECCSE1:ECCSE0
    
    *error_bits = 0;
    
    if (eccs == 0x00) {
        return ECC_STATUS_NO_ERROR;
    } else if (eccs == 0x01) {
        /* Correctable error */
        if (eccse == 0x00) *error_bits = 4;      // 1-4 bits
        else if (eccse == 0x01) *error_bits = 5;
        else if (eccse == 0x02) *error_bits = 6;
        else if (eccse == 0x03) *error_bits = 7;
        return ECC_STATUS_CORRECTED;
    } else if (eccs == 0x03) {
        *error_bits = 8;
        return ECC_STATUS_CORRECTED;
    } else if (eccs == 0x02) {
        return ECC_STATUS_UNCORRECTABLE;
    }
    
    return ECC_STATUS_NO_ERROR;
}

/**
 * @brief  Enable Quad SPI mode
 * @param  hnand: Pointer to NAND handle
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_EnableQuad(NAND_HandleTypeDef *hnand)
{
    uint8_t feature;
    NAND_StatusTypeDef status = NAND_GetFeature(hnand, REG_FEATURE, &feature);
    if (status != NAND_OK) {
        return status;
    }
    
    feature |= FEATURE_QE;
    status = NAND_SetFeature(hnand, REG_FEATURE, feature);
    if (status == NAND_OK) {
        hnand->device_info.quad_enabled = true;
    }
    
    return status;
}

/**
 * @brief  Disable Quad SPI mode
 * @param  hnand: Pointer to NAND handle
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_DisableQuad(NAND_HandleTypeDef *hnand)
{
    uint8_t feature;
    NAND_StatusTypeDef status = NAND_GetFeature(hnand, REG_FEATURE, &feature);
    if (status != NAND_OK) {
        return status;
    }
    
    feature &= ~FEATURE_QE;
    status = NAND_SetFeature(hnand, REG_FEATURE, feature);
    if (status == NAND_OK) {
        hnand->device_info.quad_enabled = false;
    }
    
    return status;
}

/**
 * @brief  Unlock all blocks
 * @param  hnand: Pointer to NAND handle
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_UnlockAllBlocks(NAND_HandleTypeDef *hnand)
{
    /* Set BP[2:0] = 000 to unlock all blocks */
    return NAND_SetFeature(hnand, REG_PROTECTION, 0x00);
}

/**
 * @brief  Lock all blocks
 * @param  hnand: Pointer to NAND handle
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_LockAllBlocks(NAND_HandleTypeDef *hnand)
{
    /* Set BP[2:0] = 111 to lock all blocks */
    return NAND_SetFeature(hnand, REG_PROTECTION, 0x38);
}

/**
 * @brief  Read a page from NAND Flash
 * @param  hnand: Pointer to NAND handle
 * @param  page_addr: Page address (0 to 131071)
 * @param  buffer: Buffer to store read data
 * @param  size: Number of bytes to read (max NAND_PAGE_SIZE)
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_ReadPage(NAND_HandleTypeDef *hnand, uint32_t page_addr, 
                                 uint8_t *buffer, uint32_t size)
{
    if (page_addr >= (NAND_TOTAL_BLOCKS * NAND_PAGES_PER_BLOCK)) {
        return NAND_INVALID_PARAM;
    }
    
    if (size > NAND_PAGE_SIZE) {
        size = NAND_PAGE_SIZE;
    }
    
    /* Step 1: Page Read to Cache (13h) */
    NAND_SendCommandWithRowAddr(CMD_PAGE_READ, page_addr);
    NAND_SPI_CS_High();
    
    /* Wait for read operation to complete */
    NAND_StatusTypeDef status = NAND_WaitReady(hnand, TIMING_PAGE_READ / 1000 + 10);
    if (status != NAND_OK) {
        return status;
    }
    
    /* Check ECC status if enabled */
    if (hnand->device_info.ecc_enabled) {
        uint8_t error_bits;
        NAND_ECC_StatusTypeDef ecc_status = NAND_GetECCStatus(hnand, &error_bits);
        
        if (ecc_status == ECC_STATUS_UNCORRECTABLE) {
            hnand->ecc_error_count++;
            return NAND_ECC_ERROR;
        }
    }
    
    /* Step 2: Read from Cache (03h or 0Bh) */
    NAND_SendCommandWithColAddr(CMD_READ_CACHE, 0x0000);
    
    /* Dummy byte for 0Bh command would go here */
    NAND_SPI_TransmitReceive(0x00);
    
    /* Read data */
    NAND_SPI_Receive(buffer, size);
    NAND_SPI_CS_High();
    
    hnand->read_count++;
    
    return NAND_OK;
}

/**
 * @brief  Read a page including spare area
 * @param  hnand: Pointer to NAND handle
 * @param  page_addr: Page address
 * @param  main_buffer: Buffer for main area (2KB)
 * @param  spare_buffer: Buffer for spare area (64B or 128B)
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_ReadPageSpare(NAND_HandleTypeDef *hnand, uint32_t page_addr,
                                      uint8_t *main_buffer, uint8_t *spare_buffer)
{
    /* Read main area */
    NAND_StatusTypeDef status = NAND_ReadPage(hnand, page_addr, main_buffer, NAND_PAGE_SIZE);
    if (status != NAND_OK) {
        return status;
    }
    
    /* Read spare area */
    NAND_SendCommandWithColAddr(CMD_READ_CACHE, NAND_PAGE_SIZE);
    NAND_SPI_TransmitReceive(0x00);  // Dummy
    
    uint32_t spare_size = hnand->device_info.ecc_enabled ? NAND_SPARE_SIZE_ECC_ON : NAND_SPARE_SIZE_ECC_OFF;
    NAND_SPI_Receive(spare_buffer, spare_size);
    NAND_SPI_CS_High();
    
    return NAND_OK;
}

/**
 * @brief  Write a page to NAND Flash
 * @param  hnand: Pointer to NAND handle
 * @param  page_addr: Page address
 * @param  buffer: Buffer containing data to write
 * @param  size: Number of bytes to write (max NAND_PAGE_SIZE)
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_WritePage(NAND_HandleTypeDef *hnand, uint32_t page_addr,
                                  const uint8_t *buffer, uint32_t size)
{
    if (page_addr >= (NAND_TOTAL_BLOCKS * NAND_PAGES_PER_BLOCK)) {
        return NAND_INVALID_PARAM;
    }
    
    if (size > NAND_PAGE_SIZE) {
        size = NAND_PAGE_SIZE;
    }
    
    /* Step 1: Write Enable */
    NAND_StatusTypeDef status = NAND_WriteEnable(hnand);
    if (status != NAND_OK) {
        return status;
    }
    
    /* Step 2: Program Load (02h) */
    NAND_SendCommandWithColAddr(CMD_PROGRAM_LOAD, 0x0000);
    
    /* Write data */
    NAND_SPI_Transmit(buffer, size);
    NAND_SPI_CS_High();
    
    /* Step 3: Program Execute (10h) */
    NAND_SendCommandWithRowAddr(CMD_PROGRAM_EXECUTE, page_addr);
    NAND_SPI_CS_High();
    
    /* Wait for program operation to complete */
    status = NAND_WaitReady(hnand, TIMING_PAGE_PROGRAM / 1000 + 10);
    if (status != NAND_OK) {
        return status;
    }
    
    /* Check program status */
    uint8_t reg_status;
    NAND_GetStatus(hnand, &reg_status);
    
    if (reg_status & STATUS_P_FAIL) {
        return NAND_PROGRAM_FAIL;
    }
    
    hnand->write_count++;
    
    return NAND_OK;
}

/**
 * @brief  Write a page including spare area
 * @param  hnand: Pointer to NAND handle
 * @param  page_addr: Page address
 * @param  main_buffer: Buffer for main area (2KB)
 * @param  spare_buffer: Buffer for spare area (64B when ECC on)
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_WritePageSpare(NAND_HandleTypeDef *hnand, uint32_t page_addr,
                                       const uint8_t *main_buffer, const uint8_t *spare_buffer)
{
    NAND_StatusTypeDef status = NAND_WriteEnable(hnand);
    if (status != NAND_OK) {
        return status;
    }
    
    /* Program Load - Main Area */
    NAND_SendCommandWithColAddr(CMD_PROGRAM_LOAD, 0x0000);
    NAND_SPI_Transmit(main_buffer, NAND_PAGE_SIZE);
    NAND_SPI_CS_High();
    
    /* Program Load Random Data - Spare Area */
    if (spare_buffer != NULL && hnand->device_info.ecc_enabled) {
        NAND_SendCommandWithColAddr(CMD_PROGRAM_LOAD_RANDOM, NAND_PAGE_SIZE);
        NAND_SPI_Transmit(spare_buffer, NAND_SPARE_SIZE_ECC_ON);
        NAND_SPI_CS_High();
    }
    
    /* Program Execute */
    NAND_SendCommandWithRowAddr(CMD_PROGRAM_EXECUTE, page_addr);
    NAND_SPI_CS_High();
    
    status = NAND_WaitReady(hnand, TIMING_PAGE_PROGRAM / 1000 + 10);
    if (status != NAND_OK) {
        return status;
    }
    
    uint8_t reg_status;
    NAND_GetStatus(hnand, &reg_status);
    
    if (reg_status & STATUS_P_FAIL) {
        return NAND_PROGRAM_FAIL;
    }
    
    hnand->write_count++;
    
    return NAND_OK;
}

/**
 * @brief  Erase a block
 * @param  hnand: Pointer to NAND handle
 * @param  block_addr: Block address (0 to 2047)
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_EraseBlock(NAND_HandleTypeDef *hnand, uint32_t block_addr)
{
    if (block_addr >= NAND_TOTAL_BLOCKS) {
        return NAND_INVALID_PARAM;
    }
    
    /* Check if it's a bad block */
    if (NAND_IsBadBlock(hnand, block_addr)) {
        return NAND_BAD_BLOCK;
    }
    
    /* Write Enable */
    NAND_StatusTypeDef status = NAND_WriteEnable(hnand);
    if (status != NAND_OK) {
        return status;
    }
    
    /* Block Erase (D8h) - Use page address of first page in block */
    uint32_t page_addr = block_addr * NAND_PAGES_PER_BLOCK;
    NAND_SendCommandWithRowAddr(CMD_BLOCK_ERASE, page_addr);
    NAND_SPI_CS_High();
    
    /* Wait for erase operation to complete */
    status = NAND_WaitReady(hnand, TIMING_BLOCK_ERASE / 1000 + 100);
    if (status != NAND_OK) {
        return status;
    }
    
    /* Check erase status */
    uint8_t reg_status;
    NAND_GetStatus(hnand, &reg_status);
    
    if (reg_status & STATUS_E_FAIL) {
        return NAND_ERASE_FAIL;
    }
    
    hnand->erase_count++;
    
    return NAND_OK;
}

/**
 * @brief  Mark a block as bad
 * @param  hnand: Pointer to NAND handle
 * @param  block_addr: Block address
 * @retval NAND status
 */
NAND_StatusTypeDef NAND_MarkBadBlock(NAND_HandleTypeDef *hnand, uint32_t block_addr)
{
    if (block_addr >= NAND_TOTAL_BLOCKS) {
        return NAND_INVALID_PARAM;
    }
    
    /* Write bad block marker (0x00) to first byte of spare area */
    uint32_t page_addr = block_addr * NAND_PAGES_PER_BLOCK;
    
    uint8_t marker = 0x00;
    
    NAND_StatusTypeDef status = NAND_WriteEnable(hnand);
    if (status != NAND_OK) {
        return status;
    }
    
    /* Write to spare area byte 0 (address 2048) */
    NAND_SendCommandWithColAddr(CMD_PROGRAM_LOAD, NAND_PAGE_SIZE);
    NAND_SPI_Transmit(&marker, 1);
    NAND_SPI_CS_High();
    
    NAND_SendCommandWithRowAddr(CMD_PROGRAM_EXECUTE, page_addr);
    NAND_SPI_CS_High();
    
    return NAND_WaitReady(hnand, TIMING_PAGE_PROGRAM / 1000 + 10);
}

/**
 * @brief  Check if a block is bad
 * @param  hnand: Pointer to NAND handle
 * @param  block_addr: Block address
 * @retval true if bad block, false otherwise
 */
bool NAND_IsBadBlock(NAND_HandleTypeDef *hnand, uint32_t block_addr)
{
    if (block_addr >= NAND_TOTAL_BLOCKS) {
        return true;
    }
    
    /* Read first byte of spare area */
    uint32_t page_addr = block_addr * NAND_PAGES_PER_BLOCK;
    
    /* Page Read to Cache */
    NAND_SendCommandWithRowAddr(CMD_PAGE_READ, page_addr);
    NAND_SPI_CS_High();
    
    NAND_WaitReady(hnand, TIMING_PAGE_READ / 1000 + 10);
    
    /* Read spare area first byte (address 2048) */
    NAND_SendCommandWithColAddr(CMD_READ_CACHE, NAND_PAGE_SIZE);
    NAND_SPI_TransmitReceive(0x00);  // Dummy
    
    uint8_t marker = NAND_SPI_TransmitReceive(0x00);
    NAND_SPI_CS_High();
    
    /* Bad block if marker is not 0xFF */
    return (marker != 0xFF);
}

/**
 * @brief  Get block address from page address
 * @param  page_addr: Page address
 * @retval Block address
 */
uint32_t NAND_GetBlockAddress(uint32_t page_addr)
{
    return page_addr / NAND_PAGES_PER_BLOCK;
}

/**
 * @brief  Get page address from block and page within block
 * @param  block_addr: Block address
 * @param  page_in_block: Page within block (0-63)
 * @retval Page address
 */
uint32_t NAND_GetPageAddress(uint32_t block_addr, uint32_t page_in_block)
{
    return (block_addr * NAND_PAGES_PER_BLOCK) + page_in_block;
}

主函数的调用:

main.c

/**
 ******************************************************************************
 * @file    main.c
 * @brief   Example application for GD5F2GM7UEYIGR NAND Flash
 * @author  Driver for GD32F407 Platform
 * @date    2026-02-09
 ******************************************************************************
 * @attention
 * 
 * This example demonstrates:
 * - NAND Flash initialization
 * - Reading device ID
 * - Erasing blocks
 * - Writing pages
 * - Reading pages with ECC
 * - Bad block management
 * 
 ******************************************************************************
 */

#include "gd32f4xx.h"
#include "gd5f2gm7_nand.h"
#include <stdio.h>
#include <string.h>

/* Private variables ---------------------------------------------------------*/
NAND_HandleTypeDef hnand;
uint8_t write_buffer[NAND_PAGE_SIZE];
uint8_t read_buffer[NAND_PAGE_SIZE];

/* Private function prototypes -----------------------------------------------*/
void system_init(void);
void uart_init(void);
void test_nand_basic(void);
void test_nand_ecc(void);
void test_nand_bad_block(void);

/**
 * @brief  Main program
 */
int main(void)
{
    /* Initialize system */
    system_init();
    uart_init();
    
    printf("\r\n");
    printf("========================================\r\n");
    printf("GD5F2GM7UEYIGR NAND Flash Test\r\n");
    printf("GD32F407 Platform\r\n");
    printf("========================================\r\n\r\n");
    
    /* Initialize NAND Flash */
    hnand.spi_handle = NULL;
    hnand.timeout_ms = 1000;
    
    printf("Initializing NAND Flash...\r\n");
    NAND_StatusTypeDef status = NAND_Init(&hnand);
    
    if (status != NAND_OK) {
        printf("ERROR: NAND initialization failed! Status: %d\r\n", status);
        while (1);
    }
    
    printf("NAND Flash initialized successfully!\r\n");
    printf("Manufacturer ID: 0x%02X\r\n", hnand.device_info.manufacturer_id);
    printf("Device ID: 0x%02X\r\n", hnand.device_info.device_id);
    printf("ECC Enabled: %s\r\n", hnand.device_info.ecc_enabled ? "Yes" : "No");
    printf("Quad Enabled: %s\r\n\r\n", hnand.device_info.quad_enabled ? "Yes" : "No");
    
    /* Run tests */
    test_nand_basic();
    test_nand_ecc();
    test_nand_bad_block();
    
    printf("\r\n========================================\r\n");
    printf("All tests completed!\r\n");
    printf("Read count: %lu\r\n", hnand.read_count);
    printf("Write count: %lu\r\n", hnand.write_count);
    printf("Erase count: %lu\r\n", hnand.erase_count);
    printf("ECC errors: %lu\r\n", hnand.ecc_error_count);
    printf("========================================\r\n");
    
    while (1) {
        /* Main loop */
    }
}

/**
 * @brief  Test basic NAND operations
 */
void test_nand_basic(void)
{
    printf("========================================\r\n");
    printf("Test 1: Basic Read/Write\r\n");
    printf("========================================\r\n");
    
    uint32_t test_block = 10;
    uint32_t test_page = NAND_GetPageAddress(test_block, 0);
    
    /* Prepare test data */
    for (uint32_t i = 0; i < NAND_PAGE_SIZE; i++) {
        write_buffer[i] = (uint8_t)(i & 0xFF);
    }
    
    printf("Erasing block %lu...\r\n", test_block);
    NAND_StatusTypeDef status = NAND_EraseBlock(&hnand, test_block);
    if (status != NAND_OK) {
        printf("ERROR: Erase failed! Status: %d\r\n", status);
        return;
    }
    printf("Block erased successfully\r\n");
    
    printf("Writing page %lu...\r\n", test_page);
    status = NAND_WritePage(&hnand, test_page, write_buffer, NAND_PAGE_SIZE);
    if (status != NAND_OK) {
        printf("ERROR: Write failed! Status: %d\r\n", status);
        return;
    }
    printf("Page written successfully\r\n");
    
    printf("Reading page %lu...\r\n", test_page);
    memset(read_buffer, 0, NAND_PAGE_SIZE);
    status = NAND_ReadPage(&hnand, test_page, read_buffer, NAND_PAGE_SIZE);
    if (status != NAND_OK) {
        printf("ERROR: Read failed! Status: %d\r\n", status);
        return;
    }
    printf("Page read successfully\r\n");
    
    printf("Verifying data...\r\n");
    bool match = true;
    for (uint32_t i = 0; i < NAND_PAGE_SIZE; i++) {
        if (read_buffer[i] != write_buffer[i]) {
            printf("ERROR: Data mismatch at offset %lu: expected 0x%02X, got 0x%02X\r\n",
                   i, write_buffer[i], read_buffer[i]);
            match = false;
            break;
        }
    }
    
    if (match) {
        printf("Data verification PASSED!\r\n");
    } else {
        printf("Data verification FAILED!\r\n");
    }
    printf("\r\n");
}

/**
 * @brief  Test ECC functionality
 */
void test_nand_ecc(void)
{
    printf("========================================\r\n");
    printf("Test 2: ECC Status Check\r\n");
    printf("========================================\r\n");
    
    uint32_t test_block = 11;
    uint32_t test_page = NAND_GetPageAddress(test_block, 0);
    
    /* Fill with pattern */
    for (uint32_t i = 0; i < NAND_PAGE_SIZE; i++) {
        write_buffer[i] = 0xAA;
    }
    
    printf("Writing test pattern with ECC enabled...\r\n");
    NAND_EraseBlock(&hnand, test_block);
    NAND_StatusTypeDef status = NAND_WritePage(&hnand, test_page, write_buffer, NAND_PAGE_SIZE);
    
    if (status != NAND_OK) {
        printf("ERROR: Write failed! Status: %d\r\n", status);
        return;
    }
    
    printf("Reading back with ECC check...\r\n");
    status = NAND_ReadPage(&hnand, test_page, read_buffer, NAND_PAGE_SIZE);
    
    if (status == NAND_OK) {
        uint8_t error_bits;
        NAND_ECC_StatusTypeDef ecc_status = NAND_GetECCStatus(&hnand, &error_bits);
        
        switch (ecc_status) {
            case ECC_STATUS_NO_ERROR:
                printf("ECC Status: No errors detected\r\n");
                break;
            case ECC_STATUS_CORRECTED:
                printf("ECC Status: %d bit errors corrected\r\n", error_bits);
                break;
            case ECC_STATUS_UNCORRECTABLE:
                printf("ECC Status: Uncorrectable errors!\r\n");
                break;
        }
    } else {
        printf("ERROR: Read failed! Status: %d\r\n", status);
    }
    printf("\r\n");
}

/**
 * @brief  Test bad block management
 */
void test_nand_bad_block(void)
{
    printf("========================================\r\n");
    printf("Test 3: Bad Block Management\r\n");
    printf("========================================\r\n");
    
    uint32_t test_block = 100;
    
    printf("Checking if block %lu is bad...\r\n", test_block);
    bool is_bad = NAND_IsBadBlock(&hnand, test_block);
    printf("Block %lu is %s\r\n", test_block, is_bad ? "BAD" : "GOOD");
    
    if (!is_bad) {
        printf("Marking block %lu as bad...\r\n", test_block);
        NAND_StatusTypeDef status = NAND_MarkBadBlock(&hnand, test_block);
        if (status == NAND_OK) {
            printf("Block marked as bad successfully\r\n");
            
            printf("Verifying bad block mark...\r\n");
            is_bad = NAND_IsBadBlock(&hnand, test_block);
            printf("Block %lu is now %s\r\n", test_block, is_bad ? "BAD" : "GOOD");
        } else {
            printf("ERROR: Failed to mark block as bad\r\n");
        }
    }
    printf("\r\n");
}

/**
 * @brief  System initialization
 */
void system_init(void)
{
    /* Configure system clock to 168MHz */
    SystemInit();
    
    /* Enable SysTick for 1ms interrupts */
    SysTick_Config(SystemCoreClock / 1000);
    
    /* Enable peripheral clocks */
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_GPIOC);
}

/**
 * @brief  UART initialization for printf
 */
void uart_init(void)
{
    /* Enable USART clock */
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_USART0);
    
    /* Configure USART Tx/Rx pins */
    gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_9);
    gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_10);
    
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
    
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
    
    /* USART configuration */
    usart_deinit(USART0);
    usart_baudrate_set(USART0, 115200U);
    usart_word_length_set(USART0, USART_WL_8BIT);
    usart_stop_bit_set(USART0, USART_STB_1BIT);
    usart_parity_config(USART0, USART_PM_NONE);
    usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE);
    usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE);
    usart_receive_config(USART0, USART_RECEIVE_ENABLE);
    usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
    usart_enable(USART0);
}

/**
 * @brief  Retarget printf to UART
 */
int fputc(int ch, FILE *f)
{
    usart_data_transmit(USART0, (uint8_t)ch);
    while (RESET == usart_flag_get(USART0, USART_FLAG_TBE));
    return ch;
}

/**
 * @brief  SysTick interrupt handler
 */
void SysTick_Handler(void)
{
    NAND_SysTick_Handler();
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐