Merge branch 'feature/esp32s2_adc_calib' into 'master'

adc: add driver for esp32s2 adc calibration scheme

Closes IDFGH-3500, IDF-1458, and IDF-2013

See merge request espressif/esp-idf!9557
This commit is contained in:
Michael (XIAO Xufeng) 2020-12-03 20:09:07 +08:00
commit a92d90435f
12 changed files with 557 additions and 264 deletions

View File

@ -546,7 +546,9 @@ esp_err_t adc2_vref_to_gpio(gpio_num_t gpio)
esp_err_t adc_vref_to_gpio(adc_unit_t adc_unit, gpio_num_t gpio)
{
#ifdef CONFIG_IDF_TARGET_ESP32
if (adc_unit & ADC_UNIT_1) return ESP_ERR_INVALID_ARG;
if (adc_unit & ADC_UNIT_1) {
return ESP_ERR_INVALID_ARG;
}
#endif
adc2_channel_t ch = ADC2_CHANNEL_MAX;
/* Check if the GPIO supported. */
@ -556,7 +558,9 @@ esp_err_t adc_vref_to_gpio(adc_unit_t adc_unit, gpio_num_t gpio)
break;
}
}
if (ch == ADC2_CHANNEL_MAX) return ESP_ERR_INVALID_ARG;
if (ch == ADC2_CHANNEL_MAX) {
return ESP_ERR_INVALID_ARG;
}
ADC_ENTER_CRITICAL();
adc_hal_set_power_manage(ADC_POWER_SW_ON);

View File

