Skip to main content

esp_idf_hal/
sd.rs

1#[cfg(feature = "alloc")]
2pub use sdcard::*;
3
4#[cfg(esp_idf_soc_sdmmc_host_supported)]
5pub mod mmc;
6pub mod spi;
7
8#[cfg(feature = "alloc")]
9mod sdcard {
10    #[cfg(esp_idf_soc_sdmmc_host_supported)]
11    use super::mmc::SdMmcHostDriver;
12    use super::spi::SdSpiHostDriver;
13
14    use core::borrow::Borrow;
15
16    use crate::spi::SpiDriver;
17    use crate::sys::*;
18
19    extern crate alloc;
20
21    const _SDMMC_HOST_FLAG_SPI: u32 = 1 << 3;
22    const _SDMMC_HOST_FLAG_DDR: u32 = 1 << 4;
23    const _SDMMC_HOST_FLAG_DEINIT_ARG: u32 = 1 << 5;
24
25    pub type SdCardConfiguration = config::Configuration;
26
27    pub mod config {
28        #[cfg(not(any(
29            esp_idf_version_major = "4",
30            all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
31            all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
32        )))] // For ESP-IDF v5.2 and later
33        use crate::sys::*;
34
35        /// (SD-MMC only): Input delay phase
36        #[cfg(not(any(
37            esp_idf_version_major = "4",
38            all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
39            all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
40        )))] // For ESP-IDF v5.2 and later
41        #[non_exhaustive]
42        #[derive(Debug, Copy, Clone, Eq, PartialEq)]
43        pub enum DelayPhase {
44            Phase0 = sdmmc_delay_phase_t_SDMMC_DELAY_PHASE_0 as isize,
45            Phase1 = sdmmc_delay_phase_t_SDMMC_DELAY_PHASE_1 as isize,
46            Phase2 = sdmmc_delay_phase_t_SDMMC_DELAY_PHASE_2 as isize,
47            Phase3 = sdmmc_delay_phase_t_SDMMC_DELAY_PHASE_3 as isize,
48        }
49
50        // #[cfg(not(any(
51        //     esp_idf_version_major = "4",
52        //     all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
53        //     all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
54        // )))] // For ESP-IDF v5.2 and later
55        // impl From<sdmmc_delay_phase_t> for DelayPhase {
56        //     fn from(phase: sdmmc_delay_phase_t) -> Self {
57        //         #[allow(non_upper_case_globals)]
58        //         match phase {
59        //             sdmmc_delay_phase_t_SDMMC_DELAY_PHASE_0 => Self::Phase0,
60        //             sdmmc_delay_phase_t_SDMMC_DELAY_PHASE_1 => Self::Phase1,
61        //             sdmmc_delay_phase_t_SDMMC_DELAY_PHASE_2 => Self::Phase2,
62        //             sdmmc_delay_phase_t_SDMMC_DELAY_PHASE_3 => Self::Phase3,
63        //             _ => panic!("Invalid delay phase"),
64        //         }
65        //     }
66        // }
67
68        /// SD-Card voltage
69        #[non_exhaustive]
70        #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
71        pub enum Voltage {
72            /// (ESP32P4 only)
73            /// Use 1.8V IO voltage for UHS-I speed
74            /// This means the user has to provide an external LDO power supply
75            /// or to enable and configure an internal LDO via the `sdkconfig` setting
76            /// "SD/MMC Example Configuration -> SD power supply comes from internal LDO IO"
77            V1P8,
78            /// Use 3.3V IO voltage for speeds below UHS-I
79            /// The only supported conf for MCUs other than ESP32P4
80            #[default]
81            V3P3,
82        }
83
84        impl Voltage {
85            pub(crate) const fn as_native(&self) -> f32 {
86                match self {
87                    Self::V1P8 => 1.8,
88                    Self::V3P3 => 3.3,
89                }
90            }
91        }
92
93        /// (SD-MMC only): Driver Strength
94        #[cfg(not(any(
95            esp_idf_version_major = "4",
96            all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
97            all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
98            all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
99            all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
100        )))] // For ESP-IDF v5.4 and later
101        #[non_exhaustive]
102        #[derive(Debug, Copy, Clone, Eq, PartialEq)]
103        pub enum DriverStrength {
104            /// Type B
105            StrengthB = sdmmc_driver_strength_t_SDMMC_DRIVER_STRENGTH_B as isize,
106            /// Type A
107            StrengthA = sdmmc_driver_strength_t_SDMMC_DRIVER_STRENGTH_A as isize,
108            /// Type C
109            StrengthC = sdmmc_driver_strength_t_SDMMC_DRIVER_STRENGTH_C as isize,
110            /// Type D
111            StrengthD = sdmmc_driver_strength_t_SDMMC_DRIVER_STRENGTH_D as isize,
112        }
113
114        /// (SD-MMC only): Driver Strength
115        #[cfg(not(any(
116            esp_idf_version_major = "4",
117            all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
118            all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
119            all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
120            all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
121        )))] // For ESP-IDF v5.4 and later
122        #[non_exhaustive]
123        #[derive(Debug, Copy, Clone, Eq, PartialEq)]
124        pub enum CurrentLimit {
125            /// < 200mA
126            Limit200mA = sdmmc_current_limit_t_SDMMC_CURRENT_LIMIT_200MA as isize,
127            /// < 400mA
128            Limit400mA = sdmmc_current_limit_t_SDMMC_CURRENT_LIMIT_400MA as isize,
129            /// < 600mA
130            Limit600mA = sdmmc_current_limit_t_SDMMC_CURRENT_LIMIT_600MA as isize,
131            /// < 800mA
132            Limit800mA = sdmmc_current_limit_t_SDMMC_CURRENT_LIMIT_800MA as isize,
133        }
134
135        /// Configuration for the SD-Card driver
136        #[non_exhaustive]
137        pub struct Configuration {
138            /// Command timeout in milliseconds. Default is 0 (no timeout)
139            pub command_timeout_ms: u32,
140            /// SD-Card IO voltage. Default is 3.3V;
141            /// 1.8V might only be necessary for ESP32P4 and UHS-I speeds
142            pub io_voltage: Voltage,
143            /// SD-Card speed in kHz. Default is 20000 kHz (20 MHz)
144            /// Maximum speed is usually 40000 kHz (40 MHz)
145            /// Speeds lower than 20000 kHz can also be used
146            pub speed_khz: u32,
147            #[cfg(not(any(
148                esp_idf_version_major = "4",
149                all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
150                all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
151            )))] // For ESP-IDF v5.2 and later
152            pub input_delay_phase: DelayPhase,
153            #[cfg(not(any(
154                esp_idf_version_major = "4",
155                all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
156                all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
157                all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
158                all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
159            )))] // For ESP-IDF v5.4 and later
160            pub driver_strength: DriverStrength,
161            #[cfg(not(any(
162                esp_idf_version_major = "4",
163                all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
164                all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
165                all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
166                all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
167            )))] // For ESP-IDF v5.4 and later
168            pub current_limit: CurrentLimit,
169        }
170
171        impl Configuration {
172            /// Create a new configuration with default values
173            pub const fn new() -> Self {
174                Self {
175                    command_timeout_ms: 0,
176                    io_voltage: Voltage::V3P3,
177                    speed_khz: 20000,
178                    #[cfg(not(any(
179                        esp_idf_version_major = "4",
180                        all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
181                        all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
182                    )))] // For ESP-IDF v5.2 and later
183                    input_delay_phase: DelayPhase::Phase0,
184                    #[cfg(not(any(
185                        esp_idf_version_major = "4",
186                        all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
187                        all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
188                        all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
189                        all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
190                    )))] // For ESP-IDF v5.4 and later
191                    driver_strength: DriverStrength::StrengthB,
192                    #[cfg(not(any(
193                        esp_idf_version_major = "4",
194                        all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
195                        all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
196                        all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
197                        all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
198                    )))] // For ESP-IDF v5.4 and later
199                    current_limit: CurrentLimit::Limit200mA,
200                }
201            }
202        }
203
204        impl Default for Configuration {
205            fn default() -> Self {
206                Self::new()
207            }
208        }
209    }
210
211    /// A high-level SD-Card driver.
212    ///
213    /// This driver is used to interface with an SD-Card by wrapping one of the two SD Host drivers:
214    /// - SD-SPI Host driver (`SdSpiHostDriver`)
215    /// - SD-MMC Host driver (`SdMmcHostDriver`) - on MCUs that do have an SD-MMC peripheral (ESP32, ESP32S3 and ESP32P4)
216    ///
217    /// The interface allows reading, writing and erasing sectors, as well as reading and writing arbitrary-length bytes.
218    ///
219    /// Currently, all interaction with the SD-Card driver is via the native, unsafe `sys::sdmmc_*` functions.
220    pub struct SdCardDriver<T> {
221        _host: T,
222        card: alloc::boxed::Box<sdmmc_card_t>,
223    }
224
225    impl<T> SdCardDriver<T> {
226        /// Get a reference to the SD-Card native structure.
227        pub fn card(&self) -> &sdmmc_card_t {
228            &self.card
229        }
230
231        // TODO: Implement the SD-Card API here, i.e. read/write/erase sectors, as well as
232        // read/write of arbitrary-length bytes.
233    }
234
235    impl<'d, T> SdCardDriver<SdSpiHostDriver<'d, T>>
236    where
237        T: Borrow<SpiDriver<'d>>,
238    {
239        /// Create a new SD-Card driver using the SD-SPI host driver instantiated with one of the SPI peripherals
240        pub fn new_spi(
241            host: SdSpiHostDriver<'d, T>,
242            configuration: &config::Configuration,
243        ) -> Result<Self, EspError> {
244            let configuration = sdmmc_host_t {
245                flags: _SDMMC_HOST_FLAG_SPI | _SDMMC_HOST_FLAG_DEINIT_ARG,
246                slot: host.handle() as _,
247                max_freq_khz: configuration.speed_khz as _,
248                io_voltage: configuration.io_voltage.as_native(),
249                init: Some(sdspi_host_init),
250                set_bus_width: None,
251                get_bus_width: None,
252                set_bus_ddr_mode: None,
253                set_card_clk: Some(sdspi_host_set_card_clk),
254                set_cclk_always_on: None,
255                do_transaction: Some(sdspi_host_do_transaction),
256                __bindgen_anon_1: sdmmc_host_t__bindgen_ty_1 {
257                    deinit_p: Some(sdspi_host_remove_device),
258                },
259                io_int_enable: Some(sdspi_host_io_int_enable),
260                io_int_wait: Some(sdspi_host_io_int_wait),
261                #[cfg(not(any(
262                    esp_idf_version_major = "4",
263                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
264                )))]    // For ESP-IDF v5.1 and later
265                get_real_freq: Some(sdspi_host_get_real_freq),
266                #[cfg(not(any(
267                    esp_idf_version_major = "4",
268                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
269                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
270                )))]    // For ESP-IDF v5.2 and later
271                input_delay_phase: configuration.input_delay_phase as _,
272                #[cfg(not(any(
273                    esp_idf_version_major = "4",
274                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
275                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
276                )))]   // For ESP-IDF v5.2 and later
277                set_input_delay: None,
278                #[cfg(esp_idf_version_at_least_6_0_0)]
279                set_input_delayline: None,
280                command_timeout_ms: configuration.command_timeout_ms as _,
281                #[cfg(not(any(
282                    esp_idf_version_major = "4",
283                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
284                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
285                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
286                )))]   // For ESP-IDF v5.3 and later
287                dma_aligned_buffer: core::ptr::null_mut(),
288                #[cfg(all(esp_idf_version_at_least_5_3_0, not(esp_idf_version_at_least_6_0_0)))] // For ESP-IDF v5.3 until v6.0
289                get_dma_info: Some(sdspi_host_get_dma_info),
290                #[cfg(not(any(
291                    esp_idf_version_major = "4",
292                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
293                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
294                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
295                    all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
296                    all(esp_idf_version_major = "5", esp_idf_version_minor = "4"),
297                )))]   // For ESP-IDF v5.5 and later
298                check_buffer_alignment: Some(sdspi_host_check_buffer_alignment),
299                #[cfg(not(any(
300                    esp_idf_version_major = "4",
301                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
302                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
303                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
304                )))]   // For ESP-IDF v5.3 and later
305                pwr_ctrl_handle: core::ptr::null_mut() as _,
306                #[cfg(not(any(
307                    esp_idf_version_major = "4",
308                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
309                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
310                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
311                    all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
312                )))] // For ESP-IDF v5.4 and later
313                driver_strength: configuration.driver_strength as _,
314                #[cfg(not(any(
315                    esp_idf_version_major = "4",
316                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
317                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
318                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
319                    all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
320                )))] // For ESP-IDF v5.4 and later
321                current_limit: configuration.current_limit as _,
322                #[cfg(not(any(
323                    esp_idf_version_major = "4",
324                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
325                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
326                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
327                    all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
328                )))] // For ESP-IDF v5.4 and later
329                is_slot_set_to_uhs1: None,
330            };
331
332            let mut card: alloc::boxed::Box<sdmmc_card_t> = Default::default();
333
334            esp!(unsafe { sdmmc_card_init(&configuration, &mut *card) })?;
335
336            Ok(Self { _host: host, card })
337        }
338    }
339
340    #[cfg(esp_idf_soc_sdmmc_host_supported)]
341    impl<'d> SdCardDriver<SdMmcHostDriver<'d>> {
342        /// Create a new SD-Card driver using the SD-MMC Host driver instantiated with one of the two SD-MMC peripheral slots
343        pub fn new_mmc(
344            host: SdMmcHostDriver<'d>,
345            configuration: &config::Configuration,
346        ) -> Result<Self, EspError> {
347            let configuration = sdmmc_host_t {
348                flags: _SDMMC_HOST_FLAG_DEINIT_ARG
349                    | _SDMMC_HOST_FLAG_DDR
350                    // Bits 0 - 2 are flags for data widths 1, 4 and 8 respectively
351                    // Set the bit corresponding to our width and all smaller widths
352                    // in case the card does not support our width, but a smaller one only
353                    | (1 | (host.width() - 1)) as u32,
354                slot: host.slot() as _,
355                max_freq_khz: configuration.speed_khz as _,
356                io_voltage: configuration.io_voltage.as_native(),
357                init: Some(sdmmc_host_init),
358                set_bus_width: Some(sdmmc_host_set_bus_width),
359                get_bus_width: Some(sdmmc_host_get_slot_width),
360                set_bus_ddr_mode: Some(sdmmc_host_set_bus_ddr_mode),
361                set_card_clk: Some(sdmmc_host_set_card_clk),
362                set_cclk_always_on: Some(sdmmc_host_set_cclk_always_on),
363                do_transaction: Some(sdmmc_host_do_transaction),
364                __bindgen_anon_1: sdmmc_host_t__bindgen_ty_1 {
365                    deinit: Some(sdmmc_host_deinit),
366                },
367                io_int_enable: Some(sdmmc_host_io_int_enable),
368                io_int_wait: Some(sdmmc_host_io_int_wait),
369                get_real_freq: Some(sdmmc_host_get_real_freq),
370                #[cfg(not(any(
371                    esp_idf_version_major = "4",
372                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
373                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
374                )))] // For ESP-IDF v5.2 and later            
375                input_delay_phase: configuration.input_delay_phase as _,
376                #[cfg(not(any(
377                    esp_idf_version_major = "4",
378                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
379                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
380                )))] // For ESP-IDF v5.2 and later            
381                set_input_delay: Some(sdmmc_host_set_input_delay),
382                #[cfg(esp_idf_version_at_least_6_0_0)]
383                set_input_delayline: None,
384                command_timeout_ms: configuration.command_timeout_ms as _,
385                #[cfg(not(any(
386                    esp_idf_version_major = "4",
387                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
388                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
389                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
390                )))]   // For ESP-IDF v5.3 and later
391                dma_aligned_buffer: core::ptr::null_mut(),
392                #[cfg(all(esp_idf_version_at_least_5_3_0, not(esp_idf_version_at_least_6_0_0)))] // For ESP-IDF v5.3 until v6.0
393                get_dma_info: Some(sdmmc_host_get_dma_info),
394                #[cfg(not(any(
395                    esp_idf_version_major = "4",
396                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
397                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
398                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
399                    all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
400                    all(esp_idf_version_major = "5", esp_idf_version_minor = "4"),
401                )))]   // For ESP-IDF v5.5 and later
402                check_buffer_alignment: Some(sdmmc_host_check_buffer_alignment),
403                #[cfg(not(any(
404                    esp_idf_version_major = "4",
405                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
406                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
407                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
408                )))]   // For ESP-IDF v5.3 and later
409                pwr_ctrl_handle: core::ptr::null_mut() as _,
410                #[cfg(not(any(
411                    esp_idf_version_major = "4",
412                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
413                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
414                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
415                    all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
416                )))] // For ESP-IDF v5.4 and later
417                driver_strength: configuration.driver_strength as _,
418                #[cfg(not(any(
419                    esp_idf_version_major = "4",
420                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
421                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
422                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
423                    all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
424                )))] // For ESP-IDF v5.4 and later
425                current_limit: configuration.current_limit as _,
426                #[cfg(not(any(
427                    esp_idf_version_major = "4",
428                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
429                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
430                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
431                    all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
432                )))] // For ESP-IDF v5.4 and later
433                is_slot_set_to_uhs1: None,
434            };
435
436            let mut card: alloc::boxed::Box<sdmmc_card_t> = Default::default();
437
438            esp!(unsafe { sdmmc_card_init(&configuration, &mut *card) })?;
439
440            Ok(Self { _host: host, card })
441        }
442    }
443}