Merge branch 'feat/lp_core_lp_adc_support' into 'master'

feat(lp_adc): Addded support for LP ADC for the LP core on esp32p4

Closes IDF-6875 and IDF-10205

See merge request espressif/esp-idf!33117
This commit is contained in:
Sudeep Mohanty 2024-09-11 17:16:40 +08:00
commit 1b6a829e81
24 changed files with 848 additions and 17 deletions

View File

@ -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;
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,
};

View File

@ -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

View File

@ -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
*/
@ -63,7 +63,15 @@ void adc_oneshot_hal_setup(adc_oneshot_hal_ctx_t *hal, adc_channel_t chan)
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);
}

View File

@ -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.

View File

@ -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);

View File

@ -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;
/**

View File

@ -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

View File

@ -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/////////////////////////////////////////////////////////////////
/**

View File

@ -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 */

View File

@ -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}

View File

@ -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})

View File

@ -0,0 +1,113 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#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

View File

@ -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 */

View File

@ -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()

View File

@ -0,0 +1,20 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#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;
}

View File

@ -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));
}

View File

@ -311,6 +311,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

View File

@ -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)

View File

@ -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
```

View File

@ -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}")

View File

@ -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

View File

@ -0,0 +1,76 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#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();
}

View File

@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdint.h>
#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;
}

View File

@ -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