基于GD32F470VIT6的spi NAND flash驱动(GD5F2GM7)
本文所选用的flash为兆易创新的GD5F2GM7 NAND flash采用标准SPI接口。NAND flash的驱动,包括.c.h结合使用手册发指令即可。
·
本文所选用的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, ®_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, ®_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, ®_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();
}
更多推荐



所有评论(0)