地址信息

#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;
}