Merge branch 'feat/esp_ldo_regulator_public_api' into 'master'

Introduce esp ldo regulator public API

See merge request espressif/esp-idf!29691
This commit is contained in:
morris 2024-03-26 11:24:06 +08:00
commit 52137a2b55
38 changed files with 579 additions and 298 deletions

View File

@ -19,7 +19,6 @@
#include "driver/gpio.h"
#include "driver/sdmmc_host.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/esp_ldo.h"
#include "sdmmc_private.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
@ -55,7 +54,6 @@ typedef struct slot_ctx_t {
size_t slot_width;
sdmmc_slot_io_info_t slot_gpio_num;
bool use_gpio_matrix;
esp_ldo_unit_handle_t ldo_unit;
} slot_ctx_t;
/**

View File

@ -39,7 +39,7 @@ void sdmmc_test_board_get_config_sdmmc(int slot_index, sdmmc_host_t *out_host_co
#define SDMMC_PWR_LDO_CHANNEL 4
sd_pwr_ctrl_ldo_config_t ldo_config = {
.ldo_unit_id = SDMMC_PWR_LDO_CHANNEL,
.ldo_chan_id = SDMMC_PWR_LDO_CHANNEL,
};
sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL;

View File

@ -73,11 +73,8 @@ if(NOT BOOTLOADER_BUILD)
endif()
endif()
if(CONFIG_SOC_MULTI_USAGE_LDO_SUPPORTED)
list(APPEND srcs "ldo/esp_ldo.c")
if(CONFIG_SPIRAM OR CONFIG_SOC_CLK_MPLL_SUPPORTED)
list(APPEND srcs "ldo/esp_ldo_psram.c")
endif()
if(CONFIG_SOC_GP_LDO_SUPPORTED)
list(APPEND srcs "ldo/esp_ldo.c" "ldo/esp_ldo_regulator.c")
endif()
if(CONFIG_SOC_ASYNC_MEMCPY_SUPPORTED)
@ -168,7 +165,7 @@ else()
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS include include/soc include/soc/${target} dma/include
INCLUDE_DIRS include include/soc include/soc/${target} dma/include ldo/include
PRIV_INCLUDE_DIRS port/include include/esp_private
REQUIRES ${requires}
PRIV_REQUIRES "${priv_requires}"

View File

@ -331,10 +331,7 @@ menu "Hardware Settings"
endmenu
menu "LDO Config"
depends on SOC_MULTI_USAGE_LDO_SUPPORTED
orsource "./port/$IDF_TARGET/Kconfig.ldo"
endmenu
orsource "./port/$IDF_TARGET/Kconfig.ldo"
# Invisible bringup bypass options for esp_hw_support component
config ESP_BRINGUP_BYPASS_CPU_CLK_SETTING

View File

@ -7,11 +7,11 @@
#include <freertos/FreeRTOS.h>
#include "clk_ctrl_os.h"
#include "soc/rtc.h"
#include "esp_ldo_regulator.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_check.h"
#if SOC_CLK_MPLL_SUPPORTED
#include "rtc_clk.h"
#include "esp_private/esp_ldo_psram.h"
#endif
#if SOC_CLK_APLL_SUPPORTED || SOC_CLK_MPLL_SUPPORTED
@ -30,7 +30,7 @@ static int s_apll_ref_cnt = 0;
#if SOC_CLK_MPLL_SUPPORTED
static uint32_t s_cur_mpll_freq = 0;
static int s_mpll_ref_cnt = 0;
static esp_ldo_unit_handle_t s_ldo_unit_hndl = NULL;
static esp_ldo_channel_handle_t s_ldo_chan = NULL;
#endif
bool periph_rtc_dig_clk8m_enable(void)
@ -125,7 +125,7 @@ esp_err_t periph_rtc_apll_freq_set(uint32_t expt_freq, uint32_t *real_freq)
if (need_config) {
ESP_LOGD(TAG, "APLL will working at %"PRIu32" Hz with coefficients [sdm0] %"PRIu32" [sdm1] %"PRIu32" [sdm2] %"PRIu32" [o_div] %"PRIu32"",
apll_freq, sdm0, sdm1, sdm2, o_div);
apll_freq, sdm0, sdm1, sdm2, o_div);
/* Set coefficients for APLL, notice that it doesn't mean APLL will start */
rtc_clk_apll_coeff_set(o_div, sdm0, sdm1, sdm2);
} else {
@ -137,51 +137,34 @@ esp_err_t periph_rtc_apll_freq_set(uint32_t expt_freq, uint32_t *real_freq)
#endif // SOC_CLK_APLL_SUPPORTED
#if SOC_CLK_MPLL_SUPPORTED
void periph_rtc_mpll_early_acquire(void)
{
portENTER_CRITICAL(&periph_spinlock);
s_mpll_ref_cnt++;
if (s_mpll_ref_cnt == 1) {
#if SOC_PSRAM_VDD_POWER_MPLL
// configure MPPL power (MPLL power pin is the same as for the PSRAM)
s_ldo_unit_hndl = esp_ldo_vdd_psram_early_init();
#endif
// For the first time enable MPLL, need to set power up
rtc_clk_mpll_enable();
}
portEXIT_CRITICAL(&periph_spinlock);
}
esp_err_t periph_rtc_mpll_acquire(void)
{
esp_err_t ret = ESP_OK;
// power up LDO for the MPLL
#if defined(CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN) && CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN != -1
esp_ldo_channel_config_t ldo_mpll_config = {
.chan_id = CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN,
.voltage_mv = CONFIG_ESP_LDO_VOLTAGE_PSRAM_DOMAIN,
};
ESP_RETURN_ON_ERROR(esp_ldo_acquire_channel(&ldo_mpll_config, &s_ldo_chan), TAG, "acquire internal LDO for MPLL failed");
#endif
portENTER_CRITICAL(&periph_spinlock);
s_mpll_ref_cnt++;
if (s_mpll_ref_cnt == 1) {
#if SOC_PSRAM_VDD_POWER_MPLL
// configure MPPL power (MPLL power pin is the same as for the PSRAM)
ret = esp_ldo_vdd_psram_init(&s_ldo_unit_hndl);
// external power supply in use is a valid condition
if (ret == ESP_ERR_INVALID_STATE) {
ret = ESP_OK;
} else if (ret != ESP_OK ) {
portEXIT_CRITICAL(&periph_spinlock);
ESP_LOGE(TAG, "failed to initialize PSRAM internal LDO");
goto err;
}
#endif
// For the first time enable MPLL, need to set power up
rtc_clk_mpll_enable();
}
portEXIT_CRITICAL(&periph_spinlock);
#if SOC_PSRAM_VDD_POWER_MPLL
err:
#endif
return ret;
return ESP_OK;
}
void periph_rtc_mpll_release(void)
{
#if defined(CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN) && CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN != -1
if (s_ldo_chan) {
esp_ldo_release_channel(s_ldo_chan);
}
#endif
portENTER_CRITICAL(&periph_spinlock);
assert(s_mpll_ref_cnt > 0);
s_mpll_ref_cnt--;
@ -189,11 +172,6 @@ void periph_rtc_mpll_release(void)
// If there is no peripheral using MPLL, shut down the power
s_cur_mpll_freq = 0;
rtc_clk_mpll_disable();
#if SOC_PSRAM_VDD_POWER_MPLL
if (s_ldo_unit_hndl) {
esp_ldo_vdd_psram_deinit(s_ldo_unit_hndl);
}
#endif
}
portEXIT_CRITICAL(&periph_spinlock);
}

