OpenHarmony海思WS63星闪平台 LittleFS文件系统移植指南(使用外扩Flash驱动)

随着物联网技术的快速发展,高性能、低功耗、多协议的无线通信芯片成为智能设备的核心组件。海思 WS63 芯片(Hi3863V100)作为一款集成了 Wi-Fi 6、星闪 SLE 1.0 和 BLE 5.2 三模通信协议的物联网 SoC 芯片,凭借其出色的性能和丰富的接口资源,为开发者提供了强大的硬件平台。

硬件架构差异与适配要点

Hi3861和WS63虽然都是面向物联网应用的芯片,但硬件能力有代际差异。Hi3861采用32位微处理器架构,主频最高160MHz,内置SRAM和Flash存储,集成Wi-Fi功能。而WS63作为新一代平台,主频提升至240MHz,内置Flash容量扩大到32M bit,SRAM增加至606KB,并额外提供300KB ROM空间。

关键硬件差异对比表:

特性 Hi3861V100 WS63
主频 最高160MHz 最高240MHz
内置Flash 2MB 32M bit
SRAM 352KB 606KB + 300KB ROM
无线功能 Wi-Fi Wi-Fi 6 + 星闪 + 蓝牙
安全特性 基础安全 国密算法SM2/SM3/SM4硬件加速

一、项目背景

在海思WS63嵌入式平台上,为了实现数据的持久化存储和文件管理功能,需要移植一个轻量级的文件系统。LittleFS是一个专为嵌入式系统设计的轻量级文件系统,具有断电保护、磨损均衡和低内存占用等优点,非常适合在资源受限的嵌入式系统中使用。

虽然WS63的sdk内置了LittleFS文件系统组件,但是它默认使用的是它的内置flash,空间太小。

内置文件系统代码在fbb_ws63/src/open_source/littlefs/v2.5.0/路径下。

本文档详细介绍如何将LittleFS文件系统移植到海思WS63平台,使用外扩的W25Q128 NOR Flash作为存储介质。

在线文档地址https://docs.hisilicon.com/repos/fbb_ws63/zh-CN/master/

论坛地址https://developers.hisilicon.com/forum/0133146886267870001

SDK代码仓地址https://gitcode.com/HiSpark/fbb_ws63

二、技术选型

2.1 LittleFS特性

LittleFS是由ARM公司开发的一个轻量级文件系统,具有以下特点:

  • 断电保护:支持断电恢复,确保数据完整性
  • 磨损均衡:自动管理Flash擦写周期,延长Flash寿命
  • 低内存占用:RAM占用极小,适合资源受限系统
  • 动态文件大小:支持动态大小的文件
  • 目录支持:支持目录结构
  • 可移植性强:通过移植层适配不同的存储介质

2.2 W25Q128 NOR Flash特性

W25Q128是Winbond公司生产的一款128Mbit(16MB)容量的串行Flash存储器,具有以下特点:

  • 容量:16MB(128Mbit)
  • 接口:SPI接口(支持标准SPI、双SPI、四SPI)
  • 擦除块大小:4KB、32KB、64KB
  • 页大小:256字节
  • 擦写寿命:10万次
  • 数据保持:20年@25°C
  • 工作电压:2.7V-3.6V

三、硬件连接与驱动配置

3.1 硬件连接

在海思WS63平台上,W25Q128通过SPI接口连接,具体连接如下:

W25Q128引脚 WS63引脚 功能说明
CS (Pin 1) GPIO_XX 片选信号
CLK (Pin 6) SPI1_CLK 时钟信号
MOSI (Pin 5) SPI1_MOSI 主出从入
MISO (Pin 2) SPI1_MISO 主入从出
VCC (Pin 8) 3.3V 电源
GND (Pin 4) GND

3.2 SPI驱动配置

WS63平台使用CMSIS-RTOS2的HAL层驱动SPI接口,配置代码如下:

c 复制代码
/* board_config.c - SPI配置 */
#include "w25q128.h"
#include "spi.h"

#define W25Q128_CS_PIN  GPIO_PIN_XX
#define W25Q128_CS_PORT GPIO_PORT_XX

static void w25q128_cs_low(void)
{
    HAL_GPIO_WritePin(W25Q128_CS_PORT, W25Q128_CS_PIN, GPIO_PIN_RESET);
}

static void w25q128_cs_high(void)
{
    HAL_GPIO_WritePin(W25Q128_CS_PORT, W25Q128_CS_PIN, GPIO_PIN_SET);
}

int flash_write_read(const uint8_t *tx_data, uint8_t *rx_data, uint32_t length)
{
    SPI_HandleTypeDef *hspi = &hspi1;
    HAL_StatusTypeDef status;
    
    w25q128_cs_low();
    status = HAL_SPI_TransmitReceive(hspi, (uint8_t *)tx_data, rx_data, length, 1000);
    w25q128_cs_high();
    
    if (status != HAL_OK) {
        elog_e("flash", "SPI transfer failed: %d", status);
        return -1;
    }
    
    return 0;
}

3.3 W25Q128驱动实现

c 复制代码
/* w25q128.h */
#ifndef W25Q128_H
#define W25Q128_H

#include <stdint.h>
#include <stdbool.h>

#define W25Q128_PAGE_SIZE       256
#define W25Q128_SECTOR_SIZE     4096
#define W25Q128_BLOCK_SIZE_32K  32768
#define W25Q128_BLOCK_SIZE_64K  65536
#define W25Q128_CHIP_SIZE       (16 * 1024 * 1024)

/* W25Q128命令定义 */
#define W25Q128_CMD_WRITE_ENABLE       0x06
#define W25Q128_CMD_WRITE_DISABLE      0x04
#define W25Q128_CMD_READ_STATUS_REG    0x05
#define W25Q128_CMD_WRITE_STATUS_REG   0x01
#define W25Q128_CMD_READ_DATA          0x03
#define W25Q128_CMD_PAGE_PROGRAM       0x02
#define W25Q128_CMD_SECTOR_ERASE       0x20
#define W25Q128_CMD_BLOCK_ERASE_32K    0x52
#define W25Q128_CMD_BLOCK_ERASE_64K    0xD8
#define W25Q128_CMD_CHIP_ERASE         0xC7
#define W25Q128_CMD_READ_JEDEC_ID      0x9F
#define W25Q128_CMD_READ_UNIQUE_ID     0x4B

int w25q128_init(void);
int w25q128_read(uint32_t addr, uint8_t *data, uint32_t len);
int w25q128_write(uint32_t addr, const uint8_t *data, uint32_t len);
int w25q128_erase(uint32_t addr, uint32_t len);
int w25q128_chip_erase(void);

#endif /* W25Q128_H */
c 复制代码
/* w25q128.c */
#include "w25q128.h"
#include "elog.h"
#include "cmsis_os2.h"

extern int flash_write_read(const uint8_t *tx_data, uint8_t *rx_data, uint32_t length);

static bool w25q128_is_busy(void)
{
    uint8_t tx_buf[2] = {W25Q128_CMD_READ_STATUS_REG, 0xFF};
    uint8_t rx_buf[2] = {0};
    
    flash_write_read(tx_buf, rx_buf, 2);
    
    return (rx_buf[1] & 0x01) != 0;
}

static int w25q128_wait_busy(uint32_t timeout_ms)
{
    uint32_t start = osKernelGetTickCount();
    
    while (w25q128_is_busy()) {
        if ((osKernelGetTickCount() - start) >= timeout_ms) {
            elog_e("w25q128", "Wait busy timeout");
            return -1;
        }
        osDelay(1);
    }
    
    return 0;
}

static int w25q128_write_enable(void)
{
    uint8_t cmd = W25Q128_CMD_WRITE_ENABLE;
    return flash_write_read(&cmd, &cmd, 1);
}

int w25q128_init(void)
{
    uint8_t tx_buf[4] = {W25Q128_CMD_READ_JEDEC_ID, 0xFF, 0xFF, 0xFF};
    uint8_t rx_buf[4] = {0};
    
    elog_i("w25q128", "Initializing W25Q128...");
    
    if (flash_write_read(tx_buf, rx_buf, 4) != 0) {
        elog_e("w25q128", "Read JEDEC ID failed");
        return -1;
    }
    
    uint32_t jedec_id = (rx_buf[1] << 16) | (rx_buf[2] << 8) | rx_buf[3];
    elog_i("w25q128", "JEDEC ID: 0x%06X", jedec_id);
    
    if ((rx_buf[1] != 0xEF) || (rx_buf[2] != 0x40)) {
        elog_e("w25q128", "Unknown flash device");
        return -1;
    }
    
    elog_i("w25q128", "W25Q128 initialized successfully");
    return 0;
}

int w25q128_read(uint32_t addr, uint8_t *data, uint32_t len)
{
    uint8_t tx_buf[4];
    
    if ((addr + len) > W25Q128_CHIP_SIZE) {
        elog_e("w25q128", "Read address out of range");
        return -1;
    }
    
    tx_buf[0] = W25Q128_CMD_READ_DATA;
    tx_buf[1] = (addr >> 16) & 0xFF;
    tx_buf[2] = (addr >> 8) & 0xFF;
    tx_buf[3] = addr & 0xFF;
    
    if (flash_write_read(tx_buf, tx_buf, 4) != 0) {
        return -1;
    }
    
    if (flash_write_read(NULL, data, len) != 0) {
        return -1;
    }
    
    return 0;
}

int w25q128_write(uint32_t addr, const uint8_t *data, uint32_t len)
{
    uint32_t offset = 0;
    uint32_t remain = len;
    
    if ((addr + len) > W25Q128_CHIP_SIZE) {
        elog_e("w25q128", "Write address out of range");
        return -1;
    }
    
    while (remain > 0) {
        uint32_t page_offset = addr % W25Q128_PAGE_SIZE;
        uint32_t page_remain = W25Q128_PAGE_SIZE - page_offset;
        uint32_t write_len = (remain < page_remain) ? remain : page_remain;
        
        if (w25q128_write_enable() != 0) {
            return -1;
        }
        
        uint8_t tx_buf[4 + W25Q128_PAGE_SIZE];
        tx_buf[0] = W25Q128_CMD_PAGE_PROGRAM;
        tx_buf[1] = (addr >> 16) & 0xFF;
        tx_buf[2] = (addr >> 8) & 0xFF;
        tx_buf[3] = addr & 0xFF;
        memcpy(&tx_buf[4], &data[offset], write_len);
        
        if (flash_write_read(tx_buf, NULL, 4 + write_len) != 0) {
            return -1;
        }
        
        if (w25q128_wait_busy(100) != 0) {
            return -1;
        }
        
        addr += write_len;
        offset += write_len;
        remain -= write_len;
    }
    
    return 0;
}

int w25q128_erase(uint32_t addr, uint32_t len)
{
    uint32_t sector_start = (addr / W25Q128_SECTOR_SIZE) * W25Q128_SECTOR_SIZE;
    uint32_t sector_end = ((addr + len - 1) / W25Q128_SECTOR_SIZE + 1) * W25Q128_SECTOR_SIZE;
    
    if (sector_end > W25Q128_CHIP_SIZE) {
        sector_end = W25Q128_CHIP_SIZE;
    }
    
    elog_d("w25q128", "Erase from 0x%08X to 0x%08X", sector_start, sector_end);
    
    for (uint32_t sector = sector_start; sector < sector_end; sector += W25Q128_SECTOR_SIZE) {
        if (w25q128_write_enable() != 0) {
            return -1;
        }
        
        uint8_t tx_buf[4];
        tx_buf[0] = W25Q128_CMD_SECTOR_ERASE;
        tx_buf[1] = (sector >> 16) & 0xFF;
        tx_buf[2] = (sector >> 8) & 0xFF;
        tx_buf[3] = sector & 0xFF;
        
        if (flash_write_read(tx_buf, NULL, 4) != 0) {
            return -1;
        }
        
        if (w25q128_wait_busy(500) != 0) {
            return -1;
        }
    }
    
    return 0;
}

int w25q128_chip_erase(void)
{
    elog_w("w25q128", "Chip erase started");
    
    if (w25q128_write_enable() != 0) {
        return -1;
    }
    
    uint8_t cmd = W25Q128_CMD_CHIP_ERASE;
    if (flash_write_read(&cmd, NULL, 1) != 0) {
        return -1;
    }
    
    if (w25q128_wait_busy(10000) != 0) {
        return -1;
    }
    
    elog_i("w25q128", "Chip erase completed");
    return 0;
}

四、LittleFS源码获取与移植

4.1 获取LittleFS源码

LittleFS的源码可以从GitHub官方仓库获取:

bash 复制代码
git clone https://github.com/littlefs-project/littlefs.git

或者直接下载最新版本的源码包。

4.2 LittleFS移植层实现

LittleFS通过移植层与底层存储介质交互,需要实现以下接口:

c 复制代码
/* littlefs_port.h */
#ifndef LITTLEFS_PORT_H
#define LITTLEFS_PORT_H

#include "lfs.h"
#include "w25q128.h"
#include <stdint.h>

#define LFS_READ_SIZE    256
#define LFS_PROG_SIZE    256
#define LFS_BLOCK_SIZE   4096
#define LFS_BLOCK_COUNT  (W25Q128_CHIP_SIZE / LFS_BLOCK_SIZE)
#define LFS_BLOCK_CYCLES 100
#define LFS_CACHE_SIZE   256
#define LFS_LOOKAHEAD_SIZE 16

int littlefs_init(void);
int littlefs_format(void);
int littlefs_mount(void);
int littlefs_unmount(void);

