esp-idf/components/esp_adc_cal/esp_adc_cal.c
Darian Leung 88b05f9391 esp_adc_cal: Added ADC calibration component
Added component containg API that is able to correct raw ADC readings
into a voltage in mV. Also provided a helper function that combines
the process of getting the raw ADC1 reading then converting it to a
voltage in mV. In doing so, the adc1_get_voltage() function of the ADC
driver has been deprecated. Instead there is now adc1_get_raw to obtain
the raw ADC1 reading, and adc1_to_voltage() that gets the raw reading
and converts all in one function. Functions using the deprecated
adc1_get_voltage() have also been updated to use adc1_get_raw().

Conversion is based on ADC characteristics. The characteristics are based
on the ADC's v_ref, herefore the appropriate structure and functions have
been provided to obtain the ADC characteristics.

The existing ADC driver has also been modified by adding a function to
route the internal ADC reference voltage to a GPIO allowing users to measure
it manually.

Relevant documentation has also been updated
2017-08-29 18:43:14 +08:00

112 lines
4.9 KiB
C

// 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 <stdint.h>
#include "driver/adc.h"
#include "esp_adc_cal.h"
static const esp_adc_cal_lookup_table_t *table_ptrs[4] = {&esp_adc_cal_table_atten_0,
&esp_adc_cal_table_atten_1,
&esp_adc_cal_table_atten_2,
&esp_adc_cal_table_atten_3};
uint32_t get_adc_vref_from_efuse()
{
//TODO: Replaced with read to eFuse once ATE confirms location of 5 bits
return 0;
}
void esp_adc_cal_get_characteristics(uint32_t v_ref,
adc_atten_t atten,
adc_bits_width_t bit_width,
esp_adc_cal_characteristics_t *chars)
{
chars->v_ref = v_ref;
chars->table = table_ptrs[atten];
chars->bit_width = bit_width;
if (v_ref >= ADC_CAL_LOW_V_REF) {
chars->gain = ((chars->v_ref - ADC_CAL_LOW_V_REF)
* chars->table->gain_m)
+ chars->table->gain_c;
chars->offset = (((chars->v_ref - ADC_CAL_LOW_V_REF)
* chars->table->offset_m)
+ chars->table->offset_c
+ ((1 << ADC_CAL_OFFSET_SCALE) / 2))
>> ADC_CAL_OFFSET_SCALE; //Bit shift to cancel 2^10 multiplier
chars->ideal_offset = (((ADC_CAL_IDEAL_V_REF - ADC_CAL_LOW_V_REF)
* chars->table->offset_m)
+ chars->table->offset_c
+ ((1 << ADC_CAL_OFFSET_SCALE) / 2)) //Rounding
>> ADC_CAL_OFFSET_SCALE;
} else { //For case where v_ref is smaller than low bound resulting in negative
chars->gain = chars->table->gain_c
- ((ADC_CAL_LOW_V_REF - chars->v_ref)
* chars->table->gain_m);
chars->offset = (chars->table->offset_c
- ((chars->v_ref - ADC_CAL_LOW_V_REF)
* chars->table->offset_m)
+ ((1 << ADC_CAL_OFFSET_SCALE) / 2)) //Rounding
>> ADC_CAL_OFFSET_SCALE; //Bit shift to cancel 2^10 multiplier
chars->ideal_offset = (chars->table->offset_c
- ((ADC_CAL_IDEAL_V_REF - ADC_CAL_LOW_V_REF)
* chars->table->offset_m)
+ ((1 << ADC_CAL_OFFSET_SCALE) / 2)) //Rounding
>> ADC_CAL_OFFSET_SCALE;
}
}
static uint32_t esp_adc_cal_interpolate_round(uint32_t lower, uint32_t upper,
uint32_t step, uint32_t point)
{
//Interpolate 'point' between 'lower' and 'upper' seperated by 'step'
return ((lower * step) - (lower * point) + (upper * point) + (step / 2)) / step;
}
uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc,
const esp_adc_cal_characteristics_t *chars)
{
//Scale ADC to 12 bit width (0 to 4095)
adc <<= (ADC_WIDTH_12Bit - chars->bit_width);
uint32_t i = (adc >> chars->table->bit_shift); //find index for lut voltages
//Refernce LUT to obtain voltage using index
uint32_t voltage = esp_adc_cal_interpolate_round(chars->table->voltage[i],
chars->table->voltage[i + 1],
(1 << chars->table->bit_shift),
adc - (i << chars->table->bit_shift));
/*
* Apply Gain, scaling(bit shift) and offset to interpolated voltage
* v_true = (((v_id - off_id)*gain)*scaling) + off_true
*/
if (voltage > chars->ideal_offset) {
voltage = (voltage - chars->ideal_offset) * chars->gain;
voltage += (1 << ADC_CAL_GAIN_SCALE) / 2; //For rounding when scaled
voltage >>= ADC_CAL_GAIN_SCALE;
voltage += chars->offset;
} else { //For case where voltage is less than ideal offset leading to negative value
voltage = ((chars->ideal_offset - voltage) * chars->gain);
voltage += (1 << ADC_CAL_GAIN_SCALE) / 2; //For rounding when scaled
voltage >>= ADC_CAL_GAIN_SCALE;
voltage = chars->offset - voltage;
}
return voltage;
}
uint32_t adc1_to_voltage(adc1_channel_t channel, const esp_adc_cal_characteristics_t *chars)
{
return esp_adc_cal_raw_to_voltage((uint32_t)adc1_get_raw(channel), chars);
}