From 43afc527438ea55086c35bb0db9f3938c660e8fc Mon Sep 17 00:00:00 2001
From: yanke <yanke2@espressif.com>
Date: Thu, 12 Dec 2024 15:25:49 +0800
Subject: [PATCH] feat(adc): support esp32s2 adc range above 2500mv

---
 components/esp_adc/Kconfig                    | 23 +++++++
 components/esp_adc/adc_oneshot.c              | 61 +++++++++++++++++++
 .../esp_adc/esp32s2/adc_cali_line_fitting.c   |  7 ++-
 components/esp_hw_support/adc_share_hw_ctrl.c |  7 +++
 4 files changed, 97 insertions(+), 1 deletion(-)

diff --git a/components/esp_adc/Kconfig b/components/esp_adc/Kconfig
index 0bdf4dd73e..f9fcb88822 100644
--- a/components/esp_adc/Kconfig
+++ b/components/esp_adc/Kconfig
@@ -66,6 +66,29 @@ menu "ADC and ADC Calibration"
             If you stick to this, you can enable this option to force use ADC2 under above conditions.
             For more details, you can search for errata on espressif website.
 
+    menu "ADC User Code Offset"
+        depends on IDF_TARGET_ESP32S2
+        config ENABLE_ADC_USER_CODE_OFFSET
+            bool "Enable ADC user code offset"
+            default y
+            help
+                On ESP32S2, you can enable the USER_CODE_OFFSET setting to adjust the ADC range to 1000mV - 3300mV.
+
+        choice
+            prompt "ADC calibration type"
+            depends on ENABLE_ADC_USER_CODE_OFFSET
+            default ADC_CAL_TYPE_FLOAT
+            config ADC_CAL_TYPE_FLOAT
+                bool "Float"
+                help
+                    Use float type for ADC calibration calculations.
+            config ADC_CAL_TYPE_DOUBLE
+                bool "Double"
+                help
+                    Use double type for ADC calibration calculations.
+        endchoice
+    endmenu
+
     config ADC_ONESHOT_FORCE_USE_ADC2_ON_C3
         depends on IDF_TARGET_ESP32C3
         bool "Force use ADC2 oneshot mode on ESP32C3"
diff --git a/components/esp_adc/adc_oneshot.c b/components/esp_adc/adc_oneshot.c
index 0691e22f1a..dceeea4735 100644
--- a/components/esp_adc/adc_oneshot.c
+++ b/components/esp_adc/adc_oneshot.c
@@ -166,6 +166,29 @@ esp_err_t adc_oneshot_config_channel(adc_oneshot_unit_handle_t handle, adc_chann
     return ESP_OK;
 }
 
