02 SD卡
作者:王棚嶙
一、TF卡概述
1.1 SD 卡
1.1.1 SD 卡简介
- 定义:SD 卡(Secure Digital Card)是一种基于半导体快闪记忆器的新一代记忆设备,被广泛用于便携式设备中存储数据。
- 特点:高存储容量、快速数据传输速度、体积小、重量轻、安全性高(支持数据加密)。
1.1.2 SD 卡类型与规格
- 标准 SD 卡:原始 SD 卡规格。
- miniSD 卡:缩小版的 SD 卡。
- microSD 卡(又称 TF 卡):最小的 SD 卡规格,常用于智能手机和微型设备。
1.1.3 SD 卡工作原理
- 文件系统:通常使用 FAT 文件系统(如 FAT16、FAT32)。
- 通信协议:基于 SPI 协议进行数据传输。
冷知识:TF卡名字中的“T”代表“Tiny”(微型),而如今它已成为全球最小的通用存储卡标准。
1.2 TF卡没办法用,是怎么回事儿?
很多合宙的老朋友们的都有反馈过说自己的卡插进去了但是一直无法挂载成功怎么回事? 那么这里面就是涉及到一个兼容性的问题而TF卡兼容性是多维度的适配能力,涉及物理接口、传输协议、系统驱动及环境耐受性等。
1.2.1 设备兼容性
- 工业设备支持:工业级TF卡需与工业相机、PLC控制器、机器人等专业设备兼容,确保在严苛环境下稳定工作。
- 消费类设备:需适配智能手机、平板电脑、行车记录仪、无人机、监控摄像头等常见设备,例如:
- 行车记录仪:实时保存高清视频数据,需支持循环录制。
- 无人机/监控设备:保障长时间连续读写,如128G卡可支持25天1080P监控录制。
- 扩展性要求:部分设备仅支持特定容量(如SDHC卡≤32GB,SDXC卡≥32GB),需匹配设备规范。
1.2.2 接口与协议兼容性
- 速度标准:需符合设备支持的传输协议,例如:
- UHS-I/UHS-III:提供104MB/s至624MB/s带宽,影响4K视频录制等场景的流畅性。
- 视频速度等级(V30/V90):确保高帧率视频录制不丢帧,如V30卡可满足4K录制需求。
- 应用性能等级(A1/A2):影响随机读写速度,A2级卡更适合安装应用或游戏(如Switch),减少加载延迟。
1.2.3 系统与驱动兼容性
- 操作系统支持:
- Linux系统:需内核驱动(如
mmc模块)或用户空间驱动支持热插拔。 - Windows/macOS/Android:需免驱即插即用,部分工业场景需定制驱动程序。
- 文件系统适配:如FAT32/exFAT格式兼容不同设备,突发断电时需防护机制避免数据损坏。
1.2.4 环境与物理兼容性
- 温度范围:
- 工业级卡支持-25℃~85℃(如监控设备),消费级卡通常为0℃~70℃。
- 耐用性:工业卡采用加固外壳和防震设计,适应车载、工厂等振动环境。
1.2.5 功能兼容性
- 加密与安全:支持硬件加密技术,用于金融、安防等领域的数据保护。
- 特殊功能:如Wi-Fi TF卡支持无线传输,需配套应用程序协同工作。
1.3 兼容性问题的常见表现与解决
- 无法识别卡:检查驱动加载、卡槽物理损坏或文件系统错误。
- 读写速度慢:升级高速读卡器(如支持UHS-II的型号),或更换高性能卡(如V30/A2级)。
1.4 我们现在测试有哪些TF卡能用
联想,闪迪,三星,三星EVO,金士顿等大品牌TF卡。所以在各位选择使用的TF卡时,我们建议不要使用白牌卡,尽量选择大品牌高速卡。

