#ifndef FLASH_XFC_H__
#define FLASH_XFC_H__
#include <stdint.h>
#include <stdbool.h>
#define FLASH_XFC_SECTOR_SIZE 4096UL // 4KB大小的数据区,小擦除单位
#define FLASH_XFC_PAGE_SIZE 256UL // 页大小,写的最小颗粒
#define FLASH_XFC_SETTINGS_ADDR 0x09FFF000UL // 读写的开始地址
/* 读一次 JEDEC ID 并打日志,用于核对容量/型号假设。无其他初始化——
* QSPI 控制器与引脚由 SystemInit 配置(代码本身就从这片 flash 运行)。 */
void FlashXFC_Init(void);
/* 读设置扇区内 [addr, addr+len)。扇区之外返回 false。 */
bool FlashXFC_Read(uint32_t addr, void *out, uint32_t len);
/* 擦除设置扇区(addr 必须恰好是 FLASH_XFC_SETTINGS_ADDR)。 */
bool FlashXFC_EraseSector(uint32_t addr);
/* 在设置扇区内编程:addr/len 4 字节对齐,目标区必须已擦除(NOR 只能
* 1→0)。data 必须指向 RAM(SRAM/栈/静态区),不要传 flash 里的 const
* 数据。扇区之外返回 false。 */
bool FlashXFC_Program(uint32_t addr, const void *data, uint32_t len);
#endif /* FLASH_XFC_H__ */
#include "flash_xfc.h"
/* 全部操作只放行设置扇区内的 [addr, addr+len) */
static bool in_settings_sector(uint32_t addr, uint32_t len)
{
return addr >= FLASH_XFC_SETTINGS_ADDR &&
len != 0UL &&
(addr - FLASH_XFC_SETTINGS_ADDR) + len <= FLASH_XFC_SECTOR_SIZE;
}
void FlashXFC_Init(void)
{
printf("[XFC] flash JEDEC ID 0x%06lX.\n",
(unsigned long)QSPI_ReadID(NULL));
}
bool FlashXFC_Read(uint32_t addr, void *out, uint32_t len)
{
if (out == NULL || !in_settings_sector(addr, len)) return false;
memcpy(out, (const void *)(uintptr_t)addr, len);
return true;
}
bool FlashXFC_EraseSector(uint32_t addr)
{
if (!in_settings_sector(addr, FLASH_XFC_SECTOR_SIZE) ||
addr != FLASH_XFC_SETTINGS_ADDR) {
return false;
}
if (FLASH_EraseSector(addr) != QSPI_STATUS_OK) {
printf("[XFC] erase 0x%08lX failed.\n", (unsigned long)addr);
CACHE_CleanAll(DCACHE);
return false;
}
/* 擦写后读回之前必须清 DCACHE(SDK 注意事项),否则可能读到旧缓存行 */
CACHE_CleanAll(DCACHE);
return true;
}
bool FlashXFC_Program(uint32_t addr, const void *data, uint32_t len)
{
const uint8_t *src = (const uint8_t *)data;
QSPI_CommandTypeDef cmd;
if (data == NULL || !in_settings_sector(addr, len)) return false;
/* ROM 例程的三重对齐要求,缺一不可,这里前置校验而不是让它半途失败 */
if ((addr & 3UL) != 0UL || (len & 3UL) != 0UL ||
((uintptr_t)data & 3UL) != 0UL) {
printf("[XFC] program 0x%08lX len=%lu: unaligned.\n",
(unsigned long)addr, (unsigned long)len);
return false;
}
/* 与参考例程一致:四线页编程(0x32),1-1-4 总线,8bit 命令 + 24bit 地址。
* 不用 DMA(传 NULL):DMA2 的可用流(S4/6/7)留给以后,编程量只有
* 几十字节的配置数据,CPU 搬运绰绰有余。 */
cmd.Instruction = QUAD_INPUT_PAGE_PROG_CMD;
cmd.BusMode = QSPI_BUSMODE_114;
cmd.CmdFormat = QSPI_CMDFORMAT_CMD8_ADDR24_PDAT;
while (len != 0UL) {
/* 一页编程不能跨 NOR 页边界(256 B) */
uint32_t page_room = FLASH_XFC_PAGE_SIZE -
(addr & (FLASH_XFC_PAGE_SIZE - 1UL));
uint32_t chunk = len < page_room ? len : page_room;
if (FLASH_ProgramPage(&cmd, NULL, addr, chunk,
(uint8_t *)(uintptr_t)src) != QSPI_STATUS_OK) { //擦除并写入
printf("[XFC] program 0x%08lX failed.\n", (unsigned long)addr);
CACHE_CleanAll(DCACHE);
return false;
}
addr += chunk;
src += chunk;
len -= chunk;
}
CACHE_CleanAll(DCACHE);
return true;
}