跳转至

SD卡

一、演示功能概述

1.1 SD 卡简介

  • 定义:SD 卡(Secure Digital Card)是一种基于半导体快闪记忆器的新一代记忆设备,被广泛用于便携式设备中存储数据。
  • 特点:高存储容量、快速数据传输速度、体积小、重量轻、安全性高(支持数据加密)。

1.2 SD 卡类型与规格

  • 标准 SD 卡:原始 SD 卡规格。
  • miniSD 卡:缩小版的 SD 卡。
  • microSD 卡(又称 TF 卡):最小的 SD 卡规格,常用于智能手机和微型设备。

1.3 SD 卡工作原理

  • 文件系统:通常使用 FAT 文件系统(如 FAT16、FAT32)。
  • 通信协议:基于 SPI 协议进行数据传输。

本 demo 展示了使用 Air780EHM 核心板通过 fatfs 库和 io 库实现对 tf 卡的高效操作,并可以挂载 fatfs 文件系统,通过文件系统相关接口去操作 fatfs 文件系统中的文件,并演示文件的读写、删除、追加以及 HTTP 服务器下载到 SD 卡等操作。

二、准备硬件环境

注意:本功能780EPM不支持。

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

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

AirMICROSD_1000 模块:点击购买

SD 卡:

可淘宝上自行选择购买

连接图:

接线方法:

Air780EHM 连接 AirMICROSD_1000

83/SPI0CS -----------------CS

84/SPI0MISO ------------MISO

85/SPI0MOSI -----------MOSI

86/SPI0CLK -----------CLK

24/VDD_EXT -----------VCC

GND -----------------GND

三、准备软件环境

烧录工具:Luatools 工具

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

内核固件:https://docs.openluat.com/air780epm/luatos/firmware/version/version/ ,本文编写的时候使用的是LuatOS-SoC_V2008_Air780EHM.soc

脚本文件:https://gitee.com/openLuat/LuatOS/tree/master/module/Air780EHM/demo/tf

LuatOS 运行所需要的 lib 文件:

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

四、代码 API 和代码解析

4.1 API 接口介绍

本教程使用 api 接口为:

https://docs.openluat.com/osapi/core/fatfs/

https://docs.openluat.com/osapi/core/io/

https://docs.openluat.com/osapi/core/fs/

4.2 软件代码介绍

代码中有详细的注释,可以用来参考:

main.lua 里面加载 tf_test.lua 文件,

--[[
@module  main
@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
@version 1.0
@date    2025.07.02
@author  李源龙
@usage
demo演示的功能为
使用Air780EHM核心板通过fatfs库和io库实现对tf卡的高效操作,并可以挂载fatfs文件系统,通过文件系统相关接口去操作fatfs文件系统中的文件,并演示文件的读写、删除、追加以及HTTP服务器下载到SD卡等操作
]]

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

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

tf_test.lua 主要是先挂载 SD 卡,然后打印下 SD 卡的信息空间内容,只读模式打开文件,读取文件内容,然后写入模式打开文件,写入内容,读取文件区和 SD 卡区的信息,用追加模式,追加写入 ABCdef,然后打开文件读出来内容和写入的内容进行对比,最后按行写入内容,然后按行读取出来,最终使用 HTTP 下载到 SD 卡里面,读取文件的大小。

--[[
@module  tf_test
@summary tf_test测试功能模块
@version 1.0
@date    2025.07.02
@author  李源龙
@usage
使用Air780EHM核心板通过fatfs库和io库实现对tf卡的高效操作,并可以挂载fatfs文件系统,通过文件系统相关接口去操作fatfs文件系统中的文件,并演示文件的读写、删除、追加以及HTTP服务器下载到SD卡等操作。
以 Air780EHM核心板为例,和AirMICROSD_1000接线如下:
Air780EHM            AirMICROSD_1000
GND(任意)            GND
VDD_EXT              VCC
GPIO8/SPI0_CS        CS,片选
SPI0_SLK             CLK,时钟
SPI0_MOSI            MOSI,主机输出,从机输入
SPI0_MISO            MISO,主机输入,从机输出
]]

