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 60% rename from components/esp_adc_cal/esp_adc_cal_esp32c3.c rename to components/esp_adc_cal/esp32c3/esp_adc_cal.c index f27c31f9ab..8f3debb3f0 100644 --- a/components/esp_adc_cal/esp_adc_cal_esp32c3.c +++ b/components/esp_adc_cal/esp32c3/esp_adc_cal.c @@ -14,6 +14,7 @@ #include "hal/adc_ll.h" #include "esp32c3/esp_efuse_rtc_calib.h" #include "esp_adc_cal.h" +#include "../esp_adc_cal_internal.h" #define ADC_CALIB_CHECK(cond, err_msg, ret) do {\ @@ -28,10 +29,40 @@ const static char LOG_TAG[] = "adc_calib"; /* ------------------------ 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; @@ -45,9 +76,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; @@ -71,7 +102,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); @@ -100,7 +131,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 ADC_CALIB_CHECK(adc_num == ADC_UNIT_1 || adc_num == ADC_UNIT_2, "Invalid unit num", ESP_ADC_CAL_VAL_NOT_SUPPORTED); ADC_CALIB_CHECK(chars != NULL, "Invalid characteristic", ESP_ADC_CAL_VAL_NOT_SUPPORTED); @@ -132,7 +163,17 @@ 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) { - ADC_CALIB_CHECK(chars != NULL, "No characteristic input.", ESP_ERR_INVALID_ARG); + 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 99% rename from components/esp_adc_cal/esp_adc_cal_esp32s2.c rename to components/esp_adc_cal/esp32s2/esp_adc_cal.c index f393c8036f..cf22474cbc 100644 --- a/components/esp_adc_cal/esp_adc_cal_esp32s2.c +++ b/components/esp_adc_cal/esp32s2/esp_adc_cal.c @@ -170,7 +170,6 @@ esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num, esp_adc_cal_characteristics_t *chars) { bool res; - bool res __attribute__((unused)); adc_calib_parsed_info efuse_parsed_data = {0}; // Check parameters assert((adc_num == ADC_UNIT_1) || (adc_num == ADC_UNIT_2)); diff --git a/components/esp_adc_cal/esp_adc_cal_common.c b/components/esp_adc_cal/esp_adc_cal_common.c index b834a8c024..af99efe93f 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 "driver/adc.h" #include "hal/adc_types.h" #include "esp_adc_cal.h" +#include "esp_adc_cal_internal.h" #define ADC_CAL_CHECK(cond, ret) ({ \ if(!(cond)){ \ @@ -44,3 +45,49 @@ esp_err_t esp_adc_cal_get_voltage(adc_channel_t channel, } 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 c83cf0f771..a50c2edeba 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 */