use crate::spi::{InnerDelay, SpiBusWrapper, SpiInitError};
use esp_hal::gpio::{Level, Output, OutputConfig};
use esp_hal::peripherals::GPIO16;
use sdmmc;
use embedded_timers::{clock::Clock, instant::TimespecInstant};
pub struct SdDelay(pub InnerDelay);
#[cfg(feature = "blocking")]
impl embedded_hal::delay::DelayNs for SdDelay {
fn delay_ns(&mut self, ns: u32) {
self.0.delay_ns(ns)
}
}
#[cfg(feature = "async")]
impl sdmmc::delay::Delay for SdDelay {
type Future = embassy_time::Timer;
fn delay_ms(&mut self, ms: u32) -> Self::Future {
let _ = &mut self.0;
embassy_time::Timer::after_millis(ms as u64)
}
}
pub struct SdClock;
impl Clock for SdClock {
type Instant = TimespecInstant;
fn now(&self) -> Self::Instant {
let now = embassy_time::Instant::now().as_millis();
TimespecInstant::new((now / 1000) as u32, ((now % 1000) * 1_000_000) as u32)
}
}
pub struct SdSpi<'a>(pub &'a SpiBusWrapper);
pub type SdBus<'a> = sdmmc::bus::spi::Bus<SdSpi<'a>, Output<'a>, SdClock>;
pub type SDC<'a> = sdmmc::SD<SdBus<'a>>;
pub type SdVolMgr<'a> = SDC<'a>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SdInitError {
SpiDevice(SpiInitError),
CardInitFailed,
}
#[cfg(feature = "AtomicBus")]
compile_error!("AtomicBus is not supported with async-sdmmc");
impl SdInitError {
pub const fn as_str(self) -> &'static str {
match self {
Self::SpiDevice(err) => err.as_str(),
Self::CardInitFailed => "SD card init failed",
}
}
}
#[cfg(feature = "blocking")]
impl<'a> sdmmc::bus::spi::Transfer for SdSpi<'a> {
type Error = SpiInitError;
fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> {
#[cfg(feature = "RefCellBus")]
{
let bus = &mut *self.0.borrow_mut();
match (!tx.is_empty(), !rx.is_empty()) {
(true, true) => {
let n = core::cmp::min(tx.len(), rx.len());
rx[..n].copy_from_slice(&tx[..n]);
embedded_hal::spi::SpiBus::transfer_in_place(bus, &mut rx[..n])
.map_err(|_| SpiInitError::DeviceInitFailed)?;
Ok(())
}
(true, false) => embedded_hal::spi::SpiBus::write(bus, tx)
.map_err(|_| SpiInitError::DeviceInitFailed),
(false, true) => embedded_hal::spi::SpiBus::read(bus, rx)
.map_err(|_| SpiInitError::DeviceInitFailed),
_ => Ok(()),
}
}
#[cfg(feature = "CriticalSectionBus")]
{
self.0.lock(|bus| {
let mut bus = bus.borrow_mut();
match (!tx.is_empty(), !rx.is_empty()) {
(true, true) => {
let n = core::cmp::min(tx.len(), rx.len());
rx[..n].copy_from_slice(&tx[..n]);
embedded_hal::spi::SpiBus::transfer_in_place(&mut *bus, &mut rx[..n])
.map_err(|_| SpiInitError::DeviceInitFailed)?;
Ok(())
}
(true, false) => embedded_hal::spi::SpiBus::write(&mut *bus, tx)
.map_err(|_| SpiInitError::DeviceInitFailed),
(false, true) => embedded_hal::spi::SpiBus::read(&mut *bus, rx)
.map_err(|_| SpiInitError::DeviceInitFailed),
_ => Ok(()),
}
})
}
}
}
#[cfg(feature = "async")]
impl<'a> sdmmc::bus::spi::Transfer for SdSpi<'a> {
type Error = SpiInitError;
async fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> {
let mut guard = self.0.lock().await;
match (!tx.is_empty(), !rx.is_empty()) {
(true, true) => {
let n = core::cmp::min(tx.len(), rx.len());
rx[..n].copy_from_slice(&tx[..n]);
embedded_hal_async::spi::SpiBus::transfer_in_place(&mut *guard, &mut rx[..n])
.await
.map_err(|_| SpiInitError::DeviceInitFailed)?;
Ok(())
}
(true, false) => embedded_hal_async::spi::SpiBus::write(&mut *guard, tx)
.await
.map_err(|_| SpiInitError::DeviceInitFailed),
(false, true) => embedded_hal_async::spi::SpiBus::read(&mut *guard, rx)
.await
.map_err(|_| SpiInitError::DeviceInitFailed),
_ => Ok(()),
}
}
}
#[cfg_attr(not(feature = "async"), deasync::deasync)]
pub async fn init_sd<'a>(
spi_bus: &'a SpiBusWrapper,
sd_cs: GPIO16<'a>,
delay: SdDelay,
) -> Result<SDC<'a>, SdInitError> {
let sd_cs = Output::new(sd_cs, Level::Low, OutputConfig::default());
let mut bus = SdBus::new(SdSpi(spi_bus), sd_cs, SdClock);
let card = bus.init(delay).await.map_err(|_| SdInitError::CardInitFailed)?;
let sd = sdmmc::SD::init(bus, card)
.await
.map_err(|_| SdInitError::CardInitFailed)?;
Ok(sd)
}