local function main_task()
    sys.wait(1000)
    -- fatfs.debug(1) -- 若挂载失败,可以尝试打开调试信息,查找原因

    -- 此为spi方式挂载SD卡
    local spi_id, pin_cs,tp = 0,8 
    spi.setup(spi_id, nil, 0, 0, pin_cs, 400 * 1000)
    gpio.setup(pin_cs, 1)
    fatfs.mount(fatfs.SPI, "/sd", spi_id, pin_cs, 24 * 1000 * 1000)

    --获取SD卡的可用空间信息
    local data, err = fatfs.getfree("/sd")
    if data then
        log.info("fatfs", "getfree", json.encode(data))
    else
        log.info("fatfs", "err", err)
    end

    -- #################################################
    -- 文件操作测试
    -- #################################################
    --只读模式,打开文件
    local f = io.open("/sd/boottime", "rb")
    local c = 0
    if f then
        --读取文件内容"a": 从当前位置开始读取整个文件。 如果已在文件末尾,返回空串。
        local data = f:read("*a")   
        log.info("fs", "data", data, data:toHex())
        c = tonumber(data)
        f:close()
    end
    log.info("fs", "boot count", c)
    if c == nil then
        c = 0
    end
    c = c + 1
    --写入模式,打开文件
    f = io.open("/sd/boottime", "wb")
    if f ~= nil then
        log.info("fs", "write c to file", c, tostring(c))
        f:write(tostring(c))
        f:close()
    else
        log.warn("sdio", "mount not good?!")
    end
    --获取文件系统信息
    if fs then
        log.info("fsstat", fs.fsstat("/"))
        log.info("fsstat", fs.fsstat("/sd"))
    end

    -- 测试一下追加
    os.remove("/sd/test_a")
    sys.wait(50)
    --打开文件,写入模式,写入内容
    f = io.open("/sd/test_a", "w")
    if f then
        f:write("ABC")
        f:close()
    end
    --打开文件,追加模式,写入内容
    f = io.open("/sd/test_a", "a+")
    if f then
        f:write("def")
        f:close()
    end
    --打开文件,只读模式
    f = io.open("/sd/test_a", "r")
    --对比下数据是不是和写入的一样
    if  f then
        local data = f:read("*a")
        log.info("data", data, data == "ABCdef")
        f:close()
    end

    -- 测试一下按行读取
    f = io.open("/sd/testline", "w")
    if f then
        f:write("abc\n")
        f:write("123\n")
        f:write("wendal\n")
        f:close()
    end
    sys.wait(100)
    f = io.open("/sd/testline", "r")
    if f then
        log.info("sdio", "line1", f:read("*l"))
        log.info("sdio", "line2", f:read("*l"))
        log.info("sdio", "line3", f:read("*l"))
        f:close()
    end

    -- 测试一下http下载到sd卡里面
    local code, headers, body =
        http.request("GET", "http://airtest.openluat.com:2900/download/1.mp3", nil, nil, {dst = "/sd/1.mp3"}).wait()
    --存到sd卡里面
    log.info("下载完成", code, headers, body)
    log.info("io.fileSize",io.fileSize("/sd/1.mp3"))
end

sys.taskInit(main_task)

6.2 效果展示

通过日志可以查看到 sd 卡已经挂载成功,并且打印了可用空间大小和文件系统的信息,文件系统操作的操作也都成功,HTTP 下载和读取文件大小也都可以读取出来。

七、总结

至此,我们已使用 Air780EHM 核心板搭配 AirMICROSD_1000 通过 SPI 方式,完成了对 SD 卡挂载 fatfs、以及对 SD 卡中文件读写操作,然后使用 HTTP 的方式把文件下载到 SD 卡区里面。

八、常见问题

1. 为什么不能识别新购买的 sd 卡:

文件系为 FAT32 格式(windows、linux 都可以正常识别),所以非 FAT 格式的 SD 卡会挂载失败,而无法正常识别。

2. SD 卡的读写路径是什么?

可以通过 fatfs.mount()的第二个参数去自定义 sd 卡的文件夹名称,本文演示的是"/sd",SD 卡文件访问通过路径前加上"/sd",如果 sd 卡中有一个文件 test.txt ,那这个文件的路径就是"/sd/test.txt"。

3. http 下载的文件可以直接保存到 sd 卡里吗?

支持,http.request 接口支持直接下载到文件系统中,下载到 sd 卡中的时候只需要注意路径设置。参考 demo 中的 HTTP 部分。