View File

@ -73,11 +73,6 @@ esp_err_t periph_rtc_apll_freq_set(uint32_t expt_freq, uint32_t *real_freq);
#endif // SOC_CLK_APLL_SUPPORTED
#if SOC_CLK_MPLL_SUPPORTED
/**
* @brief Enable MPLL power if it has not enabled (early version)
*/
void periph_rtc_mpll_early_acquire(void);
/**
* @brief Enable MPLL power if it has not enabled
*/

View File

@ -1,49 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "esp_private/esp_ldo.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Init PSRAM VDD LDO during early stage
*
* @return
* - LDO unit handle on success
* - NULL when external power supply is configured to be used
*/
esp_ldo_unit_handle_t esp_ldo_vdd_psram_early_init(void);
/**
* @brief Init PSRAM VDD LDO
*
* @param[out] ldo_unit LDO unit handle
*
* @return
* - ESP_OK Successful.
* - ESP_ERR_INVALID_STATE External power supply is configured to be used
* - ESP_ERR_INVALID_ARG Arguments is NULL or invalid LDO configuration.
* - other error codes from lower-level driver.
*
*/
esp_err_t esp_ldo_vdd_psram_init(esp_ldo_unit_handle_t *ldo_unit);
/**
* @brief De-init PSRAM VDD LDO
*
* @param[in] ldo_unit LDO unit handle
*/
esp_err_t esp_ldo_vdd_psram_deinit(esp_ldo_unit_handle_t ldo_unit);
#ifdef __cplusplus
}
#endif

View File

@ -1,62 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_check.h"
#include "soc/soc_caps.h"
#include "hal/ldo_ll.h"
#include "esp_private/esp_ldo_psram.h"
static const char *TAG = "ldo_psram";
esp_ldo_unit_handle_t esp_ldo_vdd_psram_early_init(void)
{
if (CONFIG_ESP_VDD_PSRAM_LDO_ID != -1) {
esp_ldo_unit_init_cfg_t unit_cfg = {
.unit_id = CONFIG_ESP_VDD_PSRAM_LDO_ID,
.cfg = {
.voltage_mv = CONFIG_ESP_VDD_PSRAM_LDO_VOLTAGE_MV,
},
.flags.enable_unit = true,
.flags.shared_ldo = true,
};
esp_ldo_unit_handle_t early_unit = esp_ldo_init_unit_early(&unit_cfg);
assert(early_unit);
return early_unit;
}
return NULL;
}
esp_err_t esp_ldo_vdd_psram_init(esp_ldo_unit_handle_t *ldo_unit)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(ldo_unit, ESP_ERR_INVALID_ARG, TAG, "null pointer");
if (CONFIG_ESP_VDD_PSRAM_LDO_ID != -1) {
esp_ldo_unit_init_cfg_t unit_cfg = {
.unit_id = LDO_ID2UNIT(CONFIG_ESP_VDD_PSRAM_LDO_ID),
.cfg = {
.voltage_mv = CONFIG_ESP_VDD_PSRAM_LDO_VOLTAGE_MV,
},
.flags.enable_unit = true,
.flags.shared_ldo = true,
};
ESP_RETURN_ON_ERROR(esp_ldo_init_unit(&unit_cfg, ldo_unit), TAG, "internal LDO init failed");
} else {
ESP_LOGD(TAG, "internal LDO not initialized, external power supply is configured to be used");
*ldo_unit = NULL;
return ESP_ERR_INVALID_STATE;
}
return ret;
}
esp_err_t esp_ldo_vdd_psram_deinit(esp_ldo_unit_handle_t ldo_unit)
{
return esp_ldo_deinit_unit(ldo_unit);
}

View File

