From 9696f0479565203ec3638cff3f2f38e0dfbcba4b Mon Sep 17 00:00:00 2001
From: yanke <yanke2@espressif.com>
Date: Wed, 18 Sep 2024 16:36:27 +0800
Subject: [PATCH] fix(adc): support esp32s3 adc range above 3100mv

---
 components/driver/adc.c                       |  9 ++++
 .../efuse/esp32s3/esp_efuse_rtc_calib.c       | 45 ++++++++++++++++++-
 .../esp32s3/include/esp_efuse_rtc_calib.h     |  9 ++++
 components/esp_adc_cal/esp_adc_cal_common.c   | 44 ++++++++++++++++++
 4 files changed, 106 insertions(+), 1 deletion(-)

diff --git a/components/driver/adc.c b/components/driver/adc.c
index 756ec9f26c..4743dae082 100644
--- a/components/driver/adc.c
+++ b/components/driver/adc.c
@@ -866,11 +866,17 @@ static uint16_t s_adc_cali_param[SOC_ADC_PERIPH_NUM][ADC_ATTEN_MAX] = {};
 //This function shoudn't be called inside critical section or ISR
 uint32_t adc_get_calibration_offset(adc_ll_num_t adc_n, adc_channel_t channel, adc_atten_t atten)
 {
+#ifdef CONFIG_ENABLE_ADC_USER_CODE_OFFSET
+    if (!esp_efuse_rtc_calib_query_init_code_offset_flag(adc_n, atten)) {
+#endif
     if (s_adc_cali_param[adc_n][atten]) {
         ESP_LOGV(ADC_TAG, "Use calibrated val ADC%d atten=%d: %04X", adc_n, atten, s_adc_cali_param[adc_n][atten]);
         return (uint32_t)s_adc_cali_param[adc_n][atten];
     }
 
+#ifdef CONFIG_ENABLE_ADC_USER_CODE_OFFSET
+    }
+#endif
     // check if we can fetch the values from eFuse.
     int version = esp_efuse_rtc_calib_get_ver();
 
@@ -889,6 +895,9 @@ uint32_t adc_get_calibration_offset(adc_ll_num_t adc_n, adc_channel_t channel, a
         sar_periph_ctrl_adc_oneshot_power_release();
     }
 
+#ifdef CONFIG_ENABLE_ADC_USER_CODE_OFFSET
+    esp_efuse_rtc_calib_clear_init_code_offset_flag(adc_n, atten);
+#endif
     s_adc_cali_param[adc_n][atten] = init_code;
     ESP_LOGV(ADC_TAG, "Calib(V%d) ADC%d atten=%d: %04X", version, adc_n, atten, init_code);
 
diff --git a/components/efuse/esp32s3/esp_efuse_rtc_calib.c b/components/efuse/esp32s3/esp_efuse_rtc_calib.c
index 7fdcdc46b9..8966976ff6 100644
--- a/components/efuse/esp32s3/esp_efuse_rtc_calib.c
+++ b/components/efuse/esp32s3/esp_efuse_rtc_calib.c
@@ -9,6 +9,7 @@
 #include "esp_log.h"
 #include "esp_efuse.h"
 #include "esp_efuse_table.h"
+#include "esp_efuse_rtc_calib.h"
 
 //Don't introduce new dependency of ADC, keep these macro same as ADC related definations
 #define ADC_ATTEN_MAX    4
@@ -30,6 +31,44 @@ int esp_efuse_rtc_calib_get_ver(void)
     return cali_version_v1;
 }
 
+#ifdef CONFIG_ENABLE_ADC_USER_CODE_OFFSET
+static uint32_t adc_icode_offset[ADC_NUM_MAX][4] = {0};
+static bool adc_icode_offset_flag[ADC_NUM_MAX][4] = {0};
+
+void esp_efuse_rtc_calib_set_init_code_offset(uint32_t adc_unit, int atten, uint32_t offset)
+{
+    assert(adc_unit < ADC_NUM_MAX);
+    assert(atten < 4);
+    if (adc_icode_offset[adc_unit][atten] != offset) {
+        /* code */
+        adc_icode_offset[adc_unit][atten] = offset;
+        adc_icode_offset_flag[adc_unit][atten] = true;
+        ESP_LOGV("eFuse", "adc %u atten_i %d, offset = %u", adc_unit+1, atten, offset);
+    } else {
+        ESP_LOGV("eFuse", "adc %u atten_i %d, offset = no change", adc_unit+1, atten);
+    }
+}
+
+uint32_t esp_efuse_rtc_calib_get_init_code_offset(uint32_t adc_unit, int atten)
+{
+    assert(adc_unit < ADC_NUM_MAX);
+    assert(atten < 4);
+    return adc_icode_offset[adc_unit][atten];;
+}
+
+bool esp_efuse_rtc_calib_query_init_code_offset_flag(uint32_t adc_unit, int atten)
+{
+    return adc_icode_offset_flag[adc_unit][atten];
+}
+
+bool esp_efuse_rtc_calib_clear_init_code_offset_flag(uint32_t adc_unit, int atten)
+{
+    adc_icode_offset_flag[adc_unit][atten] = false;
+    ESP_LOGV("eFuse", "adc %u atten_i %d, clear offset flag", adc_unit+1, atten);
+    return adc_icode_offset_flag[adc_unit][atten];
+}
+#endif
+
 uint32_t esp_efuse_rtc_calib_get_init_code(int version, uint32_t adc_unit, int atten)
 {
     assert(version == 1);
@@ -62,7 +101,11 @@ uint32_t esp_efuse_rtc_calib_get_init_code(int version, uint32_t adc_unit, int a
         adc_icode[3] = adc_icode_diff[3] + adc_icode[2];
     }
 
-    return adc_icode[atten];
+#ifdef CONFIG_ENABLE_ADC_USER_CODE_OFFSET
+    return adc_icode[atten] + adc_icode_offset[adc_unit][atten];
+#else
+   return adc_icode[atten];
+#endif
 }
 
 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)
