Rust嵌入式SD卡管理库embedded-sdmmc的使用:支持FAT16/FAT32文件系统的轻量级存储解决方案
Rust嵌入式SD卡管理库embedded-sdmmc的使用:支持FAT16/FAT32文件系统的轻量级存储解决方案
embedded-sdmmc是一个用于在Rust嵌入式设备上读写FAT格式SD卡文件的库,设计目标是像使用Arduino的SdFat
库一样简单。它采用纯Rust编写,支持#![no_std]
,不使用alloc
或collections
以保持低内存占用。
使用库
你需要实现BlockDevice
trait,它能够读写SD卡的512字节块(或扇区)。下面是一个基本使用示例:
use embedded_sdmmc::{SdCard, VolumeManager, Mode, VolumeIdx};
// 使用SPI设备、片选引脚和delay对象构建SD卡接口
let sdcard = SdCard::new(sdmmc_spi, delay);
// 获取卡大小(如果尚未初始化还会触发卡初始化)
println!("Card size is {} bytes", sdcard.num_bytes()?);
// 创建Volume Manager来管理块设备
let volume_mgr = VolumeManager::new(sdcard, time_source);
// 尝试访问Volume 0(即第一个分区)
let volume0 = volume_mgr.open_volume(VolumeIdx(0))?;
println!("Volume 0: {:?}", volume0);
// 打开根目录
let root_dir = volume0.open_root_dir()?;
// 在根目录中打开名为"MY_FILE.TXT"的文件
let my_file = root_dir.open_file_in_dir("MY_FILE.TXT", Mode::ReadOnly)?;
// 打印文件内容(假设使用ISO-8859-1编码)
while !my_file.is_eof() {
let mut buffer = [0u8; 32];
let num_read = my_file.read(&mut buffer)?;
for b in &buffer[0..num_read] {
print!("{}", *b as char);
}
}
写入文件示例:
let my_other_file = root_dir.open_file极客时间in_dir("MY_DATA.CSV", embedded_sdmmc::Mode::ReadWriteCreateOrAppend)?;
my_other_file.write(b"Timestamp,Signal,Value\n")?;
my_other_file.write(b"2025-01-01T00:00:00Z,TEMP,25.0\n")?;
my_other_file.write(b"2025-01-01T00:00:01Z,TEMP,25.1\n")?;
my_other_file.write(b"2025-01-01T00:00:02Z,TEMP,25.2\n")?;
// 别忘了flush文件以更新目录条目
my_other_file.flush()?;
打开目录和文件
默认情况下,VolumeManager
会初始化最多4个打开的目录、文件和卷。可以通过指定VolumeManager
的MAX_DIR
、MAX_FILES
和MAX_VOLUMES
泛型常量来定制:
// 创建最多6个打开目录、12个打开文件和4个卷(或分区)的volume manager
let cont: VolumeManager<_, _, 6, 12, 4> = VolumeManager::new_with_limits(block, time_source);
完整示例代码
use embedded_hal::blocking::delay::DelayUs;
use embedded_hal::blocking::sp极客时间i::Transfer;
use embedded_hal::digital::v2::OutputPin;
use embedded_sdmmc::{Mode, TimeSource, VolumeIdx, VolumeManager};
// 实现BlockDevice trait
struct SdCard<SPI, CS, DELAY>
where
SPI: Transfer<u8>,
CS: OutputPin,
DELAY: DelayUs<u32>,
{
spi: SPI,
cs: CS,
delay: DELAY,
}
// 实现TimeSource trait
struct DummyTimeSource;
impl TimeSource for DummyTimeSource {
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
embedded_sdmmc::Timestamp {
year_since_1970: 0,
zero_indexed_month: 0,
zero_indexed_day: 0,
hours: 0,
minutes: 0,
seconds: 0,
}
}
}
fn main() -> Result<(), embedded_sdmmc::Error<embedded_sdmmc::SdCardError>> {
// 假设这些是已经初始化的硬件外设
let spi = ...; // SPI接口
let cs = ...; // 片选引脚
let delay = ...; // delay对象
// 创建SD卡接口
let sdcard = embedded_sdmmc::SdCard::new(spi, cs, delay);
// 创建时间源
let time_source = DummyTimeSource;
// 创建Volume Manager
let mut volume_mgr = VolumeManager::new(sdcard, time_source);
// 打开第一个分区
let volume0 = volume_mgr.open_volume(VolumeIdx(0))?;
// 打开根目录
let root_dir = volume_mgr.open_root_dir(volume0)?;
// 创建并写入文件
let mut file = volume_mgr.open_file_in_dir(
&mut root_dir,
"TEST.TXT",
Mode::ReadWriteCreateOrTruncate
)?;
volume_mgr.write(&mut file, b"Hello, SD Card!\n")?;
volume_mgr.close_file(file)?;
// 重新打开文件读取内容
let mut file = volume_mgr.open_file_in_dir(
&mut root_dir,
"TEST.TXT",
Mode::ReadOnly,
)?;
let mut buffer = [0u8; 32];
let len = volume_mgr.read(&mut file, &mut buffer)?;
println!("Read {} bytes: {:?}", len, &buffer[..len]);
volume_mgr.close_file(file)?;
volume_mgr.close_dir(root_dir)?;
Ok(())
}
支持的功能
- 从打开的目录中以所有支持的方法打开文件
- 打开任意数量的目录和文件
- 从打开的文件读取数据
- 向打开的文件写入数据
- 关闭文件
- 删除文件
- 遍历根目录
- 遍历子目录
- 通过defmt或common log接口进行日志记录(需要启用相应功能标志)
No-std使用
该库是为#![no_std]
环境设计的,可以直接在嵌入式系统中使用。
许可证
该库采用以下任一许可证:
- Apache License, Version 2.0
- MIT license
贡献
除非您明确声明,否则任何贡献都将按上述方式双重许可。
1 回复
Rust嵌入式SD卡管理库embedded-sdmmc使用指南
简介
embedded-sdmmc
是一个轻量级的Rust库,专门为嵌入式系统设计,用于管理SD卡上的FAT16/FAT32文件系统。它提供了一个简单的API来访问SD卡上的文件和目录,无需复杂的文件系统驱动程序。
主要特性
- 支持FAT16和FAT32文件系统
- 零堆分配(no_std兼容)
- 提供阻塞和异步接口
- 支持文件和目录操作
- 体积小巧,适合资源受限的嵌入式环境
使用方法
添加依赖
在Cargo.toml中添加:
[dependencies]
embedded-sdmmc = "0.3"
完整示例代码
以下是一个完整的嵌入式SD卡文件操作示例,包含了初始化、文件读写等操作:
use embedded_sdmmc::{Controller, SdMmcSpi, TimeSource, Volume, VolumeIdx, Mode, Error};
use embedded_hal::blocking::delay::DelayMs;
use embedded_hal::digital::v2::OutputPin;
use embedded_hal::blocking::spi::{Transfer, Write};
// 定义时间源结构体
struct DummyTimesource;
impl TimeSource for DummyTimesource {
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
embedded_sdmmc::Timestamp {
year_since_1970: 0,
zero_indexed_month: 0,
zero_indexed_day: 0,
hours: 0,
minutes: 0,
seconds: 0,
}
}
}
// 初始化SD卡控制器
fn init_sd_card<P, D, SPI>(
spi: SPI,
cs: P,
delay: &mut D
) -> Result<Controller<SdMmcSpi<SPI, P>, DummyTimesource>, Error<SPI::Error, P::Error>>
where
P: OutputPin,
D: DelayMs<u8>,
SPI: Transfer<u8> + Write<u8>,
{
let sdmmc = SdMmcSpi::new(spi, cs);
let mut controller = Controller::new(sdmmc, DummyTimesource);
controller.device().init(delay)?;
Ok(controller)
}
fn main() -> Result<(), Error<(), ()>> {
// 假设已经初始化了SPI接口、CS引脚和延时器
// let spi = ...; // SPI接口实例
// let cs = ...; // 片选引脚
// let mut delay = ...; // 延时器
// 初始化SD卡控制器
let mut controller = init_sd_card(spi, cs, &mut delay)?;
// 打开第一个卷(通常SD卡只有一个卷)
let volume = controller.get_volume(VolumeIdx(0))?;
// 打开根目录
let root_dir = controller.open_root_dir(&volume)?;
// 读取目录内容
println!("Directory contents:");
controller.iterate_dir(&volume, &root_dir, |entry| {
println!("- {} ({} bytes)", entry.name, entry.size);
Ok(())
})?;
// 创建新文件并写入数据
println!("Creating test file...");
let mut file = controller.open_file_in_dir(
&volume,
&root_dir,
"test.txt",
Mode::ReadWriteCreateOrTruncate,
)?;
// 写入数据
controller.write(&mut file, b"Hello, SD Card from Rust!")?;
// 关闭文件
controller.close_file(file)?;
println!("File written successfully.");
// 重新打开文件读取内容
println!("Reading file contents...");
let mut file = controller.open_file_in_dir(
&volume,
&root_dir,
"test.txt",
Mode::ReadOnly,
)?;
// 读取数据
let mut buffer = [0u8; 32];
let bytes_read = controller.read(&volume, &mut file, &mut buffer)?;
println!("Read content: {}", core::str::from_utf8(&buffer[..bytes_read]).unwrap());
// 关闭文件
controller.close_file(file)?;
Ok(())
}
高级用法
异步接口示例
use embedded_sdmmc::async_::Controller;
use embedded_sdmmc::{VolumeIdx, Mode};
async fn async_sd_card_example() {
// 初始化异步控制器
let mut controller = Controller::new(sdmmc, DummyTimesource);
controller.device().init().await.unwrap();
// 获取卷和目录
let volume = controller.get_volume(VolumeIdx(0)).await.unwrap();
let root_dir = controller.open_root_dir(&volume).await.unwrap();
// 异步文件操作
let mut file = controller.open_file_in_dir(
&volume,
&root_dir,
"async_test.txt",
Mode::ReadWriteCreateOrTruncate,
).await.unwrap();
// 写入数据
controller.write(&mut file, b"Async write test").await.unwrap();
controller.close_file(file).await.unwrap();
}
大文件处理示例
fn process_large_file(
controller: &mut Controller<impl embedded_sdmmc::BlockDevice, DummyTimesource>,
volume: &Volume,
dir: &embedded_sdmmc::Directory,
) -> Result<(), embedded_sdmmc::Error<(), ()>> {
// 打开大文件
let mut file = controller.open_file_in_dir(
volume,
dir,
"large_data.bin",
Mode::ReadOnly,
)?;
// 缓冲区
let mut buffer = [0u8; 1024];
let mut total_read = 0;
// 流式读取
loop {
let bytes_read = controller.read(volume, &mut file, &mut buffer)?;
if bytes_read == 0 {
break; // 文件结束
}
// 处理数据
process_data(&buffer[..bytes_read]);
total_read += bytes_read;
}
println!("Total bytes processed: {}", total_read);
controller.close_file(file)?;
Ok(())
}
注意事项
- 确保SD卡已正确格式化为FAT16/FAT32文件系统
- 在嵌入式系统中使用时,注意SPI时钟频率不要超过SD卡的规格
- 文件操作后务必关闭文件,避免数据损坏
- 对于写操作,考虑添加适当的延时以确保数据完全写入
- 错误处理很重要,所有操作都应检查返回值
embedded-sdmmc
为嵌入式系统提供了一个简单可靠的SD卡文件系统访问方案,特别适合需要轻量级存储解决方案的应用场景。