@ -0,0 +1,169 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include <stdatomic.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "soc/soc_caps.h"
#include "hal/ldo_ll.h"
#include "esp_ldo_regulator.h"
static const char *TAG = "ldo";
typedef struct ldo_regulator_channel_t {
int chan_id;
int voltage_mv;
int ref_cnt;
struct {
uint32_t adjustable : 1;
} flags;
} ldo_regulator_channel_t;
static portMUX_TYPE s_spinlock = portMUX_INITIALIZER_UNLOCKED;
static const uint32_t s_ldo_channel_adjustable_mask = LDO_LL_ADJUSTABLE_CHAN_MASK; // each bit represents if the LDO channel is adjustable in hardware
static ldo_regulator_channel_t s_ldo_channels[LDO_LL_UNIT_NUM] = {
[0 ... LDO_LL_UNIT_NUM - 1] = {
.chan_id = -1,
.voltage_mv = 0,
.ref_cnt = 0,
.flags.adjustable = 1,
},
};
esp_err_t esp_ldo_acquire_channel(const esp_ldo_channel_config_t *config, esp_ldo_channel_handle_t *out_handle)
{
ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(ldo_ll_is_valid_ldo_id(config->chan_id), ESP_ERR_INVALID_ARG, TAG, "invalid ldo channel ID");
ESP_RETURN_ON_FALSE(config->voltage_mv <= LDO_LL_MAX_VOLTAGE_MV,
ESP_ERR_INVALID_ARG, TAG, "invalid voltage value: %d", config->voltage_mv);
int unit_id = LDO_ID2UNIT(config->chan_id);
ldo_regulator_channel_t *channel = &s_ldo_channels[unit_id];
bool check_adjustable_constraint_valid = true;
bool check_voltage_constraint_valid = true;
portENTER_CRITICAL(&s_spinlock);
if (config->flags.adjustable) {
// the user wants to adjust it
// but the channel is marked as not adjustable
if (channel->flags.adjustable == 0) {
check_adjustable_constraint_valid = false;
} else if (channel->ref_cnt != 0) {
// or the channel is already in use by others
// but we don't allow different users to adjust the same LDO, in case they set to different voltages
// that's to say, if the LDO channel is adjustable, it can only have one reference
check_adjustable_constraint_valid = false;
}
} else {
// the user doesn't want to adjust the voltage
// but the channel is already in use by others
if (channel->ref_cnt != 0) {
if (channel->flags.adjustable) {
// we don't allow to have another non-adjustable user
check_adjustable_constraint_valid = false;
} else if (channel->voltage_mv != config->voltage_mv) {
// the voltage is different from us
check_voltage_constraint_valid = false;
}
}
}
if (check_voltage_constraint_valid && check_adjustable_constraint_valid) {
if (channel->ref_cnt == 0) {
// if the channel is not in use, we need to set the voltage and enable it
ldo_ll_set_output_voltage_mv(unit_id, config->voltage_mv);
ldo_ll_enable(unit_id, true);
}
// update the channel attributes
channel->ref_cnt++;
channel->voltage_mv = config->voltage_mv;
channel->flags.adjustable = config->flags.adjustable;
channel->chan_id = config->chan_id;
}
portEXIT_CRITICAL(&s_spinlock);
ESP_RETURN_ON_FALSE(check_voltage_constraint_valid, ESP_ERR_INVALID_ARG, TAG,
"can't change the voltage for a non-adjustable channel, expect:%dmV, current:%dmV",
config->voltage_mv, channel->voltage_mv);
ESP_RETURN_ON_FALSE(check_adjustable_constraint_valid, ESP_ERR_INVALID_ARG, TAG,
"can't acquire the channel, already in use by others or not adjustable");
if (out_handle) {
*out_handle = channel;
}
return ESP_OK;
}
esp_err_t esp_ldo_release_channel(esp_ldo_channel_handle_t chan)
{
ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
int unit_id = LDO_ID2UNIT(chan->chan_id);
bool is_valid_state = true;
portENTER_CRITICAL(&s_spinlock);
if (chan->ref_cnt <= 0) {
is_valid_state = false;
} else {
chan->ref_cnt--;
if (chan->ref_cnt == 0) {
// if the channel is not in use, we need to disable it
ldo_ll_enable(unit_id, false);
// and reset the ldo voltage
chan->voltage_mv = 0;
chan->flags.adjustable = (s_ldo_channel_adjustable_mask & (1 << unit_id)) != 0;
chan->chan_id = -1;
}
}
portEXIT_CRITICAL(&s_spinlock);
ESP_RETURN_ON_FALSE(is_valid_state, ESP_ERR_INVALID_STATE, TAG, "LDO channel released too many times");
return ESP_OK;
}
esp_err_t esp_ldo_channel_adjust_voltage(esp_ldo_channel_handle_t chan, int voltage_mv)
{
ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(chan->flags.adjustable, ESP_ERR_NOT_SUPPORTED, TAG, "LDO is not adjustable");
// check if the voltage is within the valid range
ESP_RETURN_ON_FALSE(voltage_mv >= LDO_LL_MIN_VOLTAGE_MV && voltage_mv <= LDO_LL_MAX_VOLTAGE_MV,
ESP_ERR_INVALID_ARG, TAG, "invalid voltage value: %d", voltage_mv);
// About Thread Safety:
// because there won't be more than 1 consumer for the same adjustable LDO channel (guaranteed by esp_ldo_acquire_channel)
// this function should be thread safe as long as the LDO channel handle is thread safe,
// i.e., the handle is not shared between threads without mutex protection
chan->voltage_mv = voltage_mv;
int unit_id = LDO_ID2UNIT(chan->chan_id);
ldo_ll_set_output_voltage_mv(unit_id, voltage_mv);
return ESP_OK;
}
esp_err_t esp_ldo_dump(FILE *stream)
{
char line[100];
fprintf(stream, "ESP LDO Channel State:\n");
fprintf(stream, "%-5s %-5s %-10s %-12s %-5s\n", "Index", "ID", "ref_cnt", "voltage_mv", "adjustable");
for (int i = 0; i < LDO_LL_UNIT_NUM; i++) {
char *buf = line;
size_t len = sizeof(line);
memset(line, 0x0, len);
snprintf(buf, len, "%-5d %-5d %-10d %-12d %-5s\n",
i,
s_ldo_channels[i].chan_id,
s_ldo_channels[i].ref_cnt,
s_ldo_channels[i].voltage_mv,
s_ldo_channels[i].flags.adjustable ? "yes" : "no");
fputs(line, stream);
}
return ESP_OK;
}

View File

@ -0,0 +1,90 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdio.h>
#include "esp_err.h"
#include "hal/ldo_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Type of LDO regulator channel handle
*/
typedef struct ldo_regulator_channel_t *esp_ldo_channel_handle_t;
/**
* @brief LDO channel configurations
*/
typedef struct {
int chan_id; /*!< You must set the LDO channel ID according to the datasheet, e.g., set it to 1 for LDO_VO1 */
int voltage_mv; /*!< The voltage value to be set to the LDO channel */
/// Extra flags of a LDO channel
struct ldo_extra_flags {
uint32_t adjustable : 1; /*!< Whether the LDO channel is adjustable, and the voltage can be updated by `esp_ldo_channel_adjust_voltage` */
} flags; /*!< Flags for the LDO channel */
} esp_ldo_channel_config_t;
/**
* @brief Acquire an LDO channel with the specified configuration
*
* @note This function can't automatically search a LDO channel for you, you must specify a LDO channel ID manually, based on your schematic.
* @note The same channel can be acquired multiple times in different places of the application code, however,
* if the LDO channel is adjustable, you can't acquire it multiple times, in case user A changes the voltage and breaks the voltage setting of user B.
* @note You should release the channel by `esp_ldo_release_channel` when it's no longer needed.
*
* @param[in] config The configuration of the LDO channel
* @param[out] out_handle The returned LDO channel handle
* @return
* - ESP_OK: Acquire the LDO channel successfully
* - ESP_ERR_INVALID_ARG: Acquire the LDO channel failed due to invalid arguments
* - ESP_FAIL: Acquire the LDO channel failed due to other reasons
*/
esp_err_t esp_ldo_acquire_channel(const esp_ldo_channel_config_t *config, esp_ldo_channel_handle_t *out_handle);
/**
* @brief Release the LDO channel
*
* @param[in] chan The LDO channel handle returned from `esp_ldo_acquire_channel`
* @return
* - ESP_OK: Release the LDO channel successfully
* - ESP_ERR_INVALID_ARG: Release the LDO channel failed due to invalid arguments
* - ESP_ERR_INVALID_STATE: Release the LDO channel failed due to invalid state, e.g., the channel handle is double released
* - ESP_FAIL: Release the LDO channel failed due to other reasons
*/
esp_err_t esp_ldo_release_channel(esp_ldo_channel_handle_t chan);
/**
* @brief Adjust the voltage of the LDO channel
*
* @param[in] chan The LDO channel handle returned from `esp_ldo_acquire_channel`
* @param[in] voltage_mv The voltage value to be set to the LDO channel, in millivolts
* @return
* - ESP_OK: Adjust the voltage of the LDO channel successfully
* - ESP_ERR_INVALID_ARG: Adjust the voltage of the LDO channel failed due to invalid arguments
* - ESP_ERR_NOT_SUPPORTED: Adjust the voltage of the LDO channel failed due to the channel is not adjustable
* - ESP_FAIL: Adjust the voltage of the LDO channel failed due to other reasons
*/
esp_err_t esp_ldo_channel_adjust_voltage(esp_ldo_channel_handle_t chan, int voltage_mv);
/**
* @brief Dump LDO channel status to the specified stream
*
* @param[in] stream IO stream. Can be stdout, stderr, or a file/string stream.
* @return
* - ESP_OK: Dump the LDO channel status successfully
* - ESP_FAIL: Dump the LDO channel status failed
*/
esp_err_t esp_ldo_dump(FILE *stream);
#ifdef __cplusplus
}
#endif

View File

@ -1,4 +0,0 @@
[mapping:ldo_driver]
archive: libesp_hw_support.a
entries:
esp_ldo: esp_ldo_init_unit_early (noflash)

View File

