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}