diff --git a/components/efuse/esp32s3/include/esp_efuse_rtc_calib.h b/components/efuse/esp32s3/include/esp_efuse_rtc_calib.h
index e2f5b54329..9d215ac057 100644
--- a/components/efuse/esp32s3/include/esp_efuse_rtc_calib.h
+++ b/components/efuse/esp32s3/include/esp_efuse_rtc_calib.h
@@ -54,6 +54,15 @@ esp_err_t esp_efuse_rtc_calib_get_cal_voltage(int version, uint32_t adc_unit, in
  */
 float esp_efuse_rtc_calib_get_cal_temp(int version);
 
+#define CONFIG_ENABLE_ADC_USER_CODE_OFFSET 1
+
+#ifdef CONFIG_ENABLE_ADC_USER_CODE_OFFSET
+void esp_efuse_rtc_calib_set_init_code_offset(uint32_t adc_unit, int atten, uint32_t offset);
+uint32_t esp_efuse_rtc_calib_get_init_code_offset(uint32_t adc_unit, int atten);
+bool esp_efuse_rtc_calib_query_init_code_offset_flag(uint32_t adc_unit, int atten);
+bool esp_efuse_rtc_calib_clear_init_code_offset_flag(uint32_t adc_unit, int atten);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/components/esp_adc_cal/esp_adc_cal_common.c b/components/esp_adc_cal/esp_adc_cal_common.c
index 09878cc015..7910359718 100644
--- a/components/esp_adc_cal/esp_adc_cal_common.c
+++ b/components/esp_adc_cal/esp_adc_cal_common.c
@@ -12,6 +12,7 @@
 #include "esp_check.h"
 #include "driver/adc.h"
 #include "hal/adc_types.h"
+#include "esp_efuse_rtc_calib.h"
 #include "esp_adc_cal.h"
 #include "esp_adc_cal_internal.h"
 
@@ -33,11 +34,54 @@ esp_err_t esp_adc_cal_get_voltage(adc_channel_t channel,
     } else {
         ESP_RETURN_ON_FALSE(channel < SOC_ADC_CHANNEL_NUM(1), ESP_ERR_INVALID_ARG, TAG, "Invalid channel");
         ret = adc2_get_raw(channel, chars->bit_width, &adc_reading);
+        if (ret != ESP_OK) {
+            ESP_LOGD(TAG, "adc2_get_raw error, please retry");
+            return ret;
+        }
     }
 
     if (ret == ESP_OK) {
         *voltage = esp_adc_cal_raw_to_voltage((uint32_t)adc_reading, chars);
     }
+
+#if (CONFIG_IDF_TARGET_ESP32S3 & CONFIG_ENABLE_ADC_USER_CODE_OFFSET) //1000mv ~ 3500mV
+#if 0 //using double type
+        typedef const double ADC_CAL_TYPE;
+#else //using float type
+        typedef const float ADC_CAL_TYPE;
+#endif
+    if(chars->atten == ADC_ATTEN_DB_12) {
+        if (*voltage > 2900) {
+            /* add initcode offset */
+            ESP_LOGV(TAG, "first is %u", *voltage);
+            esp_efuse_rtc_calib_set_init_code_offset(chars->adc_num - 1, chars->atten, 1200);
+            if (chars->adc_num == ADC_UNIT_1) {
+                adc_reading = adc1_get_raw(channel);
+            } else {
+                ret = adc2_get_raw(channel, chars->bit_width, &adc_reading);
+                if (ret != ESP_OK) {
+                    ESP_LOGD(TAG, "adc2_get_raw error, please retry");
+                    return ret;
+                }
+            }
+            esp_efuse_rtc_calib_set_init_code_offset(chars->adc_num - 1, chars->atten, 0);
+            uint32_t voltage_b = esp_adc_cal_raw_to_voltage((uint32_t)adc_reading, chars);
+            voltage_b += 1000;
+            if (voltage_b > 2700) {
+                /* code */
+                ESP_LOGD(TAG, "before is %u", voltage_b);
+                ADC_CAL_TYPE a = -0.0000016625088686597596;
+                ADC_CAL_TYPE b = 0.0012152697844402401;
+                ADC_CAL_TYPE c = 7.660092154791914;
+                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);
+            }
+            *voltage = voltage_b;
+        }
+    }
+#endif
+
     return ret;
 }
 
-- 
2.34.1

