Merge branch 'feat/ulp_adc_multi_channel' into 'master'

feat(ulp): add multi-channel ADC init for ULP

Closes IDFGH-9831 and IDF-15815

See merge request espressif/esp-idf!49671
This commit is contained in:
Meet Patel
2026-06-22 10:09:16 +05:30
4 changed files with 79 additions and 2 deletions
@@ -471,6 +471,28 @@ TEST_CASE("ULP ADC can init-deinit-init", "[ulp]")
TEST_ASSERT_EQUAL(ESP_OK, ulp_adc_deinit());
}
TEST_CASE("ULP ADC can init multiple channels", "[ulp]")
{
/* Initialize the ADC unit and configure the first channel */
ulp_adc_cfg_t riscv_adc_cfg = {
.adc_n = ADC_UNIT,
.channel = ADC_CHANNEL_5,
.width = WIDTH,
.atten = ADC_ATTEN_DB_2_5,
.ulp_mode = ADC_ULP_MODE_FSM,
};
TEST_ASSERT_EQUAL(ESP_OK, ulp_adc_init(&riscv_adc_cfg));
/* Configure an additional channel on the same ADC unit */
ulp_adc_chan_cfg_t chan_cfg = {
.atten = ADC_ATTEN_DB_12,
.bitwidth = WIDTH,
};
TEST_ASSERT_EQUAL(ESP_OK, ulp_adc_config_channel(ADC_UNIT, ADC_CHANNEL_6, &chan_cfg));
TEST_ASSERT_EQUAL(ESP_OK, ulp_adc_deinit());
}
TEST_CASE("Test ULP RISCV delay", "[ulp]")
{
int64_t start, diff;
+30 -1
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -8,6 +8,7 @@
#include "hal/adc_types.h"
#include "esp_err.h"
#include "esp_adc/adc_oneshot.h"
#ifdef __cplusplus
extern "C" {
@@ -21,14 +22,42 @@ typedef struct {
adc_ulp_mode_t ulp_mode; // ADC ULP Mode
} ulp_adc_cfg_t; // ULP FSM ADC configuration parameters
/**
* @brief ULP ADC channel configuration parameters
*/
typedef adc_oneshot_chan_cfg_t ulp_adc_chan_cfg_t;
/**
* @brief Initialize and calibrate the ADC for use by ULP FSM
*
* @note Initializes the ADC unit for ULP use and configures the channel
* specified in cfg->channel. To monitor multiple channels, call
* ulp_adc_config_channel() afterwards to configure additional channels.
*
* @param cfg Configuration parameters
* @return esp_err_t ESP_OK for successful.
*/
esp_err_t ulp_adc_init(const ulp_adc_cfg_t *cfg);
/**
* @brief Configure and calibrate an additional ADC channel for use by ULP
*
* Configures a channel on an ADC unit that has already been initialized by
* ulp_adc_init(). This can be called multiple times to configure multiple
* channels on the same ADC unit.
*
* @note HW calibration parameters are stored per attenuation, but only one set
* is active in the ADC unit at a time. If channels use different
* attenuations, readings on channels whose attenuation differs from the
* last configured one may be less accurate.
*
* @param adc_n ADC unit (must already be initialized via ulp_adc_init())
* @param channel ADC channel to configure
* @param chan_cfg Channel configuration (attenuation and bit width)
* @return esp_err_t ESP_OK for successful.
*/
esp_err_t ulp_adc_config_channel(adc_unit_t adc_n, adc_channel_t channel, const ulp_adc_chan_cfg_t *chan_cfg);
/**
* @brief Deinitialize ADC after use with ULP, allowing it to be reclaimed
*
+24 -1
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -17,6 +17,27 @@
static const char *TAG = "ulp_adc";
static adc_oneshot_unit_handle_t s_adc_handle = NULL;
esp_err_t ulp_adc_config_channel(adc_unit_t adc_n, adc_channel_t channel, const ulp_adc_chan_cfg_t *chan_cfg)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(chan_cfg, ESP_ERR_INVALID_ARG, TAG, "chan_cfg == NULL");
ESP_RETURN_ON_FALSE(s_adc_handle, ESP_ERR_INVALID_STATE, TAG, "ADC unit not initialized");
//-------------ADC Config---------------//
ret = adc_oneshot_config_channel(s_adc_handle, channel, chan_cfg);
if (ret != ESP_OK) {
return ret;
}
//Calibrate the ADC
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
adc_set_hw_calibration_code(adc_n, chan_cfg->atten);
#endif
return ret;
}
esp_err_t ulp_adc_init(const ulp_adc_cfg_t *cfg)
{
esp_err_t ret = ESP_OK;
@@ -47,6 +68,8 @@ esp_err_t ulp_adc_init(const ulp_adc_cfg_t *cfg)
};
ret = adc_oneshot_config_channel(s_adc_handle, cfg->channel, &config);
if (ret != ESP_OK) {
adc_oneshot_del_unit(s_adc_handle);
s_adc_handle = NULL;
return ret;
}
@@ -12,6 +12,9 @@ int32_t ulp_riscv_adc_read_channel(adc_unit_t adc_n, int channel)
adc_oneshot_ll_clear_event(event);
adc_oneshot_ll_disable_all_unit();
adc_oneshot_ll_enable(adc_n);
/* Force SW control of the channel bitmap; deep-sleep entry can clear it, which would
* otherwise make the channel selection below take no effect. */
adc_ll_set_controller(adc_n, ADC_LL_CTRL_RTC);
adc_oneshot_ll_set_channel(adc_n, channel);
adc_oneshot_ll_start(adc_n);