/** ****************************************************************************** * @file gd5f2gq5ue.c * @brief GD5F2GQ5UE SPI NAND Flash 驱动实现 ****************************************************************************** */ #include "gd5f2gq5ue.h" #include 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; } /** * @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 超时 */ 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(); while (1) { GD5F_CS_LOW(); gd5f_spi_tx(&cmd, 1); gd5f_spi_tx(&addr, 1); gd5f_spi_rx(&status, 1); GD5F_CS_HIGH(); if ((status & GD5F_STATUS_OIP) == 0) { return GD5F_OK; } if ((HAL_GetTick() - tickstart) >= timeout_ms) { return GD5F_BUSY_TIMEOUT; } } } /** * @brief 写使能 */ static int gd5f_write_enable(void) { uint8_t cmd = GD5F_CMD_WRITE_ENABLE; GD5F_CS_LOW(); gd5f_spi_tx(&cmd, 1); GD5F_CS_HIGH(); return GD5F_OK; } /** * @brief 读取状态寄存器 */ static int gd5f_read_status(uint8_t *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); GD5F_CS_HIGH(); return GD5F_OK; } /** * @brief 页读取到缓存(Page Read to Cache) * @param page_addr: 页地址(行地址,包含 block + page) */ 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); GD5F_CS_HIGH(); return gd5f_wait_busy(100); } /** * @brief 从缓存读取数据(Read from Cache) * @param column: 列地址(页内偏移) * @param buf: 数据缓冲区 * @param size: 读取字节数 */ static int gd5f_read_from_cache(uint16_t column, uint8_t *buf, size_t size) { uint8_t cmd[5]; cmd[0] = GD5F_CMD_READ_FROM_CACHE; cmd[1] = (column >> 8) & 0xFF; cmd[2] = column & 0xFF; cmd[3] = 0x00; /* dummy byte */ GD5F_CS_LOW(); gd5f_spi_tx(cmd, 4); gd5f_spi_rx(buf, size); GD5F_CS_HIGH(); return GD5F_OK; } /** * @brief 页编程(从缓存写入阵列) * @param page_addr: 页地址 * @param column: 列地址 * @param buf: 数据缓冲区 * @param size: 写入字节数 */ static int gd5f_page_program(uint32_t page_addr, uint16_t column, const uint8_t *buf, size_t size) { int ret; uint8_t cmd[4]; /* 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); 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); GD5F_CS_HIGH(); /* 4. 等待完成 */ ret = gd5f_wait_busy(1000); 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) */ static int gd5f_block_erase(uint32_t block_addr) { int ret; uint8_t cmd[4]; /* 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; GD5F_CS_LOW(); gd5f_spi_tx(cmd, 4); GD5F_CS_HIGH(); /* 3. 等待完成(擦除较慢) */ ret = gd5f_wait_busy(5000); 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; } /* ===================== 公共接口实现 ===================== */ int gd5f2gq5ue_init(void) { int ret; uint8_t mid, did; /* 确保控制引脚处于非激活状态 */ GD5F_CS_HIGH(); GD5F_WP_HIGH(); GD5F_HOLD_HIGH(); /* 小延时等待上电稳定 */ HAL_Delay(10); /* 复位芯片 */ ret = gd5f2gq5ue_reset(); if (ret != GD5F_OK) return ret; HAL_Delay(5); /* 读取 ID 验证通信 */ ret = gd5f2gq5ue_read_id(&mid, &did); if (ret != GD5F_OK) return ret; if (mid != GD5F_MANUFACTURER_ID || did != GD5F_DEVICE_ID) { return GD5F_ID_MISMATCH; } return GD5F_OK; } int gd5f2gq5ue_read_id(uint8_t *mid, uint8_t *did) { uint8_t cmd = GD5F_CMD_READ_ID; uint8_t id_buf[3]; GD5F_CS_LOW(); gd5f_spi_tx(&cmd, 1); gd5f_spi_rx(id_buf, 3); /* dummy + MID + DID */ GD5F_CS_HIGH(); *mid = id_buf[1]; /* id_buf[0] = dummy */ *did = id_buf[2]; return GD5F_OK; } int gd5f2gq5ue_read(long offset, uint8_t *buf, size_t size) { int ret; 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 */ ret = gd5f_page_read(page_addr); if (ret != GD5F_OK) return ret; /* Read from Cache */ ret = gd5f_read_from_cache(column, buf, bytes); if (ret != GD5F_OK) return ret; offset += bytes; buf += bytes; size -= bytes; } return GD5F_OK; } int gd5f2gq5ue_write(long offset, const uint8_t *buf, size_t size) { int ret; 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; offset += bytes; buf += bytes; size -= bytes; } return GD5F_OK; } int gd5f2gq5ue_erase(long offset, size_t size) { int ret; /* 擦除起始地址必须块对齐 */ 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; offset += GD5F_BLOCK_SIZE; size -= GD5F_BLOCK_SIZE; } return GD5F_OK; } int gd5f2gq5ue_reset(void) { uint8_t cmd = GD5F_CMD_RESET; GD5F_CS_LOW(); gd5f_spi_tx(&cmd, 1); GD5F_CS_HIGH(); HAL_Delay(5); return GD5F_OK; }