+
+#if (CONFIG_ENABLE_ADC_USER_CODE_OFFSET & CONFIG_IDF_TARGET_ESP32S2)
+static int16_t g_adc_cal_delta = 0;
+static int16_t g_adc_cal_delta_actual = 0;
+/**
+ * @brief Set adc1 calibration delta value
+ * 
+ * @param delta_mv delta value in mv, This value will be added to the calibration value. 
+ *
+ */
+void adc1_set_cal_delta(int16_t delta_mv)
+{
+    g_adc_cal_delta = delta_mv * 1.54f;
+}
+int16_t adc1_get_cal_delta()
+{
+    return g_adc_cal_delta_actual;
+}
+
+extern uint32_t get_calibration_offset(adc_unit_t adc_n, adc_atten_t atten);
+#endif
+
+
 esp_err_t adc_oneshot_read(adc_oneshot_unit_handle_t handle, adc_channel_t chan, int *out_raw)
 {
     ESP_RETURN_ON_FALSE(handle && out_raw, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
@@ -180,7 +203,16 @@ esp_err_t adc_oneshot_read(adc_oneshot_unit_handle_t handle, adc_channel_t chan,
 #if SOC_ADC_CALIBRATION_V1_SUPPORTED
     adc_atten_t atten = adc_ll_get_atten(handle->unit_id, chan);
     adc_hal_calibration_init(handle->unit_id);
+#if (CONFIG_ENABLE_ADC_USER_CODE_OFFSET & CONFIG_IDF_TARGET_ESP32S2)
+    adc_calc_hw_calibration_code(handle->unit_id, atten);
+    uint32_t cal_val = get_calibration_offset(handle->unit_id, atten);
+    uint32_t cal_val_new = cal_val + g_adc_cal_delta;
+    cal_val_new = cal_val_new > 4095 ? 4095 : cal_val_new;
+    g_adc_cal_delta_actual = cal_val_new - cal_val;
+    adc_hal_set_calibration_param(handle->unit_id, cal_val_new);
+#else 
     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);
@@ -255,6 +287,35 @@ esp_err_t adc_oneshot_get_calibrated_result(adc_oneshot_unit_handle_t handle, ad
     ESP_LOGD(TAG, "raw: 0d%d", raw);
     ESP_RETURN_ON_ERROR(adc_cali_raw_to_voltage(cali_handle, raw, cali_result), TAG, "adc calibration fail");
 
+#if (CONFIG_IDF_TARGET_ESP32S2 & CONFIG_ENABLE_ADC_USER_CODE_OFFSET)
+    int voltage_b = 0;
+#if CONFIG_ADC_CAL_TYPE_DOUBLE
+    typedef const double ADC_CAL_TYPE;
+#else
+    typedef const float ADC_CAL_TYPE;
+#endif
+
+    adc_atten_t atten = adc_ll_get_atten(handle->unit_id, chan);
+    if(atten == ADC_ATTEN_DB_12) {
+        if(*cali_result > 2600) {
+        ESP_LOGD(TAG, "first is %u", *cali_result);
+        adc1_set_cal_delta(1000);
+        ESP_RETURN_ON_ERROR(adc_oneshot_read(handle, chan, &raw), TAG, "adc oneshot read fail");
+        adc1_set_cal_delta(0);
+        ESP_RETURN_ON_ERROR(adc_cali_raw_to_voltage(cali_handle, raw, &voltage_b), TAG, "adc calibration fail");
+
+        ESP_LOGD(TAG, "before is %u", voltage_b);
+        ADC_CAL_TYPE a = -0.0000050800531;
+        ADC_CAL_TYPE b =  0.02334678273232382;
+        ADC_CAL_TYPE c =  -26.699083271336267;
+        ADC_CAL_TYPE e = a * voltage_b * voltage_b + b * voltage_b + c;
+        voltage_b = voltage_b * (1 + e / 100);
+        ESP_LOGD(TAG, "after is %u", voltage_b);
+        *cali_result = voltage_b;
+        }
+    }
+#endif
+
     return ESP_OK;
 }
 
diff --git a/components/esp_adc/esp32s2/adc_cali_line_fitting.c b/components/esp_adc/esp32s2/adc_cali_line_fitting.c
index 2e826e1f08..a43cdc5173 100644
--- a/components/esp_adc/esp32s2/adc_cali_line_fitting.c
+++ b/components/esp_adc/esp32s2/adc_cali_line_fitting.c
@@ -143,8 +143,13 @@ static esp_err_t cali_raw_to_voltage(void *arg, int raw, int *voltage)
     //pointers are checked in the upper layer
 
     cali_chars_line_fitting_t *ctx = arg;
-    *voltage = raw * ctx->coeff_a / coeff_a_scaling + ctx->coeff_b / coeff_b_scaling;
 
+#if (CONFIG_ENABLE_ADC_USER_CODE_OFFSET & CONFIG_IDF_TARGET_ESP32S2)
+    extern int16_t adc1_get_cal_delta();
+    *voltage = (raw + adc1_get_cal_delta() * 2) * ctx->coeff_a / coeff_a_scaling + ctx->coeff_b / coeff_b_scaling;
+#else
+    *voltage = raw * ctx->coeff_a / coeff_a_scaling + ctx->coeff_b / coeff_b_scaling;
+#endif
     return ESP_OK;
 }
 
diff --git a/components/esp_hw_support/adc_share_hw_ctrl.c b/components/esp_hw_support/adc_share_hw_ctrl.c
index 32f3c67b79..74019195bc 100644
--- a/components/esp_hw_support/adc_share_hw_ctrl.c
+++ b/components/esp_hw_support/adc_share_hw_ctrl.c
@@ -96,6 +96,13 @@ void adc_calc_hw_calibration_code(adc_unit_t adc_n, adc_atten_t atten)
     ESP_EARLY_LOGV(TAG, "Calib(V%d) ADC%d atten=%d: %04" PRIX32, version, adc_n + 1, atten, init_code);
 }
 
+#if (CONFIG_ENABLE_ADC_USER_CODE_OFFSET & CONFIG_IDF_TARGET_ESP32S2)
+uint32_t get_calibration_offset(adc_unit_t adc_n, adc_atten_t atten)
+{
+    return s_adc_cali_param[adc_n][atten];
+}
+#endif
+
 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]);
-- 
2.34.1

