SD卡
一、SD 卡以及 SDIO 概述
1、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 或 SD 总线协议进行数据传输。
2、SDIO 协议介绍
2.1 定义
SDIO(Secure Digital Input and Output)是基于 SD 卡接口的新型接口标准。它不仅保持了与 SD 卡的兼容性,还扩展了连接其他外设的能力。
2.2 工作原理
SDIO 接口基于 SD 卡通信协议进行扩展。主机通过 SDIO 接口发送命令和数据到连接的设备,设备也可以向主机发送响应和数据。这种双向通信能力使得 SDIO 接口在多种应用场景中表现出色。
2.3 优势
- 高速数据传输:支持高速数据传输,提高设备性能和响应速度。
- 低功耗:在保持高性能的同时,实现了低功耗设计,延长了设备的电池寿命。
二、演示功能概述
本 demo 展示了如何在基于 Air8101 开发板上,通过 Luat 脚本使用 FATFS 文件系统库对 SD/TF 卡进行基本的文件操作。在 demo 中介绍 SDIO 初始化,fatfs 文件系统的挂载,获取可用 sd 卡空间,查看文件系统状态,还有一些文件操作测试:读取文件、写入文件、文件追加测试、按行读取测试。最后会测试通过 http 直接下载一个文件到 sd 卡中。
三、准备硬件环境
1. Air8101 开发板准备环境:
“古人云:‘工欲善其事,必先利其器。’在深入介绍本功能示例之前,我们首先需要确保以下硬件环境的准备工作已经完成。”
在我们 Air8101 开发板上已经集成了 SD 卡卡座:
参考:硬件环境清单,准备以及组装好硬件环境。
注意:
(1)用 B10 开发板的时候,SD 3.3V 需要和 SWD 3.3V 短接。
(2)B11 以及后续的开发板,不需要短接
2. SD 卡:
可淘宝上自行选择购买。
四、准备软件环境
“凡事预则立,不预则废。”在详细阐述本功能示例之前,我们需先精心筹备好以下软件环境。
1. 烧录工具 Luatools;
2. 内核固件文件(底层 core 固件文件):点击获取底层固件;
3. LuatOS 需要的脚本和资源文件:https://gitee.com/openLuat/LuatOS-Air8101/tree/master/demo/sdCard
4. lib 脚本文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件;
准备好软件环境之后,接下来查看如何烧录项目文件到 Air8101 开发板 - luatos@air8101 - 合宙文档中心,将本篇文章中演示使用的项目文件烧录到 Air8101 开发板中。
五、SD 卡软硬件参考
5.1 API 接口介绍
本教程使用 api 接口为:
https://docs.openluat.com/air8101/luatos/api/core/fatfs/
https://docs.openluat.com/air8101/luatos/api/core/fs/
https://docs.openluat.com/air8101/luatos/api/core/io/
注意:
1. luatos 的 api 接口是通用的。
2. 通常只使用 fatfs.mount 挂载 tf/sd 卡,其他操作走 io 库就可以了。
3. 挂载成 fatfs 后,可以通过 fs.fsstat 来获取文件系统信息,或 fs.fsize 来获取文件大小。
5.2 SDIO 硬件设计
SDIO 硬件设计参考电路:
六、代码示例介绍
6.1 软件代码介绍
代码中有详细的注释,可以用来参考:
-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "fatfs"
VERSION = "1.0.0"
-- sys库是标配
_G.sys = require("sys")
_G.sysplus = require("sysplus")
--[[
注意:B10开发板需要SD_3.3V和SWD3.3V短接
]]
-- 特别提醒, 由于FAT32是DOS时代的产物, 文件名超过8个字节是需要额外支持的(需要更大的ROM)
-- 例如 /sd/boottime 是合法文件名, 而/sd/boot_time就不是合法文件名, 需要启用长文件名支持.
--网络任务
-- 联网函数, 可自行删减
-- sys.taskInit(function()
-- -----------------------------
-- -- 统一联网函数, 可自行删减
-- ----------------------------
-- if wlan and wlan.connect then
-- -- wifi 联网, Air8101系列均支持
-- local ssid = "Xiaomi 15"
-- local password = "wsh123456"
-- log.info("wifi", ssid, password)
-- wlan.init()
-- wlan.connect(ssid, password, 1)
-- --等待WIFI联网结果,WIFI联网成功后,内核固件会产生一个"IP_READY"消息
-- local result, data = sys.waitUntil("IP_READY")
-- log.info("wlan", "IP_READY", result, data)
-- end
-- log.info("已联网")
-- sys.publish("net_ready")
-- end)
-- spi_id,pin_cs
**local** **function** **fatfs_spi_pin**()
**return** 0, 3, fatfs.SDIO
**end**
sys.taskInit(**function**()
sys.wait(1000)
-- fatfs.debug(1) -- 若挂载失败,可以尝试打开调试信息,查找原因
**local** spi_id, pin_cs,tp = fatfs_spi_pin()
fatfs.mount(tp or fatfs.SPI, "/sd", spi_id, pin_cs, 24 * 1000 * 1000) --挂载fatfs
**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**
**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")) --打印sd卡文件系统分区信息
**end**
-- 测试一下追加, fix in 2021.12.21
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**
-- 测试一下按行读取, fix in 2022-01-16
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**
-- #################################################
**end**)
-- 按需打印
-- code 响应值, 若大于等于 100 为服务器响应, 小于的均为错误代码
-- headers是个table, 一般作为调试数据存在
-- body是字符串. 注意lua的字符串是带长度的byte[]/char*, 是可以包含不可见字符的
-- http下载文件到sd卡功能演示任务
-- sys.taskInit(function()
-- sys.waitUntil("net_ready") -- 等联网
-- sys.wait(5000)
-- log.info("测试http下载文件到sd卡中")
-- local code, headers, body = http.request("GET", "http://airtest.openluat.com:2900/download/12345.txt", {}, "",{ dst = "/sd/test.txt" }).wait()
-- log.info("http.get", code, headers, body)
-- log.info("text size", fs.fsize("/sd/test.txt"))
-- end)
-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
6.2 效果展示
通过日志可以查看到 sd 卡已经挂载成功,并且打印了可用空间大小和文件系统的信息,文件系统操作的操作也都成功。
可以看到通过 http 下载到 sd 操作成功,读取的文件大小也与实际大小相符。
sd 卡通过读卡器接入电脑上查看相关文件。
七、总结
至此,我们已使用 Air8101 开发板的 SDIO 接口完成了对 SD 卡挂载 fatfs、以及对 sd 卡中文件读写操作。
八、常见问题
1. 为什么 air8101 不能识别新购买的 sd 卡:
文件系为 FAT32 格式(windows、linux 都可以正常识别),所以非 FAT 格式的 SD 卡会挂载失败,而无法正常识别。
2. SD 卡的读写路径是什么?
SD 卡文件访问通过路径前加上"/sd",如果 sd 卡中有一个文件 test.txt ,那这个文件的路径就是"/sd/test.txt"。
3. http 下载的文件可以直接保存到 sd 卡里吗?
支持,http.request 接口支持直接下载到文件系统中,下载到 sd 卡中的时候只需要注意路径设置。参考 demo 中注释掉的部分。