@ -1,24 +1,53 @@
config ESP_VDD_PSRAM_LDO_ID
int "PSRAM VDD connected LDO ID, set -1 for using external power supply and disable internal LDO"
default 2
range -1 4
help
PSRAM VDD pin connected LDO ID.
PSRAM VDD needs to be connected to an voltage output. This option selects the on-chip
LDO which is connected to the PSRAM VDD.
Set to -1 for connecting to external voltage output.
menu "LDO Regulator Configurations"
depends on SOC_GP_LDO_SUPPORTED
choice ESP_VDD_PSRAM_LDO_VOLTAGE_MV
prompt "PSRAM VDD connected LDO voltage"
depends on ESP_VDD_PSRAM_LDO_ID != -1
default ESP_VDD_PSRAM_LDO_VOLTAGE_MV_1900
help
Select the LDO (ESP_VDD_PSRAM_LDO_ID) voltage output
config ESP_LDO_CHAN_SPI_NOR_FLASH_DOMAIN
int "LDO regulator channel that used to power SPI NOR Flash (READ HELP)"
default 1
range -1 4
help
The internal LDO regulator can be used to power the SPI Flash specific power domain.
This option is to select which LDO channel to connect to that domain.
Please set this option correctly according to your schematic.
Set to -1 if the Flash is using any external power supply.
config ESP_VDD_PSRAM_LDO_VOLTAGE_MV_1900
bool "1.9V"
endchoice
choice ESP_LDO_VOLTAGE_SPI_NOR_FLASH_DOMAIN
prompt "SPI NOR Flash power domain voltage"
depends on ESP_LDO_CHAN_SPI_NOR_FLASH_DOMAIN != -1
default ESP_LDO_VOLTAGE_SPI_NOR_FLASH_3300_MV
help
Select the voltage used by the Flash power domain.
config ESP_VDD_PSRAM_LDO_VOLTAGE_MV
int
default 1900 if ESP_VDD_PSRAM_LDO_VOLTAGE_MV_1900
config ESP_LDO_VOLTAGE_SPI_NOR_FLASH_3300_MV
bool "3.3V"
endchoice
config ESP_LDO_VOLTAGE_SPI_NOR_FLASH_DOMAIN
int
default 3300 if ESP_LDO_VOLTAGE_SPI_NOR_FLASH_3300_MV
config ESP_LDO_CHAN_PSRAM_DOMAIN
int "LDO regulator channel that used to power PSRAM and MPLL (READ HELP)"
default 2
range -1 4
help
The internal LDO regulator can be used to power the PSRAM specific power domain.
This option is to select which LDO channel to connect to that domain.
Please set this option correctly according to your schematic.
Set to -1 if the PSRAM is using any external power supply.
choice ESP_LDO_VOLTAGE_PSRAM_DOMAIN
prompt "PSRAM power domain voltage"
depends on ESP_LDO_CHAN_PSRAM_DOMAIN != -1
default ESP_LDO_VOLTAGE_PSRAM_1900_MV
help
Select the voltage used by the PSRAM power domain.
config ESP_LDO_VOLTAGE_PSRAM_1900_MV
bool "1.9V"
endchoice
config ESP_LDO_VOLTAGE_PSRAM_DOMAIN
int
default 1900 if ESP_LDO_VOLTAGE_PSRAM_1900_MV
endmenu

View File

@ -9,7 +9,7 @@ set(srcs "test_app_main.c"
"test_key_mgr.c"
)
if(CONFIG_SOC_MULTI_USAGE_LDO_SUPPORTED)
if(CONFIG_SOC_GP_LDO_SUPPORTED)
list(APPEND srcs "test_ldo.c")
endif()

View File

@ -1,74 +1,89 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "unity.h"
#include "esp_private/esp_ldo.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "esp_ldo_regulator.h"
TEST_CASE("LDO unit early / normal allocation", "[LDO]")
TEST_CASE("LDO channel acquire and release (no adjustable)", "[LDO]")
{
esp_ldo_unit_init_cfg_t init_early_unit_cfg = {
.unit_id = LDO_UNIT_3,
.cfg = {
.voltage_mv = 1800,
},
.flags.enable_unit = true,
esp_ldo_channel_handle_t success_ldo_chans[3] = {};
esp_ldo_channel_handle_t fail_ldo_chan = NULL;
esp_ldo_channel_config_t ldo_chan_config = {
.chan_id = 4,
.voltage_mv = 1800,
};
esp_ldo_unit_handle_t early_unit = esp_ldo_init_unit_early(&init_early_unit_cfg);
TEST_ASSERT(esp_ldo_enable_unit(early_unit) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(esp_ldo_disable_unit(early_unit));
esp_ldo_unit_handle_t unit = NULL;
esp_ldo_unit_init_cfg_t init_unit_cfg = {
.unit_id = LDO_UNIT_4,
.cfg = {
.voltage_mv = 2500,
},
for (int i = 0; i < 3; i++) {
TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_chan_config, &success_ldo_chans[i]));
}
TEST_ASSERT_EQUAL(success_ldo_chans[0], success_ldo_chans[1]);
TEST_ASSERT_EQUAL(success_ldo_chans[0], success_ldo_chans[2]);
// can't acquire with a different voltage
ldo_chan_config.voltage_mv = 3300;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_ldo_acquire_channel(&ldo_chan_config, &fail_ldo_chan));
// the channel has been acquired as "not adjustable" before, so we can't acquire it as "adjustable" again
ldo_chan_config = (esp_ldo_channel_config_t) {
.chan_id = 4,
.voltage_mv = 1800,
.flags.adjustable = true,
};
TEST_ESP_OK(esp_ldo_init_unit(&init_unit_cfg, &unit));
TEST_ESP_OK(esp_ldo_enable_unit(unit));
TEST_ESP_OK(esp_ldo_disable_unit(unit));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_ldo_acquire_channel(&ldo_chan_config, &fail_ldo_chan));
init_unit_cfg.flags.shared_ldo = true;
esp_ldo_unit_handle_t shared_unit = NULL;
TEST_ESP_OK(esp_ldo_init_unit(&init_unit_cfg, &shared_unit));
// can't change the voltage for a non-adjustable channel
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, esp_ldo_channel_adjust_voltage(success_ldo_chans[0], 3300));
TEST_ESP_OK(esp_ldo_deinit_unit(early_unit));
TEST_ESP_OK(esp_ldo_deinit_unit(shared_unit));
TEST_ESP_OK(esp_ldo_deinit_unit(unit));
for (int i = 0; i < 3; i++) {
TEST_ESP_OK(esp_ldo_release_channel(success_ldo_chans[i]));
}
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_ldo_release_channel(success_ldo_chans[0]));
}
TEST_CASE("LDO unit output", "[LDO][mannual][ignore]")
TEST_CASE("LDO channel acquire and release (adjustable)", "[LDO]")
{
esp_ldo_unit_init_cfg_t early_unit_cfg = {
.unit_id = LDO_UNIT_2,
.cfg = {
.voltage_mv = 1800,
},
.flags.shared_ldo = true,
.flags.enable_unit = true,
esp_ldo_channel_handle_t success_ldo_chan = NULL;
esp_ldo_channel_handle_t fail_ldo_chan = NULL;
esp_ldo_channel_config_t ldo_chan_config = {
.chan_id = 4,
.voltage_mv = 1800,
.flags.adjustable = true,
};
esp_ldo_unit_handle_t early_unit2 = esp_ldo_init_unit_early(&early_unit_cfg);
assert(early_unit2);
TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_chan_config, &success_ldo_chan));
early_unit_cfg.unit_id = LDO_UNIT_3;
early_unit_cfg.cfg.voltage_mv = 3300;
esp_ldo_unit_handle_t early_unit3 = esp_ldo_init_unit_early(&early_unit_cfg);
assert(early_unit3);
// can't acquire multiple handles for the same adjustable channel
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_ldo_acquire_channel(&ldo_chan_config, &fail_ldo_chan));
// even we acquire another copy as non-adjustable, it's still not allowed
ldo_chan_config.flags.adjustable = false;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_ldo_acquire_channel(&ldo_chan_config, &fail_ldo_chan));
early_unit_cfg.unit_id = LDO_UNIT_4;
early_unit_cfg.cfg.voltage_mv = 1100;
esp_ldo_unit_handle_t early_unit4 = esp_ldo_init_unit_early(&early_unit_cfg);
assert(early_unit4);
// can change voltage for an adjustable channel
TEST_ESP_OK(esp_ldo_channel_adjust_voltage(success_ldo_chan, 3300));
TEST_ESP_OK(esp_ldo_release_channel(success_ldo_chan));
}
esp_ldo_usage_dump(stdout);
TEST_CASE("LDO channel state dump", "[LDO][manual][ignore]")
{
esp_ldo_channel_handle_t success_ldo_chans[3] = {};
esp_ldo_channel_config_t ldo_chan_config = {
.chan_id = 2,
.voltage_mv = 1800,
};
TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_chan_config, &success_ldo_chans[0]));
ldo_chan_config.chan_id = 3;
ldo_chan_config.voltage_mv = 2500;
TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_chan_config, &success_ldo_chans[1]));
ldo_chan_config.chan_id = 4;
ldo_chan_config.voltage_mv = 1100;
TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_chan_config, &success_ldo_chans[2]));
esp_ldo_dump(stdout);
while (1) {
vTaskDelay(1);

View File

@ -6,25 +6,21 @@
#include "unity.h"
#include "test_mipi_dsi_board.h"
#include "esp_private/esp_ldo.h"
#include "esp_ldo_regulator.h"
static esp_ldo_unit_handle_t phy_pwr_unit = NULL;
static esp_ldo_channel_handle_t ldo_phy_chan = NULL;
void test_bsp_enable_dsi_phy_power(void)
{
// Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state
esp_ldo_unit_init_cfg_t ldo_cfg = {
.unit_id = TEST_MIPI_DSI_PHY_PWR_LDO_UNIT,
.cfg = {
.voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
},
esp_ldo_channel_config_t ldo_cfg = {
.chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN,
.voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
};
TEST_ESP_OK(esp_ldo_init_unit(&ldo_cfg, &phy_pwr_unit));
TEST_ESP_OK(esp_ldo_enable_unit(phy_pwr_unit));
TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_cfg, &ldo_phy_chan));
}
void test_bsp_disable_dsi_phy_power(void)
{
TEST_ESP_OK(esp_ldo_disable_unit(phy_pwr_unit));
TEST_ESP_OK(esp_ldo_deinit_unit(phy_pwr_unit));
TEST_ESP_OK(esp_ldo_release_channel(ldo_phy_chan));
}

