1use core::fmt;
21
22use embassy_time::Delay;
23use embedded_hal_bus::spi::ExclusiveDevice;
24pub use embedded_sdmmc as fs;
25use embedded_sdmmc::{
26 Block, BlockCount, BlockDevice, BlockIdx, SdCard, SdCardError, TimeSource, Timestamp,
27};
28use esp_hal::Blocking;
29use esp_hal::gpio::{Level, Output, OutputConfig};
30use esp_hal::spi::Mode;
31use esp_hal::spi::master::{Config, Spi};
32use esp_hal::time::Rate;
33
34use crate::SpiBusResources;
35
36pub const INIT_FREQUENCY: Rate = Rate::from_khz(400);
38pub const FREQUENCY: Rate = Rate::from_mhz(20);
41
42pub type SdSpi<'d> = ExclusiveDevice<Spi<'d, Blocking>, Output<'d>, Delay>;
44pub type Card<'d> = SdCard<SdSpi<'d>, Delay>;
46
47#[derive(Debug, Clone)]
49#[cfg_attr(feature = "defmt", derive(defmt::Format))]
50pub enum Error {
51 Card(SdCardError),
53 ReadOnly,
56}
57
58impl From<SdCardError> for Error {
59 fn from(e: SdCardError) -> Self {
60 Error::Card(e)
61 }
62}
63
64impl fmt::Display for Error {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 match self {
67 Error::Card(e) => write!(f, "SD card error: {e:?}"),
68 Error::ReadOnly => f.write_str("SD card is read-only"),
69 }
70 }
71}
72
73impl core::error::Error for Error {}
74
75fn spi_config(frequency: Rate) -> Config {
76 Config::default()
77 .with_frequency(frequency)
78 .with_mode(Mode::_0)
79}
80
81impl<'d> SpiBusResources<'d> {
82 pub fn sd(&mut self) -> Result<Sd<'_>, Error> {
88 let out = OutputConfig::default();
89 let ext_cs = Output::new(self.ext_cs.reborrow(), Level::High, out);
91 let sd_cs = Output::new(self.sd_cs.reborrow(), Level::High, out);
92 let mut spi = Spi::new(self.spi.reborrow(), spi_config(INIT_FREQUENCY))
93 .expect("400 kHz mode 0 is a valid SPI configuration")
94 .with_sck(self.sck.reborrow())
95 .with_mosi(self.mosi.reborrow())
96 .with_miso(self.miso.reborrow());
97
98 spi.write(&[0xFF; 10]).map_err(|_| SdCardError::Transport)?;
101
102 let Ok(dev) = ExclusiveDevice::new(spi, sd_cs, Delay);
103 let card = SdCard::new(dev, Delay);
104 card.num_bytes()?; card.spi(|dev| dev.bus_mut().apply_config(&spi_config(FREQUENCY)))
106 .expect("20 MHz mode 0 is a valid SPI configuration");
107 Ok(Sd {
108 card,
109 writable: false,
110 _ext_cs: ext_cs,
111 })
112 }
113}
114
115pub struct Sd<'d> {
117 card: Card<'d>,
118 writable: bool,
119 _ext_cs: Output<'d>,
121}
122
123impl<'d> Sd<'d> {
124 pub fn set_writable(&mut self, writable: bool) {
128 self.writable = writable;
129 }
130
131 pub fn is_writable(&self) -> bool {
133 self.writable
134 }
135
136 pub fn size_bytes(&self) -> Result<u64, Error> {
138 Ok(self.card.num_bytes()?)
139 }
140
141 pub fn card_type(&self) -> Option<embedded_sdmmc::embedded_sdmmc_types::sdcard::CardType> {
143 self.card.get_card_type()
144 }
145
146 pub fn card(&self) -> &Card<'d> {
148 &self.card
149 }
150}
151
152impl BlockDevice for Sd<'_> {
153 type Error = Error;
154
155 fn read(&self, blocks: &mut [Block], start: BlockIdx) -> Result<(), Error> {
156 Ok(self.card.read(blocks, start)?)
157 }
158
159 fn write(&self, blocks: &[Block], start: BlockIdx) -> Result<(), Error> {
160 if !self.writable {
161 #[cfg(feature = "defmt")]
162 defmt::warn!(
163 "blocked a write to SD block {} (card is read-only; see Sd::set_writable)",
164 start.0
165 );
166 return Err(Error::ReadOnly);
167 }
168 Ok(self.card.write(blocks, start)?)
169 }
170
171 fn num_blocks(&self) -> Result<BlockCount, Error> {
172 Ok(self.card.num_blocks()?)
173 }
174}
175
176#[derive(Clone, Copy, Debug, Default)]
180pub struct FixedTime;
181
182impl TimeSource for FixedTime {
183 fn get_timestamp(&self) -> Timestamp {
184 Timestamp::from_calendar(2026, 1, 1, 0, 0, 0).expect("valid date")
185 }
186}