mirror of
https://github.com/espressif/esp-idf
synced 2025-03-09 09:09:10 -04:00
feat(ldo): support output rail voltage (3.3V)
This commit is contained in:
parent
cfe7021801
commit
85f8f25b30
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -11,7 +11,6 @@
|
||||
#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"
|
||||
@ -24,7 +23,6 @@ typedef struct ldo_regulator_channel_t {
|
||||
int ref_cnt;
|
||||
struct {
|
||||
uint32_t adjustable : 1;
|
||||
uint32_t bypass : 1;
|
||||
} flags;
|
||||
} ldo_regulator_channel_t;
|
||||
|
||||
@ -32,6 +30,8 @@ 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
|
||||
|
||||
// Allocate LDO channel memory statically
|
||||
// LDO is a fundamental module and should be usable even without a heap allocator
|
||||
static ldo_regulator_channel_t s_ldo_channels[LDO_LL_NUM_UNITS] = {
|
||||
[0 ... LDO_LL_NUM_UNITS - 1] = {
|
||||
.chan_id = -1,
|
||||
@ -45,8 +45,11 @@ esp_err_t esp_ldo_acquire_channel(const esp_ldo_channel_config_t *config, esp_ld
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(ldo_ll_is_valid_ldo_channel(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);
|
||||
// check if the target voltage is within the recommended range
|
||||
if (!((config->voltage_mv == LDO_LL_RAIL_VOLTAGE_MV) ||
|
||||
(config->voltage_mv >= LDO_LL_RECOMMEND_MIN_VOLTAGE_MV && config->voltage_mv <= LDO_LL_RECOMMEND_MAX_VOLTAGE_MV))) {
|
||||
ESP_LOGW(TAG, "The voltage value %d is out of the recommended range [%d, %d]", config->voltage_mv, LDO_LL_RECOMMEND_MIN_VOLTAGE_MV, LDO_LL_RECOMMEND_MAX_VOLTAGE_MV);
|
||||
}
|
||||
int unit_id = LDO_ID2UNIT(config->chan_id);
|
||||
ldo_regulator_channel_t *channel = &s_ldo_channels[unit_id];
|
||||
|
||||
@ -82,9 +85,10 @@ esp_err_t esp_ldo_acquire_channel(const esp_ldo_channel_config_t *config, esp_ld
|
||||
// if the channel is not in use, we need to set the initial voltage and enable it
|
||||
uint8_t dref = 0;
|
||||
uint8_t mul = 0;
|
||||
bool use_rail_voltage = false;
|
||||
// calculate the dref and mul
|
||||
ldo_ll_voltage_to_dref_mul(unit_id, config->voltage_mv, &dref, &mul);
|
||||
ldo_ll_adjust_voltage(unit_id, dref, mul, config->flags.bypass);
|
||||
ldo_ll_voltage_to_dref_mul(unit_id, config->voltage_mv, &dref, &mul, &use_rail_voltage);
|
||||
ldo_ll_adjust_voltage(unit_id, dref, mul, use_rail_voltage);
|
||||
// set the ldo unit owner ship
|
||||
ldo_ll_set_owner(unit_id, config->flags.owned_by_hw ? LDO_LL_UNIT_OWNER_HW : LDO_LL_UNIT_OWNER_SW);
|
||||
// suppress voltage ripple
|
||||
@ -95,7 +99,6 @@ esp_err_t esp_ldo_acquire_channel(const esp_ldo_channel_config_t *config, esp_ld
|
||||
channel->ref_cnt++;
|
||||
channel->voltage_mv = config->voltage_mv;
|
||||
channel->flags.adjustable = config->flags.adjustable;
|
||||
channel->flags.bypass = config->flags.bypass;
|
||||
channel->chan_id = config->chan_id;
|
||||
}
|
||||
portEXIT_CRITICAL(&s_spinlock);
|
||||
@ -143,9 +146,11 @@ esp_err_t esp_ldo_channel_adjust_voltage(esp_ldo_channel_handle_t chan, int volt
|
||||
{
|
||||
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);
|
||||
// check if the target voltage is within the recommended range
|
||||
if (!((voltage_mv == LDO_LL_RAIL_VOLTAGE_MV) ||
|
||||
(voltage_mv >= LDO_LL_RECOMMEND_MIN_VOLTAGE_MV && voltage_mv <= LDO_LL_RECOMMEND_MAX_VOLTAGE_MV))) {
|
||||
ESP_LOGW(TAG, "The voltage value %d is out of the recommended range [%d, %d]", voltage_mv, LDO_LL_RECOMMEND_MIN_VOLTAGE_MV, LDO_LL_RECOMMEND_MAX_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)
|
||||
@ -155,9 +160,10 @@ esp_err_t esp_ldo_channel_adjust_voltage(esp_ldo_channel_handle_t chan, int volt
|
||||
int unit_id = LDO_ID2UNIT(chan->chan_id);
|
||||
uint8_t dref = 0;
|
||||
uint8_t mul = 0;
|
||||
bool use_rail_voltage = false;
|
||||
// calculate the dref and mul
|
||||
ldo_ll_voltage_to_dref_mul(unit_id, voltage_mv, &dref, &mul);
|
||||
ldo_ll_adjust_voltage(unit_id, dref, mul, chan->flags.bypass);
|
||||
ldo_ll_voltage_to_dref_mul(unit_id, voltage_mv, &dref, &mul, &use_rail_voltage);
|
||||
ldo_ll_adjust_voltage(unit_id, dref, mul, use_rail_voltage);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ typedef struct {
|
||||
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` */
|
||||
uint32_t owned_by_hw: 1; /*!< If the LDO channel is owned by hardware, then software configurations can be overridden by hardware (e.g. eFuse) */
|
||||
uint32_t bypass: 1; /*!< Whether to bypass the regulator, i.e., the input voltage is sourced directly to the output */
|
||||
uint32_t bypass: 1 __attribute__((deprecated)); /*!< Whether to bypass the regulator, i.e., the input voltage is sourced directly to the output */
|
||||
} flags; /*!< Flags for the LDO channel */
|
||||
} esp_ldo_channel_config_t;
|
||||
|
||||
|
@ -63,6 +63,7 @@ TEST_CASE("LDO channel acquire and release (adjustable)", "[LDO]")
|
||||
|
||||
// can change voltage for an adjustable channel
|
||||
TEST_ESP_OK(esp_ldo_channel_adjust_voltage(success_ldo_chan, 2500));
|
||||
TEST_ESP_OK(esp_ldo_channel_adjust_voltage(success_ldo_chan, 3300));
|
||||
TEST_ESP_OK(esp_ldo_release_channel(success_ldo_chan));
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -23,8 +23,9 @@ extern "C" {
|
||||
#define LDO_LL_NUM_UNITS 4 // Number of LDO units
|
||||
#define LDO_LL_ADJUSTABLE_CHAN_MASK 0x0F // all the 4 channels are adjustable by setting "mul" and "dref" registers
|
||||
|
||||
#define LDO_LL_MAX_VOLTAGE_MV 3300
|
||||
#define LDO_LL_MIN_VOLTAGE_MV 500
|
||||
#define LDO_LL_RECOMMEND_MAX_VOLTAGE_MV 2700
|
||||
#define LDO_LL_RECOMMEND_MIN_VOLTAGE_MV 500
|
||||
#define LDO_LL_RAIL_VOLTAGE_MV 3300
|
||||
|
||||
/**
|
||||
* @brief In the analog design, the LDO output "channel" is index from 1, i.e., VO1, VO2, VO3, VO4.
|
||||
@ -62,9 +63,10 @@ static inline bool ldo_ll_is_valid_ldo_channel(int ldo_chan)
|
||||
* @param voltage_mv Voltage in mV
|
||||
* @param dref Returned dref value
|
||||
* @param mul Returned mul value
|
||||
* @param use_rail_voltage Returned value to indicate if the rail voltage should be used
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void ldo_ll_voltage_to_dref_mul(int ldo_unit, int voltage_mv, uint8_t *dref, uint8_t *mul)
|
||||
static inline void ldo_ll_voltage_to_dref_mul(int ldo_unit, int voltage_mv, uint8_t *dref, uint8_t *mul, bool *use_rail_voltage)
|
||||
{
|
||||
uint8_t efuse_k = 0;
|
||||
uint8_t efuse_vos = 0;
|
||||
@ -136,6 +138,8 @@ static inline void ldo_ll_voltage_to_dref_mul(int ldo_unit, int voltage_mv, uint
|
||||
|
||||
*dref = matched_dref;
|
||||
*mul = matched_mul;
|
||||
// if the expected voltage is 3.3V, use the rail voltage directly
|
||||
*use_rail_voltage = (voltage_mv == LDO_LL_RAIL_VOLTAGE_MV);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,10 +179,10 @@ static inline void ldo_ll_set_owner(int ldo_unit, ldo_ll_unit_owner_t owner)
|
||||
* @param ldo_unit LDO unit
|
||||
* @param dref A parameter which controls the internal reference voltage
|
||||
* @param mul Multiply factor
|
||||
* @param bypass True: bypass; False: not bypass.
|
||||
* @param use_rail_voltage Use rail voltage directly (i.e. bypass the LDO)
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void ldo_ll_adjust_voltage(int ldo_unit, uint8_t dref, uint8_t mul, bool bypass)
|
||||
static inline void ldo_ll_adjust_voltage(int ldo_unit, uint8_t dref, uint8_t mul, bool use_rail_voltage)
|
||||
{
|
||||
uint8_t index_array[LDO_LL_NUM_UNITS] = {0, 3, 1, 4};
|
||||
/**
|
||||
@ -186,7 +190,7 @@ static inline void ldo_ll_adjust_voltage(int ldo_unit, uint8_t dref, uint8_t mul
|
||||
* - 0: Vref * Mul
|
||||
* - 1: 3.3V
|
||||
*/
|
||||
PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo.tieh = bypass;
|
||||
PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo.tieh = use_rail_voltage;
|
||||
PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo_ana.dref = dref;
|
||||
PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo_ana.mul = mul;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user