Skip to main content

core_s3/
sd.rs

1//! TF/microSD card metadata and helpers for CoreS3.
2//!
3//! CoreS3 routes the TF card over the same SPI signal group used by the LCD:
4//! SCLK GPIO36, MOSI/COPI GPIO37, MISO/CIPO GPIO35, and CS GPIO4. Applications
5//! that need both display and SD access should coordinate ownership of this
6//! shared bus at the HAL layer. The card-detect switch is exposed through
7//! AW9523B port 0 bit 4 and is active-low, matching the official M5Stack demo.
8
9use embedded_hal::{delay::DelayNs, spi::SpiDevice};
10
11use crate::pins::SpiSdPins;
12
13/// Default SPI clock used by M5Stack's CoreS3 SD demo.
14pub const DEFAULT_SPI_HZ: u32 = 25_000_000;
15/// AW9523B port-0 bit used for TF card detect.
16pub const CARD_DETECT_P0_BIT: u8 = 4;
17
18/// Static CoreS3 TF-card wiring and bus settings.
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
21pub struct SdCardSlot {
22    /// SPI pins for the TF card socket.
23    pub spi: SpiSdPins,
24    /// Recommended maximum SPI clock for initialization/use.
25    pub spi_hz: u32,
26    /// Card-detect signal exposed through AW9523B input port 0.
27    pub detect: SdCardDetect,
28}
29
30impl SdCardSlot {
31    /// CoreS3 onboard TF-card slot.
32    pub const CORE_S3: Self = Self {
33        spi: SpiSdPins::TF_CARD,
34        spi_hz: DEFAULT_SPI_HZ,
35        detect: SdCardDetect::AW9523B_P0_4_ACTIVE_LOW,
36    };
37}
38
39/// Card-detect wiring description.
40#[cfg_attr(feature = "defmt", derive(defmt::Format))]
41#[derive(Clone, Copy, Debug, Eq, PartialEq)]
42pub struct SdCardDetect {
43    /// AW9523B input port number.
44    pub port: u8,
45    /// Bit in the input port.
46    pub bit: u8,
47    /// Whether a low level means card-present.
48    pub active_low: bool,
49}
50
51impl SdCardDetect {
52    /// CoreS3 TF card detect: AW9523B port 0 bit 4, active-low.
53    pub const AW9523B_P0_4_ACTIVE_LOW: Self = Self {
54        port: 0,
55        bit: CARD_DETECT_P0_BIT,
56        active_low: true,
57    };
58
59    /// Interpret a raw AW9523B input-port value.
60    pub const fn present_from_port_value(self, value: u8) -> bool {
61        let high = (value & (1 << self.bit)) != 0;
62        if self.active_low { !high } else { high }
63    }
64}
65
66/// Runtime SD-slot metadata exposed to downstream storage stacks.
67#[cfg_attr(feature = "defmt", derive(defmt::Format))]
68#[derive(Clone, Copy, Debug, Eq, PartialEq)]
69pub struct CoreS3SdSlot {
70    /// SPI SCLK GPIO number.
71    pub sclk_gpio: u8,
72    /// SPI MOSI/COPI/CMD GPIO number.
73    pub mosi_gpio: u8,
74    /// SPI MISO/CIPO/D0 GPIO number. On CoreS3 this is also the LCD D/C pad.
75    pub miso_gpio: u8,
76    /// TF-card chip-select GPIO number.
77    pub cs_gpio: u8,
78    /// Maximum SPI frequency used by the official M5Stack SD example.
79    pub max_frequency_hz: u32,
80    /// Optional direct card-detect GPIO. CoreS3 uses AW9523B instead, so this is `None`.
81    pub card_detect_gpio: Option<u8>,
82    /// Optional direct power-enable GPIO. CoreS3 SD power is board-managed, so this is `None`.
83    pub power_enable_gpio: Option<u8>,
84}
85
86impl CoreS3SdSlot {
87    /// Onboard CoreS3 TF-card slot metadata.
88    pub const CORE_S3: Self = Self {
89        sclk_gpio: 36,
90        mosi_gpio: 37,
91        miso_gpio: 35,
92        cs_gpio: 4,
93        max_frequency_hz: DEFAULT_SPI_HZ,
94        card_detect_gpio: None,
95        power_enable_gpio: None,
96    };
97}
98
99impl From<SdCardSlot> for CoreS3SdSlot {
100    fn from(slot: SdCardSlot) -> Self {
101        Self {
102            sclk_gpio: slot.spi.sclk.0,
103            mosi_gpio: slot.spi.mosi.0,
104            miso_gpio: slot.spi.miso.0,
105            cs_gpio: slot.spi.cs.0,
106            max_frequency_hz: slot.spi_hz,
107            card_detect_gpio: None,
108            power_enable_gpio: None,
109        }
110    }
111}
112
113/// Low-level SD resources returned by ESP-HAL BSP helpers.
114///
115/// `spi_device` implements [`embedded_hal::spi::SpiDevice`] and can be passed to
116/// `embedded_sdmmc::SdCard::new(spi_device, delay)` by downstream firmware. The
117/// BSP intentionally does not add Wi-Fi credential, token, or application-secret
118/// abstractions; applications should encrypt sensitive bytes before writing them.
119pub struct CoreS3SdParts<SPI, DELAY> {
120    /// Chip-select scoped SPI device for the TF-card socket.
121    pub spi_device: SPI,
122    /// Delay provider suitable for SD-card initialization.
123    pub delay: DELAY,
124    /// Static CoreS3 TF-card slot metadata.
125    pub slot: CoreS3SdSlot,
126}
127
128impl<SPI, DELAY> CoreS3SdParts<SPI, DELAY>
129where
130    SPI: SpiDevice,
131    DELAY: DelayNs,
132{
133    /// Convert these parts into an `embedded-sdmmc` SD-card block device.
134    #[cfg(feature = "sdmmc")]
135    pub fn into_sdmmc(self) -> embedded_sdmmc::SdCard<SPI, DELAY> {
136        embedded_sdmmc::SdCard::new(self.spi_device, self.delay)
137    }
138}
139
140/// Interpret CoreS3's raw AW9523B P0 input byte as TF-card presence.
141pub const fn core_s3_card_present_from_aw9523_p0(value: u8) -> bool {
142    SdCardDetect::AW9523B_P0_4_ACTIVE_LOW.present_from_port_value(value)
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[test]
150    fn card_detect_is_active_low_on_p0_bit4() {
151        assert!(core_s3_card_present_from_aw9523_p0(0b1110_1111));
152        assert!(!core_s3_card_present_from_aw9523_p0(0b0001_0000));
153    }
154}