#endif /* LITTLEFS_PORT_H */
c 复制代码
/* littlefs_port.c */
#include "littlefs_port.h"
#include "elog.h"
#include <string.h>

static lfs_t lfs;
static lfs_config_t cfg;

static int lfs_read(const struct lfs_config *c, lfs_block_t block,
                   lfs_off_t off, void *buffer, lfs_size_t size)
{
    uint32_t addr = block * c->block_size + off;
    return w25q128_read(addr, (uint8_t *)buffer, size);
}

static int lfs_prog(const struct lfs_config *c, lfs_block_t block,
                   lfs_off_t off, const void *buffer, lfs_size_t size)
{
    uint32_t addr = block * c->block_size + off;
    return w25q128_write(addr, (const uint8_t *)buffer, size);
}

static int lfs_erase(const struct lfs_config *c, lfs_block_t block)
{
    uint32_t addr = block * c->block_size;
    return w25q128_erase(addr, c->block_size);
}

static int lfs_sync(const struct lfs_config *c)
{
    return 0;
}

int littlefs_init(void)
{
    elog_i("littlefs", "Initializing LittleFS...");
    
    memset(&cfg, 0, sizeof(cfg));
    cfg.read = lfs_read;
    cfg.prog = lfs_prog;
    cfg.erase = lfs_erase;
    cfg.sync = lfs_sync;
    
    cfg.read_size = LFS_READ_SIZE;
    cfg.prog_size = LFS_PROG_SIZE;
    cfg.block_size = LFS_BLOCK_SIZE;
    cfg.block_count = LFS_BLOCK_COUNT;
    cfg.block_cycles = LFS_BLOCK_CYCLES;
    cfg.cache_size = LFS_CACHE_SIZE;
    cfg.lookahead_size = LFS_LOOKAHEAD_SIZE;
    
    elog_i("littlefs", "Block size: %d, Block count: %d, Total size: %d bytes",
           cfg.block_size, cfg.block_count, cfg.block_size * cfg.block_count);
    
    return 0;
}

int littlefs_format(void)
{
    elog_w("littlefs", "Formatting filesystem...");
    
    int err = lfs_format(&lfs, &cfg);
    if (err != LFS_ERR_OK) {
        elog_e("littlefs", "Format failed: %d", err);
        return -1;
    }
    
    elog_i("littlefs", "Format completed");
    return 0;
}

int littlefs_mount(void)
{
    elog_i("littlefs", "Mounting filesystem...");
    
    int err = lfs_mount(&lfs, &cfg);
    if (err != LFS_ERR_OK) {
        elog_e("littlefs", "Mount failed: %d", err);
        return -1;
    }
    
    elog_i("littlefs", "Filesystem mounted successfully");
    return 0;
}

int littlefs_unmount(void)
{
    elog_i("littlefs", "Unmounting filesystem...");
    
    int err = lfs_unmount(&lfs);
    if (err != LFS_ERR_OK) {
        elog_e("littlefs", "Unmount failed: %d", err);
        return -1;
    }
    
    elog_i("littlefs", "Filesystem unmounted");
    return 0;
}

五、文件系统操作API封装

为了方便使用,我们对LittleFS的底层API进行封装:

c 复制代码
/* littlefs_api.h */
#ifndef LITTLEFS_API_H
#define LITTLEFS_API_H

#include <stdint.h>
#include <stdbool.h>

#define LFS_MAX_PATH_LEN 256
#define LFS_MAX_FILENAME 64

typedef struct {
    char name[LFS_MAX_FILENAME];
    uint32_t size;
    bool is_dir;
} lfs_file_info_t;

int lfs_mkdir(const char *path);
int lfs_remove(const char *path);
int lfs_rename(const char *old_path, const char *new_path);
int lfs_stat(const char *path, lfs_file_info_t *info);
int lfs_list_dir(const char *path, lfs_file_info_t *files, uint32_t *count);
uint32_t lfs_get_free_space(void);
int lfs_file_read_all(const char *path, uint8_t *buffer, uint32_t size);
int lfs_file_write_all(const char *path, const uint8_t *data, uint32_t size);

#endif /* LITTLEFS_API_H */
c 复制代码
/* littlefs_api.c */
#include "littlefs_api.h"
#include "littlefs_port.h"
#include "elog.h"
#include <string.h>

extern lfs_t lfs;

int lfs_mkdir(const char *path)
{
    elog_i("littlefs", "Create directory: %s", path);
    
    int err = lfs_mkdir(&lfs, path);
    if (err != LFS_ERR_OK && err != LFS_ERR_EXIST) {
        elog_e("littlefs", "Create directory failed: %d", err);
        return -1;
    }
    
    return 0;
}

