diff --git a/components/esp_adc_cal/CMakeLists.txt b/components/esp_adc_cal/CMakeLists.txt index 61d15d185f..2d1f824147 100644 --- a/components/esp_adc_cal/CMakeLists.txt +++ b/components/esp_adc_cal/CMakeLists.txt @@ -1,17 +1,12 @@ idf_build_get_property(target IDF_TARGET) -if(${target} STREQUAL "esp32") - idf_component_register(SRCS "esp_adc_cal_esp32.c" - INCLUDE_DIRS "include" - REQUIRES driver efuse) - -elseif(${target} STREQUAL "esp32s2") - idf_component_register(SRCS "esp_adc_cal_esp32s2.c" - INCLUDE_DIRS "include" - REQUIRES driver efuse) - -elseif(${target} STREQUAL "esp32c3") - idf_component_register(SRCS "esp_adc_cal_esp32c3.c" - INCLUDE_DIRS "include" - REQUIRES driver efuse) +set(srcs "esp_adc_cal_common.c") +set(src_target "${target}/esp_adc_cal.c") +if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${src_target}") + list(APPEND srcs ${src_target}) endif() + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS include + REQUIRES driver + PRIV_REQUIRES efuse) diff --git a/components/esp_adc_cal/component.mk b/components/esp_adc_cal/component.mk index b12080ce87..99bda1cb26 100644 --- a/components/esp_adc_cal/component.mk +++ b/components/esp_adc_cal/component.mk @@ -2,5 +2,5 @@ # Component Makefile # +COMPONENT_SRCDIRS := . $(IDF_TARGET) COMPONENT_ADD_INCLUDEDIRS := include -COMPONENT_OBJEXCLUDE += esp_adc_cal_esp32s2.o esp_adc_cal_esp32c3.o diff --git a/components/esp_adc_cal/esp_adc_cal_esp32.c b/components/esp_adc_cal/esp32/esp_adc_cal.c similarity index 88% rename from components/esp_adc_cal/esp_adc_cal_esp32.c rename to components/esp_adc_cal/esp32/esp_adc_cal.c index 43b08c1461..8f0dde7980 100644 --- a/components/esp_adc_cal/esp_adc_cal_esp32.c +++ b/components/esp_adc_cal/esp32/esp_adc_cal.c @@ -1,16 +1,8 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include #include "esp_types.h" @@ -82,12 +74,6 @@ #define LUT_HIGH_THRESH (LUT_LOW_THRESH + LUT_ADC_STEP_SIZE) #define ADC_12_BIT_RES 4096 -#define ADC_CAL_CHECK(cond, ret) ({ \ - if(!(cond)){ \ - return ret; \ - } \ -}) - /* ------------------------ Characterization Constants ---------------------- */ static const uint32_t adc1_tp_atten_scale[4] = {65504, 86975, 120389, 224310}; static const uint32_t adc2_tp_atten_scale[4] = {65467, 86861, 120416, 224708}; @@ -366,27 +352,3 @@ uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_char return calculate_voltage_linear(adc_reading, chars->coeff_a, chars->coeff_b); } } - -esp_err_t esp_adc_cal_get_voltage(adc_channel_t channel, - const esp_adc_cal_characteristics_t *chars, - uint32_t *voltage) -{ - //Check parameters - ADC_CAL_CHECK(chars != NULL, ESP_ERR_INVALID_ARG); - ADC_CAL_CHECK(voltage != NULL, ESP_ERR_INVALID_ARG); - - int adc_reading; - if (chars->adc_num == ADC_UNIT_1) { - //Check channel is valid on ADC1 - ADC_CAL_CHECK((adc1_channel_t)channel < ADC1_CHANNEL_MAX, ESP_ERR_INVALID_ARG); - adc_reading = adc1_get_raw(channel); - } else { - //Check channel is valid on ADC2 - ADC_CAL_CHECK((adc2_channel_t)channel < ADC2_CHANNEL_MAX, ESP_ERR_INVALID_ARG); - if (adc2_get_raw(channel, chars->bit_width, &adc_reading) != ESP_OK) { - return ESP_ERR_TIMEOUT; //Timed out waiting for ADC2 - } - } - *voltage = esp_adc_cal_raw_to_voltage((uint32_t)adc_reading, chars); - return ESP_OK; -} diff --git a/components/esp_adc_cal/esp_adc_cal_esp32c3.c b/components/esp_adc_cal/esp32c3/esp_adc_cal.c similarity index 58% rename from components/esp_adc_cal/esp_adc_cal_esp32c3.c rename to components/esp_adc_cal/esp32c3/esp_adc_cal.c index f7e4495e03..8f3debb3f0 100644 --- a/components/esp_adc_cal/esp_adc_cal_esp32c3.c +++ b/components/esp_adc_cal/esp32c3/esp_adc_cal.c @@ -1,16 +1,8 @@ -// Copyright 2019-2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include #include @@ -22,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 {\ @@ -36,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; @@ -53,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; @@ -79,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); @@ -108,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); @@ -140,31 +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; -} - -esp_err_t esp_adc_cal_get_voltage(adc_channel_t channel, - const esp_adc_cal_characteristics_t *chars, - uint32_t *voltage) -{ - // Check parameters - ADC_CALIB_CHECK(chars != NULL, "No characteristic input.", ESP_ERR_INVALID_ARG); - ADC_CALIB_CHECK(voltage != NULL, "No output buffer.", ESP_ERR_INVALID_ARG); - - int adc_reading; - if (chars->adc_num == ADC_UNIT_1) { - //Check if channel is valid on ADC1 - ADC_CALIB_CHECK((adc1_channel_t)channel < ADC1_CHANNEL_MAX, "Invalid channel", ESP_ERR_INVALID_ARG); - adc_reading = adc1_get_raw(channel); - } else { - //Check if channel is valid on ADC2 - ADC_CALIB_CHECK((adc2_channel_t)channel < ADC2_CHANNEL_MAX, "Invalid channel", ESP_ERR_INVALID_ARG); - if (adc2_get_raw(channel, chars->bit_width, &adc_reading) != ESP_OK) { - return ESP_ERR_TIMEOUT; //Timed out waiting for ADC2 - } - } - *voltage = esp_adc_cal_raw_to_voltage((uint32_t)adc_reading, chars); - return ESP_OK; + 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 83% rename from components/esp_adc_cal/esp_adc_cal_esp32s2.c rename to components/esp_adc_cal/esp32s2/esp_adc_cal.c index 4230eea66c..cf22474cbc 100644 --- a/components/esp_adc_cal/esp_adc_cal_esp32s2.c +++ b/components/esp_adc_cal/esp32s2/esp_adc_cal.c @@ -1,16 +1,8 @@ -// Copyright 2019-2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include #include "esp_types.h" @@ -211,27 +203,3 @@ uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_char return adc_reading * chars->coeff_a / coeff_a_scaling + chars->coeff_b / coeff_b_scaling; } - -esp_err_t esp_adc_cal_get_voltage(adc_channel_t channel, - const esp_adc_cal_characteristics_t *chars, - uint32_t *voltage) -{ - // Check parameters - ADC_CAL_CHECK(chars != NULL, ESP_ERR_INVALID_ARG); - ADC_CAL_CHECK(voltage != NULL, ESP_ERR_INVALID_ARG); - - int adc_reading; - if (chars->adc_num == ADC_UNIT_1) { - //Check if channel is valid on ADC1 - ADC_CAL_CHECK((adc1_channel_t)channel < ADC1_CHANNEL_MAX, ESP_ERR_INVALID_ARG); - adc_reading = adc1_get_raw(channel); - } else { - //Check if channel is valid on ADC2 - ADC_CAL_CHECK((adc2_channel_t)channel < ADC2_CHANNEL_MAX, ESP_ERR_INVALID_ARG); - if (adc2_get_raw(channel, chars->bit_width, &adc_reading) != ESP_OK) { - return ESP_ERR_TIMEOUT; //Timed out waiting for ADC2 - } - } - *voltage = esp_adc_cal_raw_to_voltage((uint32_t)adc_reading, chars); - return ESP_OK; -} diff --git a/components/esp_adc_cal/esp_adc_cal_common.c b/components/esp_adc_cal/esp_adc_cal_common.c new file mode 100644 index 0000000000..af99efe93f --- /dev/null +++ b/components/esp_adc_cal/esp_adc_cal_common.c @@ -0,0 +1,93 @@ +/* + * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_types.h" +#include "esp_err.h" +#include "esp_log.h" +#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)){ \ + return ret; \ + } \ +}) + +const __attribute__((unused)) static char *TAG = "ADC_CALI"; + +esp_err_t esp_adc_cal_get_voltage(adc_channel_t channel, + const esp_adc_cal_characteristics_t *chars, + uint32_t *voltage) +{ + // Check parameters + ADC_CAL_CHECK(chars != NULL, ESP_ERR_INVALID_ARG); + ADC_CAL_CHECK(voltage != NULL, ESP_ERR_INVALID_ARG); + + esp_err_t ret = ESP_OK; + int adc_reading; + if (chars->adc_num == ADC_UNIT_1) { + ADC_CAL_CHECK(channel < SOC_ADC_CHANNEL_NUM(0), ESP_ERR_INVALID_ARG); + adc_reading = adc1_get_raw(channel); + } else { + ADC_CAL_CHECK(channel < SOC_ADC_CHANNEL_NUM(1), ESP_ERR_INVALID_ARG); + ret = adc2_get_raw(channel, chars->bit_width, &adc_reading); + } + + if (ret == ESP_OK) { + *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 9adf28078d..a50c2edeba 100644 --- a/components/esp_adc_cal/include/esp_adc_cal.h +++ b/components/esp_adc_cal/include/esp_adc_cal.h @@ -1,16 +1,8 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #ifndef __ESP_ADC_CAL_H__ #define __ESP_ADC_CAL_H__