GigaDevice(兆易创新)GD25Q64CSJGR 64Mbit FLASH
使用的是 STM32G070RBT6,
SPI1
PA12 -> MOSI
PA11->MISO
PA10->CS
PD8->SCK
FLASH代码
#include "flash.h"// 选中 Flash(拉低 CS#)
void GD25Q64_Select(void) {HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, FLASH_CS_Pin, GPIO_PIN_RESET);//HAL_Delay(0);
}// 取消选中 Flash(拉高 CS#)
void GD25Q64_Deselect(void) {HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, FLASH_CS_Pin, GPIO_PIN_SET);//HAL_Delay(0);
}// 发送 1 字节命令,并返回 Flash 响应
uint8_t GD25Q64_SendCmd(uint8_t cmd) {uint8_t rx_data = 0;HAL_SPI_TransmitReceive(&hspi1, &cmd, &rx_data, 1, 100); // 超时 100msreturn rx_data;
}// 等待 Flash 忙状态清除(擦除/编程后必须等待)
void GD25Q64_WaitBusy(void) {uint8_t status = 0;GD25Q64_Select();GD25Q64_SendCmd(CMD_READ_STATUS);do {status = GD25Q64_SendCmd(0xFF); // 持续读取状态寄存器} while (status & 0x01); // 忙标志位(BIT0)为 1 表示忙GD25Q64_Deselect();
}// 读取 Flash ID(验证通信)
void GD25Q64_ReadID(uint8_t *mfg_id, uint16_t *dev_id) {if (!mfg_id || !dev_id) return;uint8_t tx[1] = {0x9F}; // JEDEC读ID命令uint8_t rx[4] = {0}; // 接收缓冲区(1字节命令+3字节ID)// 片选拉低HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, FLASH_CS_Pin, GPIO_PIN_RESET);// 发送命令并接收ID(一次性完成,避免时序间断)HAL_SPI_TransmitReceive(&hspi1, tx, rx, 4, 100);// 片选拉高HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, FLASH_CS_Pin, GPIO_PIN_SET);// 解析结果(9F命令的ID在第2-4字节)*mfg_id = rx[1]; // 制造商ID(C8)*dev_id = (rx[2] << 8) | rx[3]; // 设备ID(4017)
/*if (!mfg_id || !dev_id) return;GD25Q64_Select();GD25Q64_SendCmd(CMD_READ_ID);// 发送固定地址 0x000000(用于 ID 读取)uint8_t addr[3] = {0x00, 0x00, 0x00};HAL_SPI_Transmit(&hspi1, addr, 3, 100);// 接收 1 字节制造商 ID 和 2 字节设备 IDuint8_t id_buf[3] = {0};HAL_SPI_Receive(&hspi1, id_buf, 3, 100);GD25Q64_Deselect();*mfg_id = id_buf[0]; // 兆易创新:0xC8*dev_id = (id_buf[1] << 8) | id_buf[2]; // GD25Q64:0x4017
*/
}// 写使能(擦除/编程前必须调用)
void GD25Q64_WriteEnable(void) {GD25Q64_Select();uint8_t cmd = CMD_WRITE_ENABLE;HAL_SPI_Transmit(&hspi1, &cmd, 1, 100);GD25Q64_Deselect();
}// 解锁Flash(清除写保护)
void GD25Q64_Unlock(void) {GD25Q64_WriteEnable(); // 解锁前必须先写使能GD25Q64_Select();uint8_t cmd[3] = {CMD_UNPROTECT, 0x00, 0x00};HAL_SPI_Transmit(&hspi1, cmd, 3, 100);GD25Q64_Deselect();GD25Q64_WaitBusy();
}// 扇区擦除(4KB)
HAL_StatusTypeDef GD25Q64_EraseSector(uint32_t sector_addr) {if (sector_addr > 0x7FFFFF || (sector_addr % 4096) != 0) return HAL_ERROR;GD25Q64_WriteEnable(); // 擦除前必须写使能GD25Q64_Select();uint8_t cmd[4] = {CMD_SECTOR_ERASE,(uint8_t)(sector_addr >> 16),(uint8_t)(sector_addr >> 8),(uint8_t)sector_addr};HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi1, cmd, 4, 100);GD25Q64_Deselect();if (status == HAL_OK) {GD25Q64_WaitBusy(); // 等待擦除完成(约100ms)}return status;
}// 页编程(最大256字节)
HAL_StatusTypeDef GD25Q64_ProgramPage(uint32_t page_addr, uint8_t *data, uint32_t len) {if (!data || page_addr > 0x7FFFFF || len > 256 || (page_addr % 256) != 0) return HAL_ERROR;GD25Q64_WriteEnable(); // 编程前必须写使能GD25Q64_Select();uint8_t cmd[4] = {CMD_PAGE_PROGRAM,(uint8_t)(page_addr >> 16),(uint8_t)(page_addr >> 8),(uint8_t)page_addr};HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi1, cmd, 4, 100);if (status != HAL_OK) {GD25Q64_Deselect();return status;}status = HAL_SPI_Transmit(&hspi1, data, len, 1000);GD25Q64_Deselect();if (status == HAL_OK) {GD25Q64_WaitBusy(); // 等待编程完成(约1ms)}return status;
}// 读取数据
HAL_StatusTypeDef GD25Q64_ReadData(uint32_t addr, uint8_t *buf, uint32_t len) {if (!buf || addr > 0x7FFFFF || len == 0) return HAL_ERROR;GD25Q64_Select();uint8_t cmd[4] = {CMD_READ,(uint8_t)(addr >> 16),(uint8_t)(addr >> 8),(uint8_t)addr};HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi1, cmd, 4, 100);if (status != HAL_OK) {GD25Q64_Deselect();return status;}status = HAL_SPI_Receive(&hspi1, buf, len, 1000);GD25Q64_Deselect();return status;
}
#ifndef _FLASH_#define _FLASH_#include "main.h"extern SPI_HandleTypeDef hspi1;#define FLASH_CS_GPIO_Port GPIOA
#define FLASH_CS_Pin GPIO_PIN_10
#define FLASH_SCK_GPIO_Port GPIOD
#define FLASH_SCK_Pin GPIO_PIN_8
#define FLASH_MOSI_GPIO_Port GPIOA
#define FLASH_MOSI_Pin GPIO_PIN_12
#define FLASH_MISO_GPIO_Port GPIOA
#define FLASH_MISO_Pin GPIO_PIN_11// GD25Q64 命令定义
#define CMD_READ_ID 0x90 // 读取 ID 命令
#define CMD_READ 0x03 // 读取数据命令
#define CMD_PAGE_PROGRAM 0x02 // 页编程命令(写数据)
#define CMD_SECTOR_ERASE 0x20 // 扇区擦除命令(4KB)
#define CMD_CHIP_ERASE 0xC7 // 整片擦除命令
#define CMD_READ_STATUS 0x05 // 读取状态寄存器命令
// 命令定义(需在flash.h中声明)#define CMD_WRITE_ENABLE 0x06 // 写使能
#define CMD_UNPROTECT 0x98 // 解除保护// 读取 Flash ID(验证通信)
void GD25Q64_ReadID(uint8_t *mfg_id, uint16_t *dev_id);
// 从指定地址读取数据
HAL_StatusTypeDef GD25Q64_ReadData(uint32_t addr, uint8_t *buf, uint32_t len);
// 扇区擦除(4KB,地址需对齐到扇区起始)
HAL_StatusTypeDef GD25Q64_EraseSector(uint32_t sector_addr);
// 页编程(写数据,一页 256 字节,地址需对齐到页起始)
HAL_StatusTypeDef GD25Q64_ProgramPage(uint32_t page_addr, uint8_t *data, uint32_t len);
// 解锁Flash(清除写保护)
void GD25Q64_Unlock(void);#endif