int lfs_remove(const char *path)
{
    elog_i("littlefs", "Remove: %s", path);
    
    int err = lfs_remove(&lfs, path);
    if (err != LFS_ERR_OK) {
        elog_e("littlefs", "Remove failed: %d", err);
        return -1;
    }
    
    return 0;
}

int lfs_rename(const char *old_path, const char *new_path)
{
    elog_i("littlefs", "Rename: %s -> %s", old_path, new_path);
    
    int err = lfs_rename(&lfs, old_path, new_path);
    if (err != LFS_ERR_OK) {
        elog_e("littlefs", "Rename failed: %d", err);
        return -1;
    }
    
    return 0;
}

int lfs_stat(const char *path, lfs_file_info_t *info)
{
    struct lfs_info lfs_info;
    
    int err = lfs_stat(&lfs, path, &lfs_info);
    if (err != LFS_ERR_OK) {
        elog_e("littlefs", "Stat failed: %d", err);
        return -1;
    }
    
    strncpy(info->name, lfs_info.name, LFS_MAX_FILENAME - 1);
    info->name[LFS_MAX_FILENAME - 1] = '\0';
    info->size = lfs_info.size;
    info->is_dir = (lfs_info.type == LFS_TYPE_DIR);
    
    return 0;
}

int lfs_list_dir(const char *path, lfs_file_info_t *files, uint32_t *count)
{
    lfs_dir_t dir;
    struct lfs_info lfs_info;
    uint32_t index = 0;
    
    int err = lfs_dir_open(&lfs, &dir, path);
    if (err != LFS_ERR_OK) {
        elog_e("littlefs", "Open directory failed: %d", err);
        return -1;
    }
    
    while (lfs_dir_read(&lfs, &dir, &lfs_info)) {
        if (strcmp(lfs_info.name, ".") == 0 || strcmp(lfs_info.name, "..") == 0) {
            continue;
        }
        
        if (index < *count) {
            strncpy(files[index].name, lfs_info.name, LFS_MAX_FILENAME - 1);
            files[index].name[LFS_MAX_FILENAME - 1] = '\0';
            files[index].size = lfs_info.size;
            files[index].is_dir = (lfs_info.type == LFS_TYPE_DIR);
            index++;
        } else {
            break;
        }
    }
    
    lfs_dir_close(&lfs, &dir);
    *count = index;
    
    return 0;
}

uint32_t lfs_get_free_space(void)
{
    lfs_ssize_t used = lfs_fs_size(&lfs);
    if (used < 0) {
        return 0;
    }
    
    return (LFS_BLOCK_COUNT - used) * LFS_BLOCK_SIZE;
}

int lfs_file_read_all(const char *path, uint8_t *buffer, uint32_t size)
{
    lfs_file_t file;
    lfs_ssize_t read_size;
    
    int err = lfs_file_open(&lfs, &file, path, LFS_O_RDONLY);
    if (err != LFS_ERR_OK) {
        elog_e("littlefs", "Open file failed: %d", err);
        return -1;
    }
    
    read_size = lfs_file_read(&lfs, &file, buffer, size);
    lfs_file_close(&lfs, &file);
    
    if (read_size < 0) {
        elog_e("littlefs", "Read file failed: %d", (int)read_size);
        return -1;
    }
    
    return (int)read_size;
}

int lfs_file_write_all(const char *path, const uint8_t *data, uint32_t size)
{
    lfs_file_t file;
    lfs_ssize_t write_size;
    
    int err = lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
    if (err != LFS_ERR_OK) {
        elog_e("littlefs", "Open file failed: %d", err);
        return -1;
    }
    
    write_size = lfs_file_write(&lfs, &file, data, size);
    lfs_file_close(&lfs, &file);
    
    if (write_size < 0) {
        elog_e("littlefs", "Write file failed: %d", (int)write_size);
        return -1;
    }
    
    return (int)write_size;
}

六、应用示例

6.1 文件系统初始化

c 复制代码
/* main.c - 文件系统初始化 */
#include "littlefs_port.h"
#include "littlefs_api.h"
#include "w25q128.h"
#include "elog.h"

