mirror of
https://github.com/espressif/esp-idf.git
synced 2026-05-19 23:45:28 +02:00
esp_adc: new esp_adc component and adc drivers
This commit is contained in:
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_types.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "assert.h"
|
||||
#include "hal/efuse_ll.h"
|
||||
#include "hal/adc_types.h"
|
||||
#include "driver/adc_types_legacy.h"
|
||||
#include "esp_adc_cal_types_legacy.h"
|
||||
|
||||
/* ----------------------------- Configuration ------------------------------ */
|
||||
#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_ENABLE
|
||||
#define EFUSE_VREF_ENABLED 1
|
||||
#else
|
||||
#define EFUSE_VREF_ENABLED 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ADC_CAL_LUT_ENABLE
|
||||
#define LUT_ENABLED 1
|
||||
#else
|
||||
#define LUT_ENABLED 0
|
||||
#endif
|
||||
|
||||
/* ESP32s with both Two Point Values and Vref burned into eFuse are required to
|
||||
* also also burn the EFUSE_BLK3_PART_RESERVE flag. A limited set of ESP32s
|
||||
* (not available through regular sales channel) DO NOT have the
|
||||
* EFUSE_BLK3_PART_RESERVE burned. Moreover, this set of ESP32s represents Vref
|
||||
* in Two's Complement format. If this is the case, modify the preprocessor
|
||||
* definitions below as follows...
|
||||
* #define CHECK_BLK3_FLAG 0 //Do not check BLK3 flag as it is not burned
|
||||
* #define VREF_FORMAT 1 //eFuse Vref is in Two's Complement format
|
||||
*/
|
||||
#define CHECK_BLK3_FLAG 1
|
||||
#define VREF_FORMAT 0
|
||||
|
||||
/* ------------------------------ eFuse Access ----------------------------- */
|
||||
#define VREF_MASK 0x1F
|
||||
#define VREF_STEP_SIZE 7
|
||||
#define VREF_OFFSET 1100
|
||||
|
||||
#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_HIGH_MASK 0x1FF
|
||||
#define TP_HIGH_VOLTAGE 850
|
||||
#define TP_STEP_SIZE 4
|
||||
|
||||
/* ----------------------- Raw to Voltage Constants ------------------------- */
|
||||
#define LIN_COEFF_A_SCALE 65536
|
||||
#define LIN_COEFF_A_ROUND (LIN_COEFF_A_SCALE/2)
|
||||
|
||||
#define LUT_VREF_LOW 1000
|
||||
#define LUT_VREF_HIGH 1200
|
||||
#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
|
||||
|
||||
/* ------------------------ 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};
|
||||
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};
|
||||
|
||||
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 --------------------------- */
|
||||
static bool check_efuse_vref(void)
|
||||
{
|
||||
//Check if Vref is burned in eFuse
|
||||
return (efuse_ll_get_adc_vref() != 0) ? true : false;
|
||||
}
|
||||
|
||||
static bool check_efuse_tp(void)
|
||||
{
|
||||
//Check if Two Point values are burned in eFuse
|
||||
if (CHECK_BLK3_FLAG && (efuse_ll_get_blk3_part_reserve() == 0)) {
|
||||
return false;
|
||||
}
|
||||
//All TP cal values must be non zero
|
||||
return efuse_ll_get_adc1_tp_low() &&
|
||||
efuse_ll_get_adc2_tp_low() &&
|
||||
efuse_ll_get_adc1_tp_high() &&
|
||||
efuse_ll_get_adc2_tp_high();
|
||||
}
|
||||
|
||||
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(void)
|
||||
{
|
||||
//eFuse stores deviation from ideal reference voltage
|
||||
uint32_t ret = VREF_OFFSET; //Ideal vref
|
||||
uint32_t bits = efuse_ll_get_adc_vref();
|
||||
ret += decode_bits(bits, VREF_MASK, VREF_FORMAT) * VREF_STEP_SIZE;
|
||||
return ret; //ADC Vref in mV
|
||||
}
|
||||
|
||||
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) {
|
||||
ret = TP_LOW1_OFFSET;
|
||||
bits = efuse_ll_get_adc1_tp_low();
|
||||
} else {
|
||||
ret = TP_LOW2_OFFSET;
|
||||
bits = efuse_ll_get_adc2_tp_low();
|
||||
}
|
||||
ret += decode_bits(bits, TP_LOW_MASK, true) * TP_STEP_SIZE;
|
||||
return ret; //Reading of ADC at 150mV
|
||||
}
|
||||
|
||||
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) {
|
||||
ret = TP_HIGH1_OFFSET;
|
||||
bits = efuse_ll_get_adc1_tp_high();
|
||||
} else {
|
||||
ret = TP_HIGH2_OFFSET;
|
||||
bits = efuse_ll_get_adc2_tp_high();
|
||||
}
|
||||
ret += decode_bits(bits, TP_HIGH_MASK, true) * TP_STEP_SIZE;
|
||||
return ret; //Reading of ADC at 850mV
|
||||
}
|
||||
|
||||
/* ----------------------- Characterization Functions ----------------------- */
|
||||
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_tp_atten_scale;
|
||||
atten_offsets = adc1_tp_atten_offset;
|
||||
} else { //Using ADC 2
|
||||
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;
|
||||
//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];
|
||||
}
|
||||
|
||||
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_offsets;
|
||||
|
||||
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_vref_atten_scale;
|
||||
atten_offsets = adc2_vref_atten_offset;
|
||||
}
|
||||
//Characterize ADC-Voltage curve as y = (coeff_a * x) + coeff_b
|
||||
//Where coeff_a = (vref/4096) * atten_scale
|
||||
*coeff_a = (vref * atten_scales[atten]) / (ADC_12_BIT_RES);
|
||||
*coeff_b = atten_offsets[atten];
|
||||
}
|
||||
|
||||
/* ------------------------ Conversion Functions --------------------------- */
|
||||
static uint32_t calculate_voltage_linear(uint32_t adc_reading, uint32_t coeff_a, uint32_t coeff_b)
|
||||
{
|
||||
//Where voltage = coeff_a * adc_reading + coeff_b
|
||||
return (((coeff_a * adc_reading) + LIN_COEFF_A_ROUND) / LIN_COEFF_A_SCALE) + coeff_b;
|
||||
}
|
||||
|
||||
//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_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) + 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 = 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
|
||||
//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) {
|
||||
return (check_efuse_tp()) ? ESP_OK : ESP_ERR_NOT_SUPPORTED;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num,
|
||||
adc_atten_t atten,
|
||||
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 (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 {
|
||||
//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;
|
||||
}
|
||||
|
||||
//Initialized remaining fields
|
||||
chars->adc_num = adc_num;
|
||||
chars->atten = atten;
|
||||
chars->bit_width = bit_width;
|
||||
chars->vref = (EFUSE_VREF_ENABLED && 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, const esp_adc_cal_characteristics_t *chars)
|
||||
{
|
||||
assert(chars != NULL);
|
||||
|
||||
//Scale adc_rading if not 12 bits wide
|
||||
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
|
||||
}
|
||||
|
||||
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 calculate_voltage_linear(adc_reading, chars->coeff_a, chars->coeff_b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "esp_types.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_efuse_rtc_calib.h"
|
||||
#include "hal/adc_ll.h"
|
||||
#include "hal/adc_types.h"
|
||||
#include "driver/adc_types_legacy.h"
|
||||
#include "esp_adc_cal_types_legacy.h"
|
||||
#include "../esp_adc_cal_internal_legacy.h"
|
||||
|
||||
const static char LOG_TAG[] = "ADC_CALI";
|
||||
|
||||
|
||||
/* ------------------------ Characterization Constants ---------------------- */
|
||||
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
uint32_t digi;
|
||||
} adc_calib_data_ver1;
|
||||
|
||||
typedef struct {
|
||||
char version_num;
|
||||
adc_unit_t adc_num;
|
||||
adc_atten_t atten_level;
|
||||
union {
|
||||
adc_calib_data_ver1 ver1;
|
||||
} efuse_data;
|
||||
} 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_t *parsed_data_storage)
|
||||
{
|
||||
assert(version_num == 1);
|
||||
esp_err_t ret;
|
||||
|
||||
parsed_data_storage->version_num = version_num;
|
||||
parsed_data_storage->adc_num = adc_num;
|
||||
parsed_data_storage->atten_level = atten;
|
||||
uint32_t voltage, digi;
|
||||
/**
|
||||
* V1 we don't have calibration data for ADC2, using the efuse data of ADC1.
|
||||
* Here passing the `adc_num` is just for compatibility
|
||||
*/
|
||||
ret = esp_efuse_rtc_calib_get_cal_voltage(version_num, adc_num, atten, &digi, &voltage);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
parsed_data_storage->efuse_data.ver1.voltage = voltage;
|
||||
parsed_data_storage->efuse_data.ver1.digi = digi;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ----------------------- Characterization Functions ----------------------- */
|
||||
/*
|
||||
* 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_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);
|
||||
|
||||
chars->coeff_a = coeff_a_scaling * parsed_data->efuse_data.ver1.voltage / parsed_data->efuse_data.ver1.digi;
|
||||
chars->coeff_b = 0;
|
||||
}
|
||||
|
||||
/* ------------------------- Public API ------------------------------------- */
|
||||
esp_err_t esp_adc_cal_check_efuse(esp_adc_cal_value_t source)
|
||||
{
|
||||
if (source != ESP_ADC_CAL_VAL_EFUSE_TP) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
uint8_t adc_encoding_version = esp_efuse_rtc_calib_get_ver();
|
||||
if (adc_encoding_version != 1) {
|
||||
// current version only accepts encoding ver 1.
|
||||
return ESP_ERR_INVALID_VERSION;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num,
|
||||
adc_atten_t atten,
|
||||
adc_bits_width_t bit_width,
|
||||
uint32_t default_vref,
|
||||
esp_adc_cal_characteristics_t *chars)
|
||||
{
|
||||
esp_err_t ret;
|
||||
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");
|
||||
ESP_RETURN_ON_FALSE(bit_width == ADC_WIDTH_BIT_12, ESP_ADC_CAL_VAL_NOT_SUPPORTED, LOG_TAG, "Invalid bit_width");
|
||||
ESP_RETURN_ON_FALSE(atten < SOC_ADC_ATTEN_NUM, ESP_ADC_CAL_VAL_NOT_SUPPORTED, LOG_TAG, "Invalid attenuation");
|
||||
|
||||
int version_num = esp_efuse_rtc_calib_get_ver();
|
||||
ESP_RETURN_ON_FALSE(version_num == 1, ESP_ADC_CAL_VAL_NOT_SUPPORTED, LOG_TAG, "No calibration efuse burnt");
|
||||
|
||||
memset(chars, 0, sizeof(esp_adc_cal_characteristics_t));
|
||||
|
||||
// make sure adc is calibrated.
|
||||
ret = prepare_calib_data_for(version_num, adc_num, atten, &efuse_parsed_data);
|
||||
if (ret != ESP_OK) {
|
||||
abort();
|
||||
}
|
||||
|
||||
calculate_characterization_coefficients(&efuse_parsed_data, chars);
|
||||
ESP_LOGD(LOG_TAG, "adc%d (atten leven %d) calibration done: A:%d B:%d\n", adc_num, atten, chars->coeff_a, chars->coeff_b);
|
||||
|
||||
// Initialize remaining fields
|
||||
chars->adc_num = adc_num;
|
||||
chars->atten = atten;
|
||||
chars->bit_width = bit_width;
|
||||
|
||||
// in esp32c3 we only use the two point method to calibrate the adc.
|
||||
return ESP_ADC_CAL_VAL_EFUSE_TP;
|
||||
}
|
||||
|
||||
uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_characteristics_t *chars)
|
||||
{
|
||||
assert(chars != NULL);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_types.h"
|
||||
#include "soc/efuse_periph.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "assert.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_efuse_table.h"
|
||||
#include "esp_efuse_rtc_table.h"
|
||||
#include "hal/adc_hal.h"
|
||||
#include "hal/adc_types.h"
|
||||
#include "driver/adc_types_legacy.h"
|
||||
#include "esp_adc_cal_types_legacy.h"
|
||||
|
||||
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
|
||||
static const int coeff_a_scaling = 65536;
|
||||
static const int coeff_b_scaling = 1024;
|
||||
/* -------------------- Characterization Helper Data Types ------------------ */
|
||||
typedef struct {
|
||||
int adc_calib_high;
|
||||
int adc_calib_low;
|
||||
} adc_calib_data_ver1;
|
||||
|
||||
typedef struct {
|
||||
int adc_calib_high; // the reading of adc ...
|
||||
int adc_calib_high_voltage; // ... at this voltage (mV)
|
||||
} adc_calib_data_ver2;
|
||||
|
||||
typedef struct {
|
||||
char version_num;
|
||||
adc_unit_t adc_num;
|
||||
adc_atten_t atten_level;
|
||||
union {
|
||||
adc_calib_data_ver1 ver1;
|
||||
adc_calib_data_ver2 ver2;
|
||||
} efuse_data;
|
||||
} adc_calib_parsed_info;
|
||||
|
||||
static bool prepare_calib_data_for(adc_unit_t adc_num, adc_atten_t atten, adc_calib_parsed_info *parsed_data_storage)
|
||||
{
|
||||
int version_num = esp_efuse_rtc_table_read_calib_version();
|
||||
int tag;
|
||||
parsed_data_storage->version_num = version_num;
|
||||
parsed_data_storage->adc_num = adc_num;
|
||||
parsed_data_storage->atten_level = atten;
|
||||
switch (version_num) {
|
||||
case 1:
|
||||
// note: use the adc_num as in hal, which start from 0.
|
||||
tag = esp_efuse_rtc_table_get_tag(version_num, adc_num, atten, RTCCALIB_V1_PARAM_VLOW);
|
||||
parsed_data_storage->efuse_data.ver1.adc_calib_low = esp_efuse_rtc_table_get_parsed_efuse_value(tag, false);
|
||||
tag = esp_efuse_rtc_table_get_tag(version_num, adc_num, atten, RTCCALIB_V1_PARAM_VHIGH);
|
||||
parsed_data_storage->efuse_data.ver1.adc_calib_high = esp_efuse_rtc_table_get_parsed_efuse_value(tag, false);
|
||||
break;
|
||||
case 2:
|
||||
tag = esp_efuse_rtc_table_get_tag(version_num, adc_num, atten, RTCCALIB_V2_PARAM_VHIGH);
|
||||
parsed_data_storage->efuse_data.ver2.adc_calib_high = esp_efuse_rtc_table_get_parsed_efuse_value(tag, false);
|
||||
switch (parsed_data_storage->atten_level) {
|
||||
case ADC_ATTEN_DB_0:
|
||||
parsed_data_storage->efuse_data.ver2.adc_calib_high_voltage = 600;
|
||||
break;
|
||||
case ADC_ATTEN_DB_2_5:
|
||||
parsed_data_storage->efuse_data.ver2.adc_calib_high_voltage = 800;
|
||||
break;
|
||||
case ADC_ATTEN_DB_6:
|
||||
parsed_data_storage->efuse_data.ver2.adc_calib_high_voltage = 1000;
|
||||
break;
|
||||
case ADC_ATTEN_DB_11:
|
||||
parsed_data_storage->efuse_data.ver2.adc_calib_high_voltage = 2000;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// fall back to case 1 with zeros as params.
|
||||
parsed_data_storage->version_num = 1;
|
||||
tag = esp_efuse_rtc_table_get_tag(version_num, adc_num, atten, RTCCALIB_V1_PARAM_VLOW);
|
||||
parsed_data_storage->efuse_data.ver1.adc_calib_high = esp_efuse_rtc_table_get_parsed_efuse_value(tag, true);
|
||||
tag = esp_efuse_rtc_table_get_tag(version_num, adc_num, atten, RTCCALIB_V1_PARAM_VHIGH);
|
||||
parsed_data_storage->efuse_data.ver1.adc_calib_low = esp_efuse_rtc_table_get_parsed_efuse_value(tag, true);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ----------------------- Characterization Functions ----------------------- */
|
||||
/**
|
||||
* (Used in V1 of calibration scheme)
|
||||
* The Two Point calibration measures the reading at two specific input voltages, and calculates the (assumed linear) relation
|
||||
* between input voltage and ADC response. (Response = A * Vinput + B)
|
||||
* A and B are scaled ints.
|
||||
* @param high The ADC response at the higher voltage of the corresponding attenuation (600mV, 800mV, 1000mV, 2000mV).
|
||||
* @param low The ADC response at the lower voltage of the corresponding attenuation (all 250mV).
|
||||
*
|
||||
*/
|
||||
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)
|
||||
{
|
||||
// once we have recovered the reference high(Dhigh) and low(Dlow) readings, we can calculate a and b from
|
||||
// the measured high and low readings
|
||||
static const uint32_t v_high[] = {600, 800, 1000, 2000};
|
||||
static const uint32_t v_low = 250;
|
||||
*coeff_a = coeff_a_scaling * (v_high[atten] - v_low) / (high - low);
|
||||
*coeff_b = coeff_b_scaling * (v_low * high - v_high[atten] * low) / (high - low);
|
||||
}
|
||||
|
||||
/*
|
||||
* Estimate the (assumed) linear relationship btwn the measured raw value and the voltage
|
||||
* with the previously done measurement when the chip was manufactured.
|
||||
* */
|
||||
static bool calculate_characterization_coefficients(const adc_calib_parsed_info *parsed_data, esp_adc_cal_characteristics_t *chars)
|
||||
{
|
||||
switch (parsed_data->version_num) {
|
||||
case 1:
|
||||
ESP_LOGD(LOG_TAG, "Calib V1, low%dmV, high%dmV\n", parsed_data->efuse_data.ver1.adc_calib_low, parsed_data->efuse_data.ver1.adc_calib_high);
|
||||
|
||||
characterize_using_two_point(parsed_data->adc_num, parsed_data->atten_level,
|
||||
parsed_data->efuse_data.ver1.adc_calib_high, parsed_data->efuse_data.ver1.adc_calib_low,
|
||||
&(chars->coeff_a), &(chars->coeff_b));
|
||||
break;
|
||||
case 2:
|
||||
ESP_LOGD(LOG_TAG, "Calib V2, volt%dmV\n", parsed_data->efuse_data.ver2.adc_calib_high);
|
||||
chars->coeff_a = coeff_a_scaling * parsed_data->efuse_data.ver2.adc_calib_high_voltage /
|
||||
parsed_data->efuse_data.ver2.adc_calib_high;
|
||||
chars->coeff_b = 0;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ------------------------- Public API ------------------------------------- */
|
||||
esp_err_t esp_adc_cal_check_efuse(esp_adc_cal_value_t source)
|
||||
{
|
||||
if (source != ESP_ADC_CAL_VAL_EFUSE_TP) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
uint8_t adc_encoding_version = esp_efuse_rtc_table_read_calib_version();
|
||||
if (adc_encoding_version != 1 && adc_encoding_version != 2) {
|
||||
// current version only accepts encoding ver 1 and ver 2.
|
||||
return ESP_ERR_INVALID_VERSION;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num,
|
||||
adc_atten_t atten,
|
||||
adc_bits_width_t bit_width,
|
||||
uint32_t default_vref,
|
||||
esp_adc_cal_characteristics_t *chars)
|
||||
{
|
||||
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));
|
||||
assert(chars != NULL);
|
||||
assert(bit_width == ADC_WIDTH_BIT_13);
|
||||
|
||||
// make sure adc is calibrated.
|
||||
res = prepare_calib_data_for(adc_num, atten, &efuse_parsed_data);
|
||||
assert(res);
|
||||
res = calculate_characterization_coefficients(&efuse_parsed_data, chars);
|
||||
assert(res);
|
||||
ESP_LOGD(LOG_TAG, "adc%d (atten leven %d) calibration done: A:%d B:%d\n", adc_num, atten, chars->coeff_a, chars->coeff_b);
|
||||
|
||||
// Initialize remaining fields
|
||||
chars->adc_num = adc_num;
|
||||
chars->atten = atten;
|
||||
chars->bit_width = bit_width;
|
||||
|
||||
// these values are not used as the corresponding calibration themes are deprecated.
|
||||
chars->vref = 0;
|
||||
chars->low_curve = NULL;
|
||||
chars->high_curve = NULL;
|
||||
|
||||
// in esp32s2 we only use the two point method to calibrate the adc.
|
||||
return ESP_ADC_CAL_VAL_EFUSE_TP;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "esp_types.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "hal/adc_types.h"
|
||||
#include "esp_efuse_rtc_calib.h"
|
||||
#include "hal/adc_types.h"
|
||||
#include "driver/adc_types_legacy.h"
|
||||
#include "esp_adc_cal_types_legacy.h"
|
||||
#include "../esp_adc_cal_internal_legacy.h"
|
||||
|
||||
const static char LOG_TAG[] = "ADC_CALI";
|
||||
|
||||
/* ------------------------ Characterization Constants ---------------------- */
|
||||
|
||||
//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 = 1000000;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
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 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 ------------------ */
|
||||
typedef struct {
|
||||
uint32_t voltage;
|
||||
uint32_t digi;
|
||||
} adc_calib_data_ver1_t;
|
||||
|
||||
typedef struct {
|
||||
char version_num;
|
||||
adc_unit_t adc_num;
|
||||
adc_atten_t atten_level;
|
||||
union {
|
||||
adc_calib_data_ver1_t ver1;
|
||||
} ref_data;
|
||||
} adc_calib_info_t;
|
||||
|
||||
|
||||
//To get the reference point (Dout, Vin)
|
||||
static esp_err_t get_reference_point(int version_num, adc_unit_t adc_num, adc_atten_t atten, adc_calib_info_t *calib_info)
|
||||
{
|
||||
assert(version_num == 1);
|
||||
esp_err_t ret;
|
||||
|
||||
calib_info->version_num = version_num;
|
||||
calib_info->adc_num = adc_num;
|
||||
calib_info->atten_level = atten;
|
||||
|
||||
uint32_t voltage = 0;
|
||||
uint32_t digi = 0;
|
||||
ret = esp_efuse_rtc_calib_get_cal_voltage(version_num, adc_num, atten, &digi, &voltage);
|
||||
assert(ret == ESP_OK);
|
||||
calib_info->ref_data.ver1.voltage = voltage;
|
||||
calib_info->ref_data.ver1.digi = digi;
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_adc_cal_check_efuse(esp_adc_cal_value_t source)
|
||||
{
|
||||
if (source != ESP_ADC_CAL_VAL_EFUSE_TP_FIT) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
uint8_t adc_encoding_version = esp_efuse_rtc_calib_get_ver();
|
||||
if (adc_encoding_version != 1) {
|
||||
// current version only accepts encoding ver 1.
|
||||
return ESP_ERR_INVALID_VERSION;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get an expected linear relationship btwn Vin and Dout
|
||||
*/
|
||||
static void calculate_characterization_coefficients(const adc_calib_info_t *parsed_data, esp_adc_cal_characteristics_t *chars)
|
||||
{
|
||||
chars->coeff_a = coeff_a_scaling * parsed_data->ref_data.ver1.voltage / parsed_data->ref_data.ver1.digi;
|
||||
chars->coeff_b = 0;
|
||||
ESP_LOGV(LOG_TAG, "Calib V1, Cal Voltage = %d, Digi out = %d, Coef_a = %d\n", parsed_data->ref_data.ver1.voltage, parsed_data->ref_data.ver1.digi, chars->coeff_a);
|
||||
}
|
||||
|
||||
esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num,
|
||||
adc_atten_t atten,
|
||||
adc_bits_width_t bit_width,
|
||||
uint32_t default_vref,
|
||||
esp_adc_cal_characteristics_t *chars)
|
||||
{
|
||||
(void) default_vref;
|
||||
|
||||
// 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");
|
||||
ESP_RETURN_ON_FALSE(atten < SOC_ADC_ATTEN_NUM, ESP_ADC_CAL_VAL_NOT_SUPPORTED, LOG_TAG, "Invalid attenuation");
|
||||
|
||||
int version_num = esp_efuse_rtc_calib_get_ver();
|
||||
ESP_RETURN_ON_FALSE(version_num == 1, ESP_ADC_CAL_VAL_NOT_SUPPORTED, LOG_TAG, "No calibration efuse burnt");
|
||||
|
||||
memset(chars, 0, sizeof(esp_adc_cal_characteristics_t));
|
||||
|
||||
adc_calib_info_t calib_info = {0};
|
||||
// make sure adc is calibrated.
|
||||
get_reference_point(version_num, adc_num, atten, &calib_info);
|
||||
calculate_characterization_coefficients(&calib_info, chars);
|
||||
|
||||
// Initialize remaining fields
|
||||
chars->adc_num = adc_num;
|
||||
chars->atten = atten;
|
||||
chars->bit_width = bit_width;
|
||||
|
||||
return ESP_ADC_CAL_VAL_EFUSE_TP_FIT;
|
||||
}
|
||||
|
||||
uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_characteristics_t *chars)
|
||||
{
|
||||
assert(chars != NULL);
|
||||
|
||||
//ADC reading won't exceed 4096. Otherwise the raw reading result is wrong, the next calculation will overflow.
|
||||
assert(adc_reading < 4096);
|
||||
|
||||
uint32_t voltage = 0;
|
||||
int32_t error = 0;
|
||||
uint64_t v_cali_1 = 0;
|
||||
|
||||
//raw * gradient * 1000000
|
||||
v_cali_1 = adc_reading * chars->coeff_a;
|
||||
//convert to real number
|
||||
v_cali_1 = v_cali_1 / coeff_a_scaling;
|
||||
ESP_LOGV(LOG_TAG, "v_cali_1 is %llu", v_cali_1);
|
||||
|
||||
//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;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp_types.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "hal/adc_types.h"
|
||||
#define CONFIG_ADC_SUPPRESS_DEPRECATE_WARN 1
|
||||
#include "esp_adc_cal.h"
|
||||
#include "esp_adc_cal_internal_legacy.h"
|
||||
#include "driver/adc.h"
|
||||
|
||||
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
|
||||
ESP_RETURN_ON_FALSE(chars != NULL, ESP_ERR_INVALID_ARG, TAG, "No characteristic input");
|
||||
ESP_RETURN_ON_FALSE(voltage != NULL, ESP_ERR_INVALID_ARG, TAG, "No output buffer");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
int adc_reading;
|
||||
if (chars->adc_num == ADC_UNIT_1) {
|
||||
ESP_RETURN_ON_FALSE(channel < SOC_ADC_CHANNEL_NUM(0), ESP_ERR_INVALID_ARG, TAG, "Invalid channel");
|
||||
adc_reading = adc1_get_raw(channel);
|
||||
} else {
|
||||
ESP_RETURN_ON_FALSE(channel < SOC_ADC_CHANNEL_NUM(1), ESP_ERR_INVALID_ARG, TAG, "Invalid channel");
|
||||
ret = adc2_get_raw(channel, chars->bit_width, &adc_reading);
|
||||
}
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
*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
|
||||
|
||||
#endif //#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#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
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
#include "hal/adc_types.h"
|
||||
#include "driver/adc_types_legacy.h"
|
||||
#include "esp_adc_cal_types_legacy.h"
|
||||
|
||||
#if !CONFIG_ADC_SUPPRESS_DEPRECATE_WARN
|
||||
#warning "legacy adc calibration driver is deprecated, please migrate to use esp_adc/adc_cali.h and esp_adc/adc_cali_scheme.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
|
||||
/**
|
||||
* @brief Checks if ADC calibration values are burned into eFuse
|
||||
*
|
||||
* 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)
|
||||
* @note in ESP32S2, only ESP_ADC_CAL_VAL_EFUSE_TP is supported. Some old ESP32S2s do not support this, either.
|
||||
* In which case you have to calibrate it manually, possibly by performing your own two-point calibration on the chip.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: The calibration mode is supported in eFuse
|
||||
* - ESP_ERR_NOT_SUPPORTED: Error, eFuse values are not burned
|
||||
* - ESP_ERR_INVALID_ARG: Error, invalid argument (ESP_ADC_CAL_VAL_DEFAULT_VREF)
|
||||
*/
|
||||
esp_err_t esp_adc_cal_check_efuse(esp_adc_cal_value_t value_type);
|
||||
|
||||
/**
|
||||
* @brief Characterize an ADC at a particular attenuation
|
||||
*
|
||||
* 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
|
||||
* For ESP32, Two Point values and eFuse Vref calibration can be enabled/disabled using menuconfig.
|
||||
* For ESP32s2, only Two Point values calibration and only ADC_WIDTH_BIT_13 is supported. The parameter default_vref is unused.
|
||||
*
|
||||
*
|
||||
* @param[in] adc_num ADC to characterize (ADC_UNIT_1 or ADC_UNIT_2)
|
||||
* @param[in] atten Attenuation to characterize
|
||||
* @param[in] bit_width Bit width configuration of ADC
|
||||
* @param[in] default_vref Default ADC reference voltage in mV (Only in ESP32, used if eFuse values is not available)
|
||||
* @param[out] chars Pointer to empty structure used to store ADC characteristics
|
||||
*
|
||||
* @return
|
||||
* - ESP_ADC_CAL_VAL_EFUSE_VREF: eFuse Vref used for characterization
|
||||
* - ESP_ADC_CAL_VAL_EFUSE_TP: Two Point value used for characterization (only in Linear Mode)
|
||||
* - ESP_ADC_CAL_VAL_DEFAULT_VREF: Default Vref used for characterization
|
||||
*/
|
||||
esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num,
|
||||
adc_atten_t atten,
|
||||
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 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] chars Pointer to initialized structure containing ADC characteristics
|
||||
*
|
||||
* @return Voltage in mV
|
||||
*/
|
||||
uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_characteristics_t *chars);
|
||||
|
||||
/**
|
||||
* @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
|
||||
* 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] chars Pointer to initialized ADC characteristics structure
|
||||
* @param[out] voltage Pointer to store converted voltage
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: ADC read and converted to mV
|
||||
* - ESP_ERR_INVALID_ARG: Error due to invalid arguments
|
||||
* - ESP_ERR_INVALID_STATE: Reading result is invalid. Try to read again.
|
||||
*/
|
||||
esp_err_t esp_adc_cal_get_voltage(adc_channel_t channel, const esp_adc_cal_characteristics_t *chars, uint32_t *voltage);
|
||||
|
||||
#endif //#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "driver/adc_types_legacy.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
|
||||
/**
|
||||
* @brief Type of calibration value used in characterization
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_ADC_CAL_VAL_EFUSE_VREF = 0, /**< Characterization based on reference voltage stored in eFuse*/
|
||||
ESP_ADC_CAL_VAL_EFUSE_TP = 1, /**< Characterization based on Two Point values stored in eFuse*/
|
||||
ESP_ADC_CAL_VAL_DEFAULT_VREF = 2, /**< Characterization based on default reference voltage*/
|
||||
ESP_ADC_CAL_VAL_EFUSE_TP_FIT = 3, /**< Characterization based on Two Point values and fitting curve coefficients stored in eFuse */
|
||||
ESP_ADC_CAL_VAL_MAX,
|
||||
ESP_ADC_CAL_VAL_NOT_SUPPORTED = ESP_ADC_CAL_VAL_MAX,
|
||||
} esp_adc_cal_value_t;
|
||||
|
||||
/**
|
||||
* @brief Structure storing characteristics of an ADC
|
||||
*
|
||||
* @note Call esp_adc_cal_characterize() to initialize the structure
|
||||
*/
|
||||
typedef struct {
|
||||
adc_unit_t adc_num; /**< ADC unit*/
|
||||
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)*/
|
||||
uint8_t version; /**< ADC Calibration */
|
||||
} esp_adc_cal_characteristics_t;
|
||||
#endif //#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user