Skip to content

Commit 2e415e8

Browse files
authored
Merge pull request #22 from gschwaer/dev/sdcard
Add support for sdcard
2 parents c2d9dbc + a3017ae commit 2e415e8

File tree

4 files changed

+193
-0
lines changed

4 files changed

+193
-0
lines changed

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ embedded-hal = "0.2.3"
1515
nb = "0.1.2"
1616
riscv = "0.6.0"
1717
st7735-lcd = { version = "0.7", optional = true }
18+
embedded-sdmmc = { version = "0.3.0", optional = true }
1819

1920
[dev-dependencies]
2021
riscv-rt = "0.8.0"
@@ -24,6 +25,7 @@ ushell = "0.3.3"
2425

2526
[features]
2627
lcd = ["st7735-lcd"]
28+
sdcard = ["embedded-sdmmc"]
2729

2830
[[example]]
2931
name = "display"
@@ -33,6 +35,10 @@ required-features = ["lcd"]
3335
name = "ferris"
3436
required-features = ["lcd"]
3537

38+
[[example]]
39+
name = "sdcard_test"
40+
required-features = ["sdcard"]
41+
3642
[package.metadata.docs.rs]
3743
features = ['lcd']
3844
rustdoc-args = ["--cfg", "docsrs"]

examples/sdcard_test.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use embedded_sdmmc::{Directory, Volume, VolumeIdx};
5+
6+
use longan_nano::sdcard::SdCard;
7+
use panic_halt as _;
8+
9+
use riscv_rt::entry;
10+
use longan_nano::hal::{pac, prelude::*};
11+
use longan_nano::{sdcard, sdcard_pins, sprint, sprintln};
12+
13+
#[entry]
14+
fn main() -> ! {
15+
let dp = pac::Peripherals::take().unwrap();
16+
17+
// Configure clocks
18+
let mut rcu = dp.RCU.configure()
19+
.ext_hf_clock(8.mhz())
20+
.sysclk(108.mhz())
21+
.freeze();
22+
23+
let mut afio = dp.AFIO.constrain(&mut rcu);
24+
25+
let gpioa = dp.GPIOA.split(&mut rcu);
26+
longan_nano::stdout::configure(dp.USART0, gpioa.pa9, gpioa.pa10, 115_200.bps(), &mut afio, &mut rcu);
27+
28+
let gpiob = dp.GPIOB.split(&mut rcu);
29+
let sdcard_pins = sdcard_pins!(gpiob);
30+
let mut sdcard = sdcard::configure(dp.SPI1, sdcard_pins, sdcard::SdCardFreq::Safe, &mut rcu);
31+
32+
sprint!("Initializing SD card ... ");
33+
if let Err(_) = sdcard.device().init() {
34+
sprintln!("Failed to initialize sdcard.");
35+
} else {
36+
sprintln!("OK");
37+
38+
let size = sdcard.device().card_size_bytes().unwrap();
39+
sprintln!("SD Card Capacity: {} MB", size / 1000 / 1000);
40+
41+
// open the first partition
42+
sprintln!("Partition 0:");
43+
let mut volume = sdcard.get_volume(VolumeIdx(0)).unwrap();
44+
45+
// list files in root dir
46+
let root_dir = sdcard.open_root_dir(&volume).unwrap();
47+
sdcard.iterate_dir(&volume, &root_dir, | entry | {
48+
sprintln!("{: >5}B {}", entry.size, entry.name);
49+
}).unwrap();
50+
51+
// if a file with the name SDTST.TXT is present, do a read/write test
52+
if let Ok(_) = sdcard.find_directory_entry(&volume, &root_dir, "SDTST.TXT") {
53+
read_write_test(&mut sdcard, &mut volume, &root_dir);
54+
}
55+
}
56+
sprintln!("Done");
57+
58+
loop { }
59+
}
60+
61+
fn read_write_test(sdcard: &mut SdCard, volume: &mut Volume, dir: &Directory) {
62+
sprint!("Write test: ");
63+
let mut file = sdcard.open_file_in_dir(volume, dir, "SDTST.CSV", embedded_sdmmc::Mode::ReadWriteCreateOrTruncate).unwrap();
64+
let data = "1,2,3,4,20";
65+
if let Ok(size_written) = sdcard.write(volume, &mut file, data.as_bytes()) {
66+
sprintln!("Success (Wrote {} bytes)", size_written);
67+
} else {
68+
sprintln!("Failed");
69+
}
70+
sdcard.close_file(volume, file).unwrap();
71+
72+
sprint!("Read test: ");
73+
let mut file = sdcard.open_file_in_dir(volume, dir, "SDTST.CSV", embedded_sdmmc::Mode::ReadOnly).unwrap();
74+
let mut buffer: [u8; 32] = [0; 32];
75+
if let Ok(size_read) = sdcard.read(volume, &mut file, &mut buffer) {
76+
if size_read == data.len() && buffer[0..size_read].eq(data.as_bytes()) {
77+
sprintln!("Success (Read same {} bytes)", size_read);
78+
} else {
79+
sprintln!("Content differs.");
80+
}
81+
} else {
82+
sprintln!("Failed");
83+
}
84+
sdcard.close_file(volume, file).unwrap();
85+
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ pub use gd32vf103xx_hal as hal;
1010
pub mod lcd;
1111
pub mod led;
1212
pub mod stdout;
13+
#[cfg(feature = "sdcard")]
14+
#[cfg_attr(docsrs, doc(cfg(feature = "sdcard")))]
15+
pub mod sdcard;

src/sdcard.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//! On-board SD Card Slot
2+
3+
use embedded_hal::digital::v2::OutputPin;
4+
use gd32vf103xx_hal::gpio::gpiob::{PB12, PB13, PB14, PB15};
5+
use gd32vf103xx_hal::gpio::{Alternate, Floating, Input, Output, PushPull};
6+
use gd32vf103xx_hal::pac::{SPI1};
7+
use gd32vf103xx_hal::rcu::Rcu;
8+
use gd32vf103xx_hal::spi::{Spi, MODE_0};
9+
use gd32vf103xx_hal::time::{Hertz, U32Ext};
10+
use embedded_sdmmc::{Controller, SdMmcSpi, TimeSource, Timestamp};
11+
12+
/// Sets up all the needed GPIO pins for the sdcard
13+
///
14+
/// ```
15+
/// let gpiob = dp.GPIOB.split(&mut rcu);
16+
/// let sdcard_pins = sdcard_pins!(gpiob);
17+
/// ```
18+
#[macro_export]
19+
macro_rules! sdcard_pins {
20+
($gpiob:ident) => {{
21+
$crate::sdcard::SdCardPins {
22+
miso: $gpiob.pb14.into_floating_input(),
23+
mosi: $gpiob.pb15.into_alternate_push_pull(),
24+
sck: $gpiob.pb13.into_alternate_push_pull(),
25+
cs: $gpiob.pb12.into_push_pull_output(),
26+
}
27+
}};
28+
}
29+
30+
type SckPin = PB13<Alternate<PushPull>>;
31+
type MisoPin = PB14<Input<Floating>>;
32+
type MosiPin = PB15<Alternate<PushPull>>;
33+
type CsPin = PB12<Output<PushPull>>;
34+
type SPI1Pins = (SckPin, MisoPin, MosiPin);
35+
36+
type Spi1 = Spi<SPI1, SPI1Pins>;
37+
38+
/// A type based on embedded_sdmmc::SdMmcSpi that is used by SdCard.
39+
pub type SdCardSpi = SdMmcSpi<Spi1, CsPin>;
40+
41+
/// A type based on embedded_sdmmc::Controller.
42+
pub type SdCard = Controller<SdCardSpi, FakeTimeSource>;
43+
44+
pub struct SdCardPins {
45+
pub miso: MisoPin,
46+
pub mosi: MosiPin,
47+
pub sck: SckPin,
48+
pub cs: CsPin,
49+
}
50+
51+
pub enum SdCardFreq {
52+
/// Should work for all cards
53+
Safe,
54+
/// May not work for some cards
55+
Fast,
56+
/// Specify SPI frequency
57+
Custom(Hertz)
58+
}
59+
60+
/// Constructs SD Card driver from the required components.
61+
pub fn configure(spi: SPI1, pins: SdCardPins, freq: SdCardFreq, rcu: &mut Rcu) -> SdCard {
62+
let freq = match freq {
63+
SdCardFreq::Safe => 200.khz().into(), // using 300 kHz here because the sdcard init needs 100 to 400 kHz (see SdMmcSpi.init)
64+
SdCardFreq::Fast => 27.mhz().into(), // this is the max SPI frequency according to datasheet
65+
SdCardFreq::Custom(val) => val,
66+
};
67+
68+
let spi1 = Spi::spi1(
69+
spi,
70+
(pins.sck, pins.miso, pins.mosi),
71+
MODE_0,
72+
freq,
73+
rcu,
74+
);
75+
76+
let mut cs = pins.cs;
77+
cs.set_high().unwrap();
78+
79+
let sdmmcspi = SdMmcSpi::new(spi1, cs);
80+
let ctime_source = FakeTimeSource {};
81+
82+
Controller::new(sdmmcspi, ctime_source)
83+
}
84+
85+
/// A fake time source that always returns a date of zero.
86+
pub struct FakeTimeSource {}
87+
88+
impl TimeSource for FakeTimeSource {
89+
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
90+
Timestamp {
91+
year_since_1970: 0,
92+
zero_indexed_month: 0,
93+
zero_indexed_day: 0,
94+
hours: 0,
95+
minutes: 0,
96+
seconds: 0,
97+
}
98+
}
99+
}

0 commit comments

Comments
 (0)