void filesystem_init(void)
{
    elog_i("app", "Initializing filesystem...");
    
    if (w25q128_init() != 0) {
        elog_e("app", "W25Q128 initialization failed");
        return;
    }
    
    if (littlefs_init() != 0) {
        elog_e("app", "LittleFS initialization failed");
        return;
    }
    
    if (littlefs_mount() != 0) {
        elog_w("app", "Mount failed, trying to format...");
        
        if (littlefs_format() != 0) {
            elog_e("app", "Format failed");
            return;
        }
        
        if (littlefs_mount() != 0) {
            elog_e("app", "Mount after format failed");
            return;
        }
    }
    
    elog_i("app", "Filesystem initialized successfully");
    elog_i("app", "Free space: %d bytes", lfs_get_free_space());
}

6.2 文件读写示例

c 复制代码
/* file_operations.c - 文件操作示例 */
#include "littlefs_api.h"
#include "elog.h"
#include <string.h>

void file_write_read_test(void)
{
    const char *test_file = "/test/data.txt";
    const char *test_data = "Hello, LittleFS on WS63!";
    uint8_t read_buffer[256];
    int ret;
    
    elog_i("test", "=== File Write/Read Test ===");
    
    ret = lfs_file_write_all(test_file, (const uint8_t *)test_data, strlen(test_data));
    if (ret < 0) {
        elog_e("test", "Write file failed");
        return;
    }
    elog_i("test", "Write %d bytes to %s", ret, test_file);
    
    ret = lfs_file_read_all(test_file, read_buffer, sizeof(read_buffer));
    if (ret < 0) {
        elog_e("test", "Read file failed");
        return;
    }
    read_buffer[ret] = '\0';
    elog_i("test", "Read %d bytes from %s: %s", ret, test_file, read_buffer);
}

6.3 目录操作示例

c 复制代码
/* dir_operations.c - 目录操作示例 */
#include "littlefs_api.h"
#include "elog.h"

void directory_operations_test(void)
{
    lfs_file_info_t files[32];
    uint32_t count = 32;
    
    elog_i("test", "=== Directory Operations Test ===");
    
    lfs_mkdir("/config");
    lfs_mkdir("/data");
    lfs_mkdir("/log");
    
    lfs_file_write_all("/config/settings.ini", (const uint8_t *)"version=1.0", 12);
    lfs_file_write_all("/data/sensor.txt", (const uint8_t *)"temp=25.5", 10);
    
    elog_i("test", "Listing root directory:");
    count = 32;
    lfs_list_dir("/", files, &count);
    for (uint32_t i = 0; i < count; i++) {
        elog_i("test", "  %s [%s] %d bytes", 
               files[i].name,
               files[i].is_dir ? "DIR" : "FILE",
               files[i].size);
    }
    
    elog_i("test", "Free space: %d bytes", lfs_get_free_space());
}

6.4 配置文件存储示例

c 复制代码
/* config_storage.c - 配置文件存储 */
#include "littlefs_api.h"
#include "elog.h"
#include <stdio.h>
#include <string.h>

typedef struct {
    char wifi_ssid[32];
    char wifi_password[64];
    int log_level;
    int sample_interval;
} app_config_t;

int config_save(const app_config_t *config)
{
    char buffer[256];
    int len;
    
    len = snprintf(buffer, sizeof(buffer),
                   "wifi_ssid=%s\n"
                   "wifi_password=%s\n"
                   "log_level=%d\n"
                   "sample_interval=%d\n",
                   config->wifi_ssid,
                   config->wifi_password,
                   config->log_level,
                   config->sample_interval);
    
    if (lfs_file_write_all("/config/app.cfg", (const uint8_t *)buffer, len) < 0) {
        elog_e("config", "Save config failed");
        return -1;
    }
    
    elog_i("config", "Config saved successfully");
    return 0;
}

int config_load(app_config_t *config)
{
    char buffer[256];
    int len;
    char *line;
    
    len = lfs_file_read_all("/config/app.cfg", (uint8_t *)buffer, sizeof(buffer) - 1);
    if (len < 0) {
        elog_e("config", "Load config failed");
        return -1;
    }
    buffer[len] = '\0';
    
    line = strtok(buffer, "\n");
    while (line != NULL) {
        if (strncmp(line, "wifi_ssid=", 10) == 0) {
            strncpy(config->wifi_ssid, line + 10, sizeof(config->wifi_ssid) - 1);
        } else if (strncmp(line, "wifi_password=", 14) == 0) {
            strncpy(config->wifi_password, line + 14, sizeof(config->wifi_password) - 1);
        } else if (strncmp(line, "log_level=", 10) == 0) {
            config->log_level = atoi(line + 10);
        } else if (strncmp(line, "sample_interval=", 16) == 0) {
            config->sample_interval = atoi(line + 16);
        }
        line = strtok(NULL, "\n");
    }
    
    elog_i("config", "Config loaded: SSID=%s, log_level=%d", 
           config->wifi_ssid, config->log_level);
    return 0;
}

