From 0c9e2c0fba7db079e14474df56c5bb81c1925f06 Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Tue, 13 Feb 2018 20:47:18 +0800 Subject: [PATCH] esp_adc_cal/Remove lookup table This commit removes the lookup table mode due to inferior performance when compared to linear mode under attenuation 0, 1 and 2. However small portions of the lookup table are kept for the higher voltages of atten 3 (above ADC reading 2880). That voltage range in atten 3 has non linear characteristics making the LUT performan better than linear mode. --- components/esp_adc_cal/Kconfig | 46 +- components/esp_adc_cal/esp_adc_cal.c | 428 ++++++++---------- .../esp_adc_cal/esp_adc_cal_constants.c | 222 --------- components/esp_adc_cal/include/esp_adc_cal.h | 121 ++--- .../include/esp_adc_cal_constants.h | 0 components/soc/esp32/include/soc/efuse_reg.h | 4 +- docs/api-reference/peripherals/adc.rst | 76 +++- .../peripherals/adc/main/adc1_example_main.c | 126 +++--- .../peripherals/i2s_adc_dac/main/app_main.c | 5 +- 9 files changed, 381 insertions(+), 647 deletions(-) delete mode 100644 components/esp_adc_cal/esp_adc_cal_constants.c delete mode 100644 components/esp_adc_cal/include/esp_adc_cal_constants.h diff --git a/components/esp_adc_cal/Kconfig b/components/esp_adc_cal/Kconfig index 0db3d4e1f..fd7c93e56 100644 --- a/components/esp_adc_cal/Kconfig +++ b/components/esp_adc_cal/Kconfig @@ -1,37 +1,27 @@ menu "ADC-Calibration" -config ADC_CAL_EFUSE_TP_DISABLE - bool "Disable Two Point values" - default "n" +config ADC_CAL_EFUSE_TP_ENABLE + bool "Use Two Point Values" + default "y" help - Some ESP32s have Two Point calibration values burned into eFuse. Enabling - this option will prevent the ADC calibration component from using Two Point - values if they are available. + Some ESP32s have Two Point calibration values burned into eFuse BLOCK3. + This option will allow the ADC calibration component to characterize the + ADC-Voltage curve using Two Point values if they are available. -config ADC_CAL_EFUSE_VREF_DISABLE - bool "Disable eFuse Vref" - default "n" +config ADC_CAL_EFUSE_VREF_ENABLE + bool "Use eFuse Vref" + default "y" help - Some ESP32s have ADC Vref burned into eFuse. Enabling this option will - prevent the ADC calibration component from using eFuse Vref if they are - available. + Some ESP32s have Vref burned into eFuse BLOCK0. This option will allow + the ADC calibration component to characterize the ADC-Voltage curve using + eFuse Vref if it is available. -config ADC_CAL_DEFAULT_VREF_DISABLE - bool "Disable Default Vref" - default "n" +config ADC_CAL_LUT_ENABLE + bool "Use Lookup Tables" + default "y" help - The esp_adc_cal_characterize() function requires the user to provide a - vref_default argument to be used if eFuse values are unavailable. Enabling - this option will prevent the ADC calibration component from using the - vref_default argument. Note that if eFuse values are also unavailabe, - the esp_adc_cal_characterize will trigger an abort. - -config ADC_CAL_NO_BLK3_RESERVE_FLAG - bool "EFUSE_BLK3_PART_RESERVE not set" - default "n" - help - By default, ESP32s will have the EFUSE_BLK3_PART_RESERVE flag set if it - uses BLK3 of eFuse to store Two Point values. Some ESP32s do not set this - flag. Enable this option if that is the case + This option will allow the ADC calibration component to use Lookup Tables + to correct for non-linear behavior in 11db attenuation. Other attenuations + do not exhibit non-linear behavior hence will not be affected by this option. endmenu # ADC-Calibration diff --git a/components/esp_adc_cal/esp_adc_cal.c b/components/esp_adc_cal/esp_adc_cal.c index f7621fc16..d0748a018 100644 --- a/components/esp_adc_cal/esp_adc_cal.c +++ b/components/esp_adc_cal/esp_adc_cal.c @@ -17,304 +17,285 @@ #include "driver/adc.h" #include "soc/efuse_reg.h" #include "esp_err.h" -#include "esp_log.h" #include "assert.h" -#include "esp_adc_cal_constants.h" #include "esp_adc_cal.h" -#define CONFIG_ADC_CAL_EFUSE_TP_DISABLE /* ----------------------------- Configuration ------------------------------ */ -#ifdef CONFIG_ADC_CAL_EFUSE_TP_DISABLE -#define EFUSE_TP_ENABLED 0 -#else +#ifdef CONFIG_ADC_CAL_EFUSE_TP_ENABLE #define EFUSE_TP_ENABLED 1 +#else +#define EFUSE_TP_ENABLED 0 #endif -#ifdef CONFIG_ADC_CAL_EFUSE_VREF_DISABLE -#define EFUSE_VREF_ENABLED 0 -#else +#ifdef CONFIG_ADC_CAL_EFUSE_VREF_ENABLE #define EFUSE_VREF_ENABLED 1 +#else +#define EFUSE_VREF_ENABLED 0 #endif -#ifdef CONFIG_ADC_CAL_DEFAULT_VREF_DISABLE -#define DEFAULT_VREF_ENABLED 0 +#ifdef CONFIG_ADC_CAL_LUT_ENABLE +#define LUT_ENABLED 1 #else -#define DEFAULT_VREF_ENABLED 1 +#define LUT_ENABLED 0 #endif +/* + * By default, ESP32s that have Two Point values burned to BLOCK3 will also + * set the EFUSE_BLK3_PART_RESERVE flag to indicate this. However, some ESP32s + * with Two Point values do not set this flag. Set the following definition + * to 0 if this is the case. + */ +#define CHECK_BLK3_FLAG 1 +/* + * By default, Vref is burned into eFuse of ESP32s in Sign-Magnitude format. + * However some chips have Vref burned in two's complement format. Set the following + * definition to 1 if this is the case. + */ +#define VREF_FORMAT 0 /* ------------------------------ eFuse Access ----------------------------- */ #define BLK3_RESERVED_REG EFUSE_BLK0_RDATA4_REG #define VREF_REG EFUSE_BLK0_RDATA4_REG -#define VREF_SIGN_BIT 0x10 -#define VREF_MAG_BITS 0x0F +#define VREF_MASK 0x1F #define VREF_STEP_SIZE 7 #define VREF_OFFSET 1100 #define TP_REG EFUSE_BLK3_RDATA3_REG #define TP_LOW1_OFFSET 278 #define TP_LOW2_OFFSET 421 +#define TP_LOW_MASK 0x7F +#define TP_LOW_VOLTAGE 150 #define TP_HIGH1_OFFSET 3265 #define TP_HIGH2_OFFSET 3406 -#define TP_LOW_SIGN_BIT 0x40 -#define TP_LOW_MAG_BITS 0x3F -#define TP_LOW_VOLTAGE 150 -#define TP_HIGH_SIGN_BIT 0x100 -#define TP_HIGH_MAG_BITS 0xFF +#define TP_HIGH_MASK 0x1FF #define TP_HIGH_VOLTAGE 850 #define TP_STEP_SIZE 4 -/* -------------------- Linear and LUT mode constants ----------------------- */ +/* ----------------------- Raw to Voltage Constants ------------------------- */ #define LIN_COEFF_A_SCALE 65536 #define LIN_COEFF_A_ROUND (LIN_COEFF_A_SCALE/2) -#define LUT_VREF_IDEAL 1100 + #define LUT_VREF_LOW 1000 #define LUT_VREF_HIGH 1200 -#define LUT_ADC_STEP_SIZE 128 -#define ADC_12_BIT_MAX 4095 +#define LUT_ADC_STEP_SIZE 64 +#define LUT_POINTS 20 +#define LUT_LOW_THRESH 2880 +#define LUT_HIGH_THRESH (LUT_LOW_THRESH + LUT_ADC_STEP_SIZE) +#define ADC_12_BIT_RES 4096 -#define ADC_CAL_ASSERT(cond, ret) ({ \ +#define ADC_CAL_CHECK(cond, ret) ({ \ if(!(cond)){ \ return ret; \ } \ }) -#define ESP_ADC_CAL_ERR_STR "No characterization possible" -static const char* ESP_ADC_CAL_TAG = "esp_adc_cal_log"; +/* ------------------------ Characterization Constants ---------------------- */ -extern const uint32_t adc1_lin_tp_atten_scale[4]; -extern const uint32_t adc2_lin_tp_atten_scale[4]; -extern const uint32_t adc1_lin_tp_atten_offset[4]; -extern const uint32_t adc2_lin_tp_atten_offset[4]; -extern const uint32_t adc1_lin_vref_atten_scale[4]; -extern const uint32_t adc2_lin_vref_atten_scale[4]; -extern const uint32_t adc1_lin_vref_atten_offset[4]; -extern const uint32_t adc2_lin_vref_atten_offset[4]; +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}; +static const uint32_t adc1_tp_atten_offset[4] = {0, 1, 27, 54}; +static const uint32_t adc2_tp_atten_offset[4] = {0, 9, 26, 66}; -extern const esp_adc_cal_lookup_table_t lut_atten0_adc1; -extern const esp_adc_cal_lookup_table_t lut_atten0_adc2; -extern const esp_adc_cal_lookup_table_t lut_atten1_adc1; -extern const esp_adc_cal_lookup_table_t lut_atten1_adc2; -extern const esp_adc_cal_lookup_table_t lut_atten2_adc1; -extern const esp_adc_cal_lookup_table_t lut_atten2_adc2; -extern const esp_adc_cal_lookup_table_t lut_atten3_adc1; -extern const esp_adc_cal_lookup_table_t lut_atten3_adc2; +static const uint32_t adc1_vref_atten_scale[4] = {57431, 76236, 105481, 196602}; +static const uint32_t adc2_vref_atten_scale[4] = {57236, 76175, 105678, 197170}; +static const uint32_t adc1_vref_atten_offset[4] = {75, 78, 107, 142}; +static const uint32_t adc2_vref_atten_offset[4] = {63, 66, 89, 128}; + +//20 Point lookup tables, covering ADC readings from 2880 to 4096, step size of 64 +static const uint32_t lut_adc1_low[LUT_POINTS] = {2240, 2297, 2352, 2405, 2457, 2512, 2564, 2616, 2664, 2709, + 2754, 2795, 2832, 2868, 2903, 2937, 2969, 3000, 3030, 3060}; +static const uint32_t lut_adc1_high[LUT_POINTS] = {2667, 2706, 2745, 2780, 2813, 2844, 2873, 2901, 2928, 2956, + 2982, 3006, 3032, 3059, 3084, 3110, 3135, 3160, 3184, 3209}; +static const uint32_t lut_adc2_low[LUT_POINTS] = {2238, 2293, 2347, 2399, 2451, 2507, 2561, 2613, 2662, 2710, + 2754, 2792, 2831, 2869, 2904, 2937, 2968, 2999, 3029, 3059}; +static const uint32_t lut_adc2_high[LUT_POINTS] = {2657, 2698, 2738, 2774, 2807, 2838, 2867, 2894, 2921, 2946, + 2971, 2996, 3020, 3043, 3067, 3092, 3116, 3139, 3162, 3185}; /* ----------------------- EFuse Access Functions --------------------------- */ -//Check if Vref is burned in eFuse static bool check_efuse_vref() { - //Check eFuse for vref + //Check if Vref is burned in eFuse return (REG_GET_FIELD(VREF_REG, EFUSE_RD_ADC_VREF) != 0) ? true : false; } -//Check if Two Point values are burned in eFuse static bool check_efuse_tp() { -#ifndef CONFIG_ADC_CAL_NO_BLK3_RESERVE_FLAG - //BLK3_PART_RESERVE flag must be set - if(REG_GET_FIELD(BLK3_RESERVED_REG, EFUSE_RD_BLK3_PART_RESERVE) == 0){ + //Check if Two Point values are burned in eFuse + if (CHECK_BLK3_FLAG && (REG_GET_FIELD(BLK3_RESERVED_REG, EFUSE_RD_BLK3_PART_RESERVE) == 0)) { return false; } -#endif //All TP cal values must be non zero - if((REG_GET_FIELD(TP_REG, EFUSE_RD_ADC1_TP_LOW) != 0) && - (REG_GET_FIELD(TP_REG, EFUSE_RD_ADC2_TP_LOW) != 0) && - (REG_GET_FIELD(TP_REG, EFUSE_RD_ADC1_TP_HIGH) != 0) && - (REG_GET_FIELD(TP_REG, EFUSE_RD_ADC2_TP_HIGH) != 0)){ + if ((REG_GET_FIELD(TP_REG, EFUSE_RD_ADC1_TP_LOW) != 0) && + (REG_GET_FIELD(TP_REG, EFUSE_RD_ADC2_TP_LOW) != 0) && + (REG_GET_FIELD(TP_REG, EFUSE_RD_ADC1_TP_HIGH) != 0) && + (REG_GET_FIELD(TP_REG, EFUSE_RD_ADC2_TP_HIGH) != 0)) { return true; } else { return false; } } -//Read Vref from eFuse +static inline int decode_bits(uint32_t bits, uint32_t mask, bool is_twos_compl) +{ + int ret; + if (bits & (~(mask >> 1) & mask)) { //Check sign bit (MSB of mask) + //Negative + if (is_twos_compl) { + ret = -(((~bits) + 1) & (mask >> 1)); //2's complement + } else { + ret = -(bits & (mask >> 1)); //Sign-magnitude + } + } else { + //Positive + ret = bits & (mask >> 1); + } + return ret; +} + static uint32_t read_efuse_vref() { //eFuse stores deviation from ideal reference voltage uint32_t ret = VREF_OFFSET; //Ideal vref uint32_t bits = REG_GET_FIELD(VREF_REG, EFUSE_ADC_VREF); - - if(bits & VREF_SIGN_BIT){ //Negative deviation from ideal Vref -#ifndef CONFIG_ADC_CAL_NO_BLK3_RESERVE_FLAG - //Deviation stored in sign-magnitude format - ret -= (bits & VREF_MAG_BITS) * VREF_STEP_SIZE; -#else - //Deviation stored in two's complement - ret -= (((~bits)+1) & VREF_MAG_BITS) * VREF_STEP_SIZE; -#endif - } else { //Positive deviation from ideal Vref - ret += (bits & VREF_MAG_BITS) * VREF_STEP_SIZE; - } + ret += decode_bits(bits, VREF_MASK, VREF_FORMAT) * VREF_STEP_SIZE; return ret; //ADC Vref in mV } -//Read Two Point low reading from eFuse static uint32_t read_efuse_tp_low(adc_unit_t adc_num) { //ADC reading at 150mV stored in two's complement format uint32_t ret; uint32_t bits; - if(adc_num == ADC_UNIT_1){ + if (adc_num == ADC_UNIT_1) { ret = TP_LOW1_OFFSET; bits = REG_GET_FIELD(TP_REG, EFUSE_RD_ADC1_TP_LOW); } else { ret = TP_LOW2_OFFSET; bits = REG_GET_FIELD(TP_REG, EFUSE_RD_ADC2_TP_LOW); } - - //Represented in two's complement format - if(bits & TP_LOW_SIGN_BIT){ //Negative - ret -= (((~bits) + 1) & TP_LOW_MAG_BITS) * TP_STEP_SIZE; - } else { //Positive - ret += (bits & TP_LOW_MAG_BITS) * TP_STEP_SIZE; - } + ret += decode_bits(bits, TP_LOW_MASK, true) * TP_STEP_SIZE; return ret; //Reading of ADC at 150mV } -//Read Two Point high reading from eFuse static uint32_t read_efuse_tp_high(adc_unit_t adc_num) { //ADC reading at 850mV stored in two's complement format uint32_t ret; uint32_t bits; - if(adc_num == ADC_UNIT_1){ + + if (adc_num == ADC_UNIT_1) { ret = TP_HIGH1_OFFSET; bits = REG_GET_FIELD(TP_REG, EFUSE_RD_ADC1_TP_HIGH); } else { ret = TP_HIGH2_OFFSET; bits = REG_GET_FIELD(TP_REG, EFUSE_RD_ADC2_TP_HIGH); } - - //Represented in two's complement format - if(bits & TP_HIGH_SIGN_BIT){ //Negative - ret -= (((~bits) + 1) & TP_HIGH_MAG_BITS) * TP_STEP_SIZE; - } else { //Positive - ret += (bits & TP_HIGH_MAG_BITS) * TP_STEP_SIZE; - } + ret += decode_bits(bits, TP_HIGH_MASK, true) * TP_STEP_SIZE; return ret; //Reading of ADC at 850mV } /* ----------------------- Characterization Functions ----------------------- */ -//Linear characterization using Two Point values -static void characterize_lin_tp(adc_unit_t adc_num, - adc_atten_t atten, - uint32_t high, - uint32_t low, - uint32_t *coeff_a, - uint32_t *coeff_b) +static void characterize_using_two_point(adc_unit_t adc_num, + adc_atten_t atten, + uint32_t high, + uint32_t low, + uint32_t *coeff_a, + uint32_t *coeff_b) { const uint32_t *atten_scales; const uint32_t *atten_offsets; - if(adc_num == ADC_UNIT_1){ //Using ADC 1 - atten_scales = adc1_lin_tp_atten_scale; - atten_offsets = adc1_lin_tp_atten_offset; + if (adc_num == ADC_UNIT_1) { //Using ADC 1 + atten_scales = adc1_tp_atten_scale; + atten_offsets = adc1_tp_atten_offset; } else { //Using ADC 2 - atten_scales = adc2_lin_tp_atten_scale; - atten_offsets = adc2_lin_tp_atten_offset; + atten_scales = adc2_tp_atten_scale; + atten_offsets = adc2_tp_atten_offset; } //Characterize ADC-Voltage curve as y = (coeff_a * x) + coeff_b uint32_t delta_x = high - low; uint32_t delta_v = TP_HIGH_VOLTAGE - TP_LOW_VOLTAGE; - //coeff_a = (delta_v/delta_x) * atten_scale - *coeff_a = (delta_v * atten_scales[atten] + (delta_x/2)) / delta_x; //+(delta_x/2) for rounding - //coeff_b = high_v - ((delta_v/delta_x) * high_x) + atten_offset - *coeff_b = TP_HIGH_VOLTAGE - ((delta_v * high + (delta_x/2)) / delta_x) + atten_offsets[atten]; + //Where coeff_a = (delta_v/delta_x) * atten_scale + *coeff_a = (delta_v * atten_scales[atten] + (delta_x / 2)) / delta_x; //+(delta_x/2) for rounding + //Where coeff_b = high_v - ((delta_v/delta_x) * high_x) + atten_offset + *coeff_b = TP_HIGH_VOLTAGE - ((delta_v * high + (delta_x / 2)) / delta_x) + atten_offsets[atten]; } -//Linear characterization using Vref -static void characterize_lin_vref(adc_unit_t adc_num, - adc_atten_t atten, - uint32_t vref, - uint32_t *coeff_a, - uint32_t *coeff_b) +static void characterize_using_vref(adc_unit_t adc_num, + adc_atten_t atten, + uint32_t vref, + uint32_t *coeff_a, + uint32_t *coeff_b) { - const uint32_t *atten_scales;; + const uint32_t *atten_scales; const uint32_t *atten_offsets; - if(adc_num == ADC_UNIT_1){ //Using ADC 1 - atten_scales = adc1_lin_vref_atten_scale; - atten_offsets = adc1_lin_vref_atten_offset; + if (adc_num == ADC_UNIT_1) { //Using ADC 1 + atten_scales = adc1_vref_atten_scale; + atten_offsets = adc1_vref_atten_offset; } else { //Using ADC 2 - atten_scales = adc2_lin_vref_atten_scale; - atten_offsets = adc2_lin_vref_atten_offset; + atten_scales = adc2_vref_atten_scale; + atten_offsets = adc2_vref_atten_offset; } //Characterize ADC-Voltage curve as y = (coeff_a * x) + coeff_b - //coeff_a = (vref/4096) * atten_scale - *coeff_a = (vref * atten_scales[atten]) / (ADC_12_BIT_MAX + 1); + //Where coeff_a = (vref/4096) * atten_scale + *coeff_a = (vref * atten_scales[atten]) / (ADC_12_BIT_RES); *coeff_b = atten_offsets[atten]; } -//LUT characterization -static void characterize_lut(adc_unit_t adc_num, - adc_atten_t atten, - uint32_t vref, - const esp_adc_cal_lookup_table_t **table_ptr) -{ - //Get pointer to the correct lookup table - if(atten == ADC_ATTEN_DB_0){ - *table_ptr = (adc_num == ADC_UNIT_1) ? &lut_atten0_adc1 : &lut_atten0_adc2; - } else if (atten == ADC_ATTEN_DB_2_5){ - *table_ptr = (adc_num == ADC_UNIT_1) ? &lut_atten1_adc1 : &lut_atten1_adc2; - } else if (atten == ADC_ATTEN_DB_6){ - *table_ptr = (adc_num == ADC_UNIT_1) ? &lut_atten2_adc1 : &lut_atten2_adc2; - } else { - *table_ptr = (adc_num == ADC_UNIT_1) ? &lut_atten3_adc1 : &lut_atten3_adc2; - } -} - /* ------------------------ Conversion Functions --------------------------- */ -//Calculate voltage using linear characterization of the ADC curve -static uint32_t linear_raw_to_voltage(uint32_t adc, - uint32_t gain, - uint32_t offset) +static uint32_t calculate_voltage_linear(uint32_t adc_reading, uint32_t coeff_a, uint32_t coeff_b) { - //ADC Curve is characterized as y = coeff_a * adc + coeff_b - //All gains scaled by 65536 - return (((gain * adc) + LIN_COEFF_A_ROUND) / LIN_COEFF_A_SCALE) + offset; + //Where voltage = coeff_a * adc_reading + coeff_b + return (((coeff_a * adc_reading) + LIN_COEFF_A_ROUND) / LIN_COEFF_A_SCALE) + coeff_b; } -//Calculate voltage using a lookup table -static uint32_t lut_raw_to_voltage(uint32_t adc, uint32_t vref, const esp_adc_cal_lookup_table_t *table) +//Only call when ADC reading is above threshold +static uint32_t calculate_voltage_lut(uint32_t adc, uint32_t vref, const uint32_t *low_vref_curve, const uint32_t *high_vref_curve) { //Get index of lower bound points of LUT - uint32_t i = (adc / LUT_ADC_STEP_SIZE); + uint32_t i = (adc - LUT_LOW_THRESH) / LUT_ADC_STEP_SIZE; //Let the X Axis be Vref, Y axis be ADC reading, and Z be voltage int x2dist = LUT_VREF_HIGH - vref; //(x2 - x) int x1dist = vref - LUT_VREF_LOW; //(x - x1) - int y2dist = ((i+1) * LUT_ADC_STEP_SIZE) - adc; //(y2 - y) - int y1dist = adc - (i * LUT_ADC_STEP_SIZE); //(y - y1) + int y2dist = ((i + 1) * LUT_ADC_STEP_SIZE) + LUT_LOW_THRESH - adc; //(y2 - y) + int y1dist = adc - ((i * LUT_ADC_STEP_SIZE) + LUT_LOW_THRESH); //(y - y1) //For points for bilinear interpolation - int q11 = (int)table->low_vref_curve[i]; //Lower bound point of low_vref_curve - int q12 = (int)table->low_vref_curve[i+1]; //Upper bound point of low_vref_curve - int q21 = (int)table->high_vref_curve[i]; //Lower bound point of high_vref_curve - int q22 = (int)table->high_vref_curve[i+1]; //Upper bound point of high_vref_curve + int q11 = low_vref_curve[i]; //Lower bound point of low_vref_curve + int q12 = low_vref_curve[i + 1]; //Upper bound point of low_vref_curve + int q21 = high_vref_curve[i]; //Lower bound point of high_vref_curve + int q22 = high_vref_curve[i + 1]; //Upper bound point of high_vref_curve //Bilinear interpolation - //z = 1/((x2-x1)*(y2-y1)) * ( (q11*x2dist*y2dist) + (q21*x1dist*y2dist) + (q12*x2dist*y1dist) + (q22*x1dist*y1dist) ) - int voltage = (q11*x2dist*y2dist) + (q21*x1dist*y2dist) + (q12*x2dist*y1dist) + (q22*x1dist*y1dist); - voltage += ((LUT_VREF_HIGH - LUT_VREF_LOW) * LUT_ADC_STEP_SIZE)/2; //Integer division rounding + //Where z = 1/((x2-x1)*(y2-y1)) * ( (q11*x2dist*y2dist) + (q21*x1dist*y2dist) + (q12*x2dist*y1dist) + (q22*x1dist*y1dist) ) + int voltage = (q11 * x2dist * y2dist) + (q21 * x1dist * y2dist) + (q12 * x2dist * y1dist) + (q22 * x1dist * y1dist); + voltage += ((LUT_VREF_HIGH - LUT_VREF_LOW) * LUT_ADC_STEP_SIZE) / 2; //Integer division rounding voltage /= ((LUT_VREF_HIGH - LUT_VREF_LOW) * LUT_ADC_STEP_SIZE); //Divide by ((x2-x1)*(y2-y1)) return (uint32_t)voltage; } +static inline uint32_t interpolate_two_points(uint32_t y1, uint32_t y2, uint32_t x_step, uint32_t x) +{ + //Interpolate between two points (x1,y1) (x2,y2) between 'lower' and 'upper' separated by 'step' + return ((y1 * x_step) + (y2 * x) - (y1 * x) + (x_step / 2)) / x_step; +} + /* ------------------------- Public API ------------------------------------- */ esp_err_t esp_adc_cal_check_efuse(esp_adc_cal_value_t source) { - if(source == ESP_ADC_CAL_VAL_EFUSE_TP){ + if (source == ESP_ADC_CAL_VAL_EFUSE_TP) { return (check_efuse_tp()) ? ESP_OK : ESP_ERR_NOT_SUPPORTED; - } else if (source == ESP_ADC_CAL_VAL_EFUSE_VREF){ + } else if (source == ESP_ADC_CAL_VAL_EFUSE_VREF) { return (check_efuse_vref()) ? ESP_OK : ESP_ERR_NOT_SUPPORTED; } else { return ESP_ERR_INVALID_ARG; @@ -323,108 +304,99 @@ esp_err_t esp_adc_cal_check_efuse(esp_adc_cal_value_t source) esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num, adc_atten_t atten, - esp_adc_cal_mode_t mode, - uint32_t vref_default, + adc_bits_width_t bit_width, + uint32_t default_vref, esp_adc_cal_characteristics_t *chars) { + //Check parameters assert((adc_num == ADC_UNIT_1) || (adc_num == ADC_UNIT_2)); assert(chars != NULL); + assert(bit_width < ADC_WIDTH_MAX); //Check eFuse if enabled to do so bool efuse_tp_present = check_efuse_tp(); bool efuse_vref_present = check_efuse_vref(); esp_adc_cal_value_t ret; - if(mode == ESP_ADC_CAL_MODE_LIN){ - if(efuse_tp_present && EFUSE_TP_ENABLED){ - uint32_t high = read_efuse_tp_high(adc_num); - uint32_t low = read_efuse_tp_low(adc_num); - characterize_lin_tp(adc_num, atten, high, low, &chars->linear_chars.coeff_a, &chars->linear_chars.coeff_b); - ret = ESP_ADC_CAL_VAL_EFUSE_TP; - } else if(efuse_vref_present && EFUSE_VREF_ENABLED){ - uint32_t vref = read_efuse_vref(); - characterize_lin_vref(adc_num, atten, vref, &chars->linear_chars.coeff_a, &chars->linear_chars.coeff_b); - ret = ESP_ADC_CAL_VAL_EFUSE_VREF; - } else if(DEFAULT_VREF_ENABLED){ - characterize_lin_vref(adc_num, atten, vref_default, &chars->linear_chars.coeff_a, &chars->linear_chars.coeff_b); - ret = ESP_ADC_CAL_VAL_DEFAULT_VREF; - } else { - goto err; - } - } else if (mode == ESP_ADC_CAL_MODE_LUT){ - if(efuse_vref_present && EFUSE_VREF_ENABLED){ - uint32_t vref = read_efuse_vref(); - chars->lut_chars.vref = vref; - characterize_lut(adc_num, atten, vref, &chars->lut_chars.table); - ret = ESP_ADC_CAL_VAL_EFUSE_VREF; - } else if(DEFAULT_VREF_ENABLED){ - chars->lut_chars.vref = vref_default; - characterize_lut(adc_num, atten, vref_default, &chars->lut_chars.table); - ret = ESP_ADC_CAL_VAL_DEFAULT_VREF; - } else{ - goto err; - } + if (efuse_tp_present && EFUSE_TP_ENABLED) { + //Characterize based on Two Point values + uint32_t high = read_efuse_tp_high(adc_num); + uint32_t low = read_efuse_tp_low(adc_num); + characterize_using_two_point(adc_num, atten, high, low, &chars->coeff_a, &chars->coeff_b); + ret = ESP_ADC_CAL_VAL_EFUSE_TP; + } else if (efuse_vref_present && EFUSE_VREF_ENABLED) { + //Characterize based on eFuse Vref + uint32_t vref = read_efuse_vref(); + characterize_using_vref(adc_num, atten, vref, &chars->coeff_a, &chars->coeff_b); + ret = ESP_ADC_CAL_VAL_EFUSE_VREF; } else { - goto err; + //Characterized based on default Vref + characterize_using_vref(adc_num, atten, default_vref, &chars->coeff_a, &chars->coeff_b); + ret = ESP_ADC_CAL_VAL_DEFAULT_VREF; } - chars->mode = mode; - chars->adc_num = adc_num; - return ret; - err: //No possible characterization - // usually only occurs if users manually disable calibration values and modes in menuconfig - ESP_LOGE(ESP_ADC_CAL_TAG, ESP_ADC_CAL_ERR_STR); - abort(); - return ESP_ADC_CAL_VAL_DEFAULT_VREF; //Should not reach this point, added to suppress Eclipse warnings + //Initialized remaining fields + chars->adc_num = adc_num; + chars->atten = atten; + chars->bit_width = bit_width; + chars->vref = (efuse_vref_present) ? read_efuse_vref() : default_vref; + //Initialize fields for lookup table if necessary + if (LUT_ENABLED && atten == ADC_ATTEN_DB_11) { + chars->low_curve = (adc_num == ADC_UNIT_1) ? lut_adc1_low : lut_adc2_low; + chars->high_curve = (adc_num == ADC_UNIT_1) ? lut_adc1_high : lut_adc2_high; + } else { + chars->low_curve = NULL; + chars->high_curve = NULL; + } + return ret; } -uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, - adc_bits_width_t bit_width, - const esp_adc_cal_characteristics_t *chars) +uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_characteristics_t *chars) { assert(chars != NULL); + //Scale adc_rading if not 12 bits wide - if(bit_width != ADC_WIDTH_BIT_12){ - adc_reading = (adc_reading << (ADC_WIDTH_BIT_12 - bit_width)); - //If adc_reading is out of 12bit range - if(adc_reading > ADC_12_BIT_MAX){ - adc_reading = ADC_12_BIT_MAX; //Set to 12bit max - } + adc_reading = (adc_reading << (ADC_WIDTH_BIT_12 - chars->bit_width)); + if (adc_reading > ADC_12_BIT_RES - 1) { + adc_reading = ADC_12_BIT_RES - 1; //Set to 12bit res max } - //Convert ADC reading to voltage in mV - if(chars->mode == ESP_ADC_CAL_MODE_LUT){ //Lookup Table - assert(chars->lut_chars.table != NULL); - return lut_raw_to_voltage(adc_reading, chars->lut_chars.vref, chars->lut_chars.table); + if (LUT_ENABLED && (chars->atten == ADC_ATTEN_DB_11) && (adc_reading >= LUT_LOW_THRESH)) { //Check if in non-linear region + //Use lookup table to get voltage in non linear portion of ADC_ATTEN_DB_11 + uint32_t lut_voltage = calculate_voltage_lut(adc_reading, chars->vref, chars->low_curve, chars->high_curve); + if (adc_reading <= LUT_HIGH_THRESH) { //If ADC is transitioning from linear region to non-linear region + //Linearly interpolate between linear voltage and lut voltage + uint32_t linear_voltage = calculate_voltage_linear(adc_reading, chars->coeff_a, chars->coeff_b); + return interpolate_two_points(linear_voltage, lut_voltage, LUT_ADC_STEP_SIZE, (adc_reading - LUT_LOW_THRESH)); + } else { + return lut_voltage; + } } else { - return linear_raw_to_voltage(adc_reading, chars->linear_chars.coeff_a, chars->linear_chars.coeff_b); + return calculate_voltage_linear(adc_reading, chars->coeff_a, chars->coeff_b); } } -esp_err_t adc_to_voltage(adc_channel_t channel, - adc_bits_width_t bit_width, - const esp_adc_cal_characteristics_t *chars, - uint32_t *voltage) +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_ASSERT(chars != NULL, ESP_ERR_INVALID_ARG); - ADC_CAL_ASSERT(voltage != NULL, ESP_ERR_INVALID_ARG); + ADC_CAL_CHECK(chars != NULL, ESP_ERR_INVALID_ARG); + ADC_CAL_CHECK(voltage != NULL, ESP_ERR_INVALID_ARG); - if(chars->adc_num == ADC_UNIT_1){ + int adc_reading; + if (chars->adc_num == ADC_UNIT_1) { //Check channel is valid on ADC1 - ADC_CAL_ASSERT((adc1_channel_t)channel < ADC1_CHANNEL_MAX, ESP_ERR_INVALID_ARG); - uint32_t adc_reading = (uint32_t)adc1_get_raw(channel); //Todo: get_raw function to change - *voltage = esp_adc_cal_raw_to_voltage(adc_reading, bit_width, chars); + 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_ASSERT((adc2_channel_t)channel < ADC2_CHANNEL_MAX, ESP_ERR_INVALID_ARG); - int adc_reading; - if(adc2_get_raw(channel, bit_width, &adc_reading) != ESP_OK){ - //Timed out waiting for ADC2 - return ESP_ERR_TIMEOUT; + 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, bit_width, chars); } + *voltage = esp_adc_cal_raw_to_voltage((uint32_t)adc_reading, chars); return ESP_OK; } @@ -435,21 +407,15 @@ void esp_adc_cal_get_characteristics(uint32_t vref, adc_bits_width_t bit_width, esp_adc_cal_characteristics_t *chars) { - //Default to ADC1 and LUT mode assert(chars != NULL); - - //bit_width parameter unused, kept due to legacy API - chars->mode = ESP_ADC_CAL_MODE_LUT; - chars->lut_chars.vref = vref; - characterize_lut(ADC_UNIT_1, atten, vref, &chars->lut_chars.table); - chars->adc_num = ADC_UNIT_1; + esp_adc_cal_characterize(ADC_UNIT_1, atten, bit_width, vref, chars); } uint32_t adc1_to_voltage(adc1_channel_t channel, const esp_adc_cal_characteristics_t *chars) { assert(chars != NULL); uint32_t voltage = 0; - adc_to_voltage((adc_channel_t) channel, ADC_WIDTH_BIT_12, chars, &voltage); + esp_adc_cal_get_voltage((adc_channel_t)channel, chars, &voltage); return voltage; } diff --git a/components/esp_adc_cal/esp_adc_cal_constants.c b/components/esp_adc_cal/esp_adc_cal_constants.c deleted file mode 100644 index 833d2bcb3..000000000 --- a/components/esp_adc_cal/esp_adc_cal_constants.c +++ /dev/null @@ -1,222 +0,0 @@ -// 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. - -#include "esp_adc_cal.h" -#include "esp_adc_cal_constants.h" - -/* ------------------------- Lookup Tables ---------------------------------- */ - -/** - * Mean error of 70 modules: 2.674297mV - * Max error of 70 modules: 12.176238mV - * Mean of max errors of 70 modules: 7.079913mV - */ -const esp_adc_cal_lookup_table_t lut_atten0_adc1 = { - .low_vref_curve = { - 58, 84, 114, 142, 168, 196, 224, 252, - 281, 308, 335, 363, 390, 417, 445, 473, - 501, 528, 556, 583, 611, 637, 665, 692, - 720, 747, 775, 802, 830, 856, 883, 910, - 937 - }, - .high_vref_curve = { - 86, 106, 152, 184, 218, 251, 283, 316, - 348, 381, 415, 447, 480, 513, 546, 579, - 612, 644, 677, 710, 742, 775, 808, 841, - 874, 906, 938, 971, 1003, 1035, 1068, 1100, - 1133 - } -}; - -/** - * Mean error of 70 modules: 3.950325mV - * Max error of 70 modules: 20.975788mV - * Mean of max errors of 70 modules: 6.629054mV - */ -const esp_adc_cal_lookup_table_t lut_atten0_adc2 = { - .low_vref_curve = { - 49, 75, 105, 132, 160, 187, 214, 241, - 268, 296, 324, 352, 380, 407, 434, 461, - 488, 516, 545, 572, 600, 627, 654, 681, - 708, 735, 763, 791, 818, 845, 872, 899, - 927 - }, - .high_vref_curve = { - 72, 103, 137, 170, 202, 235, 268, 302, - 335, 367, 399, 432, 464, 497, 530, 563, - 596, 628, 661, 693, 725, 758, 791, 824, - 857, 889, 921, 954, 986, 1018, 1051, 1084, - 1116 - } -}; - -/** - * Mean error of 70 modules: 3.339671mV - * Max error of 70 modules: 13.476428mV - * Mean of max errors of 70 modules: 5.818235mV - */ -const esp_adc_cal_lookup_table_t lut_atten1_adc1 = { - .low_vref_curve = { - 57, 87, 130, 168, 204, 240, 277, 313, - 349, 387, 424, 461, 498, 534, 571, 607, - 644, 680, 717, 753, 789, 826, 862, 898, - 934, 970, 1007, 1043, 1078, 1115, 1151, 1187, - 1223 - }, - .high_vref_curve = { - 89, 128, 178, 221, 264, 309, 353, 397, - 442, 484, 528, 572, 616, 659, 703, 747, - 790, 833, 877, 921, 964, 1006, 1050, 1094, - 1137, 1179, 1222, 1266, 1309, 1351, 1394, 1437, - 1481 - } -}; - -/** - * Mean error of 70 modules: 5.319836mV - * Max error of 70 modules: 29.034477mV - * Mean of max errors of 70 modules: 9.226072mV - */ -const esp_adc_cal_lookup_table_t lut_atten1_adc2 = { - .low_vref_curve = { - 47, 86, 122, 159, 196, 232, 268, 305, - 340, 377, 415, 452, 487, 525, 562, 598, - 635, 671, 708, 745, 782, 817, 853, 890, - 926, 962, 999, 1035, 1071, 1107, 1144, 1180, - 1215 - }, - .high_vref_curve = { - 78, 113, 165, 209, 251, 295, 339, 383, - 427, 470, 514, 558, 602, 644, 688, 732, - 775, 818, 862, 905, 948, 992, 1036, 1079, - 1122, 1164, 1208, 1252, 1295, 1338, 1381, 1424, - 1468 - } -}; - -/** - * Mean error of 70 modules: 4.943406mV - * Max error of 70 modules: 19.203104mV - * Mean of max errors of 70 modules: 9.462435mV - */ -const esp_adc_cal_lookup_table_t lut_atten2_adc1 = { - .low_vref_curve = { - 72, 117, 177, 228, 281, 330, 382, 434, - 483, 536, 587, 639, 690, 740, 791, 842, - 892, 942, 992, 1042, 1092, 1141, 1191, 1241, - 1290, 1339, 1391, 1441, 1490, 1540, 1591, 1642, - 1692 - }, - .high_vref_curve = { - 120, 175, 243, 305, 364, 426, 488, 550, - 612, 671, 732, 793, 852, 912, 973, 1033, - 1092, 1151, 1211, 1272, 1331, 1390, 1450, 1511, - 1571, 1631, 1691, 1752, 1812, 1872, 1933, 1994, - 2056 - } -}; - -/** - * Mean error of 70 modules: 7.782408mV - * Max error of 70 modules: 45.327423mV - * Mean of max errors of 70 modules: 13.569581mV - */ -const esp_adc_cal_lookup_table_t lut_atten2_adc2 = { - .low_vref_curve = { - 61, 119, 164, 216, 267, 317, 369, 420, - 471, 522, 574, 625, 676, 726, 777, 828, - 878, 928, 979, 1030, 1080, 1130, 1180, 1230, - 1280, 1330, 1381, 1432, 1483, 1532, 1583, 1634, - 1685 - }, - .high_vref_curve = { - 102, 152, 225, 286, 347, 407, 469, 530, - 590, 651, 712, 773, 832, 892, 953, 1013, - 1073, 1132, 1192, 1253, 1313, 1372, 1432, 1494, - 1554, 1613, 1675, 1735, 1795, 1856, 1917, 1978, - 2039 - } -}; - -/** - * Mean error of 70 modules: 9.568297mV - * Max error of 70 modules: 44.480817mV - * Mean of max errors of 70 modules: 20.165069mV - */ -const esp_adc_cal_lookup_table_t lut_atten3_adc1 = { - .low_vref_curve = { - 88, 185, 285, 380, 473, 568, 664, 759, - 853, 945, 1039, 1133, 1224, 1316, 1410, 1505, - 1599, 1692, 1788, 1886, 1983, 2081, 2186, 2297, - 2405, 2511, 2615, 2710, 2793, 2868, 2937, 3000, - 3061 - }, - .high_vref_curve = { - 173, 271, 399, 515, 628, 740, 853, 965, - 1075, 1186, 1299, 1411, 1523, 1634, 1749, 1863, - 1975, 2087, 2201, 2314, 2422, 2525, 2622, 2707, - 2779, 2844, 2901, 2956, 3008, 3059, 3110, 3160, - 3210 - } -}; - -/** - * Mean error of 70 modules: 12.799173mV - * Max error of 70 modules: 60.129227mV - * Mean of max errors of 70 modules: 23.334659mV - */ -const esp_adc_cal_lookup_table_t lut_atten3_adc2 = { - .low_vref_curve = { - 83, 177, 274, 370, 465, 559, 655, 751, - 844, 937, 1031, 1126, 1218, 1312, 1407, 1502, - 1597, 1691, 1787, 1885, 1983, 2081, 2185, 2293, - 2400, 2506, 2612, 2709, 2793, 2868, 2937, 2999, - 3059 - }, - .high_vref_curve = { - 155, 257, 383, 498, 610, 722, 836, 947, - 1058, 1170, 1283, 1396, 1507, 1619, 1733, 1848, - 1960, 2073, 2188, 2300, 2408, 2513, 2611, 2698, - 2773, 2838, 2895, 2946, 2996, 3044, 3092, 3139, - 3186 - } -}; - -/* ---------------- Constants for linear calibration using TP --------------- */ - -// (0.999518539509928, 1.32714033039721, 1.83698541693337, 3.42269475397192) * 65536 -const uint32_t adc1_lin_tp_atten_scale[4] = {65504, 86975, 120389, 224310}; - -// (0.99895306551877194, 1.3253993908658808, 1.8373986067502703, 3.4287787961634124) * 65536 -const uint32_t adc2_lin_tp_atten_scale[4] = {65467, 86861, 120416, 224708}; - -// (0.369882210218334, 1.33352056600476, 26.5266258898132, 54.2706443679156) -const uint32_t adc1_lin_tp_atten_offset[4] = {0, 1, 27, 54}; - -// (0.0, 3.59737421, 26.3951927, 64.738429) -const uint32_t adc2_lin_tp_atten_offset[4] = {0, 9, 26, 66}; - -/* ------------- Constants for linear calibration using Vref ---------------- */ - -// (0.87632707, 1.16327136, 1.60951523, 2.99991113) * 65536 -const uint32_t adc1_lin_vref_atten_scale[4] = {57431, 76236, 105481, 196602}; - -// (0.87335934, 1.16233322, 1.61251264, 3.00858015) * 65536 -const uint32_t adc2_lin_vref_atten_scale[4] = {57236, 76175, 105678, 197170}; - -// (74.97079, 77.67434785, 106.58843993, 142.18959459) -const uint32_t adc1_lin_vref_atten_offset[4] = {75, 78, 107, 142}; - -// (62.73368574, 66.33498527, 89.23584218, 127.69820652) -const uint32_t adc2_lin_vref_atten_offset[4] = {63, 66, 89, 128}; diff --git a/components/esp_adc_cal/include/esp_adc_cal.h b/components/esp_adc_cal/include/esp_adc_cal.h index 9cee127a4..af611357b 100644 --- a/components/esp_adc_cal/include/esp_adc_cal.h +++ b/components/esp_adc_cal/include/esp_adc_cal.h @@ -22,15 +22,6 @@ extern "C" { #include #include "esp_err.h" #include "driver/adc.h" -#include "esp_adc_cal_constants.h" - -/** - * @brief ADC characterization mode - */ -typedef enum { - ESP_ADC_CAL_MODE_LIN = 0, /**< Characterize the ADC as a linear curve*/ - ESP_ADC_CAL_MODE_LUT= 1, /**< Characterize the ADC using a lookup table*/ -} esp_adc_cal_mode_t; /** * @brief Type of calibration value used in characterization @@ -41,48 +32,27 @@ typedef enum { ESP_ADC_CAL_VAL_DEFAULT_VREF = 2, /**< Characterization based on default reference voltage*/ } esp_adc_cal_value_t; -/** - * @brief Structure storing Lookup Table - * - * A Lookup Table (LUT) of a given ADC and attenuation contains two curves - * mapping ADC readings to a voltage in mV. Each curve contains 33 equally spaced - * points separated by a step size of 128. The low_vref_curve represents the ADC - * voltage curve of a module with a reference voltage of 1000mV, whilst the - * high_vref_curve represents a reference voltage of 1200mV. - * - * @note Separate LUTs are provided for each ADC at each attenuation - */ -typedef struct { - uint32_t low_vref_curve[33]; /**< Voltage curve at a reference voltage of 1000mV*/ - uint32_t high_vref_curve[33]; /**< Voltage curve at a reference voltage of 1200mV*/ -} esp_adc_cal_lookup_table_t; - /** * @brief Structure storing characteristics of an ADC * - * @note Call esp_adc_cal_get_characteristics() to initialize the structure + * @note Call esp_adc_cal_characterize() to initialize the structure */ typedef struct { - esp_adc_cal_mode_t mode; /**< Characterization mode*/ - adc_unit_t adc_num; /**< ADC number*/ - union { - struct { - uint32_t coeff_a; /**< 1st order coefficient of linear characteristics curve*/ - uint32_t coeff_b; /**< 0th order coefficient of linear characteristics curve*/ - } linear_chars; - struct { - uint32_t vref; /**< Reference voltage*/ - const esp_adc_cal_lookup_table_t *table; /**< Pointer to lookup table*/ - } lut_chars; - }; + adc_unit_t adc_num; /**< ADC number*/ + adc_atten_t atten; /**< ADC attenuation*/ + adc_bits_width_t bit_width; /**< ADC bit width */ + uint32_t coeff_a; /**< Gradient of ADC-Voltage curve*/ + uint32_t coeff_b; /**< Offset of ADC-Voltage curve*/ + uint32_t vref; /**< Vref used by lookup table*/ + const uint32_t *low_curve; /**< Pointer to low Vref curve of lookup table (NULL if unused)*/ + const uint32_t *high_curve; /**< Pointer to high Vref curve of lookup table (NULL if unused)*/ } esp_adc_cal_characteristics_t; - /** - * @brief Checks if ADC calibration values are stored in eFuse + * @brief Checks if ADC calibration values are burned into eFuse * - * This function checks if ADC reference voltage or Two Point calibration voltages - * have been burned to the eFuse of the current ESP32 + * This function checks if ADC reference voltage or Two Point values have been + * burned to the eFuse of the current ESP32 * * @param value_type Type of calibration value (ESP_ADC_CAL_VAL_EFUSE_VREF or ESP_ADC_CAL_VAL_EFUSE_TP) * @@ -94,24 +64,19 @@ typedef struct { esp_err_t esp_adc_cal_check_efuse(esp_adc_cal_value_t value_type); /** - * @brief Characterize an ADC at a particular attenuation under Linear or LUT Mode + * @brief Characterize an ADC at a particular attenuation * - * This function will generate the characteristics curve of one of the ADCs at a - * particular attenuation. This characteristics curve will be stored in a - * characteristics structure. Linear Mode will be characterize the ADC-Voltage - * curve as a linear curve. LUT Mode will characterize the ADC-Voltage curve - * using a lookup table. Calibration values in eFuse will be used to generate - * the characteristics curve if available, and vref_default will be used if they - * are not. + * This function will characterize the ADC at a particular attenuation and generate + * the ADC-Voltage curve in the form of [y = coeff_a * x + coeff_b]. + * Characterization can be based on Two Point values, eFuse Vref, or default Vref + * and the calibration values will be prioritized in that order. * - * @note This function will abort if there are no available options for - * characterization (characterization modes and calibration value types - * can be enabled and disabled in menuconfig) + * @note Two Point values and eFuse Vref can be enabled/disabled using menuconfig. * * @param[in] adc_num ADC to characterize (ADC_UNIT_1 or ADC_UNIT_2) * @param[in] atten Attenuation to characterize - * @param[in] mode Characterization mode (Linear or LUT) - * @param[in] vref_default Default ADC reference voltage in mV (used if eFuse is not available) + * @param[in] bit_width Bit width configuration of ADC + * @param[in] default_vref Default ADC reference voltage in mV (used if eFuse values is not available) * @param[out] chars Pointer to empty structure used to store ADC characteristics * * @return @@ -121,66 +86,60 @@ esp_err_t esp_adc_cal_check_efuse(esp_adc_cal_value_t value_type); */ esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num, adc_atten_t atten, - esp_adc_cal_mode_t mode, - uint32_t vref_default, + adc_bits_width_t bit_width, + uint32_t default_vref, esp_adc_cal_characteristics_t *chars); - /** * @brief Convert an ADC reading to voltage in mV * - * This function converts a an ADC reading to a voltage in mV based on the - * ADC characteristics curve provided. + * This function converts an ADC reading to a voltage in mV based on the ADC's + * characteristics. + * + * @note Characteristics structure must be initialized before this function + * is called (call esp_adc_cal_characterize()) * * @param[in] adc_reading ADC reading - * @param[in] bit_width Bit width of the ADC reading - * @param[in] chars Pointer to initialized structure containing ADC characteristics curve + * @param[in] chars Pointer to initialized structure containing ADC characteristics * * @return Voltage in mV - * - * @note characteristics structure must be initialized first using esp_adc_cal_characterize() */ -uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, adc_bits_width_t bit_width, const esp_adc_cal_characteristics_t *chars); +uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_characteristics_t *chars); /** - * @brief Reads an ADC and returns a voltage in mV + * @brief Reads an ADC and converts the reading to a voltage in mV * * This function reads an ADC then converts the raw reading to a voltage in mV - * using the characteristics curve provided. The ADC that is read is also + * based on the characteristics provided. The ADC that is read is also * determined by the characteristics. * + * @note The Characteristics structure must be initialized before this + * function is called (call esp_adc_cal_characterize()) + * * @param[in] channel ADC Channel to read - * @param[in] bit_width Bit width of ADC reading (must be same as ADC configuration) * @param[in] chars Pointer to initialized ADC characteristics structure * @param[out] voltage Pointer to store converted voltage * * @return - * - ESP_OK: ADC read and converted - * - ESP_ERR_TIMEOUT: Error, could not read ADC + * - ESP_OK: ADC read and converted to mV + * - ESP_ERR_TIMEOUT: Error, timed out attempting to read ADC * - ESP_ERR_INVALID_ARG: Error due to invalid arguments - * - * @note The ADC must be initialized before calling this function. The - * bit_width parameter must be the same as the bit width used in - * configuration - * - * @note Characteristics structure must be initialized using before calling - * this function */ -esp_err_t adc_to_voltage(adc_channel_t channel, adc_bits_width_t bit_width, const esp_adc_cal_characteristics_t *chars, uint32_t *voltage); +esp_err_t esp_adc_cal_get_voltage(adc_channel_t channel, const esp_adc_cal_characteristics_t *chars, uint32_t *voltage); /* -------------------------- Deprecated API ------------------------------- */ /** @cond */ //Doxygen command to hide deprecated function from API Reference /** * @deprecated ADC1 characterization function. Deprecated in order to accommodate - * new characterization functions. Use esp_adc_cal_characterize() instead + * ADC2 and eFuse functionality. Use esp_adc_cal_characterize() instead */ void esp_adc_cal_get_characteristics(uint32_t vref, adc_atten_t atten, adc_bits_width_t bit_width, esp_adc_cal_characteristics_t *chars) __attribute__((deprecated)); /* * @deprecated This function reads ADC1 and returns the corrected voltage. This - * has been deprecated in order to accommodate ADC2 support and new - * ADC calibration methods. Use the new function adc_to_voltage() instead + * has been deprecated in order to accommodate ADC2 support. Use the + * new function esp_adc_cal_get_voltage() instead. */ uint32_t adc1_to_voltage(adc1_channel_t channel, const esp_adc_cal_characteristics_t *chars) __attribute__((deprecated)); diff --git a/components/esp_adc_cal/include/esp_adc_cal_constants.h b/components/esp_adc_cal/include/esp_adc_cal_constants.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/components/soc/esp32/include/soc/efuse_reg.h b/components/soc/esp32/include/soc/efuse_reg.h index 4ca9bfebc..f95eb50ab 100644 --- a/components/soc/esp32/include/soc/efuse_reg.h +++ b/components/soc/esp32/include/soc/efuse_reg.h @@ -86,7 +86,7 @@ #define EFUSE_RD_CHIP_VER_REV1_V 0x1 #define EFUSE_RD_CHIP_VER_REV1_S 15 /* EFUSE_RD_BLK3_PART_RESERVE : R/W ; bitpos:[14] ; default: 1'b0; */ -/*description: If set, this bit indicates that BLOCK3[143:96] is reserved for calibration purposes and BLOCK1/2/3 uses 3/4 encoding*/ +/*description: If set, this bit indicates that BLOCK3[143:96] is reserved for internal use*/ #define EFUSE_RD_BLK3_PART_RESERVE (BIT(14)) #define EFUSE_RD_BLK3_PART_RESERVE_M ((EFUSE_RD_BLK3_PART_RESERVE_V)<<(EFUSE_RD_BLK3_PART_RESERVE_S)) #define EFUSE_RD_BLK3_PART_RESERVE_V 0x1 @@ -336,7 +336,7 @@ #define EFUSE_CHIP_VER_REV1_V 0x1 #define EFUSE_CHIP_VER_REV1_S 15 /* EFUSE_BLK3_PART_RESERVE : R/W ; bitpos:[14] ; default: 1'b0; */ -/*description: If set, this bit indicates that BLOCK3[143:96] is reserved for calibration purposes and BLOCK1/2/3 uses 3/4 encoding*/ +/*description: If set, this bit indicates that BLOCK3[143:96] is reserved for internal use*/ #define EFUSE_BLK3_PART_RESERVE (BIT(14)) #define EFUSE_BLK3_PART_RESERVE_M ((EFUSE_BLK3_PART_RESERVE_V)<<(EFUSE_BLK3_PART_RESERVE_S)) #define EFUSE_BLK3_PART_RESERVE_V 0x1 diff --git a/docs/api-reference/peripherals/adc.rst b/docs/api-reference/peripherals/adc.rst index d5fa4ce34..fa6a6497c 100644 --- a/docs/api-reference/peripherals/adc.rst +++ b/docs/api-reference/peripherals/adc.rst @@ -99,7 +99,7 @@ The ESP32 ADC can be sensitive to noise leading to large discrepancies in ADC re ADC Calibration --------------- -The :component_file:`esp_adc_cal/include/esp_adc_cal.h` API provides functions to correct for differences in measured voltages caused by non-ideal ADC reference voltages and non-linear characteristics (only applicable at 11dB attenuation). The ideal ADC reference voltage is 1100mV, however true reference voltages can range from 1000mV to 1200 mV amongst ESP32s. +The :component_file:`esp_adc_cal/include/esp_adc_cal.h` API provides functions to correct for differences in measured voltages caused by variation of ADC reference voltages (Vref) between chips. Per design the ADC reference voltage is 1100mV, however the true reference voltage can range from 1000mV to 1200mV amongst different ESP32s. .. figure:: ../../_static/adc-vref-graph.jpg :align: center @@ -107,36 +107,64 @@ The :component_file:`esp_adc_cal/include/esp_adc_cal.h` API provides functions t Graph illustrating effect of differing reference voltages on the ADC voltage curve. -Correcting ADC readings using this API involves characterizing one of the ADCs at a given attenuation to obtain a characteristics curve (ADC-Voltage curve). The characteristics curve is used to convert ADC readings to voltages in mV. Representation of characteristics curve will differ under **Linear Mode** and **Lookup Table Mode**. Calculation of the characteristics curve is based on calibration values which can be stored in eFuse or provided by the user. - -.. _linear-mode: - -Linear Mode -^^^^^^^^^^^ - -Linear Mode characterization will generate a linear characteristics curve in the form of ``y = coeff_a * x + coeff_b``. The linear curve will map ADC readings to a voltage in mV. The calibration values which the calculation of ``coeff_a`` and ``coeff_b`` can be based on will be prioritized in the following order - -1. Two Point values -2. eFuse Vref -3. Default Vref - -.. _lut-mode: - -Lookup Table Mode -^^^^^^^^^^^^^^^^^ - -Lookup Table (LUT) Mode characterization utilizes a LUT to represent an ADC’s characteristics curve. Each LUT consists of a High and Low reference curve which are representative of the characteristic curve of ESP32s with a Vref of 1200mV and 1000mV respectively. Converting an ADC reading to a voltage using a LUT involves interpolating between the High and Low curves based on an ESP32’s true Vref. The true Vref can be read from eFuse (eFuse Vref) or provided by the user (Default Vref) if eFuse Vref is unavailable. +Correcting ADC readings using this API involves characterizing one of the ADCs at a given attenuation to obtain a characteristics curve (ADC-Voltage curve) that takes into account the difference in ADC reference voltage. The characteristics curve is in the form of ``y = coeff_a * x + coeff_b`` and is used to convert ADC readings to voltages in mV. Calculation of the characteristics curve is based on calibration values which can be stored in eFuse or provided by the user. Calibration Values ^^^^^^^^^^^^^^^^^^ -Calibration values are used during the characterization processes, and there are currently three possible types of calibration values. Note the availability of these calibration values will depend on the type of version of the ESP32 chip/module. +Calibration values are used to generate characteristic curves that account for the unique ADC reference voltage of a particular ESP32. There are currently three sources of calibration values. The availability of these calibration values will depend on the type and production date of the ESP32 chip/module. -The **Two Point** calibration values represent each of the ADCs’ readings at 150mV and 850mV. The values are burned into eFuse during factory calibration and are used in Linear Mode to generate a linear characteristics curve. Note that the Two Point values are only available on some versions of ESP32 chips/modules +**Two Point** values represent each of the ADCs’ readings at 150mV and 850mV. These values are measured and burned into eFuse ``BLOCK3`` during factory calibration. -The **eFuse Vref** value represents the true reference voltage of the ADCs and can be used in both Linear and LUT modes. This value is measured and burned into eFuse during factory calibration. Note that eFuse Vref is not available on older variations of ESP32 chips/modules +**eFuse Vref** represents the true ADC reference voltage. This value is measured and burned into eFuse ``BLOCK0`` during factory calibration. -**Default Vref** is an estimate of the ADC reference voltage provided by the user as a parameter during characterization. If Two Point or eFuse Vref values are unavailable, Default Vref will be used. To obtain an estimate of an ESP32 modules Vref, users can call the function ``adc2_vref_to_gpio()`` to route the ADC refernce voltage to a GPIO and measure it manually. +**Default Vref** is an estimate of the ADC reference voltage provided by the user as a parameter during characterization. If Two Point or eFuse Vref values are unavailable, **Default Vref** will be used. + +Application Example +^^^^^^^^^^^^^^^^^^^ + +For a full example see esp-idf: :example:`peripherals/adc` + +Characterizing an ADC at a particular attenuation:: + + #include "driver/adc.h" + #include "esp_adc_cal.h" + + ... + + //Characterize ADC at particular atten + esp_adc_cal_characteristics_t *adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t)); + esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_chars); + //Check type of calibration value used to characterize ADC + if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { + printf("eFuse Vref"); + } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { + printf("Two Point"); + } else { + printf("Default"); + } + +Reading an ADC then converting the reading to a voltage:: + + #include "driver/adc.h" + #include "esp_adc_cal.h" + + ... + uint32_t reading = adc1_get_raw(ADC1_CHANNEL_5); + uint32_t voltage = esp_adc_cal_raw_to_voltage(reading, adc_chars); + +Routing ADC reference voltage to GPIO, so it can be manually measured (for **Default Vref**):: + + #include "driver/adc.h" + + ... + + esp_err_t status = adc2_vref_to_gpio(GPIO_NUM_25); + if (status == ESP_OK) { + printf("v_ref routed to GPIO\n"); + } else { + printf("failed to route v_ref\n"); + } GPIO Lookup Macros ------------------ diff --git a/examples/peripherals/adc/main/adc1_example_main.c b/examples/peripherals/adc/main/adc1_example_main.c index 9b7d9db8d..00de0ab8b 100644 --- a/examples/peripherals/adc/main/adc1_example_main.c +++ b/examples/peripherals/adc/main/adc1_example_main.c @@ -14,67 +14,79 @@ #include "driver/adc.h" #include "esp_adc_cal.h" -#define PRINT_VAL_SUPPORT(support, type) ({ \ - if(support == ESP_OK){ \ - printf("%s: supported\n", (type)); \ - } else { \ - printf("%s: not supported\n", (type)); \ - } \ -}) - -#define PRINT_VAL_TYPE(type, mode) ({ \ - if(type == ESP_ADC_CAL_VAL_EFUSE_TP){ \ - printf("%s mode: Two Point Value\n", (mode)); \ - } else if (type == ESP_ADC_CAL_VAL_EFUSE_VREF){ \ - printf("%s mode: eFuse Vref\n", (mode)); \ - } else { \ - printf("%s mode: Default Vref\n", (mode)); \ - } \ -}) - #define DEFAULT_VREF 1100 //Use adc2_vref_to_gpio() to obtain a better estimate -#define NO_OF_SAMPLES 64 +#define NO_OF_SAMPLES 64 //Multisampling -static esp_adc_cal_characteristics_t *adc_linear_chars; -static esp_adc_cal_characteristics_t *adc_lut_chars; -static adc1_channel_t channel = ADC1_CHANNEL_6; +static esp_adc_cal_characteristics_t *adc_chars; +static const adc_channel_t channel = ADC_CHANNEL_6; //GPIO34 if ADC1, GPIO14 if ADC2 +static const adc_atten_t atten = ADC_ATTEN_DB_0; +static const adc_unit_t unit = ADC_UNIT_1; -void app_main(void) +static void check_efuse() { - //Check if Two Point or Vref are burned into eFuse - esp_err_t efuse_vref_support = esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF); - esp_err_t efuse_tp_support = esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP); - PRINT_VAL_SUPPORT(efuse_vref_support, "eFuse Vref"); - PRINT_VAL_SUPPORT(efuse_tp_support, "eFuse Two Point"); - - - //Configure ADC1 - adc1_config_width(ADC_WIDTH_BIT_12); - adc1_config_channel_atten(channel, ADC_ATTEN_DB_0); - - //Characterize ADC1 in both linear and lut mode - adc_linear_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t)); - adc_lut_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t)); - - esp_adc_cal_value_t lin_val_type = esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_0, ESP_ADC_CAL_MODE_LIN, DEFAULT_VREF, adc_linear_chars); - esp_adc_cal_value_t lut_val_type = esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_0, ESP_ADC_CAL_MODE_LUT, DEFAULT_VREF, adc_lut_chars); - PRINT_VAL_TYPE(lin_val_type, "Linear"); - PRINT_VAL_TYPE(lut_val_type, "LUT"); - - //Continuously sample ADC1 - while(1){ - uint32_t adc1_raw = 0; - //Multisample - for(int i = 0; i < NO_OF_SAMPLES; i++){ - adc1_raw += adc1_get_raw(channel); - } - adc1_raw /= NO_OF_SAMPLES; - - uint32_t corrected_linear = esp_adc_cal_raw_to_voltage(adc1_raw, ADC_WIDTH_BIT_12, adc_linear_chars); - uint32_t corrected_lut = esp_adc_cal_raw_to_voltage(adc1_raw, ADC_WIDTH_BIT_12, adc_lut_chars); - printf("Raw: %d\tLinear: %dmV\tLUT: %dmV\n", adc1_raw, corrected_linear, corrected_lut); - - vTaskDelay(pdMS_TO_TICKS(1000)); + //Check TP is burned into eFuse + if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) { + printf("eFuse Two Point: Supported\n"); + } else { + printf("eFuse Two Point: NOT supported\n"); } + //Check Vref is burned into eFuse + if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK) { + printf("eFuse Vref: Supported\n"); + } else { + printf("eFuse Vref: NOT supported\n"); + } } + +static void print_char_val_type(esp_adc_cal_value_t val_type) +{ + if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { + printf("Characterized using Two Point Value\n"); + } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { + printf("Characterized using eFuse Vref\n"); + } else { + printf("Characterized using Default Vref\n"); + } +} + +void app_main() +{ + //Check if Two Point or Vref are burned into eFuse + check_efuse(); + + //Configure ADC + if (unit == ADC_UNIT_1) { + adc1_config_width(ADC_WIDTH_BIT_12); + adc1_config_channel_atten(channel, atten); + } else { + adc2_config_channel_atten((adc2_channel_t)channel, atten); + } + + //Characterize ADC + adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t)); + esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_chars); + print_char_val_type(val_type); + + //Continuously sample ADC1 + while (1) { + uint32_t adc_reading = 0; + //Multisampling + for (int i = 0; i < NO_OF_SAMPLES; i++) { + if (unit == ADC_UNIT_1) { + adc_reading += adc1_get_raw((adc1_channel_t)channel); + } else { + int raw; + adc2_get_raw((adc2_channel_t)channel, ADC_WIDTH_BIT_12, &raw); + adc_reading += raw; + } + } + adc_reading /= NO_OF_SAMPLES; + //Convert adc_reading to voltage in mV + uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, adc_chars); + printf("Raw: %d\tVoltage: %dmV\n", adc_reading, voltage); + vTaskDelay(pdMS_TO_TICKS(1000)); + } +} + + diff --git a/examples/peripherals/i2s_adc_dac/main/app_main.c b/examples/peripherals/i2s_adc_dac/main/app_main.c index 032edd3b4..44f80d23d 100755 --- a/examples/peripherals/i2s_adc_dac/main/app_main.c +++ b/examples/peripherals/i2s_adc_dac/main/app_main.c @@ -269,9 +269,10 @@ void adc_read_task(void* arg) adc1_config_width(ADC_WIDTH_12Bit); adc1_config_channel_atten(ADC1_TEST_CHANNEL, ADC_ATTEN_11db); esp_adc_cal_characteristics_t characteristics; - esp_adc_cal_get_characteristics(V_REF, ADC_ATTEN_11db, ADC_WIDTH_12Bit, &characteristics); + esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, V_REF, &characteristics); while(1) { - uint32_t voltage = adc1_to_voltage(ADC1_TEST_CHANNEL, &characteristics); + uint32_t voltage; + esp_adc_cal_get_voltage(ADC1_TEST_CHANNEL, &characteristics, &voltage); ESP_LOGI(TAG, "%d mV", voltage); vTaskDelay(200 / portTICK_RATE_MS); }