Skip to content

Commit f3e8e21

Browse files
authored
Merge pull request #80 from rust-embedded-community/more-api-cleanups
2 parents a1aa27c + 764e646 commit f3e8e21

File tree

13 files changed

+934
-677
lines changed

13 files changed

+934
-677
lines changed

.github/workflows/rust.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
strategy:
1616
matrix:
17-
features: ['log', 'defmt-log']
17+
features: ['log', 'defmt-log', '""']
1818
steps:
1919
- uses: actions/checkout@v1
2020
- name: Build

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1010
[Unreleased]: https://github.com/rust-embedded-community/embedded-sdmmc-rs/compare/v0.4.0...develop
1111

1212
- Renamed `Controller` to `VolumeManager`, to better describe what it does.
13+
- Renamed `SdMmcSpi` to `SdCard`
14+
- Renamed `BlockSpi` to `AcquiredSdCard`
1315

1416
## [Version 0.4.0](https://github.com/rust-embedded-community/embedded-sdmmc-rs/releases/tag/v0.4.0)
1517

Cargo.toml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
[package]
2-
name = "embedded-sdmmc"
3-
version = "0.4.0"
42
authors = ["Jonathan 'theJPster' Pallant <[email protected]>"]
3+
categories = ["embedded", "no-std"]
54
description = "A basic SD/MMC driver for Embedded Rust."
5+
edition = "2021"
66
keywords = ["sdcard", "mmc", "embedded", "fat32"]
7-
categories = ["embedded", "no-std"]
87
license = "MIT OR Apache-2.0"
9-
repository = "https://github.com/rust-embedded-community/embedded-sdmmc-rs"
10-
edition = "2021"
8+
name = "embedded-sdmmc"
119
readme = "README.md"
10+
repository = "https://github.com/rust-embedded-community/embedded-sdmmc-rs"
11+
version = "0.4.0"
1212

1313
[dependencies]
14+
byteorder = {version = "1", default-features = false}
15+
defmt = {version = "0.3", optional = true}
1416
embedded-hal = "0.2.3"
15-
byteorder = { version = "1", default-features = false }
16-
log = { version = "0.4", default-features = false, optional = true }
17-
defmt = { version = "0.3", optional = true }
17+
log = {version = "0.4", default-features = false, optional = true}
1818

1919
[dev-dependencies]
20-
hex-literal = "0.3"
2120
env_logger = "0.9"
21+
hex-literal = "0.3"
2222

2323
[features]
24-
defmt-log = [ "defmt" ]
25-
default = [ "log" ]
24+
default = ["log"]
25+
defmt-log = ["defmt"]

README.md

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,29 @@ designed for readability and simplicity over performance.
1111
You will need something that implements the `BlockDevice` trait, which can read and write the 512-byte blocks (or sectors) from your card. If you were to implement this over USB Mass Storage, there's no reason this crate couldn't work with a USB Thumb Drive, but we only supply a `BlockDevice` suitable for reading SD and SDHC cards over SPI.
1212

1313
```rust
14-
// Build an SD Card interface out of an SPI device
15-
let mut spi_dev = embedded_sdmmc::SdMmcSpi::new(sdmmc_spi, sdmmc_cs);
16-
// Try and initialise the SD card
17-
let block_dev = spi_dev.acquire()?;
18-
// The SD Card was initialised, and we have a `BlockSpi` object
19-
// representing the initialised card.
20-
write!(uart, "Card size is {} bytes", block_dev.card_size_bytes()?)?;
14+
// Build an SD Card interface out of an SPI device, a chip-select pin and a delay object
15+
let sdcard = embedded_sdmmc::SdCard::new(sdmmc_spi, sdmmc_cs, delay);
16+
// Get the card size (this also triggers card initialisation because it's not been done yet)
17+
println!("Card size is {} bytes", sdcard.num_bytes()?);
2118
// Now let's look for volumes (also known as partitions) on our block device.
22-
let mut cont = embedded_sdmmc::VolumeManager::new(block_dev, time_source);
23-
// Try and access Volume 0 (i.e. the first partition)
24-
let mut volume = cont.get_volume(embedded_sdmmc::VolumeIdx(0))?;
25-
writeln!(uart, "Volume 0: {:?}", v)?;
26-
// Open the root directory
19+
// To do this we need a Volume Manager. It will take ownership of the block device.
20+
let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, time_source);
21+
// Try and access Volume 0 (i.e. the first partition).
22+
// The volume object holds information about the filesystem on that volume.
23+
// It doesn't hold a reference to the Volume Manager and so must be passed back
24+
// to every Volume Manager API call. This makes it easier to handle multiple
25+
// volumes in parallel.
26+
let mut volume0 = volume_mgr.get_volume(embedded_sdmmc::VolumeIdx(0))?;
27+
println!("Volume 0: {:?}", volume0);
28+
// Open the root directory (passing in the volume we're using).
2729
let root_dir = volume_mgr.open_root_dir(&volume0)?;
2830
// Open a file called "MY_FILE.TXT" in the root directory
2931
let mut my_file = volume_mgr.open_file_in_dir(
30-
&mut volume0, &root_dir, "MY_FILE.TXT", embedded_sdmmc::Mode::ReadOnly)?;
32+
&mut volume0,
33+
&root_dir,
34+
"MY_FILE.TXT",
35+
embedded_sdmmc::Mode::ReadOnly,
36+
)?;
3137
// Print the contents of the file
3238
while !my_file.eof() {
3339
let mut buffer = [0u8; 32];
@@ -37,7 +43,7 @@ while !my_file.eof() {
3743
}
3844
}
3945
volume_mgr.close_file(&volume0, my_file)?;
40-
volume_mgr.close_dir(&volume0, root_dir)?;
46+
volume_mgr.close_dir(&volume0, root_dir);
4147
```
4248