七、测试验证

7.1 功能测试

c 复制代码
/* filesystem_test.c - 文件系统测试 */
#include "littlefs_api.h"
#include "elog.h"
#include <string.h>

void filesystem_test(void)
{
    elog_i("test", "========== Filesystem Test Start ==========");
    
    file_write_read_test();
    directory_operations_test();
    
    elog_i("test", "Stress test: Write/Read 1000 times");
    const char *stress_file = "/test/stress.dat";
    uint8_t write_buf[256];
    uint8_t read_buf[256];
    
    for (int i = 0; i < 1000; i++) {
        memset(write_buf, i % 256, sizeof(write_buf));
        
        if (lfs_file_write_all(stress_file, write_buf, sizeof(write_buf)) < 0) {
            elog_e("test", "Stress test write failed at iteration %d", i);
            break;
        }
        
        if (lfs_file_read_all(stress_file, read_buf, sizeof(read_buf)) < 0) {
            elog_e("test", "Stress test read failed at iteration %d", i);
            break;
        }
        
        if (memcmp(write_buf, read_buf, sizeof(write_buf)) != 0) {
            elog_e("test", "Stress test data mismatch at iteration %d", i);
            break;
        }
        
        if ((i + 1) % 100 == 0) {
            elog_i("test", "Completed %d iterations", i + 1);
        }
    }
    
    elog_i("test", "Free space after test: %d bytes", lfs_get_free_space());
    elog_i("test", "========== Filesystem Test End ==========");
}

7.2 性能测试

c 复制代码
/* performance_test.c - 性能测试 */
#include "littlefs_api.h"
#include "elog.h"
#include "cmsis_os2.h"
#include <string.h>

void performance_test(void)
{
    elog_i("perf", "========== Performance Test ==========");
    
    const char *perf_file = "/test/perf.dat";
    uint8_t *buffer;
    uint32_t test_sizes[] = {256, 1024, 4096, 16384};
    
    buffer = malloc(16384);
    if (buffer == NULL) {
        elog_e("perf", "Memory allocation failed");
        return;
    }
    
    memset(buffer, 0xAA, 16384);
    
    for (int i = 0; i < sizeof(test_sizes) / sizeof(test_sizes[0]); i++) {
        uint32_t size = test_sizes[i];
        uint32_t iterations = 100;
        uint32_t start, elapsed;
        float throughput;
        
        start = osKernelGetTickCount();
        for (int j = 0; j < iterations; j++) {
            lfs_file_write_all(perf_file, buffer, size);
        }
        elapsed = osKernelGetTickCount() - start;
        throughput = (float)(size * iterations) / elapsed * 1000.0f / 1024.0f;
        elog_i("perf", "Write %d bytes x %d: %d ms, %.2f KB/s", 
               size, iterations, elapsed, throughput);
        
        start = osKernelGetTickCount();
        for (int j = 0; j < iterations; j++) {
            lfs_file_read_all(perf_file, buffer, size);
        }
        elapsed = osKernelGetTickCount() - start;
        throughput = (float)(size * iterations) / elapsed * 1000.0f / 1024.0f;
        elog_i("perf", "Read %d bytes x %d: %d ms, %.2f KB/s", 
               size, iterations, elapsed, throughput);
    }
    
    free(buffer);
    elog_i("perf", "========== Performance Test End ==========");
}

7.3 断电保护测试

c 复制代码
/* power_cycle_test.c - 断电保护测试 */
#include "littlefs_api.h"
#include "elog.h"
#include <string.h>

void power_cycle_test(void)
{
    elog_i("test", "========== Power Cycle Test ==========");
    
    const char *test_file = "/test/power.dat";
    const char *test_data = "Power cycle test data";
    uint8_t read_buffer[256];
    
    if (lfs_file_read_all(test_file, read_buffer, sizeof(read_buffer)) > 0) {
        read_buffer[sizeof(test_data)] = '\0';
        if (strcmp((char *)read_buffer, test_data) == 0) {
            elog_i("test", "Power cycle test: Data integrity verified!");
        } else {
            elog_e("test", "Power cycle test: Data corrupted!");
        }
    } else {
        elog_i("test", "Power cycle test: No existing data, writing test data");
        lfs_file_write_all(test_file, (const uint8_t *)test_data, strlen(test_data));
        elog_i("test", "Power cycle test: Test data written, please power cycle now");
    }
    
    elog_i("test", "========== Power Cycle Test End ==========");
}

八、常见问题与解决方案

8.1 挂载失败

