跳转至

SPI Flash sfud应用(Air780EPM不支持)

一、演示功能概述

1.1 Little_Flash 介绍

SFUD 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。

  • 主要特点:支持 SPI/QSPI 接口、面向对象(同时支持多个 Flash 对象)、可灵活裁剪、扩展性强、支持 4 字节地址
  • 资源占用

  • 标准占用:RAM:0.2KB ROM:5.5KB

  • 最小占用:RAM:0.1KB ROM:3.6KB

1.2 教程概述

使用 Air780EHM 核心板通过 SFUD 库实现对 SPI Flash 的高效操作,并可以挂载 sfud lfs 文件系统,通过文件系统相关接口去操作 sfud lfs 文件系统中的文件,并演示文件的读写、删除、追加等操作。

二、准备硬件环境

参考:硬件环境清单第二章节内容,准备以及组装好硬件环境。

准备一块 Air780EHM 核心板:点击购买

SPI_Flash 模块:

需要选用支持 SFUD 标准的 spi Flash。

本次教程使用的是 W25Q32,此模块可在淘宝店购买 luat.taobao.com。

组装好如图所示:

三、准备软件环境

烧录工具:Luatools 工具

Air780EHM 烧录需要的固件和脚本文件:

LuatOS 运行所需要的 lib 文件:

使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件

四、软硬件资料

4.1 API 接口介绍

58 sfud - 合宙模组资料中心

17 fs - 合宙模组资料中心

4.2 Air780EHM 与 SPI_Flash 接线指示

Air780EHM核心板
SPI_Flash
GND(任意)
GND
VDD_EXT
VCC
GPIO8/SPI0_CS
CS,片选
SPI0_SLK
CLK,时钟
SPI0_MOSI
DI,主机输出,从机输入
SPI0_MISO
DO,主机输入,从机输出

五、代码示例介绍

5.1 代码介绍

--[[
@module  main
@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
@version 1.0
@date    2025.07.01
@author  孟伟
@usage
本demo演示的功能为:
使用Air780EHM核心板通过SFUD库实现对SPI Flash的高效操作,并可以挂载sfud lfs文件系统,通过文件系统相关接口去操作sfud lfs文件系统中的文件,并演示文件的读写、删除、追加等操作。
]]

