/* * LittleFS block device adapter for W25QXX SPI Flash * * Maps littlefs read/prog/erase/sync callbacks to the W25QXX driver: * read -> W25QXX_Read * prog -> W25QXX_Write_NoCheck (littleFS guarantees erase-before-prog) * erase -> W25QXX_Erase_Sector (4 KiB sector) * sync -> W25QXX_Wait_Busy */ #include "lfs_flashbd.h" #include "../../bspMCU/include/w25qxx.h" #include int lfs_flashbd_create(const struct lfs_config *cfg, const struct lfs_flashbd_config *bdcfg) { lfs_flashbd_t *bd = (lfs_flashbd_t *)cfg->context; /* 参数校验 */ if (bdcfg->base_addr % LFS_FLASHBD_SECTOR_SIZE != 0) { return LFS_ERR_INVAL; } if (bdcfg->block_count == 0) { return LFS_ERR_INVAL; } bd->cfg = bdcfg; /* 初始化 W25QXX Flash */ W25QXX_Init(); return LFS_ERR_OK; } int lfs_flashbd_destroy(const struct lfs_config *cfg) { (void)cfg; return LFS_ERR_OK; } int lfs_flashbd_read(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) { lfs_flashbd_t *bd = (lfs_flashbd_t *)cfg->context; LFS_ASSERT(block < bd->cfg->block_count); LFS_ASSERT(off + size <= cfg->block_size); uint32_t addr = bd->cfg->base_addr + (uint32_t)block * cfg->block_size + (uint32_t)off; W25QXX_Read((uint8_t *)buffer, addr, (uint16_t)size); return LFS_ERR_OK; } int lfs_flashbd_prog(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) { lfs_flashbd_t *bd = (lfs_flashbd_t *)cfg->context; LFS_ASSERT(block < bd->cfg->block_count); LFS_ASSERT(off + size <= cfg->block_size); uint32_t addr = bd->cfg->base_addr + (uint32_t)block * cfg->block_size + (uint32_t)off; /* * W25QXX_Write_NoCheck 的第一个参数是 uint8_t*(非 const), * 但该函数只读取 buffer 内容,不会修改,故安全地去除 const。 * littleFS 保证写入前块已擦除,因此无需带擦除的 W25QXX_Write。 */ W25QXX_Write_NoCheck((uint8_t *)(uintptr_t)buffer, addr, (uint16_t)size); return LFS_ERR_OK; } int lfs_flashbd_erase(const struct lfs_config *cfg, lfs_block_t block) { lfs_flashbd_t *bd = (lfs_flashbd_t *)cfg->context; LFS_ASSERT(block < bd->cfg->block_count); uint32_t addr = bd->cfg->base_addr + (uint32_t)block * cfg->block_size; /* * W25QXX_Erase_Sector 的参数是扇区号(内部会乘以 4096), * 所以传入字节地址除以扇区大小。 */ W25QXX_Erase_Sector(addr / LFS_FLASHBD_SECTOR_SIZE); return LFS_ERR_OK; } int lfs_flashbd_sync(const struct lfs_config *cfg) { (void)cfg; W25QXX_Wait_Busy(); return LFS_ERR_OK; } void lfs_flashbd_default_config( struct lfs_config *cfg, lfs_flashbd_t *bd, struct lfs_flashbd_config *bdcfg, uint32_t base_addr, uint32_t block_count, void *read_buffer, void *prog_buffer, void *lookahead_buffer) { memset(cfg, 0, sizeof(*cfg)); /* flashbd 配置 */ bdcfg->base_addr = base_addr; bdcfg->block_count = block_count; bd->cfg = bdcfg; /* 回调函数 */ cfg->context = bd; cfg->read = lfs_flashbd_read; cfg->prog = lfs_flashbd_prog; cfg->erase = lfs_flashbd_erase; cfg->sync = lfs_flashbd_sync; /* 块设备参数 */ cfg->read_size = 256; /* W25QXX 页大小,读取可任意长度 */ cfg->prog_size = 256; /* W25QXX 页编程大小 */ cfg->block_size = LFS_FLASHBD_SECTOR_SIZE; /* 4096 字节扇区 */ cfg->block_count = block_count; cfg->block_cycles = 500; /* 磨损均衡建议值 */ cfg->cache_size = 256; /* 缓存大小 = prog_size */ /* * lookahead 缓存是一个位图,每字节跟踪 8 个块。按块数自动计算, * 保证一次分配扫描即可覆盖全部块(块数向上取整到字节)。 * RAM 消耗 = ceil(block_count / 8) 字节,例如 3072 块 -> 384 字节。 * 注意:若调用者提供静态 lookahead_buffer,其大小必须 >= 该值。 */ cfg->lookahead_size = (block_count + 7) / 8; cfg->compact_thresh = 0; /* 使用默认值 */ /* 静态缓冲区,NULL 则由 littleFS 动态分配 */ cfg->read_buffer = read_buffer; cfg->prog_buffer = prog_buffer; cfg->lookahead_buffer = lookahead_buffer; /* 使用默认值 */ cfg->name_max = 0; cfg->file_max = 0; cfg->attr_max = 0; }