Parcourir la source

adc_cali: supported channel compensation of adc calibration on esp32c6

laokaiyao il y a 2 ans
Parent
commit
ffb40a89d9
32 fichiers modifiés avec 366 ajouts et 57 suppressions
  1. 6 0
      components/driver/deprecated/adc_dma_legacy.c
  2. 6 0
      components/driver/deprecated/adc_legacy.c
  3. 4 4
      components/driver/test_apps/legacy_adc_driver/main/test_legacy_adc.c
  4. 56 8
      components/efuse/esp32c6/esp_efuse_rtc_calib.c
  5. 11 0
      components/efuse/esp32c6/include/esp_efuse_rtc_calib.h
  6. 16 2
      components/esp_adc/adc_cali_curve_fitting.c
  7. 6 0
      components/esp_adc/adc_common.c
  8. 1 1
      components/esp_adc/adc_oneshot.c
  9. 1 2
      components/esp_adc/esp32c6/curve_fitting_coefficients.c
  10. 1 0
      components/esp_adc/include/esp_adc/adc_cali_scheme.h
  11. 2 2
      components/esp_adc/test_apps/adc/main/test_adc.c
  12. 2 2
      components/esp_adc/test_apps/adc/main/test_adc_performance.c
  13. 2 1
      components/esp_adc/test_apps/adc/main/test_common_adc.c
  14. 10 6
      components/esp_adc/test_apps/adc/main/test_common_adc.h
  15. 14 0
      components/esp_hw_support/adc_share_hw_ctrl.c
  16. 22 0
      components/esp_hw_support/include/esp_private/adc_share_hw_ctrl.h
  17. 1 0
      components/esp_hw_support/port/esp32c6/CMakeLists.txt
  18. 98 0
      components/esp_hw_support/port/esp32c6/ocode_init.c
  19. 21 0
      components/esp_hw_support/port/esp32c6/private_include/ocode_init.h
  20. 4 0
      components/esp_system/port/soc/esp32c6/clk.c
  21. 1 2
      components/hal/adc_hal_common.c
  22. 5 0
      components/hal/efuse_hal.c
  23. 10 0
      components/hal/esp32/include/hal/efuse_ll.h
  24. 1 1
      components/hal/esp32c6/include/hal/adc_ll.h
  25. 5 0
      components/hal/esp32c6/include/hal/efuse_ll.h
  26. 1 1
      components/hal/esp32h2/include/hal/adc_ll.h
  27. 7 0
      components/hal/include/hal/efuse_hal.h
  28. 7 7
      components/idf_test/include/esp32c6/idf_performance_target.h
  29. 9 1
      components/soc/esp32c6/include/soc/Kconfig.soc_caps.in
  30. 3 1
      components/soc/esp32c6/include/soc/soc_caps.h
  31. 15 4
      docs/en/api-reference/peripherals/adc_calibration.rst
  32. 18 12
      examples/peripherals/adc/oneshot_read/main/oneshot_read_main.c

+ 6 - 0
components/driver/deprecated/adc_dma_legacy.c

@@ -609,6 +609,12 @@ static __attribute__((constructor)) void adc_hw_calibration(void)
              * update this when bringing up the calibration on that chip
              */
             adc_calc_hw_calibration_code(i, j);
+#if SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
+            /* Load the channel compensation from efuse */
+            for (int k = 0; k < SOC_ADC_CHANNEL_NUM(i); k++) {
+                adc_load_hw_calibration_chan_compens(i, k, j);
+            }
+#endif
         }
     }
 }

+ 6 - 0
components/driver/deprecated/adc_legacy.c

@@ -947,6 +947,12 @@ static __attribute__((constructor)) void adc_hw_calibration(void)
              * update this when bringing up the calibration on that chip
              */
             adc_calc_hw_calibration_code(i, j);
+#if SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
+            /* Load the channel compensation from efuse */
+            for (int k = 0; k < SOC_ADC_CHANNEL_NUM(i); k++) {
+                adc_load_hw_calibration_chan_compens(i, k, j);
+            }
+#endif
         }
     }
 }

+ 4 - 4
components/driver/test_apps/legacy_adc_driver/main/test_legacy_adc.c

@@ -53,11 +53,11 @@
 #define ADC_TEST_HIGH_VAL        3400
 #define ADC_TEST_HIGH_THRESH     200
 
-#elif CONFIG_IDF_TARGET_ESP32C6  // TODO: IDF-5312
-#define ADC_TEST_LOW_VAL         2144
-#define ADC_TEST_LOW_THRESH      200
+#elif CONFIG_IDF_TARGET_ESP32C6
+#define ADC_TEST_LOW_VAL         0
+#define ADC_TEST_LOW_THRESH      15
 
-#define ADC_TEST_HIGH_VAL        4081
+#define ADC_TEST_HIGH_VAL        3350
 #define ADC_TEST_HIGH_THRESH     200
 
 #elif CONFIG_IDF_TARGET_ESP32H2  // TODO: IDF-6216

+ 56 - 8
components/efuse/esp32c6/esp_efuse_rtc_calib.c

@@ -8,26 +8,36 @@
 #include "esp_efuse.h"
 #include "esp_efuse_table.h"
 #include "esp_efuse_rtc_calib.h"
