Merge branch 'fix/lp_adc_not_working_on_lp_core_v5.5' into 'release/v5.5'

Fixes for LP ADC to work when used from the LP core (v5.5)

See merge request espressif/esp-idf!45763
This commit is contained in:
Marius Vikhammer
2026-02-10 11:49:42 +08:00
4 changed files with 94 additions and 25 deletions
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -10,6 +10,8 @@
#include "ulp_lp_core_lp_adc_shared.h"
#include "hal/adc_types.h"
#include "hal/adc_ll.h"
#include "hal/adc_hal_common.h"
#include "esp_private/adc_share_hw_ctrl.h"
#define VREF (1100) /* Internal Reference voltage in millivolts (1.1V) */
#define INV_ATTEN_0DB (1000) /* Inverse of 10^0 * 1000 */
@@ -17,18 +19,48 @@
#define INV_ATTEN_6DB (1996) /* Inverse of 10^(-6/20) * 1000 */
#define INV_ATTEN_12DB (3984) /* Inverse of 10^(-12/20) * 1000 */
adc_oneshot_unit_handle_t s_adc1_handle;
/* ADC unit handles - one for each supported ADC unit */
static adc_oneshot_unit_handle_t s_adc_unit_handles[SOC_ADC_PERIPH_NUM] = {NULL};
esp_err_t lp_core_lp_adc_init(adc_unit_t unit_id)
/************************************************** Static Helper Functions **************************************************/
static esp_err_t lp_adc_validate_unit(adc_unit_t unit_id)
{
/* Currently, LP ADC2 does not work during sleep (DIG-396)
* TODO: Remove this check once DIG-396 is fixed
*/
if (unit_id != ADC_UNIT_1) {
// TODO: LP ADC2 does not work during sleep (DIG-396)
// For now, we do not allow LP ADC2 usage.
return ESP_ERR_INVALID_ARG;
}
/* Verify unit ID is within valid range */
if (unit_id >= SOC_ADC_PERIPH_NUM) {
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
static esp_err_t lp_adc_validate_channel(adc_unit_t unit_id, adc_channel_t channel)
{
if (channel >= SOC_ADC_CHANNEL_NUM(unit_id)) {
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
/************************************************** Public API **************************************************/
esp_err_t lp_core_lp_adc_init(adc_unit_t unit_id)
{
esp_err_t ret = lp_adc_validate_unit(unit_id);
if (ret != ESP_OK) {
return ret;
}
#if IS_ULP_COCPU
// Not supported
// Not supported from LP core
return ESP_ERR_NOT_SUPPORTED;
#else
/* LP ADC is being initialized from the HP core.
@@ -41,33 +73,39 @@ esp_err_t lp_core_lp_adc_init(adc_unit_t unit_id)
.ulp_mode = ADC_ULP_MODE_LP_CORE, // LP Core will use the ADC
};
return (adc_oneshot_new_unit(&init_config, &s_adc1_handle));
return (adc_oneshot_new_unit(&init_config, &s_adc_unit_handles[unit_id]));
#endif /* IS_ULP_COCPU */
}
esp_err_t lp_core_lp_adc_deinit(adc_unit_t unit_id)
{
if (unit_id != ADC_UNIT_1) {
return ESP_ERR_INVALID_ARG;
esp_err_t ret = lp_adc_validate_unit(unit_id);
if (ret != ESP_OK) {
return ret;
}
#if IS_ULP_COCPU
// Not supported
// Not supported from LP core
return ESP_ERR_NOT_SUPPORTED;
#else
return (adc_oneshot_del_unit(s_adc1_handle));
return (adc_oneshot_del_unit(s_adc_unit_handles[unit_id]));
#endif /* IS_ULP_COCPU */
}
esp_err_t lp_core_lp_adc_config_channel(adc_unit_t unit_id, adc_channel_t channel, const lp_core_lp_adc_chan_cfg_t *chan_config)
{
if (unit_id != ADC_UNIT_1) {
// TODO: LP ADC2 does not work during sleep (DIG-396)
// For now, we do not allow LP ADC2 usage.
return ESP_ERR_INVALID_ARG;
esp_err_t ret = lp_adc_validate_unit(unit_id);
if (ret != ESP_OK) {
return ret;
}
ret = lp_adc_validate_channel(unit_id, channel);
if (ret != ESP_OK) {
return ret;
}
#if IS_ULP_COCPU
// Not supported
// Not supported from LP core
return ESP_ERR_NOT_SUPPORTED;
#else
adc_oneshot_chan_cfg_t config = {
@@ -75,16 +113,37 @@ esp_err_t lp_core_lp_adc_config_channel(adc_unit_t unit_id, adc_channel_t channe
.bitwidth = chan_config->bitwidth,
};
return (adc_oneshot_config_channel(s_adc1_handle, channel, &config));
ret = adc_oneshot_config_channel(s_adc_unit_handles[unit_id], channel, &config);
if (ret != ESP_OK) {
return ret;
}
/* Set the calibration parameters for the ADC unit and channel */
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
adc_hal_calibration_init(unit_id);
adc_set_hw_calibration_code(unit_id, chan_config->atten);
#endif /* SOC_ADC_CALIBRATION_V1_SUPPORTED */
#endif /* IS_ULP_COCPU */
return ESP_OK;
}
esp_err_t lp_core_lp_adc_read_channel_raw(adc_unit_t unit_id, adc_channel_t channel, int *adc_raw)
{
if (unit_id != ADC_UNIT_1 || adc_raw == NULL) {
if (adc_raw == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t ret = lp_adc_validate_unit(unit_id);
if (ret != ESP_OK) {
return ret;
}
ret = lp_adc_validate_channel(unit_id, channel);
if (ret != ESP_OK) {
return ret;
}
#if IS_ULP_COCPU
uint32_t event = ADC_LL_EVENT_ADC1_ONESHOT_DONE;
@@ -101,7 +160,7 @@ esp_err_t lp_core_lp_adc_read_channel_raw(adc_unit_t unit_id, adc_channel_t chan
adc_oneshot_ll_disable_all_unit();
#else
return (adc_oneshot_read(s_adc1_handle, channel, adc_raw));
return (adc_oneshot_read(s_adc_unit_handles[unit_id], channel, adc_raw));
#endif /* IS_ULP_COCPU */
return ESP_OK;
@@ -109,12 +168,20 @@ esp_err_t lp_core_lp_adc_read_channel_raw(adc_unit_t unit_id, adc_channel_t chan
esp_err_t lp_core_lp_adc_read_channel_converted(adc_unit_t unit_id, adc_channel_t channel, int *voltage_mv)
{
esp_err_t ret = ESP_OK;
if (unit_id != ADC_UNIT_1 || voltage_mv == NULL) {
if (voltage_mv == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t ret = lp_adc_validate_unit(unit_id);
if (ret != ESP_OK) {
return ret;
}
ret = lp_adc_validate_channel(unit_id, channel);
if (ret != ESP_OK) {
return ret;
}
/* Read the raw ADC value */
int adc_raw;
ret = lp_core_lp_adc_read_channel_raw(unit_id, channel, &adc_raw);
@@ -22,8 +22,8 @@ extern const uint8_t lp_core_main_adc_bin_end[] asm("_binary_lp_core_test_app_
#if CONFIG_IDF_TARGET_ESP32P4
// Threshold values picked up empirically after manual testing
#define ADC_TEST_LOW_VAL 2160
#define ADC_TEST_HIGH_VAL 4090
#define ADC_TEST_LOW_VAL 1000
#define ADC_TEST_HIGH_VAL 3200
#else
#error "ADC threshold values not defined"
#endif
@@ -33,7 +33,7 @@ static void lp_uart_init(void)
static void lp_core_init(void)
{
/* Set LP core wakeup source as the HP CPU */
/* Set LP core wakeup source as the LP timer */
ulp_lp_core_cfg_t cfg = {
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER,
.lp_timer_sleep_duration_us = 1000000,
@@ -8,6 +8,7 @@
#include "sdkconfig.h"
#include "ulp_lp_core_print.h"
#include "ulp_lp_core_lp_adc_shared.h"
#include "ulp_lp_core_uart.h"
#define EXAMPLE_LP_ADC1_CHAN0 CONFIG_EXAMPLE_LP_ADC1_CHANNEL_0_SELECT
#define EXAMPLE_LP_ADC1_CHAN1 CONFIG_EXAMPLE_LP_ADC1_CHANNEL_1_SELECT
@@ -28,6 +29,7 @@ int main (void)
lp_core_printf("lpadc1 chan1 converted value = %d mV\r\n", adc_converted_value[1]);
lp_core_printf("\n");
lp_core_uart_tx_flush(LP_UART_NUM_0);
return 0;
}