二、演示功能概述
本demo演示了在嵌入式环境中对TF卡(SD卡)的完整操作流程,覆盖了从文件系统挂载到高级文件操作的完整功能链。项目分为两个核心模块:
1、main.lua:主程序入口 <br 2、tfcard_app.lua:TF卡基础应用模块,实现文件系统管理、文件操作和目录管理功能<br 3、http_download_file.lua:HTTP下载模块,实现网络检测与文件下载到TF卡的功能
三、演示硬件环境(二选一)
Air8101开发板
1、Air8101_B11开发板一块
2、TYPE-C USB数据线一根
3、闪迪C10高速TF卡一张(即micro SD卡,即微型SD卡)
4、Air8101开发板和数据线的硬件接线方式为
- Air8101开发板通过串口小板上的TYPE-C USB口供电。(USB旁边的开关拨到ON)
- TYPE-C USB数据线直接插到Air8101开发板配套的外接串口小板的的TYPE-C USB座子,另外一端连接电脑USB口
在我们 Air8101 开发板上已经集成了 SD 卡卡座:

参考:硬件环境清单,准备以及组装好硬件环境
Air8101开发板淘宝购买链接:点击购买
Air8101核心板
1、Air8101核心板一块
2、TYPE-C USB数据线一根
3、闪迪C10高速TF卡一张(即micro SD卡,即微型SD卡)
4、AirMICROSD_1010配件板一块
5、Air8101核心板和数据线的硬件接线方式为
- Air8101核心板通过板上的TYPE-C USB口供电。(正面的开关拨到3.3V,背面的开关拨到off)
- TYPE-C USB数据线直接插到Air8101核心板的TYPE-C USB座子,另外一端连接电脑USB口;
6、Air8101核心板与AirMICROSD_1010配件板通过杜邦线连接,对应管脚为
| Air8101核心板 | AirMICROSD_1010配件板 |
|---|---|
| 59/3V3 | 3V3 |
| gnd | gnd |
| 65/GPIO2 | spi_clk |
| 67/GPIO4 | spi_mosi |
| 66/GPIO3 | spi_cs |
| 8/GPIO5 | spi_miso |

Air8101核心板淘宝购买链接:点击购买
AirMICROSD_1010配件板淘宝购买链接:点击购买
四、准备软件环境
在开始实践本示例之前,先筹备一下软件环境:
1、烧录工具 Luatools;
2、本demo开发测试时使用的固件为Air8101 V2010 版本固件,本demo对固件版本没有什么特殊要求,所以你如果要测试本demo时,可以直接使用最新版本的内核固件;如果发现最新版本的内核固件测试有问题,可以使用我们开发本demo时使用的内核固件版本来对比测试;
3、LuatOS 需要的脚本和资源文件:脚本资源文件
4、lib 脚本文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件;
准备好软件环境之后,接下来查看如何烧录项目文件到 Air8101 开发板 - luatos@air8101 - 合宙文档中心,将本篇文章中演示使用的项目文件烧录到 Air8101 开发板中。
准备好软件环境之后,接下来查看如何烧录项目文件到 Air8101 核心板使用说明 - luatos@air8101 - 合宙文档中心,将本篇文章中演示使用的项目文件烧录到 Air8101 核心板中。
五、SD 卡软硬件参考
5.1 API 接口介绍
本教程使用 api 接口为:
注意:
1. luatos 的 api 接口是通用的。
2. 通常只使用 fatfs.mount 挂载 tf/sd 卡,其他操作走 io 库就可以了。
3. 挂载成 fatfs 后,可以通过 fs.fsstat 来获取文件系统信息,或 fs.fsize 来获取文件大小。
5.2 SD 硬件设计
SD 硬件设计参考电路:

