From f972cd4bbdf0be3fab643abe8e8be732edb62381 Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Tue, 27 Aug 2024 10:55:51 +0200 Subject: [PATCH 1/5] feat(clk): Addded support for LP ADC clock source for esp32p4 This commit adds support for the LP ADC clock source on the esp32p4. --- components/soc/esp32p4/include/soc/clk_tree_defs.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/components/soc/esp32p4/include/soc/clk_tree_defs.h b/components/soc/esp32p4/include/soc/clk_tree_defs.h index 307392faab..8e140f0571 100644 --- a/components/soc/esp32p4/include/soc/clk_tree_defs.h +++ b/components/soc/esp32p4/include/soc/clk_tree_defs.h @@ -635,6 +635,20 @@ typedef enum { ADC_RTC_CLK_SRC_DEFAULT = SOC_MOD_CLK_RC_FAST, /*!< Select RC_FAST as the default clock choice */ } soc_periph_adc_rtc_clk_src_t; +///////////////////////////////////////////////LP_ADC/////////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of LP_ADC + */ +#define SOC_LP_ADC_CLKS {SOC_MOD_CLK_LP_DYN_FAST} + +/** + * @brief LP ADC controller clock source + */ +typedef enum { + LP_ADC_CLK_SRC_LP_DYN_FAST = SOC_MOD_CLK_LP_DYN_FAST, /*!< Select LP_DYN_FAST as the source clock */ +} soc_periph_lp_adc_clk_src_t; + //////////////////////////////////////////////////MWDT///////////////////////////////////////////////////////////////// /** From d604e0927415f8e8f499a3f7cbbce4923ee7f59a Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Tue, 27 Aug 2024 11:03:55 +0200 Subject: [PATCH 2/5] feat(lp_adc): Added support for LP ADC initialization to the esp_adc oneshot driver This commit adds support for LP ADC initialization to the esp_adc oneshot driver, when it is used from the HP core. --- components/esp_adc/adc_oneshot.c | 16 ++++++++++++---- components/hal/adc_hal_common.c | 8 ++++---- components/hal/adc_oneshot_hal.c | 10 +++++++++- components/hal/esp32p4/include/hal/adc_ll.h | 9 +++++---- components/hal/include/hal/adc_hal_common.h | 4 ++-- components/hal/include/hal/adc_types.h | 3 +++ .../soc/esp32p4/include/soc/Kconfig.soc_caps.in | 8 ++++++++ components/soc/esp32p4/include/soc/soc_caps.h | 3 ++- 8 files changed, 45 insertions(+), 16 deletions(-) diff --git a/components/esp_adc/adc_oneshot.c b/components/esp_adc/adc_oneshot.c index 2d622687c0..b92260f63b 100644 --- a/components/esp_adc/adc_oneshot.c +++ b/components/esp_adc/adc_oneshot.c @@ -100,16 +100,24 @@ esp_err_t adc_oneshot_new_unit(const adc_oneshot_unit_init_cfg_t *init_config, a unit->unit_id = init_config->unit_id; unit->ulp_mode = init_config->ulp_mode; - adc_oneshot_clk_src_t clk_src = ADC_DIGI_CLK_SRC_DEFAULT; - if (init_config->clk_src) { - clk_src = init_config->clk_src; + adc_oneshot_clk_src_t clk_src; +#if SOC_LP_ADC_SUPPORTED + if (init_config->ulp_mode != ADC_ULP_MODE_DISABLE) { + clk_src = LP_ADC_CLK_SRC_LP_DYN_FAST; + } else +#endif /* CONFIG_SOC_LP_ADC_SUPPORTED */ + { + clk_src = ADC_DIGI_CLK_SRC_DEFAULT; + if (init_config->clk_src) { + clk_src = init_config->clk_src; + } } uint32_t clk_src_freq_hz = 0; ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz), err, TAG, "clock source not supported"); adc_oneshot_hal_cfg_t config = { .unit = init_config->unit_id, - .work_mode = (init_config->ulp_mode == ADC_ULP_MODE_FSM) ? ADC_HAL_ULP_FSM_MODE : ADC_HAL_SINGLE_READ_MODE, + .work_mode = (init_config->ulp_mode != ADC_ULP_MODE_DISABLE) ? ADC_HAL_LP_MODE : ADC_HAL_SINGLE_READ_MODE, .clk_src = clk_src, .clk_src_freq_hz = clk_src_freq_hz, }; diff --git a/components/hal/adc_hal_common.c b/components/hal/adc_hal_common.c index 4ea7ff9536..e1d8a652f2 100644 --- a/components/hal/adc_hal_common.c +++ b/components/hal/adc_hal_common.c @@ -18,8 +18,8 @@ static adc_ll_controller_t get_controller(adc_unit_t unit, adc_hal_work_mode_t w { if (unit == ADC_UNIT_1) { switch (work_mode) { -#if SOC_ULP_HAS_ADC - case ADC_HAL_ULP_FSM_MODE: +#if SOC_ULP_HAS_ADC || SOC_LP_CORE_SUPPORT_LP_ADC + case ADC_HAL_LP_MODE: return ADC_LL_CTRL_ULP; #endif case ADC_HAL_SINGLE_READ_MODE: @@ -35,8 +35,8 @@ static adc_ll_controller_t get_controller(adc_unit_t unit, adc_hal_work_mode_t w } } else { switch (work_mode) { -#if SOC_ULP_HAS_ADC - case ADC_HAL_ULP_FSM_MODE: +#if SOC_ULP_HAS_ADC || SOC_LP_CORE_SUPPORT_LP_ADC + case ADC_HAL_LP_MODE: return ADC_LL_CTRL_ULP; #endif #if !SOC_ADC_ARBITER_SUPPORTED //No ADC2 arbiter on ESP32 diff --git a/components/hal/adc_oneshot_hal.c b/components/hal/adc_oneshot_hal.c index c74079d464..00e9be9de3 100644 --- a/components/hal/adc_oneshot_hal.c +++ b/components/hal/adc_oneshot_hal.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -62,8 +62,16 @@ void adc_oneshot_hal_setup(adc_oneshot_hal_ctx_t *hal, adc_channel_t chan) adc_ll_digi_clk_sel(hal->clk_src); adc_ll_digi_controller_clk_div(ADC_LL_CLKM_DIV_NUM_DEFAULT, ADC_LL_CLKM_DIV_A_DEFAULT, ADC_LL_CLKM_DIV_B_DEFAULT); adc_ll_digi_set_clk_div(ADC_LL_DIGI_SAR_CLK_DIV_DEFAULT); +#else +#if SOC_LP_ADC_SUPPORTED + if (hal->work_mode == ADC_HAL_LP_MODE) { + adc_ll_set_sar_clk_div(unit, LP_ADC_LL_SAR_CLK_DIV_DEFAULT(unit)); + } else { + adc_ll_set_sar_clk_div(unit, ADC_LL_SAR_CLK_DIV_DEFAULT(unit)); + } #else adc_ll_set_sar_clk_div(unit, ADC_LL_SAR_CLK_DIV_DEFAULT(unit)); +#endif //SOC_LP_ADC_SUPPORTED if (unit == ADC_UNIT_2) { adc_ll_pwdet_set_cct(ADC_LL_PWDET_CCT_DEFAULT); } diff --git a/components/hal/esp32p4/include/hal/adc_ll.h b/components/hal/esp32p4/include/hal/adc_ll.h index 881cfd0c63..17745bb07a 100644 --- a/components/hal/esp32p4/include/hal/adc_ll.h +++ b/components/hal/esp32p4/include/hal/adc_ll.h @@ -39,6 +39,7 @@ extern "C" { ---------------------------------------------------------------*/ #define ADC_LL_DATA_INVERT_DEFAULT(PERIPH_NUM) (0) #define ADC_LL_SAR_CLK_DIV_DEFAULT(PERIPH_NUM) (1) +#define LP_ADC_LL_SAR_CLK_DIV_DEFAULT(PERIPH_NUM) (2) #define ADC_LL_DELAY_CYCLE_AFTER_DONE_SIGNAL (0) /*--------------------------------------------------------------- @@ -616,8 +617,8 @@ static inline void adc_ll_set_controller(adc_unit_t adc_n, adc_ll_controller_t c break; case ADC_LL_CTRL_ULP: LP_ADC.meas1_mux.sar1_dig_force = 0; // 1: Select digital control; 0: Select RTC control. - LP_ADC.meas1_ctrl2.meas1_start_force = 0; // 1: SW control RTC ADC start; 0: ULP control RTC ADC start. - LP_ADC.meas1_ctrl2.sar1_en_pad_force = 0; // 1: SW control RTC ADC bit map; 0: ULP control RTC ADC bit map; + LP_ADC.meas1_ctrl2.meas1_start_force = 1; // 1: SW control RTC ADC start; 0: ULP control RTC ADC start. + LP_ADC.meas1_ctrl2.sar1_en_pad_force = 1; // 1: SW control RTC ADC bit map; 0: ULP control RTC ADC bit map; break; case ADC_LL_CTRL_DIG: LP_ADC.meas1_mux.sar1_dig_force = 1; // 1: Select digital control; 0: Select RTC control. @@ -636,8 +637,8 @@ static inline void adc_ll_set_controller(adc_unit_t adc_n, adc_ll_controller_t c break; case ADC_LL_CTRL_ULP: LP_ADC.meas2_mux.sar2_rtc_force = 0; // 1: Select digital control; 0: Select RTC control. - LP_ADC.meas2_ctrl2.meas2_start_force = 0; // 1: SW control RTC ADC start; 0: ULP control RTC ADC start. - LP_ADC.meas2_ctrl2.sar2_en_pad_force = 0; // 1: SW control RTC ADC bit map; 0: ULP control RTC ADC bit map; + LP_ADC.meas2_ctrl2.meas2_start_force = 1; // 1: SW control RTC ADC start; 0: ULP control RTC ADC start. + LP_ADC.meas2_ctrl2.sar2_en_pad_force = 1; // 1: SW control RTC ADC bit map; 0: ULP control RTC ADC bit map; break; case ADC_LL_CTRL_DIG: LP_ADC.meas2_mux.sar2_rtc_force = 0; // 1: Select digital control; 0: Select RTC control. diff --git a/components/hal/include/hal/adc_hal_common.h b/components/hal/include/hal/adc_hal_common.h index 11503873e9..7c563a3c1e 100644 --- a/components/hal/include/hal/adc_hal_common.h +++ b/components/hal/include/hal/adc_hal_common.h @@ -26,7 +26,7 @@ typedef enum adc_hal_work_mode_t { ADC_HAL_SINGLE_READ_MODE, ADC_HAL_CONTINUOUS_READ_MODE, ADC_HAL_PWDET_MODE, - ADC_HAL_ULP_FSM_MODE, + ADC_HAL_LP_MODE, } adc_hal_work_mode_t; /** @@ -61,7 +61,7 @@ void adc_hal_arbiter_config(adc_arbiter_t *config); /** * @brief Initialize default parameter for the calibration block. * - * @param adc_n ADC index numer + * @param adc_n ADC index number */ void adc_hal_calibration_init(adc_unit_t adc_n); diff --git a/components/hal/include/hal/adc_types.h b/components/hal/include/hal/adc_types.h index 34445ee3d5..8660047579 100644 --- a/components/hal/include/hal/adc_types.h +++ b/components/hal/include/hal/adc_types.h @@ -64,6 +64,9 @@ typedef enum { ADC_ULP_MODE_DISABLE = 0, ///< ADC ULP mode is disabled ADC_ULP_MODE_FSM = 1, ///< ADC is controlled by ULP FSM ADC_ULP_MODE_RISCV = 2, ///< ADC is controlled by ULP RISCV +#if SOC_LP_ADC_SUPPORTED + ADC_ULP_MODE_LP_CORE = 3, ///< ADC is controlled by LP Core +#endif // SOC_LP_ADC_SUPPORTED } adc_ulp_mode_t; /** diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index f1ac7a0dd8..628bbd742b 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -259,6 +259,10 @@ config SOC_LP_SPI_SUPPORTED bool default y +config SOC_LP_ADC_SUPPORTED + bool + default y + config SOC_SPIRAM_SUPPORTED bool default y @@ -1926,3 +1930,7 @@ config SOC_LCDCAM_CAM_DATA_WIDTH_MAX config SOC_LP_CORE_SUPPORT_ETM bool default y + +config SOC_LP_CORE_SUPPORT_LP_ADC + bool + default y diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index a1ccd05234..4653f982da 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -82,9 +82,9 @@ #define SOC_LP_I2C_SUPPORTED 1 #define SOC_LP_I2S_SUPPORTED 1 #define SOC_LP_SPI_SUPPORTED 1 +#define SOC_LP_ADC_SUPPORTED 1 #define SOC_SPIRAM_SUPPORTED 1 #define SOC_PSRAM_DMA_CAPABLE 1 -// #define SOC_ULP_SUPPORTED 1 //TODO: IDF-7534 #define SOC_SDMMC_HOST_SUPPORTED 1 #define SOC_CLK_TREE_SUPPORTED 1 #define SOC_ASSIST_DEBUG_SUPPORTED 1 @@ -738,3 +738,4 @@ /*------------------------------------- ULP CAPS -------------------------------------*/ #define SOC_LP_CORE_SUPPORT_ETM (1) /*!< LP Core supports ETM */ +#define SOC_LP_CORE_SUPPORT_LP_ADC (1) /*!< LP ADC can be accessed from the LP-Core */ From 1e5efd7fa7091b20862d6c9c2484af2c6d5feca5 Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Tue, 27 Aug 2024 11:07:17 +0200 Subject: [PATCH 3/5] feat(lp_adc): Added support to read LP ADC from the LP core This commit adds APIs to initialize and configure the LP ADC from the HP core and also adds APIs to read the raw and converted ADC values from the LP core. --- components/ulp/CMakeLists.txt | 4 + components/ulp/cmake/IDFULPProject.cmake | 3 +- .../include/ulp_lp_core_lp_adc_shared.h | 113 +++++++++++++ .../shared/ulp_lp_core_lp_adc_shared.c | 157 ++++++++++++++++++ 4 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 components/ulp/lp_core/shared/include/ulp_lp_core_lp_adc_shared.h create mode 100644 components/ulp/lp_core/shared/ulp_lp_core_lp_adc_shared.c diff --git a/components/ulp/CMakeLists.txt b/components/ulp/CMakeLists.txt index 24ea46461d..96b38e2025 100644 --- a/components/ulp/CMakeLists.txt +++ b/components/ulp/CMakeLists.txt @@ -73,6 +73,10 @@ if(CONFIG_ULP_COPROC_TYPE_LP_CORE) if(CONFIG_SOC_LP_CORE_SUPPORT_ETM) list(APPEND srcs "lp_core/lp_core_etm.c") endif() + + if(CONFIG_SOC_LP_ADC_SUPPORTED) + list(APPEND srcs "lp_core/shared/ulp_lp_core_lp_adc_shared.c") + endif() endif() idf_component_register(SRCS ${srcs} diff --git a/components/ulp/cmake/IDFULPProject.cmake b/components/ulp/cmake/IDFULPProject.cmake index f76bb89b7e..6febf12601 100644 --- a/components/ulp/cmake/IDFULPProject.cmake +++ b/components/ulp/cmake/IDFULPProject.cmake @@ -124,7 +124,8 @@ function(ulp_apply_default_sources ulp_app_name) "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_interrupt.c" "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_i2c.c" "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_spi.c" - "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_ubsan.c") + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_ubsan.c" + "${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_lp_adc_shared.c") set(target_folder ${IDF_TARGET}) diff --git a/components/ulp/lp_core/shared/include/ulp_lp_core_lp_adc_shared.h b/components/ulp/lp_core/shared/include/ulp_lp_core_lp_adc_shared.h new file mode 100644 index 0000000000..310caa0b1a --- /dev/null +++ b/components/ulp/lp_core/shared/include/ulp_lp_core_lp_adc_shared.h @@ -0,0 +1,113 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_err.h" +#include "hal/adc_types.h" +#include "esp_adc/adc_oneshot.h" + +/** + * @brief LP ADC channel configurations + */ +typedef adc_oneshot_chan_cfg_t lp_core_lp_adc_chan_cfg_t; + +/** + * @brief Initialize the LP ADC + * + * @note We only support LP ADC1 and not LP ADC2 due to a HW issue. + * + * @param unit_id LP ADC unit to initialize + * + * @return ESP_OK on success + * ESP_ERR_INVALID_ARG if the unit_id is invalid + * ESP_ERR_NOT_SUPPORTED if the API is not supported on the LP Core + * ESP_FAIL if the ADC unit failed to initialize + */ +esp_err_t lp_core_lp_adc_init(adc_unit_t unit_id); + +/** + * @brief Deinitialize the LP ADC + * + * @param unit_id LP ADC unit to deinitialize + * + * @return ESP_OK on success + * ESP_ERR_INVALID_ARG if the unit_id is invalid + * ESP_ERR_NOT_SUPPORTED if the API is not supported on the LP Core + * ESP_FAIL if the ADC unit failed to deinitialize + */ +esp_err_t lp_core_lp_adc_deinit(adc_unit_t unit_id); + +/** + * @brief Configure an LP ADC channel + * + * @param unit_id ADC unit to configure the channel for + * @param channel ADC channel to configure + * @param chan_config Configuration for the channel + * + * @return ESP_OK on success + * ESP_ERR_INVALID_ARG if the unit_id is invalid + * ESP_ERR_NOT_SUPPORTED if the API is not supported on the LP Core + * ESP_FAIL if the channel configuration fails + */ +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); + +/** + * @brief Read the raw ADC value from a channel + * + * @note The raw value is the 12-bit value read from the ADC. + * The value is between 0 and 4095. To convert this value + * to a voltage, use the formula: + * voltage = (raw_value * (1.1v / 4095)) * attenuation + * + * Alternatively, use lp_core_lp_adc_read_channel_converted() + * to get the converted value. + * + * @param[in] unit_id ADC unit to configure the channel for + * @param[in] channel ADC channel to configure + * @param[in] adc_raw Pointer to store the raw 12-bit ADC value + * + * @return ESP_OK on success + * ESP_ERR_INVALID_ARG if the unit_id is invalid + * ESP_FAIL if the read fails + */ +esp_err_t lp_core_lp_adc_read_channel_raw(adc_unit_t unit_id, adc_channel_t channel, int *adc_raw); + +/** + * @brief Read the converted ADC value in millivolts from a channel + * + * @note The API converts the measured voltage based on the + * internal reference voltage of 1.1v and the the attenuation + * factors. It uses the formula: + * voltage = (raw_value * (1.1v / 4095)) * attenuation + * + * To avoid complex floating-point operations at runtime, + * the API converts the raw data to millivolts. Also, the + * conversion approximates the calculation when scaling + * the voltage by using pre-computed attenuation factors. + * + * @note The conversion approximates the measured voltage based on the + * internal reference voltage of 1.1v and the approximations of + * the attenuation factors. + * + * @param[in] unit_id ADC unit to configure the channel for + * @param[in] channel ADC channel to configure + * @param[out] voltage_mv Pointer to store the converted ADC value in millivolts + * + * @return ESP_OK on success + * ESP_ERR_INVALID_ARG if the unit_id is invalid or voltage_mv is NULL + * ESP_FAIL if the read fails + */ +esp_err_t lp_core_lp_adc_read_channel_converted(adc_unit_t unit_id, adc_channel_t channel, int *voltage_mv); + +#ifdef __cplusplus +} +#endif diff --git a/components/ulp/lp_core/shared/ulp_lp_core_lp_adc_shared.c b/components/ulp/lp_core/shared/ulp_lp_core_lp_adc_shared.c new file mode 100644 index 0000000000..63bc549019 --- /dev/null +++ b/components/ulp/lp_core/shared/ulp_lp_core_lp_adc_shared.c @@ -0,0 +1,157 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc_caps.h" +#if SOC_LP_ADC_SUPPORTED + +#include "ulp_lp_core_lp_adc_shared.h" +#include "hal/adc_types.h" +#include "hal/adc_ll.h" + +#define VREF (1100) /* Internal Reference voltage in millivolts (1.1V) */ +#define INV_ATTEN_0DB (1000) /* Inverse of 10^0 * 1000 */ +#define INV_ATTEN_2_5DB (1335) /* Inverse of 10^(-2.5/20) * 1000 */ +#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; + +esp_err_t lp_core_lp_adc_init(adc_unit_t unit_id) +{ + 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; + } + +#if IS_ULP_COCPU + // Not supported + return ESP_ERR_NOT_SUPPORTED; +#else + /* LP ADC is being initialized from the HP core. + * Hence, we use the standard ADC driver APIs here. + */ + + /* Initialize ADC */ + adc_oneshot_unit_init_cfg_t init_config = { + .unit_id = 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)); +#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; + } + +#if IS_ULP_COCPU + // Not supported + return ESP_ERR_NOT_SUPPORTED; +#else + return (adc_oneshot_del_unit(s_adc1_handle)); +#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; + } +#if IS_ULP_COCPU + // Not supported + return ESP_ERR_NOT_SUPPORTED; +#else + adc_oneshot_chan_cfg_t config = { + .atten = chan_config->atten, + .bitwidth = chan_config->bitwidth, + }; + + return (adc_oneshot_config_channel(s_adc1_handle, channel, &config)); +#endif /* IS_ULP_COCPU */ +} + +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) { + return ESP_ERR_INVALID_ARG; + } + +#if IS_ULP_COCPU + uint32_t event = ADC_LL_EVENT_ADC1_ONESHOT_DONE; + + adc_oneshot_ll_clear_event(event); + adc_oneshot_ll_disable_all_unit(); + adc_oneshot_ll_enable(unit_id); + adc_oneshot_ll_set_channel(unit_id, channel); + + adc_oneshot_ll_start(unit_id); + while (!adc_oneshot_ll_get_event(event)) { + ; + } + *adc_raw = adc_oneshot_ll_get_raw_result(unit_id); + + adc_oneshot_ll_disable_all_unit(); +#else + return (adc_oneshot_read(s_adc1_handle, channel, adc_raw)); +#endif /* IS_ULP_COCPU */ + + return ESP_OK; +} + +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) { + return ESP_ERR_INVALID_ARG; + } + + /* Read the raw ADC value */ + int adc_raw; + ret = lp_core_lp_adc_read_channel_raw(unit_id, channel, &adc_raw); + if (ret != ESP_OK) { + return ret; + } + + /* On the esp32p4, the ADC raw value can be 12-bit wide. The internal Vref is 1.1V. + * The formula to convert the raw value to voltage is: + * voltage = (((raw_value / (2^12 - 1)) * 1.1V) * attenuation) + * + * To avoid many floating point calculations, we precompute the attenuation factors + * and perform the conversion in millivolts instead of volts. + */ + + int measured_voltage = adc_raw * VREF; // millivolts + measured_voltage /= 4095; + + adc_atten_t atten = adc_ll_get_atten(unit_id, channel); + switch (atten) { + case ADC_ATTEN_DB_0: + *voltage_mv = (measured_voltage * INV_ATTEN_0DB) / 1000; + break; + case ADC_ATTEN_DB_2_5: + *voltage_mv = (measured_voltage * INV_ATTEN_2_5DB) / 1000; + break; + case ADC_ATTEN_DB_6: + *voltage_mv = (measured_voltage * INV_ATTEN_6DB) / 1000; + break; + case ADC_ATTEN_DB_12: + *voltage_mv = (measured_voltage * INV_ATTEN_12DB) / 1000; + break; + default: + ret = ESP_ERR_INVALID_STATE; + } + + return ret; +} + +#endif /* CONFIG_SOC_LP_ADC_SUPPORTED */ From 594880dae45985861db08910f83a694739329ce7 Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Wed, 28 Aug 2024 11:21:44 +0200 Subject: [PATCH 4/5] test(lp_adc): Added LP Core unit tests for testing LP ADC Added a unit test to verify that LP ADC1 works and can be read from the LP Core. --- .../lp_core_basic_tests/main/CMakeLists.txt | 12 + .../main/lp_core/test_main_adc.c | 20 ++ .../main/test_lp_core_adc.c | 243 ++++++++++++++++++ 3 files changed, 275 insertions(+) create mode 100644 components/ulp/test_apps/lp_core/lp_core_basic_tests/main/lp_core/test_main_adc.c create mode 100644 components/ulp/test_apps/lp_core/lp_core_basic_tests/main/test_lp_core_adc.c diff --git a/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/CMakeLists.txt b/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/CMakeLists.txt index 86b34fdf66..63d8f905d7 100644 --- a/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/CMakeLists.txt +++ b/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/CMakeLists.txt @@ -16,6 +16,10 @@ if(CONFIG_SOC_LP_CORE_SUPPORT_ETM AND CONFIG_SOC_ETM_SUPPORTED) list(APPEND app_sources "test_lp_core_etm.c") endif() +if(CONFIG_SOC_LP_ADC_SUPPORTED) + list(APPEND app_sources "test_lp_core_adc.c") +endif() + set(lp_core_sources "lp_core/test_main.c") set(lp_core_sources_counter "lp_core/test_main_counter.c") @@ -38,6 +42,10 @@ if(CONFIG_SOC_LP_SPI_SUPPORTED) set(lp_core_sources_spi_slave "lp_core/test_main_spi_slave.c") endif() +if(CONFIG_SOC_LP_ADC_SUPPORTED) + set(lp_core_sources_adc "lp_core/test_main_adc.c") +endif() + idf_component_register(SRCS ${app_sources} INCLUDE_DIRS "lp_core" REQUIRES ulp unity esp_timer test_utils @@ -67,3 +75,7 @@ if(CONFIG_SOC_LP_SPI_SUPPORTED) ulp_embed_binary(lp_core_test_app_spi_master "${lp_core_sources_spi_master}" "${lp_core_exp_dep_srcs}") ulp_embed_binary(lp_core_test_app_spi_slave "${lp_core_sources_spi_slave}" "${lp_core_exp_dep_srcs}") endif() + +if(CONFIG_SOC_LP_ADC_SUPPORTED) + ulp_embed_binary(lp_core_test_app_adc "${lp_core_sources_adc}" "${lp_core_exp_dep_srcs}") +endif() diff --git a/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/lp_core/test_main_adc.c b/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/lp_core/test_main_adc.c new file mode 100644 index 0000000000..4bc1b60220 --- /dev/null +++ b/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/lp_core/test_main_adc.c @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "ulp_lp_core_lp_adc_shared.h" + +volatile int adc_raw[8]; + +int main(void) +{ + while (1) { + for (int i = 0; i < 8; i++) { + lp_core_lp_adc_read_channel_raw(ADC_UNIT_1, i, (int *)&adc_raw[i]); + } + } + + return 0; +} diff --git a/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/test_lp_core_adc.c b/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/test_lp_core_adc.c new file mode 100644 index 0000000000..537d93b44b --- /dev/null +++ b/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/test_lp_core_adc.c @@ -0,0 +1,243 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "hal/adc_types.h" +#include "lp_core_test_app_adc.h" +#include "ulp_lp_core.h" +#include "ulp_lp_core_lp_adc_shared.h" +#include "soc/adc_periph.h" +#include "driver/gpio.h" +#include "driver/temperature_sensor.h" + +#include "unity.h" + +extern const uint8_t lp_core_main_adc_bin_start[] asm("_binary_lp_core_test_app_adc_bin_start"); +extern const uint8_t lp_core_main_adc_bin_end[] asm("_binary_lp_core_test_app_adc_bin_end"); + +#if CONFIG_IDF_TARGET_ESP32P4 +// Threshold values picked up empirically after manual testing +#define ADC_TEST_LOW_VAL 1500 +#define ADC_TEST_HIGH_VAL 2000 +#else +#error "ADC threshold values not defined" +#endif + +#define ADC_GET_IO_NUM(unit, channel) (adc_channel_io_map[unit][channel]) + +static void test_adc_set_io_level(adc_unit_t unit, adc_channel_t channel, bool level) +{ + TEST_ASSERT(channel < SOC_ADC_CHANNEL_NUM(unit) && "invalid channel"); + +#if !ADC_LL_RTC_GPIO_SUPPORTED + uint32_t io_num = ADC_GET_IO_NUM(unit, channel); + TEST_ESP_OK(gpio_set_pull_mode(io_num, (level ? GPIO_PULLUP_ONLY : GPIO_PULLDOWN_ONLY))); +#else + gpio_num_t io_num = ADC_GET_IO_NUM(unit, channel); + if (level) { + TEST_ESP_OK(rtc_gpio_pullup_en(io_num)); + TEST_ESP_OK(rtc_gpio_pulldown_dis(io_num)); + } else { + TEST_ESP_OK(rtc_gpio_pullup_dis(io_num)); + TEST_ESP_OK(rtc_gpio_pulldown_en(io_num)); + } +#endif +} + +static void load_and_start_lp_core_firmware(ulp_lp_core_cfg_t* cfg, const uint8_t* firmware_start, const uint8_t* firmware_end) +{ + TEST_ASSERT(ulp_lp_core_load_binary(firmware_start, + (firmware_end - firmware_start)) == ESP_OK); + + TEST_ASSERT(ulp_lp_core_run(cfg) == ESP_OK); + +} + +void test_lp_adc(adc_unit_t unit_id) +{ + /* Load ULP firmware and start the coprocessor */ + ulp_lp_core_cfg_t cfg = { + .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU, + }; + + load_and_start_lp_core_firmware(&cfg, lp_core_main_adc_bin_start, lp_core_main_adc_bin_end); + + /* LP ADC Init */ + ESP_ERROR_CHECK(lp_core_lp_adc_init(unit_id)); + + /* LP ADC channel config */ + const lp_core_lp_adc_chan_cfg_t config = { + .atten = ADC_ATTEN_DB_12, + .bitwidth = ADC_BITWIDTH_DEFAULT, + }; + + /* Configure all ADC channels. + * LP ADC1: Channels 0 - 7 + * LP ADC2: Channels 0 - 5 + */ + TEST_ASSERT(lp_core_lp_adc_config_channel(unit_id, ADC_CHANNEL_0, &config) == ESP_OK); + TEST_ASSERT(lp_core_lp_adc_config_channel(unit_id, ADC_CHANNEL_1, &config) == ESP_OK); + TEST_ASSERT(lp_core_lp_adc_config_channel(unit_id, ADC_CHANNEL_2, &config) == ESP_OK); + TEST_ASSERT(lp_core_lp_adc_config_channel(unit_id, ADC_CHANNEL_3, &config) == ESP_OK); + TEST_ASSERT(lp_core_lp_adc_config_channel(unit_id, ADC_CHANNEL_4, &config) == ESP_OK); + TEST_ASSERT(lp_core_lp_adc_config_channel(unit_id, ADC_CHANNEL_5, &config) == ESP_OK); + if (unit_id == ADC_UNIT_1) { + TEST_ASSERT(lp_core_lp_adc_config_channel(unit_id, ADC_CHANNEL_6, &config) == ESP_OK); + TEST_ASSERT(lp_core_lp_adc_config_channel(unit_id, ADC_CHANNEL_7, &config) == ESP_OK); + } + + /* Set all the ADC channel IOs to low */ + test_adc_set_io_level(unit_id, ADC_CHANNEL_0, 0); + test_adc_set_io_level(unit_id, ADC_CHANNEL_1, 0); + test_adc_set_io_level(unit_id, ADC_CHANNEL_2, 0); + test_adc_set_io_level(unit_id, ADC_CHANNEL_3, 0); + test_adc_set_io_level(unit_id, ADC_CHANNEL_4, 0); + test_adc_set_io_level(unit_id, ADC_CHANNEL_5, 0); + test_adc_set_io_level(unit_id, ADC_CHANNEL_6, 0); + test_adc_set_io_level(unit_id, ADC_CHANNEL_7, 0); + + vTaskDelay(10); + + int *adc_raw = (int *)&ulp_adc_raw; + + /* Verify that the LP ADC values reflect a low-state of the input pins */ + for (int i = 0; i < SOC_ADC_CHANNEL_NUM(unit_id); i++) { + printf("LP ADC low[%d] = %d\n", i, adc_raw[i]); + TEST_ASSERT_LESS_THAN_INT(ADC_TEST_LOW_VAL, adc_raw[i]); + } + + /* Set all the ADC channel IOs to high */ + test_adc_set_io_level(unit_id, ADC_CHANNEL_0, 1); + test_adc_set_io_level(unit_id, ADC_CHANNEL_1, 1); + test_adc_set_io_level(unit_id, ADC_CHANNEL_2, 1); + test_adc_set_io_level(unit_id, ADC_CHANNEL_3, 1); + test_adc_set_io_level(unit_id, ADC_CHANNEL_4, 1); + test_adc_set_io_level(unit_id, ADC_CHANNEL_5, 1); + test_adc_set_io_level(unit_id, ADC_CHANNEL_6, 1); + test_adc_set_io_level(unit_id, ADC_CHANNEL_7, 1); + + vTaskDelay(10); + + /* Verify that the LP ADC values reflect a high-state of the input pins */ + for (int i = 0; i < SOC_ADC_CHANNEL_NUM(unit_id); i++) { + printf("LP ADC high[%d] = %d\n", i, adc_raw[i]); + TEST_ASSERT_GREATER_THAN_INT(ADC_TEST_HIGH_VAL, adc_raw[i]); + } + + /* Deinit LP ADC */ + ESP_ERROR_CHECK(lp_core_lp_adc_deinit(unit_id)); +} + +TEST_CASE("LP ADC 1 raw read test", "[lp_core]") +{ + test_lp_adc(ADC_UNIT_1); +} + +// Enable when DIG-396 is fixed +// TEST_CASE("LP ADC 2 raw read test", "[lp_core]") +// { +// test_lp_adc(ADC_UNIT_2); +// } + +static void test_lp_adc_stress(adc_unit_t unit_id) +{ + /* Load ULP firmware and start the coprocessor */ + ulp_lp_core_cfg_t cfg = { + .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU, + }; + + load_and_start_lp_core_firmware(&cfg, lp_core_main_adc_bin_start, lp_core_main_adc_bin_end); + + for (int i = 0; i < 100; i++) { + /* LP ADC Init */ + ESP_ERROR_CHECK(lp_core_lp_adc_init(unit_id)); + + /* LP ADC channel config */ + const lp_core_lp_adc_chan_cfg_t config = { + .atten = ADC_ATTEN_DB_12, + .bitwidth = ADC_BITWIDTH_DEFAULT, + }; + + TEST_ASSERT(lp_core_lp_adc_config_channel(unit_id, ADC_CHANNEL_0, &config) == ESP_OK); + + /* Set LP ADC channel IO and read raw value */ + test_adc_set_io_level(unit_id, ADC_CHANNEL_0, 1); + vTaskDelay(10); + int *adc_raw = (int *)&ulp_adc_raw; + TEST_ASSERT_NOT_EQUAL(0, adc_raw[0]); + + /* De-init LP ADC */ + ESP_ERROR_CHECK(lp_core_lp_adc_deinit(unit_id)); + } +} + +TEST_CASE("LP ADC 1 raw read stress test", "[lp_core]") +{ + test_lp_adc_stress(ADC_UNIT_1); +} + +// Enable when DIG-396 is fixed +// TEST_CASE("LP ADC 2 raw read stress test", "[lp_core]") +// { +// test_lp_adc_stress(ADC_UNIT_2); +// } + +TEST_CASE("Test temperature sensor does not affect LP ADC", "[lp_core]") +{ + printf("Install temperature sensor, expected temp ranger range: 10~50 ℃\n"); + temperature_sensor_handle_t temp_sensor = NULL; + temperature_sensor_config_t temp_sensor_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80); + TEST_ESP_OK(temperature_sensor_install(&temp_sensor_config, &temp_sensor)); + int cnt = 2; + float tsens_value; + while (cnt--) { + temperature_sensor_enable(temp_sensor); + TEST_ESP_OK(temperature_sensor_get_celsius(temp_sensor, &tsens_value)); + printf("Temperature value %.02f ℃\n", tsens_value); + vTaskDelay(pdMS_TO_TICKS(100)); + TEST_ESP_OK(temperature_sensor_disable(temp_sensor)); + } + + /* Load ULP firmware and start the coprocessor */ + ulp_lp_core_cfg_t cfg = { + .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU, + }; + + load_and_start_lp_core_firmware(&cfg, lp_core_main_adc_bin_start, lp_core_main_adc_bin_end); + + /* LP ADC Init */ + ESP_ERROR_CHECK(lp_core_lp_adc_init(ADC_UNIT_1)); + + /* LP ADC channel config */ + const lp_core_lp_adc_chan_cfg_t config = { + .atten = ADC_ATTEN_DB_12, + .bitwidth = ADC_BITWIDTH_DEFAULT, + }; + + /* Configure ADC channel 0 */ + TEST_ASSERT(lp_core_lp_adc_config_channel(ADC_UNIT_1, ADC_CHANNEL_0, &config) == ESP_OK); + + int *adc_raw = (int *)&ulp_adc_raw; + cnt = 2; + while (cnt--) { + printf("LP ADC%d Channel[%d] Raw Data: %d\n", ADC_UNIT_1 + 1, 0, adc_raw[0]); + vTaskDelay(pdMS_TO_TICKS(100)); + } + + TEST_ESP_OK(lp_core_lp_adc_deinit(ADC_UNIT_1)); + + cnt = 2; + while (cnt--) { + temperature_sensor_enable(temp_sensor); + TEST_ESP_OK(temperature_sensor_get_celsius(temp_sensor, &tsens_value)); + printf("Temperature value %.02f ℃\n", tsens_value); + vTaskDelay(pdMS_TO_TICKS(100)); + TEST_ESP_OK(temperature_sensor_disable(temp_sensor)); + } + + TEST_ESP_OK(temperature_sensor_uninstall(temp_sensor)); +} From 0b75e75f2cc167ee10268d3817a1b492189d3aa3 Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Wed, 28 Aug 2024 17:33:33 +0200 Subject: [PATCH 5/5] feat(lp_adc): Added example to demonstrate LP ADC usage from LP Core This commit adds an example which demonstrates how to configure and use the LP ADC from the LP core while the main core is in deep sleep. --- examples/system/.build-test-rules.yml | 6 ++ .../system/ulp/lp_core/lp_adc/CMakeLists.txt | 8 ++ examples/system/ulp/lp_core/lp_adc/README.md | 63 +++++++++++++++ .../ulp/lp_core/lp_adc/main/CMakeLists.txt | 25 ++++++ .../ulp/lp_core/lp_adc/main/Kconfig.projbuild | 17 +++++ .../ulp/lp_core/lp_adc/main/lp_adc_main.c | 76 +++++++++++++++++++ .../ulp/lp_core/lp_adc/main/lp_core/main.c | 33 ++++++++ .../ulp/lp_core/lp_adc/sdkconfig.defaults | 10 +++ 8 files changed, 238 insertions(+) create mode 100644 examples/system/ulp/lp_core/lp_adc/CMakeLists.txt create mode 100644 examples/system/ulp/lp_core/lp_adc/README.md create mode 100644 examples/system/ulp/lp_core/lp_adc/main/CMakeLists.txt create mode 100644 examples/system/ulp/lp_core/lp_adc/main/Kconfig.projbuild create mode 100644 examples/system/ulp/lp_core/lp_adc/main/lp_adc_main.c create mode 100644 examples/system/ulp/lp_core/lp_adc/main/lp_core/main.c create mode 100644 examples/system/ulp/lp_core/lp_adc/sdkconfig.defaults diff --git a/examples/system/.build-test-rules.yml b/examples/system/.build-test-rules.yml index f4d6b6b869..0590a39954 100644 --- a/examples/system/.build-test-rules.yml +++ b/examples/system/.build-test-rules.yml @@ -315,6 +315,12 @@ examples/system/ulp/lp_core/interrupt: depends_components: - ulp +examples/system/ulp/lp_core/lp_adc: + disable: + - if: (SOC_LP_ADC_SUPPORTED != 1) + depends_components: + - ulp, esp_adc + examples/system/ulp/lp_core/lp_i2c: enable: - if: SOC_LP_I2C_SUPPORTED == 1 and SOC_DEEP_SLEEP_SUPPORTED == 1 diff --git a/examples/system/ulp/lp_core/lp_adc/CMakeLists.txt b/examples/system/ulp/lp_core/lp_adc/CMakeLists.txt new file mode 100644 index 0000000000..6701b75d56 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_adc/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +list(APPEND SDKCONFIG_DEFAULTS "sdkconfig.defaults") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(lp_core_adc) diff --git a/examples/system/ulp/lp_core/lp_adc/README.md b/examples/system/ulp/lp_core/lp_adc/README.md new file mode 100644 index 0000000000..89b1dd2d29 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_adc/README.md @@ -0,0 +1,63 @@ +| Supported Targets | ESP32-P4 | +| ----------------- | -------- | + +# LP ADC Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example demonstrates how to use the LP ADC peripheral on the LP core. The example sets up the LP ADC from the HP core before putting it to sleep. +It then reads the configured LP ADC channels from the LP core and prints the raw and converted voltage values to the LP UART console. + +## How to use example + +### Hardware Required + +* A development board with ESP SoC such as the ESP32-P4 which supports LP ADC. +* A USB cable for power supply and programming +* A USB to UART converter to monitor the LP core serial console + +In this example, you need to connect a voltage source (e.g. a DC power supply) to the GPIO pins for the input ADC channels. The ADC channels to read can be configured from the menuconfig option for this example. + +**Note:** The following pin assignments are used by default. + +#### LP UART Pin Assignment: + +| | UART Tx Pin | +| ----------------------- | ------------| +| ESP32-P4 | GPIO14 | + +The UART Tx Pin must be connected to the UART Rx pin on the host machine. + +#### LP ADC Pin Assignments: + +| | LP ADC Channel Number | Pin | +| ----------------------- | --------------------------| ------ | +| ESP32-P4 | Channel 4 | GPIO20 | +| ESP32-P4 | Channel 5 | GPIO21 | + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: +Enter `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +Use another serial monitor program/instance such as idf.py monitor, minicom or miniterm to send and receive data from the LP core. +The default baudrate used for the example is 115200. Care must be taken that the configuration matches on both the device and the serial terminal. + +## Example Output + +Running this example, you will see the following log output on the LP core serial monitor: + +``` +ESP-ROM-LP:esp32p4-20230811 +Build:Aug 11 2023 +rst:0x14 (SW_RST), wakeup:0x40000 +entry:0x50108180 +lpadc1 chan0 raw value = 0 +lpadc1 chan0 converted value = 0 mV +lpadc1 chan1 raw value = 3894 +lpadc1 chan1 converted value = 4167 mV +``` diff --git a/examples/system/ulp/lp_core/lp_adc/main/CMakeLists.txt b/examples/system/ulp/lp_core/lp_adc/main/CMakeLists.txt new file mode 100644 index 0000000000..92e6a41038 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_adc/main/CMakeLists.txt @@ -0,0 +1,25 @@ +# Register the component +idf_component_register(SRCS "lp_adc_main.c" + INCLUDE_DIRS "." + REQUIRES ulp) + +# +# ULP support additions to component CMakeLists.txt. +# +# 1. The LP Core app name must be unique (if multiple components use LP Core). +set(ulp_app_name lp_core_${COMPONENT_NAME}) +# +# 2. Specify all C files. +# Files should be placed into a separate directory (in this case, lp_core/), +# which should not be added to COMPONENT_SRCS. +set(ulp_lp_core_sources "lp_core/main.c") + +# +# 3. List all the component source files which include automatically +# generated LP Core export file, ${ulp_app_name}.h: +set(ulp_exp_dep_srcs "lp_adc_main.c") + +# +# 4. Call function to build ULP binary and embed in project using the argument +# values above. +ulp_embed_binary(${ulp_app_name} "${ulp_lp_core_sources}" "${ulp_exp_dep_srcs}") diff --git a/examples/system/ulp/lp_core/lp_adc/main/Kconfig.projbuild b/examples/system/ulp/lp_core/lp_adc/main/Kconfig.projbuild new file mode 100644 index 0000000000..f68b55eeea --- /dev/null +++ b/examples/system/ulp/lp_core/lp_adc/main/Kconfig.projbuild @@ -0,0 +1,17 @@ +menu "Example Configuration" + + config EXAMPLE_LP_ADC1_CHANNEL_0_SELECT + int "LP ADC1 Channel 0 Select" + default 4 + range 0 7 + help + Select the channel to be used for LP ADC1 Channel 0 + + config EXAMPLE_LP_ADC1_CHANNEL_1_SELECT + int "LP ADC1 Channel 1 Select" + default 5 + range 0 7 + help + Select the channel to be used for LP ADC1 Channel 1 + +endmenu diff --git a/examples/system/ulp/lp_core/lp_adc/main/lp_adc_main.c b/examples/system/ulp/lp_core/lp_adc/main/lp_adc_main.c new file mode 100644 index 0000000000..e7bd6d6e67 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_adc/main/lp_adc_main.c @@ -0,0 +1,76 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_sleep.h" +#include "lp_core_main.h" +#include "ulp_lp_core.h" +#include "lp_core_uart.h" +#include "ulp_lp_core_lp_adc_shared.h" + +/* LP ADC1 configuration */ +#define EXAMPLE_LP_ADC1_CHAN0 CONFIG_EXAMPLE_LP_ADC1_CHANNEL_0_SELECT +#define EXAMPLE_LP_ADC1_CHAN1 CONFIG_EXAMPLE_LP_ADC1_CHANNEL_1_SELECT +#define EXAMPLE_ADC_ATTEN ADC_ATTEN_DB_12 + +/* LP core binary */ +extern const uint8_t lp_core_main_bin_start[] asm("_binary_lp_core_main_bin_start"); +extern const uint8_t lp_core_main_bin_end[] asm("_binary_lp_core_main_bin_end"); + +static void lp_uart_init(void) +{ + lp_core_uart_cfg_t cfg = LP_CORE_UART_DEFAULT_CONFIG(); + + ESP_ERROR_CHECK(lp_core_uart_init(&cfg)); + + printf("LP UART initialized successfully\n"); +} + +static void lp_core_init(void) +{ + /* Set LP core wakeup source as the HP CPU */ + ulp_lp_core_cfg_t cfg = { + .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER, + .lp_timer_sleep_duration_us = 1000000, + }; + + /* Load LP core firmware */ + ESP_ERROR_CHECK(ulp_lp_core_load_binary(lp_core_main_bin_start, (lp_core_main_bin_end - lp_core_main_bin_start))); + + /* Run LP core */ + ESP_ERROR_CHECK(ulp_lp_core_run(&cfg)); + + printf("LP core loaded with firmware and running successfully\n"); +} + +void app_main(void) +{ + /* LP ADC1 Init */ + ESP_ERROR_CHECK(lp_core_lp_adc_init(ADC_UNIT_1)); + + /* LP ADC1 channel config */ + const lp_core_lp_adc_chan_cfg_t config = { + .atten = EXAMPLE_ADC_ATTEN, + .bitwidth = ADC_BITWIDTH_DEFAULT, + }; + ESP_ERROR_CHECK(lp_core_lp_adc_config_channel(ADC_UNIT_1, EXAMPLE_LP_ADC1_CHAN0, &config)); + ESP_ERROR_CHECK(lp_core_lp_adc_config_channel(ADC_UNIT_1, EXAMPLE_LP_ADC1_CHAN1, &config)); + + /* Initialize LP_UART to print the ADC values to the LP core console */ + lp_uart_init(); + + /* Load LP Core binary and start the coprocessor */ + lp_core_init(); + + /* Enable ULP wakeup */ + ESP_ERROR_CHECK(esp_sleep_enable_ulp_wakeup()); + + /* Enter Deep Sleep */ + printf("Entering deep sleep...\n"); + esp_deep_sleep_start(); +} diff --git a/examples/system/ulp/lp_core/lp_adc/main/lp_core/main.c b/examples/system/ulp/lp_core/lp_adc/main/lp_core/main.c new file mode 100644 index 0000000000..67f85a58c9 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_adc/main/lp_core/main.c @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include "sdkconfig.h" +#include "ulp_lp_core_print.h" +#include "ulp_lp_core_lp_adc_shared.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 +int main (void) +{ + + int adc_raw_value[2]; + int adc_converted_value[2]; + + lp_core_lp_adc_read_channel_raw(ADC_UNIT_1, EXAMPLE_LP_ADC1_CHAN0, &adc_raw_value[0]); + lp_core_printf("lpadc1 chan0 raw value = %d\r\n", adc_raw_value[0]); + lp_core_lp_adc_read_channel_converted(ADC_UNIT_1, EXAMPLE_LP_ADC1_CHAN0, &adc_converted_value[0]); + lp_core_printf("lpadc1 chan0 converted value = %d mV\r\n", adc_converted_value[0]); + + lp_core_lp_adc_read_channel_raw(ADC_UNIT_1, EXAMPLE_LP_ADC1_CHAN1, &adc_raw_value[1]); + lp_core_printf("lpadc1 chan1 raw value = %d\r\n", adc_raw_value[1]); + lp_core_lp_adc_read_channel_converted(ADC_UNIT_1, EXAMPLE_LP_ADC1_CHAN1, &adc_converted_value[1]); + lp_core_printf("lpadc1 chan1 converted value = %d mV\r\n", adc_converted_value[1]); + + lp_core_printf("\n"); + + return 0; +} diff --git a/examples/system/ulp/lp_core/lp_adc/sdkconfig.defaults b/examples/system/ulp/lp_core/lp_adc/sdkconfig.defaults new file mode 100644 index 0000000000..d41662df2a --- /dev/null +++ b/examples/system/ulp/lp_core/lp_adc/sdkconfig.defaults @@ -0,0 +1,10 @@ +# Enable LP Core +CONFIG_ULP_COPROC_ENABLED=y +CONFIG_ULP_COPROC_TYPE_LP_CORE=y +CONFIG_ULP_COPROC_RESERVE_MEM=4096 + +# Set log level to Warning to produce clean output +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_BOOTLOADER_LOG_LEVEL=2 +CONFIG_LOG_DEFAULT_LEVEL_WARN=y +CONFIG_LOG_DEFAULT_LEVEL=2