+#include "hal/efuse_hal.h"
+
+/**
+ * @brief Get the signed value by the raw data that read from eFuse
+ * @param data  The raw data that read from eFuse
+ * @param sign_bit  The index of the sign bit, start from 0
+ */
+#define RTC_CALIB_GET_SIGNED_VAL(data, sign_bit)    ((data & BIT##sign_bit) ? -(int)(data & ~BIT##sign_bit) : (int)data)
 
 int esp_efuse_rtc_calib_get_ver(void)
 {
-    uint32_t blk_ver_minor = 0;
-    esp_efuse_read_field_blob(ESP_EFUSE_BLK_VERSION_MINOR, &blk_ver_minor, ESP_EFUSE_BLK_VERSION_MINOR[0]->bit_count);
+    uint32_t blk_ver = efuse_hal_blk_version();
 
-    uint32_t cali_version = (blk_ver_minor == 1) ? ESP_EFUSE_ADC_CALIB_VER : 0;
-    if (!cali_version) {
+    uint32_t cali_version = 0;
+    if (blk_ver > 0) {
+        cali_version = ESP_EFUSE_ADC_CALIB_VER;
+    } else {
         ESP_LOGW("eFuse", "calibration efuse version does not match, set default version to 0");
     }
 
-    return blk_ver_minor;
+    return cali_version;
 }
 
 uint32_t esp_efuse_rtc_calib_get_init_code(int version, uint32_t adc_unit, int atten)
 {
     assert(version == ESP_EFUSE_ADC_CALIB_VER);
+    assert(atten >=0 && atten < 4);
     (void) adc_unit;
+
     const esp_efuse_desc_t** init_code_efuse;
-    assert(atten < 4);
     if (atten == 0) {
         init_code_efuse = ESP_EFUSE_ADC1_INIT_CODE_ATTEN0;
     } else if (atten == 1) {
@@ -43,7 +53,45 @@ uint32_t esp_efuse_rtc_calib_get_init_code(int version, uint32_t adc_unit, int a
 
     uint32_t init_code = 0;
     ESP_ERROR_CHECK(esp_efuse_read_field_blob(init_code_efuse, &init_code, init_code_size));
-    return init_code + 1000;    // version 1 logic
+    return init_code + 1600;    // version 1 logic
+}
+
+int esp_efuse_rtc_calib_get_chan_compens(int version, uint32_t adc_unit, uint32_t adc_channel, int atten)
+{
+    assert(version == ESP_EFUSE_ADC_CALIB_VER);
+    assert(atten < 4);
+    assert(adc_channel < SOC_ADC_CHANNEL_NUM(adc_unit));
+
+    const esp_efuse_desc_t** chan_diff_efuse = NULL;
+    switch (adc_channel) {
+        case 0:
+            chan_diff_efuse = ESP_EFUSE_ADC1_INIT_CODE_ATTEN0_CH0;
+            break;
+        case 1:
+            chan_diff_efuse = ESP_EFUSE_ADC1_INIT_CODE_ATTEN0_CH1;
+            break;
+        case 2:
+            chan_diff_efuse = ESP_EFUSE_ADC1_INIT_CODE_ATTEN0_CH2;
+            break;
+        case 3:
+            chan_diff_efuse = ESP_EFUSE_ADC1_INIT_CODE_ATTEN0_CH3;
+            break;
+        case 4:
+            chan_diff_efuse = ESP_EFUSE_ADC1_INIT_CODE_ATTEN0_CH4;
+            break;
+        case 5:
+            chan_diff_efuse = ESP_EFUSE_ADC1_INIT_CODE_ATTEN0_CH5;
+            break;
+        default:
+            chan_diff_efuse = ESP_EFUSE_ADC1_INIT_CODE_ATTEN0_CH6;
+            break;
+    }
+
+    int chan_diff_size = esp_efuse_get_field_size(chan_diff_efuse);
+    assert(chan_diff_size == 4);
+    uint32_t chan_diff = 0;
+    ESP_ERROR_CHECK(esp_efuse_read_field_blob(chan_diff_efuse, &chan_diff, chan_diff_size));
+    return RTC_CALIB_GET_SIGNED_VAL(chan_diff, 3) * (4 - atten);
 }
 
 esp_err_t esp_efuse_rtc_calib_get_cal_voltage(int version, uint32_t adc_unit, int atten, uint32_t* out_digi, uint32_t* out_vol_mv)
@@ -75,7 +123,7 @@ esp_err_t esp_efuse_rtc_calib_get_cal_voltage(int version, uint32_t adc_unit, in
     uint32_t cal_vol = 0;
     ESP_ERROR_CHECK(esp_efuse_read_field_blob(cal_vol_efuse, &cal_vol, cal_vol_efuse[0]->bit_count));
 
-    *out_digi = 2000 + ((cal_vol & BIT(9))? -(cal_vol & ~BIT9): cal_vol);
+    *out_digi = 1500 + RTC_CALIB_GET_SIGNED_VAL(cal_vol, 9);
     *out_vol_mv = calib_vol_expected_mv;
     return ESP_OK;
 }

+ 11 - 0
components/efuse/esp32c6/include/esp_efuse_rtc_calib.h

@@ -31,6 +31,17 @@ int esp_efuse_rtc_calib_get_ver(void);
  */
 uint32_t esp_efuse_rtc_calib_get_init_code(int version, uint32_t adc_unit, int atten);
 
+/**
+ * @brief Get the channel specific calibration compensation
+ *
+ * @param version   Version of the stored efuse
+ * @param adc_unit  ADC unit. Not used, for compatibility. On esp32c6, for calibration v1, both ADC units use the same init code (calibrated by ADC1)
+ * @param adc_channel ADC channel number
+ * @param atten     Attenuation of the init code
+ * @return The channel calibration compensation value
+ */
+int esp_efuse_rtc_calib_get_chan_compens(int version, uint32_t adc_unit, uint32_t adc_channel, int atten);
+
 /**
  * @brief Get the calibration digits stored in the efuse, and the corresponding voltage.
  *

+ 16 - 2
components/esp_adc/adc_cali_curve_fitting.c

@@ -16,6 +16,7 @@
 #include "esp_adc/adc_cali_scheme.h"
 #include "adc_cali_interface.h"
 #include "curve_fitting_coefficients.h"
+#include "esp_private/adc_share_hw_ctrl.h"
 
 #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
 #include "esp_efuse_rtc_calib.h"
@@ -58,6 +59,7 @@ typedef struct {
 
 typedef struct {
     adc_unit_t unit_id;                            ///< ADC unit
+    adc_channel_t chan;                            ///< ADC channel
     adc_atten_t atten;                             ///< ADC attenuation
     cali_chars_first_step_t chars_first_step;      ///< Calibration first step characteristics
     cali_chars_second_step_t chars_second_step;    ///< Calibration second step characteristics
@@ -105,6 +107,7 @@ esp_err_t adc_cali_create_scheme_curve_fitting(const adc_cali_curve_fitting_conf
     //Set second step calibration context
     calc_second_step_coefficients(config, chars);
     chars->unit_id = config->unit_id;
+    chars->chan = config->chan;
     chars->atten = config->atten;
 
     *ret_handle = scheme;
@@ -138,6 +141,16 @@ static esp_err_t cali_raw_to_voltage(void *arg, int raw, int *voltage)
     //pointers are checked in the upper layer
 
     cali_chars_curve_fitting_t *ctx = arg;
+
+#if SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
+    int chan_compensation = adc_get_hw_calibration_chan_compens(ctx->unit_id, ctx->chan, ctx->atten);
+    raw -= chan_compensation;
+    /* Limit the range */
+    int max_val = (1L << SOC_ADC_RTC_MAX_BITWIDTH) - 1;
+    raw = raw <= 0 ? 0 :
+          raw > max_val ? max_val : raw;
+#endif  // SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
+
     uint64_t v_cali_1 = raw * ctx->chars_first_step.coeff_a / coeff_a_scaling + ctx->chars_first_step.coeff_b;
     int32_t error = get_reading_error(v_cali_1, &(ctx->chars_second_step), ctx->atten);
 
@@ -180,8 +193,9 @@ static void calc_first_step_coefficients(const adc_calib_info_t *parsed_data, ca
 static void calc_second_step_coefficients(const adc_cali_curve_fitting_config_t *config, cali_chars_curve_fitting_t *ctx)
 {
     ctx->chars_second_step.term_num = (config->atten == 3) ? 5 : 3;
-#if CONFIG_IDF_TARGET_ESP32C3
-    //On esp32c3, ADC1 and ADC2 share the second step coefficients
+#if CONFIG_IDF_TARGET_ESP32C3 || SOC_ADC_PERIPH_NUM == 1
+    // On esp32c3, ADC1 and ADC2 share the second step coefficients
+    // And if the target only has 1 ADC peripheral, just use the ADC1 directly
     ctx->chars_second_step.coeff = &adc1_error_coef_atten;
     ctx->chars_second_step.sign = &adc1_error_sign;
 #else

+ 6 - 0
components/esp_adc/adc_common.c

@@ -102,6 +102,12 @@ static __attribute__((constructor)) void adc_hw_calibration(void)
              * update this when bringing up the calibration on that chip
              */
             adc_calc_hw_calibration_code(i, j);
+#if SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
+            /* Load the channel compensation from efuse */
+            for (int k = 0; k < SOC_ADC_CHANNEL_NUM(i); k++) {
+                adc_load_hw_calibration_chan_compens(i, k, j);
+            }
+#endif
         }
     }
 }

+ 1 - 1
components/esp_adc/adc_oneshot.c

@@ -173,7 +173,7 @@ esp_err_t adc_oneshot_read(adc_oneshot_unit_handle_t handle, adc_channel_t chan,
     adc_atten_t atten = adc_ll_get_atten(handle->unit_id, chan);
     adc_hal_calibration_init(handle->unit_id);
     adc_set_hw_calibration_code(handle->unit_id, atten);
-#endif
+#endif  // SOC_ADC_CALIBRATION_V1_SUPPORTED
     bool valid = false;
     valid = adc_oneshot_hal_convert(&(handle->hal), out_raw);
     sar_periph_ctrl_adc_oneshot_power_release();

+ 1 - 2
components/esp_adc/esp32c6/curve_fitting_coefficients.c

@@ -14,10 +14,9 @@
  * For each item, first element is the Coefficient, second element is the Multiple. (Coefficient / Multiple) is the real coefficient.
  *
  * @note {0,0} stands for unused item
- * @note In case of the overflow, these coeffcients are recorded as Absolute Value
+ * @note In case of the overflow, these coefficients are recorded as Absolute Value
  * @note For atten0 ~ 2, error = (K0 * X^0) + (K1 * X^1) + (K2 * X^2); For atten3, error = (K0 * X^0) + (K1 * X^1)  + (K2 * X^2) + (K3 * X^3) + (K4 * X^4);
  * @note Above formula is rewritten from the original documentation, please note that the coefficients are re-ordered.
- * @note ADC1 and ADC2 use same coeffients
  */
 const uint64_t adc1_error_coef_atten[4][5][2] = {
                                                 {{487166399931449,   1e16}, {6436483033201,   1e16}, {30410131806, 1e16}, {0, 0}, {0, 0}},   //atten0

+ 1 - 0
components/esp_adc/include/esp_adc/adc_cali_scheme.h

@@ -22,6 +22,7 @@ extern "C" {
 ---------------------------------------------------------------*/
 typedef struct {
     adc_unit_t unit_id;         ///< ADC unit
+    adc_channel_t chan;         ///< ADC channel, for chips with SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED, calibration can be per channel
     adc_atten_t atten;          ///< ADC attenuation
     adc_bitwidth_t bitwidth;    ///< ADC raw output bitwidth
 } adc_cali_curve_fitting_config_t;

+ 2 - 2
components/esp_adc/test_apps/adc/main/test_adc.c

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -149,7 +149,7 @@ static void s_adc_oneshot_with_sleep(adc_unit_t unit_id, adc_channel_t channel)
     bool do_calibration = false;
     adc_cali_handle_t cali_handle[TEST_ATTEN_NUMS] = {};
     for (int i = 0; i < TEST_ATTEN_NUMS; i++) {
-        do_calibration = test_adc_calibration_init(unit_id, g_test_atten[i], SOC_ADC_RTC_MAX_BITWIDTH, &cali_handle[i]);
+        do_calibration = test_adc_calibration_init(unit_id, channel, g_test_atten[i], SOC_ADC_RTC_MAX_BITWIDTH, &cali_handle[i]);
     }
     if (!do_calibration) {
         ESP_LOGW(TAG, "No efuse bits burnt, only test the regi2c analog register values");

+ 2 - 2
components/esp_adc/test_apps/adc/main/test_adc_performance.c

@@ -324,7 +324,7 @@ static float test_adc_oneshot_std(adc_atten_t atten, bool is_performance_test)
     //-------------ADC Calibration Init---------------//
     bool do_calibration = false;
     adc_cali_handle_t cali_handle = NULL;
-    do_calibration = test_adc_calibration_init(ADC_UNIT_1, atten, ADC_BITWIDTH_DEFAULT, &cali_handle);
+    do_calibration = test_adc_calibration_init(ADC_UNIT_1, channel, atten, ADC_BITWIDTH_DEFAULT, &cali_handle);
     if (!do_calibration) {
         ESP_LOGW(TAG, "calibration fail, jump calibration\n");
     }
@@ -420,7 +420,7 @@ static void s_adc_cali_speed(adc_unit_t unit_id, adc_channel_t channel)
     bool do_calibration = false;
     adc_cali_handle_t cali_handle[TEST_ATTEN_NUMS] = {};
     for (int i = 0; i < TEST_ATTEN_NUMS; i++) {
-        do_calibration = test_adc_calibration_init(unit_id, g_test_atten[i], SOC_ADC_RTC_MAX_BITWIDTH, &cali_handle[i]);
+        do_calibration = test_adc_calibration_init(unit_id, channel, g_test_atten[i], SOC_ADC_RTC_MAX_BITWIDTH, &cali_handle[i]);
     }
 
     if (!do_calibration) {

+ 2 - 1
components/esp_adc/test_apps/adc/main/test_common_adc.c

@@ -39,7 +39,7 @@ adc_digi_iir_filter_coeff_t g_test_filter_coeff[TEST_FILTER_COEFF_NUMS] = {
 /*---------------------------------------------------------------
         ADC Calibration
 ---------------------------------------------------------------*/
-bool test_adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_bitwidth_t bitwidth, adc_cali_handle_t *out_handle)
+bool test_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_bitwidth_t bitwidth, adc_cali_handle_t *out_handle)
 {
     esp_err_t ret = ESP_FAIL;
     adc_cali_handle_t handle = NULL;
@@ -49,6 +49,7 @@ bool test_adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_bitwidth_
     ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
     adc_cali_curve_fitting_config_t cali_config = {
         .unit_id = unit,
+        .chan = channel,
         .atten = atten,
         .bitwidth = bitwidth,
     };

+ 10 - 6
components/esp_adc/test_apps/adc/main/test_common_adc.h

@@ -71,11 +71,11 @@ extern "C" {
 #define ADC_TEST_HIGH_VAL        3400
 #define ADC_TEST_HIGH_THRESH     200
 
-#elif CONFIG_IDF_TARGET_ESP32C6  // TODO: IDF-5312
-#define ADC_TEST_LOW_VAL         2144
-#define ADC_TEST_LOW_THRESH      200
+#elif CONFIG_IDF_TARGET_ESP32C6
+#define ADC_TEST_LOW_VAL         0
+#define ADC_TEST_LOW_THRESH      15
 
-#define ADC_TEST_HIGH_VAL        4081
+#define ADC_TEST_HIGH_VAL        3350
 #define ADC_TEST_HIGH_VAL_DMA    4081
 #define ADC_TEST_HIGH_THRESH     200
 
@@ -114,13 +114,17 @@ extern adc_digi_iir_filter_coeff_t g_test_filter_coeff[TEST_FILTER_COEFF_NUMS];
 /**
  * @brief Initialise ADC Calibration
  *
- * @param[out] out_handle    ADC calibration handle
+ * @param[in]  unit         ADC unit
+ * @param[in]  channel      ADC channel
+ * @param[in]  atten        ADC attenuation
+ * @param[in]  bitwidth     ADC bit width
+ * @param[out] out_handle   ADC calibration handle
  *
  * @return
  *        - True  Calibration success
  *        - False Calibration fail
  */
-bool test_adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_bitwidth_t bitwidth, adc_cali_handle_t *out_handle);
+bool test_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_bitwidth_t bitwidth, adc_cali_handle_t *out_handle);
 
 /**
  * @brief De-initialise ADC Calibration

+ 14 - 0
components/esp_hw_support/adc_share_hw_ctrl.c

@@ -96,6 +96,20 @@ void IRAM_ATTR adc_set_hw_calibration_code(adc_unit_t adc_n, adc_atten_t atten)
 {
     adc_hal_set_calibration_param(adc_n, s_adc_cali_param[adc_n][atten]);
 }
+
+#if SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
+static int s_adc_cali_chan_compens[SOC_ADC_MAX_CHANNEL_NUM][SOC_ADC_ATTEN_NUM] = {};
+void adc_load_hw_calibration_chan_compens(adc_unit_t adc_n, adc_channel_t chan, adc_atten_t atten)
+{
+    int version = esp_efuse_rtc_calib_get_ver();
+    s_adc_cali_chan_compens[chan][atten] = esp_efuse_rtc_calib_get_chan_compens(version, adc_n, chan, atten);
+}
+
+int IRAM_ATTR adc_get_hw_calibration_chan_compens(adc_unit_t adc_n, adc_channel_t chan, adc_atten_t atten)
+{
+    return s_adc_cali_chan_compens[chan][atten];
+}
+#endif  // SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
 #endif //#if SOC_ADC_CALIBRATION_V1_SUPPORTED
 
 

+ 22 - 0
components/esp_hw_support/include/esp_private/adc_share_hw_ctrl.h

@@ -45,6 +45,28 @@ void adc_calc_hw_calibration_code(adc_unit_t adc_n, adc_atten_t atten);
  * @param atten Attenuation to use
  */
 void adc_set_hw_calibration_code(adc_unit_t adc_n, adc_atten_t atten);
+
+#if SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
+/**
+ * @brief Load the channel compensation of the ADC HW calibration from eFuse to a static array
+ *
+ * @param adc_n ADC unit to compensation
+ * @param chan  ADC channel to compensation
+ * @param atten Attenuation to use
+ */
+void adc_load_hw_calibration_chan_compens(adc_unit_t adc_n, adc_channel_t chan, adc_atten_t atten);
+
+/**
+ * @brief Get the channel compensation of the ADC HW calibration from the static array
+ *        that have been loaded from eFuse
+ *
+ * @param adc_n ADC unit to compensation
+ * @param chan  ADC channel to compensation
+ * @param atten Attenuation to use
+ * @return The channel compensation
+ */
+int adc_get_hw_calibration_chan_compens(adc_unit_t adc_n, adc_channel_t chan, adc_atten_t atten);
+#endif  // SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
 #endif //#if SOC_ADC_CALIBRATION_V1_SUPPORTED
 
 

+ 1 - 0
components/esp_hw_support/port/esp32c6/CMakeLists.txt

@@ -5,6 +5,7 @@ set(srcs "rtc_clk_init.c"
          "pmu_sleep.c"
          "rtc_time.c"
          "chip_info.c"
+         "ocode_init.c"
          )
 
 if(NOT BOOTLOADER_BUILD)

+ 98 - 0
components/esp_hw_support/port/esp32c6/ocode_init.c

@@ -0,0 +1,98 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdint.h>
+#include "soc/soc.h"
+#include "soc/rtc.h"
+#include "soc/regi2c_dig_reg.h"
+#include "soc/regi2c_lp_bias.h"
+#include "hal/efuse_hal.h"
+#include "hal/efuse_ll.h"
+#include "regi2c_ctrl.h"
+#include "esp_hw_log.h"
+
+
+static const char *TAG = "ocode_init";
+
+static void set_ocode_by_efuse(int ocode_scheme_ver)
+{
+    assert(ocode_scheme_ver == 1);
+    unsigned int ocode = efuse_ll_get_ocode();
+
+    //set ext_ocode
+    REGI2C_WRITE_MASK(I2C_ULP, I2C_ULP_EXT_CODE, ocode);
+    REGI2C_WRITE_MASK(I2C_ULP, I2C_ULP_IR_FORCE_CODE, 1);
+}
+
+static void calibrate_ocode(void)
+{
+    /*
+    Bandgap output voltage is not precise when calibrate o-code by hardware sometimes, so need software o-code calibration (must turn off PLL).
+    Method:
+    1. read current cpu config, save in old_config;
+    2. switch cpu to xtal because PLL will be closed when o-code calibration;
+    3. begin o-code calibration;
+    4. wait o-code calibration done flag(odone_flag & bg_odone_flag) or timeout;
+    5. set cpu to old-config.
+    */
+    soc_rtc_slow_clk_src_t slow_clk_src = rtc_clk_slow_src_get();
+    rtc_cal_sel_t cal_clk = RTC_CAL_RTC_MUX;
+    if (slow_clk_src == SOC_RTC_SLOW_CLK_SRC_OSC_SLOW) {
+        cal_clk = RTC_CAL_32K_OSC_SLOW;
+    } else if (slow_clk_src == SOC_RTC_SLOW_CLK_SRC_XTAL32K) {
+        cal_clk  = RTC_CAL_32K_XTAL;
+    }
+
+    uint64_t max_delay_time_us = 10000;
+    uint32_t slow_clk_period = rtc_clk_cal(cal_clk, 100);
+    uint64_t max_delay_cycle = rtc_time_us_to_slowclk(max_delay_time_us, slow_clk_period);
+    uint64_t cycle0 = rtc_time_get();
+    uint64_t timeout_cycle = cycle0 + max_delay_cycle;
+    uint64_t cycle1 = 0;
+
+    rtc_cpu_freq_config_t old_config;
+    rtc_clk_cpu_freq_get_config(&old_config);
+    rtc_clk_cpu_freq_set_xtal();
+
+    REGI2C_WRITE_MASK(I2C_ULP, I2C_ULP_IR_RESETB, 0);
+    REGI2C_WRITE_MASK(I2C_ULP, I2C_ULP_IR_RESETB, 1);
+    bool odone_flag = 0;
+    bool bg_odone_flag = 0;
+    while (1) {
+        odone_flag = REGI2C_READ_MASK(I2C_ULP, I2C_ULP_O_DONE_FLAG);
+        bg_odone_flag = REGI2C_READ_MASK(I2C_ULP, I2C_ULP_BG_O_DONE_FLAG);
+        cycle1 = rtc_time_get();
+        if (odone_flag && bg_odone_flag) {
+            break;
+        }
+        if (cycle1 >= timeout_cycle) {
+            ESP_HW_LOGW(TAG, "o_code calibration fail\n");
+            break;
+        }
+    }
+    rtc_clk_cpu_freq_set_config(&old_config);
+}
+
+void esp_ocode_calib_init(void)
+{
+    bool ignore_major = efuse_ll_get_disable_blk_version_major();
+    uint32_t blk_version = efuse_hal_blk_version();
+
+    uint8_t ocode_scheme_ver = 0;
+    if(blk_version == 0 && !ignore_major) {
+        ESP_HW_LOGE(TAG, "Invalid blk_version\n");
+        abort();
+    }
+    if(blk_version > 0) {
+        ocode_scheme_ver = 1;
+    }
+
+    if (ocode_scheme_ver == 1) {
+        set_ocode_by_efuse(ocode_scheme_ver);
+    } else {
+        calibrate_ocode();
+    }
+}

+ 21 - 0
components/esp_hw_support/port/esp32c6/private_include/ocode_init.h

@@ -0,0 +1,21 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Initialize OCode
+ *
+ */
+void esp_ocode_calib_init(void);
+
+#ifdef __cplusplus
+}
+#endif

+ 4 - 0
components/esp_system/port/soc/esp32c6/clk.c

@@ -26,6 +26,7 @@
 #include "esp_private/esp_pmu.h"
 #include "esp_rom_uart.h"
 #include "esp_rom_sys.h"
+#include "ocode_init.h"
 
 /* Number of cycles to wait from the 32k XTAL oscillator to consider it running.
  * Larger values increase startup delay. Smaller values may cause false positive
@@ -44,6 +45,9 @@ static const char *TAG = "clk";
 {
 #if !CONFIG_IDF_ENV_FPGA
     pmu_init();
+    if (esp_rom_get_reset_reason(0) == RESET_REASON_CHIP_POWER_ON) {
+        esp_ocode_calib_init();
+    }
 
     assert(rtc_clk_xtal_freq_get() == RTC_XTAL_FREQ_40M);
 

+ 1 - 2
components/hal/adc_hal_common.c

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -204,7 +204,6 @@ uint32_t adc_hal_self_calibration(adc_unit_t adc_n, adc_atten_t atten, bool inte
 
     adc_ll_calibration_finish(adc_n);
     return ret;
-    return 0;
 }
 #endif  //#if SOC_ADC_SELF_HW_CALI_SUPPORTED
 #endif //SOC_ADC_CALIBRATION_V1_SUPPORTED

+ 5 - 0
components/hal/efuse_hal.c

@@ -24,6 +24,11 @@ IRAM_ATTR uint32_t efuse_hal_chip_revision(void)
     return efuse_hal_get_major_chip_version() * 100 + efuse_hal_get_minor_chip_version();
 }
 
+uint32_t efuse_hal_blk_version(void)
+{
+    return efuse_ll_get_blk_version_major() * 100 + efuse_ll_get_blk_version_minor();
+}
+
 IRAM_ATTR bool efuse_hal_get_disable_wafer_version_major(void)
 {
     return efuse_ll_get_disable_wafer_version_major();

+ 10 - 0
components/hal/esp32/include/hal/efuse_ll.h

@@ -119,6 +119,16 @@ __attribute__((always_inline)) static inline bool efuse_ll_get_disable_wafer_ver
     return false;
 }
 
+__attribute__((always_inline)) static inline bool efuse_ll_get_blk_version_major(void)
+{
+    return 0;
+}
+
+__attribute__((always_inline)) static inline uint32_t efuse_ll_get_blk_version_minor(void)
+{
+    return 0;
+}
+
 __attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_scheme(void)
 {
     return EFUSE.blk0_rdata6.rd_coding_scheme;

+ 1 - 1
components/hal/esp32c6/include/hal/adc_ll.h

@@ -540,7 +540,7 @@ static inline void adc_ll_set_controller(adc_unit_t adc_n, adc_ll_controller_t c
 __attribute__((always_inline))
 static inline void adc_ll_calibration_init(adc_unit_t adc_n)
 {
-    HAL_ASSERT(adc_n == ADC_UNIT_1);
+    (void)adc_n;
     REGI2C_WRITE_MASK(I2C_SAR_ADC, ADC_SAR1_DREF_ADDR, 1);
 }
 

+ 5 - 0
components/hal/esp32c6/include/hal/efuse_ll.h

@@ -82,6 +82,11 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_chip_ver_pkg(
     return EFUSE.rd_mac_spi_sys_3.pkg_version;
 }
 
+__attribute__((always_inline)) static inline uint32_t efuse_ll_get_ocode(void)
+{
+    return EFUSE.rd_sys_part1_data4.ocode;
+}
+
 /******************* eFuse control functions *************************/
 
 __attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void)

+ 1 - 1
components/hal/esp32h2/include/hal/adc_ll.h

@@ -540,7 +540,7 @@ static inline void adc_ll_set_controller(adc_unit_t adc_n, adc_ll_controller_t c
 __attribute__((always_inline))
 static inline void adc_ll_calibration_init(adc_unit_t adc_n)
 {
-    HAL_ASSERT(adc_n == ADC_UNIT_1);
+    (void)adc_n;
     REGI2C_WRITE_MASK(I2C_SAR_ADC, ADC_SAR1_DREF_ADDR, 1);
 }
 

+ 7 - 0
components/hal/include/hal/efuse_hal.h

@@ -27,6 +27,13 @@ void efuse_hal_get_mac(uint8_t *mac);
  */
 uint32_t efuse_hal_chip_revision(void);
 
+/**
+ * @brief Return block version
+ *
+ * @return Block version in format: Major * 100 + Minor
+ */
+uint32_t efuse_hal_blk_version(void);
+
 /**
  * @brief Is flash encryption currently enabled in hardware?
  *

+ 7 - 7
components/idf_test/include/esp32c6/idf_performance_target.h

@@ -23,10 +23,10 @@
 #define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING_NO_DMA                     32
 #define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING_NO_DMA                        15
 
-//TODO: IDF-5312
-#define IDF_PERFORMANCE_MAX_ADC_CONTINUOUS_STD_ATTEN3_NO_FILTER                 10
-#define IDF_PERFORMANCE_MAX_ADC_CONTINUOUS_STD_ATTEN3_FILTER_2                  10
-#define IDF_PERFORMANCE_MAX_ADC_CONTINUOUS_STD_ATTEN3_FILTER_4                  10
-#define IDF_PERFORMANCE_MAX_ADC_CONTINUOUS_STD_ATTEN3_FILTER_8                  10
-#define IDF_PERFORMANCE_MAX_ADC_CONTINUOUS_STD_ATTEN3_FILTER_16                 10
-#define IDF_PERFORMANCE_MAX_ADC_CONTINUOUS_STD_ATTEN3_FILTER_64                 10
+#define IDF_PERFORMANCE_MAX_ADC_CONTINUOUS_STD_ATTEN3_NO_FILTER                 5
+#define IDF_PERFORMANCE_MAX_ADC_CONTINUOUS_STD_ATTEN3_FILTER_2                  5
+#define IDF_PERFORMANCE_MAX_ADC_CONTINUOUS_STD_ATTEN3_FILTER_4                  5
+#define IDF_PERFORMANCE_MAX_ADC_CONTINUOUS_STD_ATTEN3_FILTER_8                  5
+#define IDF_PERFORMANCE_MAX_ADC_CONTINUOUS_STD_ATTEN3_FILTER_16                 5
+#define IDF_PERFORMANCE_MAX_ADC_CONTINUOUS_STD_ATTEN3_FILTER_64                 5
+#define IDF_PERFORMANCE_MAX_ADC_ONESHOT_STD_ATTEN3                              5

+ 9 - 1
components/soc/esp32c6/include/soc/Kconfig.soc_caps.in

@@ -285,7 +285,15 @@ config SOC_ADC_RTC_MAX_BITWIDTH
 
 config SOC_ADC_CALIBRATION_V1_SUPPORTED
     bool
-    default n
+    default y
+
+config SOC_ADC_SELF_HW_CALI_SUPPORTED
+    bool
+    default y
+
+config SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
+    bool
+    default y
 
 config SOC_ADC_TEMPERATURE_SHARE_INTR
     bool

+ 3 - 1
components/soc/esp32c6/include/soc/soc_caps.h

@@ -114,7 +114,9 @@
 #define SOC_ADC_RTC_MAX_BITWIDTH                (12)
 
 /*!< Calibration */
-#define SOC_ADC_CALIBRATION_V1_SUPPORTED        (0) /*!< support HW offset calibration version 1*/
+#define SOC_ADC_CALIBRATION_V1_SUPPORTED        (1) /*!< support HW offset calibration version 1*/
+#define SOC_ADC_SELF_HW_CALI_SUPPORTED          (1) /*!< support HW offset self calibration */
+#define SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED (1) /*!< support channel compensation to the HW offset calibration */
 
 /*!< Interrupt */
 #define SOC_ADC_TEMPERATURE_SHARE_INTR          (1)

+ 15 - 4
docs/en/api-reference/peripherals/adc_calibration.rst

@@ -80,16 +80,27 @@ For those users who use their custom ADC calibration schemes, you could either m
         ESP_ERROR_CHECK(adc_cali_delete_scheme_line_fitting(handle));
 
 
-.. only:: esp32c3 or esp32s3
+.. only:: esp32c3 or esp32s3 or esp32c6
 
     ADC Calibration Curve Fitting Scheme
     ````````````````````````````````````
 
     {IDF_TARGET_NAME} supports :c:macro:`ADC_CALI_SCHEME_VER_CURVE_FITTING` scheme. To create this scheme, set up :cpp:type:`adc_cali_curve_fitting_config_t` first.
 
-    -  :cpp:member:`adc_cali_curve_fitting_config_t::unit_id`, the ADC that your ADC raw results are from.
-    -  :cpp:member:`adc_cali_curve_fitting_config_t::atten`, ADC attenuation that your ADC raw results use.
-    -  :cpp:member:`adc_cali_curve_fitting_config_t::bitwidth`, the ADC raw result bitwidth.
+
+    .. only:: not esp32c6
+
+        -  :cpp:member:`adc_cali_curve_fitting_config_t::unit_id`, the ADC that your ADC raw results are from.
+        -  :cpp:member:`adc_cali_curve_fitting_config_t::chan`, this member is kept here for extensibility. The calibration scheme only differs by attenuation, there's no difference among different channels.
+        -  :cpp:member:`adc_cali_curve_fitting_config_t::atten`, ADC attenuation that your ADC raw results use.
+        -  :cpp:member:`adc_cali_curve_fitting_config_t::bitwidth`, the ADC raw result bitwidth.
+
+    .. only:: esp32c6
+
+        -  :cpp:member:`adc_cali_curve_fitting_config_t::unit_id`, the ADC that your ADC raw results are from.
+        -  :cpp:member:`adc_cali_curve_fitting_config_t::chan`, the ADC channel that your ADC raw results are from. The calibration scheme not only differs by attenuation but also related to the channels.
+        -  :cpp:member:`adc_cali_curve_fitting_config_t::atten`, ADC attenuation that your ADC raw results use.
+        -  :cpp:member:`adc_cali_curve_fitting_config_t::bitwidth`, the ADC raw result bitwidth.
 
     After setting up the configuration structure, call :cpp:func:`adc_cali_create_scheme_curve_fitting` to create a Curve Fitting calibration scheme handle. This function may fail due to reasons such as :c:macro:`ESP_ERR_INVALID_ARG` or :c:macro:`ESP_ERR_NO_MEM`. Especially, when the function return :c:macro:`ESP_ERR_NOT_SUPPORTED`, this means the calibration scheme required eFuse bits are not burnt on your board.
 

+ 18 - 12
examples/peripherals/adc/oneshot_read/main/oneshot_read_main.c

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -49,7 +49,7 @@ const static char *TAG = "EXAMPLE";
 
 static int adc_raw[2][10];
 static int voltage[2][10];
-static bool example_adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_cali_handle_t *out_handle);
+static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle);
 static void example_adc_calibration_deinit(adc_cali_handle_t handle);
 
 
@@ -71,8 +71,10 @@ void app_main(void)
     ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, EXAMPLE_ADC1_CHAN1, &config));
 
     //-------------ADC1 Calibration Init---------------//
-    adc_cali_handle_t adc1_cali_handle = NULL;
-    bool do_calibration1 = example_adc_calibration_init(ADC_UNIT_1, EXAMPLE_ADC_ATTEN, &adc1_cali_handle);
+    adc_cali_handle_t adc1_cali_chan0_handle = NULL;
+    adc_cali_handle_t adc1_cali_chan1_handle = NULL;
+    bool do_calibration1_chan0 = example_adc_calibration_init(ADC_UNIT_1, EXAMPLE_ADC1_CHAN0, EXAMPLE_ADC_ATTEN, &adc1_cali_chan0_handle);
+    bool do_calibration1_chan1 = example_adc_calibration_init(ADC_UNIT_1, EXAMPLE_ADC1_CHAN1, EXAMPLE_ADC_ATTEN, &adc1_cali_chan1_handle);
 
 
 #if EXAMPLE_USE_ADC2
@@ -86,7 +88,7 @@ void app_main(void)
 
     //-------------ADC2 Calibration Init---------------//
     adc_cali_handle_t adc2_cali_handle = NULL;
-    bool do_calibration2 = example_adc_calibration_init(ADC_UNIT_2, EXAMPLE_ADC_ATTEN, &adc2_cali_handle);
+    bool do_calibration2 = example_adc_calibration_init(ADC_UNIT_2, EXAMPLE_ADC2_CHAN0, EXAMPLE_ADC_ATTEN, &adc2_cali_handle);
 
     //-------------ADC2 Config---------------//
     ESP_ERROR_CHECK(adc_oneshot_config_channel(adc2_handle, EXAMPLE_ADC2_CHAN0, &config));
@@ -95,16 +97,16 @@ void app_main(void)
     while (1) {
         ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN0, &adc_raw[0][0]));
         ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN0, adc_raw[0][0]);
-        if (do_calibration1) {
-            ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_handle, adc_raw[0][0], &voltage[0][0]));
+        if (do_calibration1_chan0) {
+            ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_chan0_handle, adc_raw[0][0], &voltage[0][0]));
             ESP_LOGI(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN0, voltage[0][0]);
         }
         vTaskDelay(pdMS_TO_TICKS(1000));
 
         ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN1, &adc_raw[0][1]));
         ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN1, adc_raw[0][1]);
-        if (do_calibration1) {
-            ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_handle, adc_raw[0][1], &voltage[0][1]));
+        if (do_calibration1_chan1) {
+            ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_chan1_handle, adc_raw[0][1], &voltage[0][1]));
             ESP_LOGI(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN1, voltage[0][1]);
         }
         vTaskDelay(pdMS_TO_TICKS(1000));
@@ -122,8 +124,11 @@ void app_main(void)
 
     //Tear Down
     ESP_ERROR_CHECK(adc_oneshot_del_unit(adc1_handle));
-    if (do_calibration1) {
-        example_adc_calibration_deinit(adc1_cali_handle);
+    if (do_calibration1_chan0) {
+        example_adc_calibration_deinit(adc1_cali_chan0_handle);
+    }
+    if (do_calibration1_chan1) {
+        example_adc_calibration_deinit(adc1_cali_chan1_handle);
     }
 
 #if EXAMPLE_USE_ADC2
@@ -138,7 +143,7 @@ void app_main(void)
 /*---------------------------------------------------------------
         ADC Calibration
 ---------------------------------------------------------------*/
-static bool example_adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_cali_handle_t *out_handle)
+static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle)
 {
     adc_cali_handle_t handle = NULL;
     esp_err_t ret = ESP_FAIL;
@@ -149,6 +154,7 @@ static bool example_adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc
         ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
         adc_cali_curve_fitting_config_t cali_config = {
             .unit_id = unit,
+            .chan = channel,
             .atten = atten,
             .bitwidth = ADC_BITWIDTH_DEFAULT,
         };