4349
### Open directories and files

examples/readme_test.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
//! This is the code from the README.md file.
2+
//!
3+
//! We add enough stuff to make it compile, but it won't run because our fake
4+
//! SPI doesn't do any replies.
5+
6+
struct FakeSpi();
7+
8+
impl embedded_hal::blocking::spi::Transfer<u8> for FakeSpi {
9+
type Error = core::convert::Infallible;
10+
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
11+
Ok(words)
12+
}
13+
}
14+
15+
impl embedded_hal::blocking::spi::Write<u8> for FakeSpi {
16+
type Error = core::convert::Infallible;
17+
fn write<'w>(&mut self, _words: &'w [u8]) -> Result<(), Self::Error> {
18+
Ok(())
19+
}
20+
}
21+
22+
struct FakeCs();
23+
24+
impl embedded_hal::digital::v2::OutputPin for FakeCs {
25+
type Error = core::convert::Infallible;
26+
fn set_low(&mut self) -> Result<(), Self::Error> {
27+
Ok(())
28+
}
29+
30+
fn set_high(&mut self) -> Result<(), Self::Error> {
31+
Ok(())
32+
}
33+
}
34+
35+
struct FakeDelayer();
36+
37+
impl embedded_hal::blocking::delay::DelayUs<u8> for FakeDelayer {
38+
fn delay_us(&mut self, us: u8) {
39+
std::thread::sleep(std::time::Duration::from_micros(u64::from(us)));
40+
}
41+
}
42+
43+
struct FakeTimesource();
44+
45+
impl embedded_sdmmc::TimeSource for FakeTimesource {
46+
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
47+
embedded_sdmmc::Timestamp {
48+
year_since_1970: 0,
49+
zero_indexed_month: 0,
50+
zero_indexed_day: 0,
51+
hours: 0,
52+
minutes: 0,
53+
seconds: 0,
54+
}
55+
}
56+
}
57+
58+
#[derive(Debug, Clone)]
59+
enum Error {
60+
Filesystem(embedded_sdmmc::Error<embedded_sdmmc::SdCardError>),
61+
Disk(embedded_sdmmc::SdCardError),
62+
}
63+
64+
impl From<embedded_sdmmc::Error<embedded_sdmmc::SdCardError>> for Error {
65+
fn from(value: embedded_sdmmc::Error<embedded_sdmmc::SdCardError>) -> Error {
66+
Error::Filesystem(value)
67+
}
68+
}
69+
70+
impl From<embedded_sdmmc::SdCardError> for Error {
71+
fn from(value: embedded_sdmmc::SdCardError) -> Error {
72+
Error::Disk(value)
73+
}
74+
}
75+
76+
fn main() -> Result<(), Error> {
77+
let sdmmc_spi = FakeSpi();
78+
let sdmmc_cs = FakeCs();
79+
let delay = FakeDelayer();
80+
let time_source = FakeTimesource();
81+
// Build an SD Card interface out of an SPI device, a chip-select pin and the delay object
82+
let sdcard = embedded_sdmmc::SdCard::new(sdmmc_spi, sdmmc_cs, delay);
83+
// Get the card size (this also triggers card initialisation because it's not been done yet)
84+
println!("Card size is {} bytes", sdcard.num_bytes()?);
85+
// Now let's look for volumes (also known as partitions) on our block device.
86+
// To do this we need a Volume Manager. It will take ownership of the block device.
87+
let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, time_source);
88+
// Try and access Volume 0 (i.e. the first partition).
89+
// The volume object holds information about the filesystem on that volume.
90+
// It doesn't hold a reference to the Volume Manager and so must be passed back
91+
// to every Volume Manager API call. This makes it easier to handle multiple
92+
// volumes in parallel.
93+
let mut volume0 = volume_mgr.get_volume(embedded_sdmmc::VolumeIdx(0))?;
94+
println!("Volume 0: {:?}", volume0);
95+
// Open the root directory (passing in the volume we're using).
96+
let root_dir = volume_mgr.open_root_dir(&volume0)?;
97+
// Open a file called "MY_FILE.TXT" in the root directory
98+
let mut my_file = volume_mgr.open_file_in_dir(
99+
&mut volume0,
100+
&root_dir,
101+
"MY_FILE.TXT",
102+
embedded_sdmmc::Mode::ReadOnly,
103+
)?;
104+
// Print the contents of the file
105+
while !my_file.eof() {
106+
let mut buffer = [0u8; 32];
107+
let num_read = volume_mgr.read(&volume0, &mut my_file, &mut buffer)?;
108+
for b in &buffer[0..num_read] {
109+
print!("{}", *b as char);
110+
}
111+
}
112+
volume_mgr.close_file(&volume0, my_file)?;
113+
volume_mgr.close_dir(&volume0, root_dir);
114+
Ok(())
115+
}