问题:文件系统挂载失败,返回LFS_ERR_CORRUPT错误。

原因:Flash数据损坏或首次使用未格式化。

解决方案

c 复制代码
if (littlefs_mount() != 0) {
    elog_w("app", "Mount failed, formatting...");
    if (littlefs_format() == 0) {
        littlefs_mount();
    }
}

8.2 写入失败

问题:文件写入失败,返回LFS_ERR_NOSPC错误。

原因:存储空间不足。

解决方案

c 复制代码
uint32_t free_space = lfs_get_free_space();
if (free_space < required_size) {
    elog_w("app", "Not enough space: %d bytes required, %d bytes available", 
           required_size, free_space);
    lfs_remove("/log/old.log");
}

8.3 SPI通信错误

问题:Flash读写操作频繁失败。

原因:SPI时钟频率过高或片选信号时序问题。

解决方案

c 复制代码
/* 降低SPI时钟频率 */
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
HAL_SPI_Init(&hspi1);

/* 增加片选信号切换延迟 */
static void w25q128_cs_low(void)
{
    HAL_GPIO_WritePin(W25Q128_CS_PORT, W25Q128_CS_PIN, GPIO_PIN_RESET);
    osDelay(1);
}

static void w25q128_cs_high(void)
{
    osDelay(1);
    HAL_GPIO_WritePin(W25Q128_CS_PORT, W25Q128_CS_PIN, GPIO_PIN_SET);
}

8.4 文件系统性能优化

问题:文件读写速度较慢。

解决方案

c 复制代码
/* 增加缓存大小 */
#define LFS_CACHE_SIZE   1024
#define LFS_LOOKAHEAD_SIZE 32

/* 使用更大的读写块大小 */
#define LFS_READ_SIZE    512
#define LFS_PROG_SIZE    512

九、总结

本文档详细介绍了将LittleFS文件系统移植到海思WS63平台的完整过程,包括:

  1. 硬件配置:W25Q128 NOR Flash与WS63平台的SPI连接
  2. 驱动实现:W25Q128底层驱动和LittleFS移植层
  3. API封装:提供简洁易用的文件操作接口
  4. 应用示例:文件读写、目录操作、配置存储等实用示例
  5. 测试验证:功能测试、性能测试、断电保护测试
  6. 问题解决:常见问题的诊断和解决方案

通过本次移植,WS63平台获得了可靠的文件系统支持,可以方便地进行数据持久化存储和文件管理。LittleFS的断电保护和磨损均衡特性,确保了在嵌入式环境下的数据安全性和Flash寿命。

十、参考资料

相关推荐
键盘鼓手苏苏16 小时前
Flutter 三方库 p2plib 的鸿蒙化适配指南 - 实现高性能的端到端(P2P)加密通讯、支持分布式节点发现与去中心化数据流传输实战
flutter·harmonyos·鸿蒙·openharmony
加农炮手Jinx16 小时前
Flutter 组件 heart 适配鸿蒙 HarmonyOS 实战:分布式心跳监控,构建全场景保活检测与链路哨兵架构
flutter·harmonyos·鸿蒙·openharmony
王码码203516 小时前
Flutter 三方库 dns_client 的鸿蒙化适配指南 - 告别 DNS 劫持、探索 DNS-over-HTTPS (DoH) 技术、构建安全的鸿蒙网络请求环境
flutter·harmonyos·鸿蒙·openharmony·dns_client
键盘鼓手苏苏16 小时前
Flutter 组件 highlighter 适配鸿蒙 HarmonyOS 实战:高性能语法高亮,构建大规模代码分析与文本染色架构
flutter·harmonyos·鸿蒙·openharmony
雷帝木木16 小时前
Flutter 三方库 http_client_interceptor 的鸿蒙化适配指南 - 实现原生 HttpClient 的全量请求拦截、支持端侧动态 Headers 注入与网络流量审计实战
flutter·harmonyos·鸿蒙·openharmony·http_client_interceptor
ocr_ww20 小时前
护照阅读器助力传统酒店智慧转型的介绍
智能手机·智能硬件
坚果派·白晓明3 天前
三方库ada
harmonyos·鸿蒙·openharmony
学嵌入式的小杨同学3 天前
STM32 进阶封神之路(二十一):DMA 深度解析 —— 从直接内存访问到无 CPU 干预数据传输(底层原理 + 寄存器配置)
stm32·单片机·嵌入式硬件·mcu·硬件架构·硬件工程·智能硬件
特立独行的猫a4 天前
CMake与GN构建系统对比及GN使用指南
harmonyos·cmake·openharmony·构建·gn