六、演示核心步骤
6.1 挂载前准备
开启tf卡供电(GPIO13高电平)
-- 供电控制 (Air8101专用)
--gpio13为8101TF卡的供电控制引脚,在挂载前需要设置为高电平,不能省略
gpio.setup(13, 1)
-- 在Air8101核心板上TF卡的的pin_cs为gpio3,spi_id为1.请根据实际硬件修改
spi_id, pin_cs = 1, 3
spi.setup(spi_id, nil, 0, 0, 8, 2000000)
--初始化后拉高pin_cs,准备开始挂载TF卡
gpio.setup(pin_cs, 1)
6.2 TF卡核心演示模块(tfcard_app.lua)
6.2.1 文件系统挂载
调用fatfs库挂载TF卡到/sd路径
-- ########## 开始进行tf卡挂载 ##########
local mount_ok, mount_err = fatfs.mount(fatfs.SPI, "/sd", spi_id, pin_cs, 24 * 1000 * 1000)
if mount_ok then
log.info("fatfs.mount", "挂载成功", mount_err)
else
log.error("fatfs.mount", "挂载失败", mount_err)
goto resource_cleanup
end
6.2.2 文件操作实战
-- 执行tfcard文件操作演示
log.info("文件操作", "===== 开始文件操作 =====")
dir_path = "/sd/io_test"
-- 1. 创建目录
if not io.dexist(dir_path) then
if io.mkdir(dir_path) then
log.info("io.mkdir", "目录创建成功", "路径:" .. dir_path)
else
log.error("io.mkdir", "目录创建失败", "路径:" .. dir_path)
goto resource_cleanup
end
else
log.warn("io.mkdir", "目录已存在,跳过创建", "路径:" .. dir_path)
end
-- 2. 创建并写入文件
file_path = dir_path .. "/boottime"
file = io.open(file_path, "wb")
if file then
file:write("这是io库API文档示例的测试内容")
file:close()
--在LuatOS文件操作中,执行file:close()是必须且关键的操作,它用于关闭文件句柄,释放资源,并确保数据被正确写入磁盘。
-- 如果不执行file:close(),可能会导致数据丢失、文件损坏或其他不可预测的问题。
log.info("文件创建", "文件写入成功", "路径:" .. file_path)
else
log.error("文件创建", "文件创建失败", "路径:" .. file_path)
goto resource_cleanup
end
-- 3. 检查文件是否存在
if io.exists(file_path) then
log.info("io.exists", "文件存在", "路径:" .. file_path)
else
log.error("io.exists", "文件不存在", "路径:" .. file_path)
goto resource_cleanup
end
-- 4. 获取文件大小
file_size = io.fileSize(file_path)
if file_size then
log.info("io.fileSize", "文件大小:" .. file_size .. "字节", "路径:" .. file_path)
else
log.error("io.fileSize", "获取文件大小失败", "路径:" .. file_path)
goto resource_cleanup
end
-- 5. 读取文件内容
file = io.open(file_path, "rb")
if file then
content = file:read("*a")
log.info("文件读取", "路径:" .. file_path, "内容:" .. content)
file:close()
else
log.error("文件操作", "无法打开文件读取内容", "路径:" .. file_path)
goto resource_cleanup
end
-- 6. 启动计数文件操作
count = 0
--以只读模式打开文件
file = io.open(file_path, "rb")
if file then
data = file:read("*a")
log.info("启动计数", "文件内容:", data, "十六进制:", data:toHex())
count = tonumber(data) or 0
file:close()
else
log.warn("启动计数", "文件不存在或无法打开")
end
log.info("启动计数", "当前值:", count)
count=count + 1
log.info("启动计数", "更新值:", count)
file = io.open(file_path, "wb")
if file then
file:write(tostring(count))
file:close()
log.info("文件写入", "路径:" .. file_path, "内容:", count)
else
log.error("文件写入", "无法打开文件", "路径:" .. file_path)
goto resource_cleanup
end
-- 7. 文件追加测试
append_file = dir_path .. "/test_a"
-- 清理旧文件
os.remove(append_file)
-- 创建并写入初始内容
file = io.open(append_file, "wb")
if file then
file:write("ABC")
file:close()
log.info("文件创建", "路径:" .. append_file, "初始内容:ABC")
else
log.error("文件创建", "无法创建文件", "路径:" .. append_file)
goto resource_cleanup
end
-- 追加内容
file = io.open(append_file, "a+")
if file then
file:write("def")
file:close()
log.info("文件追加", "路径:" .. append_file, "追加内容:def")
else
log.error("文件追加", "无法打开文件进行追加", "路径:" .. append_file)
goto resource_cleanup
end
-- 验证追加结果
file = io.open(append_file, "r")
if file then
data = file:read("*a")
log.info("文件验证", "路径:" .. append_file, "内容:" .. data, "结果:",
data == "ABCdef" and "成功" or "失败")
file:close()
else
log.error("文件验证", "无法打开文件进行验证", "路径:" .. append_file)
goto resource_cleanup
end
-- 8. 按行读取测试
line_file = dir_path .. "/testline"
file = io.open(line_file, "w")
if file then
file:write("abc\n")
file:write("123\n")
file:write("wendal\n")
file:close()
log.info("文件创建", "路径:" .. line_file, "写入3行文本")
else
log.error("文件创建", "无法创建文件", "路径:" .. line_file)
goto resource_cleanup
end
-- 按行读取文件
file = io.open(line_file, "r")
if file then
log.info("按行读取", "路径:" .. line_file, "第1行:", file:read("*l"))
log.info("按行读取", "路径:" .. line_file, "第2行:", file:read("*l"))
log.info("按行读取", "路径:" .. line_file, "第3行:", file:read("*l"))
file:close()
else
log.error("按行读取", "无法打开文件", "路径:" .. line_file)
goto resource_cleanup
end
-- 9. 文件重命名
old_path = append_file
new_path = dir_path .. "/renamed_file.txt"
success, err = os.rename(old_path, new_path)
if success then
log.info("os.rename", "文件重命名成功", "原路径:" .. old_path, "新路径:" .. new_path)
-- 验证重命名结果
if io.exists(new_path) and not io.exists(old_path) then
log.info("验证结果", "重命名验证成功", "新文件存在", "原文件不存在")
else
log.error("验证结果", "重命名验证失败")
end
else
log.error("os.rename", "重命名失败", "错误:" .. tostring(err), "原路径:" .. old_path)
goto resource_cleanup
end
-- 10. 列举目录内容
log.info("目录操作", "===== 开始目录列举 =====")
ret, data = io.lsdir(dir_path, 50, 0) -- 50表示最多返回50个文件,0表示从目录开头开始
if ret then
log.info("fs", "lsdir", json.encode(data))
else
log.info("fs", "lsdir", "fail", ret, data)
goto resource_cleanup
end
-- 11. 删除文件测试
-- 测试删除renamed_file.txt文件
if os.remove(new_path) then
log.info("os.remove", "文件删除成功", "路径:" .. new_path)
-- 验证renamed_file.txt删除结果
if not io.exists(new_path) then
log.info("验证结果", "renamed_file.txt文件删除验证成功")
else
log.error("验证结果", "renamed_file.txt文件删除验证失败")
end
else
log.error("io.remove", "renamed_file.txt文件删除失败", "路径:" .. new_path)
goto resource_cleanup
end
-- 测试删除testline文件
if os.remove(line_file) then
log.info("os.remove", "testline文件删除成功", "路径:" .. line_file)
-- 验证删除结果
if not io.exists(line_file) then
log.info("验证结果", "testline文件删除验证成功")
else
log.error("验证结果", "testline文件删除验证失败")
end
else
log.error("io.remove", "testline文件删除失败", "路径:" .. line_file)
goto resource_cleanup
end
if os.remove(file_path) then
log.info("os.remove", "文件删除成功", "路径:" .. file_path)
-- 验证删除结果
if not io.exists(file_path) then
log.info("验证结果", "boottime文件删除验证成功")
else
log.error("验证结果", "boottime文件删除验证失败")
end
else
log.error("io.remove", "boottime文件删除失败", "路径:" .. file_path)
goto resource_cleanup
end
-- 12. 删除目录(不能删除非空目录,所以在删除目录前要确保目录内没有文件或子目录)
if io.rmdir(dir_path) then
log.info("io.rmdir", "目录删除成功", "路径:" .. dir_path)
-- 验证删除结果
if not io.exists(dir_path) then
log.info("验证结果", "目录删除验证成功")
else
log.error("验证结果", "目录删除验证失败")
end
else
log.error("io.rmdir", "目录删除失败", "路径:" .. dir_path)
goto resource_cleanup
end
log.info("文件操作", "===== 文件操作完成 =====")
6.3 HTTP下载功能 (http_download_file.lua)
local function http_download_file_task()
-- 阶段1: 网络就绪检测
-- 要连接的WIFI路由器名称
local ssid ="A上海合宙通讯"
-- 要连接的WIFI路由器密码
local password = "HZ88888888"
log.info("wifi", ssid, password)
wlan.init()
-- 连接WIFI
wlan.connect(ssid, password, 1)
while not socket.adapter(socket.dft()) do
log.warn("HTTP下载", "等待网络连接", socket.dft())
-- 等待IP_READY消息,超时设为1秒
sys.waitUntil("IP_READY", 1000)
end
-- 检测到了IP_READY消息
log.info("HTTP下载", "网络已就绪", socket.dft())
-- TF卡供电控制(AIR8101专用)
gpio.setup(13, 1)
-- 在Air8101核心板上TF卡的的pin_cs为gpio3,spi_id为1.请根据实际硬件修改
spi_id, pin_cs = 1, 3
spi.setup(spi_id, nil, 0, 0, 8, 2000000)
--初始化后拉高pin_cs,准备开始挂载TF卡
gpio.setup(pin_cs, 1)
-- ########## 开始进行tf卡挂载 ##########
local mount_ok, mount_err = fatfs.mount(fatfs.SPI, "/sd", spi_id, pin_cs, 24 * 1000 * 1000)
if not mount_ok then
log.error("HTTP下载", "文件系统挂载失败")
return
end
-- 阶段2: 执行下载任务
log.info("HTTP下载", "开始下载任务")
-- 核心下载操作开始 (支持http和https)
-- local code, headers, body = http.request("GET", "...", nil, nil, {dst = "/sd/1.mp3"}).wait()
-- 其中 "..."为url地址, 支持 http和https, 支持域名, 支持自定义端口。
local code, headers, body_size = http.request("GET",
"https://gitee.com/openLuat/LuatOS/raw/master/module/Air780EHM_Air780EHV_Air780EGH/demo/audio/1.mp3",
nil, nil, {dst = "/sd/1.mp3"}).wait()
-- 阶段3: 记录下载结果
log.info("HTTP下载", "下载完成",
code==200 and "success" or "error",
code,
-- headers是下载的文件头信息
json.encode(headers or {}),
-- body_size是下载的文件大小(字节数)
body_size)
if code == 200 then
-- 获取实际文件大小
local actual_size = io.fileSize("/sd/1.mp3")
log.info("HTTP下载", "文件大小验证", "预期:", body_size, "实际:", actual_size)
if actual_size~= body_size then
log.error("HTTP下载", "文件大小不一致", "预期:", body_size, "实际:", actual_size)
end
end
-- 阶段4: 资源清理
fatfs.unmount("/sd")
log.info("HTTP下载", "资源清理完成")
end
-- 创建下载任务
sys.taskInit(http_download_file_task)
6.4 安全卸载流程
-- ########## 功能: 收尾功能演示##########
-- 卸载文件系统
::resource_cleanup::
log.info("结束", "开始执行关闭操作...")
-- 如已挂载需先卸载文件系统
if mount_ok then
if fatfs.unmount("/sd") then
log.info("文件系统", "卸载成功")
else
log.error("文件系统", "卸载失败")
end
end
-- 2. 关闭SPI接口
spi.close(spi_id)
log.info("SPI接口", "已关闭")
end
七、日志展示
1、搭建好硬件环境
2、通过Luatools将demo与固件烧录到核心板或开发板中
3、烧录好后,板子开机将会在Luatools上看到如下打印:
(1)TF卡初始化与挂载
[2026-03-04 14:39:10.096] luat:U(702):I/user.fatfs.mount 挂载成功 0
[2026-03-04 14:39:10.096] luat:U(702):I/user.fatfs getfree {"free_sectors":122114816,"total_kb":61057440,"free_kb":61057408,"total_sectors":122114880}
[2026-03-04 14:39:10.096] luat:U(703):I/user.fs lsmount [{"fs":"lfs2","path":"/"},{"fs":"inline","path":"/lua/"},{"fs":"ram","path":"/ram/"},{"fs":"luadb","path":"/luadb/"},{"fs":"fatfs","path":"/sd"}]
(2)文件操作演示
[2026-03-04 14:39:10.096] luat:U(704):I/user.文件操作 ===== 开始文件操作 =====
[2026-03-04 14:39:10.245] luat:U(782):I/user.io.mkdir 目录创建成功 路径:/sd/io_test
[2026-03-04 14:39:10.245] luat:U(794):I/user.文件创建 文件写入成功 路径:/sd/io_test/boottime
[2026-03-04 14:39:10.245] luat:U(796):I/user.io.exists 文件存在 路径:/sd/io_test/boottime
[2026-03-04 14:39:10.245] luat:U(798):I/user.io.fileSize 文件大小:41字节 路径:/sd/io_test/boottime
[2026-03-04 14:39:10.245] luat:U(801):I/user.文件读取 路径:/sd/io_test/boottime 内容:这是io库API文档示例的测试内容
[2026-03-04 14:39:10.245] luat:U(804):I/user.启动计数 文件内容: 这是io库API文档示例的测试内容 十六进制: E8BF99E698AF696FE5BA93415049E69687E6A1A3E7A4BAE4BE8BE79A84E6B58BE8AF95E58685E5AEB9 82
[2026-03-04 14:39:10.245] luat:U(805):I/user.启动计数 当前值: 0
[2026-03-04 14:39:10.245] luat:U(805):I/user.启动计数 更新值: 1
[2026-03-04 14:39:10.245] luat:U(821):I/user.文件写入 路径:/sd/io_test/boottime 内容: 1
[2026-03-04 14:39:10.245] luat:U(834):I/user.文件创建 路径:/sd/io_test/test_a 初始内容:ABC
[2026-03-04 14:39:10.245] luat:U(840):I/user.文件追加 路径:/sd/io_test/test_a 追加内容:def
[2026-03-04 14:39:10.245] luat:U(843):I/user.文件验证 路径:/sd/io_test/test_a 内容:ABCdef 结果: 成功
[2026-03-04 14:39:10.324] luat:U(855):I/user.文件创建 路径:/sd/io_test/testline 写入3行文本
[2026-03-04 14:39:10.324] luat:U(858):I/user.按行读取 路径:/sd/io_test/testline 第1行: abc
[2026-03-04 14:39:10.324] luat:U(859):I/user.按行读取 路径:/sd/io_test/testline 第2行: 123
[2026-03-04 14:39:10.324] luat:U(859):I/user.按行读取 路径:/sd/io_test/testline 第3行: wendal
[2026-03-04 14:39:10.324] luat:U(864):I/user.os.rename 文件重命名成功 原路径:/sd/io_test/test_a 新路径:/sd/io_test/renamed_file.txt
[2026-03-04 14:39:10.324] luat:D(868):fatfs:f_open /io_test/test_a 4
[2026-03-04 14:39:10.324] luat:D(868):vfs:fopen /sd/io_test/test_a r not found
[2026-03-04 14:39:10.324] luat:U(868):I/user.验证结果 重命名验证成功 新文件存在 原文件不存在
[2026-03-04 14:39:10.324] luat:U(868):I/user.目录操作 ===== 开始目录列举 =====
[2026-03-04 14:39:10.324] luat:U(875):I/user.fs lsdir [{"name":"boottime","size":1,"type":0},{"name":"testline","size":15,"type":0},{"name":"renamed_file.txt","size":6,"type":0}]
[2026-03-04 14:39:10.324] luat:U(883):I/user.os.remove 文件删除成功 路径:/sd/io_test/renamed_file.txt
[2026-03-04 14:39:10.324] luat:D(885):fatfs:f_open /io_test/renamed_file.txt 4
[2026-03-04 14:39:10.324] luat:D(885):vfs:fopen /sd/io_test/renamed_file.txt r not found
[2026-03-04 14:39:10.324] luat:U(885):I/user.验证结果 renamed_file.txt文件删除验证成功
[2026-03-04 14:39:10.324] luat:U(893):I/user.os.remove testline文件删除成功 路径:/sd/io_test/testline
[2026-03-04 14:39:10.324] luat:D(894):fatfs:f_open /io_test/testline 4
[2026-03-04 14:39:10.324] luat:D(895):vfs:fopen /sd/io_test/testline r not found
[2026-03-04 14:39:10.324] luat:U(895):I/user.验证结果 testline文件删除验证成功
[2026-03-04 14:39:10.324] luat:U(902):I/user.os.remove 文件删除成功 路径:/sd/io_test/boottime
[2026-03-04 14:39:10.324] luat:D(904):fatfs:f_open /io_test/boottime 4
[2026-03-04 14:39:10.324] luat:D(904):vfs:fopen /sd/io_test/boottime r not found
[2026-03-04 14:39:10.324] luat:U(904):I/user.验证结果 boottime文件删除验证成功
[2026-03-04 14:39:10.324] luat:U(913):I/user.io.rmdir 目录删除成功 路径:/sd/io_test
[2026-03-04 14:39:10.324] luat:D(914):fatfs:f_open /io_test 4
[2026-03-04 14:39:10.324] luat:D(914):vfs:fopen /sd/io_test r not found
[2026-03-04 14:39:10.324] luat:U(914):I/user.验证结果 目录删除验证成功
[2026-03-04 14:39:10.324] luat:U(914):I/user.文件操作 ===== 文件操作完成 =====
[2026-03-04 14:39:10.324] luat:U(914):I/user.结束 开始执行关闭操作...
[2026-03-04 14:39:10.324] luat:U(915):I/user.文件系统 卸载成功
[2026-03-04 14:39:10.324] luat:U(915):I/user.SPI接口 已关闭
(3)网络连接与HTTP下载
[2026-03-04 14:56:38.981] luat:U(3270):I/user.HTTP下载 开始下载任务
[2026-03-04 14:56:38.981] luat:D(3272):DNS:gitee.com state 0 id 1 ipv6 0 use dns server2, try 0
[2026-03-04 14:56:38.981] luat:D(3272):net:adatper 2 dns server 192.168.1.1
[2026-03-04 14:56:38.981] luat:D(3272):net:dns udp sendto 192.168.1.1:53 from 192.168.1.106
[2026-03-04 14:56:38.981] luat:I(3291):DNS:dns all done ,now stop
[2026-03-04 14:56:38.981] luat:D(3292):net:adapter 2 connect 180.76.199.13:443 TCP
[2026-03-04 14:56:39.382] luat:I(3739):http:event 00000005 -1 host gitee.com port 443
[2026-03-04 14:56:39.394] luat:W(3758):http:download fail, remove file /sd/1.mp3
[2026-03-04 14:56:39.427] luat:I(3777):http:http close 0x609c4cf8
[2026-03-04 14:56:39.427] luat:E(3780):http:http_ctrl is NULL for idg 1
[2026-03-04 14:56:39.441] luat:U(3781):I/user.HTTP下载 下载完成 error 404 luat:U(3781):{"BDWAF-Request-ID":"afd00252-f8a4-43d2-a638-2aa77f246231","X-Served-By":"cache-ffe9","Age":"0","Access-Control-Allow-Headers":"Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With,X-CustomHeader,Content-Range,Range,Set-Language","Connection":"close","Content-Length":"47","Via":"1.1 varnish","Access-Control-Allow-Methods":"GET, POST, PUT, PATCH, DELETE, OPTIONS","X-Request-Id":"ffacb4ea-01ff-4cae-8c13-6931b988f002","Access-Control-Allow-Credentials":"true","X-Gitee-Server":"http-pilot 1.9.28","Content-Type":"text/plain; charset=UTF-8","X-Cache":"MISS","X-Frame-Options":"DENY","Date":"Wed, 04 Mar 2026 06:56:39 GMT","Server":"ADAS/1.0.214"}luat:U(3782): 47
[2026-03-04 14:56:39.441] luat:U(3782):I/user.HTTP下载 资源清理完成
八、常见问题
8.1 为什么 air8101 不能识别新购买的 sd 卡
文件系为 FAT32 格式(windows、linux 都可以正常识别),所以非 FAT 格式的 SD 卡会挂载失败,而无法正常识别。
8.2 SD 卡的读写路径是什么?
SD 卡文件访问通过路径前加上"/sd",如果 sd 卡中有一个文件 test.txt ,那这个文件的路径就是"/sd/test.txt"。
8.3 http 下载的文件可以直接保存到 sd 卡里吗?
支持,http.request 接口支持直接下载到文件系统中,下载到 sd 卡中的时候只需要注意路径设置。参考 demo 中注释掉的部分。
8.4 注意事项
建议可以先阅读readme文档以熟悉整体流程,再根据具体需求进行修改。