src/fat/volume.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
//! FAT volume
22
3-
#[cfg(feature = "log")]
4-
use log::{debug, trace, warn};
5-
6-
#[cfg(feature = "defmt-log")]
7-
use defmt::{debug, trace, warn};
8-
93
use crate::{
4+
debug,
105
fat::{
116
Bpb, Fat16Info, Fat32Info, FatSpecificInfo, FatType, InfoSector, OnDiskDirEntry,
127
RESERVED_ENTRIES,
138
},
14-
Attributes, Block, BlockCount, BlockDevice, BlockIdx, Cluster, DirEntry, Directory, Error,
15-
ShortFileName, TimeSource, VolumeManager, VolumeType,
9+
trace, warn, Attributes, Block, BlockCount, BlockDevice, BlockIdx, Cluster, DirEntry,
10+
Directory, Error, ShortFileName, TimeSource, VolumeManager, VolumeType,
1611
};
1712
use byteorder::{ByteOrder, LittleEndian};
1813
use core::convert::TryFrom;
@@ -149,7 +144,7 @@ impl FatVolume {
149144
}
150145
FatSpecificInfo::Fat32(_fat32_info) => {
151146
// FAT32 => 4 bytes per entry
152-
let fat_offset = cluster.0 as u32 * 4;
147+
let fat_offset = cluster.0 * 4;
153148
this_fat_block_num = self.lba_start + self.fat_start.offset_bytes(fat_offset);
154149
let this_fat_ent_offset = (fat_offset % Block::LEN_U32) as usize;
155150
volume_mgr

src/filesystem/filename.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,7 @@ impl ShortFileName {
8181
}
8282
}
8383
_ => {
84-
let ch = if (b'a'..=b'z').contains(&ch) {
85-
// Uppercase characters only
86-
ch - 32
87-
} else {
88-
ch
89-
};
84+
let ch = ch.to_ascii_uppercase();
9085
if seen_dot {
9186
if (Self::FILENAME_BASE_MAX_LEN..Self::FILENAME_MAX_LEN).contains(&idx) {
9287
sfn.contents[idx] = ch;

src/filesystem/timestamp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub struct Timestamp {
2727
impl Timestamp {
2828
/// Create a `Timestamp` from the 16-bit FAT date and time fields.
2929
pub fn from_fat(date: u16, time: u16) -> Timestamp {
30-
let year = (1980 + (date >> 9)) as u16;
30+
let year = 1980 + (date >> 9);
3131
let month = ((date >> 5) & 0x000F) as u8;
3232
let day = (date & 0x001F) as u8;
3333
let hours = ((time >> 11) & 0x001F) as u8;

src/lib.rs

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,14 @@
2121
//! # struct DummyCsPin;
2222
//! # struct DummyUart;
2323
//! # struct DummyTimeSource;
24+
//! # struct DummyDelayer;
2425
//! # impl embedded_hal::blocking::spi::Transfer<u8> for DummySpi {
2526
//! # type Error = ();
26-
//! # fn transfer<'w>(&mut self, data: &'w mut [u8]) -> Result<&'w [u8], ()> { Ok(&[0]) }
27+
//! # fn transfer<'w>(&mut self, data: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { Ok(&[0]) }
28+
//! # }
29+
//! # impl embedded_hal::blocking::spi::Write<u8> for DummySpi {
30+
//! # type Error = ();
31+
//! # fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> { Ok(()) }
2732
//! # }
2833
//! # impl embedded_hal::digital::v2::OutputPin for DummyCsPin {
2934
//! # type Error = ();
@@ -33,18 +38,21 @@
3338
//! # impl embedded_sdmmc::TimeSource for DummyTimeSource {
3439
//! # fn get_timestamp(&self) -> embedded_sdmmc::Timestamp { embedded_sdmmc::Timestamp::from_fat(0, 0) }
3540
//! # }
41+
//! # impl embedded_hal::blocking::delay::DelayUs<u8> for DummyDelayer {
42+
//! # fn delay_us(&mut self, us: u8) {}
43+
//! # }
3644
//! # impl std::fmt::Write for DummyUart { fn write_str(&mut self, s: &str) -> std::fmt::Result { Ok(()) } }
3745
//! # use std::fmt::Write;
3846
//! # use embedded_sdmmc::VolumeManager;
39-
//! # fn main() -> Result<(), embedded_sdmmc::Error<embedded_sdmmc::SdMmcError>> {
47+
//! # fn main() -> Result<(), embedded_sdmmc::Error<embedded_sdmmc::SdCardError>> {
4048
//! # let mut sdmmc_spi = DummySpi;
4149
//! # let mut sdmmc_cs = DummyCsPin;
4250
//! # let time_source = DummyTimeSource;
43-
//! let mut spi_dev = embedded_sdmmc::SdMmcSpi::new(sdmmc_spi, sdmmc_cs);
44-
//! let block = spi_dev.acquire()?;
45-
//! println!("Card size {} bytes", block.card_size_bytes()?);
46-
//! let mut volume_mgr = VolumeManager::new(block, time_source);
47-
//! println!("Card size is still {} bytes", volume_mgr.device().card_size_bytes()?);
51+
//! # let delayer = DummyDelayer;
52+
//! let sdcard = embedded_sdmmc::SdCard::new(sdmmc_spi, sdmmc_cs, delayer);
53+
//! println!("Card size {} bytes", sdcard.num_bytes()?);
54+
//! let mut volume_mgr = VolumeManager::new(sdcard, time_source);
55+
//! println!("Card size is still {} bytes", volume_mgr.device().num_bytes()?);
4856
//! let mut volume0 = volume_mgr.get_volume(embedded_sdmmc::VolumeIdx(0))?;
4957
//! println!("Volume 0: {:?}", volume0);
5058
//! let root_dir = volume_mgr.open_root_dir(&volume0)?;
@@ -91,24 +99,53 @@ mod structure;
9199
pub mod blockdevice;
92100
pub mod fat;
93101
pub mod filesystem;
94-
pub mod sdmmc;
95-
pub mod sdmmc_proto;
102+
pub mod sdcard;
96103

97104
pub use crate::blockdevice::{Block, BlockCount, BlockDevice, BlockIdx};
98105
pub use crate::fat::FatVolume;
99106
pub use crate::filesystem::{
100107
Attributes, Cluster, DirEntry, Directory, File, FilenameError, Mode, ShortFileName, TimeSource,
101108
Timestamp, MAX_FILE_SIZE,
102109
};
103-
pub use crate::sdmmc::Error as SdMmcError;
104-
pub use crate::sdmmc::{BlockSpi, SdMmcSpi};
110+
pub use crate::sdcard::Error as SdCardError;
111+
pub use crate::sdcard::SdCard;
105112

106113
mod volume_mgr;
107114
pub use volume_mgr::VolumeManager;
108115

109116
#[deprecated]
110117
pub use volume_mgr::VolumeManager as Controller;
111118

119+
#[cfg(all(feature = "defmt-log", feature = "log"))]
120+
compile_error!("Cannot enable both log and defmt-log");
121+
122+
#[cfg(feature = "log")]
123+
use log::{debug, trace, warn};
124+
125+
#[cfg(feature = "defmt-log")]
126+
use defmt::{debug, trace, warn};
127+
128+
#[cfg(all(not(feature = "defmt-log"), not(feature = "log")))]
129+
#[macro_export]
130+
/// Like log::debug! but does nothing at all
131+
macro_rules! debug {
132+
($($arg:tt)+) => {};
133+
}
134+
135+
#[cfg(all(not(feature = "defmt-log"), not(feature = "log")))]
136+
#[macro_export]
137+
/// Like log::trace! but does nothing at all
138+
macro_rules! trace {
139+
($($arg:tt)+) => {};
140+
}
141+
142+
#[cfg(all(not(feature = "defmt-log"), not(feature = "log")))]
143+
#[macro_export]
144+
/// Like log::warn! but does nothing at all
145+
macro_rules! warn {
146+
($($arg:tt)+) => {};
147+
}
148+
112149
// ****************************************************************************
113150
//
114151
// Public Types

0 commit comments

Comments
 (0)