From 175132a87445a4feb8b43b3d046d161f3a30487f Mon Sep 17 00:00:00 2001 From: Armando Date: Thu, 20 Jan 2022 20:34:37 +0800 Subject: [PATCH] adc: upgrade adc calibration algorithm to version 2 on c3 --- components/esp_adc_cal/CMakeLists.txt | 2 +- .../esp_adc_cal.c} | 0 .../esp_adc_cal.c} | 60 ++++++++-- .../esp_adc_cal.c} | 0 .../esp_adc_cal.c} | 104 +++++++----------- components/esp_adc_cal/esp_adc_cal_common.c | 47 ++++++++ components/esp_adc_cal/esp_adc_cal_internal.h | 50 +++++++++ components/esp_adc_cal/include/esp_adc_cal.h | 2 +- 8 files changed, 187 insertions(+), 78 deletions(-) rename components/esp_adc_cal/{esp_adc_cal_esp32.c => esp32/esp_adc_cal.c} (100%) rename components/esp_adc_cal/{esp_adc_cal_esp32c3.c => esp32c3/esp_adc_cal.c} (59%) rename components/esp_adc_cal/{esp_adc_cal_esp32s2.c => esp32s2/esp_adc_cal.c} (100%) rename components/esp_adc_cal/{esp_adc_cal_esp32s3.c => esp32s3/esp_adc_cal.c} (59%) create mode 100644 components/esp_adc_cal/esp_adc_cal_internal.h diff --git a/components/esp_adc_cal/CMakeLists.txt b/components/esp_adc_cal/CMakeLists.txt index e2fbb5c456..2d1f824147 100644 --- a/components/esp_adc_cal/CMakeLists.txt +++ b/components/esp_adc_cal/CMakeLists.txt @@ -1,7 +1,7 @@ idf_build_get_property(target IDF_TARGET) set(srcs "esp_adc_cal_common.c") -set(src_target "esp_adc_cal_${target}.c") +set(src_target "${target}/esp_adc_cal.c") if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${src_target}") list(APPEND srcs ${src_target}) endif() diff --git a/components/esp_adc_cal/esp_adc_cal_esp32.c b/components/esp_adc_cal/esp32/esp_adc_cal.c similarity index 100% rename from components/esp_adc_cal/esp_adc_cal_esp32.c rename to components/esp_adc_cal/esp32/esp_adc_cal.c diff --git a/components/esp_adc_cal/esp_adc_cal_esp32c3.c b/components/esp_adc_cal/esp32c3/esp_adc_cal.c similarity index 59% rename from components/esp_adc_cal/esp_adc_cal_esp32c3.c rename to components/esp_adc_cal/esp32c3/esp_adc_cal.c index fab3e78b65..bf73affbec 100644 --- a/components/esp_adc_cal/esp_adc_cal_esp32c3.c +++ b/components/esp_adc_cal/esp32c3/esp_adc_cal.c @@ -15,16 +15,47 @@ #include "hal/adc_ll.h" #include "esp_efuse_rtc_calib.h" #include "esp_adc_cal.h" +#include "../esp_adc_cal_internal.h" -const static char LOG_TAG[] = "adc_calib"; +const static char LOG_TAG[] = "ADC_CALI"; /* ------------------------ Characterization Constants ---------------------- */ -// coeff_a and coeff_b are actually floats -// they are scaled to put them into uint32_t so that the headers do not have to be changed +// coeff_a is actually a float number +// it is scaled to put them into uint32_t so that the headers do not have to be changed static const int coeff_a_scaling = 65536; -static const int coeff_b_scaling = 1024; + +/** + * @note Error Calculation + * Coefficients for calculating the reading voltage error. + * Four sets of coefficients for atten0 ~ atten3 respectively. + * + * 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 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 static uint64_t adc_error_coef_atten[4][5][2] = { + {{225966470500043, 1e15}, {7265418501948, 1e16}, {109410402681, 1e16}, {0, 0}, {0, 0}}, //atten0 + {{4229623392600516, 1e16}, {731527490903, 1e16}, {88166562521, 1e16}, {0, 0}, {0, 0}}, //atten1 + {{1017859239236435, 1e15}, {97159265299153, 1e16}, {149794028038, 1e16}, {0, 0}, {0, 0}}, //atten2 + {{14912262772850453, 1e16}, {228549975564099, 1e16}, {356391935717, 1e16}, {179964582, 1e16}, {42046, 1e16}} //atten3 + }; +/** + * Term sign + * @note ADC1 and ADC2 use same coeffients + */ +const static int32_t adc_error_sign[4][5] = { + {-1, -1, 1, 0, 0}, //atten0 + { 1, -1, 1, 0, 0}, //atten1 + {-1, -1, 1, 0, 0}, //atten2 + {-1, -1, 1, -1, 1} //atten3 + }; + /* -------------------- Characterization Helper Data Types ------------------ */ typedef struct { uint32_t voltage; @@ -38,9 +69,9 @@ typedef struct { union { adc_calib_data_ver1 ver1; } efuse_data; -} adc_calib_parsed_info; +} adc_calib_parsed_info_t; -static esp_err_t prepare_calib_data_for(int version_num, adc_unit_t adc_num, adc_atten_t atten, adc_calib_parsed_info *parsed_data_storage) +static esp_err_t prepare_calib_data_for(int version_num, adc_unit_t adc_num, adc_atten_t atten, adc_calib_parsed_info_t *parsed_data_storage) { assert(version_num == 1); esp_err_t ret; @@ -64,7 +95,7 @@ static esp_err_t prepare_calib_data_for(int version_num, adc_unit_t adc_num, adc * Estimate the (assumed) linear relationship btwn the measured raw value and the voltage * with the previously done measurement when the chip was manufactured. */ -static void calculate_characterization_coefficients(const adc_calib_parsed_info *parsed_data, esp_adc_cal_characteristics_t *chars) +static void calculate_characterization_coefficients(const adc_calib_parsed_info_t *parsed_data, esp_adc_cal_characteristics_t *chars) { ESP_LOGD(LOG_TAG, "Calib V1, Cal Voltage = %d, Digi out = %d\n", parsed_data->efuse_data.ver1.voltage, parsed_data->efuse_data.ver1.digi); @@ -93,7 +124,7 @@ esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num, esp_adc_cal_characteristics_t *chars) { esp_err_t ret; - adc_calib_parsed_info efuse_parsed_data = {0}; + adc_calib_parsed_info_t efuse_parsed_data = {0}; // Check parameters ESP_RETURN_ON_FALSE(adc_num == ADC_UNIT_1 || adc_num == ADC_UNIT_2, ESP_ADC_CAL_VAL_NOT_SUPPORTED, LOG_TAG, "Invalid unit num"); ESP_RETURN_ON_FALSE(chars != NULL, ESP_ADC_CAL_VAL_NOT_SUPPORTED, LOG_TAG, "Ivalid characteristic"); @@ -126,5 +157,16 @@ esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num, uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_characteristics_t *chars) { assert(chars != NULL); - return adc_reading * chars->coeff_a / coeff_a_scaling + chars->coeff_b / coeff_b_scaling; + + int32_t error = 0; + uint64_t v_cali_1 = adc_reading * chars->coeff_a / coeff_a_scaling; + esp_adc_error_calc_param_t param = { + .v_cali_input = v_cali_1, + .term_num = (chars->atten == 3) ? 5 : 3, + .coeff = &adc_error_coef_atten, + .sign = &adc_error_sign, + }; + error = esp_adc_cal_get_reading_error(¶m, chars->atten); + + return (int32_t)v_cali_1 - error; } diff --git a/components/esp_adc_cal/esp_adc_cal_esp32s2.c b/components/esp_adc_cal/esp32s2/esp_adc_cal.c similarity index 100% rename from components/esp_adc_cal/esp_adc_cal_esp32s2.c rename to components/esp_adc_cal/esp32s2/esp_adc_cal.c diff --git a/components/esp_adc_cal/esp_adc_cal_esp32s3.c b/components/esp_adc_cal/esp32s3/esp_adc_cal.c similarity index 59% rename from components/esp_adc_cal/esp_adc_cal_esp32s3.c rename to components/esp_adc_cal/esp32s3/esp_adc_cal.c index 7b9726d8bb..46a5b8ad2d 100644 --- a/components/esp_adc_cal/esp_adc_cal_esp32s3.c +++ b/components/esp_adc_cal/esp32s3/esp_adc_cal.c @@ -15,6 +15,7 @@ #include "hal/adc_types.h" #include "esp_efuse_rtc_calib.h" #include "esp_adc_cal.h" +#include "../esp_adc_cal_internal.h" const static char LOG_TAG[] = "ADC_CALI"; @@ -33,33 +34,35 @@ static const int coeff_a_scaling = 1000000; * * @note {0,0} stands for unused item * @note In case of the overflow, these coeffcients are recorded as Absolute Value - * @note For atten0 ~ 2, error = a1 * X^2 + a2 * X + a3; For atten3, error = a1 * X^4 + a2 * X^3 + a3 * X^2 + a4 * X + a5; + * @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. */ -const static uint64_t adc_error_coef_atten[4][10][2] = { - {{9798249589, 1e15}, {50871540569528, 1e16}, {3, 1}, {0, 0}, {0, 0}, //ADC1 atten0 - {36615265189, 1e16}, {1353548869615, 1e16}, {3, 1}, {0, 0}, {0, 0}}, //ADC2 atten0 - - {{101379430548, 1e16}, {49393185868806, 1e16}, {3, 1}, {0, 0}, {0, 0}, //ADC1 atten1 - {118964995959, 1e16}, {66319894226185, 1e16}, {2, 1}, {0, 0}, {0, 0}}, //ADC2 atten1 - - {{208385525314, 1e16}, {147640181047414, 1e16}, {2, 1}, {0, 0}, {0, 0}, //ADC1 atten2 - {259011467956, 1e16}, {200996773954387, 1e16}, {1, 1}, {0, 0}, {0, 0}}, //ADC2 atten2 - - {{13515, 1e15}, {70769718, 1e15}, {1297891447611, 1e16}, {644334888647536, 1e16}, {1,1}, //ADC1 atten3 - {15038, 1e15}, {79672528, 1e15}, {1478791187119, 1e16}, {755717904943462, 1e16}, {1,1}} //ADC2 atten3 +const static uint64_t adc1_error_coef_atten[4][5][2] = { + {{27856531419538344, 1e16}, {50871540569528, 1e16}, {9798249589, 1e15}, {0, 0}, {0, 0}}, //ADC1 atten0 + {{29831022915028695, 1e16}, {49393185868806, 1e16}, {101379430548, 1e16}, {0, 0}, {0, 0}}, //ADC1 atten1 + {{23285545746296417, 1e16}, {147640181047414, 1e16}, {208385525314, 1e16}, {0, 0}, {0, 0}}, //ADC1 atten2 + {{644403418269478, 1e15}, {644334888647536, 1e16}, {1297891447611, 1e16}, {70769718, 1e15}, {13515, 1e15}} //ADC1 atten3 }; -const static int32_t adc_error_sign[4][10] = { - {1, -1, -1, 0, 0, //ADC1 atten0 - 1, 1, -1, 0, 0}, //ADC2 atten0 - - {1, -1, -1, 0, 0, //ADC1 atten1 - 1, -1, -1, 0, 0}, //ADC2 atten1 - - {1, -1, -1, 0, 0, //ADC1 atten2 - 1, -1, -1, 0, 0}, //ADC2 atten2 - - {1, -1, 1, -1, -1, //ADC1 atten3 - 1, -1, 1, -1, 1} //ADC2 atten3 +const static uint64_t adc2_error_coef_atten[4][5][2] = { + {{25668651654328927, 1e16}, {1353548869615, 1e16}, {36615265189, 1e16}, {0, 0}, {0, 0}}, //ADC2 atten0 + {{23690184690298404, 1e16}, {66319894226185, 1e16}, {118964995959, 1e16}, {0, 0}, {0, 0}}, //ADC2 atten1 + {{9452499397020617, 1e16}, {200996773954387, 1e16}, {259011467956, 1e16}, {0, 0}, {0, 0}}, //ADC2 atten2 + {{12247719764336924,1e16}, {755717904943462, 1e16}, {1478791187119, 1e16}, {79672528, 1e15}, {15038, 1e15}} //ADC2 atten3 + }; +/** + * Term sign + */ +const static int32_t adc1_error_sign[4][5] = { + {-1, -1, 1, 0, 0}, //ADC1 atten0 + {-1, -1, 1, 0, 0}, //ADC1 atten1 + {-1, -1, 1, 0, 0}, //ADC1 atten2 + {-1, -1, 1, -1, 1} //ADC1 atten3 + }; +const static int32_t adc2_error_sign[4][5] = { + {-1, 1, 1, 0, 0}, //ADC2 atten0 + {-1, -1, 1, 0, 0}, //ADC2 atten1 + {-1, -1, 1, 0, 0}, //ADC2 atten2 + { 1, -1, 1, -1, 1} //ADC2 atten3 }; /* -------------------- Characterization Helper Data Types ------------------ */ @@ -151,47 +154,6 @@ esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num, return ESP_ADC_CAL_VAL_EFUSE_TP_FIT; } -static int32_t get_reading_error(uint64_t v_cali_1, uint8_t adc_num, uint8_t atten) -{ - if (v_cali_1 == 0) { - return 0; - } - - uint8_t term_max = (atten == 3) ? 5 : 3; - int32_t error = 0; - uint64_t coeff = 0; - uint64_t term[5] = {0}; - - /** - * For atten0 ~ 2: - * error = a1 * X^2 + a2 * X + a3; - * - * For atten3: - * error = a1 * X^4 + a2 * X^3 + a3 * X^2 + a4 * X + a5; - */ - - //Calculate all the power beforehand - term[term_max-1] = 1; - term[term_max-2] = v_cali_1; - for (int term_id = term_max - 3; term_id >= 0; term_id--) { - term[term_id] = term[term_id + 1] * v_cali_1; - } - - //Calculate each term - uint8_t coef_id_start = (adc_num == ADC_UNIT_1) ? 0 : 5; - for (int i = 0; i < term_max; i++) { - coeff = adc_error_coef_atten[atten][coef_id_start + i][0]; - term[i] = term[i] * coeff; - ESP_LOGV(LOG_TAG, "big coef is %llu, big term%d is %llu, coef_id is %d", coeff, i, term[i], coef_id_start + i); - - term[i] = term[i] / adc_error_coef_atten[atten][coef_id_start + i][1]; - error += (int32_t)term[i] * adc_error_sign[atten][i]; - ESP_LOGV(LOG_TAG, "term%d is %llu, error is %d", i, term[i], error); - } - - return error; -} - uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_characteristics_t *chars) { assert(chars != NULL); @@ -209,7 +171,15 @@ uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_char v_cali_1 = v_cali_1 / coeff_a_scaling; ESP_LOGV(LOG_TAG, "v_cali_1 is %llu", v_cali_1); - error = get_reading_error(v_cali_1, chars->adc_num, chars->atten); + //Curve Fitting error correction + esp_adc_error_calc_param_t param = { + .v_cali_input = v_cali_1, + .term_num = (chars->atten == 3) ? 5 : 3, + .coeff = (chars->adc_num == ADC_UNIT_1) ? &adc1_error_coef_atten : &adc2_error_coef_atten, + .sign = (chars->adc_num == ADC_UNIT_1) ? &adc1_error_sign : &adc2_error_sign, + }; + error = esp_adc_cal_get_reading_error(¶m, chars->atten); + voltage = (int32_t)v_cali_1 - error; return voltage; diff --git a/components/esp_adc_cal/esp_adc_cal_common.c b/components/esp_adc_cal/esp_adc_cal_common.c index 809c001625..6fb7040afe 100644 --- a/components/esp_adc_cal/esp_adc_cal_common.c +++ b/components/esp_adc_cal/esp_adc_cal_common.c @@ -13,6 +13,7 @@ #include "driver/adc.h" #include "hal/adc_types.h" #include "esp_adc_cal.h" +#include "esp_adc_cal_internal.h" const static char *TAG = "ADC_CALI"; @@ -36,3 +37,49 @@ esp_err_t esp_adc_cal_get_voltage(adc_channel_t channel, *voltage = esp_adc_cal_raw_to_voltage((uint32_t)adc_reading, chars); return ret; } + +#if ESP_ADC_CAL_CURVE_FITTING_SUPPORTED +/*------------------------------------------------------------------------------ + * Private API + *----------------------------------------------------------------------------*/ +int32_t esp_adc_cal_get_reading_error(const esp_adc_error_calc_param_t *param, uint8_t atten) +{ + if (param->v_cali_input == 0) { + return 0; + } + + uint64_t v_cali_1 = param->v_cali_input; + uint8_t term_num = param->term_num; + int32_t error = 0; + uint64_t coeff = 0; + uint64_t variable[term_num]; + uint64_t term[term_num]; + memset(variable, 0, term_num * sizeof(uint64_t)); + memset(term, 0, term_num * sizeof(uint64_t)); + + /** + * 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); + */ + variable[0] = 1; + coeff = (*param->coeff)[atten][0][0]; + term[0] = variable[0] * coeff / (*param->coeff)[atten][0][1]; + error = (int32_t)term[0] * (*param->sign)[atten][0]; + + for (int i = 1; i < term_num; i++) { + variable[i] = variable[i-1] * v_cali_1; + coeff = (*param->coeff)[atten][i][0]; + term[i] = variable[i] * coeff; + ESP_LOGV(TAG, "big coef is %llu, big term%d is %llu, coef_id is %d", coeff, i, term[i], i); + + term[i] = term[i] / (*param->coeff)[atten][i][1]; + error += (int32_t)term[i] * (*param->sign)[atten][i]; + ESP_LOGV(TAG, "term%d is %llu, error is %d", i, term[i], error); + } + + return error; +} +#endif //#if ESP_ADC_CAL_CURVE_FITTING_SUPPORTED diff --git a/components/esp_adc_cal/esp_adc_cal_internal.h b/components/esp_adc_cal/esp_adc_cal_internal.h new file mode 100644 index 0000000000..518c397f5b --- /dev/null +++ b/components/esp_adc_cal/esp_adc_cal_internal.h @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 +#define ESP_ADC_CAL_CURVE_FITTING_SUPPORTED 1 + +#define COEFF_GROUP_NUM 4 +#define TERM_MAX 5 +#endif + +#if ESP_ADC_CAL_CURVE_FITTING_SUPPORTED +/** + * Calculation parameters used for curve fitting calibration algorithm error + * + * @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); + * Where X is the `v_cali_input`. + */ +typedef struct { + uint64_t v_cali_input; //Input to calculate the error + uint8_t term_num; //Term number of the algorithm formula + const uint64_t (*coeff)[COEFF_GROUP_NUM][TERM_MAX][2]; //Coeff of each term. See `adc_error_coef_atten` for details (and the magic number 2) + const int32_t (*sign)[COEFF_GROUP_NUM][TERM_MAX]; //Sign of each term +} esp_adc_error_calc_param_t; + +/** + * Calculate the curve fitting error + * + * @param param see `esp_adc_error_calc_param_t` + * @param atten ADC attenuation + */ +int32_t esp_adc_cal_get_reading_error(const esp_adc_error_calc_param_t *param, uint8_t atten); + +#endif //#if ESP_ADC_CAL_CURVE_FITTING_SUPPORTED + + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_adc_cal/include/esp_adc_cal.h b/components/esp_adc_cal/include/esp_adc_cal.h index 6be5ef2453..445b7f65d3 100644 --- a/components/esp_adc_cal/include/esp_adc_cal.h +++ b/components/esp_adc_cal/include/esp_adc_cal.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */