Jelajahi Sumber

Merge branch 'feature/adc_digi_monitor_support' into 'master'

adc digi monitor support

Closes IDF-2530

See merge request espressif/esp-idf!22839
Wan Lei 2 tahun lalu
induk
melakukan
47fe2b9521

+ 3 - 0
components/esp_adc/CMakeLists.txt

@@ -10,6 +10,9 @@ set(srcs "adc_cali.c"
 
 if(CONFIG_SOC_ADC_DMA_SUPPORTED)
     list(APPEND srcs "adc_continuous.c")
+    if(CONFIG_SOC_ADC_MONITOR_SUPPORTED)
+        list(APPEND srcs "adc_monitor.c")
+    endif()
 endif()
 
 if(CONFIG_SOC_ADC_DIG_IIR_FILTER_SUPPORTED)

+ 22 - 1
components/esp_adc/adc_continuous_internal.h

@@ -24,6 +24,7 @@
 #endif
 
 #include "esp_adc/adc_filter.h"
+#include "esp_adc/adc_monitor.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -34,21 +35,38 @@ typedef enum {
     ADC_FSM_STARTED,
 } adc_fsm_t;
 
+typedef enum {
+    ADC_MONITOR_FSM_INIT,
+    ADC_MONITOR_FSM_ENABLED,
+} adc_monitor_fsm_t;
+
 /*---------------------------------------------------------------
             Driver Context
 ---------------------------------------------------------------*/
 typedef struct adc_iir_filter_t adc_iir_filter_t;
+typedef struct adc_monitor_t adc_monitor_t;
 typedef struct adc_continuous_ctx_t adc_continuous_ctx_t;
 
 /**
  * @brief ADC iir filter context
  */
 struct adc_iir_filter_t {
-    adc_digi_iir_filter_t filter_id;                            // Filter ID
+    adc_digi_iir_filter_t               filter_id;              // Filter ID
     adc_continuous_iir_filter_config_t  cfg;                    //filter configuration
     adc_continuous_ctx_t                *continuous_ctx;        //ADC continuous driver context
 };
 
+/**
+ * @brief ADC digi monitor context
+ */
+struct adc_monitor_t {
+    adc_monitor_id_t        monitor_id;         // monitor unit number
+    adc_monitor_fsm_t       fsm;                // monitor status indicator
+    adc_monitor_config_t    config;             // monitor configuration
+    adc_monitor_evt_cbs_t   cbs;                // monitor thresh callbacks
+    void                    *user_data;         // user data pointer to use in cb
+};
+
 /**
  * @brief ADC continuous driver context
  */
@@ -79,6 +97,9 @@ struct adc_continuous_ctx_t {
 #if SOC_ADC_DIG_IIR_FILTER_SUPPORTED
     adc_iir_filter_t                *iir_filter[SOC_ADC_DIGI_IIR_FILTER_NUM];  //ADC IIR filter context
 #endif
+#if SOC_ADC_MONITOR_SUPPORTED
+    adc_monitor_t                   *adc_monitor[SOC_ADC_DIGI_MONITOR_NUM];    // adc monitor context
+#endif
 };
 
 

+ 274 - 0
components/esp_adc/adc_monitor.c

@@ -0,0 +1,274 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdatomic.h>
+#include "esp_check.h"
+#include "esp_memory_utils.h"
+#include "esp_intr_alloc.h"
+#include "esp_heap_caps.h"
+#include "freertos/FreeRTOS.h"
+#include "adc_continuous_internal.h"
+#include "soc/periph_defs.h"
+#include "esp_adc/adc_monitor.h"
+
+static const char *MNTOR_TAG = "adc_monitor";
+
+/**
+ * @brief context for adc continuous driver
+ */
+typedef struct adc_monitor_platform_t {
+    adc_continuous_ctx_t    *continuous_ctx;     // ADC continuous driver context
+    intr_handle_t           monitor_intr_handle; // monitor intr handler
+    portMUX_TYPE            monitor_spinlock;    // spinlock
+} adc_monitor_platform_t;
+
+// Global context of adc monitor, other member will be lazy loaded
+static adc_monitor_platform_t s_adc_monitor_platform = {.monitor_spinlock = portMUX_INITIALIZER_UNLOCKED};
+
+
+#if CONFIG_IDF_TARGET_ESP32S2
+// Monitor unit index need equal to ADC unit index on ESP32S2
+static atomic_bool s_adc_monitor_claimed[SOC_ADC_DIGI_MONITOR_NUM] = {};
+static esp_err_t s_adc_monitor_claim(adc_continuous_handle_t handle, adc_monitor_t *monitor_ctx, adc_unit_t unit)
+{
+    assert(handle && monitor_ctx);
+    esp_err_t ret = ESP_ERR_NOT_FOUND;
+    bool false_var = false;
+    if (atomic_compare_exchange_strong(&s_adc_monitor_claimed[unit], &false_var, true)) {
+        monitor_ctx->monitor_id = unit;
+        handle->adc_monitor[unit] = monitor_ctx;
+        ret = ESP_OK;
+    } else {
+        ESP_LOGE(MNTOR_TAG, "monitor %d already in use", (int)unit);
+    }
+    return ret;
+}
+
+static esp_err_t s_adc_monitor_release(adc_monitor_t *monitor_ctx)
+{
+    assert(monitor_ctx);
+    esp_err_t ret = ESP_ERR_NOT_FOUND;
+    bool true_var = true;
+    if (atomic_compare_exchange_strong(&s_adc_monitor_claimed[monitor_ctx->monitor_id], &true_var, false)) {
+        s_adc_monitor_platform.continuous_ctx->adc_monitor[monitor_ctx->monitor_id] = NULL;
+        ret = ESP_OK;
+    }
+    return ret;
+}
+
+#else
+static esp_err_t s_adc_monitor_claim(adc_continuous_handle_t handle, adc_monitor_t *monitor_ctx, adc_unit_t unit)
+{
+    (void)unit;
+    assert(handle && monitor_ctx);
+    portENTER_CRITICAL(&s_adc_monitor_platform.monitor_spinlock);
+    for (int i = 0; i < SOC_ADC_DIGI_MONITOR_NUM; i++) {
+        if (!handle->adc_monitor[i]) {
+            monitor_ctx->monitor_id = i;
+            handle->adc_monitor[i] = monitor_ctx;
+            portEXIT_CRITICAL(&s_adc_monitor_platform.monitor_spinlock);
+            return ESP_OK;
+        }
+    }
+    portEXIT_CRITICAL(&s_adc_monitor_platform.monitor_spinlock);
+    ESP_LOGE(MNTOR_TAG, "no free monitor");
+    return ESP_ERR_NOT_FOUND;
+}
+
+static esp_err_t s_adc_monitor_release(adc_monitor_t *monitor_ctx)
+{
+    assert(monitor_ctx);
+    portENTER_CRITICAL(&s_adc_monitor_platform.monitor_spinlock);
+    s_adc_monitor_platform.continuous_ctx->adc_monitor[monitor_ctx->monitor_id] = NULL;
+    portEXIT_CRITICAL(&s_adc_monitor_platform.monitor_spinlock);
+
+    return ESP_OK;
+}
+#endif
+
+static void IRAM_ATTR s_adc_digi_monitor_isr(void *args)
+{
+    bool need_yield = false;
+    uint32_t intr_val = *((uint32_t *)adc_ll_digi_monitor_get_intr_status_addr());
+
+    // clear all intr flags as have save intr status in `intr_val`
+    adc_ll_digi_monitor_clear_intr();
+
+    for (uint8_t i = 0; i < SOC_ADC_DIGI_MONITOR_NUM; i++) {
+        adc_monitor_handle_t monitor_handle = s_adc_monitor_platform.continuous_ctx->adc_monitor[i];
+
+        // check if high threshold alert
+        if (intr_val & ADC_LL_GET_HIGH_THRES_MASK(i)) {
+            assert(monitor_handle);
+            assert(monitor_handle->monitor_id == i);
+
+            if (monitor_handle->cbs.on_over_high_thresh) {
+                adc_monitor_evt_data_t event_data = {};
+                need_yield |= monitor_handle->cbs.on_over_high_thresh(monitor_handle, &event_data, monitor_handle->user_data);
+            }
+        }
+        // check if low threshold alert
+        if (intr_val & ADC_LL_GET_LOW_THRES_MASK(i)) {
+            assert(monitor_handle);
+            assert(monitor_handle->monitor_id == i);
+
+            if (monitor_handle->cbs.on_below_low_thresh) {
+                adc_monitor_evt_data_t event_data = {};
+                need_yield |= monitor_handle->cbs.on_below_low_thresh(monitor_handle, &event_data, monitor_handle->user_data);
+            }
+        }
+    }
+    if (need_yield) {
+        portYIELD_FROM_ISR();
+    }
+}
+
+static esp_err_t adc_monitor_intr_alloc(void)
+{
+    esp_err_t ret = ESP_OK;
+    int intr_flags = ESP_INTR_FLAG_LOWMED;
+#if CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE
+    intr_flags |= ESP_INTR_FLAG_IRAM;
+#endif
+
+#if SOC_ADC_TEMPERATURE_SHARE_INTR
+    intr_flags |= ESP_INTR_FLAG_SHARED;
+    ret = esp_intr_alloc_intrstatus(ETS_APB_ADC_INTR_SOURCE, intr_flags,
+                                    (uint32_t)adc_ll_digi_monitor_get_intr_status_addr(),
+                                    ADC_LL_THRES_ALL_INTR_ST_M, s_adc_digi_monitor_isr, NULL, &s_adc_monitor_platform.monitor_intr_handle);
+#else
+
+    ret = esp_intr_alloc(ETS_APB_ADC_INTR_SOURCE, intr_flags, s_adc_digi_monitor_isr, NULL, &s_adc_monitor_platform.monitor_intr_handle);
+#endif  //SOC_ADC_TEMPERATURE_SHARE_INTR
+    return ret;
+}
+
+//-------------------------------------------PUBLIC APIs--------------------------------------------//
+esp_err_t adc_new_continuous_monitor(adc_continuous_handle_t handle, const adc_monitor_config_t *monitor_cfg, adc_monitor_handle_t *ret_handle)
+{
+    esp_err_t ret;
+    ESP_RETURN_ON_FALSE(handle && monitor_cfg && ret_handle, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer");
+    ESP_RETURN_ON_FALSE(monitor_cfg->adc_unit < SOC_ADC_PERIPH_NUM, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid adc_unit");
+    ESP_RETURN_ON_FALSE(handle->fsm == ADC_FSM_INIT, ESP_ERR_INVALID_STATE, MNTOR_TAG, "ADC continuous driver should be in init state");
+#if CONFIG_IDF_TARGET_ESP32S2
+    ESP_RETURN_ON_FALSE(!((monitor_cfg->h_threshold >= 0) && (monitor_cfg->l_threshold >= 0)), ESP_ERR_NOT_SUPPORTED, MNTOR_TAG, "ESP32S2 support only one threshold");
+#endif
+
+    // alloc handler memory
+    adc_monitor_t *monitor_ctx = heap_caps_calloc(1, sizeof(adc_monitor_t), MALLOC_CAP_INTERNAL);
+    ESP_RETURN_ON_FALSE(monitor_ctx, ESP_ERR_NO_MEM, MNTOR_TAG, "no mem");
+
+    // alloc monitor hardware
+    ESP_GOTO_ON_ERROR(s_adc_monitor_claim(handle, monitor_ctx, monitor_cfg->adc_unit), claim_err, MNTOR_TAG, "ADC monitor claim failed");
+    memcpy(&monitor_ctx->config, monitor_cfg, sizeof(adc_monitor_config_t));
+    s_adc_monitor_platform.continuous_ctx = handle;
+
+    // alloc cpu intr
+    portENTER_CRITICAL(&s_adc_monitor_platform.monitor_spinlock);
+    bool alloc_intr = !s_adc_monitor_platform.monitor_intr_handle;
+    portEXIT_CRITICAL(&s_adc_monitor_platform.monitor_spinlock);
+    if (alloc_intr) {
+        ESP_GOTO_ON_ERROR(adc_monitor_intr_alloc(), intr_err, MNTOR_TAG, "esp intr alloc failed");
+    }
+
+    // config hardware
+    adc_ll_digi_monitor_clear_intr();
+    adc_ll_digi_monitor_set_thres(monitor_ctx->monitor_id, monitor_ctx->config.adc_unit, monitor_ctx->config.channel, monitor_ctx->config.h_threshold, monitor_ctx->config.l_threshold);
+
+    *ret_handle = monitor_ctx;
+    return ESP_OK;
+
+intr_err:
+    s_adc_monitor_release(monitor_ctx);
+claim_err:
+    free(monitor_ctx);
+    return ret;
+}
+
+esp_err_t adc_continuous_monitor_register_event_callbacks(adc_monitor_handle_t monitor_handle, const adc_monitor_evt_cbs_t *cbs, void *user_data)
+{
+    ESP_RETURN_ON_FALSE(monitor_handle && cbs, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer");
+    ESP_RETURN_ON_FALSE(monitor_handle->fsm == ADC_MONITOR_FSM_INIT, ESP_ERR_INVALID_STATE, MNTOR_TAG, "monitor should be in init state");
+    ESP_RETURN_ON_FALSE(!(monitor_handle->cbs.on_over_high_thresh || monitor_handle->cbs.on_below_low_thresh), ESP_ERR_INVALID_STATE, MNTOR_TAG, "callbacks had beed registered");
+#if CONFIG_IDF_TARGET_ESP32S2
+    ESP_RETURN_ON_FALSE(!(cbs->on_below_low_thresh && cbs->on_over_high_thresh), ESP_ERR_NOT_SUPPORTED, MNTOR_TAG, "ESP32S2 support only one threshold");
+#endif
+
+    // If iram_safe enabled, check if user_data and cbs is iram_safe
+#if CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE
+    if (cbs->on_over_high_thresh) {
+        ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_over_high_thresh), ESP_ERR_INVALID_ARG, MNTOR_TAG, "on_over_high_thresh func not in iram");
+    }
+    if (cbs->on_below_low_thresh) {
+        ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_below_low_thresh), ESP_ERR_INVALID_ARG, MNTOR_TAG, "on_below_low_thresh func not in iram");
+    }
+    if (user_data) {
+        ESP_RETURN_ON_FALSE(esp_ptr_in_dram(user_data) || esp_ptr_in_diram_dram(user_data), ESP_ERR_INVALID_ARG, MNTOR_TAG, "user_data not in iram");
+    }
+#endif
+
+    memcpy(&monitor_handle->cbs, cbs, sizeof(adc_monitor_evt_cbs_t));
+    monitor_handle->user_data = user_data;
+    return ESP_OK;
+}
+
+esp_err_t adc_continuous_monitor_enable(adc_monitor_handle_t monitor_handle)
+{
+    ESP_RETURN_ON_FALSE(monitor_handle, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer");
+    ESP_RETURN_ON_FALSE(monitor_handle->fsm == ADC_MONITOR_FSM_INIT, ESP_ERR_INVALID_STATE, MNTOR_TAG, "monitor should be in init state");
+
+    // enable peripheral intr_ena
+    if ((monitor_handle->config.h_threshold >= 0)) {
+        adc_ll_digi_monitor_enable_intr(monitor_handle->monitor_id, ADC_MONITOR_MODE_HIGH, true);
+    }
+    if ((monitor_handle->config.l_threshold >= 0)) {
+        adc_ll_digi_monitor_enable_intr(monitor_handle->monitor_id, ADC_MONITOR_MODE_LOW, true);
+    }
+
+    adc_ll_digi_monitor_user_start(monitor_handle->monitor_id, true);
+    monitor_handle->fsm = ADC_MONITOR_FSM_ENABLED;
+    return esp_intr_enable(s_adc_monitor_platform.monitor_intr_handle);
+}
+
+esp_err_t adc_continuous_monitor_disable(adc_monitor_handle_t monitor_handle)
+{
+    ESP_RETURN_ON_FALSE(monitor_handle, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer");
+    ESP_RETURN_ON_FALSE(monitor_handle->fsm == ADC_MONITOR_FSM_ENABLED, ESP_ERR_INVALID_STATE, MNTOR_TAG, "monitor not in running");
+
+    // disable peripheral intr_ena
+    if ((monitor_handle->config.h_threshold >= 0)) {
+        adc_ll_digi_monitor_enable_intr(monitor_handle->monitor_id, ADC_MONITOR_MODE_HIGH, false);
+    }
+    if ((monitor_handle->config.l_threshold >= 0)) {
+        adc_ll_digi_monitor_enable_intr(monitor_handle->monitor_id, ADC_MONITOR_MODE_LOW, false);
+    }
+
+    adc_ll_digi_monitor_user_start(monitor_handle->monitor_id, false);
+    monitor_handle->fsm = ADC_MONITOR_FSM_INIT;
+    return esp_intr_disable(s_adc_monitor_platform.monitor_intr_handle);
+}
+
+esp_err_t adc_del_continuous_monitor(adc_monitor_handle_t monitor_handle)
+{
+    ESP_RETURN_ON_FALSE(monitor_handle, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer");
+    ESP_RETURN_ON_FALSE((monitor_handle->fsm == ADC_MONITOR_FSM_INIT) && (s_adc_monitor_platform.continuous_ctx->fsm == ADC_FSM_INIT), \
+                        ESP_ERR_INVALID_STATE, MNTOR_TAG, "monitor and ADC continuous driver should all be in init state");
+    ESP_RETURN_ON_ERROR(s_adc_monitor_release(monitor_handle), MNTOR_TAG, "monitor not find or isn't in use");
+
+    for (int i = 0; i < SOC_ADC_DIGI_MONITOR_NUM; i++) {
+        if (s_adc_monitor_platform.continuous_ctx->adc_monitor[i]) {
+            // If any other monitor not freed, then delete self and exit now. del_monitor is complete
+            free(monitor_handle);
+            return ESP_OK;
+        }
+    }
+
+    // If no monitor is using, the release intr handle as well
+    ESP_RETURN_ON_ERROR(esp_intr_free(s_adc_monitor_platform.monitor_intr_handle), MNTOR_TAG, "esp intr release failed\n");
+    s_adc_monitor_platform.monitor_intr_handle = NULL;
+    free(monitor_handle);
+    return ESP_OK;
+}

+ 123 - 0
components/esp_adc/include/esp_adc/adc_monitor.h

@@ -0,0 +1,123 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+
+#include "adc_continuous.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Type of adc digi monitor handle
+ */
+typedef struct adc_monitor_t *adc_monitor_handle_t;
+
+/**
+ * @brief ADC digital controller (DMA mode) monitor configuration.
+ */
+typedef struct {
+    adc_unit_t adc_unit;            /*!<Set adc unit number for monitor. */
+    adc_channel_t channel;          /*!<Set adc channel number for monitor. */
+    int32_t h_threshold;            /*!<Set monitor high threshold of adc digital controller, -1 if not used. */
+    int32_t l_threshold;            /*!<Set monitor low threshold of adc digital controller, -1 if not used. */
+} adc_monitor_config_t;
+
+/**
+ * @brief Type of adc monitor event data
+ */
+typedef struct adc_monitor_evt_data {
+    // reserved for extensibility
+} adc_monitor_evt_data_t;
+
+/**
+ * @brief ADC digital controller (DMA mode) monitor callback type.
+ */
+typedef bool (*adc_monitor_evt_cb_t)(adc_monitor_handle_t monitor_handle, const adc_monitor_evt_data_t *event_data, void *user_data);
+
+/**
+ * @brief Struct type of many different adc_monitor evt callbacks.
+ */
+typedef struct {
+    adc_monitor_evt_cb_t on_over_high_thresh;           /*!< adc_monitor high value interrupt callback */
+    adc_monitor_evt_cb_t on_below_low_thresh;           /*!< adc_monitor low value interrupt callback */
+} adc_monitor_evt_cbs_t;
+
+
+/**
+ * @brief Allocate an ADC continuous mode monitor (and configure it into an initial state)
+ *
+ * @param[in]  handle           ADC continuous mode driver handle
+ * @param[in]  monitor_cfg      ADC monitor config struct
+ * @param[out] ret_handle       Handle of allocated monitor
+ *
+ * @return
+ *       - ESP_OK:                  On success
+ *       - ESP_ERR_INVALID_ARG:     Invalid argument
+ *       - ESP_ERR_INVALID_STATE:  Install monitor when ADC converter is running
+ *       - ESP_ERR_NOT_FOUND:      No appropriate monitor Or no free monitor
+ *       - ESP_ERR_NOT_SUPPORTED:   Threshold configuration not supported
+ *       - ESP_ERR_NO_MEM:          Free memory not enough
+ */
+esp_err_t adc_new_continuous_monitor(adc_continuous_handle_t handle, const adc_monitor_config_t *monitor_cfg, adc_monitor_handle_t *ret_handle);
+
+/**
+ * @brief Register threshold interrupt callbacks for allocated monitor.
+ *
+ * @param[in]  monitor_handle       Monitor handle
+ * @param[in]  cbs                  Pointer to a adc_monitor_evt_cbs_t struct
+ * @param[in]  user_data            User data, which will be delivered to the callback functions directly
+ *
+ * @return
+ *       - ESP_OK:                  On success
+ *       - ESP_ERR_INVALID_STATE:   To register cbs when monitor is running, or cbs has been installed
+ *       - ESP_ERR_NOT_SUPPORTED:   Register not supported callbacks to esp32s2
+ *       - ESP_ERR_INVALID_ARG:     Invalid argument
+ */
+esp_err_t adc_continuous_monitor_register_event_callbacks(adc_monitor_handle_t monitor_handle, const adc_monitor_evt_cbs_t *cbs, void *user_data);
+
+/**
+ * @brief Enable an ADC continuous mode monitor.
+ *
+ * @param[in]  monitor_handle       Monitor handle
+ *
+ * @return
+ *       - ESP_OK:                  On success
+ *       - ESP_ERR_INVALID_STATE:   Monitor has enabled, no need to enable again
+ *       - ESP_ERR_INVALID_ARG:     Invalid argument
+ */
+esp_err_t adc_continuous_monitor_enable(adc_monitor_handle_t monitor_handle);
+
+/**
+ * @brief Disable an ADC continuous mode monitor.
+ *
+ * @param[in]  monitor_handle       Monitor handle
+ *
+ * @return
+ *       - ESP_OK:                  On success
+ *       - ESP_ERR_INVALID_STATE:   Monitor not enabled, no need to disable
+ *       - ESP_ERR_INVALID_ARG:     Invalid argument
+ */
+esp_err_t adc_continuous_monitor_disable(adc_monitor_handle_t monitor_handle);
+
+/**
+ * @brief Free an ADC continuous mode monitor.
+ *
+ * @param[in]  monitor_handle       Monitor handle
+ *
+ * @return
+ *       - ESP_OK:                  On success
+ *       - ESP_ERR_INVALID_STATE:   Monitor is in enabled state, should call `adc_continuous_monitor_disable` first
+ *       - ESP_ERR_NOT_FOUND:       Monitor haven't been used
+ *       - ESP_ERR_INVALID_ARG:     Invalid argument
+ */
+esp_err_t adc_del_continuous_monitor(adc_monitor_handle_t monitor_handle);
+
+#ifdef __cplusplus
+}
+#endif

+ 215 - 0
components/esp_adc/test_apps/adc/main/test_adc.c

@@ -7,8 +7,11 @@
 #include <stdlib.h>
 #include <string.h>
 #include "esp_log.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
 #include "soc/adc_periph.h"
 #include "esp_adc/adc_oneshot.h"
+#include "esp_adc/adc_monitor.h"
 #include "driver/gpio.h"
 #include "driver/rtc_io.h"
 #include "test_common_adc.h"
@@ -263,3 +266,215 @@ TEST_CASE("test ADC2 Single Read with Light Sleep", "[adc][manul][ignore]")
 #endif  //#if (SOC_ADC_PERIPH_NUM >= 2) && !CONFIG_IDF_TARGET_ESP32C3
 
 #endif  //#if SOC_ADC_CALIBRATION_V1_SUPPORTED
+
+
+#if SOC_ADC_MONITOR_SUPPORTED && CONFIG_SOC_ADC_DMA_SUPPORTED
+#if CONFIG_IDF_TARGET_ESP32S2
+#define TEST_ADC_FORMATE_TYPE   ADC_DIGI_OUTPUT_FORMAT_TYPE1
+#else
+#define TEST_ADC_FORMATE_TYPE   ADC_DIGI_OUTPUT_FORMAT_TYPE2
+#endif
+bool IRAM_ATTR test_high_cb(adc_monitor_handle_t monitor_handle, const adc_monitor_evt_data_t *event_data, void *user_data){
+    return false;
+}
+TEST_CASE("ADC continuous monitor init_deinit", "[adc]")
+{
+    adc_continuous_handle_t handle = NULL;
+    adc_continuous_handle_cfg_t adc_config = {
+        .max_store_buf_size = 1024,
+        .conv_frame_size = SOC_ADC_DIGI_DATA_BYTES_PER_CONV * 2,
+    };
+    TEST_ESP_OK(adc_continuous_new_handle(&adc_config, &handle));
+
+    adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0};
+    for (int i = 0; i < 1; i++) {
+        adc_pattern[i].atten = ADC_ATTEN_DB_11;
+        adc_pattern[i].channel = i;
+        adc_pattern[i].unit = ADC_UNIT_1;
+        adc_pattern[i].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH;
+    }
+    adc_continuous_config_t dig_cfg = {
+        .pattern_num = 1,
+        .adc_pattern = adc_pattern,
+        .sample_freq_hz = SOC_ADC_SAMPLE_FREQ_THRES_LOW,
+        .conv_mode = ADC_CONV_SINGLE_UNIT_1,
+        .format = TEST_ADC_FORMATE_TYPE,
+    };
+    TEST_ESP_OK(adc_continuous_config(handle, &dig_cfg));
+
+    //try to enable without installed
+    adc_monitor_handle_t monitor_handle = NULL;
+    TEST_ESP_ERR(ESP_ERR_INVALID_ARG, adc_continuous_monitor_enable(monitor_handle));
+
+    //try to install with invalid argument
+    adc_monitor_config_t adc_monitor_cfg = {
+        .adc_unit = 2,
+        .channel = 2,
+        .h_threshold = 3000,
+        .l_threshold = -1,
+    };
+    TEST_ESP_ERR(ESP_ERR_INVALID_ARG, adc_new_continuous_monitor(handle, &adc_monitor_cfg, &monitor_handle));
+
+    //try to install when adc is running
+    adc_monitor_cfg.adc_unit = ADC_UNIT_1;
+    TEST_ESP_OK(adc_continuous_start(handle));
+    TEST_ESP_ERR(ESP_ERR_INVALID_STATE, adc_new_continuous_monitor(handle, &adc_monitor_cfg, &monitor_handle));
+    TEST_ESP_OK(adc_continuous_stop(handle));
+
+    //normal install
+    TEST_ESP_OK(adc_new_continuous_monitor(handle, &adc_monitor_cfg, &monitor_handle));
+
+    //try register callback funcs when monitor is running
+    adc_monitor_evt_cbs_t monitor_cb = {
+        .on_over_high_thresh = test_high_cb,
+        .on_below_low_thresh = NULL,
+    };
+    TEST_ESP_OK(adc_continuous_monitor_enable(monitor_handle));
+    TEST_ESP_ERR(ESP_ERR_INVALID_STATE, adc_continuous_monitor_register_event_callbacks(monitor_handle, &monitor_cb, NULL));
+    TEST_ESP_OK(adc_continuous_monitor_disable(monitor_handle));
+
+    //normal register cbs
+    TEST_ESP_OK(adc_continuous_monitor_register_event_callbacks(monitor_handle, &monitor_cb, NULL));
+
+    //try init so many monitor, we totally have 2 monitors actually
+    adc_monitor_handle_t monitor_handle_2 = NULL, monitor_handle_3 = NULL;
+#if CONFIG_IDF_TARGET_ESP32S2
+    adc_monitor_cfg.adc_unit = ADC_UNIT_2;  //s2 can't use two monitor on same ADC unit
+#endif
+    TEST_ESP_OK(adc_new_continuous_monitor(handle, &adc_monitor_cfg, &monitor_handle_2));
+    TEST_ESP_ERR(ESP_ERR_NOT_FOUND, adc_new_continuous_monitor(handle, &adc_monitor_cfg, &monitor_handle_3));
+
+    //try delete them, as monitor_handle_3 should be NULL because it should init failed
+    TEST_ESP_OK(adc_del_continuous_monitor(monitor_handle_2));
+    TEST_ESP_ERR(ESP_ERR_INVALID_ARG, adc_del_continuous_monitor(monitor_handle_3));
+
+    //try register cbs again
+    TEST_ESP_ERR(ESP_ERR_INVALID_STATE, adc_continuous_monitor_register_event_callbacks(monitor_handle, &monitor_cb, &monitor_cb));
+
+    //try delete it when adc is running but monitor not running
+    TEST_ESP_OK(adc_continuous_start(handle));
+    TEST_ESP_ERR(ESP_ERR_INVALID_STATE, adc_del_continuous_monitor(monitor_handle));
+    TEST_ESP_OK(adc_continuous_stop(handle));
+
+    //normal option
+    TEST_ESP_OK(adc_continuous_monitor_enable(monitor_handle));
+    TEST_ESP_OK(adc_continuous_monitor_disable(monitor_handle));
+
+    //normal uninstall
+    TEST_ESP_OK(adc_del_continuous_monitor(monitor_handle));
+
+    TEST_ESP_OK(adc_continuous_deinit(handle));
+}
+
+
+/**
+ * NOTE: To run this special feature test case, you need wire ADC channel pin you want to monit
+ *       to a wave output pin defined below.
+ *
+ *       +---------+
+ *       |         |
+ *       |    (adc)|------------+
+ *       |         |            |
+ *       |   (wave)|------------+
+ *       |         |
+ *       |  ESP32  |
+ *       +---------+
+ *
+ *       or you can connect your signals from signal generator to ESP32 pin which you monitoring
+ **/
+#define TEST_ADC_CHANNEL    ADC_CHANNEL_0   //GPIO_1
+#define TEST_WAVE_OUT_PIN   GPIO_NUM_2      //GPIO_2
+static uint32_t m1h_cnt, m1l_cnt;
+
+bool IRAM_ATTR m1h_cb(adc_monitor_handle_t monitor_handle, const adc_monitor_evt_data_t *event_data, void *user_data){
+    m1h_cnt ++;
+    return false;
+}
+bool IRAM_ATTR m1l_cb(adc_monitor_handle_t monitor_handle, const adc_monitor_evt_data_t *event_data, void *user_data){
+    m1l_cnt ++;
+    return false;
+}
+TEST_CASE("ADC continuous monitor functionary", "[adc][manual][ignore]")
+{
+    adc_continuous_handle_t handle = NULL;
+    adc_continuous_handle_cfg_t adc_config = {
+        .max_store_buf_size = 1024,
+        .conv_frame_size = SOC_ADC_DIGI_DATA_BYTES_PER_CONV * 2,
+    };
+    TEST_ESP_OK(adc_continuous_new_handle(&adc_config, &handle));
+
+    adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0};
+    for (int i = 0; i < 2; i++) {
+        adc_pattern[i].atten = ADC_ATTEN_DB_11;
+        adc_pattern[i].channel = TEST_ADC_CHANNEL;
+        adc_pattern[i].unit = ADC_UNIT_1;
+        adc_pattern[i].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH;
+    }
+    adc_continuous_config_t dig_cfg = {
+        .pattern_num = 2,
+        .adc_pattern = adc_pattern,
+        .sample_freq_hz = SOC_ADC_SAMPLE_FREQ_THRES_LOW,
+        .conv_mode = ADC_CONV_SINGLE_UNIT_1,
+        .format = TEST_ADC_FORMATE_TYPE,
+    };
+    TEST_ESP_OK(adc_continuous_config(handle, &dig_cfg));
+
+    //config monitor
+    adc_monitor_handle_t monitor_handle;
+    adc_monitor_config_t adc_monitor_cfg = {
+        .adc_unit = ADC_UNIT_1,
+        .channel = TEST_ADC_CHANNEL,
+#if CONFIG_IDF_TARGET_ESP32S2
+        .h_threshold = -1,      //S2 support only one threshold for one monitor
+#else
+        .h_threshold = 3000,
+#endif
+        .l_threshold = 1000,
+    };
+    adc_monitor_evt_cbs_t monitor_cb = {
+#if !CONFIG_IDF_TARGET_ESP32S2
+        .on_over_high_thresh = m1h_cb,
+#endif
+        .on_below_low_thresh = m1l_cb,
+    };
+    TEST_ESP_OK(adc_new_continuous_monitor(handle, &adc_monitor_cfg, &monitor_handle));
+    TEST_ESP_OK(adc_continuous_monitor_register_event_callbacks(monitor_handle, &monitor_cb, NULL));
+
+    //config a pin to generate wave
+    gpio_config_t gpio_cfg = {
+        .pin_bit_mask = (1ULL << TEST_WAVE_OUT_PIN),
+        .mode = GPIO_MODE_INPUT_OUTPUT,
+        .pull_up_en = GPIO_PULLDOWN_ENABLE,
+    };
+    TEST_ESP_OK(gpio_config(&gpio_cfg));
+
+    TEST_ESP_OK(adc_continuous_monitor_enable(monitor_handle));
+    TEST_ESP_OK(adc_continuous_start(handle));
+
+    for (uint8_t i=0; i<8; i++)
+    {
+        vTaskDelay(1000);
+
+        // check monitor cb
+        printf("%d\t high_cnt %4ld\tlow_cnt %4ld\n", i, m1h_cnt, m1l_cnt);
+        if (gpio_get_level(TEST_WAVE_OUT_PIN)) {
+#if !CONFIG_IDF_TARGET_ESP32S2
+            // TEST_ASSERT_UINT32_WITHIN(SOC_ADC_SAMPLE_FREQ_THRES_LOW*0.1, SOC_ADC_SAMPLE_FREQ_THRES_LOW, m1h_cnt);
+            // TEST_ASSERT_LESS_THAN_UINT32(5, m1l_cnt);   //Actually, it will still encountered 1~2 times because hardware run very quickly
+#endif
+            m1h_cnt = 0;
+            gpio_set_level(TEST_WAVE_OUT_PIN, 0);
+        } else {
+            TEST_ASSERT_UINT32_WITHIN(SOC_ADC_SAMPLE_FREQ_THRES_LOW*0.1, SOC_ADC_SAMPLE_FREQ_THRES_LOW, m1l_cnt);
+            TEST_ASSERT_LESS_THAN_UINT32(5, m1h_cnt);   //Actually, it will still encountered 1~2 times because hardware run very quickly
+            m1l_cnt = 0;
+            gpio_set_level(TEST_WAVE_OUT_PIN, 1);
+        }
+    }
+    TEST_ESP_OK(adc_continuous_stop(handle));
+    TEST_ESP_OK(adc_continuous_monitor_disable(monitor_handle));
+    TEST_ESP_OK(adc_del_continuous_monitor(monitor_handle));
+    TEST_ESP_OK(adc_continuous_deinit(handle));
+}
+
+#endif  //SOC_ADC_MONITOR_SUPPORTED && CONFIG_SOC_ADC_DMA_SUPPORTED

+ 16 - 19
components/esp_adc/test_apps/adc/main/test_app_main.c

@@ -1,40 +1,37 @@
 /*
- * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: CC0-1.0
  */
 
 #include "unity.h"
-#include "unity_test_runner.h"
+#include "unity_test_utils.h"
 #include "esp_heap_caps.h"
 
-#define TEST_MEMORY_LEAK_THRESHOLD (-600)
-
-static size_t before_free_8bit;
-static size_t before_free_32bit;
-
-static void check_leak(size_t before_free, size_t after_free, const char *type)
-{
-    ssize_t delta = after_free - before_free;
-    printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
-    TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
-}
+#define TEST_MEMORY_LEAK_THRESHOLD (600)
 
 void setUp(void)
 {
-    before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
-    before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+    unity_utils_record_free_mem();
 }
 
 void tearDown(void)
 {
-    size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
-    size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
-    check_leak(before_free_8bit, after_free_8bit, "8BIT");
-    check_leak(before_free_32bit, after_free_32bit, "32BIT");
+    unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
 }
 
 void app_main(void)
 {
+    //  _____         _        _    ____   ____
+    // |_   _|__  ___| |_     / \  |  _ \ / ___|
+    //   | |/ _ \/ __| __|   / _ \ | | | | |
+    //   | |  __/\__ \ |_   / ___ \| |_| | |___
+    //   |_|\___||___/\__| /_/   \_\____/ \____|
+
+    printf(" _____         _        _    ____   ____ \n");
+    printf("|_   _|__  ___| |_     / \\  |  _ \\ / ___|\n");
+    printf("  | |/ _ \\/ __| __|   / _ \\ | | | | |    \n");
+    printf("  | |  __/\\__ \\ |_   / ___ \\| |_| | |___ \n");
+    printf("  |_|\\___||___/\\__| /_/   \\_\\____/ \\____|\n");
     unity_run_menu();
 }

+ 0 - 36
components/hal/esp32c2/include/hal/adc_ll.h

@@ -247,42 +247,6 @@ static inline void adc_ll_digi_filter_enable(adc_digi_iir_filter_t idx, adc_unit
     //nothing to do to enable, after adc_ll_digi_filter_set_factor, it's enabled.
 }
 
-/**
- * Set monitor mode of adc digital controller.
- *
- * @note If the channel info is not supported, the monitor function will not be enabled.
- * @param adc_n ADC unit.
- * @param is_larger true:  If ADC_OUT >  threshold, Generates monitor interrupt.
- *                  false: If ADC_OUT <  threshold, Generates monitor interrupt.
- */
-static inline void adc_ll_digi_monitor_set_mode(adc_digi_monitor_idx_t idx, adc_digi_monitor_t *cfg)
-{
-    if (idx == ADC_DIGI_MONITOR_IDX0) {
-        APB_SARADC.saradc_thres0_ctrl.saradc_thres0_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7);
-        APB_SARADC.saradc_thres0_ctrl.saradc_thres0_high = cfg->h_threshold;
-        APB_SARADC.saradc_thres0_ctrl.saradc_thres0_low = cfg->l_threshold;
-    } else { // ADC_DIGI_MONITOR_IDX1
-        APB_SARADC.saradc_thres1_ctrl.saradc_thres1_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7);
-        APB_SARADC.saradc_thres1_ctrl.saradc_thres1_high = cfg->h_threshold;
-        APB_SARADC.saradc_thres1_ctrl.saradc_thres1_low = cfg->l_threshold;
-    }
-}
-
-/**
- * Enable/disable monitor of adc digital controller.
- *
- * @note If the channel info is not supported, the monitor function will not be enabled.
- * @param adc_n ADC unit.
- */
-static inline void adc_ll_digi_monitor_disable(adc_digi_monitor_idx_t idx)
-{
-    if (idx == ADC_DIGI_MONITOR_IDX0) {
-        APB_SARADC.saradc_thres0_ctrl.saradc_thres0_channel = 0xF;
-    } else { // ADC_DIGI_MONITOR_IDX1
-        APB_SARADC.saradc_thres1_ctrl.saradc_thres1_channel = 0xF;
-    }
-}
-
 /**
  * Reset adc digital controller.
  */

+ 77 - 28
components/hal/esp32c3/include/hal/adc_ll.h

@@ -29,10 +29,13 @@ extern "C" {
 
 #define ADC_LL_EVENT_ADC1_ONESHOT_DONE    BIT(31)
 #define ADC_LL_EVENT_ADC2_ONESHOT_DONE    BIT(30)
-#define ADC_LL_EVENT_THRES0_HIGH          BIT(29)
-#define ADC_LL_EVENT_THRES1_HIGH          BIT(28)
-#define ADC_LL_EVENT_THRES0_LOW           BIT(27)
-#define ADC_LL_EVENT_THRES1_LOW           BIT(26)
+
+#define ADC_LL_THRES_ALL_INTR_ST_M  (APB_SARADC_THRES0_HIGH_INT_ST_M | \
+                                     APB_SARADC_THRES1_HIGH_INT_ST_M | \
+                                     APB_SARADC_THRES0_LOW_INT_ST_M  | \
+                                     APB_SARADC_THRES1_LOW_INT_ST_M)
+#define ADC_LL_GET_HIGH_THRES_MASK(monitor_id)    ((monitor_id == 0) ? APB_SARADC_THRES0_HIGH_INT_ST_M : APB_SARADC_THRES1_HIGH_INT_ST_M)
+#define ADC_LL_GET_LOW_THRES_MASK(monitor_id)     ((monitor_id == 0) ? APB_SARADC_THRES0_LOW_INT_ST_M : APB_SARADC_THRES1_LOW_INT_ST_M)
 
 /*---------------------------------------------------------------
                     Oneshot
@@ -391,39 +394,85 @@ static inline void adc_ll_digi_filter_enable(adc_digi_iir_filter_t idx, adc_unit
 }
 
 /**
- * Set monitor mode of adc digital controller.
+ * Set monitor threshold of adc digital controller on specific channel.
  *
- * @note If the channel info is not supported, the monitor function will not be enabled.
- * @param adc_n ADC unit.
- * @param is_larger true:  If ADC_OUT >  threshold, Generates monitor interrupt.
- *                  false: If ADC_OUT <  threshold, Generates monitor interrupt.
- */
-static inline void adc_ll_digi_monitor_set_mode(adc_digi_monitor_idx_t idx, adc_digi_monitor_t *cfg)
-{
-    if (idx == ADC_DIGI_MONITOR_IDX0) {
-        APB_SARADC.thres0_ctrl.thres0_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7);
-        APB_SARADC.thres0_ctrl.thres0_high = cfg->h_threshold;
-        APB_SARADC.thres0_ctrl.thres0_low = cfg->l_threshold;
-    } else { // ADC_DIGI_MONITOR_IDX1
-        APB_SARADC.thres1_ctrl.thres1_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7);
-        APB_SARADC.thres1_ctrl.thres1_high = cfg->h_threshold;
-        APB_SARADC.thres1_ctrl.thres1_low = cfg->l_threshold;
+ * @param monitor_id ADC digi monitor unit index.
+ * @param adc_n      Which adc unit the channel belong to.
+ * @param channel    Which channel of adc want to be monitored.
+ * @param h_thresh   High threshold of this monitor.
+ * @param l_thresh   Low threshold of this monitor.
+ */
+static inline void adc_ll_digi_monitor_set_thres(adc_monitor_id_t monitor_id, adc_unit_t adc_n, uint8_t channel, int32_t h_thresh, int32_t l_thresh)
+{
+    if (monitor_id == ADC_MONITOR_0) {
+        APB_SARADC.thres0_ctrl.thres0_channel = (adc_n << 3) | (channel & 0x7);
+        APB_SARADC.thres0_ctrl.thres0_high = h_thresh;
+        APB_SARADC.thres0_ctrl.thres0_low = l_thresh;
+    } else { // ADC_MONITOR_1
+        APB_SARADC.thres1_ctrl.thres1_channel = (adc_n << 3) | (channel & 0x7);
+        APB_SARADC.thres1_ctrl.thres1_high = h_thresh;
+        APB_SARADC.thres1_ctrl.thres1_low = l_thresh;
     }
 }
 
 /**
- * Enable/disable monitor of adc digital controller.
+ * Start/Stop monitor of adc digital controller.
  *
- * @note If the channel info is not supported, the monitor function will not be enabled.
- * @param adc_n ADC unit.
+ * @param monitor_id ADC digi monitor unit index.
+ * @param start 1 for start, 0 for stop
+ */
+static inline void adc_ll_digi_monitor_user_start(adc_monitor_id_t monitor_id, bool start)
+{
+    if (monitor_id == ADC_MONITOR_0) {
+        APB_SARADC.thres_ctrl.thres0_en = start;
+    } else {
+        APB_SARADC.thres_ctrl.thres1_en = start;
+    }
+}
+
+/**
+ * Enable/disable a intr of adc digital monitor.
+ *
+ * @param monitor_id ADC digi monitor unit index.
+ * @param mode monit mode to enable/disable intr.
+ * @param enable enable or disable.
  */
-static inline void adc_ll_digi_monitor_disable(adc_digi_monitor_idx_t idx)
+static inline void adc_ll_digi_monitor_enable_intr(adc_monitor_id_t monitor_id, adc_monitor_mode_t mode, bool enable)
 {
-    if (idx == ADC_DIGI_MONITOR_IDX0) {
-        APB_SARADC.thres0_ctrl.thres0_channel = 0xF;
-    } else { // ADC_DIGI_MONITOR_IDX1
-        APB_SARADC.thres1_ctrl.thres1_channel = 0xF;
+    if (monitor_id == ADC_MONITOR_0) {
+        if (mode == ADC_MONITOR_MODE_HIGH) {
+            APB_SARADC.int_ena.thres0_high = enable;
+        } else {
+            APB_SARADC.int_ena.thres0_low = enable;
+        }
     }
+    if (monitor_id == ADC_MONITOR_1) {
+        if (mode == ADC_MONITOR_MODE_HIGH) {
+            APB_SARADC.int_ena.thres1_high = enable;
+        } else {
+            APB_SARADC.int_ena.thres1_low = enable;
+        }
+    }
+}
+
+/**
+ * Get the address of digi monitor intr statue register.
+ *
+ * @return address of register.
+ */
+__attribute__((always_inline))
+static inline volatile const void *adc_ll_digi_monitor_get_intr_status_addr(void)
+{
+    return &APB_SARADC.int_st.val;
+}
+
+/**
+ * Clear intr raw for adc digi monitors.
+ */
+__attribute__((always_inline))
+static inline void adc_ll_digi_monitor_clear_intr(void)
+{
+    APB_SARADC.int_clr.val |= ADC_LL_THRES_ALL_INTR_ST_M;
 }
 
 /**

+ 77 - 29
components/hal/esp32c6/include/hal/adc_ll.h

@@ -30,10 +30,13 @@ extern "C" {
 
 #define ADC_LL_EVENT_ADC1_ONESHOT_DONE    BIT(31)
 #define ADC_LL_EVENT_ADC2_ONESHOT_DONE    BIT(30)
-#define ADC_LL_EVENT_THRES0_HIGH          BIT(29)
-#define ADC_LL_EVENT_THRES1_HIGH          BIT(28)
-#define ADC_LL_EVENT_THRES0_LOW           BIT(27)
-#define ADC_LL_EVENT_THRES1_LOW           BIT(26)
+
+#define ADC_LL_THRES_ALL_INTR_ST_M  (APB_SARADC_APB_SARADC_THRES0_HIGH_INT_ST_M | \
+                                     APB_SARADC_APB_SARADC_THRES1_HIGH_INT_ST_M | \
+                                     APB_SARADC_APB_SARADC_THRES0_LOW_INT_ST_M  | \
+                                     APB_SARADC_APB_SARADC_THRES1_LOW_INT_ST_M)
+#define ADC_LL_GET_HIGH_THRES_MASK(monitor_id)    ((monitor_id == 0) ? APB_SARADC_APB_SARADC_THRES0_HIGH_INT_ST_M : APB_SARADC_APB_SARADC_THRES1_HIGH_INT_ST_M)
+#define ADC_LL_GET_LOW_THRES_MASK(monitor_id)     ((monitor_id == 0) ? APB_SARADC_APB_SARADC_THRES0_LOW_INT_ST_M : APB_SARADC_APB_SARADC_THRES1_LOW_INT_ST_M)
 
 /*---------------------------------------------------------------
                     Oneshot
@@ -50,7 +53,6 @@ extern "C" {
 #define ADC_LL_FSM_STANDBY_WAIT_DEFAULT                (100)
 #define ADC_LL_SAMPLE_CYCLE_DEFAULT                    (2)
 #define ADC_LL_DIGI_SAR_CLK_DIV_DEFAULT                (1)
-
 #define ADC_LL_CLKM_DIV_NUM_DEFAULT       15
 #define ADC_LL_CLKM_DIV_B_DEFAULT         1
 #define ADC_LL_CLKM_DIV_A_DEFAULT         0
@@ -407,39 +409,85 @@ static inline void adc_ll_digi_filter_enable(adc_digi_iir_filter_t idx, adc_unit
 }
 
 /**
- * Set monitor mode of adc digital controller.
+ * Set monitor threshold of adc digital controller on specific channel.
  *
- * @note If the channel info is not supported, the monitor function will not be enabled.
- * @param adc_n ADC unit.
- * @param is_larger true:  If ADC_OUT >  threshold, Generates monitor interrupt.
- *                  false: If ADC_OUT <  threshold, Generates monitor interrupt.
- */
-static inline void adc_ll_digi_monitor_set_mode(adc_digi_monitor_idx_t idx, adc_digi_monitor_t *cfg)
-{
-    if (idx == ADC_DIGI_MONITOR_IDX0) {
-        APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7);
-        APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_high = cfg->h_threshold;
-        APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_low = cfg->l_threshold;
-    } else { // ADC_DIGI_MONITOR_IDX1
-        APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7);
-        APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_low = cfg->h_threshold;
-        APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_low = cfg->l_threshold;
+ * @param monitor_id ADC digi monitor unit index.
+ * @param adc_n      Which adc unit the channel belong to.
+ * @param channel    Which channel of adc want to be monitored.
+ * @param h_thresh   High threshold of this monitor.
+ * @param l_thresh   Low threshold of this monitor.
+ */
+static inline void adc_ll_digi_monitor_set_thres(adc_monitor_id_t monitor_id, adc_unit_t adc_n, uint8_t channel, int32_t h_thresh, int32_t l_thresh)
+{
+    if (monitor_id == ADC_MONITOR_0) {
+        APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_channel = (adc_n << 3) | (channel & 0x7);
+        APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_high = h_thresh;
+        APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_low = l_thresh;
+    } else { // ADC_MONITOR_1
+        APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_channel = (adc_n << 3) | (channel & 0x7);
+        APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_high = h_thresh;
+        APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_low = l_thresh;
     }
 }
 
 /**
- * Enable/disable monitor of adc digital controller.
+ * Start/Stop monitor of adc digital controller.
  *
- * @note If the channel info is not supported, the monitor function will not be enabled.
- * @param adc_n ADC unit.
+ * @param monitor_id ADC digi monitor unit index.
+ * @param start 1 for start, 0 for stop
  */
-static inline void adc_ll_digi_monitor_disable(adc_digi_monitor_idx_t idx)
+static inline void adc_ll_digi_monitor_user_start(adc_monitor_id_t monitor_id, bool start)
 {
-    if (idx == ADC_DIGI_MONITOR_IDX0) {
-        APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_channel = 0xF;
-    } else { // ADC_DIGI_MONITOR_IDX1
-        APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_channel = 0xF;
+    if (monitor_id == ADC_MONITOR_0) {
+        APB_SARADC.saradc_thres_ctrl.saradc_apb_saradc_thres0_en = start;
+    } else {
+        APB_SARADC.saradc_thres_ctrl.saradc_apb_saradc_thres1_en = start;
+    }
+}
+
+/**
+ * Enable/disable a intr of adc digital monitor.
+ *
+ * @param monitor_id ADC digi monitor unit index.
+ * @param mode monit mode to enable/disable intr.
+ * @param enable enable or disable.
+ */
+static inline void adc_ll_digi_monitor_enable_intr(adc_monitor_id_t monitor_id, adc_monitor_mode_t mode, bool enable)
+{
+    if (monitor_id == ADC_MONITOR_0) {
+        if (mode == ADC_MONITOR_MODE_HIGH) {
+            APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres0_high_int_ena = enable;
+        } else {
+            APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres0_low_int_ena = enable;
+        }
     }
+    if (monitor_id == ADC_MONITOR_1) {
+        if (mode == ADC_MONITOR_MODE_HIGH) {
+            APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres1_high_int_ena = enable;
+        } else {
+            APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres1_low_int_ena = enable;
+        }
+    }
+}
+
+/**
+ * Clear intr raw for adc digi monitors.
+ */
+__attribute__((always_inline))
+static inline void adc_ll_digi_monitor_clear_intr(void)
+{
+    APB_SARADC.saradc_int_clr.val |= ADC_LL_THRES_ALL_INTR_ST_M;
+}
+
+/**
+ * Get the address of digi monitor intr statue register.
+ *
+ * @return address of register.
+ */
+__attribute__((always_inline))
+static inline volatile const void *adc_ll_digi_monitor_get_intr_status_addr(void)
+{
+    return &APB_SARADC.saradc_int_st.val;
 }
 
 /**

+ 77 - 28
components/hal/esp32h2/include/hal/adc_ll.h

@@ -30,10 +30,13 @@ extern "C" {
 
 #define ADC_LL_EVENT_ADC1_ONESHOT_DONE    BIT(31)
 #define ADC_LL_EVENT_ADC2_ONESHOT_DONE    BIT(30)
-#define ADC_LL_EVENT_THRES0_HIGH          BIT(29)
-#define ADC_LL_EVENT_THRES1_HIGH          BIT(28)
-#define ADC_LL_EVENT_THRES0_LOW           BIT(27)
-#define ADC_LL_EVENT_THRES1_LOW           BIT(26)
+
+#define ADC_LL_THRES_ALL_INTR_ST_M  (APB_SARADC_APB_SARADC_THRES0_HIGH_INT_ST_M | \
+                                     APB_SARADC_APB_SARADC_THRES1_HIGH_INT_ST_M | \
+                                     APB_SARADC_APB_SARADC_THRES0_LOW_INT_ST_M  | \
+                                     APB_SARADC_APB_SARADC_THRES1_LOW_INT_ST_M)
+#define ADC_LL_GET_HIGH_THRES_MASK(monitor_id)    ((monitor_id == 0) ? APB_SARADC_APB_SARADC_THRES0_HIGH_INT_ST_M : APB_SARADC_APB_SARADC_THRES1_HIGH_INT_ST_M)
+#define ADC_LL_GET_LOW_THRES_MASK(monitor_id)     ((monitor_id == 0) ? APB_SARADC_APB_SARADC_THRES0_LOW_INT_ST_M : APB_SARADC_APB_SARADC_THRES1_LOW_INT_ST_M)
 
 /*---------------------------------------------------------------
                     Oneshot
@@ -407,41 +410,87 @@ static inline void adc_ll_digi_filter_enable(adc_digi_iir_filter_t idx, adc_unit
 }
 
 /**
- * Set monitor mode of adc digital controller.
+ * Set monitor threshold of adc digital controller on specific channel.
  *
- * @note If the channel info is not supported, the monitor function will not be enabled.
- * @param adc_n ADC unit.
- * @param is_larger true:  If ADC_OUT >  threshold, Generates monitor interrupt.
- *                  false: If ADC_OUT <  threshold, Generates monitor interrupt.
- */
-static inline void adc_ll_digi_monitor_set_mode(adc_digi_monitor_idx_t idx, adc_digi_monitor_t *cfg)
-{
-    if (idx == ADC_DIGI_MONITOR_IDX0) {
-        APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7);
-        APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_high = cfg->h_threshold;
-        APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_low = cfg->l_threshold;
-    } else { // ADC_DIGI_MONITOR_IDX1
-        APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7);
-        APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_low = cfg->h_threshold;
-        APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_low = cfg->l_threshold;
+ * @param monitor_id ADC digi monitor unit index.
+ * @param adc_n      Which adc unit the channel belong to.
+ * @param channel    Which channel of adc want to be monitored.
+ * @param h_thresh   High threshold of this monitor.
+ * @param l_thresh   Low threshold of this monitor.
+ */
+static inline void adc_ll_digi_monitor_set_thres(adc_monitor_id_t monitor_id, adc_unit_t adc_n, uint8_t channel, int32_t h_thresh, int32_t l_thresh)
+{
+    if (monitor_id == ADC_MONITOR_0) {
+        APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_channel = (adc_n << 3) | (channel & 0x7);
+        APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_high = h_thresh;
+        APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_low = l_thresh;
+    } else { // ADC_MONITOR_1
+        APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_channel = (adc_n << 3) | (channel & 0x7);
+        APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_high = h_thresh;
+        APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_low = l_thresh;
     }
 }
 
 /**
- * Enable/disable monitor of adc digital controller.
+ * Start/Stop monitor of adc digital controller.
  *
- * @note If the channel info is not supported, the monitor function will not be enabled.
- * @param adc_n ADC unit.
+ * @param monitor_id ADC digi monitor unit index.
+ * @param start 1 for start, 0 for stop
  */
-static inline void adc_ll_digi_monitor_disable(adc_digi_monitor_idx_t idx)
+static inline void adc_ll_digi_monitor_user_start(adc_monitor_id_t monitor_id, bool start)
 {
-    if (idx == ADC_DIGI_MONITOR_IDX0) {
-        APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_channel = 0xF;
-    } else { // ADC_DIGI_MONITOR_IDX1
-        APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_channel = 0xF;
+    if (monitor_id == ADC_MONITOR_0) {
+        APB_SARADC.saradc_thres_ctrl.saradc_apb_saradc_thres0_en = start;
+    } else {
+        APB_SARADC.saradc_thres_ctrl.saradc_apb_saradc_thres1_en = start;
     }
 }
 
+/**
+ * Enable/disable a intr of adc digital monitor.
+ *
+ * @param monitor_id ADC digi monitor unit index.
+ * @param mode monit mode to enable/disable intr.
+ * @param enable enable or disable.
+ */
+static inline void adc_ll_digi_monitor_enable_intr(adc_monitor_id_t monitor_id, adc_monitor_mode_t mode, bool enable)
+{
+    if (monitor_id == ADC_MONITOR_0) {
+        if (mode == ADC_MONITOR_MODE_HIGH) {
+            APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres0_high_int_ena = enable;
+        } else {
+            APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres0_low_int_ena = enable;
+        }
+    }
+    if (monitor_id == ADC_MONITOR_1) {
+        if (mode == ADC_MONITOR_MODE_HIGH) {
+            APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres1_high_int_ena = enable;
+        } else {
+            APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres1_low_int_ena = enable;
+        }
+    }
+}
+
+/**
+ * Clear intr raw for adc digi monitors.
+ */
+__attribute__((always_inline))
+static inline void adc_ll_digi_monitor_clear_intr(void)
+{
+    APB_SARADC.saradc_int_clr.val |= ADC_LL_THRES_ALL_INTR_ST_M;
+}
+
+/**
+ * Get the address of digi monitor intr statue register.
+ *
+ * @return address of register.
+ */
+__attribute__((always_inline))
+static inline volatile const void *adc_ll_digi_monitor_get_intr_status_addr(void)
+{
+    return &APB_SARADC.saradc_int_st.val;
+}
+
 /**
  * Set DMA eof num of adc digital controller.
  * If the number of measurements reaches `dma_eof_num`, then `dma_in_suc_eof` signal is generated.

+ 65 - 21
components/hal/esp32s2/include/hal/adc_ll.h

@@ -30,6 +30,10 @@ extern "C" {
 #define ADC_LL_EVENT_ADC1_ONESHOT_DONE    (1 << 0)
 #define ADC_LL_EVENT_ADC2_ONESHOT_DONE    (1 << 1)
 
+#define ADC_LL_THRES_ALL_INTR_ST_M  (APB_SARADC_ADC1_THRES_INT_ST_M | APB_SARADC_ADC2_THRES_INT_ST_M)
+#define ADC_LL_GET_HIGH_THRES_MASK(monitor_id)    ((monitor_id == 0) ? APB_SARADC_ADC1_THRES_INT_ST_M : APB_SARADC_ADC2_THRES_INT_ST_M)
+#define ADC_LL_GET_LOW_THRES_MASK(monitor_id)     ((monitor_id == 0) ? APB_SARADC_ADC1_THRES_INT_ST_M : APB_SARADC_ADC2_THRES_INT_ST_M)
+
 /*---------------------------------------------------------------
                     Oneshot
 ---------------------------------------------------------------*/
@@ -460,48 +464,88 @@ static inline uint32_t adc_ll_digi_filter_read_data(adc_unit_t adc_n)
  * Set monitor mode of adc digital controller.
  *
  * @note The monitor will monitor all the enabled channel data of the each ADC unit at the same time.
- * @param adc_n ADC unit.
+ * @param monitor_id ADC digi monitor unit index.
  * @param is_larger true:  If ADC_OUT >  threshold, Generates monitor interrupt.
  *                  false: If ADC_OUT <  threshold, Generates monitor interrupt.
  */
-static inline void adc_ll_digi_monitor_set_mode(adc_unit_t adc_n, bool is_larger)
+static inline void adc_ll_digi_monitor_set_mode(adc_monitor_id_t monitor_id, bool is_larger)
 {
-    if (adc_n == ADC_UNIT_1) {
+    if (monitor_id == ADC_MONITOR_0) {
         APB_SARADC.thres_ctrl.adc1_thres_mode = is_larger;
-    } else { // adc_n == ADC_UNIT_2
+    } else { // monitor_id == ADC_MONITOR_1
         APB_SARADC.thres_ctrl.adc2_thres_mode = is_larger;
     }
 }
 
 /**
- * Set monitor threshold of adc digital controller.
+ * Set monitor threshold of adc digital controller on specific channel.
  *
- * @note The monitor will monitor all the enabled channel data of the each ADC unit at the same time.
- * @param adc_n ADC unit.
- * @param threshold Monitor threshold.
+ * @param monitor_id ADC digi monitor unit index.
+ * @param adc_n      Which adc unit the channel belong to.
+ * @param channel    Which channel of adc want to be monitored.
+ * @param h_thresh   High threshold of this monitor.
+ * @param l_thresh   Low threshold of this monitor.
  */
-static inline void adc_ll_digi_monitor_set_thres(adc_unit_t adc_n, uint32_t threshold)
+static inline void adc_ll_digi_monitor_set_thres(adc_monitor_id_t monitor_id, adc_unit_t adc_n, uint8_t channel, int32_t h_thresh, int32_t l_thresh)
 {
-    if (adc_n == ADC_UNIT_1) {
-        APB_SARADC.thres_ctrl.adc1_thres = threshold;
-    } else { // adc_n == ADC_UNIT_2
-        APB_SARADC.thres_ctrl.adc2_thres = threshold;
+    if (monitor_id == ADC_MONITOR_0) {
+        APB_SARADC.thres_ctrl.adc1_thres = (h_thresh == -1)? l_thresh : h_thresh;
+    } else { // monitor_id == ADC_MONITOR_1
+        APB_SARADC.thres_ctrl.adc2_thres = (h_thresh == -1)? l_thresh : h_thresh;
     }
+    adc_ll_digi_monitor_set_mode(monitor_id, l_thresh == -1);
 }
 
 /**
- * Enable/disable monitor of adc digital controller.
+ * Start/Stop monitor of adc digital controller.
  *
- * @note The monitor will monitor all the enabled channel data of the each ADC unit at the same time.
- * @param adc_n ADC unit.
+ * @param monitor_id ADC digi monitor unit index.
+ * @param start 1 for start, 0 for stop
  */
-static inline void adc_ll_digi_monitor_enable(adc_unit_t adc_n, bool enable)
+static inline void adc_ll_digi_monitor_user_start(adc_monitor_id_t monitor_id, bool start)
 {
-    if (adc_n == ADC_UNIT_1) {
-        APB_SARADC.thres_ctrl.adc1_thres_en = enable;
-    } else { // adc_n == ADC_UNIT_2
-        APB_SARADC.thres_ctrl.adc2_thres_en = enable;
+    if (monitor_id == ADC_MONITOR_0) {
+        APB_SARADC.thres_ctrl.adc1_thres_en = start;
+    } else {
+        APB_SARADC.thres_ctrl.adc2_thres_en = start;
+    }
+}
+
+/**
+ * Enable/disable a intr of adc digital monitor.
+ *
+ * @param monitor_id ADC digi monitor unit index.
+ * @param mode monit mode to enable/disable intr.
+ * @param enable enable or disable.
+ */
+static inline void adc_ll_digi_monitor_enable_intr(adc_monitor_id_t monitor_id, adc_monitor_mode_t mode, bool enable)
+{
+    if (monitor_id == ADC_MONITOR_0) {
+        APB_SARADC.int_ena.adc1_thres = enable;
     }
+    if (monitor_id == ADC_MONITOR_1) {
+        APB_SARADC.int_ena.adc2_thres = enable;
+    }
+}
+
+/**
+ * Clear intr raw for adc digi monitors.
+ */
+__attribute__((always_inline))
+static inline void adc_ll_digi_monitor_clear_intr(void)
+{
+    APB_SARADC.int_clr.val |= ADC_LL_THRES_ALL_INTR_ST_M;
+}
+
+/**
+ * Get the address of digi monitor intr statue register.
+ *
+ * @return address of register.
+ */
+__attribute__((always_inline))
+static inline volatile const void *adc_ll_digi_monitor_get_intr_status_addr(void)
+{
+    return &APB_SARADC.int_st.val;
 }
 
 /**

+ 71 - 18
components/hal/esp32s3/include/hal/adc_ll.h

@@ -30,6 +30,13 @@ extern "C" {
 #define ADC_LL_EVENT_ADC1_ONESHOT_DONE    (1 << 0)
 #define ADC_LL_EVENT_ADC2_ONESHOT_DONE    (1 << 1)
 
+#define ADC_LL_THRES_ALL_INTR_ST_M  (APB_SARADC_THRES0_HIGH_INT_ST_M | \
+                                     APB_SARADC_THRES1_HIGH_INT_ST_M | \
+                                     APB_SARADC_THRES0_LOW_INT_ST_M  | \
+                                     APB_SARADC_THRES1_LOW_INT_ST_M)
+#define ADC_LL_GET_HIGH_THRES_MASK(monitor_id)    ((monitor_id == 0) ? APB_SARADC_THRES0_HIGH_INT_ST_M : APB_SARADC_THRES1_HIGH_INT_ST_M)
+#define ADC_LL_GET_LOW_THRES_MASK(monitor_id)     ((monitor_id == 0) ? APB_SARADC_THRES0_LOW_INT_ST_M : APB_SARADC_THRES1_LOW_INT_ST_M)
+
 /*---------------------------------------------------------------
                     Oneshot
 ---------------------------------------------------------------*/
@@ -456,39 +463,85 @@ static inline void adc_ll_digi_filter_enable(adc_digi_iir_filter_t idx, adc_unit
 }
 
 /**
- * Set monitor mode of adc digital controller.
+ * Set monitor threshold of adc digital controller on specific channel.
  *
- * @note The monitor will monitor all the enabled channel data of the each ADC unit at the same time.
- * @param adc_n ADC unit.
- * @param is_larger true:  If ADC_OUT >  threshold, Generates monitor interrupt.
- *                  false: If ADC_OUT <  threshold, Generates monitor interrupt.
+ * @param monitor_id ADC digi monitor unit index.
+ * @param adc_n      Which adc unit the channel belong to.
+ * @param channel    Which channel of adc want to be monitored.
+ * @param h_thresh   High threshold of this monitor.
+ * @param l_thresh   Low threshold of this monitor.
  */
-static inline void adc_ll_digi_monitor_set_mode(adc_unit_t adc_n, bool is_larger)
+static inline void adc_ll_digi_monitor_set_thres(adc_monitor_id_t monitor_id, adc_unit_t adc_n, uint8_t channel, int32_t h_thresh, int32_t l_thresh)
 {
-    abort();
+    if (monitor_id == ADC_MONITOR_0) {
+        APB_SARADC.thres0_ctrl.thres0_channel = (adc_n << 3) | (channel & 0x7);
+        APB_SARADC.thres0_ctrl.thres0_high = h_thresh;
+        APB_SARADC.thres0_ctrl.thres0_low = l_thresh;
+    } else { // ADC_MONITOR_1
+        APB_SARADC.thres1_ctrl.thres1_channel = (adc_n << 3) | (channel & 0x7);
+        APB_SARADC.thres1_ctrl.thres1_high = h_thresh;
+        APB_SARADC.thres1_ctrl.thres1_low = l_thresh;
+    }
 }
 
 /**
- * Set monitor threshold of adc digital controller.
+ * Start/Stop monitor of adc digital controller.
  *
- * @note The monitor will monitor all the enabled channel data of the each ADC unit at the same time.
- * @param adc_n ADC unit.
- * @param threshold Monitor threshold.
+ * @param monitor_id ADC digi monitor unit index.
+ * @param start 1 for start, 0 for stop
  */
-static inline void adc_ll_digi_monitor_set_thres(adc_unit_t adc_n, uint32_t threshold)
+static inline void adc_ll_digi_monitor_user_start(adc_monitor_id_t monitor_id, bool start)
 {
-    abort();
+    if (monitor_id == ADC_MONITOR_0) {
+        APB_SARADC.thres_ctrl.thres0_en = start;
+    } else {
+        APB_SARADC.thres_ctrl.thres1_en = start;
+    }
 }
 
 /**
- * Enable/disable monitor of adc digital controller.
+ * Enable/disable a intr of adc digital monitor.
  *
- * @note The monitor will monitor all the enabled channel data of the each ADC unit at the same time.
- * @param adc_n ADC unit.
+ * @param monitor_id ADC digi monitor unit index.
+ * @param mode monit mode to enable/disable intr.
+ * @param enable enable or disable.
  */
-static inline void adc_ll_digi_monitor_enable(adc_unit_t adc_n, bool enable)
+static inline void adc_ll_digi_monitor_enable_intr(adc_monitor_id_t monitor_id, adc_monitor_mode_t mode, bool enable)
 {
-    abort();
+    if (monitor_id == ADC_MONITOR_0) {
+        if (mode == ADC_MONITOR_MODE_HIGH) {
+            APB_SARADC.int_ena.thres0_high = enable;
+        } else {
+            APB_SARADC.int_ena.thres0_low = enable;
+        }
+    }
+    if (monitor_id == ADC_MONITOR_1) {
+        if (mode == ADC_MONITOR_MODE_HIGH) {
+            APB_SARADC.int_ena.thres1_high = enable;
+        } else {
+            APB_SARADC.int_ena.thres1_low = enable;
+        }
+    }
+}
+
+/**
+ * Clear intr raw for adc digi monitors.
+ */
+__attribute__((always_inline))
+static inline void adc_ll_digi_monitor_clear_intr(void)
+{
+    APB_SARADC.int_clr.val |= ADC_LL_THRES_ALL_INTR_ST_M;
+}
+
+/**
+ * Get the address of digi monitor intr statue register.
+ *
+ * @return address of register.
+ */
+__attribute__((always_inline))
+static inline volatile const void *adc_ll_digi_monitor_get_intr_status_addr(void)
+{
+    return &APB_SARADC.int_st.val;
 }
 
 /**

+ 20 - 0
components/hal/include/hal/adc_types.h

@@ -116,6 +116,25 @@ typedef enum {
     ADC_DIGI_IIR_FILTER_COEFF_64,    ///< The filter coefficient is 64
 } adc_digi_iir_filter_coeff_t;
 
+/*---------------------------------------------------------------
+                        ADC Monitor
+---------------------------------------------------------------*/
+/**
+ * @brief ADC monitor (continuous mode) ID
+ */
+typedef enum {
+    ADC_MONITOR_0,          ///< The monitor index 0.
+    ADC_MONITOR_1,          ///< The monitor index 1.
+} adc_monitor_id_t;
+
+/**
+ * @brief Monitor config/event mode type
+ */
+typedef enum {
+    ADC_MONITOR_MODE_HIGH = 0,      ///< ADC raw_result > threshold value, monitor interrupt will be generated.
+    ADC_MONITOR_MODE_LOW,           ///< ADC raw_result < threshold value, monitor interrupt will be generated.
+} adc_monitor_mode_t;
+
 /*---------------------------------------------------------------
                     Output Format
 ---------------------------------------------------------------*/
@@ -205,6 +224,7 @@ typedef struct {
 
 #endif
 
+
 #if CONFIG_IDF_TARGET_ESP32S2
 /**
  * @brief ADC digital controller (DMA mode) clock system setting.

+ 0 - 49
components/hal/include/hal/adc_types_private.h

@@ -47,55 +47,6 @@ typedef struct {
 }
 #endif  //#if SOC_ADC_ARBITER_SUPPORTED
 
-#if SOC_ADC_MONITOR_SUPPORTED
-/*---------------------------------------------------------------
-                    Monitor
----------------------------------------------------------------*/
-/**
- * @brief ADC digital controller (DMA mode) monitor index options.
- *
- * @note  For ESP32-S2, The monitor object of the ADC is fixed.
- */
-typedef enum {
-    ADC_DIGI_MONITOR_IDX0 = 0, /*!<The monitor index 0.
-                                  For ESP32-S2, It can only be used to monitor all enabled channels of ADC1 unit at the same time. */
-    ADC_DIGI_MONITOR_IDX1,     /*!<The monitor index 1.
-                                  For ESP32-S2, It can only be used to monitor all enabled channels of ADC2 unit at the same time. */
-    ADC_DIGI_MONITOR_IDX_MAX
-} adc_digi_monitor_idx_t;
-
-/**
- * @brief Set monitor mode of adc digital controller.
- *        MONITOR_HIGH:If ADC_OUT >  threshold, Generates monitor interrupt.
- *        MONITOR_LOW: If ADC_OUT <  threshold, Generates monitor interrupt.
- */
-typedef enum {
-#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2
-    ADC_DIGI_MONITOR_DIS = 0,  /*!<Disable monitor. */
-    ADC_DIGI_MONITOR_EN,       /*!<If ADC_OUT <  threshold, Generates monitor interrupt. */
-                               /*!<If ADC_OUT >  threshold, Generates monitor interrupt. */
-#else
-    ADC_DIGI_MONITOR_HIGH = 0,  /*!<If ADC_OUT >  threshold, Generates monitor interrupt. */
-    ADC_DIGI_MONITOR_LOW,       /*!<If ADC_OUT <  threshold, Generates monitor interrupt. */
-#endif
-    ADC_DIGI_MONITOR_MAX
-} adc_digi_monitor_mode_t;
-
-/**
- * @brief ADC digital controller (DMA mode) monitor configuration.
- *
- */
-typedef struct {
-    adc_unit_t adc_unit;            /*!<Set adc unit number for monitor.
-                                        For ESP32-S2, monitor IDX0/IDX1 can only be used to monitor all enabled channels of ADC1/ADC2 unit at the same time. */
-    adc_channel_t channel;          /*!<Set adc channel number for monitor.
-                                        For ESP32-S2, it's always `ADC_CHANNEL_MAX` */
-    adc_digi_monitor_mode_t mode;   /*!<Set adc monitor mode. See ``adc_digi_monitor_mode_t``. */
-    uint32_t h_threshold;           /*!<Set monitor threshold of adc digital controller. */
-    uint32_t l_threshold;           /*!<Set monitor threshold of adc digital controller. */
-} adc_digi_monitor_t;
-#endif  //#if SOC_ADC_MONITOR_SUPPORTED
-
 #ifdef __cplusplus
 }
 #endif

+ 4 - 0
components/soc/esp32/include/soc/Kconfig.soc_caps.in

@@ -211,6 +211,10 @@ config SOC_ADC_DIGI_DATA_BYTES_PER_CONV
     int
     default 4
 
+config SOC_ADC_DIGI_MONITOR_NUM
+    int
+    default 0
+
 config SOC_ADC_SAMPLE_FREQ_THRES_HIGH
     int
     default 2

+ 1 - 0
components/soc/esp32/include/soc/soc_caps.h

@@ -125,6 +125,7 @@
 #define SOC_ADC_DIGI_MAX_BITWIDTH               (12)
 #define SOC_ADC_DIGI_RESULT_BYTES               (2)
 #define SOC_ADC_DIGI_DATA_BYTES_PER_CONV        (4)
+#define SOC_ADC_DIGI_MONITOR_NUM                (0U) // to reference `IDF_TARGET_SOC_ADC_DIGI_MONITOR_NUM` in document
 #define SOC_ADC_SAMPLE_FREQ_THRES_HIGH          (2*1000*1000)
 #define SOC_ADC_SAMPLE_FREQ_THRES_LOW           (20*1000)
 

+ 4 - 0
components/soc/esp32s2/include/soc/Kconfig.soc_caps.in

@@ -243,6 +243,10 @@ config SOC_ADC_DIGI_DATA_BYTES_PER_CONV
     int
     default 2
 
+config SOC_ADC_DIGI_MONITOR_NUM
+    int
+    default 2
+
 config SOC_ADC_SAMPLE_FREQ_THRES_HIGH
     int
     default 83333

+ 1 - 0
components/soc/esp32s2/include/soc/soc_caps.h

@@ -108,6 +108,7 @@
 #define SOC_ADC_DIGI_IIR_FILTER_NUM             (2)
 #define SOC_ADC_DIGI_RESULT_BYTES               (2)
 #define SOC_ADC_DIGI_DATA_BYTES_PER_CONV        (2)
+#define SOC_ADC_DIGI_MONITOR_NUM                (2)
 /*!< F_sample = F_digi_con / 2 / interval. F_digi_con = 5M for now. 30 <= interval<= 4095 */
 #define SOC_ADC_SAMPLE_FREQ_THRES_HIGH          83333
 #define SOC_ADC_SAMPLE_FREQ_THRES_LOW           611

+ 4 - 0
components/soc/esp32s3/include/soc/Kconfig.soc_caps.in

@@ -279,6 +279,10 @@ config SOC_ADC_DIGI_IIR_FILTER_NUM
     int
     default 2
 
+config SOC_ADC_DIGI_MONITOR_NUM
+    int
+    default 2
+
 config SOC_ADC_SAMPLE_FREQ_THRES_HIGH
     int
     default 83333

+ 1 - 0
components/soc/esp32s3/include/soc/soc_caps.h

@@ -100,6 +100,7 @@
 #define SOC_ADC_DIGI_RESULT_BYTES               (4)
 #define SOC_ADC_DIGI_DATA_BYTES_PER_CONV        (4)
 #define SOC_ADC_DIGI_IIR_FILTER_NUM             (2)
+#define SOC_ADC_DIGI_MONITOR_NUM                (2)
 /*!< F_sample = F_digi_con / 2 / interval. F_digi_con = 5M for now. 30 <= interval<= 4095 */
 #define SOC_ADC_SAMPLE_FREQ_THRES_HIGH          83333
 #define SOC_ADC_SAMPLE_FREQ_THRES_LOW           611

+ 30 - 0
docs/en/api-reference/peripherals/adc_continuous.rst

@@ -73,6 +73,9 @@ If the ADC continuous mode driver is no longer used, you should deinitialize the
 
 .. only:: SOC_ADC_DIG_IIR_FILTER_SUPPORTED
 
+    IIR filter
+    ~~~~~~~~~~
+
     Two IIR filters are available when ADC is working under continuous mode. To create an ADC IIR filter, you should set up the :cpp:type:`adc_continuous_iir_filter_config_t`, and call :cpp:func:`adc_new_continuous_iir_filter`.
 
     - :cpp:member:`adc_digi_filter_config_t::unit`, ADC  unit.
@@ -91,6 +94,33 @@ If the ADC continuous mode driver is no longer used, you should deinitialize the
 
             If you use both the filters on a same ADC channel, then only the first one will take effect.
 
+.. only:: SOC_ADC_MONITOR_SUPPORTED
+
+    Monitor
+    ~~~~~~~
+
+    {IDF_TARGET_SOC_ADC_DIGI_MONITOR_NUM} monitors are available when ADC is working under continuous mode, you can set one or two threshold(s) of a monitor on a working ADC channel, then monitor will invoke interrupts every sample loop if converted value outranges of the threshold. To create an ADC monitor, you need setup the :cpp:type:`adc_monitor_config_t` and call :cpp:func:`adc_new_continuous_monitor`.
+    - :cpp:member:`adc_monitor_config_t::adc_unit`, What ADC unit the channel you want to monit belongs to.
+    - :cpp:member:`adc_monitor_config_t::channel`, The channel you want to monit.
+    - :cpp:member:`adc_monitor_config_t::h_threshold`, The high threshold, convert value lager than this value will invoke interrupt, set to -1 if don't use.
+    - :cpp:member:`adc_monitor_config_t::l_threshold`, The low threshold, convert value less than this value will invoke interrupt, set to -1 if don't use.
+
+    Once a monitor is created, you can operate it by following APIs to construct your apps.
+
+    - :cpp:func:`adc_continuous_monitor_enable`, Enable a monitor.
+    - :cpp:func:`adc_continuous_monitor_disable`, Disable a monitor.
+    - :cpp:func:`adc_monitor_register_callbacks`, Register user callbacks to do something when ADC value outrange of the threshold.
+    - :cpp:func:`adc_del_continuous_monitor`, Delete a created monitor, free resources.
+
+    .. only:: esp32s2
+
+        .. NOTE::
+
+            There are some hardware limitations on ESP32S2:
+            1. Only one threshold supported for one monitor.
+            2. Only one monitor supported for one adc unit.
+            3. All enabled channel(s) of a certain adc unit in adc continuous mode driver will be monitored, param :cpp:member:`adc_monitor_config_t::channel` will not used.
+
 Initialize the ADC Continuous Mode Driver
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~