Files
STM32F4-Base/Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c
2026-07-19 15:27:49 +08:00

448 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 妯″潡鍚嶇О锛欸D5F2GQ5UE SPI NAND Flash 椹卞姩
* 妯″潡鍔熻兘锛氭彁锟?GD5F2GQ5UE SPI NAND Flash 鐨勫垵濮嬪寲銆佽<E98A86>銆佸啓銆佹摝闄ゆ帴锟? * 閫傜敤骞冲彴锛歋TM32F407ZGT6 + SPI1 纭<>欢 SPI
* 浣滆€咃細鐜嬪缓锟? * 鍒涘缓鏃ユ湡锟?026-07-16
* 淇<>敼璁板綍锟? * 2026-07-16 鐜嬪缓锟? 鍒涘缓鍒濆<E98D92>鐗堟湰
* 2026-07-17 鐜嬪缓锟? 鍒囨崲纭<E5B4B2>欢 SPI锛屼慨姝摝闄ゅ湴鍧€锛屽弬锟?NuttX 椹卞姩
*/
/* 澶存枃浠跺寘鍚<E5AF98>尯 */
#include <string.h>
#include "gd5f2gq5ue.h"
/*
* 璋冭瘯杈撳嚭閰嶇疆
*/
#define DBG_TAG "[NAND]"
#include "dbg_log.h"
/* 绉佹湁瀹忓畾涔夊尯 */
#define GD5F_SPI_TIMEOUT 100
/* 澶栭儴 SPI 鍙ユ焺澹版槑 */
extern SPI_HandleTypeDef hspi1;
/* ======================== 绉佹湁鍑芥暟澹版槑 ======================== */
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);
/* ======================== 绉佹湁鍑芥暟瀹氫箟 ======================== */
/*
* 鍑芥暟鍔熻兘锛氱瓑寰呰姱鐗囨搷浣滃畬鎴愶紙杞<E7B499><E69D9E> OIP 浣嶏級
* 鍏ュ彛鍙傛暟锛歵imeout_ms - 瓒呮椂鏃堕棿 uint32_t > 0
* 杩斿洖鍊硷細0 - 鎿嶄綔瀹屾垚锟?2 - 瓒呮椂
* 闄愬畾鏉欢锛歋PI 宸插垵濮嬪寲
* 鍑芥暟璇存槑锛氬惊鐜<E6838A><E9909C>鍙栫姸鎬佸瘎瀛樺櫒鐩村埌 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 = 0;
uint32_t tick_start = HAL_GetTick();
while (1) {
GD5F_CS_LOW();
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() - tick_start) >= timeout_ms) {
return GD5F_BUSY_TIMEOUT;
}
}
}
/*
* 鍑芥暟鍔熻兘锛氬彂閫佸啓浣胯兘鍛戒护
* 鍏ュ彛鍙傛暟锛氭棤
* 杩斿洖鍊硷細0 - 鎴愬姛
* 闄愬畾鏉欢锛歋PI 宸插垵濮嬪寲
* 鍑芥暟璇存槑锛欳S# 鎷変綆鍚庡彂锟?06h 鍛戒护鍐嶆媺锟? */
static int gd5f_write_enable(void) {
uint8_t cmd = GD5F_CMD_WRITE_ENABLE;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
return GD5F_OK;
}
/*
* 鍑芥暟鍔熻兘锛氳<E9949B>鍙栫姸鎬佸瘎瀛樺櫒
* 鍏ュ彛鍙傛暟锛歱_status - 鐘舵€佸€艰緭鍑烘寚锟? uint8_t* 涓嶄负 NULL
* 杩斿洖鍊硷細0 - 鎴愬姛
* 闄愬畾鏉欢锛歋PI 宸插垵濮嬪寲
* 鍑芥暟璇存槑锛氬彂锟?0Fh + C0h 鍦板潃鍚庤<E98D9A>锟?瀛楄妭鐘舵€侊拷? */
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();
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;
}
/*
* 鍑芥暟鍔熻兘锛氶〉璇诲彇锛堝皢鏁版嵁浠庡瓨鍌ㄩ樀鍒楀姞杞藉埌鍐呴儴缂撳瓨锟? * 鍏ュ彛鍙傛暟锛歱age_addr - 椤靛湴鍧€ uint32_t
* 杩斿洖鍊硷細0 - 鎴愬姛锛屽叾锟?- 閿欒<E996BF>锟? * 闄愬畾鏉欢锛歋PI 宸插垵濮嬪寲
* 鍑芥暟璇存槑锛氬彂锟?13h + 3瀛楄妭琛屽湴鍧€锛岀瓑锟?OIP 娓呴浂
*/
static int gd5f_page_read(uint32_t page_addr) {
uint8_t cmd[4] = {0};
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();
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
return gd5f_wait_busy(100);
}
/*
* 鍑芥暟鍔熻兘锛氫粠鍐呴儴缂撳瓨璇诲彇鏁版嵁
* 鍏ュ彛鍙傛暟锛歝olumn - 鍒楀湴鍧€锛堥〉鍐呭亸绉伙級 uint16_t 0 - 2047
* p_buf - 鏁版嵁杈撳嚭缂撳啿锟? uint8_t* 涓嶄负 NULL
* size - 璇诲彇瀛楄妭锟? size_t > 0
* 杩斿洖鍊硷細0 - 鎴愬姛
* 闄愬畾鏉′欢锛氬繀椤诲厛璋冪敤 gd5f_page_read 瀹屾垚鏁版嵁鍔犺浇
* 鍑芥暟璇存槑锛氬彂锟?0Bh + 2瀛楄妭鍒楀湴鍧€ +1瀛楄妭 dummy 鍚庤<E98D9A>鍙栨暟锟? */
static int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
size_t size) {
uint8_t cmd[4] = {0};
cmd[0] = GD5F_CMD_READ_FROM_CACHE;
cmd[1] = (column >> 8) & 0xFF;
cmd[2] = column & 0xFF;
cmd[3] = 0x00;
GD5F_CS_LOW();
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;
}
/*
* 鍑芥暟鍔熻兘锛氶〉缂栫▼锛堝皢鏁版嵁鍐欏叆鎸囧畾椤碉級
* 鍏ュ彛鍙傛暟锛歱age_addr - 椤靛湴鍧€ uint32_t
* column - 鍒楀湴鍧€ uint16_t 0 - 2047
* p_buf - 鏁版嵁缂撳啿锟? const uint8_t*
* size - 鍐欏叆瀛楄妭锟? size_t > 0
* 杩斿洖鍊硷細0 - 鎴愬姛锛屽叾锟?- 閿欒<E996BF>锟? * 闄愬畾鏉′欢锛氱洰鏍囧尯鍩熷凡鎿﹂櫎
* 鍑芥暟璇存槑锟?. 鍐欎娇锟?- 02h 鍔犺浇鏁版嵁 - 10h 鎵ц<E98EB5>缂栫▼ - 绛夊緟瀹屾垚
* 2. 缂栫▼瀹屾垚鍚庢<E98D9A>锟?P_FAIL 锟? */
static int gd5f_page_program(uint32_t page_addr, uint16_t column,
const uint8_t *p_buf, size_t size) {
int ret = GD5F_OK;
uint8_t cmd[4];
uint8_t status = 0;
gd5f_write_enable();
cmd[0] = GD5F_CMD_PROGRAM_LOAD;
cmd[1] = (column >> 8) & 0xFF;
cmd[2] = column & 0xFF;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, cmd, 3, GD5F_SPI_TIMEOUT);
HAL_SPI_Transmit(&hspi1, (uint8_t *)p_buf, size,
GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
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();
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
ret = gd5f_wait_busy(1000);
if (ret != GD5F_OK) {
return ret;
}
gd5f_read_status(&status);
if (status & GD5F_STATUS_P_FAIL) {
return GD5F_PROGRAM_FAIL;
}
return GD5F_OK;
}
/*
* 鍑芥暟鍔熻兘锛氳<E9949B>锟?Feature 瀵勫瓨锟? * 鍏ュ彛鍙傛暟锛歛ddr - 瀵勫瓨鍣ㄥ湴鍧€ uint8_t
* data - 鍐欏叆鏁版嵁 uint8_t
* 杩斿洖鍊硷細0 - 鎴愬姛
* 闄愬畾鏉欢锛歋PI 宸插垵濮嬪寲
* 鍑芥暟璇存槑锛氬厛鍐欎娇鑳斤紝鍙戯拷?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;
}
/*
* 鍑芥暟鍔熻兘锛氬潡鎿﹂櫎锛堟摝锟?28KB鍧楋級
* 鍏ュ彛鍙傛暟锛歜lock_addr - 鍧楃紪锟? uint32_t 0 - 2047
* 杩斿洖鍊硷細0 - 鎴愬姛锛屽叾锟?- 閿欒<E996BF>锟? * 闄愬畾鏉欢锛歡d5f2gq5ue_init() 宸茶皟锟? * 鍑芥暟璇存槑锟?. 鍐欎娇锟?- D8h + 3瀛楄妭瀛楄妭鍦板潃 - 绛夊緟瀹屾垚
* 2. 鎿﹂櫎瀹屾垚鍚庢<E98D9A>锟?E_FAIL 锟? */
static int gd5f_block_erase(uint32_t block_addr) {
int ret = GD5F_OK;
uint8_t cmd[4];
uint8_t status = 0;
uint32_t byte_addr = block_addr * GD5F_BLOCK_SIZE;
gd5f_write_enable();
cmd[0] = GD5F_CMD_BLOCK_ERASE;
cmd[1] = (byte_addr >> 16) & 0xFF;
cmd[2] = (byte_addr >> 8) & 0xFF;
cmd[3] = byte_addr & 0xFF;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
ret = gd5f_wait_busy(5000);
if (ret != GD5F_OK) {
return ret;
}
gd5f_read_status(&status);
if (status & GD5F_STATUS_E_FAIL) {
return GD5F_ERASE_FAIL;
}
return GD5F_OK;
}
/* ======================== 鍏<>叡鍑芥暟瀹氫箟 ======================== */
/*
* 鍑芥暟鍔熻兘锛氬垵濮嬪寲 GD5F2GQ5UE
* 鍏ュ彛鍙傛暟锛氭棤
* 杩斿洖鍊硷細0 - 鎴愬姛锛屽叾锟?- 閿欒<E996BF>锟? * 闄愬畾鏉欢锛歋PI1 鍜岀浉锟?GPIO 宸茬敱 CubeMX 鍒濆<E98D92>鍖栧畬锟? * 鍑芥暟璇存槑锟?. 鍙戦€佸<E282AC>浣嶅懡浠ゅ苟绛夊緟瀹屾垚
* 2. 璇诲彇鑺<E5BD87>墖 ID 骞舵牎锟? * 3. 浣胯兘鍐呴儴 ECC (B0h bit4)
* 4. 瑙i櫎鎵€鏈夊潡淇濇姢 (A0h = 0x00)
*/
int gd5f2gq5ue_init(void) {
int ret = GD5F_OK;
uint8_t mid = 0;
uint8_t did = 0;
GD5F_CS_HIGH();
GD5F_WP_HIGH();
GD5F_HOLD_HIGH();
HAL_Delay(10);
DBG_INFO("Resetting NAND...");
ret = gd5f2gq5ue_reset();
if (ret != GD5F_OK) {
DBG_ERROR("Reset failed: %d", ret);
return ret;
}
HAL_Delay(5);
DBG_INFO("Reading NAND ID...");
ret = gd5f2gq5ue_read_id(&mid, &did);
if (ret != GD5F_OK) {
DBG_ERROR("Read ID failed: %d", ret);
return ret;
}
DBG_INFO("NAND ID: MID=0x%02X, DID=0x%02X", mid, did);
if (mid != GD5F_MANUFACTURER_ID || did != GD5F_DEVICE_ID) {
DBG_ERROR("ID mismatch: expected MID=0x%02X DID=0x%02X, got MID=0x%02X DID=0x%02X",
GD5F_MANUFACTURER_ID, GD5F_DEVICE_ID, mid, did);
return GD5F_ID_MISMATCH;
}
DBG_INFO("Enabling ECC...");
gd5f_set_feature(0xB0, 0x10);
gd5f_wait_busy(100);
DBG_INFO("Unlocking block protection...");
gd5f_set_feature(GD5F_REG_PROTECT, 0x00);
gd5f_wait_busy(100);
DBG_INFO("NAND init OK");
return GD5F_OK;
}
/*
* 鍑芥暟鍔熻兘锛氳<E9949B>鍙栬姱锟?ID锛圡ID + DID锟? * 鍏ュ彛鍙傛暟锛歮id - 鍒堕€犲晢 ID 杈撳嚭鎸囬拡 uint8_t* 涓嶄负 NULL
* did - 璁惧<E79281> ID 杈撳嚭鎸囬拡 uint8_t* 涓嶄负 NULL
* 杩斿洖鍊硷細0 - 鎴愬姛
* 闄愬畾鏉欢锛歋PI 宸插垵濮嬪寲
* 鍑芥暟璇存槑锛氬彂锟?9Fh 鍛戒护鍚庢帴锟?锟?dummy + MID + DID
*/
int gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did) {
uint8_t cmd = GD5F_CMD_READ_ID;
uint8_t id_buf[3] = {0};
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
HAL_SPI_Receive(&hspi1, id_buf, 3, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
*p_mid = id_buf[1];
*p_did = id_buf[2];
return GD5F_OK;
}
/*
* 鍑芥暟鍔熻兘锛氫粠 NAND 璇诲彇鏁版嵁锛堟敮鎸佽法椤碉級
* 鍏ュ彛鍙傛暟锛歰ffset - 璧峰<E792A7>瀛楄妭鍋忕Щ long 0 ~ 鎬诲<E98EAC>锟?1
* p_buf - 鏁版嵁缂撳啿锟? uint8_t* 涓嶄负 NULL
* size - 璇诲彇瀛楄妭锟? size_t > 0
* 杩斿洖鍊硷細0 - 鎴愬姛锛屽叾锟?- 閿欒<E996BF>锟? * 闄愬畾鏉欢锛歡d5f2gq5ue_init() 宸叉垚鍔熻皟锟? * 鍑芥暟璇存槑锛氳嚜鍔ㄥ<E98D94>鐞嗚法椤佃<E6A4A4>锟? */
int gd5f2gq5ue_read(long offset, uint8_t *p_buf, size_t size) {
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_read(page_addr);
if (ret != GD5F_OK) {
return ret;
}
ret = gd5f_read_from_cache(column, p_buf, bytes);
if (ret != GD5F_OK) {
return ret;
}
offset += bytes;
p_buf += bytes;
size -= bytes;
}
return GD5F_OK;
}
/*
* 鍑芥暟鍔熻兘锛氬悜 NAND 鍐欏叆鏁版嵁锛堟敮鎸佽法椤碉級
* 鍏ュ彛鍙傛暟锛歰ffset - 璧峰<E792A7>瀛楄妭鍋忕Щ long 0 ~ 鎬诲<E98EAC>锟?1
* p_buf - 鏁版嵁缂撳啿锟? const uint8_t* 涓嶄负 NULL
* size - 鍐欏叆瀛楄妭锟? size_t > 0
* 杩斿洖鍊硷細0 - 鎴愬姛锛屽叾锟?- 閿欒<E996BF>锟? * 闄愬畾鏉欢锛歡d5f2gq5ue_init() 宸叉垚鍔熻皟鐢<E79A9F>紝鐩<E7B49D>爣鍖哄煙宸叉摝锟? * 鍑芥暟璇存槑锛氳嚜鍔ㄥ<E98D94>鐞嗚法椤靛啓锟? */
int gd5f2gq5ue_write(long offset, const uint8_t *p_buf, size_t size) {
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, p_buf, bytes);
if (ret != GD5F_OK) {
return ret;
}
offset += bytes;
p_buf += bytes;
size -= bytes;
}
return GD5F_OK;
}
/*
* 鍑芥暟鍔熻兘锛氭摝闄ゅ潡锛堟寜鍧楁摝闄わ紝鏈€灏忓崟锟?128KB锟? * 鍏ュ彛鍙傛暟锛歰ffset - 璧峰<E792A7>瀛楄妭鍋忕Щ long 蹇呴』鍧楀<E98DA7>锟? * size - 鎿﹂櫎瀛楄妭锟? size_t 蹇呴』鍧楀ぇ灏忔暣鏁帮拷? * 杩斿洖鍊硷細0 - 鎴愬姛锛屽叾锟?- 閿欒<E996BF>锟? * 闄愬畾鏉欢锛歡d5f2gq5ue_init() 宸叉垚鍔熻皟锟? * 鍑芥暟璇存槑锛氭摝闄ゆ搷浣滀互鍧椾负鍗曚綅
*/
int gd5f2gq5ue_erase(long offset, size_t size) {
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;
}
offset += GD5F_BLOCK_SIZE;
size -= GD5F_BLOCK_SIZE;
}
return GD5F_OK;
}
/*
* 鍑芥暟鍔熻兘锛氬<E9949B>浣嶈姱锟? * 鍏ュ彛鍙傛暟锛氭棤
* 杩斿洖鍊硷細0 - 鎴愬姛
* 闄愬畾鏉欢锛歋PI 宸插垵濮嬪寲
* 鍑芥暟璇存槑锛氬彂锟?FFh 澶嶄綅鍛戒护鍚庣瓑锟?5ms
*/
int gd5f2gq5ue_reset(void) {
uint8_t cmd = GD5F_CMD_RESET;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
HAL_Delay(5);
return GD5F_OK;
}