View File

@ -20,7 +20,7 @@ extern "C" {
#define MIPI_DSI_LCD_VBP 16
#define MIPI_DSI_LCD_VFP 16
#define TEST_MIPI_DSI_PHY_PWR_LDO_UNIT 3
#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN 3
#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV 2500
void test_bsp_enable_dsi_phy_power(void);

View File

@ -9,7 +9,6 @@
#include "esp_err.h"
#include "esp_log.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/esp_ldo_psram.h"
#include "esp_private/mspi_timing_tuning.h"
#include "../esp_psram_impl.h"
#include "hal/psram_ctrlr_ll.h"
@ -366,7 +365,7 @@ static void s_configure_psram_ecc(void)
esp_err_t esp_psram_impl_enable(void)
{
#if SOC_CLK_MPLL_SUPPORTED
periph_rtc_mpll_early_acquire();
periph_rtc_mpll_acquire();
periph_rtc_mpll_freq_set(AP_HEX_PSRAM_MPLL_DEFAULT_FREQ_MHZ * 1000000, NULL);
#endif

View File

@ -76,7 +76,6 @@
#endif
#include "esp_private/rtc_clk.h"
#include "esp_private/esp_ldo_psram.h"
#if SOC_INT_CLIC_SUPPORTED
#include "hal/interrupt_clic_ll.h"

View File

@ -18,7 +18,6 @@
#include "hal/assert.h"
#include "soc/pmu_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -28,6 +27,11 @@ extern "C" {
*/
#define LDO_LL_UNIT_NUM 4
#define LDO_LL_ADJUSTABLE_CHAN_MASK 0x0F // all the 4 channels can be adjustable
#define LDO_LL_MAX_VOLTAGE_MV 3300
#define LDO_LL_MIN_VOLTAGE_MV 500
/**
* LDO LL macros, these macros are in the unit of mV
*/

View File

@ -12,7 +12,7 @@ set(srcs "sdmmc_cmd.c"
"sdmmc_sd.c"
"sd_pwr_ctrl/sd_pwr_ctrl.c")
if(CONFIG_SOC_MULTI_USAGE_LDO_SUPPORTED)
if(CONFIG_SOC_GP_LDO_SUPPORTED)
list(APPEND srcs "sd_pwr_ctrl/sd_pwr_ctrl_by_on_chip_ldo.c")
endif()

View File

@ -19,7 +19,7 @@ extern "C" {
* @brief LDO configurations
*/
typedef struct {
int ldo_unit_id; ///< On-chip LDO ID
int ldo_chan_id; ///< On-chip LDO channel ID, e.g. set to `4` is the `LDO_VO4` is connected to power the SDMMC IO
} sd_pwr_ctrl_ldo_config_t;
/**

View File

@ -12,14 +12,14 @@
#include "esp_log.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "esp_private/esp_ldo.h"
#include "esp_ldo_regulator.h"
#include "soc/soc_caps.h"
#include "sd_pwr_ctrl.h"
#include "sd_pwr_ctrl_by_on_chip_ldo.h"
#include "sd_pwr_ctrl_interface.h"
typedef struct {
esp_ldo_unit_handle_t ldo_unit;
esp_ldo_channel_handle_t ldo_chan;
int voltage_mv;
} sd_pwr_ctrl_ldo_ctx_t;
@ -37,19 +37,16 @@ esp_err_t sd_pwr_ctrl_new_on_chip_ldo(const sd_pwr_ctrl_ldo_config_t *configs, s
sd_pwr_ctrl_ldo_ctx_t *ctx = (sd_pwr_ctrl_ldo_ctx_t *)heap_caps_calloc(1, sizeof(sd_pwr_ctrl_ldo_ctx_t), MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT);
ESP_GOTO_ON_FALSE(ctx, ESP_ERR_NO_MEM, err, TAG, "no mem for on-chip ldo control driver context");
esp_ldo_unit_init_cfg_t unit_cfg = {
.unit_id = configs->ldo_unit_id,
.cfg = {
.voltage_mv = 0, //will be adjusted dynamically by sdmmc driver later
},
esp_ldo_channel_config_t chan_cfg = {
.chan_id = configs->ldo_chan_id,
.flags.adjustable = true, // the SDMMC power control driver will adjust the voltage later according to different speed mode
};
esp_ldo_unit_handle_t ldo_unit = NULL;
ESP_GOTO_ON_ERROR(esp_ldo_init_unit(&unit_cfg, &ldo_unit), err, TAG, "failed to create an on-chip LDO unit handle");
ESP_GOTO_ON_ERROR(esp_ldo_enable_unit(ldo_unit), err, TAG, "failed to enable the on-chip LDO unit");
esp_ldo_channel_handle_t ldo_chan = NULL;
ESP_GOTO_ON_ERROR(esp_ldo_acquire_channel(&chan_cfg, &ldo_chan), err, TAG, "failed to enable the on-chip LDO unit");
driver->set_io_voltage = s_ldo_set_voltage;
driver->ctx = ctx;
ctx->ldo_unit = ldo_unit;
ctx->ldo_chan = ldo_chan;
ctx->voltage_mv = 0;
*ret_drv = driver;
@ -66,8 +63,7 @@ esp_err_t sd_pwr_ctrl_del_on_chip_ldo(sd_pwr_ctrl_handle_t handle)
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid arg: null pointer");
sd_pwr_ctrl_ldo_ctx_t *ctx = handle->ctx;
ESP_RETURN_ON_ERROR(esp_ldo_disable_unit(ctx->ldo_unit), TAG, "failed to disable the on-chip LDO unit");
ESP_RETURN_ON_ERROR(esp_ldo_deinit_unit(ctx->ldo_unit), TAG, "failed to deinit the LDO unit handle");
ESP_RETURN_ON_ERROR(esp_ldo_release_channel(ctx->ldo_chan), TAG, "failed to release the on-chip LDO channel");
free(handle->ctx);
handle->ctx = NULL;
free(handle);
@ -80,7 +76,7 @@ static esp_err_t s_ldo_set_voltage(void *arg, int voltage_mv)
{
//API checks done by caller
sd_pwr_ctrl_ldo_ctx_t *ctx = arg;
ESP_RETURN_ON_ERROR(esp_ldo_set_voltage(ctx->ldo_unit, voltage_mv), TAG, "failed to set LDO unit output voltage");
ESP_RETURN_ON_ERROR(esp_ldo_channel_adjust_voltage(ctx->ldo_chan, voltage_mv), TAG, "failed to set LDO unit output voltage");
ctx->voltage_mv = voltage_mv;
return ESP_OK;
}

View File

@ -227,7 +227,7 @@ config SOC_SPI_FLASH_SUPPORTED
bool
default y
config SOC_MULTI_USAGE_LDO_SUPPORTED
config SOC_GP_LDO_SUPPORTED
bool
default y

View File

@ -85,7 +85,7 @@
#define SOC_SPI_FLASH_SUPPORTED 1
// #define SOC_TOUCH_SENSOR_SUPPORTED 1 //TODO: IDF-7477
// #define SOC_RNG_SUPPORTED 1 //TODO: IDF-6522
#define SOC_MULTI_USAGE_LDO_SUPPORTED 1
#define SOC_GP_LDO_SUPPORTED 1 // General purpose LDO
// #define SOC_PPA_SUPPORTED 1 //TODO: IDF-6878
#define SOC_LIGHT_SLEEP_SUPPORTED 1
// #define SOC_DEEP_SLEEP_SUPPORTED 1 //TODO: IDF-7529

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -15,6 +15,7 @@
#include "esp_heap_caps.h"
#include "hal/spi_types.h"
#include "esp_private/spi_share_hw_ctrl.h"
#include "esp_ldo_regulator.h"
#include "hal/spi_flash_hal.h"
#include "hal/gpio_hal.h"
#include "esp_flash_internal.h"
@ -420,6 +421,19 @@ esp_err_t esp_flash_init_default_chip(void)
esp_err_t esp_flash_app_init(void)
{
esp_err_t err = ESP_OK;
// Acquire the LDO channel used by the SPI NOR flash
// in case the LDO voltage is changed by other users
#if defined(CONFIG_ESP_LDO_CHAN_SPI_NOR_FLASH_DOMAIN) && CONFIG_ESP_LDO_CHAN_SPI_NOR_FLASH_DOMAIN != -1
static esp_ldo_channel_handle_t s_ldo_chan = NULL;
esp_ldo_channel_config_t ldo_config = {
.chan_id = CONFIG_ESP_LDO_CHAN_SPI_NOR_FLASH_DOMAIN,
.voltage_mv = CONFIG_ESP_LDO_VOLTAGE_SPI_NOR_FLASH_DOMAIN,
};
err = esp_ldo_acquire_channel(&ldo_config, &s_ldo_chan);
if (err != ESP_OK) return err;
#endif
spi_flash_init_lock();
spi_flash_guard_set(&g_flash_guard_default_ops);
#if CONFIG_SPI_FLASH_ENABLE_COUNTERS

View File

@ -102,6 +102,8 @@ DAC_DOCS = ['api-reference/peripherals/dac.rst']
ETM_DOCS = ['api-reference/peripherals/etm.rst']
LDO_DOCS = ['api-reference/peripherals/ldo_regulator.rst']
TEMP_SENSOR_DOCS = ['api-reference/peripherals/temp_sensor.rst']
TOUCH_SENSOR_DOCS = ['api-reference/peripherals/touch_pad.rst']
@ -251,6 +253,7 @@ conditional_include_dict = {'SOC_BT_SUPPORTED':BT_DOCS,
'SOC_SPI_SUPPORT_SLAVE_HD_VER2':SPI_SLAVE_HD_DOCS,
'SOC_WIFI_NAN_SUPPORT':NAN_DOCS,
'SOC_JPEG_CODEC_SUPPORTED':JPEG_DOCS,
'SOC_GP_LDO_SUPPORTED':LDO_DOCS,
'esp32':ESP32_DOCS,
'esp32s2':ESP32S2_DOCS,
'esp32s3':ESP32S3_DOCS,

View File

@ -173,6 +173,7 @@ INPUT = \
$(PROJECT_PATH)/components/esp_hw_support/include/esp_mac.h \
$(PROJECT_PATH)/components/esp_hw_support/include/esp_random.h \
$(PROJECT_PATH)/components/esp_hw_support/include/esp_sleep.h \
$(PROJECT_PATH)/components/esp_hw_support/ldo/include/esp_ldo_regulator.h \
$(PROJECT_PATH)/components/esp_lcd/include/esp_lcd_panel_io.h \
$(PROJECT_PATH)/components/esp_lcd/include/esp_lcd_panel_ops.h \
$(PROJECT_PATH)/components/esp_lcd/include/esp_lcd_panel_vendor.h \

View File

@ -32,7 +32,7 @@ Hardware
Some PSRAM chips are 1.8 V devices and some are 3.3 V. Consult the datasheet for your PSRAM chip and {IDF_TARGET_NAME} device to find out the working voltages.
By default, the PSRAM is powered up by the on-chip LDO2. You can use :ref:`CONFIG_ESP_VDD_PSRAM_LDO_ID` to switch the LDO ID accordingly. Set this value to -1 to use an external power supply, which means the on-chip LDO will not be used. By default, the PSRAM connected to LDO is set to the correct voltage based on the Espressif module used. You can still use :ref:`CONFIG_ESP_VDD_PSRAM_LDO_VOLTAGE_MV` to select the LDO output voltage if you are not using an Espressif module. When using an external power supply, this option does not exist.
By default, the PSRAM is powered up by the on-chip LDO2. You can use :ref:`CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN` to switch the LDO channel accordingly. Set this value to -1 to use an external power supply, which means the on-chip LDO will not be used. By default, the PSRAM connected to LDO is set to the correct voltage based on the Espressif module used. You can still use :ref:`CONFIG_ESP_LDO_VOLTAGE_PSRAM_DOMAIN` to select the LDO output voltage if you are not using an Espressif module. When using an external power supply, this option does not exist.
.. note::

View File

@ -156,7 +156,7 @@ Application Example
.. list::
* Emulate UART/I2C/SPI peripherals in assembly with dedicate CPU instructions designed for manipulating the GPIOs: :example:`peripherals/dedicated_gpio`.
* Software emulation (bit banging) of the UART/I2C/SPI protocols in assembly using the dedicated GPIOs and their associated CPU instructions: :example:`peripherals/dedicated_gpio`.
:SOC_DEDIC_GPIO_HAS_INTERRUPT: * Matrix keyboard example based on dedicated GPIO: :example:`peripherals/gpio/matrix_keyboard`.

View File

@ -22,6 +22,7 @@ Peripherals API
i2c
:SOC_I2S_SUPPORTED: i2s
lcd/index
:SOC_GP_LDO_SUPPORTED: ldo_regulator
ledc
:SOC_MCPWM_SUPPORTED: mcpwm
:SOC_PARLIO_SUPPORTED: parlio

View File

@ -0,0 +1,59 @@
Low Dropout Voltage Regulator (LDO)
===================================
:link_to_translation:`zh_CN:[中文]`
{IDF_TARGET_LDO_CHANNELS:default="unknown", esp32p4="4"}
Introduction
------------
The {IDF_TARGET_NAME} chip internally integrates {IDF_TARGET_LDO_CHANNELS} channels of low-dropout voltage regulators (LDOs). Each channel's voltage is programmable. In our hardware reference designs, some of these LDO outputs are typically used to power the internal Flash and PSRAM, while the remaining LDOs can be used to supply external devices.
.. note::
It's essential to read the manual first and ensure that the required current does not exceed the chip's specifications.
Functional Overview
-------------------
The description of the LDO driver is divided into the following sections:
- :ref:`ldo-channel-acquisition` - Introduces the types of LDO channels and how to apply for LDO channel resources.
- :ref:`ldo-adjust-voltage` - Describes how to adjust the voltage of the LDO channel.
.. _ldo-channel-acquisition:
LDO Channel Acquisition
^^^^^^^^^^^^^^^^^^^^^^^
LDO channels can be classified into two types: fixed voltage and adjustable voltage. For a fixed voltage channel, it allows multiple users to simultaneously use it (in software, we allow a variable to have multiple immutable references ). However, for an adjustable voltage channel, only one user is allowed to use it at a time (in software, we don't allow a variable to have multiple mutable references or coexistence of mutable and immutable references).
In the driver, the LDO channel is represented by the :cpp:type:`esp_ldo_channel_handle_t`. You can use the :cpp:func:`esp_ldo_acquire_channel` function to request LDO channel resources. Upon successful acquisition, a handle for the LDO channel will be returned, which can be used for subsequent voltage adjustment operations. When applying for a channel, the :cpp:type:`esp_ldo_channel_config_t` structure is used to specify the basic information of the LDO channel, including the channel ID, the desired output voltage, and whether the voltage can be dynamically adjusted.
- :cpp:member:`esp_ldo_channel_config_t::chan_id` - LDO channels are uniquely identified by a label, which is used to distinguish different LDO channels. Please note that this information needs to be determined based on the circuit schematic and chip datasheet. For example, an LDO channel labeled as ``LDO_VO3`` corresponds to an ID of ``3``.
- :cpp:member:`esp_ldo_channel_config_t::voltage_mv` - The desired output voltage of the LDO channel, in millivolts.
- :cpp:member:`esp_ldo_channel_config_t::ldo_extra_flags::adjustable` - Whether the LDO channel's output voltage can be dynamically adjusted. Only when it is set to `true`, can the :cpp:func:`esp_ldo_channel_adjust_voltage` function be used to dynamically adjust the output voltage.
Since multiple users are allowed to use a fixed voltage LDO channel simultaneously, the driver internally maintains a reference counter. The LDO channel will be automatically closed when the last user releases the LDO channel resources. The function to release LDO channel resources is :cpp:func:`esp_ldo_release_channel`. Additionally, it is important to note that the acquisition and release of LDO channels should appear in pairs during usage.
.. _ldo-adjust-voltage:
LDO Voltage Adjustment
^^^^^^^^^^^^^^^^^^^^^^
:cpp:func:`esp_ldo_channel_adjust_voltage` function is used to adjust the output voltage of an LDO channel at runtime. However, please note that this function can only be used for LDO channels with adjustable voltage. Attempting to use this function on a fixed voltage LDO channel will result in an error.
Also, it is important to keep in mind that due to hardware limitations, the LDO channel voltage may have a deviation of approximately 50-100mV. Therefore, it is not advisable to rely on the LDO channel's output voltage for precise analog control.
Application Examples
--------------------
.. list::
:SOC_MIPI_DSI_SUPPORTED: * Use the internal LDO channel to power up the MIPI DPHY: :example:`peripherals/lcd/mipi_dsi`
API Reference
-------------
.. include-build-file:: inc/esp_ldo_regulator.inc

View File

@ -32,7 +32,7 @@
请查询相应 PSRAM 芯片以及 {IDF_TARGET_NAME} 的技术规格书获取准确的工作电压。
PSRAM 默认由片上 LDO2 供电。可设置 :ref:`CONFIG_ESP_VDD_PSRAM_LDO_ID` 来切换相应的 LDO ID,将该值设为 -1 表示使用外部电源,即不使用片上 LDO。默认情况下连接到 LDO 的 PSRAM 会基于所使用的乐鑫模组设置正确电压。如果未使用乐鑫模组,仍可设置 :ref:`CONFIG_ESP_VDD_PSRAM_LDO_VOLTAGE_MV` 来选择 LDO 输出电压。使用外部电源时,该选项不存在。
PSRAM 默认由片上 LDO2 供电。可设置 :ref:`CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN` 来切换相应的 LDO 输出通道,将该值设为 -1 表示使用外部电源,即不使用片上 LDO。默认情况下连接到 LDO 的 PSRAM 会基于所使用的乐鑫模组设置正确电压。如果未使用乐鑫模组,仍可设置 :ref:`CONFIG_ESP_LDO_VOLTAGE_PSRAM_DOMAIN` 来选择 LDO 输出电压。使用外部电源时,该选项不存在。
.. note::

View File

@ -156,7 +156,7 @@ GPIO 捆绑包操作
.. list::
* 通过汇编代码使用专用的 CPU 指令来操作 GPIO 以模拟 UART/I2C/SPI 外设 :example:`peripherals/dedicated_gpio`.
* 通过汇编代码使用专用的 CPU 指令来操作 GPIO 以模拟 UART/I2C/SPI 协议Bit Banging :example:`peripherals/dedicated_gpio`.
:SOC_DEDIC_GPIO_HAS_INTERRUPT: * 基于专用 GPIO 驱动的矩阵键盘::example:`peripherals/gpio/matrix_keyboard`.

View File

@ -22,6 +22,7 @@
i2c
:SOC_I2S_SUPPORTED: i2s
lcd/index
:SOC_GP_LDO_SUPPORTED: ldo_regulator
ledc
:SOC_MCPWM_SUPPORTED: mcpwm
:SOC_PARLIO_SUPPORTED: parlio

View File

@ -0,0 +1,59 @@
低压差线性稳压器 (LDO)
======================
:link_to_translation:`en:[English]`
{IDF_TARGET_LDO_CHANNELS:default="unknown", esp32p4="4"}
简介
----
{IDF_TARGET_NAME} 芯片内部集成了 {IDF_TARGET_LDO_CHANNELS} 路低压差线性稳压器 (LDO),每路的电压都是可编程调节的。在硬件的参考设计中,我们通常会将其中一些的 LDO 输出作为内部 Flash 和 PSRAM 的电源,剩余的一些 LDO 可以用于给外部设备供电。
.. note::
使用前请阅读手册,确保你需要的电流不会超过芯片的规格。
功能概述
--------
下文将分节介绍 LDO 驱动的功能:
- :ref:`ldo-channel-acquisition` - 介绍LDO通道的种类以及如何申请 LDO 通道资源
- :ref:`ldo-adjust-voltage` - 介绍如何调节 LDO 通道的输出电压
.. _ldo-channel-acquisition:
LDO 通道申请
^^^^^^^^^^^^
LDO 通道可以分为两种,一种是电压固定的,另一种是电压可调的。对于同一个输出通道,如果电压固定,那么可以允许有多个用户同时使用(软件允许一个变量拥有多个不可变引用)。如果电压可调,那么只能允许一个用户使用(软件上不允许一个变量有多个可变引用或者可变和不可变引用同时存在)。
LDO 通道在驱动软件中由 :cpp:type:`esp_ldo_channel_handle_t` 句柄表示。申请 LDO 通道资源的函数是 :cpp:func:`esp_ldo_acquire_channel`。申请成功后,会返回一个 LDO 通道的句柄,这个句柄可以用于后续的电压调节操作。在申请通道的时候,我们需要通过 :cpp:type:`esp_ldo_channel_config_t` 结构体来指定 LDO 通道的基本信息,包括通道 ID期望的输出电压以及电压是否可以动态调节。
- :cpp:member:`esp_ldo_channel_config_t::chan_id` - LDO 通道的唯一标记,用于区分不同的 LDO 通道。注意,这需要你根据电路原理图和芯片数据手册来确定。比如,一个标记着 "LDO_VO3" 的 LDO 通道,对应的 ID 是 3。
- :cpp:member:`esp_ldo_channel_config_t::voltage_mv` - 期望的输出电压,单位是毫伏。
- :cpp:member:`esp_ldo_channel_config_t::ldo_extra_flags::adjustable` - 是否允许调节输出电压。只有设置为 `true`,才允许使用 :cpp:func:`esp_ldo_channel_adjust_voltage` 函数来动态地调节输出电压。
由于允许多个用户同时使用固定电压的 LDO 通道,所以驱动内部会维持一个引用计数器。当最后一个用户释放 LDO 通道资源时LDO 通道会被自动关闭。释放 LDO 通道资源的函数是 :cpp:func:`esp_ldo_release_channel`。另外还需要注意,申请和释放 LDO 通道在使用的时候需要成对出现。
.. _ldo-adjust-voltage:
LDO 通道电压调节
^^^^^^^^^^^^^^^^
:cpp:func:`esp_ldo_channel_adjust_voltage` 函数用来在运行时调整 LDO 通道的输出电压。但是,这个函数只能用于可调节电压的 LDO 通道,否则会返回错误。
注意由于硬件限制LDO 通道电压的精度可能会 50~100mV 左右的偏差,请勿依赖于 LDO 通道的输出电压来进行精确的模拟量控制。
应用示例
--------
.. list::
:SOC_MIPI_DSI_SUPPORTED: * Use the internal LDO channel to power up the MIPI DPHY: :example:`peripherals/lcd/mipi_dsi`
API 参考
--------
.. include-build-file:: inc/esp_ldo_regulator.inc

View File

@ -1,3 +1,3 @@
dependencies:
lvgl/lvgl: "~9.0.0"
lvgl/lvgl: "^9.0.0"
esp_lcd_ili9881c: "*"

View File

@ -9,11 +9,11 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_private/esp_ldo.h"
#include "esp_timer.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_mipi_dsi.h"
#include "esp_lcd_ili9881c.h"
#include "esp_ldo_regulator.h"
#include "driver/gpio.h"
#include "esp_err.h"
#include "esp_log.h"
@ -43,8 +43,8 @@ static const char *TAG = "example";
//////////////////// Please update the following configuration according to your Board Design //////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// The "VDD_MIPI_DPHY" should be supplied with 2.5V, it can source from the integrated LDO unit or from external LDO chip
#define EXAMPLE_MIPI_DSI_PHY_PWR_LDO_UNIT 3 // LDO_VO3 is connected to VDD_MIPI_DPHY
// The "VDD_MIPI_DPHY" should be supplied with 2.5V, it can source from the internal LDO regulator or from external LDO chip
#define EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN 3 // LDO_VO3 is connected to VDD_MIPI_DPHY
#define EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV 2500
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
@ -126,17 +126,13 @@ static bool example_notify_lvgl_flush_ready(esp_lcd_panel_handle_t panel, esp_lc
static void example_bsp_enable_dsi_phy_power(void)
{
// Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state
esp_ldo_unit_handle_t phy_pwr_unit = NULL;
#if EXAMPLE_MIPI_DSI_PHY_PWR_LDO_UNIT > 0
esp_ldo_unit_init_cfg_t ldo_cfg = {
.unit_id = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_UNIT,
.cfg = {
.voltage_mv = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
},
esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
#ifdef EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN
esp_ldo_channel_config_t ldo_mipi_phy_config = {
.chan_id = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN,
.voltage_mv = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
};
ESP_ERROR_CHECK(esp_ldo_init_unit(&ldo_cfg, &phy_pwr_unit));
ESP_ERROR_CHECK(esp_ldo_enable_unit(phy_pwr_unit));
ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
#endif
}

View File

@ -132,7 +132,7 @@ void app_main(void)
*/
#if CONFIG_EXAMPLE_SDMMC_IO_POWER_INTERNAL_LDO
sd_pwr_ctrl_ldo_config_t ldo_config = {
.ldo_unit_id = 4,
.ldo_chan_id = 4, // `LDO_VO4` is used as the SDMMC IO power
};
sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL;