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                // Added in ESP-IDF 6.0.1 and backported to ESP-IDF 5.5.5
331                #[cfg(any(
332                    esp_idf_version_at_least_6_0_1,
333                    esp_idf_version_patch_at_least_5_5_5
334                ))]
335                unaligned_multi_block_rw_max_chunk_size: 0,
336            };
337
338            let mut card: alloc::boxed::Box<sdmmc_card_t> = Default::default();
339
340            esp!(unsafe { sdmmc_card_init(&configuration, &mut *card) })?;
341
342            Ok(Self { _host: host, card })
343        }
344    }
345
346    #[cfg(esp_idf_soc_sdmmc_host_supported)]
347    impl<'d> SdCardDriver<SdMmcHostDriver<'d>> {
348        /// Create a new SD-Card driver using the SD-MMC Host driver instantiated with one of the two SD-MMC peripheral slots
349        pub fn new_mmc(
350            host: SdMmcHostDriver<'d>,
351            configuration: &config::Configuration,
352        ) -> Result<Self, EspError> {
353            let configuration = sdmmc_host_t {
354                flags: _SDMMC_HOST_FLAG_DEINIT_ARG
355                    | _SDMMC_HOST_FLAG_DDR
356                    // Bits 0 - 2 are flags for data widths 1, 4 and 8 respectively
357                    // Set the bit corresponding to our width and all smaller widths
358                    // in case the card does not support our width, but a smaller one only
359                    | (1 | (host.width() - 1)) as u32,
360                slot: host.slot() as _,
361                max_freq_khz: configuration.speed_khz as _,
362                io_voltage: configuration.io_voltage.as_native(),
363                init: Some(sdmmc_host_init),
364                set_bus_width: Some(sdmmc_host_set_bus_width),
365                get_bus_width: Some(sdmmc_host_get_slot_width),
366                set_bus_ddr_mode: Some(sdmmc_host_set_bus_ddr_mode),
367                set_card_clk: Some(sdmmc_host_set_card_clk),
368                set_cclk_always_on: Some(sdmmc_host_set_cclk_always_on),
369                do_transaction: Some(sdmmc_host_do_transaction),
370                __bindgen_anon_1: sdmmc_host_t__bindgen_ty_1 {
371                    deinit: Some(sdmmc_host_deinit),
372                },
373                io_int_enable: Some(sdmmc_host_io_int_enable),
374                io_int_wait: Some(sdmmc_host_io_int_wait),
375                get_real_freq: Some(sdmmc_host_get_real_freq),
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                input_delay_phase: configuration.input_delay_phase as _,
382                #[cfg(not(any(
383                    esp_idf_version_major = "4",
384                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
385                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
386                )))] // For ESP-IDF v5.2 and later            
387                set_input_delay: Some(sdmmc_host_set_input_delay),
388                #[cfg(esp_idf_version_at_least_6_0_0)]
389                set_input_delayline: None,
390                command_timeout_ms: configuration.command_timeout_ms as _,
391                #[cfg(not(any(
392                    esp_idf_version_major = "4",
393                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
394                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
395                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
396                )))]   // For ESP-IDF v5.3 and later
397                dma_aligned_buffer: core::ptr::null_mut(),
398                #[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
399                get_dma_info: Some(sdmmc_host_get_dma_info),
400                #[cfg(not(any(
401                    esp_idf_version_major = "4",
402                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
403                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
404                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
405                    all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
406                    all(esp_idf_version_major = "5", esp_idf_version_minor = "4"),
407                )))]   // For ESP-IDF v5.5 and later
408                check_buffer_alignment: Some(sdmmc_host_check_buffer_alignment),
409                #[cfg(not(any(
410                    esp_idf_version_major = "4",
411                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
412                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
413                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
414                )))]   // For ESP-IDF v5.3 and later
415                pwr_ctrl_handle: core::ptr::null_mut() as _,
416                #[cfg(not(any(
417                    esp_idf_version_major = "4",
418                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
419                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
420                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
421                    all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
422                )))] // For ESP-IDF v5.4 and later
423                driver_strength: configuration.driver_strength as _,
424                #[cfg(not(any(
425                    esp_idf_version_major = "4",
426                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
427                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
428                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
429                    all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
430                )))] // For ESP-IDF v5.4 and later
431                current_limit: configuration.current_limit as _,
432                #[cfg(not(any(
433                    esp_idf_version_major = "4",
434                    all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
435                    all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
436                    all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
437                    all(esp_idf_version_major = "5", esp_idf_version_minor = "3"),
438                )))] // For ESP-IDF v5.4 and later
439                is_slot_set_to_uhs1: None,
440                // Added in ESP-IDF 6.0.1 and backported to ESP-IDF 5.5.5
441                #[cfg(any(
442                    esp_idf_version_at_least_6_0_1,
443                    esp_idf_version_patch_at_least_5_5_5
444                ))]
445                unaligned_multi_block_rw_max_chunk_size: 0,
446            };
447
448            let mut card: alloc::boxed::Box<sdmmc_card_t> = Default::default();
449
450            esp!(unsafe { sdmmc_card_init(&configuration, &mut *card) })?;
451
452            Ok(Self { _host: host, card })
453        }
454    }
455}