--[[
必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
PROJECT:项目名,ascii string类型
        可以随便定义,只要不使用,就行
VERSION:项目版本号,ascii string类型
        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
PROJECT = "sfuddemo"
VERSION = "001.000.000"

-- 在日志中打印项目名和项目版本号
log.info("main", PROJECT, VERSION)

--添加硬狗防止程序卡死
if wdt then
    wdt.init(9000)--初始化watchdog设置为9s
    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
end

-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
-- 启动errDump日志存储并且上传功能,600秒上传一次
-- if errDump then
--     errDump.config(true, 600)
-- end

-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
-- 可以使用合宙的iot.openluat.com平台进行远程升级
-- 也可以使用客户自己搭建的平台进行远程升级
-- 远程升级的详细用法,可以参考fota的demo进行使用

-- 启动一个循环定时器
-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
-- 方便分析内存使用是否有异常
-- sys.timerLoopStart(function()
--     log.info("mem.lua", rtos.meminfo())
--     log.info("mem.sys", rtos.meminfo("sys"))
-- end, 3000)

--加载SFUD测试应用模块
require "sfud_test"

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
--[[
@module  sfud_test
@summary sfud_test测试功能模块
@version 1.0
@date    2025.07.01
@author  孟伟
@usage
本demo演示的功能为:使用Air780EHM核心板通过SFUD库实现对SPI Flash的高效操作,并可以挂载sfud lfs文件系统,通过文件系统相关接口去操作sfud lfs文件系统中的文件,并演示文件的读写、删除、追加等操作。
以 Air780EHM核心板为例, 接线如下:
Air780EHM            SPI_FLASH
GND(任意)            GND
VDD_EXT              VCC
GPIO8/SPI0_CS        CS,片选
SPI0_SLK             CLK,时钟
SPI0_MOSI            DI,主机输出,从机输入
SPI0_MISO            DO,主机输入,从机输出
]]
--使用SPI0,CS接在gpio8上
local spi_id,pin_cs = 0,8

function sfud_test_func()
    sys.wait(1000)
    log.info("sfud", "SPI", spi_id, "CS PIN", pin_cs)
    --以对象的方式初始化spi_flash
    spi_flash = spi.deviceSetup(spi_id,pin_cs,0,0,8,20*1000*1000,spi.MSB,1,0)
    log.info("sfud", "spi_flash", spi_flash)
    --初始化sfud,并判断初始化结果
    local ret = sfud.init(spi_flash)
    if ret then
        log.info("sfud.init ok")
    else
        log.info("sfud.init Error")
        return
    end
    --获取flash设备信息表中的设备总数
    log.info("sfud.getDeviceNum",sfud.getDeviceNum())
    --获取flash设备信息表
    local sfud_device = sfud.getDeviceTable()
    --判断是否支持sfud.getInfo接口,支持的话获取 Flash 容量和page大小
    if sfud.getInfo then
        log.info("sfud.getInfo", sfud.getInfo(sfud_device))
    end
    --定义两个变量,用来控制是通过sfud接口来操作flash还是挂载为sfud lfs文件系统。也可以同时使用两种方式,不过要注意同时使用时flash地址和挂载时的偏移量需要设计好
    local test_sfud_raw = true
    local test_sfud_mount = false
    --通过sfud接口对flash进行写入和读取操作
    if test_sfud_raw then
        local test_str = "luatos-sfud1234567890123456789012345678901234567890"
        log.info("sfud.eraseWrite",sfud.eraseWrite(sfud_device,1024,test_str))
        local read_str = sfud.read(sfud_device,1024,#test_str)
        if test_str == read_str then
            log.info("sfud 写入与读取数据成功")
        else
            log.info("sfud 写入与读取数据失败")
        end

    end
    --挂载为sfud lfs文件系统,通过文件系统相关接口去操作sfud lfs文件系统中的文件,并演示文件的读写、删除、追加等操作。
    if test_sfud_mount then
        --挂载sfud lfs文件系统
        local ret = sfud.mount(sfud_device,"/sfud")
        log.info("sfud.mount", ret)
        if ret then
            log.info("sfud", "挂载成功")
            log.info("fsstat", fs.fsstat("/sfud"))

            -- 挂载成功后,可以像操作文件一样操作
            local f = io.open("/sfud/test", "w")
            local write_str = os.date()
            log.info("/sfud/test文件写入数据",write_str)
            --写入当前时间到文件中
            f:write(write_str)
            --关闭文件
            f:close()
            --读取文件内容并打印
            local read_str = io.readFile("/sfud/test")
            log.info("sfud_lfs read", read_str)
            if read_str == write_str then
                log.info("写入测试成功,写入字符串与读出字符串一样")
            else
                log.info("写入测试失败,写入字符串与读出字符串不一样")
            end
            -- 文件追加
            --文件追加测试写入之前先删除一下
            os.remove("/sfud/test2")
            --将"LuatOS"字符串写入到test2文件中
            io.writeFile("/sfud/test2", "LuatOS-")
            --以追加的方式打开test2文件
            local f = io.open("/sfud/test2", "a+")
            local time_str = os.date()
            f:write(time_str)
            write_str = "LuatOS-"..time_str
            log.info("/sfud/test2",write_str)
            --关闭文件
            f:close()
            read_str = io.readFile("/sfud/test2")
            log.info("sfud read", read_str)
            if write_str == read_str then
                log.info("追加测试成功,写入字符串与读出字符串一样")
            else
                log.info("追加测试失败,写入字符串与读出字符串不一样")
            end
        else
            log.info("sfud", "挂载失败")
        end

    end
end
--创建并且启动一个task
--运行这个task的主函数sfud_test_func
sys.taskInit(sfud_test_func)

5.2 功能测试

在 sfud_test.lua 中,通过 test_sfud_raw 和 test_sfud_mountl 两个变量来控制是通过 sfud 接口去读写 Flash 还是挂载为 sfud lfs 文件系统,通过文件系统相关接口去操作 sfud lfs 文件系统中的文件。也可以同时使用这两种方式,不过要注意同时使用 sfud 直接写入时的地址和挂载 sfud lfs 时的偏移量需要设计好(注意偏移量需要是 64k 的倍数),避免发生冲突。

直接操作 sfud 接口去读写 Flash,写入字符串 luatos-sfud1234567890123456789012345678901234567890,然后读取前 4 字节数据,结果如下图所示:

我们将 Flash 设备成功挂载为 sfud lfs 文件系统后,通过标准化文件管理接口对文件系统进行了全流程验证。日志输出显示,文件系统挂载过程顺利完成,且文件读写、内容追加、文件删除等核心操作均能稳定执行,整个交互过程无异常报错信息,充分验证了 sfud lfs 文件系统在嵌入式存储场景下的功能完整性。

六、总结

通过本次演示,我们基于 Air780EHM 核心板完整呈现了 SFUD 驱动 SPI Flash 的完整流程,并重点展示了两种实战化读写方案:一是通过 SFUD 原生接口直接操作 Flash 存储单元,二是将 Flash 设备挂载为 sfud lfs 文件系统实现标准化文件管理。