Rust嵌入式SD卡管理库embedded-sdmmc的使用:支持FAT16/FAT32文件系统的轻量级存储解决方案

Rust嵌入式SD卡管理库embedded-sdmmc的使用:支持FAT16/FAT32文件系统的轻量级存储解决方案

embedded-sdmmc是一个用于在Rust嵌入式设备上读写FAT格式SD卡文件的库,设计目标是像使用Arduino的SdFat库一样简单。它采用纯Rust编写,支持#![no_std],不使用alloccollections以保持低内存占用。

使用库

你需要实现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个打开的目录、文件和卷。可以通过指定VolumeManagerMAX_DIRMAX_FILESMAX_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(())
}

注意事项

  1. 确保SD卡已正确格式化为FAT16/FAT32文件系统
  2. 在嵌入式系统中使用时,注意SPI时钟频率不要超过SD卡的规格
  3. 文件操作后务必关闭文件,避免数据损坏
  4. 对于写操作,考虑添加适当的延时以确保数据完全写入
  5. 错误处理很重要,所有操作都应检查返回值

embedded-sdmmc为嵌入式系统提供了一个简单可靠的SD卡文件系统访问方案,特别适合需要轻量级存储解决方案的应用场景。

回到顶部