From c0601e512bea4c6996c5ac0f828cfbd7cc3800c0 Mon Sep 17 00:00:00 2001 From: Meet Patel Date: Mon, 15 Jun 2026 17:41:15 +0530 Subject: [PATCH] feat(ulp): add multi-channel ADC init for ULP ulp_adc_init() could only configure a single ADC channel, so monitoring several channels from the ULP on one ADC unit was not possible. Add ulp_adc_multi_channel_init() to configure multiple channels with per-channel attenuation while keeping the existing single-channel API unchanged. Closes https://github.com/espressif/esp-idf/issues/11160 --- .../test_apps/ulp_riscv/main/test_ulp_riscv.c | 22 +++++++++++++ components/ulp/ulp_common/include/ulp_adc.h | 31 ++++++++++++++++++- components/ulp/ulp_common/ulp_adc.c | 25 ++++++++++++++- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/components/ulp/test_apps/ulp_riscv/main/test_ulp_riscv.c b/components/ulp/test_apps/ulp_riscv/main/test_ulp_riscv.c index 7994118f2e9..9affa5d8686 100644 --- a/components/ulp/test_apps/ulp_riscv/main/test_ulp_riscv.c +++ b/components/ulp/test_apps/ulp_riscv/main/test_ulp_riscv.c @@ -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; diff --git a/components/ulp/ulp_common/include/ulp_adc.h b/components/ulp/ulp_common/include/ulp_adc.h index e838d4cdbbe..d25b7071af2 100644 --- a/components/ulp/ulp_common/include/ulp_adc.h +++ b/components/ulp/ulp_common/include/ulp_adc.h @@ -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 * diff --git a/components/ulp/ulp_common/ulp_adc.c b/components/ulp/ulp_common/ulp_adc.c index 21fbc1e93c7..ada7aa2740d 100644 --- a/components/ulp/ulp_common/ulp_adc.c +++ b/components/ulp/ulp_common/ulp_adc.c @@ -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; }