@ -25,6 +25,8 @@
#include "soc/sens_struct.h"
#include "driver/temp_sensor.h"
#include "regi2c_ctrl.h"
#include "esp_log.h"
#include "esp32s2/esp_efuse_rtc_table.h"
static const char *TAG = "tsens";
@ -59,6 +61,8 @@ static const tsens_dac_offset_t dac_offset[TSENS_DAC_MAX] = {
static SemaphoreHandle_t rtc_tsens_mux = NULL;
static float deltaT = 1000; // greater than range
esp_err_t temp_sensor_set_config(temp_sensor_config_t tsens)
{
CLEAR_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_SAR_I2C_FORCE_PD_M);
@ -133,6 +137,28 @@ esp_err_t temp_sensor_read_raw(uint32_t *tsens_out)
return ESP_OK;
}
static void read_delta_t_from_efuse(void)
{
uint32_t version = esp_efuse_rtc_table_read_calib_version();
if (version == 1 || version == 2) {
// fetch calibration value for temp sensor from eFuse
deltaT = esp_efuse_rtc_table_get_parsed_efuse_value(RTCCALIB_IDX_TMPSENSOR, false) / 10.0;
} else {
// no value to fetch, use 0.
deltaT = 0;
}
ESP_LOGD(TAG, "deltaT = %f\n", deltaT);
}
static float parse_temp_sensor_raw_value(uint32_t tsens_raw, const tsens_dac_offset_t *dac)
{
if (deltaT > 512) { //suggests that the value is not initialized
read_delta_t_from_efuse();
}
float result = (TSENS_ADC_FACTOR * (float)tsens_raw - TSENS_DAC_FACTOR * dac->offset - TSENS_SYS_OFFSET) - deltaT;
return result;
}
esp_err_t temp_sensor_read_celsius(float *celsius)
{
TSENS_CHECK(celsius != NULL, ESP_ERR_INVALID_ARG);
@ -143,7 +169,7 @@ esp_err_t temp_sensor_read_celsius(float *celsius)
ret = temp_sensor_read_raw(&tsens_out);
TSENS_CHECK(ret == ESP_OK, ret);
const tsens_dac_offset_t *dac = &dac_offset[tsens.dac_offset];
*celsius = (TSENS_ADC_FACTOR * (float)tsens_out - TSENS_DAC_FACTOR * dac->offset - TSENS_SYS_OFFSET);
*celsius = parse_temp_sensor_raw_value(tsens_out, dac);
if (*celsius < dac->range_min || *celsius > dac->range_max) {
ESP_LOGW(TAG, "Exceeding the temperature range!");
ret = ESP_ERR_INVALID_STATE;

View File

@ -8,6 +8,9 @@ if(EXISTS "${COMPONENT_DIR}/${target}")
list(APPEND srcs "src/${target}/esp_efuse_api.c"
"src/${target}/esp_efuse_fields.c"
"src/${target}/esp_efuse_utility.c")
if("esp32s2" STREQUAL "${target}")
list(APPEND srcs "src/${target}/esp_efuse_rtc_table.c")
endif()
endif()
list(APPEND srcs "src/esp_efuse_api.c"

View File

@ -0,0 +1,108 @@
// Copyright 2017-2018 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include "esp_err.h"
#include "sdkconfig.h"
#define RTCCALIB_ESP32S2_ADCCOUNT 2
#define RTCCALIB_ESP32S2_ATTENCOUNT 4
#define RTCCALIB_V1_PARAM_VLOW 0
#define RTCCALIB_V1_PARAM_VHIGH 1
#define RTCCALIB_V2_PARAM_VHIGH 0
#define RTCCALIB_V2_PARAM_VINIT 1
// these are the tags. Either use them directly or use esp_efuse_rtc_table_get_tag to calculate
// the corresponding tag.
#define RTCCALIB_V1IDX_A10L 1
#define RTCCALIB_V1IDX_A11L 2
#define RTCCALIB_V1IDX_A12L 3
#define RTCCALIB_V1IDX_A13L 4
#define RTCCALIB_V1IDX_A20L 5
#define RTCCALIB_V1IDX_A21L 6
#define RTCCALIB_V1IDX_A22L 7
#define RTCCALIB_V1IDX_A23L 8
#define RTCCALIB_V1IDX_A10H 9
#define RTCCALIB_V1IDX_A11H 10
#define RTCCALIB_V1IDX_A12H 11
#define RTCCALIB_V1IDX_A13H 12
#define RTCCALIB_V1IDX_A20H 13
#define RTCCALIB_V1IDX_A21H 14
#define RTCCALIB_V1IDX_A22H 15
#define RTCCALIB_V1IDX_A23H 16
#define RTCCALIB_V2IDX_A10H 17
#define RTCCALIB_V2IDX_A11H 18
#define RTCCALIB_V2IDX_A12H 19
#define RTCCALIB_V2IDX_A13H 20
#define RTCCALIB_V2IDX_A20H 21
#define RTCCALIB_V2IDX_A21H 22
#define RTCCALIB_V2IDX_A22H 23
#define RTCCALIB_V2IDX_A23H 24
#define RTCCALIB_V2IDX_A10I 25
#define RTCCALIB_V2IDX_A11I 26
#define RTCCALIB_V2IDX_A12I 27
#define RTCCALIB_V2IDX_A13I 28
#define RTCCALIB_V2IDX_A20I 29
#define RTCCALIB_V2IDX_A21I 30
#define RTCCALIB_V2IDX_A22I 31
#define RTCCALIB_V2IDX_A23I 32
#define RTCCALIB_IDX_TMPSENSOR 33
/**
* @brief Get rtc calibration version.
*/
int esp_efuse_rtc_table_read_calib_version(void);
/**
* @brief Helper function to calculate a tag from human-readable parameters.
* Tag is used to index the desired data from the efuse.
* For example, (1, 1, 3, 1) yields the tag RTCCALIB_V1IDX_A13H
* extra params are used for identification when a adc_num-atten combination has
* multiple efuse values.
* @param adc_channel_num verbatim numbering of the ADC channel. For channel 1, use 1 and not 0.
* @param atten attenuation. use the enum value.
* @param version the version of the scheme to index for.
* @param extra_params defined differently for each version.
* */
int esp_efuse_rtc_table_get_tag(int version, int adc_channel_num, int atten, int extra_params);
/**
* @brief Fetches a raw value from efuse and does signed bit parsing
* @param tag tag obtained with esp_efuse_rtc_table_get_tag
*
* */
int esp_efuse_rtc_table_get_raw_efuse_value(int tag);
/**
* @brief Fetches a raw value from efuse and resolve it to get
* the original number that it meant to represent.
*
* @param tag tag obtained with esp_efuse_rtc_table_get_tag
* @param use_zero_inputs Does not perform the raw value fetching before resolving the number,
* but proceed as if all zeros were read from efuse.
*
* */
int esp_efuse_rtc_table_get_parsed_efuse_value(int tag, bool skip_efuse_reading);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,167 @@
// Copyright 2017-2018 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 <stdbool.h>
#include "esp32s2/esp_efuse_rtc_table.h"
#include "esp_efuse.h"
#include "esp_efuse_table.h"
#include "esp_log.h"
#include "soc/soc_caps.h"
#define RTC_TBL_LOG_TAG "efuse_rtc_table"
/* Note on definition of tags
*
* For adc calibration, value = raw * multiplier + offset, but these values are kind of arbitrary so
* we use a lookup table to do the bookkeeping.
*
* The offset of an item can be calculated as follows:
* PARAM_OFFSET + ADC_NUM(which is the UNIT_COUNT minus 1) * ATTEN_NUM + ATTEN_NUM
* where PARAM_OFFSET is the index of the first item.
*
* ADC, ATTEN form a 2-dim array. For each (version number, extra parameters) tuple we keep a such array,
* and use if-else statements to choose which array we use.
* */
#define RTCCALIB_V1_ADCREADINGLOW_OFFSET RTCCALIB_V1IDX_A10L
#define RTCCALIB_V1_ADCREADINGHIGH_OFFSET RTCCALIB_V1IDX_A10H
#define RTCCALIB_V2_ADCREADINGHIGH_OFFSET RTCCALIB_V2IDX_A10H
#define RTCCALIB_V2_ADCREADINGINIT_OFFSET RTCCALIB_V2IDX_A10I
typedef struct {
const int tag; // should be the same as the index in adc_efuse_raw_map
const int block;
const int begin_bit;
const int length;
const int multiplier;
const int base;
const int depends;
} efuse_map_info_t;
static const efuse_map_info_t adc_efuse_raw_map[] = {
{0},
// INDEXING TAG, BLOCK, BEGIN_BIT, LENGTH, MULTIPLIER, OFFSET BASE, OFFSET DEP
{RTCCALIB_V1IDX_A10L, 2, 208, 6, 4, 2231, 0},
{RTCCALIB_V1IDX_A11L, 2, 214, 6, 4, 1643, 0},
{RTCCALIB_V1IDX_A12L, 2, 220, 6, 4, 1290, 0},
{RTCCALIB_V1IDX_A13L, 2, 226, 6, 4, 701, 0},
{RTCCALIB_V1IDX_A20L, 2, 232, 6, 4, 2305, 0},
{RTCCALIB_V1IDX_A21L, 2, 238, 6, 4, 1693, 0},
{RTCCALIB_V1IDX_A22L, 2, 244, 6, 4, 1343, 0},
{RTCCALIB_V1IDX_A23L, 2, 250, 6, 4, 723, 0},
{RTCCALIB_V1IDX_A10H, 2, 144, 8, 4, 5775, 0},
{RTCCALIB_V1IDX_A11H, 2, 152, 8, 4, 5693, 0},
{RTCCALIB_V1IDX_A12H, 2, 160, 8, 4, 5723, 0},
{RTCCALIB_V1IDX_A13H, 2, 168, 8, 4, 6209, 0},
{RTCCALIB_V1IDX_A20H, 2, 176, 8, 4, 5817, 0},
{RTCCALIB_V1IDX_A21H, 2, 184, 8, 4, 5703, 0},
{RTCCALIB_V1IDX_A22H, 2, 192, 8, 4, 5731, 0},
{RTCCALIB_V1IDX_A23H, 2, 200, 8, 4, 6157, 0},
{RTCCALIB_V2IDX_A10H, 2, 197, 6, 2, 169, RTCCALIB_V2IDX_A12H},
{RTCCALIB_V2IDX_A11H, 2, 203, 6, 2, -26, RTCCALIB_V2IDX_A12H},
{RTCCALIB_V2IDX_A12H, 2, 209, 9, 2, 126, RTCCALIB_V2IDX_A21H},
{RTCCALIB_V2IDX_A13H, 2, 218, 7, 2, 387, RTCCALIB_V2IDX_A12H},
{RTCCALIB_V2IDX_A20H, 2, 225, 7, 2, 177, RTCCALIB_V2IDX_A21H},
{RTCCALIB_V2IDX_A21H, 2, 232, 10, 2, 5815, 0},
{RTCCALIB_V2IDX_A22H, 2, 242, 7, 2, 27, RTCCALIB_V2IDX_A21H},
{RTCCALIB_V2IDX_A23H, 2, 249, 7, 2, 410, RTCCALIB_V2IDX_A21H},
{RTCCALIB_V2IDX_A10I, 2, 147, 8, 2, 1519, 0},
{RTCCALIB_V2IDX_A11I, 2, 155, 6, 2, 88, RTCCALIB_V2IDX_A10I},
{RTCCALIB_V2IDX_A12I, 2, 161, 5, 2, 8, RTCCALIB_V2IDX_A11I},
{RTCCALIB_V2IDX_A13I, 2, 166, 6, 2, 70, RTCCALIB_V2IDX_A12I},
{RTCCALIB_V2IDX_A20I, 2, 172, 8, 2, 1677, 0},
{RTCCALIB_V2IDX_A21I, 2, 180, 6, 2, 23, RTCCALIB_V2IDX_A20I},
{RTCCALIB_V2IDX_A22I, 2, 186, 5, 2, 6, RTCCALIB_V2IDX_A21I},
{RTCCALIB_V2IDX_A23I, 2, 191, 6, 2, 13, RTCCALIB_V2IDX_A22I},
{RTCCALIB_IDX_TMPSENSOR, 2, 135, 9, 1, 0, 0},
};
int esp_efuse_rtc_table_read_calib_version(void)
{
uint32_t result = 0;
esp_efuse_read_field_blob(ESP_EFUSE_BLOCK2_VERSION, &result, 32);
return result;
}
int esp_efuse_rtc_table_get_tag(int version, int adc_num, int atten, int extra_params)
{
int param_offset; // used to index which (adc_num, atten) array to use.
if (version == 1 && extra_params == RTCCALIB_V1_PARAM_VLOW) { // Volage LOW, Version 1
param_offset = RTCCALIB_V1_ADCREADINGLOW_OFFSET;
} else if (version == 1 && extra_params == RTCCALIB_V1_PARAM_VHIGH) {
param_offset = RTCCALIB_V1_ADCREADINGHIGH_OFFSET;
} else if (version == 2 && extra_params == RTCCALIB_V2_PARAM_VHIGH) {
param_offset = RTCCALIB_V2_ADCREADINGHIGH_OFFSET;
} else if (version == 2 && extra_params == RTCCALIB_V2_PARAM_VINIT) {
param_offset = RTCCALIB_V2_ADCREADINGINIT_OFFSET;
} else {
return -1;
}
int result = param_offset + (adc_num - 1) * RTCCALIB_ESP32S2_ATTENCOUNT + atten;
ESP_EARLY_LOGV(RTC_TBL_LOG_TAG, "V%d ADC%d ATTEN%d PARAM%d -> %d", version, adc_num, atten, extra_params, result);
return result;
}
/*
* Converts a signed-bit int to a normal (2-complement) int.
* */
static int signed_bit_to_int(uint32_t number, int len)
{
if (number >> (len - 1)) {
// first bit is set, unset that bit and negate the number.
number = -(number ^ (1 << (len - 1)));
}
return number;
}
int esp_efuse_rtc_table_get_raw_efuse_value(int tag)
{
assert(tag > 0);
if (tag == 0) {
return 0;
}
uint32_t val = 0;
esp_efuse_read_block(adc_efuse_raw_map[tag].block, &val, adc_efuse_raw_map[tag].begin_bit, adc_efuse_raw_map[tag].length);
int result = signed_bit_to_int(val, adc_efuse_raw_map[tag].length);
ESP_EARLY_LOGV(RTC_TBL_LOG_TAG, "Fetching raw for tag %d @blk%d bit%d len%d: %d", tag, adc_efuse_raw_map[tag].block, adc_efuse_raw_map[tag].begin_bit, adc_efuse_raw_map[tag].length,
result);
return result;
}
int esp_efuse_rtc_table_get_parsed_efuse_value(int tag, bool skip_efuse_reading)
{
assert(tag >= 0);
if (tag == 0) {
return 0; // tag 0 is the dummy tag and has no value. (used by depends)
}
int efuse_val = 0;
if (!skip_efuse_reading) {
efuse_val = esp_efuse_rtc_table_get_raw_efuse_value(tag) * adc_efuse_raw_map[tag].multiplier;
}
int result = efuse_val + adc_efuse_raw_map[tag].base +
esp_efuse_rtc_table_get_parsed_efuse_value(adc_efuse_raw_map[tag].depends, skip_efuse_reading);
ESP_EARLY_LOGV(RTC_TBL_LOG_TAG, "Parsed efuse val for tag %d: %d", tag, result);
return result;
}

View File

@ -68,7 +68,7 @@ esp_err_t esp_efuse_utility_process(const esp_efuse_desc_t* field[], void* ptr,
if ((bits_counter + num_bits) > req_size) { // Limits the length of the field.
num_bits = req_size - bits_counter;
}
ESP_LOGD(TAG, "In EFUSE_BLK%d__DATA%d_REG is used %d bits starting with %d bit",
ESP_EARLY_LOGD(TAG, "In EFUSE_BLK%d__DATA%d_REG is used %d bits starting with %d bit",
(int)field[i]->efuse_block, num_reg, num_bits, start_bit);
err = func_proc(num_reg, field[i]->efuse_block, start_bit, num_bits, ptr, &bits_counter);
++i_reg;

View File

@ -20,166 +20,94 @@
#include "assert.h"
#include "esp_adc_cal.h"
#include "esp_efuse.h"
#include "esp_efuse_table.h"
#include "esp32s2/esp_efuse_rtc_table.h"
#include "hal/adc_hal.h"
#define ADC_CAL_CHECK(cond, ret) ({ \
if(!(cond)){ \
return ret; \
} \
})
const static char LOG_TAG[] = "adc_calib";
/* ------------------------ Characterization Constants ---------------------- */
#define ADC_CHAR_VERSION1_EFUSEVAL 1
static const uint32_t adc1_D_mean_low[] = {2231, 1643, 1290, 701};
static const uint32_t adc2_D_mean_low[] = {2305, 1693, 1343, 723};
static const uint32_t adc1_D_mean_high[] = {5775, 5692, 5725, 6209};
static const uint32_t adc2_D_mean_high[] = {5817, 5703, 5731, 6157};
static const int Dlow_data_length = 6;
static const int Dhigh_data_length = 8;
static const int adc_efuse_block = 2;
static const int adc_calib_ver_block = 2;
static const int adc_calib_ver_word_loc = 4;
static const int adc_calib_ver_offset = 4;
static const int adc_calib_ver_len = 3;
static const int adc1_atten0_Dlow_word_loc = 6;
static const int adc2_atten0_Dlow_word_loc = 7;
static const int adc1_atten0_Dhigh_word_loc = 4;
static const int adc2_atten0_Dhigh_word_loc = 5;
static const int adc1_atten0_Dlow_offset = 16;
static const int adc2_atten0_Dlow_offset = 8;
static const int adc1_atten0_Dhigh_offset = 16;
static const int adc2_atten0_Dhigh_offset = 16;
/* ----------------------- EFuse Access Functions --------------------------- */
/**
* Convenience function that reads a few bits from efuse and assembles them.
* For example, if the contents of the EFuse are:
* Word2: 0x1234 Word3:0x5678
* Then, setting base=2, offset=24, len=24 will yield 0x456.
* @note does not check for boundaries, make sure parameters are correct
* @param blk EFuse Block
* @param base the starting word
* @param offset the bit offset in the starting word
* @param bit how many consecutive bits to fetch
* @return the assembled number
*/
static uint32_t get_consecutive_bits_from_blk(int blk, uint32_t base, int offset, int len)
{
base += offset / 32;
offset %= 32;
if (offset + len <= 32 || base == 7) {
uint32_t result = esp_efuse_read_reg(blk, base);
result <<= (32 - offset - len);
result >>= (32 - len);
return result;
} else {
// need to fetch both bytes.
uint64_t result = ((uint64_t)esp_efuse_read_reg(blk, base + 1) << 32) + esp_efuse_read_reg(blk, base);
result &= ((uint64_t)1 << (offset + len)) - 1;
result >>= offset;
return result;
}
}
/**
* To save space in EFuse, the calibration values for adc are compressed.
* The compression scheme is: for X bits of ADC Efuse data,
* The actual ADC reading is: BASE_VALUE + 4*ADC_OFFSET
* where ADC_OFFSET = bits X-1:0 in Efuse, the highest bit is the sign bit (0:+, 1:-).
*
* The following functions do this conversion.
* @param efuse_val raw values read from efuse.
* @param adc_num Specifies the channel number. The 2 adc channels each have different calibration values.
* @param attem Specifies the attenuation. Different attenuation level have different calibration values.
*/
static uint32_t efuse_low_val_to_d(uint16_t efuse_val, adc_unit_t adc_num, adc_atten_t atten)
{
// efuse_val is 5 bits + 6th sign bit.
int32_t rawoffsetval = efuse_val & ((1 << (Dlow_data_length - 1)) - 1);
// if the sign bit is 1, it means it is a negative sign.
int32_t offset = (efuse_val & (1 << (Dlow_data_length - 1))) ? (-rawoffsetval * 4) : (rawoffsetval * 4);
if (adc_num == ADC_UNIT_1) {
return offset + adc1_D_mean_low[atten - ADC_ATTEN_DB_0];
} else {
return offset + adc2_D_mean_low[atten - ADC_ATTEN_DB_0];
}
}
static uint32_t efuse_high_val_to_d (uint16_t efuse_val, adc_unit_t adc_num, adc_atten_t atten)
{
// efuse_val is 7 bits + 8th sign bit.
int32_t rawoffsetval = efuse_val & ((1 << (Dhigh_data_length - 1)) - 1);
int32_t offset = (efuse_val & (1 << (Dhigh_data_length - 1))) ? (-rawoffsetval * 4) : (rawoffsetval * 4);
if (adc_num == ADC_UNIT_1) {
return offset + adc1_D_mean_high[atten - ADC_ATTEN_DB_0];
} else {
return offset + adc2_D_mean_high[atten - ADC_ATTEN_DB_0];
}
}
/**
* To save space in EFuse, the calibration values for adc are compressed.
* The compression scheme is: for X bits of ADC Efuse data,
* The actual ADC reading is: BASE_VALUE + 4*ADC_OFFSET
* where ADC_OFFSET = bits X-1:0 in Efuse, the highest bit is the sign bit (0:+, 1:-).
*
* The following functions do the reading.
* @param efuse_val raw values read from efuse.
* @param adc_num Specifies the channel number. The 2 adc channels each have different calibration values.
* @param attem Specifies the attenuation. Different attenuation level have different calibration values.
*/
static uint32_t read_efuse_tp_low(adc_unit_t adc_num, adc_atten_t atten)
{
// this fcn retrieves and decodes the calibration value stored in efuse.
uint32_t base;
int offset;
// may need to move magic numbers out
if (adc_num == ADC_UNIT_1) {
// the first value is at the 16th bit of the 6th word of the efuse block 2, each value is 6 bits long.
base = adc1_atten0_Dlow_word_loc;
offset = adc1_atten0_Dlow_offset + Dlow_data_length * (atten - ADC_ATTEN_DB_0);
} else {
// the first value is at the 8th bit of the 7th word of the efuse block 2, each value is 6 bits long.
base = adc2_atten0_Dlow_word_loc;
offset = adc2_atten0_Dlow_offset + Dlow_data_length * (atten - ADC_ATTEN_DB_0);
}
uint32_t read_result = get_consecutive_bits_from_blk(adc_efuse_block, base, offset, Dlow_data_length);
return read_result;
}
static uint32_t read_efuse_tp_high(adc_unit_t adc_num, adc_atten_t atten)
{
// this fcn retrieves and decodes the calibration value stored in efuse.
uint32_t base;
int offset;
if (adc_num == ADC_UNIT_1) {
// the first value is at the 16th bit of the 4th word of the efuse block 2, each value is 8 bits long.
base = adc1_atten0_Dhigh_word_loc;
offset = adc1_atten0_Dhigh_offset + Dhigh_data_length * (atten - ADC_ATTEN_DB_0);
} else {
// the first value is at the 16th bit of the 5th word of the efuse block 2, each value is 8 bits long.
base = adc2_atten0_Dhigh_word_loc;
offset = adc2_atten0_Dhigh_offset + Dhigh_data_length * (atten - ADC_ATTEN_DB_0);
}
uint32_t read_result = get_consecutive_bits_from_blk(adc_efuse_block, base, offset, Dhigh_data_length);
return read_result;
}
/* ----------------------- Characterization Functions ----------------------- */
// 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.
@ -202,6 +130,32 @@ static void characterize_using_two_point(adc_unit_t adc_num,
*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)
@ -209,14 +163,9 @@ 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 adc1_atten0_dh = get_consecutive_bits_from_blk(adc_efuse_block, adc1_atten0_Dhigh_word_loc, adc1_atten0_Dhigh_offset, Dhigh_data_length);
uint8_t adc2_atten0_dh = get_consecutive_bits_from_blk(adc_efuse_block, adc2_atten0_Dhigh_word_loc, adc2_atten0_Dhigh_offset, Dhigh_data_length);
if (!adc1_atten0_dh || !adc2_atten0_dh) {
return ESP_ERR_NOT_SUPPORTED;
}
uint8_t adc_encoding_version = get_consecutive_bits_from_blk(adc_calib_ver_block, adc_calib_ver_word_loc, adc_calib_ver_offset, adc_calib_ver_len);
if (adc_encoding_version != 1) {
// current version only accepts encoding ver 1.
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;
@ -228,24 +177,20 @@ esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num,
uint32_t default_vref,
esp_adc_cal_characteristics_t *chars)
{
bool res;
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);
// Characterize based on efuse Two Point values. If these values are not present in efuse,
// or efuse values are of a version that we do not recognize, automatically assume default values.
uint32_t adc_calib_high, adc_calib_low;
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) {
adc_calib_high = read_efuse_tp_high(adc_num, atten);
adc_calib_low = read_efuse_tp_low(adc_num, atten);
} else {
adc_calib_high = 0;
adc_calib_low = 0;
}
uint32_t high = efuse_high_val_to_d(adc_calib_high, adc_num, atten);
uint32_t low = efuse_low_val_to_d(adc_calib_low, adc_num, atten);
characterize_using_two_point(adc_num, atten, high, low, &(chars->coeff_a), &(chars->coeff_b));
// 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;

View File

@ -56,6 +56,8 @@ typedef struct {
* 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

View File

@ -9,6 +9,7 @@ idf_component_register(SRCS "compare_set.c"
"cpu_util.c"
INCLUDE_DIRS include
REQUIRES ${requires}
PRIV_REQUIRES efuse
LDFRAGMENTS linker.lf)
idf_build_get_property(target IDF_TARGET)

View File

@ -37,3 +37,10 @@
#define I2C_ULP_BG_O_DONE_FLAG 3
#define I2C_ULP_BG_O_DONE_FLAG_MSB 3
#define I2C_ULP_BG_O_DONE_FLAG_LSB 3
#define I2C_ULP_OCODE_ADDR 6
#define I2C_ULP_OCODE_ADDR_MSB 7
#define I2C_ULP_OCODE_ADDR_LSB 0
#define I2C_ULP_IR_FORCE_CODE_ADDR 5
#define I2C_ULP_IR_FORCE_CODE_ADDR_MSB 6
#define I2C_ULP_IR_FORCE_CODE_ADDR_LSB 6

View File

@ -21,9 +21,11 @@
#include "soc/gpio_reg.h"
#include "soc/spi_mem_reg.h"
#include "soc/extmem_reg.h"
#include "regi2c_ulp.h"
#include "regi2c_ctrl.h"
#include "soc_log.h"
#include "esp_efuse.h"
#include "esp_efuse_table.h"
static const char *TAG = "rtc_init";
void rtc_init(rtc_config_t cfg)
@ -146,55 +148,73 @@ void rtc_init(rtc_config_t cfg)
CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_FORCE_UNHOLD);
CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_FORCE_NOISO);
}
if (cfg.cali_ocode)
{
/*
Bangap output voltage is not precise when calibrate o-code by hardware sometimes, so need software o-code calibration(must close PLL).
Method:
1. read current cpu config, save in old_config;
2. switch cpu to xtal because PLL will be closed when o-code calibration;
3. begin o-code calibration;
4. wait o-code calibration done flag(odone_flag & bg_odone_flag) or timeout;
5. set cpu to old-config.
*/
rtc_slow_freq_t slow_clk_freq = rtc_clk_slow_freq_get();
rtc_slow_freq_t rtc_slow_freq_x32k = RTC_SLOW_FREQ_32K_XTAL;
rtc_slow_freq_t rtc_slow_freq_8MD256 = RTC_SLOW_FREQ_8MD256;
rtc_cal_sel_t cal_clk = RTC_CAL_RTC_MUX;
if (slow_clk_freq == (rtc_slow_freq_x32k)) {
cal_clk = RTC_CAL_32K_XTAL;
} else if (slow_clk_freq == rtc_slow_freq_8MD256) {
cal_clk = RTC_CAL_8MD256;
}
uint64_t max_delay_time_us = 10000;
uint32_t slow_clk_period = rtc_clk_cal(cal_clk, 100);
uint64_t max_delay_cycle = rtc_time_us_to_slowclk(max_delay_time_us, slow_clk_period);
uint64_t cycle0 = rtc_time_get();
uint64_t timeout_cycle = cycle0 + max_delay_cycle;
uint64_t cycle1 = 0;
rtc_cpu_freq_config_t old_config;
rtc_clk_cpu_freq_get_config(&old_config);
rtc_clk_cpu_freq_set_xtal();
REGI2C_WRITE_MASK(I2C_ULP, I2C_ULP_IR_RESETB, 0);
REGI2C_WRITE_MASK(I2C_ULP, I2C_ULP_IR_RESETB, 1);
bool odone_flag = 0;
bool bg_odone_flag = 0;
while(1) {
odone_flag = REGI2C_READ_MASK(I2C_ULP, I2C_ULP_O_DONE_FLAG);
bg_odone_flag = REGI2C_READ_MASK(I2C_ULP, I2C_ULP_BG_O_DONE_FLAG);
cycle1 = rtc_time_get();
if (odone_flag && bg_odone_flag)
break;
if (cycle1 >= timeout_cycle) {
SOC_LOGW(TAG, "o_code calibration fail");
break;
if (cfg.cali_ocode) {
uint32_t rtc_calib_version = 0;
esp_efuse_read_field_blob(ESP_EFUSE_BLOCK2_VERSION, &rtc_calib_version, 32);
if (rtc_calib_version == 2) {
// use efuse ocode.
uint32_t ocode1 = 0;
uint32_t ocode2 = 0;
uint32_t ocode;
esp_efuse_read_block(2, &ocode1, 16*8, 4);
esp_efuse_read_block(2, &ocode2, 18*8, 3);
ocode = (ocode2 << 4) + ocode1;
if (ocode >> 6) {
ocode = 93 - (ocode ^ (1 << 6));
} else {
ocode = 93 + ocode;
}
REGI2C_WRITE_MASK(I2C_ULP, I2C_ULP_OCODE_ADDR, ocode);
REGI2C_WRITE_MASK(I2C_ULP, I2C_ULP_IR_FORCE_CODE_ADDR, 1);
} else {
/*
Bangap output voltage is not precise when calibrate o-code by hardware sometimes, so need software o-code calibration(must close PLL).
Method:
1. read current cpu config, save in old_config;
2. switch cpu to xtal because PLL will be closed when o-code calibration;
3. begin o-code calibration;
4. wait o-code calibration done flag(odone_flag & bg_odone_flag) or timeout;
5. set cpu to old-config.
*/
rtc_slow_freq_t slow_clk_freq = rtc_clk_slow_freq_get();
rtc_slow_freq_t rtc_slow_freq_x32k = RTC_SLOW_FREQ_32K_XTAL;
rtc_slow_freq_t rtc_slow_freq_8MD256 = RTC_SLOW_FREQ_8MD256;
rtc_cal_sel_t cal_clk = RTC_CAL_RTC_MUX;
if (slow_clk_freq == (rtc_slow_freq_x32k)) {
cal_clk = RTC_CAL_32K_XTAL;
} else if (slow_clk_freq == rtc_slow_freq_8MD256) {
cal_clk = RTC_CAL_8MD256;
}
uint64_t max_delay_time_us = 10000;
uint32_t slow_clk_period = rtc_clk_cal(cal_clk, 100);
uint64_t max_delay_cycle = rtc_time_us_to_slowclk(max_delay_time_us, slow_clk_period);
uint64_t cycle0 = rtc_time_get();
uint64_t timeout_cycle = cycle0 + max_delay_cycle;
uint64_t cycle1 = 0;
rtc_cpu_freq_config_t old_config;
rtc_clk_cpu_freq_get_config(&old_config);
rtc_clk_cpu_freq_set_xtal();
REGI2C_WRITE_MASK(I2C_ULP, I2C_ULP_IR_RESETB, 0);
REGI2C_WRITE_MASK(I2C_ULP, I2C_ULP_IR_RESETB, 1);
bool odone_flag = 0;
bool bg_odone_flag = 0;
while(1) {
odone_flag = REGI2C_READ_MASK(I2C_ULP, I2C_ULP_O_DONE_FLAG);
bg_odone_flag = REGI2C_READ_MASK(I2C_ULP, I2C_ULP_BG_O_DONE_FLAG);
cycle1 = rtc_time_get();
if (odone_flag && bg_odone_flag)
break;
if (cycle1 >= timeout_cycle) {
SOC_LOGW(TAG, "o_code calibration fail");
break;
}
}
rtc_clk_cpu_freq_set_config(&old_config);
}
rtc_clk_cpu_freq_set_config(&old_config);
}
}

View File

@ -18,6 +18,8 @@
#include "hal/adc_hal.h"
#include "hal/adc_types.h"
#include "hal/adc_hal_conf.h"
#include "esp_log.h"
#include "esp32s2/esp_efuse_rtc_table.h"
/*---------------------------------------------------------------
Digital controller setting
@ -189,13 +191,6 @@ uint32_t adc_hal_calibration(adc_ll_num_t adc_n, adc_channel_t channel, adc_atte
}
}
uint32_t code_list[ADC_HAL_CAL_TIMES] = {0};
uint32_t code_sum = 0;
uint32_t code_h = 0;
uint32_t code_l = 0;
uint32_t chk_code = 0;
uint32_t dout = 0;
adc_hal_set_power_manage(ADC_POWER_SW_ON);
if (adc_n == ADC_NUM_2) {
adc_arbiter_t config = ADC_ARBITER_CONFIG_DEFAULT();
@ -206,56 +201,71 @@ uint32_t adc_hal_calibration(adc_ll_num_t adc_n, adc_channel_t channel, adc_atte
// adc_hal_arbiter_config(adc_arbiter_t *config)
adc_ll_calibration_prepare(adc_n, channel, internal_gnd);
/* Enable/disable internal connect GND (for calibration). */
if (internal_gnd) {
adc_ll_rtc_disable_channel(adc_n, channel);
adc_ll_set_atten(adc_n, 0, atten); // Note: when disable all channel, HW auto select channel0 atten param.
uint32_t dout = 0;
// check if we can fetch the values from eFuse.
int version = esp_efuse_rtc_table_read_calib_version();
if (version == 2) {
int tag = esp_efuse_rtc_table_get_tag(version, adc_n + 1, atten, RTCCALIB_V2_PARAM_VINIT);
dout = esp_efuse_rtc_table_get_parsed_efuse_value(tag, false);
} else {
adc_ll_rtc_enable_channel(adc_n, channel);
adc_ll_set_atten(adc_n, channel, atten);
}
uint32_t code_list[ADC_HAL_CAL_TIMES] = {0};
uint32_t code_sum = 0;
uint32_t code_h = 0;
uint32_t code_l = 0;
uint32_t chk_code = 0;
for (uint8_t rpt = 0 ; rpt < ADC_HAL_CAL_TIMES ; rpt ++) {
code_h = ADC_HAL_CAL_OFFSET_RANGE;
code_l = 0;
chk_code = (code_h + code_l) / 2;
adc_ll_set_calibration_param(adc_n, chk_code);
dout = adc_hal_read_self_cal(adc_n, channel);
while (code_h - code_l > 1) {
if (dout == 0) {
code_h = chk_code;
} else {
code_l = chk_code;
}
/* Enable/disable internal connect GND (for calibration). */
if (internal_gnd) {
adc_ll_rtc_disable_channel(adc_n, channel);
adc_ll_set_atten(adc_n, 0, atten); // Note: when disable all channel, HW auto select channel0 atten param.
} else {
adc_ll_rtc_enable_channel(adc_n, channel);
adc_ll_set_atten(adc_n, channel, atten);
}
for (uint8_t rpt = 0 ; rpt < ADC_HAL_CAL_TIMES ; rpt ++) {
code_h = ADC_HAL_CAL_OFFSET_RANGE;
code_l = 0;
chk_code = (code_h + code_l) / 2;
adc_ll_set_calibration_param(adc_n, chk_code);
dout = adc_hal_read_self_cal(adc_n, channel);
if ((code_h - code_l == 1)) {
chk_code += 1;
while (code_h - code_l > 1) {
if (dout == 0) {
code_h = chk_code;
} else {
code_l = chk_code;
}
chk_code = (code_h + code_l) / 2;
adc_ll_set_calibration_param(adc_n, chk_code);
dout = adc_hal_read_self_cal(adc_n, channel);
if ((code_h - code_l == 1)) {
chk_code += 1;
adc_ll_set_calibration_param(adc_n, chk_code);
dout = adc_hal_read_self_cal(adc_n, channel);
}
}
code_list[rpt] = chk_code;
code_sum += chk_code;
}
code_l = code_list[0];
code_h = code_list[0];
for (uint8_t i = 0 ; i < ADC_HAL_CAL_TIMES ; i++) {
if (code_l > code_list[i]) {
code_l = code_list[i];
}
if (code_h < code_list[i]) {
code_h = code_list[i];
}
}
code_list[rpt] = chk_code;
code_sum += chk_code;
}
code_l = code_list[0];
code_h = code_list[0];
for (uint8_t i = 0 ; i < ADC_HAL_CAL_TIMES ; i++) {
if (code_l > code_list[i]) {
code_l = code_list[i];
}
if (code_h < code_list[i]) {
code_h = code_list[i];
}
}
chk_code = code_h + code_l;
dout = ((code_sum - chk_code) % (ADC_HAL_CAL_TIMES - 2) < 4)
? (code_sum - chk_code) / (ADC_HAL_CAL_TIMES - 2)
: (code_sum - chk_code) / (ADC_HAL_CAL_TIMES - 2) + 1;
chk_code = code_h + code_l;
dout = ((code_sum - chk_code) % (ADC_HAL_CAL_TIMES - 2) < 4)
? (code_sum - chk_code) / (ADC_HAL_CAL_TIMES - 2)
: (code_sum - chk_code) / (ADC_HAL_CAL_TIMES - 2) + 1;
}
adc_ll_set_calibration_param(adc_n, dout);
adc_ll_calibration_finish(adc_n);
s_adc_cali_param[adc_n][atten] = (uint16_t)dout;
return dout;
}