Skip to content

esp32/sdcard: Add support for SDMMC power control with internal LDO.#18985

Open
sfe-SparkFro wants to merge 2 commits intomicropython:masterfrom
sparkfun:esp32_sd_pwr_ctrl_ldo
Open

esp32/sdcard: Add support for SDMMC power control with internal LDO.#18985
sfe-SparkFro wants to merge 2 commits intomicropython:masterfrom
sparkfun:esp32_sd_pwr_ctrl_ldo

Conversation

@sfe-SparkFro
Copy link
Copy Markdown
Contributor

Summary

Fixes #18984.

Testing

Before this change, the following error was thrown when trying to mount an SD card:

>>> import machine
>>> vfs.mount(machine.SDCard(), "/sd")
E (11418) sdmmc_common: sdmmc_init_ocr: send_op_cond (1) returned 0x107
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: 16

After this change, the SD card mounts just fine:

>>> import machine, os
>>> vfs.mount(machine.SDCard(), "/sd")
>>> os.listdir()
['sd', 'boot.py']

Trade-offs and Alternatives

This PR changes ESP32_GENERIC_P4 to assume LDO 4 is used for powering the SD card, which may not be true for all boards. This board definition is meant to be generic, so I can see arguments for not doing this. However without this change, an SD card cannot be used with the ESP32-P4-Function-EV-Board at all. Since the ESP32-P4-Function-EV-Board is what's depicted on the MicroPython download page, I would argue in favor of keeping this change.

A possible alternative is for the LDO to be specified as an argument in machine.SDCard() instead of the board definition. However for a given board, the LDO is a fixed parameter, so IMO it really should live in the board definition to avoid extra tedium with manually specifying the LDO in user code; I see the generic board definition as an exception.

Generative AI

I did not use generative AI tools when creating this PR.

A board can #define MICROPY_HW_SDMMC_LDO_CHAN_ID in mpconfigboard.h
for an internal LDO to control power to the SDMMC GPIO pins. This
is needed to use SDIO 3.0, because the IO level has to switch between
3.3V and 1.8V.

Signed-off-by: Dryw Wade <dryw.wade@sparkfun.com>
The ESP32-P4-Function-EV-Board uses LDO 4 for controlling SDMMC
GPIO power. This is required to use SD cards with this board.

Signed-off-by: Dryw Wade <dryw.wade@sparkfun.com>
@github-actions
Copy link
Copy Markdown

Code size report:

Reference:  tools/ci.sh: Increase qemu_arm test run timeout. [5c00edc]
Comparison: esp32/boards/ESP32_GENERIC_P4: Set SDMMC LDO channel. [merge of effd609]
  mpy-cross:    +0 +0.000% 
   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
      esp32:    +0 +0.000% ESP32_GENERIC
     mimxrt:    +0 +0.000% TEENSY40
        rp2:    +0 +0.000% RPI_PICO_W
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@itdogwowo
Copy link
Copy Markdown

try:
    from esp32 import LDO
    ldo = LDO(4, 3300, adjustable=True)
    sd = machine.SDCard(slot=0, width=4,
        sck=43, cmd=44,
        data=[39, 40, 41, 42],
        freq=40000000)
    os.mount(sd, '/sd')
except Exception as e:
    print(f"❌ SD card init error: {e}")

[https://github.com//pull/18538]

Is the driver duplicated?

@sfe-SparkFro
Copy link
Copy Markdown
Contributor Author

Ah, I was not aware of that feature, thanks!

Hmm, the tricky thing here is that the SDIO 3.0 spec requires the GPIO voltage to dynamically change between 3.3V and 1.8V, though I admittedly don't know when the voltage is meant to change back and forth. That's handled by the ESP-IDF when you set self->host.pwr_ctrl_handle = pwr_ctrl_handle. My guess is that if you don't set that, then the ESP-IDF disables SDIO 3.0 and limits the max transfer speed.

Perhaps machine.SDCard() needs an extra argument on the ESP32-P4 to optionally pass an esp32.LDO? Would also be nice if a default LDO can be specified in mpconfigboard.h on a per-board basis so users don't need to think about this (I'm guessing most users don't even know it's a thing that the ESP32-P4 does).

What do you think?

@sfe-SparkFro
Copy link
Copy Markdown
Contributor Author

Ok, I did some reading, and my suggestion of optionally passing an esp32.LDO to machine.SDCard() probably won't work. esp_ldo_acquire_channel() would get called twice, first when the esp32.LDO is created, and again when machine.SDCard() eventually calls sd_pwr_ctrl_new_on_chip_ldo(). An adjustable LDO cannot be acquired twice, quick test confirms this:

>>> import esp32
>>> esp32.LDO(4, 3300, adjustable=True)
LDO(channel=4, voltage=3300mV)
>>> esp32.LDO(4, 3300, adjustable=True)
E (11780) ldo: esp_ldo_acquire_channel(109): can't acquire the channel, already in use by others or not adjustable
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: (-258, 'ESP_ERR_INVALID_ARG')
>>> 

So this actually means the user needs to not create the esp32.LDO if they want to use SDIO 3.0.

@sfe-SparkFro
Copy link
Copy Markdown
Contributor Author

[https://github.com//pull/18538]

Is the driver duplicated?

To more directly answer your question - no, this PR is not a duplicate of the esp32.LDO driver. This PR gives the machine.SDCard control over an LDO so it can use SDIO 3.0, whereas esp32.LDO gives the user control of an LDO for whatever they want. These are 2 different things that utilize the same kind of resource (an LDO) for different use cases, but cannot not be used simultaneously with the same LDO (should explain this in the docs).

@itdogwowo
Copy link
Copy Markdown

Thank you for your response. I asked this question because I often encounter issues with objects failing to initialize after repeated soft reboots during development, and I can see this point reflected in your reply as well. Therefore, I have the same concern about the module implicitly initializing the LDO, as it could lead to multiple issues stacking up and making it difficult to determine which step went wrong (one action triggering two objects initially). However, I believe this can be addressed by clearly explaining it in the documentation.

@sfe-SparkFro
Copy link
Copy Markdown
Contributor Author

I asked this question because I often encounter issues with objects failing to initialize after repeated soft reboots during development

Is that possibly related to #19008?

In general, the same code should execute the same way after a soft reboot. If it doesn't, it's probably a bug.

FWIW I've tested this PR with soft reboots and it works fine on my end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

esp32/sdcard: Unable to use SD card on some ESP32-P4 boards.

3 participants