Merge branch 'feature/adc_driver_ng' into 'master'

ADC Driver NG

Closes IDF-4560, IDF-3908, IDF-4225, IDF-2482, IDF-4111, IDF-3610, IDF-4058, IDF-3801, IDF-3636, IDF-2537, IDF-4310, IDF-5150, IDF-5151, and IDF-4979

See merge request espressif/esp-idf!17960
This commit is contained in:
Armando (Dou Yiwen) 2022-07-19 21:28:31 +08:00
commit 9f6f61345b
185 changed files with 7783 additions and 6462 deletions

View File

@ -77,7 +77,7 @@
/components/cxx/ @esp-idf-codeowners/system
/components/driver/ @esp-idf-codeowners/peripherals
/components/efuse/ @esp-idf-codeowners/system
/components/esp_adc_cal/ @esp-idf-codeowners/peripherals
/components/esp_adc/ @esp-idf-codeowners/peripherals
/components/esp_common/ @esp-idf-codeowners/system
/components/esp_eth/ @esp-idf-codeowners/network
/components/esp_event/ @esp-idf-codeowners/system

View File

@ -24,9 +24,11 @@ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${target}/include")
endif()
if(CONFIG_SOC_ADC_SUPPORTED)
list(APPEND srcs
"adc_single.c"
"adc.c")
list(APPEND srcs "deprecated/adc_legacy.c")
endif()
if(CONFIG_SOC_ADC_DMA_SUPPORTED)
list(APPEND srcs "deprecated/adc_dma_legacy.c")
endif()
if(CONFIG_SOC_MCPWM_SUPPORTED)
@ -87,8 +89,7 @@ if(${target} STREQUAL "esp32")
"sdio_slave.c"
"touch_sensor_common.c"
"esp32/touch_sensor.c"
"esp32/adc.c"
"adc_deprecated.c"
"deprecated/adc_i2s_deprecated.c"
"esp32/dac.c")
endif()
@ -96,9 +97,6 @@ if(${target} STREQUAL "esp32s2")
list(APPEND srcs "dac_common.c"
"touch_sensor_common.c"
"esp32s2/touch_sensor.c"
"esp32s2/adc.c"
"adc_deprecated.c"
"esp32s2/adc2_init_cal.c"
"esp32s2/dac.c")
endif()
@ -107,19 +105,18 @@ if(${target} STREQUAL "esp32s3")
"esp32s3/touch_sensor.c")
endif()
if(${target} STREQUAL "esp32c3")
list(APPEND srcs "esp32c3/adc2_init_cal.c")
endif()
if(BOOTLOADER_BUILD)
# Bootloader shall NOT depend on the drivers
idf_component_register()
else()
# (REQUIRES cannot hide soc headers, since many arguments in the driver headers are chip-dependent)
# (Legacy drivers requires `esp_adc`, due to ADC HW resource mutex logics are there.
# Can be removed together with legacy drivers)
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ${includes}
PRIV_INCLUDE_DIRS "include/driver"
PRIV_REQUIRES efuse esp_timer
PRIV_REQUIRES efuse esp_timer esp_adc
REQUIRES esp_pm esp_ringbuf freertos soc hal esp_hw_support
LDFRAGMENTS linker.lf)
endif()

View File

@ -1,16 +1,9 @@
menu "Driver Configurations"
menu "ADC Configuration"
config ADC_FORCE_XPD_FSM
bool "Use the FSM to control ADC power"
default n
help
ADC power can be controlled by the FSM instead of software. This allows the ADC to
be shut off when it is not working leading to lower power consumption. However
using the FSM control ADC power will increase the noise of ADC.
menu "Legacy ADC Configuration"
config ADC_DISABLE_DAC
depends on SOC_DAC_SUPPORTED
bool "Disable DAC when ADC2 is used on GPIO 25 and 26"
default y
help
@ -19,6 +12,54 @@ menu "Driver Configurations"
For testing, disable this option so that we can measure the output of DAC by internal ADC.
config ADC_SUPPRESS_DEPRECATE_WARN
bool "Suppress legacy driver deprecated warning"
default n
help
Wether to suppress the deprecation warnings when using legacy adc driver (driver/adc.h).
If you want to continue using the legacy driver, and don't want to see related deprecation warnings,
you can enable this option.
menu "Legacy ADC Calibration Configuration"
config ADC_CAL_EFUSE_TP_ENABLE
depends on IDF_TARGET_ESP32
bool "Use Two Point Values"
default "y"
help
Some ESP32s have Two Point calibration values burned into eFuse BLOCK3.
This option will allow the ADC calibration component to characterize the
ADC-Voltage curve using Two Point values if they are available.
config ADC_CAL_EFUSE_VREF_ENABLE
depends on IDF_TARGET_ESP32
bool "Use eFuse Vref"
default "y"
help
Some ESP32s have Vref burned into eFuse BLOCK0. This option will allow
the ADC calibration component to characterize the ADC-Voltage curve using
eFuse Vref if it is available.
config ADC_CAL_LUT_ENABLE
depends on IDF_TARGET_ESP32
bool "Use Lookup Tables"
default "y"
help
This option will allow the ADC calibration component to use Lookup Tables
to correct for non-linear behavior in 11db attenuation. Other attenuations
do not exhibit non-linear behavior hence will not be affected by this option.
config ADC_CALI_SUPPRESS_DEPRECATE_WARN
bool "Suppress legacy driver deprecated warning"
default n
help
Wether to suppress the deprecation warnings when using legacy adc calibration
driver (esp_adc_cal.h).
If you want to continue using the legacy driver, and don't want to see related
deprecation warnings, you can enable this option.
endmenu
endmenu # ADC Configuration
menu "SPI Configuration"

View File

@ -1,640 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*----------------------------------------------------------------------------------
This file contains ESP32 and ESP32S2 Depricated ADC APIs and functions
-----------------------------------------------------------------------------------*/
#include "sdkconfig.h"
#include "esp_types.h"
#include "esp_log.h"
#include "esp_intr_alloc.h"
#include "driver/rtc_io.h"
#include "hal/adc_ll.h"
#include "hal/adc_types.h"
#include "hal/adc_hal_conf.h"
#ifdef CONFIG_PM_ENABLE
#include "esp_pm.h"
#endif
#include "adc.h"
#include "esp_private/adc_cali.h"
#include "freertos/FreeRTOS.h"
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#include "deprecated/driver/adc_types_deprecated.h"
static const char *ADC_TAG = "ADC";
#define ADC_CHECK_RET(fun_ret) ({ \
if (fun_ret != ESP_OK) { \
ESP_LOGE(ADC_TAG,"%s:%d\n",__FUNCTION__,__LINE__); \
return ESP_FAIL; \
} \
})
#define ADC_CHECK(a, str, ret_val) ({ \
if (!(a)) { \
ESP_LOGE(ADC_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
return (ret_val); \
} \
})
#define ADC_CHANNEL_CHECK(periph, channel) ADC_CHECK(channel < SOC_ADC_CHANNEL_NUM(periph), "ADC"#periph" channel error", ESP_ERR_INVALID_ARG)
#define ADC_GET_IO_NUM(periph, channel) (adc_channel_io_map[periph][channel])
extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate position after the rtc module is finished.
#define ADC_ENTER_CRITICAL() portENTER_CRITICAL(&rtc_spinlock)
#define ADC_EXIT_CRITICAL() portEXIT_CRITICAL(&rtc_spinlock)
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_handle_t adc_digi_arbiter_lock = NULL;
#endif //CONFIG_PM_ENABLE
#if CONFIG_IDF_TARGET_ESP32
/*---------------------------------------------------------------
ESP32 Depricated ADC APIs and functions
---------------------------------------------------------------*/
#define ADC_MEAS_NUM_LIM_DEFAULT (1)
#define ADC_MAX_MEAS_NUM_DEFAULT (255)
#define DIG_ADC_OUTPUT_FORMAT_DEFUALT (ADC_DIGI_FORMAT_12BIT)
#define DIG_ADC_ATTEN_DEFUALT (ADC_ATTEN_DB_11)
#define DIG_ADC_BIT_WIDTH_DEFUALT (ADC_WIDTH_BIT_12)
esp_err_t adc_digi_init(void)
{
ADC_ENTER_CRITICAL();
adc_ll_digi_set_fsm_time(ADC_HAL_FSM_RSTB_WAIT_DEFAULT, ADC_HAL_FSM_START_WAIT_DEFAULT,
ADC_HAL_FSM_STANDBY_WAIT_DEFAULT);
adc_ll_set_sample_cycle(ADC_HAL_SAMPLE_CYCLE_DEFAULT);
adc_hal_pwdet_set_cct(ADC_HAL_PWDET_CCT_DEFAULT);
adc_ll_digi_output_invert(ADC_UNIT_1, ADC_HAL_DIGI_DATA_INVERT_DEFAULT(ADC_UNIT_1));
adc_ll_digi_output_invert(ADC_UNIT_2, ADC_HAL_DIGI_DATA_INVERT_DEFAULT(ADC_UNIT_2));
adc_ll_digi_set_clk_div(ADC_HAL_DIGI_SAR_CLK_DIV_DEFAULT);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t adc_digi_deinit(void)
{
adc_power_release();
ADC_ENTER_CRITICAL();
adc_hal_digi_deinit(NULL);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
/**
* Set adc output 16-bit-data format from digital controller.
*
* @param data_sel 1: [15] unit, [14:11] channel, [10:0] data, 11-bit-width at most. Only work under `ADC_LL_DIGI_CONV_BOTH_UNIT` or `ADC_LL_DIGI_CONV_ALTER_UNIT` mode.
* 0: [15:12] channel, [11:0] data, 12-bit-width at most. Only work under `ADC_LL_DIGI_CONV_ONLY_ADC1` or `ADC_LL_DIGI_CONV_ONLY_ADC2` mode
* @note see `adc_ll_digi_pattern_table_t` for more detail of data bit width
*/
static inline void adc_ll_digi_set_output_format(bool data_sel)
{
SYSCON.saradc_ctrl.data_sar_sel = data_sel;
}
static inline void adc_ll_digi_prepare_pattern_table(adc_unit_t adc_n, uint32_t pattern_index, adc_digi_pattern_table_t pattern)
{
uint32_t tab;
uint8_t index = pattern_index / 4;
uint8_t offset = (pattern_index % 4) * 8;
if (adc_n == ADC_UNIT_1) {
tab = SYSCON.saradc_sar1_patt_tab[index]; // Read old register value
tab &= (~(0xFF000000 >> offset)); // clear old data
tab |= ((uint32_t)pattern.val << 24) >> offset; // Fill in the new data
SYSCON.saradc_sar1_patt_tab[index] = tab; // Write back
} else { // adc_n == ADC_UNIT_2
tab = SYSCON.saradc_sar2_patt_tab[index]; // Read old register value
tab &= (~(0xFF000000 >> offset)); // clear old data
tab |= ((uint32_t)pattern.val << 24) >> offset; // Fill in the new data
SYSCON.saradc_sar2_patt_tab[index] = tab; // Write back
}
}
void adc_digi_controller_reg_set(const adc_digi_config_t *cfg)
{
/* On ESP32, only support ADC1 */
switch (cfg->conv_mode) {
case ADC_CONV_SINGLE_UNIT_1:
adc_ll_digi_set_convert_mode(ADC_LL_DIGI_CONV_ONLY_ADC1);
break;
case ADC_CONV_SINGLE_UNIT_2:
adc_ll_digi_set_convert_mode(ADC_LL_DIGI_CONV_ONLY_ADC2);
break;
case ADC_CONV_BOTH_UNIT:
adc_ll_digi_set_convert_mode(ADC_LL_DIGI_CONV_BOTH_UNIT);
break;
case ADC_CONV_ALTER_UNIT:
adc_ll_digi_set_convert_mode(ADC_LL_DIGI_CONV_ALTER_UNIT);
break;
default:
abort();
}
if (cfg->conv_mode & ADC_CONV_SINGLE_UNIT_1) {
adc_ll_set_controller(ADC_UNIT_1, ADC_LL_CTRL_DIG);
if (cfg->adc1_pattern_len) {
adc_ll_digi_clear_pattern_table(ADC_UNIT_1);
adc_ll_digi_set_pattern_table_len(ADC_UNIT_1, cfg->adc1_pattern_len);
for (uint32_t i = 0; i < cfg->adc1_pattern_len; i++) {
adc_ll_digi_prepare_pattern_table(ADC_UNIT_1, i, cfg->adc1_pattern[i]);
}
}
}
if (cfg->conv_mode & ADC_CONV_SINGLE_UNIT_2) {
adc_ll_set_controller(ADC_UNIT_2, ADC_LL_CTRL_DIG);
if (cfg->adc2_pattern_len) {
adc_ll_digi_clear_pattern_table(ADC_UNIT_2);
adc_ll_digi_set_pattern_table_len(ADC_UNIT_2, cfg->adc2_pattern_len);
for (uint32_t i = 0; i < cfg->adc2_pattern_len; i++) {
adc_ll_digi_prepare_pattern_table(ADC_UNIT_2, i, cfg->adc2_pattern[i]);
}
}
}
adc_ll_digi_set_output_format(cfg->format);
if (cfg->conv_limit_en) {
adc_ll_digi_set_convert_limit_num(cfg->conv_limit_num);
adc_ll_digi_convert_limit_enable();
} else {
adc_ll_digi_convert_limit_disable();
}
adc_ll_digi_set_data_source(ADC_I2S_DATA_SRC_ADC);
}
esp_err_t adc_digi_controller_config(const adc_digi_config_t *config)
{
adc_power_acquire();
ADC_ENTER_CRITICAL();
adc_digi_controller_reg_set(config);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t adc_set_i2s_data_source(adc_i2s_source_t src)
{
ADC_CHECK(src < ADC_I2S_DATA_SRC_MAX, "ADC i2s data source error", ESP_ERR_INVALID_ARG);
ADC_ENTER_CRITICAL();
adc_ll_digi_set_data_source(src);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
extern esp_err_t adc_common_gpio_init(adc_unit_t adc_unit, adc_channel_t channel);
esp_err_t adc_i2s_mode_init(adc_unit_t adc_unit, adc_channel_t channel)
{
if (adc_unit == ADC_UNIT_1) {
ADC_CHECK((SOC_ADC_SUPPORT_DMA_MODE(ADC_UNIT_1)), "ADC1 not support DMA for now.", ESP_ERR_INVALID_ARG);
ADC_CHANNEL_CHECK(ADC_UNIT_1, channel);
} else if (adc_unit == ADC_UNIT_2) {
//ADC2 does not support DMA mode
ADC_CHECK((SOC_ADC_SUPPORT_DMA_MODE(ADC_UNIT_2)), "ADC2 not support DMA for now.", ESP_ERR_INVALID_ARG);
ADC_CHANNEL_CHECK(ADC_UNIT_2, channel);
}
adc_digi_pattern_table_t adc1_pattern[1];
adc_digi_pattern_table_t adc2_pattern[1];
adc_digi_config_t dig_cfg = {
.conv_limit_en = ADC_MEAS_NUM_LIM_DEFAULT,
.conv_limit_num = ADC_MAX_MEAS_NUM_DEFAULT,
.format = DIG_ADC_OUTPUT_FORMAT_DEFUALT,
.conv_mode = ADC_CONV_SINGLE_UNIT_1,
};
if (adc_unit == ADC_UNIT_1) {
adc1_pattern[0].atten = DIG_ADC_ATTEN_DEFUALT;
adc1_pattern[0].bit_width = DIG_ADC_BIT_WIDTH_DEFUALT;
adc1_pattern[0].channel = channel;
dig_cfg.adc1_pattern_len = 1;
dig_cfg.adc1_pattern = adc1_pattern;
} else if (adc_unit == ADC_UNIT_2) {
adc2_pattern[0].atten = DIG_ADC_ATTEN_DEFUALT;
adc2_pattern[0].bit_width = DIG_ADC_BIT_WIDTH_DEFUALT;
adc2_pattern[0].channel = channel;
dig_cfg.adc2_pattern_len = 1;
dig_cfg.adc2_pattern = adc2_pattern;
}
adc_common_gpio_init(adc_unit, channel);
ADC_ENTER_CRITICAL();
adc_ll_digi_set_fsm_time(ADC_HAL_FSM_RSTB_WAIT_DEFAULT, ADC_HAL_FSM_START_WAIT_DEFAULT,
ADC_HAL_FSM_STANDBY_WAIT_DEFAULT);
adc_ll_set_sample_cycle(ADC_HAL_SAMPLE_CYCLE_DEFAULT);
adc_hal_pwdet_set_cct(ADC_HAL_PWDET_CCT_DEFAULT);
adc_ll_digi_output_invert(ADC_UNIT_1, ADC_HAL_DIGI_DATA_INVERT_DEFAULT(ADC_UNIT_1));
adc_ll_digi_output_invert(ADC_UNIT_2, ADC_HAL_DIGI_DATA_INVERT_DEFAULT(ADC_UNIT_2));
adc_ll_digi_set_clk_div(ADC_HAL_DIGI_SAR_CLK_DIV_DEFAULT);
adc_digi_controller_reg_set(&dig_cfg);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
#endif //#if CONFIG_IDF_TARGET_ESP32
#if CONFIG_IDF_TARGET_ESP32S2
/*---------------------------------------------------------------
ESP32S2 Depricated ADC functions and APIs
---------------------------------------------------------------*/
esp_err_t adc_arbiter_config(adc_unit_t adc_unit, adc_arbiter_t *config)
{
if (adc_unit == ADC_UNIT_1) {
return ESP_ERR_NOT_SUPPORTED;
}
ADC_ENTER_CRITICAL();
adc_hal_arbiter_config(config);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
/**
* Enable interrupt of adc digital controller by bitmask.
*
* @param adc_n ADC unit.
* @param intr Interrupt bitmask.
*/
static inline void adc_ll_digi_intr_enable(adc_unit_t adc_n, adc_digi_intr_t intr)
{
if (adc_n == ADC_UNIT_1) {
if (intr & ADC_DIGI_INTR_MASK_MONITOR) {
APB_SARADC.int_ena.adc1_thres = 1;
}
if (intr & ADC_DIGI_INTR_MASK_MEAS_DONE) {
APB_SARADC.int_ena.adc1_done = 1;
}
} else { // adc_n == ADC_UNIT_2
if (intr & ADC_DIGI_INTR_MASK_MONITOR) {
APB_SARADC.int_ena.adc2_thres = 1;
}
if (intr & ADC_DIGI_INTR_MASK_MEAS_DONE) {
APB_SARADC.int_ena.adc2_done = 1;
}
}
}
esp_err_t adc_digi_intr_enable(adc_unit_t adc_unit, adc_digi_intr_t intr_mask)
{
ADC_ENTER_CRITICAL();
if (adc_unit == ADC_UNIT_1) {
adc_ll_digi_intr_enable(ADC_UNIT_1, intr_mask);
} else if (adc_unit == ADC_UNIT_2) {
adc_ll_digi_intr_enable(ADC_UNIT_2, intr_mask);
}
ADC_EXIT_CRITICAL();
return ESP_OK;
}
/**
* Disable interrupt of adc digital controller by bitmask.
*
* @param adc_n ADC unit.
* @param intr Interrupt bitmask.
*/
static inline void adc_ll_digi_intr_disable(adc_unit_t adc_n, adc_digi_intr_t intr)
{
if (adc_n == ADC_UNIT_1) {
if (intr & ADC_DIGI_INTR_MASK_MONITOR) {
APB_SARADC.int_ena.adc1_thres = 0;
}
if (intr & ADC_DIGI_INTR_MASK_MEAS_DONE) {
APB_SARADC.int_ena.adc1_done = 0;
}
} else { // adc_n == ADC_UNIT_2
if (intr & ADC_DIGI_INTR_MASK_MONITOR) {
APB_SARADC.int_ena.adc2_thres = 0;
}
if (intr & ADC_DIGI_INTR_MASK_MEAS_DONE) {
APB_SARADC.int_ena.adc2_done = 0;
}
}
}
esp_err_t adc_digi_intr_disable(adc_unit_t adc_unit, adc_digi_intr_t intr_mask)
{
ADC_ENTER_CRITICAL();
if (adc_unit == ADC_UNIT_1) {
adc_ll_digi_intr_disable(ADC_UNIT_1, intr_mask);
} else if (adc_unit == ADC_UNIT_2) {
adc_ll_digi_intr_disable(ADC_UNIT_2, intr_mask);
}
ADC_EXIT_CRITICAL();
return ESP_OK;
}
/**
* Clear interrupt of adc digital controller by bitmask.
*
* @param adc_n ADC unit.
* @param intr Interrupt bitmask.
*/
static inline void adc_ll_digi_intr_clear(adc_unit_t adc_n, adc_digi_intr_t intr)
{
if (adc_n == ADC_UNIT_1) {
if (intr & ADC_DIGI_INTR_MASK_MONITOR) {
APB_SARADC.int_clr.adc1_thres = 1;
}
if (intr & ADC_DIGI_INTR_MASK_MEAS_DONE) {
APB_SARADC.int_clr.adc1_done = 1;
}
} else { // adc_n == ADC_UNIT_2
if (intr & ADC_DIGI_INTR_MASK_MONITOR) {
APB_SARADC.int_clr.adc2_thres = 1;
}
if (intr & ADC_DIGI_INTR_MASK_MEAS_DONE) {
APB_SARADC.int_clr.adc2_done = 1;
}
}
}
esp_err_t adc_digi_intr_clear(adc_unit_t adc_unit, adc_digi_intr_t intr_mask)
{
ADC_ENTER_CRITICAL();
if (adc_unit == ADC_UNIT_1) {
adc_ll_digi_intr_clear(ADC_UNIT_1, intr_mask);
} else if (adc_unit == ADC_UNIT_2) {
adc_ll_digi_intr_clear(ADC_UNIT_2, intr_mask);
}
ADC_EXIT_CRITICAL();
return ESP_OK;
}
/**
* Get interrupt status mask of adc digital controller.
*
* @param adc_n ADC unit.
* @return
* - intr Interrupt bitmask.
*/
static inline uint32_t adc_ll_digi_get_intr_status(adc_unit_t adc_n)
{
uint32_t int_st = APB_SARADC.int_st.val;
uint32_t ret_msk = 0;
if (adc_n == ADC_UNIT_1) {
if (int_st & APB_SARADC_ADC1_DONE_INT_ST_M) {
ret_msk |= ADC_DIGI_INTR_MASK_MEAS_DONE;
}
if (int_st & APB_SARADC_ADC1_THRES_INT_ST) {
ret_msk |= ADC_DIGI_INTR_MASK_MONITOR;
}
} else { // adc_n == ADC_UNIT_2
if (int_st & APB_SARADC_ADC2_DONE_INT_ST_M) {
ret_msk |= ADC_DIGI_INTR_MASK_MEAS_DONE;
}
if (int_st & APB_SARADC_ADC2_THRES_INT_ST_M) {
ret_msk |= ADC_DIGI_INTR_MASK_MONITOR;
}
}
return ret_msk;
}
uint32_t adc_digi_intr_get_status(adc_unit_t adc_unit)
{
uint32_t ret = 0;
ADC_ENTER_CRITICAL();
if (adc_unit == ADC_UNIT_1) {
ret = adc_ll_digi_get_intr_status(ADC_UNIT_1);
} else if (adc_unit == ADC_UNIT_2) {
ret = adc_ll_digi_get_intr_status(ADC_UNIT_2);
}
ADC_EXIT_CRITICAL();
return ret;
}
static uint8_t s_isr_registered = 0;
static intr_handle_t s_adc_isr_handle = NULL;
esp_err_t adc_digi_isr_register(void (*fn)(void *), void *arg, int intr_alloc_flags)
{
ADC_CHECK((fn != NULL), "Parameter error", ESP_ERR_INVALID_ARG);
ADC_CHECK(s_isr_registered == 0, "ADC ISR have installed, can not install again", ESP_FAIL);
esp_err_t ret = esp_intr_alloc(ETS_APB_ADC_INTR_SOURCE, intr_alloc_flags, fn, arg, &s_adc_isr_handle);
if (ret == ESP_OK) {
s_isr_registered = 1;
}
return ret;
}
esp_err_t adc_digi_isr_deregister(void)
{
esp_err_t ret = ESP_FAIL;
if (s_isr_registered) {
ret = esp_intr_free(s_adc_isr_handle);
if (ret == ESP_OK) {
s_isr_registered = 0;
}
}
return ret;
}
esp_err_t adc_digi_init(void)
{
adc_arbiter_t config = ADC_ARBITER_CONFIG_DEFAULT();
ADC_ENTER_CRITICAL();
adc_ll_digi_set_fsm_time(ADC_HAL_FSM_RSTB_WAIT_DEFAULT, ADC_HAL_FSM_START_WAIT_DEFAULT,
ADC_HAL_FSM_STANDBY_WAIT_DEFAULT);
adc_ll_set_sample_cycle(ADC_HAL_SAMPLE_CYCLE_DEFAULT);
adc_hal_pwdet_set_cct(ADC_HAL_PWDET_CCT_DEFAULT);
adc_ll_digi_output_invert(ADC_UNIT_1, ADC_HAL_DIGI_DATA_INVERT_DEFAULT(ADC_UNIT_1));
adc_ll_digi_output_invert(ADC_UNIT_2, ADC_HAL_DIGI_DATA_INVERT_DEFAULT(ADC_UNIT_2));
adc_ll_digi_set_clk_div(ADC_HAL_DIGI_SAR_CLK_DIV_DEFAULT);
adc_hal_arbiter_config(&config);
ADC_EXIT_CRITICAL();
adc_hal_calibration_init(ADC_UNIT_1);
adc_hal_calibration_init(ADC_UNIT_2);
return ESP_OK;
}
esp_err_t adc_digi_deinit(void)
{
#ifdef CONFIG_PM_ENABLE
if (adc_digi_arbiter_lock) {
esp_pm_lock_delete(adc_digi_arbiter_lock);
adc_digi_arbiter_lock = NULL;
}
#endif
adc_power_release();
ADC_ENTER_CRITICAL();
adc_hal_digi_deinit(NULL);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
/**
* @brief Reset FSM of adc digital controller.
*
* @return
* - ESP_OK Success
*/
esp_err_t adc_digi_reset(void)
{
ADC_ENTER_CRITICAL();
adc_ll_digi_reset();
adc_ll_digi_clear_pattern_table(ADC_UNIT_1);
adc_ll_digi_clear_pattern_table(ADC_UNIT_2);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
/**
* Set adc output data format for digital controller.
*
* @param format Output data format.
*/
static inline void adc_ll_digi_set_output_format(adc_digi_output_format_t format)
{
APB_SARADC.ctrl.data_sar_sel = format;
}
static inline void adc_ll_digi_prepare_pattern_table(adc_unit_t adc_n, uint32_t pattern_index, adc_digi_pattern_table_t pattern)
{
uint32_t tab;
uint8_t index = pattern_index / 4;
uint8_t offset = (pattern_index % 4) * 8;
if (adc_n == ADC_UNIT_1) {
tab = APB_SARADC.sar1_patt_tab[index]; // Read old register value
tab &= (~(0xFF000000 >> offset)); // clear old data
tab |= ((uint32_t)pattern.val << 24) >> offset; // Fill in the new data
APB_SARADC.sar1_patt_tab[index] = tab; // Write back
} else { // adc_n == ADC_UNIT_2
tab = APB_SARADC.sar2_patt_tab[index]; // Read old register value
tab &= (~(0xFF000000 >> offset)); // clear old data
tab |= ((uint32_t)pattern.val << 24) >> offset; // Fill in the new data
APB_SARADC.sar2_patt_tab[index] = tab; // Write back
}
}
static void adc_digi_controller_reg_set(const adc_digi_config_t *cfg)
{
/* Single channel mode or multi channel mode. */
switch (cfg->conv_mode) {
case ADC_CONV_SINGLE_UNIT_1:
adc_ll_digi_set_convert_mode(ADC_LL_DIGI_CONV_ONLY_ADC1);
break;
case ADC_CONV_SINGLE_UNIT_2:
adc_ll_digi_set_convert_mode(ADC_LL_DIGI_CONV_ONLY_ADC2);
break;
case ADC_CONV_BOTH_UNIT:
adc_ll_digi_set_convert_mode(ADC_LL_DIGI_CONV_BOTH_UNIT);
break;
case ADC_CONV_ALTER_UNIT:
adc_ll_digi_set_convert_mode(ADC_LL_DIGI_CONV_ALTER_UNIT);
break;
default:
abort();
}
if (cfg->conv_mode & ADC_CONV_SINGLE_UNIT_1) {
if (cfg->adc1_pattern_len) {
adc_ll_digi_clear_pattern_table(ADC_UNIT_1);
adc_ll_digi_set_pattern_table_len(ADC_UNIT_1, cfg->adc1_pattern_len);
for (uint32_t i = 0; i < cfg->adc1_pattern_len; i++) {
adc_ll_digi_prepare_pattern_table(ADC_UNIT_1, i, cfg->adc1_pattern[i]);
}
}
}
if (cfg->conv_mode & ADC_CONV_SINGLE_UNIT_2) {
if (cfg->adc2_pattern_len) {
adc_ll_digi_clear_pattern_table(ADC_UNIT_2);
adc_ll_digi_set_pattern_table_len(ADC_UNIT_2, cfg->adc2_pattern_len);
for (uint32_t i = 0; i < cfg->adc2_pattern_len; i++) {
adc_ll_digi_prepare_pattern_table(ADC_UNIT_2, i, cfg->adc2_pattern[i]);
}
}
}
if (cfg->conv_mode & ADC_CONV_SINGLE_UNIT_1) {
adc_ll_set_controller(ADC_UNIT_1, ADC_LL_CTRL_DIG);
}
if (cfg->conv_mode & ADC_CONV_SINGLE_UNIT_2) {
adc_ll_set_controller(ADC_UNIT_2, ADC_LL_CTRL_ARB);
}
adc_ll_digi_set_output_format(cfg->format);
if (cfg->conv_limit_en) {
adc_ll_digi_set_convert_limit_num(cfg->conv_limit_num);
adc_ll_digi_convert_limit_enable();
} else {
adc_ll_digi_convert_limit_disable();
}
adc_ll_digi_set_trigger_interval(cfg->interval);
adc_ll_digi_controller_clk_div(cfg->dig_clk.div_num, cfg->dig_clk.div_b, cfg->dig_clk.div_a);
adc_ll_digi_clk_sel(cfg->dig_clk.use_apll);
adc_ll_digi_dma_set_eof_num(cfg->dma_eof_num);
}
esp_err_t adc_digi_controller_config(const adc_digi_config_t *config)
{
#ifdef CONFIG_PM_ENABLE
esp_err_t err;
if (adc_digi_arbiter_lock == NULL) {
if (config->dig_clk.use_apll) {
err = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "adc_dma", &adc_digi_arbiter_lock);
} else {
err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "adc_dma", &adc_digi_arbiter_lock);
}
if (err != ESP_OK) {
adc_digi_arbiter_lock = NULL;
ESP_LOGE(ADC_TAG, "ADC-DMA pm lock error");
return err;
}
}
#endif //CONFIG_PM_ENABLE
if (config->conv_mode & ADC_CONV_SINGLE_UNIT_1) {
for (int i = 0; i < config->adc1_pattern_len; i++) {
adc_cal_offset(ADC_UNIT_1, config->adc1_pattern[i].atten);
}
}
if (config->conv_mode & ADC_CONV_SINGLE_UNIT_2) {
for (int i = 0; i < config->adc2_pattern_len; i++) {
adc_cal_offset(ADC_UNIT_2, config->adc2_pattern[i].atten);
}
}
/* If enable digtal controller, adc xpd should always on. */
adc_power_acquire();
ADC_ENTER_CRITICAL();
adc_digi_controller_reg_set(config);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
#endif // #if CONFIG_IDF_TARGET_ESP32S2
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3
/*---------------------------------------------------------------
ESP32S2 Depricated ADC functions and APIs
---------------------------------------------------------------*/
esp_err_t adc_gpio_init(adc_unit_t adc_unit, adc_channel_t channel)
{
gpio_num_t gpio_num = 0;
//If called with `ADC_UNIT_BOTH (ADC_UNIT_1 | ADC_UNIT_2)`, both if blocks will be run
if (adc_unit == ADC_UNIT_1) {
ADC_CHANNEL_CHECK(ADC_UNIT_1, channel);
gpio_num = ADC_GET_IO_NUM(ADC_UNIT_1, channel);
ADC_CHECK_RET(rtc_gpio_init(gpio_num));
ADC_CHECK_RET(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED));
ADC_CHECK_RET(rtc_gpio_pulldown_dis(gpio_num));
ADC_CHECK_RET(rtc_gpio_pullup_dis(gpio_num));
}
if (adc_unit == ADC_UNIT_2) {
ADC_CHANNEL_CHECK(ADC_UNIT_2, channel);
gpio_num = ADC_GET_IO_NUM(ADC_UNIT_2, channel);
ADC_CHECK_RET(rtc_gpio_init(gpio_num));
ADC_CHECK_RET(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED));
ADC_CHECK_RET(rtc_gpio_pulldown_dis(gpio_num));
ADC_CHECK_RET(rtc_gpio_pullup_dis(gpio_num));
}
return ESP_OK;
}
#endif //#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3

View File

@ -19,11 +19,15 @@
#include "freertos/timers.h"
#include "freertos/ringbuf.h"
#include "esp_private/periph_ctrl.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "esp_private/adc_private.h"
#include "esp_private/adc_lock.h"
#include "hal/adc_types.h"
#include "hal/adc_hal.h"
#include "hal/dma_types.h"
#include "driver/gpio.h"
#include "driver/adc_types_legacy.h"
//For calibration
#if CONFIG_IDF_TARGET_ESP32S2
#include "esp_efuse_rtc_table.h"
@ -37,7 +41,6 @@
#include "hal/spi_types.h"
#include "esp_private/spi_common_internal.h"
#elif CONFIG_IDF_TARGET_ESP32
#include "hal/i2s_types.h"
#include "driver/i2s_types.h"
#include "soc/i2s_periph.h"
#include "esp_private/i2s_platform.h"
@ -51,21 +54,6 @@ extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate posi
#define ADC_ENTER_CRITICAL() portENTER_CRITICAL(&rtc_spinlock)
#define ADC_EXIT_CRITICAL() portEXIT_CRITICAL(&rtc_spinlock)
/**
* 1. sar_adc1_lock: this mutex lock is to protect the SARADC1 module.
* 2. sar_adc2_lock: this mutex lock is to protect the SARADC2 module.
* 3. adc_reg_lock: this spin lock is to protect the shared registers used by ADC1 / ADC2 single read mode.
*/
static _lock_t sar_adc1_lock;
#define SAR_ADC1_LOCK_ACQUIRE() _lock_acquire(&sar_adc1_lock)
#define SAR_ADC1_LOCK_RELEASE() _lock_release(&sar_adc1_lock)
static _lock_t sar_adc2_lock;
#define SAR_ADC2_LOCK_ACQUIRE() _lock_acquire(&sar_adc2_lock)
#define SAR_ADC2_LOCK_RELEASE() _lock_release(&sar_adc2_lock)
portMUX_TYPE adc_reg_lock = portMUX_INITIALIZER_UNLOCKED;
#define ADC_REG_LOCK_ENTER() portENTER_CRITICAL(&adc_reg_lock)
#define ADC_REG_LOCK_EXIT() portEXIT_CRITICAL(&adc_reg_lock)
#define INTERNAL_BUF_NUM 5
/*---------------------------------------------------------------
@ -101,10 +89,6 @@ static adc_digi_context_t *s_adc_digi_ctx = NULL;
extern esp_pm_lock_handle_t adc_digi_arbiter_lock;
#endif //CONFIG_PM_ENABLE
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
uint32_t adc_get_calibration_offset(adc_unit_t adc_n, adc_atten_t atten);
#endif
/*---------------------------------------------------------------
ADC Continuous Read Mode (via DMA)
---------------------------------------------------------------*/
@ -152,9 +136,54 @@ static esp_err_t adc_digi_gpio_init(adc_unit_t adc_unit, uint16_t channel_mask)
return ret;
}
esp_err_t adc_digi_deinitialize(void)
{
if (!s_adc_digi_ctx) {
return ESP_ERR_INVALID_STATE;
}
if (s_adc_digi_ctx->driver_start_flag != 0) {
ESP_LOGE(ADC_TAG, "The driver is not stopped");
return ESP_ERR_INVALID_STATE;
}
if (s_adc_digi_ctx->ringbuf_hdl) {
vRingbufferDelete(s_adc_digi_ctx->ringbuf_hdl);
s_adc_digi_ctx->ringbuf_hdl = NULL;
}
#if CONFIG_PM_ENABLE
if (s_adc_digi_ctx->pm_lock) {
esp_pm_lock_delete(s_adc_digi_ctx->pm_lock);
}
#endif //CONFIG_PM_ENABLE
free(s_adc_digi_ctx->rx_dma_buf);
free(s_adc_digi_ctx->hal.rx_desc);
free(s_adc_digi_ctx->hal_digi_ctrlr_cfg.adc_pattern);
#if SOC_GDMA_SUPPORTED
gdma_disconnect(s_adc_digi_ctx->rx_dma_channel);
gdma_del_channel(s_adc_digi_ctx->rx_dma_channel);
#elif CONFIG_IDF_TARGET_ESP32S2
esp_intr_free(s_adc_digi_ctx->intr_hdl);
spicommon_dma_chan_free(s_adc_digi_ctx->spi_host);
spicommon_periph_free(s_adc_digi_ctx->spi_host);
#elif CONFIG_IDF_TARGET_ESP32
esp_intr_free(s_adc_digi_ctx->intr_hdl);
i2s_platform_release_occupation(s_adc_digi_ctx->i2s_host);
#endif
free(s_adc_digi_ctx);
s_adc_digi_ctx = NULL;
periph_module_disable(PERIPH_SARADC_MODULE);
return ESP_OK;
}
esp_err_t adc_digi_initialize(const adc_digi_init_config_t *init_config)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE((init_config->conv_num_each_intr % SOC_ADC_DIGI_DATA_BYTES_PER_CONV == 0), ESP_ERR_INVALID_ARG, ADC_TAG, "conv_frame_size should be in multiples of `SOC_ADC_DIGI_DATA_BYTES_PER_CONV`");
s_adc_digi_ctx = calloc(1, sizeof(adc_digi_context_t));
if (s_adc_digi_ctx == NULL) {
@ -282,7 +311,7 @@ esp_err_t adc_digi_initialize(const adc_digi_init_config_t *init_config)
#endif
.desc_max_num = INTERNAL_BUF_NUM,
.dma_chan = dma_chan,
.eof_num = init_config->conv_num_each_intr / ADC_HAL_DATA_LEN_PER_CONV
.eof_num = init_config->conv_num_each_intr / SOC_ADC_DIGI_DATA_BYTES_PER_CONV
};
adc_hal_dma_ctx_config(&s_adc_digi_ctx->hal, &config);
@ -362,116 +391,83 @@ static IRAM_ATTR bool s_adc_dma_intr(adc_digi_context_t *adc_digi_ctx)
esp_err_t adc_digi_start(void)
{
if (s_adc_digi_ctx) {
if (s_adc_digi_ctx->driver_start_flag != 0) {
ESP_LOGE(ADC_TAG, "The driver is already started");
return ESP_ERR_INVALID_STATE;
}
adc_power_acquire();
//reset flags
s_adc_digi_ctx->ringbuf_overflow_flag = 0;
s_adc_digi_ctx->driver_start_flag = 1;
if (s_adc_digi_ctx->use_adc1) {
SAR_ADC1_LOCK_ACQUIRE();
}
if (s_adc_digi_ctx->use_adc2) {
SAR_ADC2_LOCK_ACQUIRE();
}
if (s_adc_digi_ctx->driver_start_flag != 0) {
ESP_LOGE(ADC_TAG, "The driver is already started");
return ESP_ERR_INVALID_STATE;
}
adc_power_acquire();
//reset flags
s_adc_digi_ctx->ringbuf_overflow_flag = 0;
s_adc_digi_ctx->driver_start_flag = 1;
if (s_adc_digi_ctx->use_adc1) {
adc_lock_acquire(ADC_UNIT_1);
}
if (s_adc_digi_ctx->use_adc2) {
adc_lock_acquire(ADC_UNIT_2);
}
#if CONFIG_PM_ENABLE
// Lock APB frequency while ADC driver is in use
esp_pm_lock_acquire(s_adc_digi_ctx->pm_lock);
// Lock APB frequency while ADC driver is in use
esp_pm_lock_acquire(s_adc_digi_ctx->pm_lock);
#endif
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
if (s_adc_digi_ctx->use_adc1) {
uint32_t cal_val = adc_get_calibration_offset(ADC_UNIT_1, s_adc_digi_ctx->adc1_atten);
adc_hal_set_calibration_param(ADC_UNIT_1, cal_val);
}
if (s_adc_digi_ctx->use_adc2) {
uint32_t cal_val = adc_get_calibration_offset(ADC_UNIT_2, s_adc_digi_ctx->adc2_atten);
adc_hal_set_calibration_param(ADC_UNIT_2, cal_val);
}
if (s_adc_digi_ctx->use_adc1) {
adc_set_hw_calibration_code(ADC_UNIT_1, s_adc_digi_ctx->adc1_atten);
}
if (s_adc_digi_ctx->use_adc2) {
adc_set_hw_calibration_code(ADC_UNIT_2, s_adc_digi_ctx->adc2_atten);
}
#endif //#if SOC_ADC_CALIBRATION_V1_SUPPORTED
#if SOC_ADC_ARBITER_SUPPORTED
adc_arbiter_t config = ADC_ARBITER_CONFIG_DEFAULT();
adc_hal_arbiter_config(&config);
adc_arbiter_t config = ADC_ARBITER_CONFIG_DEFAULT();
adc_hal_arbiter_config(&config);
#endif //#if SOC_ADC_ARBITER_SUPPORTED
adc_hal_set_controller(ADC_UNIT_1, ADC_HAL_CONTINUOUS_READ_MODE);
adc_hal_set_controller(ADC_UNIT_2, ADC_HAL_CONTINUOUS_READ_MODE);
adc_hal_set_controller(ADC_UNIT_1, ADC_HAL_CONTINUOUS_READ_MODE);
adc_hal_set_controller(ADC_UNIT_2, ADC_HAL_CONTINUOUS_READ_MODE);
adc_hal_digi_init(&s_adc_digi_ctx->hal);
adc_hal_digi_controller_config(&s_adc_digi_ctx->hal, &s_adc_digi_ctx->hal_digi_ctrlr_cfg);
adc_hal_digi_init(&s_adc_digi_ctx->hal);
adc_hal_digi_controller_config(&s_adc_digi_ctx->hal, &s_adc_digi_ctx->hal_digi_ctrlr_cfg);
//start conversion
adc_hal_digi_start(&s_adc_digi_ctx->hal, s_adc_digi_ctx->rx_dma_buf);
//start conversion
adc_hal_digi_start(&s_adc_digi_ctx->hal, s_adc_digi_ctx->rx_dma_buf);
}
#if CONFIG_IDF_TARGET_ESP32S2
//For being compatible with the deprecated behaviour
else {
ESP_LOGE(ADC_TAG, "API used without driver initialization before. The following behaviour is deprecated!!");
#ifdef CONFIG_PM_ENABLE
ESP_RETURN_ON_FALSE((adc_digi_arbiter_lock), ESP_FAIL, ADC_TAG, "Should start after call `adc_digi_controller_config`");
esp_pm_lock_acquire(adc_digi_arbiter_lock);
#endif
ADC_ENTER_CRITICAL();
adc_ll_digi_dma_enable();
adc_ll_digi_trigger_enable();
ADC_EXIT_CRITICAL();
}
#endif //#if CONFIG_IDF_TARGET_ESP32S2
return ESP_OK;
}
esp_err_t adc_digi_stop(void)
{
if (s_adc_digi_ctx) {
if (s_adc_digi_ctx->driver_start_flag != 1) {
ESP_LOGE(ADC_TAG, "The driver is already stopped");
return ESP_ERR_INVALID_STATE;
}
s_adc_digi_ctx->driver_start_flag = 0;
if (s_adc_digi_ctx->driver_start_flag != 1) {
ESP_LOGE(ADC_TAG, "The driver is already stopped");
return ESP_ERR_INVALID_STATE;
}
s_adc_digi_ctx->driver_start_flag = 0;
//disable the in suc eof intrrupt
adc_hal_digi_dis_intr(&s_adc_digi_ctx->hal, ADC_HAL_DMA_INTR_MASK);
//clear the in suc eof interrupt
adc_hal_digi_clr_intr(&s_adc_digi_ctx->hal, ADC_HAL_DMA_INTR_MASK);
//stop ADC
adc_hal_digi_stop(&s_adc_digi_ctx->hal);
//disable the in suc eof intrrupt
adc_hal_digi_dis_intr(&s_adc_digi_ctx->hal, ADC_HAL_DMA_INTR_MASK);
//clear the in suc eof interrupt
adc_hal_digi_clr_intr(&s_adc_digi_ctx->hal, ADC_HAL_DMA_INTR_MASK);
//stop ADC
adc_hal_digi_stop(&s_adc_digi_ctx->hal);
adc_hal_digi_deinit(&s_adc_digi_ctx->hal);
adc_hal_digi_deinit(&s_adc_digi_ctx->hal);
#if CONFIG_PM_ENABLE
if (s_adc_digi_ctx->pm_lock) {
esp_pm_lock_release(s_adc_digi_ctx->pm_lock);
}
if (s_adc_digi_ctx->pm_lock) {
esp_pm_lock_release(s_adc_digi_ctx->pm_lock);
}
#endif //CONFIG_PM_ENABLE
if (s_adc_digi_ctx->use_adc1) {
SAR_ADC1_LOCK_RELEASE();
}
if (s_adc_digi_ctx->use_adc2) {
SAR_ADC2_LOCK_RELEASE();
}
adc_power_release();
if (s_adc_digi_ctx->use_adc2) {
adc_lock_release(ADC_UNIT_2);
}
#if CONFIG_IDF_TARGET_ESP32S2
else {
//For being compatible with the deprecated behaviour
ESP_LOGE(ADC_TAG, "API used without driver initialization before. The following behaviour is deprecated!!");
#ifdef CONFIG_PM_ENABLE
if (adc_digi_arbiter_lock) {
esp_pm_lock_release(adc_digi_arbiter_lock);
}
#endif
ADC_ENTER_CRITICAL();
adc_ll_digi_trigger_disable();
adc_ll_digi_dma_disable();
ADC_EXIT_CRITICAL();
if (s_adc_digi_ctx->use_adc1) {
adc_lock_release(ADC_UNIT_1);
}
#endif //#if CONFIG_IDF_TARGET_ESP32S2
adc_power_release();
return ESP_OK;
}
@ -507,50 +503,6 @@ esp_err_t adc_digi_read_bytes(uint8_t *buf, uint32_t length_max, uint32_t *out_l
return ret;
}
esp_err_t adc_digi_deinitialize(void)
{
if (!s_adc_digi_ctx) {
return ESP_ERR_INVALID_STATE;
}
if (s_adc_digi_ctx->driver_start_flag != 0) {
ESP_LOGE(ADC_TAG, "The driver is not stopped");
return ESP_ERR_INVALID_STATE;
}
if (s_adc_digi_ctx->ringbuf_hdl) {
vRingbufferDelete(s_adc_digi_ctx->ringbuf_hdl);
s_adc_digi_ctx->ringbuf_hdl = NULL;
}
#if CONFIG_PM_ENABLE
if (s_adc_digi_ctx->pm_lock) {
esp_pm_lock_delete(s_adc_digi_ctx->pm_lock);
}
#endif //CONFIG_PM_ENABLE
free(s_adc_digi_ctx->rx_dma_buf);
free(s_adc_digi_ctx->hal.rx_desc);
free(s_adc_digi_ctx->hal_digi_ctrlr_cfg.adc_pattern);
#if SOC_GDMA_SUPPORTED
gdma_disconnect(s_adc_digi_ctx->rx_dma_channel);
gdma_del_channel(s_adc_digi_ctx->rx_dma_channel);
#elif CONFIG_IDF_TARGET_ESP32S2
esp_intr_free(s_adc_digi_ctx->intr_hdl);
spicommon_dma_chan_free(s_adc_digi_ctx->spi_host);
spicommon_periph_free(s_adc_digi_ctx->spi_host);
#elif CONFIG_IDF_TARGET_ESP32
esp_intr_free(s_adc_digi_ctx->intr_hdl);
i2s_platform_release_occupation(s_adc_digi_ctx->i2s_host);
#endif
free(s_adc_digi_ctx);
s_adc_digi_ctx = NULL;
periph_module_disable(PERIPH_SARADC_MODULE);
return ESP_OK;
}
/*---------------------------------------------------------------
Digital controller setting
---------------------------------------------------------------*/
@ -573,9 +525,6 @@ esp_err_t adc_digi_controller_configure(const adc_digi_configuration_t *config)
}
#endif
ESP_RETURN_ON_FALSE(config->sample_freq_hz <= SOC_ADC_SAMPLE_FREQ_THRES_HIGH && config->sample_freq_hz >= SOC_ADC_SAMPLE_FREQ_THRES_LOW, ESP_ERR_INVALID_ARG, ADC_TAG, "ADC sampling frequency out of range");
#if CONFIG_IDF_TARGET_ESP32
ESP_RETURN_ON_FALSE(config->conv_limit_en == 1, ESP_ERR_INVALID_ARG, ADC_TAG, "`conv_limit_en` should be set to 1");
#endif
#if CONFIG_IDF_TARGET_ESP32
ESP_RETURN_ON_FALSE(config->format == ADC_DIGI_OUTPUT_FORMAT_TYPE1, ESP_ERR_INVALID_ARG, ADC_TAG, "Please use type1");
@ -589,8 +538,6 @@ esp_err_t adc_digi_controller_configure(const adc_digi_configuration_t *config)
ESP_RETURN_ON_FALSE(config->format == ADC_DIGI_OUTPUT_FORMAT_TYPE2, ESP_ERR_INVALID_ARG, ADC_TAG, "Please use type2");
#endif
s_adc_digi_ctx->hal_digi_ctrlr_cfg.conv_limit_en = config->conv_limit_en;
s_adc_digi_ctx->hal_digi_ctrlr_cfg.conv_limit_num = config->conv_limit_num;
s_adc_digi_ctx->hal_digi_ctrlr_cfg.adc_pattern_len = config->pattern_num;
s_adc_digi_ctx->hal_digi_ctrlr_cfg.sample_freq_hz = config->sample_freq_hz;
s_adc_digi_ctx->hal_digi_ctrlr_cfg.conv_mode = config->conv_mode;
@ -626,319 +573,18 @@ esp_err_t adc_digi_controller_configure(const adc_digi_configuration_t *config)
return ESP_OK;
}
#if CONFIG_IDF_TARGET_ESP32C3
/*---------------------------------------------------------------
ADC Single Read Mode
---------------------------------------------------------------*/
static adc_atten_t s_atten1_single[ADC1_CHANNEL_MAX]; //Array saving attenuate of each channel of ADC1, used by single read API
static adc_atten_t s_atten2_single[ADC2_CHANNEL_MAX]; //Array saving attenuate of each channel of ADC2, used by single read API
esp_err_t adc_vref_to_gpio(adc_unit_t adc_unit, gpio_num_t gpio)
/**
* @brief This function will be called during start up, to check that adc_continuous driver is not running along with the legacy adc_continuous driver
*/
__attribute__((constructor))
static void check_adc_continuous_driver_conflict(void)
{
esp_err_t ret;
uint32_t channel = ADC2_CHANNEL_MAX;
if (adc_unit == ADC_UNIT_2) {
for (int i = 0; i < ADC2_CHANNEL_MAX; i++) {
if (gpio == ADC_GET_IO_NUM(ADC_UNIT_2, i)) {
channel = i;
break;
}
}
if (channel == ADC2_CHANNEL_MAX) {
return ESP_ERR_INVALID_ARG;
}
// This function was declared as weak here. adc_continuous driver has one implementation.
// So if adc_continuous driver is not linked in, then `adc_continuous_new_handle` should be NULL at runtime.
extern __attribute__((weak)) esp_err_t adc_continuous_new_handle(const void *init_config, void **ret_handle);
if ((void *)adc_continuous_new_handle != NULL) {
ESP_EARLY_LOGE(ADC_TAG, "CONFLICT! driver_ng is not allowed to be used with the legacy driver");
abort();
}
adc_power_acquire();
if (adc_unit == ADC_UNIT_1) {
ADC_ENTER_CRITICAL();
adc_hal_vref_output(ADC_UNIT_1, channel, true);
ADC_EXIT_CRITICAL();
} else { //ADC_UNIT_2
ADC_ENTER_CRITICAL();
adc_hal_vref_output(ADC_UNIT_2, channel, true);
ADC_EXIT_CRITICAL();
}
ret = adc_digi_gpio_init(ADC_UNIT_2, BIT(channel));
return ret;
ESP_EARLY_LOGW(ADC_TAG, "legacy driver is deprecated, please migrate to `esp_adc/adc_continuous.h`");
}
esp_err_t adc1_config_width(adc_bits_width_t width_bit)
{
//On ESP32C3, the data width is always 12-bits.
if (width_bit != ADC_WIDTH_BIT_12) {
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
esp_err_t adc1_config_channel_atten(adc1_channel_t channel, adc_atten_t atten)
{
ESP_RETURN_ON_FALSE(channel < SOC_ADC_CHANNEL_NUM(ADC_UNIT_1), ESP_ERR_INVALID_ARG, ADC_TAG, "ADC1 channel error");
ESP_RETURN_ON_FALSE((atten < SOC_ADC_ATTEN_NUM), ESP_ERR_INVALID_ARG, ADC_TAG, "ADC Atten Err");
esp_err_t ret = ESP_OK;
s_atten1_single[channel] = atten;
ret = adc_digi_gpio_init(ADC_UNIT_1, BIT(channel));
adc_hal_calibration_init(ADC_UNIT_1);
return ret;
}
int adc1_get_raw(adc1_channel_t channel)
{
int raw_out = 0;
periph_module_enable(PERIPH_SARADC_MODULE);
adc_power_acquire();
SAR_ADC1_LOCK_ACQUIRE();
adc_atten_t atten = s_atten1_single[channel];
uint32_t cal_val = adc_get_calibration_offset(ADC_UNIT_1, atten);
adc_hal_set_calibration_param(ADC_UNIT_1, cal_val);
ADC_REG_LOCK_ENTER();
adc_oneshot_ll_set_atten(ADC_UNIT_2, channel, atten);
adc_hal_convert(ADC_UNIT_1, channel, &raw_out);
ADC_REG_LOCK_EXIT();
SAR_ADC1_LOCK_RELEASE();
adc_power_release();
periph_module_disable(PERIPH_SARADC_MODULE);
return raw_out;
}
esp_err_t adc2_config_channel_atten(adc2_channel_t channel, adc_atten_t atten)
{
ESP_RETURN_ON_FALSE(channel < SOC_ADC_CHANNEL_NUM(ADC_UNIT_2), ESP_ERR_INVALID_ARG, ADC_TAG, "ADC2 channel error");
ESP_RETURN_ON_FALSE((atten <= ADC_ATTEN_11db), ESP_ERR_INVALID_ARG, ADC_TAG, "ADC2 Atten Err");
esp_err_t ret = ESP_OK;
s_atten2_single[channel] = atten;
ret = adc_digi_gpio_init(ADC_UNIT_2, BIT(channel));
adc_hal_calibration_init(ADC_UNIT_2);
return ret;
}
esp_err_t adc2_get_raw(adc2_channel_t channel, adc_bits_width_t width_bit, int *raw_out)
{
//On ESP32C3, the data width is always 12-bits.
if (width_bit != ADC_WIDTH_BIT_12) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t ret = ESP_OK;
periph_module_enable(PERIPH_SARADC_MODULE);
adc_power_acquire();
SAR_ADC2_LOCK_ACQUIRE();
adc_arbiter_t config = ADC_ARBITER_CONFIG_DEFAULT();
adc_hal_arbiter_config(&config);
adc_atten_t atten = s_atten2_single[channel];
uint32_t cal_val = adc_get_calibration_offset(ADC_UNIT_2, atten);
adc_hal_set_calibration_param(ADC_UNIT_2, cal_val);
ADC_REG_LOCK_ENTER();
adc_oneshot_ll_set_atten(ADC_UNIT_2, channel, atten);
ret = adc_hal_convert(ADC_UNIT_2, channel, raw_out);
ADC_REG_LOCK_EXIT();
SAR_ADC2_LOCK_RELEASE();
adc_power_release();
periph_module_disable(PERIPH_SARADC_MODULE);
return ret;
}
/*************************************/
/* Digital controller filter setting */
/*************************************/
esp_err_t adc_digi_filter_reset(adc_digi_filter_idx_t idx)
{
ADC_ENTER_CRITICAL();
adc_hal_digi_filter_reset(idx);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t adc_digi_filter_set_config(adc_digi_filter_idx_t idx, adc_digi_filter_t *config)
{
ADC_ENTER_CRITICAL();
adc_hal_digi_filter_set_factor(idx, config);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t adc_digi_filter_get_config(adc_digi_filter_idx_t idx, adc_digi_filter_t *config)
{
ADC_ENTER_CRITICAL();
adc_hal_digi_filter_get_factor(idx, config);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t adc_digi_filter_enable(adc_digi_filter_idx_t idx, bool enable)
{
ADC_ENTER_CRITICAL();
adc_hal_digi_filter_enable(idx, enable);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
/**************************************/
/* Digital controller monitor setting */
/**************************************/
esp_err_t adc_digi_monitor_set_config(adc_digi_monitor_idx_t idx, adc_digi_monitor_t *config)
{
ADC_ENTER_CRITICAL();
adc_hal_digi_monitor_config(idx, config);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t adc_digi_monitor_enable(adc_digi_monitor_idx_t idx, bool enable)
{
ADC_ENTER_CRITICAL();
adc_hal_digi_monitor_enable(idx, enable);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
#endif //#if CONFIG_IDF_TARGET_ESP32C3
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
/*---------------------------------------------------------------
Hardware Calibration Setting
---------------------------------------------------------------*/
#if CONFIG_IDF_TARGET_ESP32S2
#define esp_efuse_rtc_calib_get_ver() esp_efuse_rtc_table_read_calib_version()
static inline uint32_t esp_efuse_rtc_calib_get_init_code(int version, uint32_t adc_unit, int atten)
{
int tag = esp_efuse_rtc_table_get_tag(version, adc_unit + 1, atten, RTCCALIB_V2_PARAM_VINIT);
return esp_efuse_rtc_table_get_parsed_efuse_value(tag, false);
}
#endif
static uint16_t s_adc_cali_param[SOC_ADC_PERIPH_NUM][SOC_ADC_ATTEN_NUM] = {};
//NOTE: according to calibration version, different types of lock may be taken during the process:
// 1. Semaphore when reading efuse
// 2. Lock (Spinlock, or Mutex) if we actually do ADC calibration in the future
//This function shoudn't be called inside critical section or ISR
uint32_t adc_get_calibration_offset(adc_unit_t adc_n, adc_atten_t atten)
{
if (s_adc_cali_param[adc_n][atten]) {
ESP_LOGV(ADC_TAG, "Use calibrated val ADC%d atten=%d: %04X", adc_n, atten, s_adc_cali_param[adc_n][atten]);
return (uint32_t)s_adc_cali_param[adc_n][atten];
}
// check if we can fetch the values from eFuse.
int version = esp_efuse_rtc_calib_get_ver();
uint32_t init_code = 0;
if (version == ESP_EFUSE_ADC_CALIB_VER) {
init_code = esp_efuse_rtc_calib_get_init_code(version, adc_n, atten);
} else {
ESP_LOGD(ADC_TAG, "Calibration eFuse is not configured, use self-calibration for ICode");
adc_power_acquire();
ADC_ENTER_CRITICAL();
const bool internal_gnd = true;
init_code = adc_hal_self_calibration(adc_n, atten, internal_gnd);
ADC_EXIT_CRITICAL();
adc_power_release();
}
s_adc_cali_param[adc_n][atten] = init_code;
ESP_LOGV(ADC_TAG, "Calib(V%d) ADC%d atten=%d: %04X", version, adc_n, atten, init_code);
return init_code;
}
// Internal function to calibrate PWDET for WiFi
esp_err_t adc_cal_offset(adc_unit_t adc_n, adc_atten_t atten)
{
adc_hal_calibration_init(adc_n);
uint32_t cal_val = adc_get_calibration_offset(adc_n, atten);
ADC_ENTER_CRITICAL();
adc_hal_set_calibration_param(adc_n, cal_val);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
#endif //#if SOC_ADC_CALIBRATION_V1_SUPPORTED
/*---------------------------------------------------------------
Deprecated API
---------------------------------------------------------------*/
#if CONFIG_IDF_TARGET_ESP32C3
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#include "deprecated/driver/adc_deprecated.h"
#include "deprecated/driver/adc_types_deprecated.h"
esp_err_t adc_digi_controller_config(const adc_digi_config_t *config)
{
if (!s_adc_digi_ctx) {
return ESP_ERR_INVALID_STATE;
}
ESP_RETURN_ON_FALSE((config->sample_freq_hz <= SOC_ADC_SAMPLE_FREQ_THRES_HIGH && config->sample_freq_hz >= SOC_ADC_SAMPLE_FREQ_THRES_LOW), ESP_ERR_INVALID_ARG, ADC_TAG, "DC sampling frequency out of range");
s_adc_digi_ctx->hal_digi_ctrlr_cfg.conv_limit_en = config->conv_limit_en;
s_adc_digi_ctx->hal_digi_ctrlr_cfg.conv_limit_num = config->conv_limit_num;
s_adc_digi_ctx->hal_digi_ctrlr_cfg.adc_pattern_len = config->adc_pattern_len;
s_adc_digi_ctx->hal_digi_ctrlr_cfg.sample_freq_hz = config->sample_freq_hz;
for (int i = 0; i < config->adc_pattern_len; i++) {
s_adc_digi_ctx->hal_digi_ctrlr_cfg.adc_pattern[i].atten = config->adc_pattern[i].atten;
s_adc_digi_ctx->hal_digi_ctrlr_cfg.adc_pattern[i].channel = config->adc_pattern[i].channel;
s_adc_digi_ctx->hal_digi_ctrlr_cfg.adc_pattern[i].unit = config->adc_pattern[i].unit;
}
const int atten_uninitialized = 999;
s_adc_digi_ctx->adc1_atten = atten_uninitialized;
s_adc_digi_ctx->adc2_atten = atten_uninitialized;
s_adc_digi_ctx->use_adc1 = 0;
s_adc_digi_ctx->use_adc2 = 0;
for (int i = 0; i < config->adc_pattern_len; i++) {
const adc_digi_pattern_config_t *pat = &s_adc_digi_ctx->hal_digi_ctrlr_cfg.adc_pattern[i];
if (pat->unit == ADC_UNIT_1) {
s_adc_digi_ctx->use_adc1 = 1;
if (s_adc_digi_ctx->adc1_atten == atten_uninitialized) {
s_adc_digi_ctx->adc1_atten = pat->atten;
} else if (s_adc_digi_ctx->adc1_atten != pat->atten) {
return ESP_ERR_INVALID_ARG;
}
} else if (pat->unit == ADC_UNIT_2) {
//See whether ADC2 will be used or not. If yes, the ``sar_adc2_mutex`` should be acquired in the continuous read driver
s_adc_digi_ctx->use_adc2 = 1;
if (s_adc_digi_ctx->adc2_atten == atten_uninitialized) {
s_adc_digi_ctx->adc2_atten = pat->atten;
} else if (s_adc_digi_ctx->adc2_atten != pat->atten) {
return ESP_ERR_INVALID_ARG;
}
}
}
return ESP_OK;
}
#endif //#if CONFIG_IDF_TARGET_ESP32C3

View File

@ -0,0 +1,252 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*----------------------------------------------------------------------------------
This file contains ESP32 and ESP32S2 Depricated ADC APIs and functions
-----------------------------------------------------------------------------------*/
#include "sdkconfig.h"
#include "esp_types.h"
#include "esp_log.h"
#include "esp_intr_alloc.h"
#include "driver/rtc_io.h"
#include "hal/adc_hal.h"
#include "hal/adc_ll.h"
#include "hal/adc_types.h"
#include "hal/adc_hal_conf.h"
#ifdef CONFIG_PM_ENABLE
#include "esp_pm.h"
#endif
#include "esp_private/adc_private.h"
#include "freertos/FreeRTOS.h"
#include "driver/adc_i2s_legacy.h"
#include "driver/adc_types_legacy.h"
static __attribute__((unused)) const char *ADC_TAG = "ADC";
#define ADC_CHECK_RET(fun_ret) ({ \
if (fun_ret != ESP_OK) { \
ESP_LOGE(ADC_TAG,"%s:%d\n",__FUNCTION__,__LINE__); \
return ESP_FAIL; \
} \
})
#define ADC_CHECK(a, str, ret_val) ({ \
if (!(a)) { \
ESP_LOGE(ADC_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
return (ret_val); \
} \
})
#define ADC_CHANNEL_CHECK(periph, channel) ADC_CHECK(channel < SOC_ADC_CHANNEL_NUM(periph), "ADC"#periph" channel error", ESP_ERR_INVALID_ARG)
#define ADC_GET_IO_NUM(periph, channel) (adc_channel_io_map[periph][channel])
extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate position after the rtc module is finished.
#define ADC_ENTER_CRITICAL() portENTER_CRITICAL(&rtc_spinlock)
#define ADC_EXIT_CRITICAL() portEXIT_CRITICAL(&rtc_spinlock)
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_handle_t adc_digi_arbiter_lock = NULL;
#endif //CONFIG_PM_ENABLE
#if CONFIG_IDF_TARGET_ESP32
/*---------------------------------------------------------------
ESP32 Depricated ADC APIs and functions
---------------------------------------------------------------*/
#define DIG_ADC_OUTPUT_FORMAT_DEFUALT (ADC_DIGI_FORMAT_12BIT)
#define DIG_ADC_ATTEN_DEFUALT (ADC_ATTEN_DB_11)
#define DIG_ADC_BIT_WIDTH_DEFUALT (3) //3 for ADC_WIDTH_BIT_12
/**
* @brief ADC digital controller (DMA mode) conversion rules setting.
*/
typedef struct {
union {
struct {
uint8_t atten: 2; /*!< ADC sampling voltage attenuation configuration. Modification of attenuation affects the range of measurements.
0: measurement range 0 - 800mV,
1: measurement range 0 - 1100mV,
2: measurement range 0 - 1350mV,
3: measurement range 0 - 2600mV. */
uint8_t bit_width: 2; /*!< ADC resolution.
- 0: 9 bit;
- 1: 10 bit;
- 2: 11 bit;
- 3: 12 bit. */
int8_t channel: 4; /*!< ADC channel index. */
};
uint8_t val; /*!<Raw data value */
};
} adc_digi_pattern_table_t;
/**
* @brief ADC digital controller (DMA mode) output data format option.
*/
typedef enum {
ADC_DIGI_FORMAT_12BIT, /*!<ADC to DMA data format, [15:12]-channel, [11: 0]-12 bits ADC data (`adc_digi_output_data_t`). Note: For single convert mode. */
ADC_DIGI_FORMAT_11BIT, /*!<ADC to DMA data format, [15]-adc unit, [14:11]-channel, [10: 0]-11 bits ADC data (`adc_digi_output_data_t`). Note: For multi or alter convert mode. */
ADC_DIGI_FORMAT_MAX,
} adc_digi_format_t;
/**
* Explanation of the relationship between `conv_limit_num`, `dma_eof_num` and the number of DMA outputs:
*
* +---------------------+--------+--------+--------+
* | conv_mode | single | both | alter |
* +---------------------+--------+--------+--------+
* | trigger meas times | 1 | 1 | 1 |
* +---------------------+--------+--------+--------+
* | conv_limit_num | +1 | +1 | +1 |
* | dma_eof_num | +1 | +2 | +1 |
* | dma output (byte) | +2 | +4 | +2 |
* +---------------------+--------+--------+--------+
*/
typedef struct {
uint32_t adc1_pattern_len; /*!<Pattern table length for digital controller. Range: 0 ~ 16 (0: Don't change the pattern table setting).
The pattern table that defines the conversion rules for each SAR ADC. Each table has 16 items, in which channel selection,
resolution and attenuation are stored. When the conversion is started, the controller reads conversion rules from the
pattern table one by one. For each controller the scan sequence has at most 16 different rules before repeating itself. */
uint32_t adc2_pattern_len; /*!<Refer to ``adc1_pattern_len`` */
adc_digi_pattern_table_t *adc1_pattern; /*!<Pointer to pattern table for digital controller. The table size defined by `adc1_pattern_len`. */
adc_digi_pattern_table_t *adc2_pattern; /*!<Refer to `adc1_pattern` */
adc_digi_convert_mode_t conv_mode; /*!<ADC conversion mode for digital controller. See ``adc_digi_convert_mode_t``. */
adc_digi_format_t format; /*!<ADC output data format for digital controller. See ``adc_digi_format_t``. */
} adc_digi_config_t;
/**
* Set adc output 16-bit-data format from digital controller.
*
* @param data_sel 1: [15] unit, [14:11] channel, [10:0] data, 11-bit-width at most. Only work under `ADC_LL_DIGI_CONV_BOTH_UNIT` or `ADC_LL_DIGI_CONV_ALTER_UNIT` mode.
* 0: [15:12] channel, [11:0] data, 12-bit-width at most. Only work under `ADC_LL_DIGI_CONV_ONLY_ADC1` or `ADC_LL_DIGI_CONV_ONLY_ADC2` mode
* @note see `adc_ll_digi_pattern_table_t` for more detail of data bit width
*/
static inline void adc_ll_digi_set_output_format(bool data_sel)
{
SYSCON.saradc_ctrl.data_sar_sel = data_sel;
}
static inline void adc_ll_digi_prepare_pattern_table(adc_unit_t adc_n, uint32_t pattern_index, adc_digi_pattern_table_t pattern)
{
uint32_t tab;
uint8_t index = pattern_index / 4;
uint8_t offset = (pattern_index % 4) * 8;
if (adc_n == ADC_UNIT_1) {
tab = SYSCON.saradc_sar1_patt_tab[index]; // Read old register value
tab &= (~(0xFF000000 >> offset)); // clear old data
tab |= ((uint32_t)pattern.val << 24) >> offset; // Fill in the new data
SYSCON.saradc_sar1_patt_tab[index] = tab; // Write back
} else { // adc_n == ADC_UNIT_2
tab = SYSCON.saradc_sar2_patt_tab[index]; // Read old register value
tab &= (~(0xFF000000 >> offset)); // clear old data
tab |= ((uint32_t)pattern.val << 24) >> offset; // Fill in the new data
SYSCON.saradc_sar2_patt_tab[index] = tab; // Write back
}
}
static void adc_digi_controller_reg_set(const adc_digi_config_t *cfg)
{
/* On ESP32, only support ADC1 */
switch (cfg->conv_mode) {
case ADC_CONV_SINGLE_UNIT_1:
adc_ll_digi_set_convert_mode(ADC_LL_DIGI_CONV_ONLY_ADC1);
break;
case ADC_CONV_SINGLE_UNIT_2:
adc_ll_digi_set_convert_mode(ADC_LL_DIGI_CONV_ONLY_ADC2);
break;
case ADC_CONV_BOTH_UNIT:
adc_ll_digi_set_convert_mode(ADC_LL_DIGI_CONV_BOTH_UNIT);
break;
case ADC_CONV_ALTER_UNIT:
adc_ll_digi_set_convert_mode(ADC_LL_DIGI_CONV_ALTER_UNIT);
break;
default:
abort();
}
if (cfg->conv_mode & ADC_CONV_SINGLE_UNIT_1) {
adc_ll_set_controller(ADC_UNIT_1, ADC_LL_CTRL_DIG);
if (cfg->adc1_pattern_len) {
adc_ll_digi_clear_pattern_table(ADC_UNIT_1);
adc_ll_digi_set_pattern_table_len(ADC_UNIT_1, cfg->adc1_pattern_len);
for (uint32_t i = 0; i < cfg->adc1_pattern_len; i++) {
adc_ll_digi_prepare_pattern_table(ADC_UNIT_1, i, cfg->adc1_pattern[i]);
}
}
}
if (cfg->conv_mode & ADC_CONV_SINGLE_UNIT_2) {
adc_ll_set_controller(ADC_UNIT_2, ADC_LL_CTRL_DIG);
if (cfg->adc2_pattern_len) {
adc_ll_digi_clear_pattern_table(ADC_UNIT_2);
adc_ll_digi_set_pattern_table_len(ADC_UNIT_2, cfg->adc2_pattern_len);
for (uint32_t i = 0; i < cfg->adc2_pattern_len; i++) {
adc_ll_digi_prepare_pattern_table(ADC_UNIT_2, i, cfg->adc2_pattern[i]);
}
}
}
adc_ll_digi_set_output_format(cfg->format);
adc_ll_digi_convert_limit_enable(ADC_LL_DEFAULT_CONV_LIMIT_EN);
adc_ll_digi_set_convert_limit_num(ADC_LL_DEFAULT_CONV_LIMIT_NUM);
adc_ll_digi_set_data_source(ADC_I2S_DATA_SRC_ADC);
}
esp_err_t adc_set_i2s_data_source(adc_i2s_source_t src)
{
ADC_CHECK((src == ADC_I2S_DATA_SRC_IO_SIG || src == ADC_I2S_DATA_SRC_ADC), "ADC i2s data source error", ESP_ERR_INVALID_ARG);
ADC_ENTER_CRITICAL();
adc_ll_digi_set_data_source(src);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
extern esp_err_t adc_common_gpio_init(adc_unit_t adc_unit, adc_channel_t channel);
esp_err_t adc_i2s_mode_init(adc_unit_t adc_unit, adc_channel_t channel)
{
if (adc_unit == ADC_UNIT_1) {
ADC_CHANNEL_CHECK(ADC_UNIT_1, channel);
} else if (adc_unit == ADC_UNIT_2) {
//ADC2 does not support DMA mode
ADC_CHECK(false, "ADC2 not support DMA for now.", ESP_ERR_INVALID_ARG);
ADC_CHANNEL_CHECK(ADC_UNIT_2, channel);
}
adc_digi_pattern_table_t adc1_pattern[1];
adc_digi_pattern_table_t adc2_pattern[1];
adc_digi_config_t dig_cfg = {
.format = DIG_ADC_OUTPUT_FORMAT_DEFUALT,
.conv_mode = ADC_CONV_SINGLE_UNIT_1,
};
if (adc_unit == ADC_UNIT_1) {
adc1_pattern[0].atten = DIG_ADC_ATTEN_DEFUALT;
adc1_pattern[0].bit_width = DIG_ADC_BIT_WIDTH_DEFUALT;
adc1_pattern[0].channel = channel;
dig_cfg.adc1_pattern_len = 1;
dig_cfg.adc1_pattern = adc1_pattern;
} else if (adc_unit == ADC_UNIT_2) {
adc2_pattern[0].atten = DIG_ADC_ATTEN_DEFUALT;
adc2_pattern[0].bit_width = DIG_ADC_BIT_WIDTH_DEFUALT;
adc2_pattern[0].channel = channel;
dig_cfg.adc2_pattern_len = 1;
dig_cfg.adc2_pattern = adc2_pattern;
}
adc_common_gpio_init(adc_unit, channel);
ADC_ENTER_CRITICAL();
adc_ll_digi_set_fsm_time(ADC_HAL_FSM_RSTB_WAIT_DEFAULT, ADC_HAL_FSM_START_WAIT_DEFAULT,
ADC_HAL_FSM_STANDBY_WAIT_DEFAULT);
adc_ll_set_sample_cycle(ADC_HAL_SAMPLE_CYCLE_DEFAULT);
adc_hal_pwdet_set_cct(ADC_HAL_PWDET_CCT_DEFAULT);
adc_ll_digi_output_invert(ADC_UNIT_1, ADC_HAL_DIGI_DATA_INVERT_DEFAULT(ADC_UNIT_1));
adc_ll_digi_output_invert(ADC_UNIT_2, ADC_HAL_DIGI_DATA_INVERT_DEFAULT(ADC_UNIT_2));
adc_ll_digi_set_clk_div(ADC_HAL_DIGI_SAR_CLK_DIV_DEFAULT);
adc_digi_controller_reg_set(&dig_cfg);
ADC_EXIT_CRITICAL();
return ESP_OK;
}
#endif //#if CONFIG_IDF_TARGET_ESP32

View File

@ -8,6 +8,7 @@
#include <stdlib.h>
#include <ctype.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/timers.h"
@ -17,11 +18,14 @@
#include "driver/rtc_io.h"
#include "sys/lock.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "esp_private/adc_private.h"
#include "adc1_private.h"
#include "hal/adc_types.h"
#include "hal/adc_hal.h"
#include "hal/adc_hal_conf.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/adc_lock.h"
#include "driver/adc_types_legacy.h"
#if SOC_DAC_SUPPORTED
#include "driver/dac.h"
@ -79,7 +83,6 @@ extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate posi
#define FSM_ENTER() RTC_ENTER_CRITICAL()
#define FSM_EXIT() RTC_EXIT_CRITICAL()
//TODO: IDF-3610
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
//prevent ADC1 being used by I2S dma and other tasks at the same time.
static _lock_t adc1_dma_lock;
@ -102,97 +105,20 @@ In ADC2, there're two locks used for different cases:
Since conversions are short (about 31us), app returns the lock very soon,
we use a spinlock to stand there waiting to do conversions one by one.
adc2_spinlock should be acquired first, then adc2_wifi_lock or rtc_spinlock.
adc2_spinlock should be acquired first, then call `adc_lock_release(ADC_UNIT_2)` or rtc_spinlock.
*/
#ifdef CONFIG_IDF_TARGET_ESP32
//prevent ADC2 being used by wifi and other tasks at the same time.
static _lock_t adc2_wifi_lock;
/** For ESP32S2 the ADC2 The right to use ADC2 is controlled by the arbiter, and there is no need to set a lock. */
#define SARADC2_ACQUIRE() _lock_acquire( &adc2_wifi_lock )
#define SARADC2_RELEASE() _lock_release( &adc2_wifi_lock )
#define SARADC2_TRY_ACQUIRE() _lock_try_acquire( &adc2_wifi_lock )
#define SARADC2_LOCK_CHECK() ((uint32_t *)adc2_wifi_lock != NULL)
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#define SARADC2_ACQUIRE()
#define SARADC2_RELEASE()
#define SARADC2_TRY_ACQUIRE() (0) //WIFI controller and rtc controller have independent parameter configuration.
#define SARADC2_LOCK_CHECK() (true)
#endif // CONFIG_IDF_TARGET_*
#if CONFIG_IDF_TARGET_ESP32S2
#ifdef CONFIG_PM_ENABLE
static esp_pm_lock_handle_t s_adc2_arbiter_lock;
#endif //CONFIG_PM_ENABLE
#endif // !CONFIG_IDF_TARGET_ESP32
static esp_err_t adc_hal_convert(adc_unit_t adc_n, int channel, int *out_raw);
/*---------------------------------------------------------------
ADC Common
---------------------------------------------------------------*/
// ADC Power
// This gets incremented when adc_power_acquire() is called, and decremented when
// adc_power_release() is called. ADC is powered down when the value reaches zero.
// Should be modified within critical section (ADC_ENTER/EXIT_CRITICAL).
static int s_adc_power_on_cnt;
static void adc_power_on_internal(void)
{
/* Set the power always on to increase precision. */
adc_hal_set_power_manage(ADC_POWER_SW_ON);
}
void adc_power_acquire(void)
{
ADC_POWER_ENTER();
s_adc_power_on_cnt++;
if (s_adc_power_on_cnt == 1) {
adc_power_on_internal();
}
ADC_POWER_EXIT();
}
void adc_power_on(void)
{
ADC_POWER_ENTER();
adc_power_on_internal();
ADC_POWER_EXIT();
}
static void adc_power_off_internal(void)
{
#if CONFIG_IDF_TARGET_ESP32
adc_hal_set_power_manage(ADC_POWER_SW_OFF);
#else
adc_hal_set_power_manage(ADC_POWER_BY_FSM);
#endif
}
void adc_power_release(void)
{
ADC_POWER_ENTER();
s_adc_power_on_cnt--;
/* Sanity check */
if (s_adc_power_on_cnt < 0) {
ADC_POWER_EXIT();
ESP_LOGE(ADC_TAG, "%s called, but s_adc_power_on_cnt == 0", __func__);
abort();
} else if (s_adc_power_on_cnt == 0) {
adc_power_off_internal();
}
ADC_POWER_EXIT();
}
void adc_power_off(void)
{
ADC_POWER_ENTER();
adc_power_off_internal();
ADC_POWER_EXIT();
}
esp_err_t adc1_pad_get_io_num(adc1_channel_t channel, gpio_num_t *gpio_num)
{
ADC_CHANNEL_CHECK(ADC_UNIT_1, channel);
@ -207,6 +133,7 @@ esp_err_t adc1_pad_get_io_num(adc1_channel_t channel, gpio_num_t *gpio_num)
return ESP_OK;
}
#if (SOC_ADC_PERIPH_NUM >= 2)
esp_err_t adc2_pad_get_io_num(adc2_channel_t channel, gpio_num_t *gpio_num)
{
ADC_CHANNEL_CHECK(ADC_UNIT_2, channel);
@ -220,20 +147,10 @@ esp_err_t adc2_pad_get_io_num(adc2_channel_t channel, gpio_num_t *gpio_num)
return ESP_OK;
}
#endif
//------------------------------------------------------------RTC Single Read----------------------------------------------//
#if SOC_ADC_RTC_CTRL_SUPPORTED
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
uint32_t get_calibration_offset(adc_unit_t adc_n, adc_channel_t chan)
{
adc_atten_t atten = adc_ll_get_atten(adc_n, chan);
extern uint32_t adc_get_calibration_offset(adc_unit_t adc_n, adc_atten_t atten);
return adc_get_calibration_offset(adc_n, atten);
}
#endif //SOC_ADC_CALIBRATION_V1_SUPPORTED
esp_err_t adc_set_clk_div(uint8_t clk_div)
{
DIGI_CONTROLLER_ENTER();
@ -264,11 +181,6 @@ static void adc_rtc_chan_init(adc_unit_t adc_unit)
}
}
/**
* This function is NOT an API.
* Now some to-be-deprecated APIs are using this function, so don't make it static for now.
* Will make this static on v5.0
*/
esp_err_t adc_common_gpio_init(adc_unit_t adc_unit, adc_channel_t channel)
{
gpio_num_t gpio_num = 0;
@ -314,21 +226,25 @@ esp_err_t adc_set_data_width(adc_unit_t adc_unit, adc_bits_width_t width_bit)
ADC_CHECK(width_bit < ADC_WIDTH_MAX, "unsupported bit width", ESP_ERR_INVALID_ARG);
adc_bitwidth_t bitwidth = 0;
#if CONFIG_IDF_TARGET_ESP32
switch(width_bit) {
case ADC_WIDTH_BIT_9:
bitwidth = ADC_BITWIDTH_9;
break;
case ADC_WIDTH_BIT_10:
bitwidth = ADC_BITWIDTH_10;
break;
case ADC_WIDTH_BIT_11:
bitwidth = ADC_BITWIDTH_11;
break;
case ADC_WIDTH_BIT_12:
bitwidth = ADC_BITWIDTH_12;
break;
default:
abort();
if ((uint32_t)width_bit == (uint32_t)ADC_BITWIDTH_DEFAULT) {
bitwidth = SOC_ADC_RTC_MAX_BITWIDTH;
} else {
switch(width_bit) {
case ADC_WIDTH_BIT_9:
bitwidth = ADC_BITWIDTH_9;
break;
case ADC_WIDTH_BIT_10:
bitwidth = ADC_BITWIDTH_10;
break;
case ADC_WIDTH_BIT_11:
bitwidth = ADC_BITWIDTH_11;
break;
case ADC_WIDTH_BIT_12:
bitwidth = ADC_BITWIDTH_12;
break;
default:
return ESP_ERR_INVALID_ARG;
}
}
#elif CONFIG_IDF_TARGET_ESP32S2
bitwidth = ADC_BITWIDTH_13;
@ -392,21 +308,25 @@ esp_err_t adc1_config_width(adc_bits_width_t width_bit)
ADC_CHECK(width_bit < ADC_WIDTH_MAX, "unsupported bit width", ESP_ERR_INVALID_ARG);
adc_bitwidth_t bitwidth = 0;
#if CONFIG_IDF_TARGET_ESP32
switch(width_bit) {
case ADC_WIDTH_BIT_9:
bitwidth = ADC_BITWIDTH_9;
break;
case ADC_WIDTH_BIT_10:
bitwidth = ADC_BITWIDTH_10;
break;
case ADC_WIDTH_BIT_11:
bitwidth = ADC_BITWIDTH_11;
break;
case ADC_WIDTH_BIT_12:
bitwidth = ADC_BITWIDTH_12;
break;
default:
abort();
if ((uint32_t)width_bit == (uint32_t)ADC_BITWIDTH_DEFAULT) {
bitwidth = SOC_ADC_RTC_MAX_BITWIDTH;
} else {
switch(width_bit) {
case ADC_WIDTH_BIT_9:
bitwidth = ADC_BITWIDTH_9;
break;
case ADC_WIDTH_BIT_10:
bitwidth = ADC_BITWIDTH_10;
break;
case ADC_WIDTH_BIT_11:
bitwidth = ADC_BITWIDTH_11;
break;
case ADC_WIDTH_BIT_12:
bitwidth = ADC_BITWIDTH_12;
break;
default:
return ESP_ERR_INVALID_ARG;
}
}
#elif CONFIG_IDF_TARGET_ESP32S2
bitwidth = ADC_BITWIDTH_13;
@ -470,9 +390,8 @@ int adc1_get_raw(adc1_channel_t channel)
adc1_rtc_mode_acquire();
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
// Get calibration value before going into critical section
uint32_t cal_val = get_calibration_offset(ADC_UNIT_1, channel);
adc_hal_set_calibration_param(ADC_UNIT_1, cal_val);
adc_atten_t atten = adc_ll_get_atten(ADC_UNIT_1, channel);
adc_set_hw_calibration_code(ADC_UNIT_1, atten);
#endif //SOC_ADC_CALIBRATION_V1_SUPPORTED
SARADC1_ENTER();
@ -515,27 +434,10 @@ void adc1_ulp_enable(void)
}
#endif
#if (SOC_ADC_PERIPH_NUM >= 2)
/*---------------------------------------------------------------
ADC2
---------------------------------------------------------------*/
/** For ESP32S2 the ADC2 The right to use ADC2 is controlled by the arbiter, and there is no need to set a lock.*/
esp_err_t adc2_wifi_acquire(void)
{
/* Wi-Fi module will use adc2. Use locks to avoid conflicts. */
SARADC2_ACQUIRE();
ESP_LOGD( ADC_TAG, "Wi-Fi takes adc2 lock." );
return ESP_OK;
}
esp_err_t adc2_wifi_release(void)
{
ADC_CHECK(SARADC2_LOCK_CHECK(), "wifi release called before acquire", ESP_ERR_INVALID_STATE );
SARADC2_RELEASE();
ESP_LOGD( ADC_TAG, "Wi-Fi returns adc2 lock." );
return ESP_OK;
}
esp_err_t adc2_config_channel_atten(adc2_channel_t channel, adc_atten_t atten)
{
ADC_CHANNEL_CHECK(ADC_UNIT_2, channel);
@ -543,10 +445,13 @@ esp_err_t adc2_config_channel_atten(adc2_channel_t channel, adc_atten_t atten)
adc_common_gpio_init(ADC_UNIT_2, channel);
if ( SARADC2_TRY_ACQUIRE() == -1 ) {
#if CONFIG_IDF_TARGET_ESP32
/** For ESP32S2 and S3, the right to use ADC2 is controlled by the arbiter, and there is no need to set a lock.*/
if (adc_lock_try_acquire(ADC_UNIT_2) != ESP_OK) {
//try the lock, return if failed (wifi using).
return ESP_ERR_TIMEOUT;
}
#endif
//avoid collision with other tasks
SARADC2_ENTER();
@ -554,7 +459,9 @@ esp_err_t adc2_config_channel_atten(adc2_channel_t channel, adc_atten_t atten)
adc_oneshot_ll_set_atten(ADC_UNIT_2, channel, atten);
SARADC2_EXIT();
SARADC2_RELEASE();
#if CONFIG_IDF_TARGET_ESP32
adc_lock_release(ADC_UNIT_2);
#endif
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
adc_hal_calibration_init(ADC_UNIT_2);
@ -610,21 +517,25 @@ esp_err_t adc2_get_raw(adc2_channel_t channel, adc_bits_width_t width_bit, int *
ADC_CHECK(channel < ADC2_CHANNEL_MAX, "ADC Channel Err", ESP_ERR_INVALID_ARG);
ADC_CHECK(width_bit < ADC_WIDTH_MAX, "unsupported bit width", ESP_ERR_INVALID_ARG);
#if CONFIG_IDF_TARGET_ESP32
switch(width_bit) {
case ADC_WIDTH_BIT_9:
bitwidth = ADC_BITWIDTH_9;
break;
case ADC_WIDTH_BIT_10:
bitwidth = ADC_BITWIDTH_10;
break;
case ADC_WIDTH_BIT_11:
bitwidth = ADC_BITWIDTH_11;
break;
case ADC_WIDTH_BIT_12:
bitwidth = ADC_BITWIDTH_12;
break;
default:
abort();
if ((uint32_t)width_bit == (uint32_t)ADC_BITWIDTH_DEFAULT) {
bitwidth = SOC_ADC_RTC_MAX_BITWIDTH;
} else {
switch(width_bit) {
case ADC_WIDTH_BIT_9:
bitwidth = ADC_BITWIDTH_9;
break;
case ADC_WIDTH_BIT_10:
bitwidth = ADC_BITWIDTH_10;
break;
case ADC_WIDTH_BIT_11:
bitwidth = ADC_BITWIDTH_11;
break;
case ADC_WIDTH_BIT_12:
bitwidth = ADC_BITWIDTH_12;
break;
default:
return ESP_ERR_INVALID_ARG;
}
}
#elif CONFIG_IDF_TARGET_ESP32S2
bitwidth = ADC_BITWIDTH_13;
@ -633,15 +544,17 @@ esp_err_t adc2_get_raw(adc2_channel_t channel, adc_bits_width_t width_bit, int *
#endif
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
// Get calibration value before going into critical section
uint32_t cal_val = get_calibration_offset(ADC_UNIT_2, channel);
adc_hal_set_calibration_param(ADC_UNIT_2, cal_val);
adc_atten_t atten = adc_ll_get_atten(ADC_UNIT_2, channel);
adc_set_hw_calibration_code(ADC_UNIT_2, atten);
#endif //SOC_ADC_CALIBRATION_V1_SUPPORTED
if ( SARADC2_TRY_ACQUIRE() == -1 ) {
#if CONFIG_IDF_TARGET_ESP32
/** For ESP32S2 and S3, the right to use ADC2 is controlled by the arbiter, and there is no need to set a lock.*/
if (adc_lock_try_acquire(ADC_UNIT_2) != ESP_OK) {
//try the lock, return if failed (wifi using).
return ESP_ERR_TIMEOUT;
}
#endif
adc_power_acquire(); //in critical section with whole rtc module
//avoid collision with other tasks
@ -689,17 +602,14 @@ esp_err_t adc2_get_raw(adc2_channel_t channel, adc_bits_width_t width_bit, int *
SARADC2_EXIT();
adc_power_release();
SARADC2_RELEASE();
#if CONFIG_IDF_TARGET_ESP32
adc_lock_release(ADC_UNIT_2);
#endif
*raw_out = adc_value;
return ret;
}
esp_err_t adc2_vref_to_gpio(gpio_num_t gpio)
{
return adc_vref_to_gpio(ADC_UNIT_2, gpio);
}
esp_err_t adc_vref_to_gpio(adc_unit_t adc_unit, gpio_num_t gpio)
{
#ifdef CONFIG_IDF_TARGET_ESP32
@ -736,3 +646,278 @@ esp_err_t adc_vref_to_gpio(adc_unit_t adc_unit, gpio_num_t gpio)
}
#endif //SOC_ADC_RTC_CTRL_SUPPORTED
#endif //#if (SOC_ADC_PERIPH_NUM >= 2)
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
/*---------------------------------------------------------------
Legacy ADC Single Read Mode
when RTC controller isn't supported
---------------------------------------------------------------*/
#include "esp_check.h"
portMUX_TYPE adc_reg_lock = portMUX_INITIALIZER_UNLOCKED;
#define ADC_REG_LOCK_ENTER() portENTER_CRITICAL(&adc_reg_lock)
#define ADC_REG_LOCK_EXIT() portEXIT_CRITICAL(&adc_reg_lock)
static adc_atten_t s_atten1_single[ADC1_CHANNEL_MAX]; //Array saving attenuate of each channel of ADC1, used by single read API
#if (SOC_ADC_PERIPH_NUM >= 2)
static adc_atten_t s_atten2_single[ADC2_CHANNEL_MAX]; //Array saving attenuate of each channel of ADC2, used by single read API
#endif
static int8_t adc_digi_get_io_num(adc_unit_t adc_unit, uint8_t adc_channel)
{
assert(adc_unit <= SOC_ADC_PERIPH_NUM);
uint8_t adc_n = (adc_unit == ADC_UNIT_1) ? 0 : 1;
return adc_channel_io_map[adc_n][adc_channel];
}
static esp_err_t adc_digi_gpio_init(adc_unit_t adc_unit, uint16_t channel_mask)
{
esp_err_t ret = ESP_OK;
uint64_t gpio_mask = 0;
uint32_t n = 0;
int8_t io = 0;
while (channel_mask) {
if (channel_mask & 0x1) {
io = adc_digi_get_io_num(adc_unit, n);
if (io < 0) {
return ESP_ERR_INVALID_ARG;
}
gpio_mask |= BIT64(io);
}
channel_mask = channel_mask >> 1;
n++;
}
gpio_config_t cfg = {
.pin_bit_mask = gpio_mask,
.mode = GPIO_MODE_DISABLE,
};
ret = gpio_config(&cfg);
return ret;
}
#if CONFIG_IDF_TARGET_ESP32C3
esp_err_t adc_vref_to_gpio(adc_unit_t adc_unit, gpio_num_t gpio)
{
esp_err_t ret;
uint32_t channel = ADC2_CHANNEL_MAX;
if (adc_unit == ADC_UNIT_2) {
for (int i = 0; i < ADC2_CHANNEL_MAX; i++) {
if (gpio == ADC_GET_IO_NUM(ADC_UNIT_2, i)) {
channel = i;
break;
}
}
if (channel == ADC2_CHANNEL_MAX) {
return ESP_ERR_INVALID_ARG;
}
}
adc_power_acquire();
if (adc_unit == ADC_UNIT_1) {
RTC_ENTER_CRITICAL();
adc_hal_vref_output(ADC_UNIT_1, channel, true);
RTC_EXIT_CRITICAL();
} else { //ADC_UNIT_2
RTC_ENTER_CRITICAL();
adc_hal_vref_output(ADC_UNIT_2, channel, true);
RTC_EXIT_CRITICAL();
}
ret = adc_digi_gpio_init(ADC_UNIT_2, BIT(channel));
return ret;
}
#endif
esp_err_t adc1_config_width(adc_bits_width_t width_bit)
{
//On ESP32C3, the data width is always 12-bits.
if (width_bit != ADC_WIDTH_BIT_12) {
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
esp_err_t adc1_config_channel_atten(adc1_channel_t channel, adc_atten_t atten)
{
ESP_RETURN_ON_FALSE(channel < SOC_ADC_CHANNEL_NUM(ADC_UNIT_1), ESP_ERR_INVALID_ARG, ADC_TAG, "ADC1 channel error");
ESP_RETURN_ON_FALSE((atten < SOC_ADC_ATTEN_NUM), ESP_ERR_INVALID_ARG, ADC_TAG, "ADC Atten Err");
esp_err_t ret = ESP_OK;
s_atten1_single[channel] = atten;
ret = adc_digi_gpio_init(ADC_UNIT_1, BIT(channel));
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
adc_hal_calibration_init(ADC_UNIT_1);
#endif
return ret;
}
int adc1_get_raw(adc1_channel_t channel)
{
int raw_out = 0;
if (adc_lock_try_acquire(ADC_UNIT_1) != ESP_OK) {
return ESP_ERR_TIMEOUT;
}
periph_module_enable(PERIPH_SARADC_MODULE);
adc_power_acquire();
adc_ll_digi_clk_sel(0);
adc_atten_t atten = s_atten1_single[channel];
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
adc_set_hw_calibration_code(ADC_UNIT_1, atten);
#endif
ADC_REG_LOCK_ENTER();
adc_oneshot_ll_set_atten(ADC_UNIT_2, channel, atten);
adc_hal_convert(ADC_UNIT_1, channel, &raw_out);
ADC_REG_LOCK_EXIT();
adc_power_release();
periph_module_disable(PERIPH_SARADC_MODULE);
adc_lock_release(ADC_UNIT_1);
return raw_out;
}
#if (SOC_ADC_PERIPH_NUM >= 2)
esp_err_t adc2_config_channel_atten(adc2_channel_t channel, adc_atten_t atten)
{
ESP_RETURN_ON_FALSE(channel < SOC_ADC_CHANNEL_NUM(ADC_UNIT_2), ESP_ERR_INVALID_ARG, ADC_TAG, "ADC2 channel error");
ESP_RETURN_ON_FALSE((atten <= ADC_ATTEN_DB_11), ESP_ERR_INVALID_ARG, ADC_TAG, "ADC2 Atten Err");
esp_err_t ret = ESP_OK;
s_atten2_single[channel] = atten;
ret = adc_digi_gpio_init(ADC_UNIT_2, BIT(channel));
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
adc_hal_calibration_init(ADC_UNIT_2);
#endif
return ret;
}
esp_err_t adc2_get_raw(adc2_channel_t channel, adc_bits_width_t width_bit, int *raw_out)
{
//On ESP32C3, the data width is always 12-bits.
if (width_bit != ADC_WIDTH_BIT_12) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t ret = ESP_OK;
if (adc_lock_try_acquire(ADC_UNIT_2) != ESP_OK) {
return ESP_ERR_TIMEOUT;
}
periph_module_enable(PERIPH_SARADC_MODULE);
adc_power_acquire();
adc_arbiter_t config = ADC_ARBITER_CONFIG_DEFAULT();
adc_hal_arbiter_config(&config);
adc_atten_t atten = s_atten2_single[channel];
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
adc_set_hw_calibration_code(ADC_UNIT_2, atten);
#endif
ADC_REG_LOCK_ENTER();
adc_oneshot_ll_set_atten(ADC_UNIT_2, channel, atten);
ret = adc_hal_convert(ADC_UNIT_2, channel, raw_out);
ADC_REG_LOCK_EXIT();
adc_power_release();
periph_module_disable(PERIPH_SARADC_MODULE);
adc_lock_release(ADC_UNIT_2);
return ret;
}
#endif //#if (SOC_ADC_PERIPH_NUM >= 2)
#endif //#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
static void adc_hal_onetime_start(adc_unit_t adc_n)
{
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
(void)adc_n;
/**
* There is a hardware limitation. If the APB clock frequency is high, the step of this reg signal: ``onetime_start`` may not be captured by the
* ADC digital controller (when its clock frequency is too slow). A rough estimate for this step should be at least 3 ADC digital controller
* clock cycle.
*
* This limitation will be removed in hardware future versions.
*
*/
uint32_t digi_clk = APB_CLK_FREQ / (ADC_LL_CLKM_DIV_NUM_DEFAULT + ADC_LL_CLKM_DIV_A_DEFAULT / ADC_LL_CLKM_DIV_B_DEFAULT + 1);
//Convert frequency to time (us). Since decimals are removed by this division operation. Add 1 here in case of the fact that delay is not enough.
uint32_t delay = (1000 * 1000) / digi_clk + 1;
//3 ADC digital controller clock cycle
delay = delay * 3;
//This coefficient (8) is got from test. When digi_clk is not smaller than ``APB_CLK_FREQ/8``, no delay is needed.
if (digi_clk >= APB_CLK_FREQ/8) {
delay = 0;
}
adc_oneshot_ll_start(false);
esp_rom_delay_us(delay);
adc_oneshot_ll_start(true);
//No need to delay here. Becuase if the start signal is not seen, there won't be a done intr.
#else
adc_oneshot_ll_start(adc_n);
#endif
}
static esp_err_t adc_hal_convert(adc_unit_t adc_n, int channel, int *out_raw)
{
uint32_t event = (adc_n == ADC_UNIT_1) ? ADC_LL_EVENT_ADC1_ONESHOT_DONE : ADC_LL_EVENT_ADC2_ONESHOT_DONE;
adc_oneshot_ll_clear_event(event);
adc_oneshot_ll_disable_all_unit();
adc_oneshot_ll_enable(adc_n);
adc_oneshot_ll_set_channel(adc_n, channel);
adc_hal_onetime_start(adc_n);
while (adc_oneshot_ll_get_event(event) != true) {
;
}
*out_raw = adc_oneshot_ll_get_raw_result(adc_n);
if (adc_oneshot_ll_raw_check_valid(adc_n, *out_raw) == false) {
return ESP_ERR_INVALID_STATE;
}
//HW workaround: when enabling periph clock, this should be false
adc_oneshot_ll_disable_all_unit();
return ESP_OK;
}
#if !CONFIG_IDF_TARGET_ESP32
//wrapper should be removed after I2S deprecation
/**
* @brief This function will be called during start up, to check that adc_oneshot driver is not running along with the legacy adc oneshot driver
*/
__attribute__((constructor))
static void check_adc_oneshot_driver_conflict(void)
{
// This function was declared as weak here. adc_oneshot driver has one implementation.
// So if adc_oneshot driver is not linked in, then `adc_oneshot_new_unit` should be NULL at runtime.
extern __attribute__((weak)) esp_err_t adc_oneshot_new_unit(const void *init_config, void **ret_unit);
if ((void *)adc_oneshot_new_unit != NULL) {
ESP_EARLY_LOGE(ADC_TAG, "CONFLICT! driver_ng is not allowed to be used with the legacy driver");
abort();
}
ESP_EARLY_LOGW(ADC_TAG, "legacy driver is deprecated, please migrate to `esp_adc/adc_oneshot.h`");
}
#endif

View File

@ -1,152 +1,31 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
/*----------------------------------------------------------------------------------
This file contains Deprecated ADC APIs
-----------------------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#pragma once
#include "sdkconfig.h"
#include "esp_err.h"
#include "driver/gpio.h"
#include "driver/adc_types_legacy.h"
#include "hal/adc_types.h"
#if !CONFIG_ADC_SUPPRESS_DEPRECATE_WARN
#warning "legacy adc driver is deprecated, please migrate to use esp_adc/adc_oneshot.h and esp_adc/adc_continuous.h for oneshot mode and continuous mode drivers respectively"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if CONFIG_IDF_TARGET_ESP32
/**** `adc1_channel_t` will be deprecated functions, combine into `adc_channel_t` ********/
typedef enum {
ADC1_CHANNEL_0 = 0, /*!< ADC1 channel 0 is GPIO36 */
ADC1_CHANNEL_1, /*!< ADC1 channel 1 is GPIO37 */
ADC1_CHANNEL_2, /*!< ADC1 channel 2 is GPIO38 */
ADC1_CHANNEL_3, /*!< ADC1 channel 3 is GPIO39 */
ADC1_CHANNEL_4, /*!< ADC1 channel 4 is GPIO32 */
ADC1_CHANNEL_5, /*!< ADC1 channel 5 is GPIO33 */
ADC1_CHANNEL_6, /*!< ADC1 channel 6 is GPIO34 */
ADC1_CHANNEL_7, /*!< ADC1 channel 7 is GPIO35 */
ADC1_CHANNEL_MAX,
} adc1_channel_t;
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 // TODO ESP32-S3 channels are wrong IDF-1776
/**** `adc1_channel_t` will be deprecated functions, combine into `adc_channel_t` ********/
typedef enum {
ADC1_CHANNEL_0 = 0, /*!< ADC1 channel 0 is GPIO1 */
ADC1_CHANNEL_1, /*!< ADC1 channel 1 is GPIO2 */
ADC1_CHANNEL_2, /*!< ADC1 channel 2 is GPIO3 */
ADC1_CHANNEL_3, /*!< ADC1 channel 3 is GPIO4 */
ADC1_CHANNEL_4, /*!< ADC1 channel 4 is GPIO5 */
ADC1_CHANNEL_5, /*!< ADC1 channel 5 is GPIO6 */
ADC1_CHANNEL_6, /*!< ADC1 channel 6 is GPIO7 */
ADC1_CHANNEL_7, /*!< ADC1 channel 7 is GPIO8 */
ADC1_CHANNEL_8, /*!< ADC1 channel 8 is GPIO9 */
ADC1_CHANNEL_9, /*!< ADC1 channel 9 is GPIO10 */
ADC1_CHANNEL_MAX,
} adc1_channel_t;
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C2
/**** `adc1_channel_t` will be deprecated functions, combine into `adc_channel_t` ********/
typedef enum {
ADC1_CHANNEL_0 = 0, /*!< ADC1 channel 0 is GPIO0 */
ADC1_CHANNEL_1, /*!< ADC1 channel 1 is GPIO1 */
ADC1_CHANNEL_2, /*!< ADC1 channel 2 is GPIO2 */
ADC1_CHANNEL_3, /*!< ADC1 channel 3 is GPIO3 */
ADC1_CHANNEL_4, /*!< ADC1 channel 4 is GPIO4 */
ADC1_CHANNEL_MAX,
} adc1_channel_t;
#endif // CONFIG_IDF_TARGET_*
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 // TODO ESP32-S3 channels are wrong IDF-1776
/**** `adc2_channel_t` will be deprecated functions, combine into `adc_channel_t` ********/
typedef enum {
ADC2_CHANNEL_0 = 0, /*!< ADC2 channel 0 is GPIO4 (ESP32), GPIO11 (ESP32-S2) */
ADC2_CHANNEL_1, /*!< ADC2 channel 1 is GPIO0 (ESP32), GPIO12 (ESP32-S2) */
ADC2_CHANNEL_2, /*!< ADC2 channel 2 is GPIO2 (ESP32), GPIO13 (ESP32-S2) */
ADC2_CHANNEL_3, /*!< ADC2 channel 3 is GPIO15 (ESP32), GPIO14 (ESP32-S2) */
ADC2_CHANNEL_4, /*!< ADC2 channel 4 is GPIO13 (ESP32), GPIO15 (ESP32-S2) */
ADC2_CHANNEL_5, /*!< ADC2 channel 5 is GPIO12 (ESP32), GPIO16 (ESP32-S2) */
ADC2_CHANNEL_6, /*!< ADC2 channel 6 is GPIO14 (ESP32), GPIO17 (ESP32-S2) */
ADC2_CHANNEL_7, /*!< ADC2 channel 7 is GPIO27 (ESP32), GPIO18 (ESP32-S2) */
ADC2_CHANNEL_8, /*!< ADC2 channel 8 is GPIO25 (ESP32), GPIO19 (ESP32-S2) */
ADC2_CHANNEL_9, /*!< ADC2 channel 9 is GPIO26 (ESP32), GPIO20 (ESP32-S2) */
ADC2_CHANNEL_MAX,
} adc2_channel_t;
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C2
/**** `adc2_channel_t` will be deprecated functions, combine into `adc_channel_t` ********/
typedef enum {
ADC2_CHANNEL_0 = 0, /*!< ADC2 channel 0 is GPIO5 */
ADC2_CHANNEL_MAX,
} adc2_channel_t;
#endif
/**
* @brief ADC rtc controller attenuation option.
*
* @note This definitions are only for being back-compatible
*/
#define ADC_ATTEN_0db ADC_ATTEN_DB_0
#define ADC_ATTEN_2_5db ADC_ATTEN_DB_2_5
#define ADC_ATTEN_6db ADC_ATTEN_DB_6
#define ADC_ATTEN_11db ADC_ATTEN_DB_11
/**
* The default (max) bit width of the ADC of current version. You can also get the maximum bitwidth
* by `SOC_ADC_RTC_MAX_BITWIDTH` defined in soc_caps.h.
*/
#define ADC_WIDTH_BIT_DEFAULT (ADC_WIDTH_MAX-1)
//this definitions are only for being back-compatible
#define ADC_WIDTH_9Bit ADC_WIDTH_BIT_9
#define ADC_WIDTH_10Bit ADC_WIDTH_BIT_10
#define ADC_WIDTH_11Bit ADC_WIDTH_BIT_11
#define ADC_WIDTH_12Bit ADC_WIDTH_BIT_12
/**
* @brief ADC digital controller encode option.
*
* @deprecated The ESP32-S2 doesn't use I2S DMA. Call ``adc_digi_output_format_t`` instead.
*/
typedef enum {
ADC_ENCODE_12BIT, /*!< ADC to DMA data format, , [15:12]-channel [11:0]-12 bits ADC data */
ADC_ENCODE_11BIT, /*!< ADC to DMA data format, [15]-unit, [14:11]-channel [10:0]-11 bits ADC data */
ADC_ENCODE_MAX,
} adc_i2s_encode_t;
/*---------------------------------------------------------------
Common setting
Deprecated API
---------------------------------------------------------------*/
/**
* @brief Enable ADC power
* @deprecated Use adc_power_acquire and adc_power_release instead.
*/
void adc_power_on(void) __attribute__((deprecated));
/**
* @brief Power off SAR ADC
* @deprecated Use adc_power_acquire and adc_power_release instead.
* This function will force power down for ADC.
* This function is deprecated because forcing power ADC power off may
* disrupt operation of other components which may be using the ADC.
*/
void adc_power_off(void) __attribute__((deprecated));
/**
* @brief Increment the usage counter for ADC module.
* ADC will stay powered on while the counter is greater than 0.
* Call adc_power_release when done using the ADC.
*/
void adc_power_acquire(void);
/**
* @brief Decrement the usage counter for ADC module.
* ADC will stay powered on while the counter is greater than 0.
* Call this function when done using the ADC.
*/
void adc_power_release(void);
/*---------------------------------------------------------------
ADC Single Read Setting
---------------------------------------------------------------*/
@ -288,6 +167,7 @@ esp_err_t adc_set_data_width(adc_unit_t adc_unit, adc_bits_width_t width_bit);
void adc1_ulp_enable(void);
#endif //#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#if (SOC_ADC_PERIPH_NUM >= 2)
/**
* @brief Get the GPIO number of a specific ADC2 channel.
*
@ -396,59 +276,13 @@ esp_err_t adc2_get_raw(adc2_channel_t channel, adc_bits_width_t width_bit, int *
* - ESP_ERR_INVALID_ARG: Unsupported GPIO
*/
esp_err_t adc_vref_to_gpio(adc_unit_t adc_unit, gpio_num_t gpio);
#endif //#if (SOC_ADC_PERIPH_NUM >= 2)
/**
* @brief Output ADC2 reference voltage to ``adc2_channe_t``'s IO.
*
* This function routes the internal reference voltage of ADCn to one of
* ADC2's channels. This reference voltage can then be manually measured
* for calibration purposes.
*
* @deprecated Use ``adc_vref_to_gpio`` instead.
*
* @param[in] gpio GPIO number (ADC2's channels are supported)
*
* @return
* - ESP_OK: v_ref successfully routed to selected GPIO
* - ESP_ERR_INVALID_ARG: Unsupported GPIO
*/
esp_err_t adc2_vref_to_gpio(gpio_num_t gpio) __attribute__((deprecated));
/*---------------------------------------------------------------
DMA setting
ADC DMA Read Setting
---------------------------------------------------------------*/
/**
* @brief Digital ADC DMA read max timeout value, it may make the ``adc_digi_read_bytes`` block forever if the OS supports
*/
#define ADC_MAX_DELAY UINT32_MAX
/**
* @brief ADC DMA driver configuration
*/
typedef struct adc_digi_init_config_s {
uint32_t max_store_buf_size; ///< Max length of the converted data that driver can store before they are processed.
uint32_t conv_num_each_intr; ///< Bytes of data that can be converted in 1 interrupt.
uint32_t adc1_chan_mask; ///< Channel list of ADC1 to be initialized.
uint32_t adc2_chan_mask; ///< Channel list of ADC2 to be initialized.
} adc_digi_init_config_t;
/**
* @brief ADC digital controller settings
*/
typedef struct {
bool conv_limit_en; ///< To limit ADC conversion times. Conversion stops after finishing `conv_limit_num` times conversion
uint32_t conv_limit_num; ///< Set the upper limit of the number of ADC conversion triggers. Range: 1 ~ 255.
uint32_t pattern_num; ///< Number of ADC channels that will be used
adc_digi_pattern_config_t *adc_pattern; ///< List of configs for each ADC channel that will be used
uint32_t sample_freq_hz; /*!< The expected ADC sampling frequency in Hz. Range: 611Hz ~ 83333Hz
Fs = Fd / interval / 2
Fs: sampling frequency;
Fd: digital controller frequency, no larger than 5M for better performance
interval: interval between 2 measurement trigger signal, the smallest interval should not be smaller than the ADC measurement period, the largest interval should not be larger than 4095 */
adc_digi_convert_mode_t conv_mode; ///< ADC DMA conversion mode, see `adc_digi_convert_mode_t`.
adc_digi_output_format_t format; ///< ADC DMA conversion output format, see `adc_digi_output_format_t`.
} adc_digi_configuration_t;
#if SOC_ADC_DMA_SUPPORTED
/**
* @brief Initialize the Digital ADC.
*
@ -515,130 +349,6 @@ esp_err_t adc_digi_deinitialize(void);
* - ESP_OK On success
*/
esp_err_t adc_digi_controller_configure(const adc_digi_configuration_t *config);
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3
//TODO IDF-3610
/**
* @brief Reset adc digital controller filter.
*
* @param idx Filter index.
*
* @return
* - ESP_OK Success
*/
esp_err_t adc_digi_filter_reset(adc_digi_filter_idx_t idx);
/**
* @brief Set adc digital controller filter configuration.
*
* @note For ESP32S2, Filter IDX0/IDX1 can only be used to filter all enabled channels of ADC1/ADC2 unit at the same time.
*
* @param idx Filter index.
* @param config See ``adc_digi_filter_t``.
*
* @return
* - ESP_OK Success
*/
esp_err_t adc_digi_filter_set_config(adc_digi_filter_idx_t idx, adc_digi_filter_t *config);
/**
* @brief Get adc digital controller filter configuration.
*
* @note For ESP32S2, Filter IDX0/IDX1 can only be used to filter all enabled channels of ADC1/ADC2 unit at the same time.
*
* @param idx Filter index.
* @param config See ``adc_digi_filter_t``.
*
* @return
* - ESP_OK Success
*/
esp_err_t adc_digi_filter_get_config(adc_digi_filter_idx_t idx, adc_digi_filter_t *config);
/**
* @brief Enable/disable adc digital controller filter.
* Filtering the ADC data to obtain smooth data at higher sampling rates.
*
* @note For ESP32S2, Filter IDX0/IDX1 can only be used to filter all enabled channels of ADC1/ADC2 unit at the same time.
*
* @param idx Filter index.
* @param enable Enable/Disable filter.
*
* @return
* - ESP_OK Success
*/
esp_err_t adc_digi_filter_enable(adc_digi_filter_idx_t idx, bool enable);
/**
* @brief Config monitor of adc digital controller.
*
* @note For ESP32S2, The monitor will monitor all the enabled channel data of the each ADC unit at the same time.
*
* @param idx Monitor index.
* @param config See ``adc_digi_monitor_t``.
*
* @return
* - ESP_OK Success
*/
esp_err_t adc_digi_monitor_set_config(adc_digi_monitor_idx_t idx, adc_digi_monitor_t *config);
/**
* @brief Enable/disable monitor of adc digital controller.
*
* @note For ESP32S2, The monitor will monitor all the enabled channel data of the each ADC unit at the same time.
*
* @param idx Monitor index.
* @param enable True or false enable monitor.
*
* @return
* - ESP_OK Success
*/
esp_err_t adc_digi_monitor_enable(adc_digi_monitor_idx_t idx, bool enable);
#endif //#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3
#if CONFIG_IDF_TARGET_ESP32
//TODO IDF-3610
/**
* @brief Read Hall Sensor
*
* @note When the power switch of SARADC1, SARADC2, HALL sensor and AMP sensor is turned on,
* the input of GPIO36 and GPIO39 will be pulled down for about 80ns.
* When enabling power for any of these peripherals, ignore input from GPIO36 and GPIO39.
* Please refer to section 3.11 of 'ECO_and_Workarounds_for_Bugs_in_ESP32' for the description of this issue.
*
* @note The Hall Sensor uses channels 0 and 3 of ADC1. Do not configure
* these channels for use as ADC channels.
*
* @note The ADC1 module must be enabled by calling
* adc1_config_width() before calling hall_sensor_read(). ADC1
* should be configured for 12 bit readings, as the hall sensor
* readings are low values and do not cover the full range of the
* ADC.
*
* @return The hall sensor reading.
*/
int hall_sensor_read(void);
/*---------------------------------------------------------------
To Be Deprecated TODO: IDF-3610
---------------------------------------------------------------*/
/**
* @brief Set I2S data source
* @param src I2S DMA data source, I2S DMA can get data from digital signals or from ADC.
* @return
* - ESP_OK success
*/
esp_err_t adc_set_i2s_data_source(adc_i2s_source_t src);
/**
* @brief Initialize I2S ADC mode
* @param adc_unit ADC unit index
* @param channel ADC channel index
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t adc_i2s_mode_init(adc_unit_t adc_unit, adc_channel_t channel);
#endif
#ifdef __cplusplus

View File

@ -1,163 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*----------------------------------------------------------------------------------
This file contains Deprecated ADC APIs
-----------------------------------------------------------------------------------*/
#pragma once
#include "esp_err.h"
#include "hal/adc_types.h"
#include "driver/adc_types_deprecated.h"
#ifdef __cplusplus
extern "C" {
#endif
#if CONFIG_IDF_TARGET_ESP32S2
/*---------------------------------------------------------------
ESP32S2 Deprecated ADC APIs
---------------------------------------------------------------*/
/**
* @brief Config ADC module arbiter.
* The arbiter is to improve the use efficiency of ADC2. After the control right is robbed by the high priority,
* the low priority controller will read the invalid ADC2 data, and the validity of the data can be judged by the flag bit in the data.
*
* @note Only ADC2 support arbiter.
* @note Default priority: Wi-Fi > RTC > Digital;
* @note In normal use, there is no need to call this interface to config arbiter.
*
* @param adc_unit ADC unit.
* @param config Refer to `adc_arbiter_t`.
*
* @return
* - ESP_OK Success
* - ESP_ERR_NOT_SUPPORTED ADC unit not support arbiter.
*/
esp_err_t adc_arbiter_config(adc_unit_t adc_unit, adc_arbiter_t *config) __attribute__((deprecated));
/**
* @brief Enable interrupt of adc digital controller by bitmask.
*
* @param adc_unit ADC unit.
* @param intr_mask Interrupt bitmask. See ``adc_digi_intr_t``.
*
* @return
* - ESP_OK Success
*/
esp_err_t adc_digi_intr_enable(adc_unit_t adc_unit, adc_digi_intr_t intr_mask) __attribute__((deprecated));
/**
* @brief Disable interrupt of adc digital controller by bitmask.
*
* @param adc_unit ADC unit.
* @param intr_mask Interrupt bitmask. See ``adc_digi_intr_t``.
*
* @return
* - ESP_OK Success
*/
esp_err_t adc_digi_intr_disable(adc_unit_t adc_unit, adc_digi_intr_t intr_mask) __attribute__((deprecated));
/**
* @brief Clear interrupt of adc digital controller by bitmask.
*
* @param adc_unit ADC unit.
* @param intr_mask Interrupt bitmask. See ``adc_digi_intr_t``.
*
* @return
* - ESP_OK Success
*/
esp_err_t adc_digi_intr_clear(adc_unit_t adc_unit, adc_digi_intr_t intr_mask) __attribute__((deprecated));
/**
* @brief Get interrupt status mask of adc digital controller.
*
* @param adc_unit ADC unit.
* @return
* - intr Interrupt bitmask, See ``adc_digi_intr_t``.
*/
uint32_t adc_digi_intr_get_status(adc_unit_t adc_unit) __attribute__((deprecated));
/**
* @brief Register ADC interrupt handler, the handler is an ISR.
* The handler will be attached to the same CPU core that this function is running on.
*
* @param fn Interrupt handler function.
* @param arg Parameter for handler function
* @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred)
* ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info.
*
* @return
* - ESP_OK Success
* - ESP_ERR_NOT_FOUND Can not find the interrupt that matches the flags.
* - ESP_ERR_INVALID_ARG Function pointer error.
*/
esp_err_t adc_digi_isr_register(void (*fn)(void *), void *arg, int intr_alloc_flags) __attribute__((deprecated));
/**
* @brief Deregister ADC interrupt handler, the handler is an ISR.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG hander error.
* - ESP_FAIL ISR not be registered.
*/
esp_err_t adc_digi_isr_deregister(void) __attribute__((deprecated));
#endif // #if CONFIG_IDF_TARGET_ESP32S2
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
/*---------------------------------------------------------------
ESP32, ESP32S2 Deprecated ADC APIs
---------------------------------------------------------------*/
/**
* @brief ADC digital controller initialization.
* @return
* - ESP_OK Success
*/
esp_err_t adc_digi_init(void) __attribute__((deprecated));
/**
* @brief ADC digital controller deinitialization.
* @return
* - ESP_OK Success
*/
esp_err_t adc_digi_deinit(void) __attribute__((deprecated));
#endif //#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3
/*---------------------------------------------------------------
ESP32, ESP32S2, ESP32C3 Deprecated ADC APIs
---------------------------------------------------------------*/
/**
* @brief Setting the digital controller.
*
* @param config Pointer to digital controller paramter. Refer to ``adc_digi_config_t``.
*
* @return
* - ESP_ERR_INVALID_STATE Driver state is invalid.
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid.
* - ESP_OK On success
*/
esp_err_t adc_digi_controller_config(const adc_digi_config_t *config) __attribute__((deprecated));
/**
* @brief Initialize ADC pad
* @param adc_unit ADC unit index
* @param channel ADC channel index
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t adc_gpio_init(adc_unit_t adc_unit, adc_channel_t channel) __attribute__((deprecated));;
#endif //#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#include "esp_err.h"
#include "hal/adc_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef CONFIG_IDF_TARGET_ESP32
/**
* @brief ESP32 ADC DMA source selection.
*/
typedef enum {
ADC_I2S_DATA_SRC_IO_SIG = 0, /*!< I2S data from GPIO matrix signal */
ADC_I2S_DATA_SRC_ADC = 1, /*!< I2S data from ADC */
} adc_i2s_source_t;
#endif
#if CONFIG_IDF_TARGET_ESP32
/*---------------------------------------------------------------
ESP32 Deprecated API
---------------------------------------------------------------*/
/**
* @brief Set I2S data source
* @param src I2S DMA data source, I2S DMA can get data from digital signals or from ADC.
* @return
* - ESP_OK success
*/
esp_err_t adc_set_i2s_data_source(adc_i2s_source_t src);
/**
* @brief Initialize I2S ADC mode
* @param adc_unit ADC unit index
* @param channel ADC channel index
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t adc_i2s_mode_init(adc_unit_t adc_unit, adc_channel_t channel);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -1,203 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#include "hal/adc_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#if CONFIG_IDF_TARGET_ESP32
/*---------------------------------------------------------------
ESP32 Deprecated Types
---------------------------------------------------------------*/
/**
* @brief ADC digital controller (DMA mode) conversion rules setting.
*/
typedef struct {
union {
struct {
uint8_t atten: 2; /*!< ADC sampling voltage attenuation configuration. Modification of attenuation affects the range of measurements.
0: measurement range 0 - 800mV,
1: measurement range 0 - 1100mV,
2: measurement range 0 - 1350mV,
3: measurement range 0 - 2600mV. */
uint8_t bit_width: 2; /*!< ADC resolution.
- 0: 9 bit;
- 1: 10 bit;
- 2: 11 bit;
- 3: 12 bit. */
int8_t channel: 4; /*!< ADC channel index. */
};
uint8_t val; /*!<Raw data value */
};
} adc_digi_pattern_table_t __attribute__((deprecated));
/**
* Explanation of the relationship between `conv_limit_num`, `dma_eof_num` and the number of DMA outputs:
*
* +---------------------+--------+--------+--------+
* | conv_mode | single | both | alter |
* +---------------------+--------+--------+--------+
* | trigger meas times | 1 | 1 | 1 |
* +---------------------+--------+--------+--------+
* | conv_limit_num | +1 | +1 | +1 |
* | dma_eof_num | +1 | +2 | +1 |
* | dma output (byte) | +2 | +4 | +2 |
* +---------------------+--------+--------+--------+
*/
typedef struct {
bool conv_limit_en; /*!<Enable the function of limiting ADC conversion times.
If the number of ADC conversion trigger count is equal to the `limit_num`, the conversion is stopped. */
uint32_t conv_limit_num; /*!<Set the upper limit of the number of ADC conversion triggers. Range: 1 ~ 255. */
uint32_t adc1_pattern_len; /*!<Pattern table length for digital controller. Range: 0 ~ 16 (0: Don't change the pattern table setting).
The pattern table that defines the conversion rules for each SAR ADC. Each table has 16 items, in which channel selection,
resolution and attenuation are stored. When the conversion is started, the controller reads conversion rules from the
pattern table one by one. For each controller the scan sequence has at most 16 different rules before repeating itself. */
uint32_t adc2_pattern_len; /*!<Refer to ``adc1_pattern_len`` */
adc_digi_pattern_table_t *adc1_pattern; /*!<Pointer to pattern table for digital controller. The table size defined by `adc1_pattern_len`. */
adc_digi_pattern_table_t *adc2_pattern; /*!<Refer to `adc1_pattern` */
adc_digi_convert_mode_t conv_mode; /*!<ADC conversion mode for digital controller. See ``adc_digi_convert_mode_t``. */
adc_digi_output_format_t format; /*!<ADC output data format for digital controller. See ``adc_digi_output_format_t``. */
} adc_digi_config_t __attribute__((deprecated));
#endif //#if CONFIG_IDF_TARGET_ESP32
#if CONFIG_IDF_TARGET_ESP32S2
/*---------------------------------------------------------------
ESP32S2 Deprecated Types
---------------------------------------------------------------*/
/**
* @brief ADC digital controller (DMA mode) conversion rules setting.
*/
typedef struct {
union {
struct {
uint8_t atten: 2; /*!< ADC sampling voltage attenuation configuration. Modification of attenuation affects the range of measurements.
0: measurement range 0 - 800mV,
1: measurement range 0 - 1100mV,
2: measurement range 0 - 1350mV,
3: measurement range 0 - 2600mV. */
uint8_t reserved: 2; /*!< reserved0 */
uint8_t channel: 4; /*!< ADC channel index. */
};
uint8_t val; /*!<Raw data value */
};
} adc_digi_pattern_table_t __attribute__((deprecated));
/**
* @brief ADC digital controller (DMA mode) configuration parameters.
*
* Example setting: When using ADC1 channel0 to measure voltage, the sampling rate is required to be 1 kHz:
*
* +---------------------+--------+--------+--------+
* | sample rate | 1 kHz | 1 kHz | 1 kHz |
* +---------------------+--------+--------+--------+
* | conv_mode | single | both | alter |
* | adc1_pattern_len | 1 | 1 | 1 |
* | dig_clk.use_apll | 0 | 0 | 0 |
* | dig_clk.div_num | 99 | 99 | 99 |
* | dig_clk.div_b | 0 | 0 | 0 |
* | dig_clk.div_a | 0 | 0 | 0 |
* | interval | 400 | 400 | 200 |
* +---------------------+--------+--------+--------+
* | `trigger_meas_freq` | 1 kHz | 1 kHz | 2 kHz |
* +---------------------+--------+--------+--------+
*
* Explanation of the relationship between `conv_limit_num`, `dma_eof_num` and the number of DMA outputs:
*
* +---------------------+--------+--------+--------+
* | conv_mode | single | both | alter |
* +---------------------+--------+--------+--------+
* | trigger meas times | 1 | 1 | 1 |
* +---------------------+--------+--------+--------+
* | conv_limit_num | +1 | +1 | +1 |
* | dma_eof_num | +1 | +2 | +1 |
* | dma output (byte) | +2 | +4 | +2 |
* +---------------------+--------+--------+--------+
*/
typedef struct {
bool conv_limit_en; /*!<Enable the function of limiting ADC conversion times.
If the number of ADC conversion trigger count is equal to the `limit_num`, the conversion is stopped. */
uint32_t conv_limit_num; /*!<Set the upper limit of the number of ADC conversion triggers. Range: 1 ~ 255. */
uint32_t adc1_pattern_len; /*!<Pattern table length for digital controller. Range: 0 ~ 16 (0: Don't change the pattern table setting).
The pattern table that defines the conversion rules for each SAR ADC. Each table has 16 items, in which channel selection,
resolution and attenuation are stored. When the conversion is started, the controller reads conversion rules from the
pattern table one by one. For each controller the scan sequence has at most 16 different rules before repeating itself. */
uint32_t adc2_pattern_len; /*!<Refer to ``adc1_pattern_len`` */
adc_digi_pattern_table_t *adc1_pattern; /*!<Pointer to pattern table for digital controller. The table size defined by `adc1_pattern_len`. */
adc_digi_pattern_table_t *adc2_pattern; /*!<Refer to `adc1_pattern` */
adc_digi_convert_mode_t conv_mode; /*!<ADC conversion mode for digital controller. See ``adc_digi_convert_mode_t``. */
adc_digi_output_format_t format; /*!<ADC output data format for digital controller. See ``adc_digi_output_format_t``. */
uint32_t interval; /*!<The number of interval clock cycles for the digital controller to trigger the measurement.
The unit is the divided clock. Range: 40 ~ 4095.
Expression: `trigger_meas_freq` = `controller_clk` / 2 / interval. Refer to ``adc_digi_clk_t``.
Note: The sampling rate of each channel is also related to the conversion mode (See ``adc_digi_convert_mode_t``) and pattern table settings. */
adc_digi_clk_t dig_clk; /*!<ADC digital controller clock divider settings. Refer to ``adc_digi_clk_t``.
Note: The clocks of the DAC digital controller use the ADC digital controller clock divider. */
uint32_t dma_eof_num; /*!<DMA eof num of adc digital controller.
If the number of measurements reaches `dma_eof_num`, then `dma_in_suc_eof` signal is generated in DMA.
Note: The converted data in the DMA in link buffer will be multiple of two bytes. */
} adc_digi_config_t __attribute__((deprecated));
/**
* @brief ADC digital controller (DMA mode) interrupt type options.
*/
typedef enum {
ADC_DIGI_INTR_MASK_MONITOR = 0x1,
ADC_DIGI_INTR_MASK_MEAS_DONE = 0x2,
ADC_DIGI_INTR_MASK_ALL = 0x3,
} adc_digi_intr_t __attribute__((deprecated));
FLAG_ATTR(adc_digi_intr_t)
#endif //#if CONFIG_IDF_TARGET_ESP32S2
#if CONFIG_IDF_TARGET_ESP32C3
/*---------------------------------------------------------------
ESP32C3 Deprecated Types
---------------------------------------------------------------*/
/**
* @brief ADC digital controller (DMA mode) conversion rules setting.
*/
typedef struct {
union {
struct {
uint8_t atten: 2; /*!< ADC sampling voltage attenuation configuration. Modification of attenuation affects the range of measurements.
0: measurement range 0 - 800mV,
1: measurement range 0 - 1100mV,
2: measurement range 0 - 1350mV,
3: measurement range 0 - 2600mV. */
uint8_t channel: 3; /*!< ADC channel index. */
uint8_t unit: 1; /*!< ADC unit index. */
uint8_t reserved: 2; /*!< reserved0 */
};
uint8_t val; /*!<Raw data value */
};
} adc_digi_pattern_table_t __attribute__((deprecated));
typedef struct {
bool conv_limit_en; /*!<Enable the function of limiting ADC conversion times.
If the number of ADC conversion trigger count is equal to the `limit_num`, the conversion is stopped. */
uint32_t conv_limit_num; /*!<Set the upper limit of the number of ADC conversion triggers. Range: 1 ~ 255. */
uint32_t adc_pattern_len; /*!<Pattern table length for digital controller. Range: 0 ~ 7 (0: Don't change the pattern table setting).
The pattern table that defines the conversion rules for each SAR ADC. Each table has 7 items, in which channel selection,
resolution and attenuation are stored. When the conversion is started, the controller reads conversion rules from the
pattern table one by one. For each controller the scan sequence has at most 16 different rules before repeating itself. */
adc_digi_pattern_table_t *adc_pattern; /*!<Pointer to pattern table for digital controller. The table size defined by `adc_pattern_len`. */
uint32_t sample_freq_hz; /*!< The expected ADC sampling frequency in Hz. Range: 611Hz ~ 83333Hz
Fs = Fd / interval / 2
Fs: sampling frequency;
Fd: digital controller frequency, no larger than 5M for better performance
interval: interval between 2 measurement trigger signal, the smallest interval should not be smaller than the ADC measurement period, the largest interval should not be larger than 4095 */
} adc_digi_config_t __attribute__((deprecated));
#endif //#if CONFIG_IDF_TARGET_ESP32C3
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,128 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "hal/adc_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief ADC resolution setting option.
* @note Only used in single read mode
*/
typedef enum {
#if CONFIG_IDF_TARGET_ESP32
ADC_WIDTH_BIT_9 = 9, /*!< ADC capture width is 9Bit. */
ADC_WIDTH_BIT_10 = 10, /*!< ADC capture width is 10Bit. */
ADC_WIDTH_BIT_11 = 11, /*!< ADC capture width is 11Bit. */
ADC_WIDTH_BIT_12 = 12, /*!< ADC capture width is 12Bit. */
#elif SOC_ADC_RTC_MAX_BITWIDTH == 12
ADC_WIDTH_BIT_12 = 12, /*!< ADC capture width is 12Bit. */
#elif SOC_ADC_RTC_MAX_BITWIDTH == 13
ADC_WIDTH_BIT_13 = 13, /*!< ADC capture width is 13Bit. */
#endif
ADC_WIDTH_MAX,
} adc_bits_width_t;
/**
* The default (max) bit width of the ADC of current version. You can also get the maximum bitwidth
* by `SOC_ADC_RTC_MAX_BITWIDTH` defined in soc_caps.h.
*/
#define ADC_WIDTH_BIT_DEFAULT (ADC_WIDTH_MAX-1)
#if CONFIG_IDF_TARGET_ESP32
typedef enum {
ADC1_CHANNEL_0 = 0, /*!< ADC1 channel 0 is GPIO36 */
ADC1_CHANNEL_1, /*!< ADC1 channel 1 is GPIO37 */
ADC1_CHANNEL_2, /*!< ADC1 channel 2 is GPIO38 */
ADC1_CHANNEL_3, /*!< ADC1 channel 3 is GPIO39 */
ADC1_CHANNEL_4, /*!< ADC1 channel 4 is GPIO32 */
ADC1_CHANNEL_5, /*!< ADC1 channel 5 is GPIO33 */
ADC1_CHANNEL_6, /*!< ADC1 channel 6 is GPIO34 */
ADC1_CHANNEL_7, /*!< ADC1 channel 7 is GPIO35 */
ADC1_CHANNEL_MAX,
} adc1_channel_t;
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
typedef enum {
ADC1_CHANNEL_0 = 0, /*!< ADC1 channel 0 is GPIO1 */
ADC1_CHANNEL_1, /*!< ADC1 channel 1 is GPIO2 */
ADC1_CHANNEL_2, /*!< ADC1 channel 2 is GPIO3 */
ADC1_CHANNEL_3, /*!< ADC1 channel 3 is GPIO4 */
ADC1_CHANNEL_4, /*!< ADC1 channel 4 is GPIO5 */
ADC1_CHANNEL_5, /*!< ADC1 channel 5 is GPIO6 */
ADC1_CHANNEL_6, /*!< ADC1 channel 6 is GPIO7 */
ADC1_CHANNEL_7, /*!< ADC1 channel 7 is GPIO8 */
ADC1_CHANNEL_8, /*!< ADC1 channel 8 is GPIO9 */
ADC1_CHANNEL_9, /*!< ADC1 channel 9 is GPIO10 */
ADC1_CHANNEL_MAX,
} adc1_channel_t;
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32H2
typedef enum {
ADC1_CHANNEL_0 = 0, /*!< ADC1 channel 0 is GPIO0 */
ADC1_CHANNEL_1, /*!< ADC1 channel 1 is GPIO1 */
ADC1_CHANNEL_2, /*!< ADC1 channel 2 is GPIO2 */
ADC1_CHANNEL_3, /*!< ADC1 channel 3 is GPIO3 */
ADC1_CHANNEL_4, /*!< ADC1 channel 4 is GPIO4 */
ADC1_CHANNEL_MAX,
} adc1_channel_t;
#endif // CONFIG_IDF_TARGET_*
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
typedef enum {
ADC2_CHANNEL_0 = 0, /*!< ADC2 channel 0 is GPIO4 (ESP32), GPIO11 (ESP32-S2) */
ADC2_CHANNEL_1, /*!< ADC2 channel 1 is GPIO0 (ESP32), GPIO12 (ESP32-S2) */
ADC2_CHANNEL_2, /*!< ADC2 channel 2 is GPIO2 (ESP32), GPIO13 (ESP32-S2) */
ADC2_CHANNEL_3, /*!< ADC2 channel 3 is GPIO15 (ESP32), GPIO14 (ESP32-S2) */
ADC2_CHANNEL_4, /*!< ADC2 channel 4 is GPIO13 (ESP32), GPIO15 (ESP32-S2) */
ADC2_CHANNEL_5, /*!< ADC2 channel 5 is GPIO12 (ESP32), GPIO16 (ESP32-S2) */
ADC2_CHANNEL_6, /*!< ADC2 channel 6 is GPIO14 (ESP32), GPIO17 (ESP32-S2) */
ADC2_CHANNEL_7, /*!< ADC2 channel 7 is GPIO27 (ESP32), GPIO18 (ESP32-S2) */
ADC2_CHANNEL_8, /*!< ADC2 channel 8 is GPIO25 (ESP32), GPIO19 (ESP32-S2) */
ADC2_CHANNEL_9, /*!< ADC2 channel 9 is GPIO26 (ESP32), GPIO20 (ESP32-S2) */
ADC2_CHANNEL_MAX,
} adc2_channel_t;
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32H2
typedef enum {
ADC2_CHANNEL_0 = 0, /*!< ADC2 channel 0 is GPIO5 */
ADC2_CHANNEL_MAX,
} adc2_channel_t;
#endif
#if SOC_ADC_DMA_SUPPORTED
/**
* @brief Digital ADC DMA read max timeout value, it may make the ``adc_digi_read_bytes`` block forever if the OS supports
*/
#define ADC_MAX_DELAY UINT32_MAX
/**
* @brief ADC DMA driver configuration
*/
typedef struct adc_digi_init_config_s {
uint32_t max_store_buf_size; ///< Max length of the converted data that driver can store before they are processed.
uint32_t conv_num_each_intr; ///< Bytes of data that can be converted in 1 interrupt. This should be in multiples of `SOC_ADC_DIGI_DATA_BYTES_PER_CONV`.
uint32_t adc1_chan_mask; ///< Channel list of ADC1 to be initialized.
uint32_t adc2_chan_mask; ///< Channel list of ADC2 to be initialized.
} adc_digi_init_config_t;
/**
* @brief ADC digital controller settings
*/
typedef struct {
bool conv_limit_en; ///< Suggest leaving it empty, this parameter has been deprecated
uint32_t conv_limit_num; ///< suggest leaving it empty, this parameter has been deprecated
uint32_t pattern_num; ///< Number of ADC channels that will be used
adc_digi_pattern_config_t *adc_pattern; ///< List of configs for each ADC channel that will be used
uint32_t sample_freq_hz; /*!< Please refer to `soc/soc_caps.h` to know the ADC sampling frequency range*/
adc_digi_convert_mode_t conv_mode; ///< ADC DMA conversion mode, see `adc_digi_convert_mode_t`.
adc_digi_output_format_t format; ///< ADC DMA conversion output format, see `adc_digi_output_format_t`.
} adc_digi_configuration_t;
#endif // #if SOC_ADC_DMA_SUPPORTED
#ifdef __cplusplus
}
#endif

View File

@ -27,8 +27,10 @@
#include "hal/i2s_hal.h"
#if SOC_I2S_SUPPORTS_DAC
#include "driver/dac.h"
#include "driver/adc.h"
#include "../adc1_private.h"
#include "esp_private/adc_private.h"
#include "adc1_private.h"
#include "driver/adc_i2s_legacy.h"
#include "driver/adc_types_legacy.h"
#endif // SOC_I2S_SUPPORTS_ADC
#if SOC_GDMA_SUPPORTED

View File

@ -1,77 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <ctype.h>
#include "sdkconfig.h"
#include "esp_types.h"
#include "esp_log.h"
#include "sys/lock.h"
#include "soc/rtc.h"
#include "soc/periph_defs.h"
#include "freertos/FreeRTOS.h"
#include "freertos/xtensa_api.h"
#include "freertos/semphr.h"
#include "freertos/timers.h"
#include "esp_intr_alloc.h"
#include "driver/rtc_io.h"
#include "esp_private/rtc_ctrl.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#ifndef NDEBUG
// Enable built-in checks in queue.h in debug builds
#define INVARIANTS
#endif
#include "sys/queue.h"
#include "hal/adc_types.h"
#include "hal/adc_hal.h"
#define ADC_GET_IO_NUM(periph, channel) (adc_channel_io_map[periph][channel])
extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate position after the rtc module is finished.
#define ADC_ENTER_CRITICAL() portENTER_CRITICAL(&rtc_spinlock)
#define ADC_EXIT_CRITICAL() portEXIT_CRITICAL(&rtc_spinlock)
/*---------------------------------------------------------------
HALL SENSOR
---------------------------------------------------------------*/
static int hall_sensor_get_value(void) //hall sensor without LNA
{
int hall_value;
adc_power_acquire();
ADC_ENTER_CRITICAL();
/* disable other peripherals. */
adc_ll_amp_disable();
adc_ll_hall_enable();
// set controller
adc_ll_set_controller( ADC_UNIT_1, ADC_LL_CTRL_RTC );
hall_value = adc_hal_hall_convert();
adc_ll_hall_disable();
ADC_EXIT_CRITICAL();
adc_power_release();
return hall_value;
}
/**
* To Be Deprecated
*/
extern esp_err_t adc_common_gpio_init(adc_unit_t adc_unit, adc_channel_t channel);
int hall_sensor_read(void)
{
adc_common_gpio_init(ADC_UNIT_1, ADC1_CHANNEL_0);
adc_common_gpio_init(ADC_UNIT_1, ADC1_CHANNEL_3);
adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_0);
adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_DB_0);
return hall_sensor_get_value();
}

View File

@ -1,135 +0,0 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <ctype.h>
#include "sdkconfig.h"
#include "esp_types.h"
#include "esp_log.h"
#include "sys/lock.h"
#include "freertos/FreeRTOS.h"
#include "hal/adc_types.h"
#include "hal/adc_ll.h"
extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate position after the rtc module is finished.
#define ADC_ENTER_CRITICAL() portENTER_CRITICAL(&rtc_spinlock)
#define ADC_EXIT_CRITICAL() portEXIT_CRITICAL(&rtc_spinlock)
/**
* Config monitor of adc digital controller.
*
* @note The monitor will monitor all the enabled channel data of the each ADC unit at the same time.
* @param adc_n ADC unit.
* @param config Refer to ``adc_digi_monitor_t``.
*/
static void adc_digi_monitor_config(adc_unit_t adc_n, adc_digi_monitor_t *config)
{
adc_ll_digi_monitor_set_mode(adc_n, config->mode);
adc_ll_digi_monitor_set_thres(adc_n, config->threshold);
}
/*************************************/
/* Digital controller filter setting */
/*************************************/
esp_err_t adc_digi_filter_reset(adc_digi_filter_idx_t idx)
{
ADC_ENTER_CRITICAL();
if (idx == ADC_DIGI_FILTER_IDX0) {
adc_ll_digi_filter_reset(ADC_UNIT_1);
} else if (idx == ADC_DIGI_FILTER_IDX1) {
adc_ll_digi_filter_reset(ADC_UNIT_2);
}
ADC_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t adc_digi_filter_set_config(adc_digi_filter_idx_t idx, adc_digi_filter_t *config)
{
ADC_ENTER_CRITICAL();
if (idx == ADC_DIGI_FILTER_IDX0) {
adc_ll_digi_filter_set_factor(ADC_UNIT_1, config->mode);
} else if (idx == ADC_DIGI_FILTER_IDX1) {
adc_ll_digi_filter_set_factor(ADC_UNIT_2, config->mode);
}
ADC_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t adc_digi_filter_get_config(adc_digi_filter_idx_t idx, adc_digi_filter_t *config)
{
ADC_ENTER_CRITICAL();
if (idx == ADC_DIGI_FILTER_IDX0) {
config->adc_unit = ADC_UNIT_1;
config->channel = SOC_ADC_CHANNEL_NUM(0);
adc_ll_digi_filter_get_factor(ADC_UNIT_1, &config->mode);
} else if (idx == ADC_DIGI_FILTER_IDX1) {
config->adc_unit = ADC_UNIT_2;
config->channel = SOC_ADC_CHANNEL_NUM(1);
adc_ll_digi_filter_get_factor(ADC_UNIT_2, &config->mode);
}
ADC_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t adc_digi_filter_enable(adc_digi_filter_idx_t idx, bool enable)
{
ADC_ENTER_CRITICAL();
if (idx == ADC_DIGI_FILTER_IDX0) {
adc_ll_digi_filter_enable(ADC_UNIT_1, enable);
} else if (idx == ADC_DIGI_FILTER_IDX1) {
adc_ll_digi_filter_enable(ADC_UNIT_2, enable);
}
ADC_EXIT_CRITICAL();
return ESP_OK;
}
/**
* @brief Get the filtered data of adc digital controller filter. For debug.
* The data after each measurement and filtering is updated to the DMA by the digital controller. But it can also be obtained manually through this API.
*
* @note For ESP32S2, The filter will filter all the enabled channel data of the each ADC unit at the same time.
* @param idx Filter index.
* @return Filtered data. if <0, the read data invalid.
*/
int adc_digi_filter_read_data(adc_digi_filter_idx_t idx)
{
if (idx == ADC_DIGI_FILTER_IDX0) {
return adc_ll_digi_filter_read_data(ADC_UNIT_1);
} else if (idx == ADC_DIGI_FILTER_IDX1) {
return adc_ll_digi_filter_read_data(ADC_UNIT_2);
} else {
return -1;
}
}
/**************************************/
/* Digital controller monitor setting */
/**************************************/
esp_err_t adc_digi_monitor_set_config(adc_digi_monitor_idx_t idx, adc_digi_monitor_t *config)
{
ADC_ENTER_CRITICAL();
if (idx == ADC_DIGI_MONITOR_IDX0) {
adc_digi_monitor_config(ADC_UNIT_1, config);
} else if (idx == ADC_DIGI_MONITOR_IDX1) {
adc_digi_monitor_config(ADC_UNIT_2, config);
}
ADC_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t adc_digi_monitor_enable(adc_digi_monitor_idx_t idx, bool enable)
{
ADC_ENTER_CRITICAL();
if (idx == ADC_DIGI_MONITOR_IDX0) {
adc_ll_digi_monitor_enable(ADC_UNIT_1, enable);
} else if (idx == ADC_DIGI_MONITOR_IDX1) {
adc_ll_digi_monitor_enable(ADC_UNIT_2, enable);
}
ADC_EXIT_CRITICAL();
return ESP_OK;
}

View File

@ -27,6 +27,7 @@
#if SOC_I2S_SUPPORTS_ADC_DAC
#include "hal/adc_ll.h"
#include "driver/adc_i2s_legacy.h"
#endif
#if SOC_I2S_SUPPORTS_APLL
#include "clk_ctrl_os.h"

View File

@ -1,11 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* This file is only a wrapper for `driver/adc.h` for back-compatability.
*/
#include "adc.h"

View File

@ -1,32 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// Internal header for calibration, don't use in app
#include "sdkconfig.h"
#include "esp_err.h"
#include "hal/adc_hal.h"
#ifdef __cplusplus
extern "C" {
#endif
#if !CONFIG_IDF_TARGET_ESP32
/**
* @brief Calibrate the offset of ADC. (Based on the pre-stored efuse or actual calibration)
*
* @param adc_n ADC unit to calibrate
* @param atten Attenuation to use
* @return Always ESP_OK
*/
extern esp_err_t adc_cal_offset(adc_unit_t adc_n, adc_atten_t atten);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -1,4 +1,4 @@
idf_component_register(SRC_DIRS . param_test touch_sensor_test adc_dma_test dac_dma_test
idf_component_register(SRC_DIRS . param_test touch_sensor_test dac_dma_test
PRIV_INCLUDE_DIRS include param_test/include touch_sensor_test/include
PRIV_REQUIRES cmock test_utils driver nvs_flash esp_serial_slave_link
esp_adc_cal esp_timer)
esp_timer esp_adc)

View File

@ -1,654 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
Tests for the adc device driver on ESP32-S2 only
*/
#include "sdkconfig.h"
#include "unity.h"
#include "test_utils.h"
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2) //TODO: IDF-3160
#if CONFIG_IDF_TARGET_ESP32S2
#include "esp_system.h"
#include "esp_intr_alloc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/adc.h"
#include "driver/rtc_io.h"
#include "driver/gpio.h"
#include "unity.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "test_utils.h"
#include "soc/adc_periph.h"
#include "test/test_common_adc.h"
#include "esp_rom_sys.h"
#include "driver/dac.h"
#include "soc/system_reg.h"
#include "soc/spi_reg.h"
#include "soc/soc.h"
#include "soc/lldesc.h"
#include "test/test_adc_dac_dma.h"
#include "driver/adc_deprecated.h"
#include "hal/adc_ll.h"
#include "esp_pm.h"
static const char *TAG = "test_adc";
#define PLATFORM_SELECT (1) //0: pxp; 1: chip
#if (PLATFORM_SELECT == 0) //PXP platform
#include "soc/syscon_reg.h"
#define SET_BREAK_POINT(flag) REG_WRITE(SYSCON_DATE_REG, flag)
//PXP clk is slower.
#define SYS_DELAY_TIME_MOM (1/40)
#define RTC_SLOW_CLK_FLAG 1 // Slow clock is 32KHz.
static void test_pxp_deinit_io(void)
{
for (int i = 0; i < 22; i++) {
rtc_gpio_init(i);
}
}
#else
//PXP clk is slower.
#define SET_BREAK_POINT(flag)
#define SYS_DELAY_TIME_MOM (1)
#define RTC_SLOW_CLK_FLAG 0 // Slow clock is 32KHz.
#endif
#define ADC_REG_BASE_TEST() ({ \
TEST_ASSERT_EQUAL_UINT32(REG_GET_FIELD(APB_SARADC_APB_CTRL_DATE_REG, APB_SARADC_APB_CTRL_DATE), APB_SARADC.apb_ctrl_date); \
TEST_ASSERT_EQUAL_UINT32(REG_GET_FIELD(SENS_SARDATE_REG, SENS_SAR_DATE), SENS.sardate.sar_date); \
TEST_ASSERT_EQUAL_UINT32(REG_GET_FIELD(RTC_IO_DATE_REG, RTC_IO_IO_DATE), RTCIO.date.date); \
})
/** Sample rate = APB_CLK(80 MHz) / (CLK_DIV + 1) / TRIGGER_INTERVAL / 2. */
#define TEST_ADC_TRIGGER_INTERVAL_DEFAULT (40)
#define TEST_ADC_DIGI_CLK_DIV_DEFAULT (9)
static uint8_t adc_test_num = 9;
static adc_channel_t adc_list[SOC_ADC_PATT_LEN_MAX] = {
ADC_CHANNEL_0,
ADC_CHANNEL_1,
ADC_CHANNEL_2,
ADC_CHANNEL_3,
ADC_CHANNEL_4,
ADC_CHANNEL_5,
ADC_CHANNEL_6,
// ADC_CHANNEL_7, // Workaround: IO18 is pullup outside in ESP32S2-Saola Runner.
ADC_CHANNEL_8,
ADC_CHANNEL_9,
};
/* For ESP32S2, it should use same atten, or, it will have error. */
#define TEST_ADC_ATTEN_DEFAULT (ADC_ATTEN_11db)
/* Work mode.
* single: eof_num;
* double: SAR_EOF_NUMBER/2;
* alter: eof_num;
* */
#define SAR_SIMPLE_NUM 512 // Set sample number of enabled unit.
/* Use two DMA linker to save ADC data. ADC sample 1 times -> 2 byte data -> 2 DMA link buf. */
#define SAR_DMA_DATA_SIZE(unit, sample_num) (SAR_EOF_NUMBER(unit, sample_num))
#define SAR_EOF_NUMBER(unit, sample_num) ((sample_num) * (unit))
#define SAR_MEAS_LIMIT_NUM(unit, sample_num) (SAR_SIMPLE_NUM)
#define SAR_SIMPLE_TIMEOUT_MS 1000
typedef struct dma_msg {
uint32_t int_msk;
uint8_t *data;
uint32_t data_len;
} adc_dma_event_t;
static uint8_t link_buf[2][SAR_DMA_DATA_SIZE(2, SAR_SIMPLE_NUM)] = {0};
static lldesc_t dma1 = {0};
static lldesc_t dma2 = {0};
static QueueHandle_t que_adc = NULL;
static adc_dma_event_t adc_evt;
/**
* @brief Reset FSM of adc digital controller.
*
* @return
* - ESP_OK Success
*/
static esp_err_t adc_digi_reset(void)
{
adc_ll_digi_reset();
adc_ll_digi_clear_pattern_table(ADC_NUM_1);
adc_ll_digi_clear_pattern_table(ADC_NUM_2);
return ESP_OK;
}
/** ADC-DMA ISR handler. */
static IRAM_ATTR void adc_dma_isr(void *arg)
{
uint32_t int_st = REG_READ(SPI_DMA_INT_ST_REG(3));
int task_awoken = pdFALSE;
REG_WRITE(SPI_DMA_INT_CLR_REG(3), int_st);
if (int_st & SPI_IN_SUC_EOF_INT_ST_M) {
adc_evt.int_msk = int_st;
xQueueSendFromISR(que_adc, &adc_evt, &task_awoken);
}
if (int_st & SPI_IN_DONE_INT_ST) {
adc_evt.int_msk = int_st;
xQueueSendFromISR(que_adc, &adc_evt, &task_awoken);
}
ESP_EARLY_LOGV(TAG, "int msk%x\n", int_st);
if (task_awoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
/**
* DMA liner initialization and start.
* @param is_loop
* - true: The two dma linked lists are connected end to end, with no end mark (eof).
* - false: The two dma linked lists are connected end to end, with end mark (eof).
*/
static uint32_t adc_dma_linker_init(adc_unit_t adc, bool is_loop)
{
dma1 = (lldesc_t) {
.size = SAR_DMA_DATA_SIZE((adc > 2) ? 2 : 1, SAR_SIMPLE_NUM),
.owner = 1,
.buf = &link_buf[0][0],
.qe.stqe_next = &dma2,
};
dma2 = (lldesc_t) {
.size = SAR_DMA_DATA_SIZE((adc > 2) ? 2 : 1, SAR_SIMPLE_NUM),
.owner = 1,
.buf = &link_buf[1][0],
};
if (is_loop) {
dma2.qe.stqe_next = &dma1;
} else {
dma2.qe.stqe_next = NULL;
}
return (uint32_t)&dma1;
}
#define DEBUG_CHECK_ENABLE 1
#define DEBUG_PRINT_ENABLE 1
#define DEBUG_CHECK_ERROR 10
/**
* Check the ADC-DMA data in linker buffer by input level.
* ideal_level
* - -1: Don't check data.
* - 0: ADC channel voltage is 0v.
* - 1: ADC channel voltage is 3.3v.
* - 2: ADC channel voltage is 1.4v.
*/
static esp_err_t adc_dma_data_check(adc_unit_t adc, int ideal_level)
{
int unit_old = 1;
int ch_cnt = 0;
for (int cnt = 0; cnt < 2; cnt++) {
esp_rom_printf("\n[%s] link_buf[%d]: \n", __func__, cnt % 2);
for (int i = 0; i < SAR_DMA_DATA_SIZE((adc > 2) ? 2 : 1, SAR_SIMPLE_NUM); i += 2) {
uint8_t h = link_buf[cnt % 2][i + 1], l = link_buf[cnt % 2][i];
uint16_t temp = (h << 8 | l);
adc_digi_output_data_t *data = (adc_digi_output_data_t *)&temp;
if (adc > ADC_UNIT_2) { //ADC_ENCODE_11BIT
#if DEBUG_PRINT_ENABLE
if (i % 16 == 0) {
esp_rom_printf("\n");
}
esp_rom_printf("[%d_%d_%04x] ", data->type2.unit, data->type2.channel, data->type2.data);
#endif
#if DEBUG_CHECK_ENABLE
if (ideal_level >= 0) {
TEST_ASSERT_NOT_EQUAL(unit_old, data->type2.unit);
unit_old = data->type2.unit;
if (data->type2.channel > ADC_CHANNEL_MAX) {
printf("Data invalid [%d]\n", data->type2.channel);
continue;
}
int cur_ch = ((ch_cnt++ / 2) % adc_test_num);
TEST_ASSERT_EQUAL( data->type2.channel, adc_list[cur_ch] );
}
if (ideal_level == 1) { // high level 3.3v
TEST_ASSERT_EQUAL( 0x7FF, data->type2.data );
} else if (ideal_level == 0) { // low level 0v
TEST_ASSERT_LESS_THAN( 10, data->type2.data );
} else if (ideal_level == 2) { // middle level 1.4v
TEST_ASSERT_INT_WITHIN( 128, 1100, data->type2.data );
} else if (ideal_level == 3) { // normal level
} else { // no check
}
#endif
} else { //ADC_ENCODE_12BIT
#if DEBUG_PRINT_ENABLE
if (i % 16 == 0) {
esp_rom_printf("\n");
}
esp_rom_printf("[%d_%04x] ", data->type1.channel, data->type1.data);
#endif
#if DEBUG_CHECK_ENABLE
if (ideal_level >= 0) {
int cur_ch = ((ch_cnt++) % adc_test_num);
TEST_ASSERT_EQUAL( adc_list[cur_ch], data->type1.channel );
}
if (ideal_level == 1) { // high level 3.3v
TEST_ASSERT_EQUAL( 0XFFF, data->type1.data );
} else if (ideal_level == 0) { // low level 0v
TEST_ASSERT_LESS_THAN( 10, data->type1.data );
} else if (ideal_level == 2) { // middle level 1.4v
TEST_ASSERT_INT_WITHIN( 256, 2200, data->type1.data );
} else if (ideal_level == 3) { // normal level
} else { // no check
}
#endif
}
link_buf[cnt % 2][i] = 0;
link_buf[cnt % 2][i + 1] = 0;
}
esp_rom_printf("\n");
}
return ESP_OK;
}
static esp_err_t adc_dma_data_multi_st_check(adc_unit_t adc, void *dma_addr, uint32_t int_mask)
{
adc_dma_event_t evt;
ESP_LOGI(TAG, "adc IO normal, test ...");
for (int i = 0; i < adc_test_num; i++) {
adc_io_normal(adc, adc_list[i]);
}
TEST_ESP_OK( adc_digi_start() );
while (1) {
TEST_ASSERT_EQUAL( xQueueReceive(que_adc, &evt, SAR_SIMPLE_TIMEOUT_MS / portTICK_PERIOD_MS), pdTRUE );
if (evt.int_msk & SPI_IN_SUC_EOF_INT_ENA) {
break;
}
}
TEST_ESP_OK( adc_digi_stop() );
adc_dac_dma_linker_start(DMA_ONLY_ADC_INLINK, (void *)dma_addr, int_mask);
adc_digi_reset();
TEST_ESP_OK( adc_dma_data_check(adc, -1) ); // Don't check data.
ESP_LOGI(TAG, "adc IO fake tie high, test ...");
for (int i = 0; i < adc_test_num; i++) {
adc_fake_tie_high(adc, adc_list[i]);
}
TEST_ESP_OK( adc_digi_start() );
while (1) {
TEST_ASSERT_EQUAL( xQueueReceive(que_adc, &evt, SAR_SIMPLE_TIMEOUT_MS / portTICK_PERIOD_MS), pdTRUE );
if (evt.int_msk & SPI_IN_SUC_EOF_INT_ENA) {
break;
}
}
TEST_ESP_OK( adc_digi_stop() );
adc_dac_dma_linker_start(DMA_ONLY_ADC_INLINK, (void *)dma_addr, int_mask);
adc_digi_reset();
TEST_ESP_OK( adc_dma_data_check(adc, 1) );
ESP_LOGI(TAG, "adc IO fake tie low, test ...");
for (int i = 0; i < adc_test_num; i++) {
adc_fake_tie_low(adc, adc_list[i]);
}
TEST_ESP_OK( adc_digi_start() );
while (1) {
TEST_ASSERT_EQUAL( xQueueReceive(que_adc, &evt, SAR_SIMPLE_TIMEOUT_MS / portTICK_PERIOD_MS), pdTRUE );
if (evt.int_msk & SPI_IN_SUC_EOF_INT_ENA) {
break;
}
}
TEST_ESP_OK( adc_digi_stop() );
adc_dac_dma_linker_start(DMA_ONLY_ADC_INLINK, (void *)dma_addr, int_mask);
adc_digi_reset();
TEST_ESP_OK( adc_dma_data_check(adc, 0) );
ESP_LOGI(TAG, "adc IO fake tie middle, test ...");
for (int i = 0; i < adc_test_num; i++) {
adc_fake_tie_middle(adc, adc_list[i]);
}
TEST_ESP_OK( adc_digi_start() );
while (1) {
TEST_ASSERT_EQUAL( xQueueReceive(que_adc, &evt, SAR_SIMPLE_TIMEOUT_MS / portTICK_PERIOD_MS), pdTRUE );
if (evt.int_msk & SPI_IN_SUC_EOF_INT_ENA) {
break;
}
}
TEST_ESP_OK( adc_digi_stop() );
adc_dac_dma_linker_start(DMA_ONLY_ADC_INLINK, (void *)dma_addr, int_mask);
adc_digi_reset();
TEST_ESP_OK( adc_dma_data_check(adc, 2) );
return ESP_OK;
}
#include "soc/apb_saradc_struct.h"
/**
* Test the partten table setting. It's easy wrong.
*
* @param adc_n ADC unit.
* @param in_partten_len The length of partten be set.
* @param in_last_ch The channel number of the last message.
*/
static esp_err_t adc_check_patt_table(adc_unit_t adc, uint32_t in_partten_len, adc_channel_t in_last_ch)
{
esp_err_t ret = ESP_FAIL;
uint8_t index = (in_partten_len - 1) / 4;
uint8_t offset = 24 - ((in_partten_len - 1) % 4) * 8;
uint32_t temp = 0, len;
if (adc & ADC_UNIT_1) {
len = APB_SARADC.ctrl.sar1_patt_len + 1;
temp = APB_SARADC.sar1_patt_tab[index];
printf("patt1 len %d\n", len);
printf("patt1 0x%08x\n", APB_SARADC.sar1_patt_tab[0]);
printf("patt1 0x%08x\n", APB_SARADC.sar1_patt_tab[1]);
printf("patt1 0x%08x\n", APB_SARADC.sar1_patt_tab[2]);
printf("patt1 0x%08x\n", APB_SARADC.sar1_patt_tab[3]);
if (in_partten_len == len) {
if (in_last_ch == (((temp >> (offset + 4))) & 0xf)) {
ret = ESP_OK;
}
}
}
if (adc & ADC_UNIT_2) {
len = APB_SARADC.ctrl.sar2_patt_len + 1;
temp = APB_SARADC.sar2_patt_tab[index];
printf("patt2 len %d\n", len);
printf("patt2 0x%08x\n", APB_SARADC.sar2_patt_tab[0]);
printf("patt2 0x%08x\n", APB_SARADC.sar2_patt_tab[1]);
printf("patt2 0x%08x\n", APB_SARADC.sar2_patt_tab[2]);
printf("patt2 0x%08x\n", APB_SARADC.sar2_patt_tab[3]);
if (in_partten_len == len) {
if (in_last_ch == (((temp >> (offset + 4))) & 0xf)) {
ret = ESP_OK;
}
}
}
return ret;
}
/**
* Testcase: Check the base function of ADC-DMA. Include:
* - Various conversion modes.
* - Whether the channel and data are lost.
* - Whether the data is the same as the channel voltage.
*/
int test_adc_dig_dma_single_unit(adc_unit_t adc)
{
ESP_LOGI(TAG, " >> %s << ", __func__);
ESP_LOGI(TAG, " >> adc unit: %x << ", adc);
TEST_ESP_OK( adc_digi_init() );
/* arbiter config */
adc_arbiter_t arb_cfg = {
.mode = ADC_ARB_MODE_FIX,
.dig_pri = 0,
.pwdet_pri = 2,
.rtc_pri = 1,
};
TEST_ESP_OK( adc_arbiter_config(ADC_UNIT_2, &arb_cfg) ); // If you want use force
adc_digi_config_t config = {
.conv_limit_en = false,
.conv_limit_num = 0,
.interval = TEST_ADC_TRIGGER_INTERVAL_DEFAULT,
.dig_clk.use_apll = 0, // APB clk
.dig_clk.div_num = TEST_ADC_DIGI_CLK_DIV_DEFAULT,
.dig_clk.div_b = 0,
.dig_clk.div_a = 0,
.dma_eof_num = SAR_EOF_NUMBER((adc > 2) ? 2 : 1, SAR_SIMPLE_NUM),
};
/* Config pattern table */
adc_digi_pattern_table_t adc1_patt[SOC_ADC_PATT_LEN_MAX] = {0};
adc_digi_pattern_table_t adc2_patt[SOC_ADC_PATT_LEN_MAX] = {0};
if (adc & ADC_UNIT_1) {
config.adc1_pattern_len = adc_test_num;
config.adc1_pattern = adc1_patt;
for (int i = 0; i < adc_test_num; i++) {
adc1_patt[i].atten = TEST_ADC_ATTEN_DEFAULT;
adc1_patt[i].channel = adc_list[i];
adc_gpio_init(ADC_UNIT_1, adc_list[i]);
}
}
if (adc & ADC_UNIT_2) {
config.adc2_pattern_len = adc_test_num;
config.adc2_pattern = adc2_patt;
for (int i = 0; i < adc_test_num; i++) {
adc2_patt[i].atten = TEST_ADC_ATTEN_DEFAULT;
adc2_patt[i].channel = adc_list[i];
adc_gpio_init(ADC_UNIT_2, adc_list[i]);
}
}
if (adc == ADC_UNIT_1) {
config.conv_mode = ADC_CONV_SINGLE_UNIT_1;
config.format = ADC_DIGI_FORMAT_12BIT;
} else if (adc == ADC_UNIT_2) {
config.conv_mode = ADC_CONV_SINGLE_UNIT_2;
config.format = ADC_DIGI_FORMAT_12BIT;
} else if (adc == ADC_UNIT_BOTH) {
config.conv_mode = ADC_CONV_BOTH_UNIT;
config.format = ADC_DIGI_FORMAT_11BIT;
} else if (adc == ADC_UNIT_ALTER) {
config.conv_mode = ADC_CONV_ALTER_UNIT;
config.format = ADC_DIGI_FORMAT_11BIT;
}
TEST_ESP_OK( adc_digi_controller_config(&config) );
/* ADC-DMA linker init */
if (que_adc == NULL) {
que_adc = xQueueCreate(5, sizeof(adc_dma_event_t));
} else {
xQueueReset(que_adc);
}
uint32_t int_mask = SPI_IN_SUC_EOF_INT_ENA;
uint32_t dma_addr = adc_dma_linker_init(adc, false);
adc_dac_dma_isr_register(adc_dma_isr, NULL, int_mask);
adc_dac_dma_linker_start(DMA_ONLY_ADC_INLINK, (void *)dma_addr, int_mask);
TEST_ESP_OK( adc_check_patt_table(adc, adc_test_num, adc_list[adc_test_num - 1]) );
adc_dma_data_multi_st_check(adc, (void *)dma_addr, int_mask);
adc_dac_dma_linker_deinit();
adc_dac_dma_isr_deregister(adc_dma_isr, NULL);
TEST_ESP_OK( adc_digi_deinit() );
vTaskDelay(10 / portTICK_PERIOD_MS);
return 0;
}
TEST_CASE("ADC DMA single read", "[ADC]")
{
test_adc_dig_dma_single_unit(ADC_UNIT_BOTH);
test_adc_dig_dma_single_unit(ADC_UNIT_ALTER);
test_adc_dig_dma_single_unit(ADC_UNIT_1);
test_adc_dig_dma_single_unit(ADC_UNIT_2);
}
#include "touch_scope.h"
/**
* 0: ADC1 channels raw data debug.
* 1: ADC2 channels raw data debug.
* 2: ADC1 one channel raw data debug.
*/
#define SCOPE_DEBUG_TYPE 0
#define SCOPE_DEBUG_CHANNEL_MAX (10)
#define SCOPE_DEBUG_ENABLE (0)
#define SCOPE_UART_BUADRATE (256000)
#define SCOPE_DEBUG_FREQ_MS (50)
#define SCOPE_OUTPUT_UART (0)
static float scope_temp[SCOPE_DEBUG_CHANNEL_MAX] = {0}; // max scope channel is 10.
int test_adc_dig_scope_debug_unit(adc_unit_t adc)
{
ESP_LOGI(TAG, " >> %s << ", __func__);
ESP_LOGI(TAG, " >> adc unit: %x << ", adc);
TEST_ESP_OK( adc_digi_init() );
if (adc & ADC_UNIT_2) {
/* arbiter config */
adc_arbiter_t arb_cfg = {
.mode = ADC_ARB_MODE_FIX,
.dig_pri = 0,
.pwdet_pri = 2,
.rtc_pri = 1,
};
TEST_ESP_OK( adc_arbiter_config(ADC_UNIT_2, &arb_cfg) ); // If you want use force
}
adc_digi_config_t config = {
.conv_limit_en = false,
.conv_limit_num = 0,
.interval = TEST_ADC_TRIGGER_INTERVAL_DEFAULT,
.dig_clk.use_apll = 0, // APB clk
.dig_clk.div_num = TEST_ADC_DIGI_CLK_DIV_DEFAULT,
.dig_clk.div_a = 0,
.dig_clk.div_b = 0,
.dma_eof_num = SAR_EOF_NUMBER((adc > 2) ? 2 : 1, SAR_SIMPLE_NUM),
};
/* Config pattern table */
adc_digi_pattern_table_t adc1_patt[SOC_ADC_PATT_LEN_MAX] = {0};
adc_digi_pattern_table_t adc2_patt[SOC_ADC_PATT_LEN_MAX] = {0};
if (adc & ADC_UNIT_1) {
config.adc1_pattern_len = adc_test_num;
config.adc1_pattern = adc1_patt;
for (int i = 0; i < adc_test_num; i++) {
adc1_patt[i].atten = TEST_ADC_ATTEN_DEFAULT;
adc1_patt[i].channel = adc_list[i];
adc_gpio_init(ADC_UNIT_1, adc_list[i]);
}
}
if (adc & ADC_UNIT_2) {
config.adc2_pattern_len = adc_test_num;
config.adc2_pattern = adc2_patt;
for (int i = 0; i < adc_test_num; i++) {
adc2_patt[i].atten = TEST_ADC_ATTEN_DEFAULT;
adc2_patt[i].channel = adc_list[i];
adc_gpio_init(ADC_UNIT_2, adc_list[i]);
}
}
if (adc == ADC_UNIT_1) {
config.conv_mode = ADC_CONV_SINGLE_UNIT_1;
config.format = ADC_DIGI_FORMAT_12BIT;
} else if (adc == ADC_UNIT_2) {
config.conv_mode = ADC_CONV_SINGLE_UNIT_2;
config.format = ADC_DIGI_FORMAT_12BIT;
} else if (adc == ADC_UNIT_BOTH) {
config.conv_mode = ADC_CONV_BOTH_UNIT;
config.format = ADC_DIGI_FORMAT_11BIT;
} else if (adc == ADC_UNIT_ALTER) {
config.conv_mode = ADC_CONV_ALTER_UNIT;
config.format = ADC_DIGI_FORMAT_11BIT;
}
TEST_ESP_OK( adc_digi_controller_config(&config) );
/* ADC-DMA linker init */
if (que_adc == NULL) {
que_adc = xQueueCreate(5, sizeof(adc_dma_event_t));
} else {
xQueueReset(que_adc);
}
uint32_t int_mask = SPI_IN_SUC_EOF_INT_ENA;
uint32_t dma_addr = adc_dma_linker_init(adc, false);
adc_dac_dma_isr_register(adc_dma_isr, NULL, int_mask);
adc_dac_dma_linker_start(DMA_ONLY_ADC_INLINK, (void *)dma_addr, int_mask);
ESP_LOGI(TAG, "adc IO fake tie middle, test ...");
for (int i = 0; i < adc_test_num; i++) {
adc_fake_tie_middle(adc, adc_list[i]);
}
return 0;
}
static void scope_output(int adc_num, int channel, int data)
{
/** can replace by uart log.*/
#if SCOPE_OUTPUT_UART
static int icnt = 0;
if (icnt++ % 8 == 0) {
esp_rom_printf("\n");
}
esp_rom_printf("[%d_%d_%04x] ", adc_num, channel, data);
return;
#endif
#if SCOPE_DEBUG_TYPE == 0
if (adc_num != 0) {
return;
}
#elif SCOPE_DEBUG_TYPE == 1
if (adc_num != 1) {
return;
}
#endif
int i;
/* adc Read */
for (i = 0; i < adc_test_num; i++) {
if (adc_list[i] == channel && scope_temp[i] == 0) {
scope_temp[i] = data;
break;
}
}
if (i == adc_test_num) {
test_tp_print_to_scope(scope_temp, adc_test_num);
vTaskDelay(SCOPE_DEBUG_FREQ_MS / portTICK_PERIOD_MS);
for (int i = 0; i < adc_test_num; i++) {
scope_temp[i] = 0;
}
}
}
/**
* Manual test: Capture ADC-DMA data and display it on the serial oscilloscope. Used to observe the stability of the data.
* Use step:
* 1. Run this test from the unit test app.
* 2. Use `ESP-Tuning Tool`(download from `www.espressif.com`) to capture.
* 3. The readings of multiple channels will be displayed on the tool.
*/
TEST_CASE("test_adc_digi_slope_debug", "[adc_dma][ignore]")
{
adc_dma_event_t evt;
test_tp_scope_debug_init(0, -1, -1, SCOPE_UART_BUADRATE);
adc_unit_t adc = ADC_CONV_BOTH_UNIT;
test_adc_dig_scope_debug_unit(adc);
while (1) {
TEST_ESP_OK( adc_digi_start() );
TEST_ASSERT_EQUAL( xQueueReceive(que_adc, &evt, portMAX_DELAY), pdTRUE );
if (evt.int_msk & SPI_IN_SUC_EOF_INT_ST) {
TEST_ESP_OK( adc_digi_stop() );
adc_digi_reset();
for (int cnt = 0; cnt < 2; cnt++) {
esp_rom_printf("cnt%d\n", cnt);
for (int i = 0; i < SAR_DMA_DATA_SIZE((adc > 2) ? 2 : 1, SAR_SIMPLE_NUM); i += 2) {
uint8_t h = link_buf[cnt % 2][i + 1], l = link_buf[cnt % 2][i];
uint16_t temp = (h << 8 | l);
adc_digi_output_data_t *data = (adc_digi_output_data_t *)&temp;
if (adc > ADC_UNIT_2) { //ADC_ENCODE_11BIT
scope_output(data->type2.unit, data->type2.channel, data->type2.data);
} else { //ADC_ENCODE_12BIT
if (adc == ADC_UNIT_1) {
scope_output(0, data->type1.channel, data->type1.data);
} else if (adc == ADC_UNIT_2) {
scope_output(1, data->type1.channel, data->type1.data);
}
}
link_buf[cnt % 2][i] = 0;
link_buf[cnt % 2][i + 1] = 0;
}
}
}
}
}
#endif // CONFIG_IDF_TARGET_ESP32S2
#endif //#if !DISABLED_FOR_TARGETS(ESP32S2)

View File

@ -15,7 +15,6 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/adc.h"
#include "driver/rtc_io.h"
#include "driver/gpio.h"
#include "unity.h"

View File

@ -6,7 +6,7 @@
#pragma once
#include "driver/adc.h"
#include "hal/adc_types.h"
/**@{*/
/**

View File

@ -1,608 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <sys/param.h>
#include <string.h>
#include "esp_log.h"
#include "test_utils.h"
#include "esp_adc_cal.h"
#include "driver/adc_common.h"
#include "esp_cpu.h"
__attribute__((unused)) static const char *TAG = "ADC";
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32, ESP32S2, ESP32S3, ESP32C3, ESP32C2)
//TODO: IDF-3160
#define TEST_COUNT 4096
#define MAX_ARRAY_SIZE 4096
#define TEST_ATTEN ADC_ATTEN_MAX //Set to ADC_ATTEN_*db to test a single attenuation only
static int s_adc_count[MAX_ARRAY_SIZE]={};
static int s_adc_offset = -1;
static int insert_point(uint32_t value)
{
const bool fixed_size = true;
if (s_adc_offset < 0) {
if (fixed_size) {
TEST_ASSERT_GREATER_OR_EQUAL(4096, MAX_ARRAY_SIZE);
s_adc_offset = 0; //Fixed to 0 because the array can hold all the data in 12 bits
} else {
s_adc_offset = MAX((int)value - MAX_ARRAY_SIZE/2, 0);
}
}
if (!fixed_size && (value < s_adc_offset || value >= s_adc_offset + MAX_ARRAY_SIZE)) {
TEST_ASSERT_GREATER_OR_EQUAL(s_adc_offset, value);
TEST_ASSERT_LESS_THAN(s_adc_offset + MAX_ARRAY_SIZE, value);
}
s_adc_count[value - s_adc_offset] ++;
return value - s_adc_offset;
}
static void reset_array(void)
{
memset(s_adc_count, 0, sizeof(s_adc_count));
s_adc_offset = -1;
}
static uint32_t get_average(void)
{
uint32_t sum = 0;
int count = 0;
for (int i = 0; i < MAX_ARRAY_SIZE; i++) {
sum += s_adc_count[i] * (s_adc_offset+i);
count += s_adc_count[i];
}
return sum/count;
}
static void print_summary(bool figure)
{
const int MAX_WIDTH=20;
int max_count = 0;
int start = -1;
int end = -1;
uint32_t sum = 0;
int count = 0;
for (int i = 0; i < MAX_ARRAY_SIZE; i++) {
if (s_adc_count[i] > max_count) {
max_count = s_adc_count[i];
}
if (s_adc_count[i] > 0 && start < 0) {
start = i;
}
if (s_adc_count[i] > 0) {
end = i;
}
count += s_adc_count[i];
sum += s_adc_count[i] * (s_adc_offset+i);
}
if (figure) {
for (int i = start; i <= end; i++) {
printf("%4d ", i+s_adc_offset);
int count = s_adc_count[i] * MAX_WIDTH / max_count;
for (int j = 0; j < count; j++) {
putchar('|');
}
printf(" %d\n", s_adc_count[i]);
}
}
float average = (float)sum/count;
float variation_square = 0;
for (int i = start; i <= end; i ++) {
if (s_adc_count[i] == 0) {
continue;
}
float delta = i + s_adc_offset - average;
variation_square += (delta * delta) * s_adc_count[i];
}
printf("%d points.\n", count);
printf("average: %.1f\n", (float)sum/count);
printf("std: %.2f\n", sqrt(variation_square/count));
}
static void continuous_adc_init(uint16_t adc1_chan_mask, uint16_t adc2_chan_mask, adc_channel_t *channel, uint8_t channel_num, adc_atten_t atten)
{
adc_digi_init_config_t adc_dma_config = {
.max_store_buf_size = TEST_COUNT*2,
.conv_num_each_intr = 128,
.adc1_chan_mask = adc1_chan_mask,
.adc2_chan_mask = adc2_chan_mask,
};
TEST_ESP_OK(adc_digi_initialize(&adc_dma_config));
adc_digi_pattern_table_t adc_pattern[10] = {0};
adc_digi_config_t dig_cfg = {
.conv_limit_en = 0,
.conv_limit_num = 250,
.sample_freq_hz = 83333,
};
dig_cfg.adc_pattern_len = channel_num;
for (int i = 0; i < channel_num; i++) {
uint8_t unit = ((channel[i] >> 3) & 0x1);
uint8_t ch = channel[i] & 0x7;
adc_pattern[i].atten = atten;
adc_pattern[i].channel = ch;
adc_pattern[i].unit = unit;
}
dig_cfg.adc_pattern = adc_pattern;
TEST_ESP_OK(adc_digi_controller_config(&dig_cfg));
}
TEST_CASE("test_adc_dma", "[adc][ignore][manual]")
{
uint16_t adc1_chan_mask = BIT(2);
uint16_t adc2_chan_mask = 0;
adc_channel_t channel[1] = {ADC1_CHANNEL_2};
adc_atten_t target_atten = TEST_ATTEN;
const int output_data_size = sizeof(adc_digi_output_data_t);
int buffer_size = TEST_COUNT*output_data_size;
uint8_t* read_buf = malloc(buffer_size);
TEST_ASSERT_NOT_NULL(read_buf);
adc_atten_t atten;
bool print_figure;
if (target_atten == ADC_ATTEN_MAX) {
atten = ADC_ATTEN_DB_0;
target_atten = ADC_ATTEN_DB_11;
print_figure = false;
} else {
atten = target_atten;
print_figure = true;
}
while (1) {
ESP_LOGI("TEST_ADC", "Test with atten: %d", atten);
memset(read_buf, 0xce, buffer_size);
bool do_calibration = false;
esp_adc_cal_characteristics_t chan1_char = {};
esp_adc_cal_value_t cal_ret = esp_adc_cal_characterize(ADC_UNIT_1, atten, ADC_WIDTH_12Bit, 0, &chan1_char);
if (cal_ret == ESP_ADC_CAL_VAL_EFUSE_TP) {
do_calibration = true;
}
continuous_adc_init(adc1_chan_mask, adc2_chan_mask, channel, sizeof(channel) / sizeof(adc_channel_t), atten);
adc_digi_start();
int remain_count = TEST_COUNT;
while (remain_count) {
int already_got = TEST_COUNT - remain_count;
uint32_t ret_num;
TEST_ESP_OK(adc_digi_read_bytes(read_buf + already_got*output_data_size,
remain_count*output_data_size, &ret_num, ADC_MAX_DELAY));
TEST_ASSERT((ret_num % output_data_size) == 0);
remain_count -= ret_num / output_data_size;
}
adc_digi_output_data_t *p = (void*)read_buf;
reset_array();
for (int i = 0; i < TEST_COUNT; i++) {
insert_point(p[i].type2.data);
}
print_summary(print_figure);
if (do_calibration) {
uint32_t raw = get_average();
uint32_t voltage_mv = esp_adc_cal_raw_to_voltage(raw, &chan1_char);
printf("Voltage = %d mV\n", voltage_mv);
}
adc_digi_stop();
TEST_ESP_OK(adc_digi_deinitialize());
if (atten == target_atten) {
break;
}
atten++;
}
free(read_buf);
}
TEST_CASE("test_adc_single", "[adc][ignore][manual]")
{
adc_atten_t target_atten = TEST_ATTEN;
adc_atten_t atten;
bool print_figure;
if (target_atten == ADC_ATTEN_MAX) {
atten = ADC_ATTEN_DB_0;
target_atten = ADC_ATTEN_DB_11;
print_figure = false;
} else {
atten = target_atten;
print_figure = true;
}
adc1_config_width(ADC_WIDTH_BIT_12);
while (1) {
ESP_LOGI("TEST_ADC", "Test with atten: %d", atten);
adc1_config_channel_atten(ADC1_CHANNEL_2, atten);
bool do_calibration = false;
esp_adc_cal_characteristics_t chan1_char = {};
esp_adc_cal_value_t cal_ret = esp_adc_cal_characterize(ADC_UNIT_1, atten, ADC_WIDTH_12Bit, 0, &chan1_char);
if (cal_ret == ESP_ADC_CAL_VAL_EFUSE_TP) {
do_calibration = true;
}
const int test_count = TEST_COUNT;
adc1_channel_t channel = ADC1_CHANNEL_2;
while (1) {
reset_array();
for (int i = 0; i < test_count; i++) {
uint32_t raw = adc1_get_raw(channel);
insert_point(raw);
}
print_summary(print_figure);
break;
}
if (do_calibration) {
uint32_t raw = get_average();
uint32_t voltage_mv = esp_adc_cal_raw_to_voltage(raw, &chan1_char);
printf("Voltage = %d mV\n", voltage_mv);
}
if (atten == target_atten) {
break;
}
atten++;
}
}
#endif //#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32, ESP32S2, ESP32S3, ESP32C3, ESP32C2)
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2) //TODO IDF-3908
/********************************************************************************
* ADC Speed Related Tests
********************************************************************************/
#define RECORD_TIME_PREPARE() uint32_t __t1, __t2
#define RECORD_TIME_START() do {__t1 = esp_cpu_get_ccount();}while(0)
#define RECORD_TIME_END(p_time) do{__t2 = esp_cpu_get_ccount(); *p_time = (__t2-__t1);}while(0)
#define GET_US_BY_CCOUNT(t) ((double)t/CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ)
//ADC Channels
#if CONFIG_IDF_TARGET_ESP32
#define ADC1_CALI_TEST_CHAN0 ADC1_CHANNEL_6
#define ADC2_CALI_TEST_CHAN0 ADC2_CHANNEL_0
#else
#define ADC1_CALI_TEST_CHAN0 ADC1_CHANNEL_2
#define ADC2_CALI_TEST_CHAN0 ADC2_CHANNEL_0
#endif
//ADC Calibration
#if CONFIG_IDF_TARGET_ESP32
#define ADC_TEST_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_VREF
#elif CONFIG_IDF_TARGET_ESP32S2
#define ADC_TEST_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP
#elif CONFIG_IDF_TARGET_ESP32C3
#define ADC_TEST_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP
#elif CONFIG_IDF_TARGET_ESP32S3
#define ADC_TEST_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP_FIT
#endif
#define TIMES_PER_ATTEN 10
static esp_adc_cal_characteristics_t adc1_chars;
static esp_adc_cal_characteristics_t adc2_chars;
static void adc_single_cali_init(adc_unit_t adc_n, adc_channel_t chan, uint32_t atten)
{
esp_err_t ret;
esp_adc_cal_value_t ret_val = ESP_ADC_CAL_VAL_NOT_SUPPORTED;
ret = esp_adc_cal_check_efuse(ADC_TEST_CALI_SCHEME);
if (ret == ESP_ERR_NOT_SUPPORTED) {
ESP_LOGE(TAG, "Cali scheme not supported!");
TEST_ASSERT(ret != ESP_ERR_NOT_SUPPORTED);
} else if (ret != ESP_OK) {
ESP_LOGW(TAG, "No cali eFuse, but will run the test");
}
if (adc_n == ADC_UNIT_1) {
ret_val = esp_adc_cal_characterize(adc_n, atten, ADC_WIDTH_BIT_DEFAULT, 0, &adc1_chars);
TEST_ESP_OK(adc1_config_width(ADC_WIDTH_BIT_DEFAULT));
TEST_ESP_OK(adc1_config_channel_atten((adc1_channel_t)chan, atten));
} else if (adc_n == ADC_UNIT_2) {
TEST_ESP_OK(adc2_config_channel_atten((adc2_channel_t)chan, atten));
ret_val = esp_adc_cal_characterize(adc_n, atten, ADC_WIDTH_BIT_DEFAULT, 0, &adc2_chars);
}
if (ret_val == ESP_ADC_CAL_VAL_NOT_SUPPORTED) {
ESP_LOGW(TAG, "No cali eFuse, or invalid arg, but will run the test");
}
ESP_LOGI(TAG, "ADC%d, channel%d, atten%d", adc_n, chan, atten);
}
static IRAM_ATTR NOINLINE_ATTR uint32_t get_cali_time_in_ccount(uint32_t adc_raw, esp_adc_cal_characteristics_t *chars)
{
uint32_t time;
RECORD_TIME_PREPARE();
RECORD_TIME_START();
esp_adc_cal_raw_to_voltage(adc_raw, chars);
RECORD_TIME_END(&time);
return time;
}
TEST_CASE("test_adc_single_cali_time", "[adc][ignore][manual]")
{
ESP_LOGI(TAG, "CPU FREQ is %dMHz", CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ);
uint32_t adc1_time_record[4][TIMES_PER_ATTEN] = {};
uint32_t adc2_time_record[4][TIMES_PER_ATTEN] = {};
int adc1_raw = 0;
int adc2_raw = 0;
//atten0 ~ atten3
for (int i = 0; i < 4; i++) {
ESP_LOGI(TAG, "----------------atten%d----------------", i);
adc_single_cali_init(ADC_UNIT_1, ADC1_CALI_TEST_CHAN0, i);
adc_single_cali_init(ADC_UNIT_2, ADC2_CALI_TEST_CHAN0, i);
for (int j = 0; j < TIMES_PER_ATTEN; j++) {
adc1_raw = adc1_get_raw(ADC1_CALI_TEST_CHAN0);
TEST_ESP_OK(adc2_get_raw(ADC2_CALI_TEST_CHAN0, ADC_WIDTH_BIT_DEFAULT, &adc2_raw));
adc1_time_record[i][j] = get_cali_time_in_ccount(adc1_raw, &adc1_chars);
adc2_time_record[i][j] = get_cali_time_in_ccount(adc2_raw, &adc2_chars);
IDF_LOG_PERFORMANCE("ADC1 Cali time", "%d us", (int)GET_US_BY_CCOUNT(adc1_time_record[i][j]));
IDF_LOG_PERFORMANCE("ADC2 Cali time", "%d us", (int)GET_US_BY_CCOUNT(adc2_time_record[i][j]));
}
}
}
/********************************************************************************
* ADC Single with Light Sleep
********************************************************************************/
#include <inttypes.h>
#include "esp_sleep.h"
#include "esp_private/regi2c_ctrl.h"
#if REGI2C_ANA_CALI_PD_WORKAROUND
#include "soc/regi2c_saradc.h"
#endif
//ADC Channels
#if CONFIG_IDF_TARGET_ESP32
#define ADC1_SLEEP_TEST_CHAN ADC1_CHANNEL_6
#define ADC2_SLEEP_TEST_CHAN ADC2_CHANNEL_0
static const char *TAG_CH[2][10] = {{"ADC1_CH6"}, {"ADC2_CH0"}};
#else
#define ADC1_SLEEP_TEST_CHAN ADC1_CHANNEL_2
#define ADC2_SLEEP_TEST_CHAN ADC2_CHANNEL_0
static const char *TAG_CH[2][10] = {{"ADC1_CH2"}, {"ADC2_CH0"}};
#endif
//ADC Attenuation
#define ADC_SLEEP_TEST_ATTEN ADC_ATTEN_DB_6
//ADC Calibration
#if CONFIG_IDF_TARGET_ESP32
#define ADC_SLEEP_TEST_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_VREF
#elif CONFIG_IDF_TARGET_ESP32S2
#define ADC_SLEEP_TEST_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP
#elif CONFIG_IDF_TARGET_ESP32C3
#define ADC_SLEEP_TEST_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP
#elif CONFIG_IDF_TARGET_ESP32S3
#define ADC_SLEEP_TEST_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP_FIT
#endif
static esp_adc_cal_characteristics_t adc1_chars;
static esp_adc_cal_characteristics_t adc2_chars;
static bool adc_calibration_init(void)
{
esp_err_t ret;
bool cali_enable = false;
ret = esp_adc_cal_check_efuse(ADC_SLEEP_TEST_CALI_SCHEME);
if (ret == ESP_ERR_NOT_SUPPORTED) {
ESP_LOGW(TAG, "Calibration scheme not supported, skip software calibration");
} else if (ret == ESP_ERR_INVALID_VERSION) {
ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
} else if (ret == ESP_OK) {
cali_enable = true;
esp_adc_cal_characterize(ADC_UNIT_1, ADC_SLEEP_TEST_ATTEN, ADC_WIDTH_BIT_DEFAULT, 0, &adc1_chars);
esp_adc_cal_characterize(ADC_UNIT_2, ADC_SLEEP_TEST_ATTEN, ADC_WIDTH_BIT_DEFAULT, 0, &adc2_chars);
} else {
ESP_LOGE(TAG, "Invalid arg");
}
return cali_enable;
}
#define TEST_REGI2C_ANA_CALI_BYTE_NUM 8
TEST_CASE("test ADC1 Single Read with Light Sleep", "[adc][manul][ignore]")
{
//ADC1 config
TEST_ESP_OK(adc1_config_width(ADC_WIDTH_BIT_DEFAULT));
TEST_ESP_OK(adc1_config_channel_atten(ADC1_SLEEP_TEST_CHAN, ADC_SLEEP_TEST_ATTEN));
//ADC config calibration
bool cali_en = adc_calibration_init();
int raw_expected = 0;
uint32_t cali_expected = 0;
uint8_t regi2c_cali_val_before[TEST_REGI2C_ANA_CALI_BYTE_NUM] = {};
int raw_after_sleep = 0;
uint32_t cali_after_sleep = 0;
uint8_t regi2c_cali_val_after[TEST_REGI2C_ANA_CALI_BYTE_NUM] = {};
//---------------------------------Before Sleep-----------------------------------//
ESP_LOGI("Before", "Light Sleep");
//Read
raw_expected = adc1_get_raw(ADC1_SLEEP_TEST_CHAN);
if (cali_en) {
cali_expected = esp_adc_cal_raw_to_voltage(raw_expected, &adc1_chars);
}
#if REGI2C_ANA_CALI_PD_WORKAROUND
//Print regi2c
for (int i = 0; i < TEST_REGI2C_ANA_CALI_BYTE_NUM; i++) {
regi2c_cali_val_before[i] = regi2c_ctrl_read_reg(I2C_SAR_ADC, I2C_SAR_ADC_HOSTID, i);
printf("regi2c cali val is 0x%x", regi2c_cali_val_before[i]);
}
printf("\n");
#endif
//Print result
ESP_LOGI(TAG_CH[0][0], "ADC1 raw data: %d", raw_expected);
if (cali_en) {
ESP_LOGI(TAG_CH[0][0], "ADC1 cali data: %d", cali_expected);
}
//---------------------------------After Sleep-----------------------------------//
ESP_LOGI("After", "Light Sleep");
esp_sleep_enable_timer_wakeup(30 * 1000);
esp_light_sleep_start();
ESP_LOGI(TAG, "Wakeup from light sleep.");
#if REGI2C_ANA_CALI_PD_WORKAROUND
//Print regi2c
for (int i = 0; i < TEST_REGI2C_ANA_CALI_BYTE_NUM; i++) {
regi2c_cali_val_after[i] = regi2c_ctrl_read_reg(I2C_SAR_ADC, I2C_SAR_ADC_HOSTID, i);
printf("regi2c cali val is 0x%x", regi2c_cali_val_after[i]);
}
printf("\n");
#endif
//Read
raw_after_sleep = adc1_get_raw(ADC1_SLEEP_TEST_CHAN);
if (cali_en) {
cali_after_sleep = esp_adc_cal_raw_to_voltage(raw_after_sleep, &adc1_chars);
}
//Print result
ESP_LOGI(TAG_CH[0][0], "after light sleep, ADC1 cali data: %d", raw_after_sleep);
if (cali_en) {
ESP_LOGI(TAG_CH[0][0], "after light sleep, ADC1 cali data: %d", cali_after_sleep);
}
//Compare
int32_t raw_diff = raw_expected - raw_after_sleep;
IDF_LOG_PERFORMANCE("ADC1 raw diff after sleep", "%d", raw_diff);
if (cali_en) {
int32_t cali_diff = cali_expected - cali_after_sleep;
IDF_LOG_PERFORMANCE("ADC1 cali diff after sleep", "%d mV", cali_diff);
}
for (int i = 0; i < TEST_REGI2C_ANA_CALI_BYTE_NUM; i++) {
TEST_ASSERT_EQUAL(regi2c_cali_val_before[i], regi2c_cali_val_after[i]);
}
}
TEST_CASE("test ADC2 Single Read with Light Sleep", "[adc][manul][ignore]")
{
//ADC2 config
ESP_ERROR_CHECK(adc2_config_channel_atten(ADC2_SLEEP_TEST_CHAN, ADC_SLEEP_TEST_ATTEN));
//ADC config calibration
bool cali_en = adc_calibration_init();
int raw_expected = 0;
uint32_t cali_expected = 0;
uint8_t regi2c_cali_val_before[TEST_REGI2C_ANA_CALI_BYTE_NUM] = {};
int raw_after_sleep = 0;
uint32_t cali_after_sleep = 0;
uint8_t regi2c_cali_val_after[TEST_REGI2C_ANA_CALI_BYTE_NUM] = {};
//---------------------------------Before Sleep-----------------------------------//
ESP_LOGI("Before", "Light Sleep");
//Read
TEST_ESP_OK(adc2_get_raw(ADC2_SLEEP_TEST_CHAN, ADC_WIDTH_BIT_DEFAULT, &raw_expected));
if (cali_en) {
cali_expected = esp_adc_cal_raw_to_voltage(raw_expected, &adc2_chars);
}
#if REGI2C_ANA_CALI_PD_WORKAROUND
//Print regi2c
for (int i = 0; i < TEST_REGI2C_ANA_CALI_BYTE_NUM; i++) {
regi2c_cali_val_before[i] = regi2c_ctrl_read_reg(I2C_SAR_ADC, I2C_SAR_ADC_HOSTID, i);
printf("regi2c cali val is 0x%x", regi2c_cali_val_before[i]);
}
printf("\n");
#endif
//Print result
ESP_LOGI(TAG_CH[1][0], "ADC2 raw data: %d", raw_expected);
if (cali_en) {
ESP_LOGI(TAG_CH[1][0], "ADC2 cali data: %d", cali_expected);
}
//---------------------------------After Sleep-----------------------------------//
ESP_LOGI("After", "Light Sleep");
esp_sleep_enable_timer_wakeup(30 * 1000);
esp_light_sleep_start();
ESP_LOGI(TAG, "Wakeup from light sleep.");
#if REGI2C_ANA_CALI_PD_WORKAROUND
//Print regi2c
for (int i = 0; i < TEST_REGI2C_ANA_CALI_BYTE_NUM; i++) {
regi2c_cali_val_after[i] = regi2c_ctrl_read_reg(I2C_SAR_ADC, I2C_SAR_ADC_HOSTID, i);
printf("regi2c cali val is 0x%x", regi2c_cali_val_after[i]);
}
printf("\n");
#endif
//Read
TEST_ESP_OK(adc2_get_raw(ADC2_SLEEP_TEST_CHAN, ADC_WIDTH_BIT_DEFAULT, &raw_after_sleep));
if (cali_en) {
cali_after_sleep += esp_adc_cal_raw_to_voltage(raw_after_sleep, &adc2_chars);
}
//Print result
ESP_LOGI(TAG_CH[1][0], "after light sleep, ADC2 cali data: %d", raw_after_sleep);
if (cali_en) {
ESP_LOGI(TAG_CH[1][0], "after light sleep, ADC2 cali data: %d", cali_after_sleep);
}
//Compare
int32_t raw_diff = raw_expected - raw_after_sleep;
IDF_LOG_PERFORMANCE("ADC2 raw diff after sleep", "%d", raw_diff);
if (cali_en) {
int32_t cali_diff = cali_expected - cali_after_sleep;
IDF_LOG_PERFORMANCE("ADC2 cali diff after sleep", "%d mV", cali_diff);
}
for (int i = 0; i < TEST_REGI2C_ANA_CALI_BYTE_NUM; i++) {
TEST_ASSERT_EQUAL(regi2c_cali_val_before[i], regi2c_cali_val_after[i]);
}
}
#endif //#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2) //TODO IDF-3908

View File

@ -15,12 +15,13 @@
#include "nvs_flash.h"
#include "test_utils.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#define CONFIG_ADC_SUPPRESS_DEPRECATE_WARN 1
#include "driver/adc.h"
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C3, ESP32S3, ESP32C2)
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C3, ESP32C2)
static const char* TAG = "test_adc2";
@ -149,7 +150,7 @@ TEST_CASE("adc2 work with wifi","[adc]")
bool test_list[TEST_NUM] ={1, 1, 0, 0, 1, 0, 1, 0};
adc2_pad_get_io_num(ADC2_CHAN1, &test_adc_io);
TEST_ESP_OK(adc2_config_channel_atten(ADC2_CHAN1, ADC_ATTEN_0db));
TEST_ESP_OK(adc2_config_channel_atten(ADC2_CHAN1, ADC_ATTEN_DB_0));
printf("test_adc_io is %d\n", test_adc_io);
//---------------------------------GPIO init-----------------------------------//

View File

@ -1,394 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
Tests for the adc device driver
*/
#include "esp_system.h"
#include "driver/adc.h"
#include "driver/rtc_io.h"
#include "driver/gpio.h"
#include "unity.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "test_utils.h"
#include "soc/adc_periph.h"
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S3,ESP32C3, ESP32C2)
#include "driver/dac.h"
static const char *TAG = "test_adc";
#ifdef CONFIG_IDF_TARGET_ESP32
#define ADC1_TEST_WIDTH ADC_WIDTH_BIT_12
#define ADC2_TEST_WIDTH ADC_WIDTH_BIT_12
#elif defined CONFIG_IDF_TARGET_ESP32S2
#define ADC1_TEST_WIDTH ADC_WIDTH_BIT_13 //ESP32S2 only support 13 bit width
#define ADC2_TEST_WIDTH ADC_WIDTH_BIT_13 //ESP32S2 only support 13 bit width
#endif
#define ADC1_TEST_ATTEN ADC_ATTEN_DB_11
#define ADC2_TEST_ATTEN ADC_ATTEN_DB_11
#if CONFIG_IDF_TARGET_ESP32
#define ADC1_TEST_CHANNEL_NUM 8
#elif CONFIG_IDF_TARGET_ESP32S2
#define ADC1_TEST_CHANNEL_NUM 10
#endif
#define ADC2_TEST_CHANNEL_NUM 6
static const int adc1_ch[ADC1_TEST_CHANNEL_NUM] = {
ADC1_CHANNEL_0,
ADC1_CHANNEL_1,
ADC1_CHANNEL_2,
ADC1_CHANNEL_3,
ADC1_CHANNEL_4,
ADC1_CHANNEL_5,
ADC1_CHANNEL_6,
ADC1_CHANNEL_7,
#if CONFIG_IDF_TARGET_ESP32S2
ADC1_CHANNEL_8,
ADC1_CHANNEL_9,
#endif
};
static const int adc2_ch[ADC2_TEST_CHANNEL_NUM] = {
ADC2_CHANNEL_0,
ADC2_CHANNEL_1,
ADC2_CHANNEL_2,
ADC2_CHANNEL_3,
ADC2_CHANNEL_4,
ADC2_CHANNEL_5,
};
#define ADC_GET_IO_NUM(periph, channel) (adc_channel_io_map[periph][channel])
void adc_fake_tie_middle(adc_unit_t adc_unit, adc_channel_t channel)
{
gpio_num_t gpio_num = 0;
if (adc_unit == ADC_UNIT_1) {
gpio_num = ADC_GET_IO_NUM(0, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_en(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_en(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLUP_PULLDOWN));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED));
}
if (adc_unit == ADC_UNIT_2) {
gpio_num = ADC_GET_IO_NUM(1, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_en(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_en(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLUP_PULLDOWN));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED));
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
void adc_fake_tie_high(adc_unit_t adc_unit, adc_channel_t channel)
{
gpio_num_t gpio_num = 0;
if (adc_unit == ADC_UNIT_1) {
gpio_num = ADC_GET_IO_NUM(0, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_en(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_dis(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLUP_ONLY));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_OUTPUT_ONLY));
TEST_ESP_OK(rtc_gpio_set_level(gpio_num, 1));
}
if (adc_unit == ADC_UNIT_2) {
gpio_num = ADC_GET_IO_NUM(1, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_en(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_dis(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLUP_ONLY));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_OUTPUT_ONLY));
TEST_ESP_OK(rtc_gpio_set_level(gpio_num, 1));
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
void adc_fake_tie_low(adc_unit_t adc_unit, adc_channel_t channel)
{
gpio_num_t gpio_num = 0;
if (adc_unit == ADC_UNIT_1) {
gpio_num = ADC_GET_IO_NUM(0, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_dis(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_en(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLDOWN_ONLY));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_OUTPUT_ONLY));
TEST_ESP_OK(rtc_gpio_set_level(gpio_num, 0));
}
if (adc_unit == ADC_UNIT_2) {
gpio_num = ADC_GET_IO_NUM(1, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_dis(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_en(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_PULLDOWN_ONLY));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_OUTPUT_ONLY));
TEST_ESP_OK(rtc_gpio_set_level(gpio_num, 0));
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
void adc_io_normal(adc_unit_t adc_unit, adc_channel_t channel)
{
gpio_num_t gpio_num = 0;
if (adc_unit == ADC_UNIT_1) {
gpio_num = ADC_GET_IO_NUM(0, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_dis(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_dis(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_FLOATING));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED));
}
if (adc_unit == ADC_UNIT_2) {
gpio_num = ADC_GET_IO_NUM(1, channel);
TEST_ESP_OK(rtc_gpio_init(gpio_num));
TEST_ESP_OK(rtc_gpio_pullup_dis(gpio_num));
TEST_ESP_OK(rtc_gpio_pulldown_dis(gpio_num));
TEST_ESP_OK(gpio_set_pull_mode(gpio_num, GPIO_FLOATING));
TEST_ESP_OK(rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED));
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
TEST_CASE("ADC1 rtc read", "[adc1]")
{
int adc1_val[ADC1_TEST_CHANNEL_NUM] = {0};
/* adc1 Configure */
adc1_config_width(ADC1_TEST_WIDTH);
ESP_LOGI(TAG, "ADC1 [CH - GPIO]:");
for (int i = 0; i < ADC1_TEST_CHANNEL_NUM; i++) {
TEST_ESP_OK( adc1_config_channel_atten(adc1_ch[i], ADC1_TEST_ATTEN) );
ESP_LOGI(TAG, "[CH%d - IO%d]:", adc1_ch[i], ADC_GET_IO_NUM(0, adc1_ch[i]));
}
printf("ADC tie normal read: ");
vTaskDelay(10 / portTICK_PERIOD_MS);
/* adc Read */
printf("ADC1: ");
for (int i = 0; i < ADC1_TEST_CHANNEL_NUM; i++) {
adc1_val[i] = adc1_get_raw((adc1_channel_t)adc1_ch[i]);
printf("CH%d-%d ", adc1_ch[i], adc1_val[i]);
}
printf("\n");
/* tie high */
for (int i = 0; i < ADC1_TEST_CHANNEL_NUM; i++) {
adc_fake_tie_high(ADC_UNIT_1, adc1_ch[i]);
}
printf("ADC tie high read: ");
vTaskDelay(50 / portTICK_PERIOD_MS);
/* adc Read */
printf("ADC1: ");
for (int i = 0; i < ADC1_TEST_CHANNEL_NUM; i++) {
adc1_val[i] = adc1_get_raw((adc1_channel_t)adc1_ch[i]);
printf("CH%d-%d ", adc1_ch[i], adc1_val[i]);
#ifdef CONFIG_IDF_TARGET_ESP32S2
TEST_ASSERT_EQUAL( adc1_val[i], 0x1fff );
#endif
}
printf("\n");
/* tie low */
for (int i = 0; i < ADC1_TEST_CHANNEL_NUM; i++) {
adc_fake_tie_low(ADC_UNIT_1, adc1_ch[i]);
}
printf("ADC tie low read: ");
vTaskDelay(50 / portTICK_PERIOD_MS);
/* adc Read */
printf("ADC1: ");
for (int i = 0; i < ADC1_TEST_CHANNEL_NUM; i++) {
adc1_val[i] = adc1_get_raw((adc1_channel_t)adc1_ch[i]);
printf("CH%d-%d ", adc1_ch[i], adc1_val[i]);
#ifdef CONFIG_IDF_TARGET_ESP32S2
TEST_ASSERT_INT_WITHIN( 100, 0, adc1_val[i] );
#endif
}
printf("\n");
/* tie midedle */
for (int i = 0; i < ADC1_TEST_CHANNEL_NUM; i++) {
adc_fake_tie_middle(ADC_UNIT_1, adc1_ch[i]);
}
printf("ADC tie mid read: ");
vTaskDelay(50 / portTICK_PERIOD_MS);
/* adc Read */
printf("ADC1: ");
for (int i = 0; i < ADC1_TEST_CHANNEL_NUM; i++) {
adc1_val[i] = adc1_get_raw((adc1_channel_t)adc1_ch[i]);
printf("CH%d-%d ", adc1_ch[i], adc1_val[i]);
#ifdef CONFIG_IDF_TARGET_ESP32S2
TEST_ASSERT_NOT_EQUAL( adc1_val[i], 0x1fff );
TEST_ASSERT_NOT_EQUAL( adc1_val[i], 0 );
#endif
}
printf("\n");
for (int i = 0; i < ADC1_TEST_CHANNEL_NUM; i++) {
adc_io_normal(ADC_UNIT_1, adc1_ch[i]);
}
}
TEST_CASE("ADC2 rtc read", "[adc2]")
{
int adc2_val[ADC2_TEST_CHANNEL_NUM] = {0};
/* adc2 Configure */
ESP_LOGI(TAG, "ADC2 [CH - GPIO]:");
for (int i = 0; i < ADC2_TEST_CHANNEL_NUM; i++) {
TEST_ESP_OK( adc2_config_channel_atten(adc2_ch[i], ADC2_TEST_ATTEN) );
ESP_LOGI(TAG, "[CH%d - IO%d]:", adc2_ch[i], ADC_GET_IO_NUM(1, adc2_ch[i]));
}
printf("ADC float read: ");
vTaskDelay(10 / portTICK_PERIOD_MS);
/* adc Read */
printf("ADC2: ");
for (int i = 0; i < ADC2_TEST_CHANNEL_NUM; i++) {
TEST_ESP_OK( adc2_get_raw((adc2_channel_t)adc2_ch[i], ADC2_TEST_WIDTH, &adc2_val[i]) );
printf("CH%d-%d ", adc2_ch[i], adc2_val[i]);
}
printf("\n");
/* tie high */
for (int i = 0; i < ADC2_TEST_CHANNEL_NUM; i++) {
adc_fake_tie_high(ADC_UNIT_2, adc2_ch[i]);
}
printf("ADC tie high read: ");
vTaskDelay(10 / portTICK_PERIOD_MS);
/* adc Read */
printf("ADC2: ");
for (int i = 0; i < ADC2_TEST_CHANNEL_NUM; i++) {
TEST_ESP_OK( adc2_get_raw((adc2_channel_t)adc2_ch[i], ADC2_TEST_WIDTH, &adc2_val[i]) );
printf("CH%d-%d ", adc2_ch[i], adc2_val[i]);
#ifdef CONFIG_IDF_TARGET_ESP32S2
TEST_ASSERT_EQUAL( adc2_val[i], 0x1fff );
#endif
}
printf("\n");
/* tie low */
for (int i = 0; i < ADC2_TEST_CHANNEL_NUM; i++) {
adc_fake_tie_low(ADC_UNIT_2, adc2_ch[i]);
}
printf("ADC tie low read: ");
vTaskDelay(10 / portTICK_PERIOD_MS);
/* adc Read */
printf("ADC2: ");
for (int i = 0; i < ADC2_TEST_CHANNEL_NUM; i++) {
TEST_ESP_OK( adc2_get_raw((adc2_channel_t)adc2_ch[i], ADC2_TEST_WIDTH, &adc2_val[i]) );
printf("CH%d-%d ", adc2_ch[i], adc2_val[i]);
#ifdef CONFIG_IDF_TARGET_ESP32S2
TEST_ASSERT_INT_WITHIN( 100, 0, adc2_val[i] );
#endif
}
printf("\n");
/* tie midedle */
for (int i = 0; i < ADC2_TEST_CHANNEL_NUM; i++) {
adc_fake_tie_middle(ADC_UNIT_2, adc2_ch[i]);
}
printf("ADC tie middle read: ");
vTaskDelay(10 / portTICK_PERIOD_MS);
/* adc Read */
printf("ADC2: ");
for (int i = 0; i < ADC2_TEST_CHANNEL_NUM; i++) {
TEST_ESP_OK( adc2_get_raw((adc2_channel_t)adc2_ch[i], ADC2_TEST_WIDTH, &adc2_val[i]) );
printf("CH%d-%d ", adc2_ch[i], adc2_val[i]);
#ifdef CONFIG_IDF_TARGET_ESP32S2
TEST_ASSERT_NOT_EQUAL( 0, adc2_val[i] );
TEST_ASSERT_NOT_EQUAL( 0x1fff, adc2_val[i] );
#endif
}
printf("\n");
for (int i = 0; i < ADC1_TEST_CHANNEL_NUM; i++) {
adc_io_normal(ADC_UNIT_1, adc1_ch[i]);
}
}
#include "touch_scope.h"
/**
* 0: ADC1 channels raw data debug.
* 1: ADC2 channels raw data debug.
*/
#define SCOPE_DEBUG_TYPE 1
#define SCOPE_DEBUG_CHANNEL_MAX (10)
#define SCOPE_DEBUG_ENABLE (0)
#define SCOPE_UART_BUADRATE (256000)
#define SCOPE_DEBUG_FREQ_MS (50)
/**
* Manual test: Capture ADC-DMA data and display it on the serial oscilloscope. Used to observe the stability of the data.
* Use step:
* 1. Call this function in `esp-idf/tools/unit-test-app/main/app_main.c`.
* 2. Use `ESP-Tuning Tool`(download from `www.espressif.com`) to capture.
* 3. The readings of multiple channels will be displayed on the tool.
*/
void test_adc_slope_debug(void)
{
float scope_temp[SCOPE_DEBUG_CHANNEL_MAX] = {0}; // max scope channel is 10.
test_tp_scope_debug_init(0, -1, -1, SCOPE_UART_BUADRATE);
#if SCOPE_DEBUG_TYPE == 0
/* adc1 Configure */
adc1_config_width(ADC1_TEST_WIDTH);
ESP_LOGI(TAG, "ADC1 [CH - GPIO] atten %d:", ADC1_TEST_ATTEN);
for (int i = 0; i < ADC1_TEST_CHANNEL_NUM; i++) {
TEST_ESP_OK( adc1_config_channel_atten(adc1_ch[i], ADC1_TEST_ATTEN) );
ESP_LOGI(TAG, "[CH%d - IO%d]", adc1_ch[i], ADC_GET_IO_NUM(0, adc1_ch[i]));
}
/* tie midedle */
for (int i = 0; i < ADC1_TEST_CHANNEL_NUM; i++) {
adc_fake_tie_middle(ADC_UNIT_1, adc1_ch[i]);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
while (1) {
/* adc Read */
for (int i = 0; i < ADC1_TEST_CHANNEL_NUM; i++) {
scope_temp[i] = adc1_get_raw((adc1_channel_t)adc1_ch[i]);
}
test_tp_print_to_scope(scope_temp, ADC1_TEST_CHANNEL_NUM);
vTaskDelay(SCOPE_DEBUG_FREQ_MS / portTICK_PERIOD_MS);
}
#elif SCOPE_DEBUG_TYPE == 1
int adc2_val[ADC2_TEST_CHANNEL_NUM] = {0};
/* adc2 Configure */
ESP_LOGI(TAG, "ADC2 [CH - GPIO] atten %d:", ADC2_TEST_ATTEN);
for (int i = 0; i < ADC2_TEST_CHANNEL_NUM; i++) {
TEST_ESP_OK( adc2_config_channel_atten(adc2_ch[i], ADC2_TEST_ATTEN) );
ESP_LOGI(TAG, "[CH%d - IO%d]:", adc2_ch[i], ADC_GET_IO_NUM(1, adc2_ch[i]));
}
/* tie midedle */
for (int i = 0; i < ADC2_TEST_CHANNEL_NUM; i++) {
adc_fake_tie_middle(ADC_UNIT_2, adc2_ch[i]);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
while (1) {
/* adc Read */
printf("ADC2: ");
for (int i = 0; i < ADC2_TEST_CHANNEL_NUM; i++) {
adc2_get_raw((adc2_channel_t)adc2_ch[i], ADC2_TEST_WIDTH, &adc2_val[i]);
scope_temp[i] = adc2_val[i];
}
test_tp_print_to_scope(scope_temp, ADC2_TEST_CHANNEL_NUM);
vTaskDelay(SCOPE_DEBUG_FREQ_MS / portTICK_PERIOD_MS);
}
#endif
}
#endif

View File

@ -8,7 +8,6 @@
*/
#include "esp_system.h"
#include "driver/adc.h"
#include "unity.h"
#include "esp_system.h"
#include "esp_event.h"
@ -17,6 +16,8 @@
#include "nvs_flash.h"
#include "test_utils.h"
#include "soc/soc_caps.h"
#define CONFIG_ADC_SUPPRESS_DEPRECATE_WARN 1
#include "driver/adc.h"
#if SOC_DAC_SUPPORTED
#include "driver/dac.h"

View File

@ -13,7 +13,6 @@
#if CONFIG_IDF_TARGET_ESP32
#include "esp_system.h"
#include "driver/adc.h"
#include "unity.h"
#include "esp_system.h"
#include "esp_event.h"

View File

@ -1,3 +1,4 @@
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y
CONFIG_I2S_ENABLE_DEBUG_LOG=y
CONFIG_ESP_TASK_WDT=n

View File

@ -1,3 +1,4 @@
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y
CONFIG_I2S_ENABLE_DEBUG_LOG=y
CONFIG_ESP_TASK_WDT=n

View File

@ -0,0 +1,5 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(legacy_adc_driver_test)

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |

View File

@ -0,0 +1,5 @@
set(srcs "test_app_main.c"
"test_legacy_adc.c")
idf_component_register(SRCS ${srcs}
WHOLE_ARCHIVE)

View File

@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
#define TEST_MEMORY_LEAK_THRESHOLD (-300)
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
unity_run_menu();
}

View File

@ -0,0 +1,129 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <stdlib.h>
#include "unity.h"
#include "esp_log.h"
#include "driver/adc.h"
#include "driver/gpio.h"
#include "driver/rtc_io.h"
#include "soc/adc_periph.h"
#define ADC_GET_IO_NUM(unit, channel) (adc_channel_io_map[unit][channel])
/**
* We use weak pulldown, `ADC_TEST_LOW_THRESH` may vary.
* If connect to GND, `ADC_TEST_LOW_THRESH` will usually within 10
*/
#if CONFIG_IDF_TARGET_ESP32
#define ADC_TEST_LOW_VAL 0
#define ADC_TEST_LOW_THRESH 10
#define ADC_TEST_HIGH_VAL 4095
#define ADC_TEST_HIGH_THRESH 10
#elif CONFIG_IDF_TARGET_ESP32S2
#define ADC_TEST_LOW_VAL 0
#define ADC_TEST_LOW_THRESH 25
#define ADC_TEST_HIGH_VAL 8191
#define ADC_TEST_HIGH_THRESH 10
#elif CONFIG_IDF_TARGET_ESP32C3
#define ADC_TEST_LOW_VAL 0
#define ADC_TEST_LOW_THRESH 30
#define ADC_TEST_HIGH_VAL 4095
#define ADC_TEST_HIGH_THRESH 10
#elif CONFIG_IDF_TARGET_ESP32S3
#define ADC_TEST_LOW_VAL 0
#define ADC_TEST_LOW_THRESH 15
#define ADC_TEST_HIGH_VAL 4095
#define ADC_TEST_HIGH_THRESH 0
#elif CONFIG_IDF_TARGET_ESP32C2
#define ADC_TEST_LOW_VAL 2147
#define ADC_TEST_LOW_THRESH 50
#define ADC_TEST_HIGH_VAL 4095
#define ADC_TEST_HIGH_THRESH 0
#elif CONFIG_IDF_TARGET_ESP32H2
#define ADC_TEST_LOW_VAL 2147
#define ADC_TEST_LOW_THRESH 50
#define ADC_TEST_HIGH_VAL 4095
#define ADC_TEST_HIGH_THRESH 0
#endif
//ADC Channels
#if CONFIG_IDF_TARGET_ESP32
#define ADC1_TEST_CHAN0 ADC1_CHANNEL_4
#define ADC2_TEST_CHAN0 ADC2_CHANNEL_0
#elif (SOC_ADC_PERIPH_NUM >= 2)
#define ADC1_TEST_CHAN0 ADC1_CHANNEL_2
#define ADC2_TEST_CHAN0 ADC2_CHANNEL_0
#else
#define ADC1_TEST_CHAN0 ADC1_CHANNEL_2
#endif
const __attribute__((unused)) static char *TAG = "TEST_ADC_LEGACY";
void test_adc_set_io_level(adc_unit_t unit, adc_channel_t channel, bool level)
{
TEST_ASSERT(channel < SOC_ADC_CHANNEL_NUM(unit) && "invalid channel");
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
uint32_t io_num = ADC_GET_IO_NUM(unit, channel);
TEST_ESP_OK(gpio_set_pull_mode(io_num, (level ? GPIO_PULLUP_ONLY: GPIO_PULLDOWN_ONLY)));
#else
gpio_num_t io_num = ADC_GET_IO_NUM(unit, channel);
if (level) {
TEST_ESP_OK(rtc_gpio_pullup_en(io_num));
TEST_ESP_OK(rtc_gpio_pulldown_dis(io_num));
} else {
TEST_ESP_OK(rtc_gpio_pullup_dis(io_num));
TEST_ESP_OK(rtc_gpio_pulldown_en(io_num));
}
#endif
}
TEST_CASE("Legacy ADC oneshot high/low test", "[legacy_adc_oneshot]")
{
int adc_raw = 0;
//ADC1 config
TEST_ESP_OK(adc1_config_width(ADC_WIDTH_BIT_DEFAULT));
TEST_ESP_OK(adc1_config_channel_atten(ADC1_TEST_CHAN0, ADC_ATTEN_DB_11));
#if (SOC_ADC_PERIPH_NUM >= 2)
//ADC2 config
TEST_ESP_OK(adc2_config_channel_atten(ADC2_TEST_CHAN0, ADC_ATTEN_DB_11));
#endif
test_adc_set_io_level(ADC_UNIT_1, (adc1_channel_t)ADC1_TEST_CHAN0, 0);
adc_raw = adc1_get_raw(ADC1_TEST_CHAN0);
ESP_LOGI(TAG, "ADC%d Channel %d raw: %d\n", ADC_UNIT_1, ADC1_TEST_CHAN0, adc_raw);
TEST_ASSERT_INT_WITHIN(ADC_TEST_LOW_THRESH, ADC_TEST_LOW_VAL, adc_raw);
test_adc_set_io_level(ADC_UNIT_1, (adc1_channel_t)ADC1_TEST_CHAN0, 1);
adc_raw = adc1_get_raw(ADC1_TEST_CHAN0);
ESP_LOGI(TAG, "ADC%d Channel %d raw: %d\n", ADC_UNIT_1, ADC1_TEST_CHAN0, adc_raw);
TEST_ASSERT_INT_WITHIN(ADC_TEST_HIGH_THRESH, ADC_TEST_HIGH_VAL, adc_raw);
#if (SOC_ADC_PERIPH_NUM >= 2)
test_adc_set_io_level(ADC_UNIT_2, (adc2_channel_t)ADC2_TEST_CHAN0, 0);
TEST_ESP_OK(adc2_get_raw(ADC2_TEST_CHAN0, ADC_WIDTH_BIT_DEFAULT, &adc_raw));
ESP_LOGI(TAG, "ADC%d Channel %d raw: %d\n", ADC_UNIT_2, ADC2_TEST_CHAN0, adc_raw);
TEST_ASSERT_INT_WITHIN(ADC_TEST_LOW_THRESH, ADC_TEST_LOW_VAL, adc_raw);
test_adc_set_io_level(ADC_UNIT_2, (adc2_channel_t)ADC2_TEST_CHAN0, 1);
TEST_ESP_OK(adc2_get_raw(ADC2_TEST_CHAN0, ADC_WIDTH_BIT_DEFAULT, &adc_raw));
ESP_LOGI(TAG, "ADC%d Channel %d raw: %d\n", ADC_UNIT_2, ADC2_TEST_CHAN0, adc_raw);
TEST_ASSERT_INT_WITHIN(ADC_TEST_HIGH_THRESH, ADC_TEST_HIGH_VAL, adc_raw);
#endif
}

View File

@ -0,0 +1,24 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.esp32c3
@pytest.mark.esp32c2
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'release',
],
indirect=True,
)
def test_legacy_adc(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('*')
dut.expect_unity_test_output(timeout=240)

View File

@ -0,0 +1,5 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@ -0,0 +1,3 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n
CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y

View File

@ -39,8 +39,9 @@ uint32_t esp_efuse_rtc_calib_get_init_code(int version, uint32_t adc_unit, int a
return init_code + 1000; // version 1 logic
}
esp_err_t esp_efuse_rtc_calib_get_cal_voltage(int version, int atten, uint32_t* out_digi, uint32_t* out_vol_mv)
esp_err_t esp_efuse_rtc_calib_get_cal_voltage(int version, uint32_t adc_unit, int atten, uint32_t* out_digi, uint32_t* out_vol_mv)
{
(void)adc_unit; //On esp32c3, V1 we don't have calibration data for ADC2, using the efuse data of ADC1
const esp_efuse_desc_t** cal_vol_efuse;
uint32_t calib_vol_expected_mv;
if (version != 1) {

View File

@ -35,6 +35,7 @@ uint32_t esp_efuse_rtc_calib_get_init_code(int version, uint32_t adc_unit, int a
* @brief Get the calibration digits stored in the efuse, and the corresponding voltage.
*
* @param version Version of the stored efuse
* @param adc_unit ADC unit (not used on ESP32C3, for compatibility)
* @param atten Attenuation to use
* @param out_digi Output buffer of the digits
* @param out_vol_mv Output of the voltage, in mV
@ -42,7 +43,7 @@ uint32_t esp_efuse_rtc_calib_get_init_code(int version, uint32_t adc_unit, int a
* - ESP_ERR_INVALID_ARG: If efuse version or attenuation is invalid
* - ESP_OK: if success
*/
esp_err_t esp_efuse_rtc_calib_get_cal_voltage(int version, int atten, uint32_t* out_digi, uint32_t* out_vol_mv);
esp_err_t esp_efuse_rtc_calib_get_cal_voltage(int version, uint32_t adc_unit, int atten, uint32_t* out_digi, uint32_t* out_vol_mv);
/**
* @brief Get the temperature sensor calibration number delta_T stored in the efuse.

View File

@ -0,0 +1,39 @@
idf_build_get_property(target IDF_TARGET)
set(includes "include" "interface" "${target}/include" "deprecated/include")
set(srcs "adc_cali.c"
"adc_cali_curve_fitting.c"
"adc_oneshot.c"
"adc_common.c"
"adc_lock.c"
"deprecated/esp_adc_cal_common_legacy.c")
if(CONFIG_SOC_ADC_DMA_SUPPORTED)
list(APPEND srcs "adc_continuous.c")
endif()
# init constructor for wifi
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${target}/adc2_init_cal.c")
list(APPEND srcs "${target}/adc2_init_cal.c")
endif()
# line fitting scheme
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${target}/adc_cali_line_fitting.c")
list(APPEND srcs "${target}/adc_cali_line_fitting.c")
endif()
# curve fitting scheme coefficients
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${target}/curve_fitting_coefficients.c")
list(APPEND srcs "${target}/curve_fitting_coefficients.c")
endif()
# legacy calibration driver
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/deprecated/${target}/esp_adc_cal_legacy.c")
list(APPEND srcs "deprecated/${target}/esp_adc_cal_legacy.c")
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${includes}
PRIV_REQUIRES driver efuse
LDFRAGMENTS linker.lf)

View File

@ -0,0 +1,47 @@
menu "ADC and ADC Calibration"
config ADC_ONESHOT_CTRL_FUNC_IN_IRAM
bool "Place ISR version ADC oneshot mode read function into IRAM"
default n
help
Place ISR version ADC oneshot mode read function into IRAM.
config ADC_CONTINUOUS_ISR_IRAM_SAFE
depends on SOC_ADC_DMA_SUPPORTED
bool "ADC continuous mode driver ISR IRAM-Safe"
default n
select GDMA_ISR_IRAM_SAFE if SOC_ADC_DMA_SUPPORTED && SOC_GDMA_SUPPORTED
help
Ensure the ADC continuous mode ISR is IRAM-Safe. When enabled, the ISR handler
will be available when the cache is disabled.
menu "ADC Calibration Configurations"
depends on IDF_TARGET_ESP32
config ADC_CALI_EFUSE_TP_ENABLE
bool "Use Two Point Values"
default "y"
help
Some ESP32s have Two Point calibration values burned into eFuse BLOCK3.
This option will allow the ADC calibration component to characterize the
ADC-Voltage curve using Two Point values if they are available.
config ADC_CALI_EFUSE_VREF_ENABLE
bool "Use eFuse Vref"
default "y"
help
Some ESP32s have Vref burned into eFuse BLOCK0. This option will allow
the ADC calibration component to characterize the ADC-Voltage curve using
eFuse Vref if it is available.
config ADC_CALI_LUT_ENABLE
bool "Use Lookup Tables"
default "y"
help
This option will allow the ADC calibration component to use Lookup Tables
to correct for non-linear behavior in 11db attenuation. Other attenuations
do not exhibit non-linear behavior hence will not be affected by this option.
endmenu
endmenu

View File

@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <sys/cdefs.h>
#include "esp_types.h"
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "hal/adc_types.h"
#include "esp_adc/adc_cali.h"
#include "adc_cali_interface.h"
const __attribute__((unused)) static char *TAG = "adc_cali";
esp_err_t adc_cali_check_scheme(adc_cali_scheme_ver_t *scheme_mask)
{
ESP_RETURN_ON_FALSE(scheme_mask, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
*scheme_mask = 0;
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
*scheme_mask |= ADC_CALI_SCHEME_VER_LINE_FITTING;
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
*scheme_mask |= ADC_CALI_SCHEME_VER_CURVE_FITTING;
#endif
//Add your custom ADC calibration scheme here
ESP_RETURN_ON_FALSE((*scheme_mask) != 0, ESP_ERR_NOT_SUPPORTED, TAG, "no supported calibration scheme yet");
return ESP_OK;
}
esp_err_t adc_cali_raw_to_voltage(adc_cali_handle_t handle, int raw, int *voltage)
{
ESP_RETURN_ON_FALSE(handle && voltage, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE(handle->ctx, ESP_ERR_INVALID_STATE, TAG, "no calibration scheme, create a scheme first");
return handle->raw_to_voltage(handle->ctx, raw, voltage);
}

View File

@ -0,0 +1,247 @@
/*
* 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_heap_caps.h"
#include "soc/soc_caps.h"
#include "esp_adc/adc_cali_scheme.h"
#include "adc_cali_interface.h"
#include "curve_fitting_coefficients.h"
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
#include "esp_efuse_rtc_calib.h"
const __attribute__((unused)) static char *TAG = "adc_cali";
// 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;
/* -------------------- 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 unit_id;
adc_atten_t atten;
union {
adc_calib_data_ver1_t ver1;
} ref_data;
} adc_calib_info_t;
/* ------------------------ Context Structure--------------------------- */
typedef struct {
uint32_t coeff_a; ///< Gradient of ADC-Voltage curve
uint32_t coeff_b; ///< Offset of ADC-Voltage curve
} cali_chars_first_step_t;
typedef struct {
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
} cali_chars_second_step_t;
typedef struct {
adc_unit_t unit_id; ///< ADC unit
adc_atten_t atten; ///< ADC attenuation
cali_chars_first_step_t chars_first_step; ///< Calibration first step characteristics
cali_chars_second_step_t chars_second_step; ///< Calibration second step characteristics
} cali_chars_curve_fitting_t;
/* ----------------------- Characterization Functions ----------------------- */
static void get_first_step_reference_point(int version_num, adc_unit_t unit_id, adc_atten_t atten, adc_calib_info_t *calib_info);
static void calc_first_step_coefficients(const adc_calib_info_t *parsed_data, cali_chars_curve_fitting_t *chars);
static void calc_second_step_coefficients(const adc_cali_curve_fitting_config_t *config, cali_chars_curve_fitting_t *ctx);
static int32_t get_reading_error(uint64_t v_cali_1, const cali_chars_second_step_t *param, adc_atten_t atten);
static esp_err_t check_valid(const adc_cali_curve_fitting_config_t *config);
/* ------------------------ Interface Functions --------------------------- */
static esp_err_t cali_raw_to_voltage(void *arg, int raw, int *voltage);
/* ------------------------- Public API ------------------------------------- */
esp_err_t adc_cali_create_scheme_curve_fitting(const adc_cali_curve_fitting_config_t *config, adc_cali_handle_t *ret_handle)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(config && ret_handle, ESP_ERR_INVALID_ARG, TAG, "invalid arg: null pointer");
ret = check_valid(config);
if (ret != ESP_OK) {
return ret;
}
// current version only accepts encoding ver 1.
uint8_t adc_encoding_version = esp_efuse_rtc_calib_get_ver();
ESP_RETURN_ON_FALSE(adc_encoding_version == 1, ESP_ERR_NOT_SUPPORTED, TAG, "Calibration required eFuse bits not burnt");
adc_cali_scheme_t *scheme = (adc_cali_scheme_t *)heap_caps_calloc(1, sizeof(adc_cali_scheme_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(scheme, ESP_ERR_NO_MEM, TAG, "no mem for adc calibration scheme");
cali_chars_curve_fitting_t *chars = (cali_chars_curve_fitting_t *)heap_caps_calloc(1, sizeof(cali_chars_curve_fitting_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_GOTO_ON_FALSE(chars, ESP_ERR_NO_MEM, err, TAG, "no memory for the calibration characteristics");
scheme->raw_to_voltage = cali_raw_to_voltage;
scheme->ctx = chars;
//Prepare calibration characteristics
adc_calib_info_t calib_info = {0};
//Set first step calibration context
get_first_step_reference_point(adc_encoding_version, config->unit_id, config->atten, &calib_info);
calc_first_step_coefficients(&calib_info, chars);
//Set second step calibration context
calc_second_step_coefficients(config, chars);
chars->unit_id = config->unit_id;
chars->atten = config->atten;
*ret_handle = scheme;
return ESP_OK;
err:
if (scheme) {
free(scheme);
}
return ret;
}
esp_err_t adc_cali_delete_scheme_curve_fitting(adc_cali_handle_t handle)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
free(handle->ctx);
handle->ctx = NULL;
free(handle);
handle = NULL;
return ESP_OK;
}
/* ------------------------ Interface Functions --------------------------- */
static esp_err_t cali_raw_to_voltage(void *arg, int raw, int *voltage)
{
//pointers are checked in the upper layer
cali_chars_curve_fitting_t *ctx = arg;
uint64_t v_cali_1 = raw * ctx->chars_first_step.coeff_a / coeff_a_scaling + ctx->chars_first_step.coeff_b;
int32_t error = get_reading_error(v_cali_1, &(ctx->chars_second_step), ctx->atten);
*voltage = (int32_t)v_cali_1 - error;
return ESP_OK;
}
/* ----------------------- Characterization Functions ----------------------- */
//To get the reference point (Dout, Vin)
static void get_first_step_reference_point(int version_num, adc_unit_t unit_id, 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->unit_id = unit_id;
calib_info->atten = atten;
uint32_t voltage = 0;
uint32_t digi = 0;
ret = esp_efuse_rtc_calib_get_cal_voltage(version_num, unit_id, atten, &digi, &voltage);
assert(ret == ESP_OK);
calib_info->ref_data.ver1.voltage = voltage;
calib_info->ref_data.ver1.digi = digi;
}
/*
* 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 calc_first_step_coefficients(const adc_calib_info_t *parsed_data, cali_chars_curve_fitting_t *ctx)
{
ctx->chars_first_step.coeff_a = coeff_a_scaling * parsed_data->ref_data.ver1.voltage / parsed_data->ref_data.ver1.digi;
ctx->chars_first_step.coeff_b = 0;
ESP_LOGV(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, ctx->chars_first_step.coeff_a);
}
static void calc_second_step_coefficients(const adc_cali_curve_fitting_config_t *config, cali_chars_curve_fitting_t *ctx)
{
ctx->chars_second_step.term_num = (config->atten == 3) ? 5 : 3;
#if CONFIG_IDF_TARGET_ESP32C3
//On esp32c3, ADC1 and ADC2 share the second step coefficients
ctx->chars_second_step.coeff = &adc1_error_coef_atten;
ctx->chars_second_step.sign = &adc1_error_sign;
#else
ctx->chars_second_step.coeff = (config->unit_id == ADC_UNIT_1) ? &adc1_error_coef_atten : &adc2_error_coef_atten;
ctx->chars_second_step.sign = (config->unit_id == ADC_UNIT_1) ? &adc1_error_sign : &adc2_error_sign;
#endif
}
static int32_t get_reading_error(uint64_t v_cali_1, const cali_chars_second_step_t *param, adc_atten_t atten)
{
if (v_cali_1 == 0) {
return 0;
}
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;
}
static esp_err_t check_valid(const adc_cali_curve_fitting_config_t *config)
{
ESP_RETURN_ON_FALSE(config->unit_id < SOC_ADC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid ADC unit");
ESP_RETURN_ON_FALSE(config->atten < SOC_ADC_ATTEN_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid ADC attenuation");
bool available_oneshot_bitwidth = (config->bitwidth >= SOC_ADC_RTC_MIN_BITWIDTH && config->bitwidth <= SOC_ADC_RTC_MAX_BITWIDTH);
bool available_dma_bitwidth = (config->bitwidth >= SOC_ADC_DIGI_MIN_BITWIDTH && config->bitwidth <= SOC_ADC_DIGI_MAX_BITWIDTH);
bool default_bitwidth_mark = (config->bitwidth == ADC_BITWIDTH_DEFAULT);
bool available_bitwidth = (available_oneshot_bitwidth || available_dma_bitwidth || default_bitwidth_mark);
ESP_RETURN_ON_FALSE(available_bitwidth, ESP_ERR_INVALID_ARG, TAG, "invalid bitwidth");
return ESP_OK;
}
#endif //#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED

View File

@ -0,0 +1,208 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <esp_types.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_check.h"
#include "freertos/FreeRTOS.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/adc_private.h"
#include "driver/gpio.h"
#include "hal/adc_hal.h"
#include "hal/adc_hal_common.h"
#include "hal/adc_hal_conf.h"
#include "soc/adc_periph.h"
//For calibration
#if CONFIG_IDF_TARGET_ESP32S2
#include "esp_efuse_rtc_table.h"
#elif SOC_ADC_CALIBRATION_V1_SUPPORTED
#include "esp_efuse_rtc_calib.h"
#endif
static const char *TAG = "adc_common";
static portMUX_TYPE s_spinlock = portMUX_INITIALIZER_UNLOCKED;
extern portMUX_TYPE rtc_spinlock;
/*------------------------------------------------------------------------------
* For those who use APB_SARADC periph
*----------------------------------------------------------------------------*/
static int s_adc_digi_ctrlr_cnt;
void adc_apb_periph_claim(void)
{
portENTER_CRITICAL(&s_spinlock);
s_adc_digi_ctrlr_cnt++;
if (s_adc_digi_ctrlr_cnt == 1) {
//enable ADC digital part
periph_module_enable(PERIPH_SARADC_MODULE);
//reset ADC digital part
periph_module_reset(PERIPH_SARADC_MODULE);
}
portEXIT_CRITICAL(&s_spinlock);
}
void adc_apb_periph_free(void)
{
portENTER_CRITICAL(&s_spinlock);
s_adc_digi_ctrlr_cnt--;
if (s_adc_digi_ctrlr_cnt == 0) {
periph_module_disable(PERIPH_SARADC_MODULE);
} else if (s_adc_digi_ctrlr_cnt < 0) {
portEXIT_CRITICAL(&s_spinlock);
ESP_LOGE(TAG, "%s called, but `s_adc_digi_ctrlr_cnt == 0`", __func__);
abort();
}
portEXIT_CRITICAL(&s_spinlock);
}
/*------------------------------------------------------------------------------
* ADC Power
*----------------------------------------------------------------------------*/
// This gets incremented when adc_power_acquire() is called, and decremented when
// adc_power_release() is called. ADC is powered down when the value reaches zero.
// Should be modified within critical section (ADC_ENTER/EXIT_CRITICAL).
static int s_adc_power_on_cnt;
static void adc_power_on_internal(void)
{
/* Set the power always on to increase precision. */
adc_hal_set_power_manage(ADC_POWER_SW_ON);
}
void adc_power_acquire(void)
{
portENTER_CRITICAL(&rtc_spinlock);
s_adc_power_on_cnt++;
if (s_adc_power_on_cnt == 1) {
adc_power_on_internal();
}
portEXIT_CRITICAL(&rtc_spinlock);
}
static void adc_power_off_internal(void)
{
#if CONFIG_IDF_TARGET_ESP32
adc_hal_set_power_manage(ADC_POWER_SW_OFF);
#else
adc_hal_set_power_manage(ADC_POWER_BY_FSM);
#endif
}
void adc_power_release(void)
{
portENTER_CRITICAL(&rtc_spinlock);
s_adc_power_on_cnt--;
/* Sanity check */
if (s_adc_power_on_cnt < 0) {
portEXIT_CRITICAL(&rtc_spinlock);
ESP_LOGE(TAG, "%s called, but s_adc_power_on_cnt == 0", __func__);
abort();
} else if (s_adc_power_on_cnt == 0) {
adc_power_off_internal();
}
portEXIT_CRITICAL(&rtc_spinlock);
}
/*---------------------------------------------------------------
ADC IOs
---------------------------------------------------------------*/
esp_err_t adc_io_to_channel(int io_num, adc_unit_t *unit_id, adc_channel_t *channel)
{
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(io_num), ESP_ERR_INVALID_ARG, TAG, "invalid gpio number");
ESP_RETURN_ON_FALSE(unit_id && channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
bool found = false;
for (int i = 0; i < SOC_ADC_PERIPH_NUM; i++) {
for (int j = 0; j < SOC_ADC_MAX_CHANNEL_NUM; j++) {
if (adc_channel_io_map[i][j] == io_num) {
*channel = j;
*unit_id = i;
found = true;
}
}
}
return (found) ? ESP_OK : ESP_ERR_NOT_FOUND;
}
esp_err_t adc_channel_to_io(adc_unit_t unit_id, adc_channel_t channel, int *io_num)
{
ESP_RETURN_ON_FALSE(unit_id < SOC_ADC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid unit");
ESP_RETURN_ON_FALSE(channel < SOC_ADC_CHANNEL_NUM(unit_id), ESP_ERR_INVALID_ARG, TAG, "invalid channel");
ESP_RETURN_ON_FALSE(io_num, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
*io_num = adc_channel_io_map[unit_id][channel];
return ESP_OK;
}
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
/*---------------------------------------------------------------
ADC Hardware Calibration
---------------------------------------------------------------*/
#if CONFIG_IDF_TARGET_ESP32S2
#define esp_efuse_rtc_calib_get_ver() esp_efuse_rtc_table_read_calib_version()
static inline uint32_t esp_efuse_rtc_calib_get_init_code(int version, uint32_t adc_unit, int atten)
{
int tag = esp_efuse_rtc_table_get_tag(version, adc_unit + 1, atten, RTCCALIB_V2_PARAM_VINIT);
return esp_efuse_rtc_table_get_parsed_efuse_value(tag, false);
}
#endif
static uint32_t s_adc_cali_param[SOC_ADC_PERIPH_NUM][SOC_ADC_ATTEN_NUM] = {};
void adc_calc_hw_calibration_code(adc_unit_t adc_n, adc_atten_t atten)
{
if (s_adc_cali_param[adc_n][atten]) {
ESP_EARLY_LOGV(TAG, "Use calibrated val ADC%d atten=%d: %04X", adc_n + 1, atten, s_adc_cali_param[adc_n][atten]);
return ;
}
// check if we can fetch the values from eFuse.
int version = esp_efuse_rtc_calib_get_ver();
uint32_t init_code = 0;
if (version == ESP_EFUSE_ADC_CALIB_VER) {
init_code = esp_efuse_rtc_calib_get_init_code(version, adc_n, atten);
} else {
ESP_EARLY_LOGD(TAG, "Calibration eFuse is not configured, use self-calibration for ICode");
adc_power_acquire();
portENTER_CRITICAL(&rtc_spinlock);
adc_ll_pwdet_set_cct(ADC_HAL_PWDET_CCT_DEFAULT);
const bool internal_gnd = true;
init_code = adc_hal_self_calibration(adc_n, atten, internal_gnd);
portEXIT_CRITICAL(&rtc_spinlock);
adc_power_release();
}
s_adc_cali_param[adc_n][atten] = init_code;
ESP_EARLY_LOGV(TAG, "Calib(V%d) ADC%d atten=%d: %04X", version, adc_n + 1, atten, init_code);
}
void IRAM_ATTR adc_set_hw_calibration_code(adc_unit_t adc_n, adc_atten_t atten)
{
adc_hal_set_calibration_param(adc_n, s_adc_cali_param[adc_n][atten]);
}
static __attribute__((constructor)) void adc_hw_calibration(void)
{
//Calculate all ICode
for (int i = 0; i < SOC_ADC_PERIPH_NUM; i++) {
adc_hal_calibration_init(i);
for (int j = 0; j < SOC_ADC_ATTEN_NUM; j++) {
/**
* This may get wrong when attenuations are NOT consecutive on some chips,
* update this when bringing up the calibration on that chip
*/
adc_calc_hw_calibration_code(i, j);
}
}
}
#endif //#if SOC_ADC_CALIBRATION_V1_SUPPORTED

View File

@ -0,0 +1,630 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <esp_types.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_intr_alloc.h"
#include "esp_log.h"
#include "esp_pm.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/timers.h"
#include "freertos/ringbuf.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/adc_private.h"
#include "esp_private/adc_lock.h"
#include "driver/gpio.h"
#include "esp_adc/adc_continuous.h"
#include "hal/adc_types.h"
#include "hal/adc_hal.h"
#include "hal/dma_types.h"
//For DMA
#if SOC_GDMA_SUPPORTED
#include "esp_private/gdma.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "hal/spi_types.h"
#include "esp_private/spi_common_internal.h"
#elif CONFIG_IDF_TARGET_ESP32
#include "hal/i2s_types.h"
#include "driver/i2s_types.h"
#include "soc/i2s_periph.h"
#include "esp_private/i2s_platform.h"
#endif
static const char *ADC_TAG = "adc_continuous";
#define ADC_GET_IO_NUM(periph, channel) (adc_channel_io_map[periph][channel])
extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate position after the rtc module is finished.
#define ADC_ENTER_CRITICAL() portENTER_CRITICAL(&rtc_spinlock)
#define ADC_EXIT_CRITICAL() portEXIT_CRITICAL(&rtc_spinlock)
#define INTERNAL_BUF_NUM 5
typedef enum {
ADC_FSM_INIT,
ADC_FSM_STARTED,
} adc_fsm_t;
/*---------------------------------------------------------------
Continuous Mode Driverr Context
---------------------------------------------------------------*/
typedef struct adc_continuous_ctx_t {
uint8_t *rx_dma_buf; //dma buffer
adc_hal_dma_ctx_t hal; //hal context
#if SOC_GDMA_SUPPORTED
gdma_channel_handle_t rx_dma_channel; //dma rx channel handle
#elif CONFIG_IDF_TARGET_ESP32S2
spi_host_device_t spi_host; //ADC uses this SPI DMA
#elif CONFIG_IDF_TARGET_ESP32
i2s_port_t i2s_host; //ADC uses this I2S DMA
#endif
intr_handle_t dma_intr_hdl; //DMA Interrupt handler
RingbufHandle_t ringbuf_hdl; //RX ringbuffer handler
void* ringbuf_storage; //Ringbuffer storage buffer
void* ringbuf_struct; //Ringbuffer structure buffer
intptr_t rx_eof_desc_addr; //eof descriptor address of RX channel
adc_fsm_t fsm; //ADC continuous mode driver internal states
bool use_adc1; //1: ADC unit1 will be used; 0: ADC unit1 won't be used.
bool use_adc2; //1: ADC unit2 will be used; 0: ADC unit2 won't be used. This determines whether to acquire sar_adc2_mutex lock or not.
adc_atten_t adc1_atten; //Attenuation for ADC1. On this chip each ADC can only support one attenuation.
adc_atten_t adc2_atten; //Attenuation for ADC2. On this chip each ADC can only support one attenuation.
adc_hal_digi_ctrlr_cfg_t hal_digi_ctrlr_cfg; //Hal digital controller configuration
adc_continuous_evt_cbs_t cbs; //Callbacks
void *user_data; //User context
esp_pm_lock_handle_t pm_lock; //For power management
} adc_continuous_ctx_t;
#ifdef CONFIG_PM_ENABLE
//Only for deprecated API
extern esp_pm_lock_handle_t adc_digi_arbiter_lock;
#endif //CONFIG_PM_ENABLE
/*---------------------------------------------------------------
ADC Continuous Read Mode (via DMA)
---------------------------------------------------------------*/
//Function to address transaction
static bool s_adc_dma_intr(adc_continuous_ctx_t *adc_digi_ctx);
#if SOC_GDMA_SUPPORTED
static bool adc_dma_in_suc_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
#else
static void adc_dma_intr_handler(void *arg);
#endif
static int8_t adc_digi_get_io_num(adc_unit_t adc_unit, uint8_t adc_channel)
{
assert(adc_unit <= SOC_ADC_PERIPH_NUM);
uint8_t adc_n = (adc_unit == ADC_UNIT_1) ? 0 : 1;
return adc_channel_io_map[adc_n][adc_channel];
}
static esp_err_t adc_digi_gpio_init(adc_unit_t adc_unit, uint16_t channel_mask)
{
esp_err_t ret = ESP_OK;
uint64_t gpio_mask = 0;
uint32_t n = 0;
int8_t io = 0;
while (channel_mask) {
if (channel_mask & 0x1) {
io = adc_digi_get_io_num(adc_unit, n);
if (io < 0) {
return ESP_ERR_INVALID_ARG;
}
gpio_mask |= BIT64(io);
}
channel_mask = channel_mask >> 1;
n++;
}
gpio_config_t cfg = {
.pin_bit_mask = gpio_mask,
.mode = GPIO_MODE_DISABLE,
};
ret = gpio_config(&cfg);
return ret;
}
esp_err_t adc_continuous_new_handle(const adc_continuous_handle_cfg_t *hdl_config, adc_continuous_handle_t *ret_handle)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE((hdl_config->conv_frame_size % SOC_ADC_DIGI_DATA_BYTES_PER_CONV == 0), ESP_ERR_INVALID_ARG, ADC_TAG, "conv_frame_size should be in multiples of `SOC_ADC_DIGI_DATA_BYTES_PER_CONV`");
adc_continuous_ctx_t *adc_ctx = heap_caps_calloc(1, sizeof(adc_continuous_ctx_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
if (adc_ctx == NULL) {
ret = ESP_ERR_NO_MEM;
goto cleanup;
}
//ringbuffer storage/struct buffer
adc_ctx->ringbuf_storage = heap_caps_calloc(1, hdl_config->max_store_buf_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
adc_ctx->ringbuf_struct = heap_caps_calloc(1, sizeof(StaticRingbuffer_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
if (!adc_ctx->ringbuf_storage || !adc_ctx->ringbuf_struct) {
ret = ESP_ERR_NO_MEM;
goto cleanup;
}
//ringbuffer
adc_ctx->ringbuf_hdl = xRingbufferCreateStatic(hdl_config->max_store_buf_size, RINGBUF_TYPE_BYTEBUF, adc_ctx->ringbuf_storage, adc_ctx->ringbuf_struct);
if (!adc_ctx->ringbuf_hdl) {
ret = ESP_ERR_NO_MEM;
goto cleanup;
}
//malloc internal buffer used by DMA
adc_ctx->rx_dma_buf = heap_caps_calloc(1, hdl_config->conv_frame_size * INTERNAL_BUF_NUM, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
if (!adc_ctx->rx_dma_buf) {
ret = ESP_ERR_NO_MEM;
goto cleanup;
}
//malloc dma descriptor
adc_ctx->hal.rx_desc = heap_caps_calloc(1, (sizeof(dma_descriptor_t)) * INTERNAL_BUF_NUM, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
if (!adc_ctx->hal.rx_desc) {
ret = ESP_ERR_NO_MEM;
goto cleanup;
}
//malloc pattern table
adc_ctx->hal_digi_ctrlr_cfg.adc_pattern = calloc(1, SOC_ADC_PATT_LEN_MAX * sizeof(adc_digi_pattern_config_t));
if (!adc_ctx->hal_digi_ctrlr_cfg.adc_pattern) {
ret = ESP_ERR_NO_MEM;
goto cleanup;
}
#if CONFIG_PM_ENABLE
ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "adc_dma", &adc_ctx->pm_lock);
if (ret != ESP_OK) {
goto cleanup;
}
#endif //CONFIG_PM_ENABLE
#if SOC_GDMA_SUPPORTED
//alloc rx gdma channel
gdma_channel_alloc_config_t rx_alloc_config = {
.direction = GDMA_CHANNEL_DIRECTION_RX,
};
ret = gdma_new_channel(&rx_alloc_config, &adc_ctx->rx_dma_channel);
if (ret != ESP_OK) {
goto cleanup;
}
gdma_connect(adc_ctx->rx_dma_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_ADC, 0));
gdma_strategy_config_t strategy_config = {
.auto_update_desc = true,
.owner_check = true
};
gdma_apply_strategy(adc_ctx->rx_dma_channel, &strategy_config);
gdma_rx_event_callbacks_t cbs = {
.on_recv_eof = adc_dma_in_suc_eof_callback
};
gdma_register_rx_event_callbacks(adc_ctx->rx_dma_channel, &cbs, adc_ctx);
int dma_chan;
gdma_get_channel_id(adc_ctx->rx_dma_channel, &dma_chan);
#elif CONFIG_IDF_TARGET_ESP32S2
//ADC utilises SPI3 DMA on ESP32S2
bool spi_success = false;
uint32_t dma_chan = 0;
spi_success = spicommon_periph_claim(SPI3_HOST, "adc");
ret = spicommon_dma_chan_alloc(SPI3_HOST, SPI_DMA_CH_AUTO, &dma_chan, &dma_chan);
if (ret == ESP_OK) {
adc_ctx->spi_host = SPI3_HOST;
}
if (!spi_success || (adc_ctx->spi_host != SPI3_HOST)) {
goto cleanup;
}
ret = esp_intr_alloc(spicommon_irqdma_source_for_host(adc_ctx->spi_host), ESP_INTR_FLAG_IRAM, adc_dma_intr_handler,
(void *)adc_ctx, &adc_ctx->dma_intr_hdl);
if (ret != ESP_OK) {
goto cleanup;
}
#elif CONFIG_IDF_TARGET_ESP32
//ADC utilises I2S0 DMA on ESP32
uint32_t dma_chan = 0;
ret = i2s_platform_acquire_occupation(I2S_NUM_0, "adc");
if (ret != ESP_OK) {
ret = ESP_ERR_NOT_FOUND;
goto cleanup;
}
adc_ctx->i2s_host = I2S_NUM_0;
ret = esp_intr_alloc(i2s_periph_signal[adc_ctx->i2s_host].irq, ESP_INTR_FLAG_IRAM, adc_dma_intr_handler,
(void *)adc_ctx, &adc_ctx->dma_intr_hdl);
if (ret != ESP_OK) {
goto cleanup;
}
#endif
adc_hal_dma_config_t config = {
#if SOC_GDMA_SUPPORTED
.dev = (void *)GDMA_LL_GET_HW(0),
#elif CONFIG_IDF_TARGET_ESP32S2
.dev = (void *)SPI_LL_GET_HW(adc_ctx->spi_host),
#elif CONFIG_IDF_TARGET_ESP32
.dev = (void *)I2S_LL_GET_HW(adc_ctx->i2s_host),
#endif
.desc_max_num = INTERNAL_BUF_NUM,
.dma_chan = dma_chan,
.eof_num = hdl_config->conv_frame_size / SOC_ADC_DIGI_DATA_BYTES_PER_CONV
};
adc_hal_dma_ctx_config(&adc_ctx->hal, &config);
adc_ctx->fsm = ADC_FSM_INIT;
*ret_handle = adc_ctx;
//enable ADC digital part
periph_module_enable(PERIPH_SARADC_MODULE);
//reset ADC digital part
periph_module_reset(PERIPH_SARADC_MODULE);
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
adc_hal_calibration_init(ADC_UNIT_1);
adc_hal_calibration_init(ADC_UNIT_2);
#endif //#if SOC_ADC_CALIBRATION_V1_SUPPORTED
return ret;
cleanup:
adc_continuous_deinit(adc_ctx);
return ret;
}
#if SOC_GDMA_SUPPORTED
static IRAM_ATTR bool adc_dma_in_suc_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
{
assert(event_data);
adc_continuous_ctx_t *ctx = (adc_continuous_ctx_t *)user_data;
ctx->rx_eof_desc_addr = event_data->rx_eof_desc_addr;
return s_adc_dma_intr(user_data);
}
#else
static IRAM_ATTR void adc_dma_intr_handler(void *arg)
{
adc_continuous_ctx_t *ctx = (adc_continuous_ctx_t *)arg;
bool need_yield = false;
bool conversion_finish = adc_hal_check_event(&ctx->hal, ADC_HAL_DMA_INTR_MASK);
if (conversion_finish) {
adc_hal_digi_clr_intr(&ctx->hal, ADC_HAL_DMA_INTR_MASK);
intptr_t desc_addr = adc_hal_get_desc_addr(&ctx->hal);
ctx->rx_eof_desc_addr = desc_addr;
need_yield = s_adc_dma_intr(ctx);
}
if (need_yield) {
portYIELD_FROM_ISR();
}
}
#endif
static IRAM_ATTR bool s_adc_dma_intr(adc_continuous_ctx_t *adc_digi_ctx)
{
portBASE_TYPE taskAwoken = 0;
bool need_yield = false;
BaseType_t ret;
adc_hal_dma_desc_status_t status = false;
dma_descriptor_t *current_desc = NULL;
while (1) {
status = adc_hal_get_reading_result(&adc_digi_ctx->hal, adc_digi_ctx->rx_eof_desc_addr, &current_desc);
if (status != ADC_HAL_DMA_DESC_VALID) {
break;
}
ret = xRingbufferSendFromISR(adc_digi_ctx->ringbuf_hdl, current_desc->buffer, current_desc->dw0.length, &taskAwoken);
need_yield |= (taskAwoken == pdTRUE);
if (adc_digi_ctx->cbs.on_conv_done) {
adc_continuous_evt_data_t edata = {
.conv_frame_buffer = current_desc->buffer,
.size = current_desc->dw0.length,
};
if (adc_digi_ctx->cbs.on_conv_done(adc_digi_ctx, &edata, adc_digi_ctx->user_data)) {
need_yield |= true;
}
}
if (ret == pdFALSE) {
//ringbuffer overflow
if (adc_digi_ctx->cbs.on_pool_ovf) {
adc_continuous_evt_data_t edata = {};
if (adc_digi_ctx->cbs.on_conv_done(adc_digi_ctx, &edata, adc_digi_ctx->user_data)) {
need_yield |= true;
}
}
}
}
if (status == ADC_HAL_DMA_DESC_NULL) {
//start next turns of dma operation
adc_hal_digi_start(&adc_digi_ctx->hal, adc_digi_ctx->rx_dma_buf);
}
return need_yield;
}
esp_err_t adc_continuous_start(adc_continuous_handle_t handle)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_STATE, ADC_TAG, "The driver isn't initialised");
ESP_RETURN_ON_FALSE(handle->fsm == ADC_FSM_INIT, ESP_ERR_INVALID_STATE, ADC_TAG, "ADC continuous mode isn't in the init state, it's started already");
if (handle->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(handle->pm_lock), ADC_TAG, "acquire pm_lock failed");
}
handle->fsm = ADC_FSM_STARTED;
adc_power_acquire();
//reset flags
if (handle->use_adc1) {
adc_lock_acquire(ADC_UNIT_1);
}
if (handle->use_adc2) {
adc_lock_acquire(ADC_UNIT_2);
}
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
if (handle->use_adc1) {
adc_set_hw_calibration_code(ADC_UNIT_1, handle->adc1_atten);
}
if (handle->use_adc2) {
adc_set_hw_calibration_code(ADC_UNIT_2, handle->adc2_atten);
}
#endif //#if SOC_ADC_CALIBRATION_V1_SUPPORTED
#if SOC_ADC_ARBITER_SUPPORTED
if (handle->use_adc2) {
adc_arbiter_t config = ADC_ARBITER_CONFIG_DEFAULT();
adc_hal_arbiter_config(&config);
}
#endif //#if SOC_ADC_ARBITER_SUPPORTED
if (handle->use_adc1) {
adc_hal_set_controller(ADC_UNIT_1, ADC_HAL_CONTINUOUS_READ_MODE);
}
if (handle->use_adc2) {
adc_hal_set_controller(ADC_UNIT_2, ADC_HAL_CONTINUOUS_READ_MODE);
}
adc_hal_digi_init(&handle->hal);
adc_hal_digi_controller_config(&handle->hal, &handle->hal_digi_ctrlr_cfg);
//start conversion
adc_hal_digi_start(&handle->hal, handle->rx_dma_buf);
return ESP_OK;
}
esp_err_t adc_continuous_stop(adc_continuous_handle_t handle)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_STATE, ADC_TAG, "The driver isn't initialised");
ESP_RETURN_ON_FALSE(handle->fsm == ADC_FSM_STARTED, ESP_ERR_INVALID_STATE, ADC_TAG, "The driver is already stopped");
handle->fsm = ADC_FSM_INIT;
//disable the in suc eof intrrupt
adc_hal_digi_dis_intr(&handle->hal, ADC_HAL_DMA_INTR_MASK);
//clear the in suc eof interrupt
adc_hal_digi_clr_intr(&handle->hal, ADC_HAL_DMA_INTR_MASK);
//stop ADC
adc_hal_digi_stop(&handle->hal);
adc_hal_digi_deinit(&handle->hal);
#if CONFIG_PM_ENABLE
if (handle->pm_lock) {
esp_pm_lock_release(handle->pm_lock);
}
#endif //CONFIG_PM_ENABLE
if (handle->use_adc2) {
adc_lock_release(ADC_UNIT_2);
}
if (handle->use_adc1) {
adc_lock_release(ADC_UNIT_1);
}
adc_power_release();
//release power manager lock
if (handle->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_release(handle->pm_lock), ADC_TAG, "release pm_lock failed");
}
return ESP_OK;
}
esp_err_t adc_continuous_read(adc_continuous_handle_t handle, uint8_t *buf, uint32_t length_max, uint32_t *out_length, uint32_t timeout_ms)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_STATE, ADC_TAG, "The driver isn't initialised");
ESP_RETURN_ON_FALSE(handle->fsm == ADC_FSM_STARTED, ESP_ERR_INVALID_STATE, ADC_TAG, "The driver is already stopped");
TickType_t ticks_to_wait;
esp_err_t ret = ESP_OK;
uint8_t *data = NULL;
size_t size = 0;
ticks_to_wait = timeout_ms / portTICK_PERIOD_MS;
if (timeout_ms == ADC_MAX_DELAY) {
ticks_to_wait = portMAX_DELAY;
}
data = xRingbufferReceiveUpTo(handle->ringbuf_hdl, &size, ticks_to_wait, length_max);
if (!data) {
ESP_LOGV(ADC_TAG, "No data, increase timeout");
ret = ESP_ERR_TIMEOUT;
*out_length = 0;
return ret;
}
memcpy(buf, data, size);
vRingbufferReturnItem(handle->ringbuf_hdl, data);
assert((size % 4) == 0);
*out_length = size;
return ret;
}
esp_err_t adc_continuous_deinit(adc_continuous_handle_t handle)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_STATE, ADC_TAG, "The driver isn't initialised");
ESP_RETURN_ON_FALSE(handle->fsm == ADC_FSM_INIT, ESP_ERR_INVALID_STATE, ADC_TAG, "The driver is still running");
if (handle->ringbuf_hdl) {
vRingbufferDelete(handle->ringbuf_hdl);
handle->ringbuf_hdl = NULL;
free(handle->ringbuf_storage);
free(handle->ringbuf_struct);
}
#if CONFIG_PM_ENABLE
if (handle->pm_lock) {
esp_pm_lock_delete(handle->pm_lock);
}
#endif //CONFIG_PM_ENABLE
free(handle->rx_dma_buf);
free(handle->hal.rx_desc);
free(handle->hal_digi_ctrlr_cfg.adc_pattern);
#if SOC_GDMA_SUPPORTED
gdma_disconnect(handle->rx_dma_channel);
gdma_del_channel(handle->rx_dma_channel);
#elif CONFIG_IDF_TARGET_ESP32S2
esp_intr_free(handle->dma_intr_hdl);
spicommon_dma_chan_free(handle->spi_host);
spicommon_periph_free(handle->spi_host);
#elif CONFIG_IDF_TARGET_ESP32
esp_intr_free(handle->dma_intr_hdl);
i2s_platform_release_occupation(handle->i2s_host);
#endif
free(handle);
handle = NULL;
periph_module_disable(PERIPH_SARADC_MODULE);
return ESP_OK;
}
/*---------------------------------------------------------------
Digital controller setting
---------------------------------------------------------------*/
esp_err_t adc_continuous_config(adc_continuous_handle_t handle, const adc_continuous_config_t *config)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_STATE, ADC_TAG, "The driver isn't initialised");
ESP_RETURN_ON_FALSE(handle->fsm == ADC_FSM_INIT, ESP_ERR_INVALID_STATE, ADC_TAG, "ADC continuous mode isn't in the init state, it's started already");
//Pattern related check
ESP_RETURN_ON_FALSE(config->pattern_num <= SOC_ADC_PATT_LEN_MAX, ESP_ERR_INVALID_ARG, ADC_TAG, "Max pattern num is %d", SOC_ADC_PATT_LEN_MAX);
#if CONFIG_IDF_TARGET_ESP32
for (int i = 0; i < config->pattern_num; i++) {
ESP_RETURN_ON_FALSE((config->adc_pattern[i].bit_width >= SOC_ADC_DIGI_MIN_BITWIDTH && config->adc_pattern->bit_width <= SOC_ADC_DIGI_MAX_BITWIDTH), ESP_ERR_INVALID_ARG, ADC_TAG, "ADC bitwidth not supported");
ESP_RETURN_ON_FALSE(config->adc_pattern[i].unit == 0, ESP_ERR_INVALID_ARG, ADC_TAG, "Only support using ADC1 DMA mode");
}
#else
for (int i = 0; i < config->pattern_num; i++) {
ESP_RETURN_ON_FALSE((config->adc_pattern[i].bit_width == SOC_ADC_DIGI_MAX_BITWIDTH), ESP_ERR_INVALID_ARG, ADC_TAG, "ADC bitwidth not supported");
}
#endif
ESP_RETURN_ON_FALSE(config->sample_freq_hz <= SOC_ADC_SAMPLE_FREQ_THRES_HIGH && config->sample_freq_hz >= SOC_ADC_SAMPLE_FREQ_THRES_LOW, ESP_ERR_INVALID_ARG, ADC_TAG, "ADC sampling frequency out of range");
#if CONFIG_IDF_TARGET_ESP32
ESP_RETURN_ON_FALSE(config->format == ADC_DIGI_OUTPUT_FORMAT_TYPE1, ESP_ERR_INVALID_ARG, ADC_TAG, "Please use type1");
#elif CONFIG_IDF_TARGET_ESP32S2
if (config->conv_mode == ADC_CONV_BOTH_UNIT || config->conv_mode == ADC_CONV_ALTER_UNIT) {
ESP_RETURN_ON_FALSE(config->format == ADC_DIGI_OUTPUT_FORMAT_TYPE2, ESP_ERR_INVALID_ARG, ADC_TAG, "Please use type2");
} else if (config->conv_mode == ADC_CONV_SINGLE_UNIT_1 || config->conv_mode == ADC_CONV_SINGLE_UNIT_2) {
ESP_RETURN_ON_FALSE(config->format == ADC_DIGI_OUTPUT_FORMAT_TYPE1, ESP_ERR_INVALID_ARG, ADC_TAG, "Please use type1");
}
#else
ESP_RETURN_ON_FALSE(config->format == ADC_DIGI_OUTPUT_FORMAT_TYPE2, ESP_ERR_INVALID_ARG, ADC_TAG, "Please use type2");
#endif
handle->hal_digi_ctrlr_cfg.adc_pattern_len = config->pattern_num;
handle->hal_digi_ctrlr_cfg.sample_freq_hz = config->sample_freq_hz;
handle->hal_digi_ctrlr_cfg.conv_mode = config->conv_mode;
memcpy(handle->hal_digi_ctrlr_cfg.adc_pattern, config->adc_pattern, config->pattern_num * sizeof(adc_digi_pattern_config_t));
const int atten_uninitialized = 999;
handle->adc1_atten = atten_uninitialized;
handle->adc2_atten = atten_uninitialized;
handle->use_adc1 = 0;
handle->use_adc2 = 0;
uint32_t adc1_chan_mask = 0;
uint32_t adc2_chan_mask = 0;
for (int i = 0; i < config->pattern_num; i++) {
const adc_digi_pattern_config_t *pat = &config->adc_pattern[i];
if (pat->unit == ADC_UNIT_1) {
handle->use_adc1 = 1;
adc1_chan_mask |= BIT(pat->channel);
if (handle->adc1_atten == atten_uninitialized) {
handle->adc1_atten = pat->atten;
} else if (handle->adc1_atten != pat->atten) {
return ESP_ERR_INVALID_ARG;
}
} else if (pat->unit == ADC_UNIT_2) {
handle->use_adc2 = 1;
adc2_chan_mask |= BIT(pat->channel);
if (handle->adc2_atten == atten_uninitialized) {
handle->adc2_atten = pat->atten;
} else if (handle->adc2_atten != pat->atten) {
return ESP_ERR_INVALID_ARG;
}
}
}
if (handle->use_adc1) {
adc_digi_gpio_init(ADC_UNIT_1, adc1_chan_mask);
}
if (handle->use_adc2) {
adc_digi_gpio_init(ADC_UNIT_2, adc2_chan_mask);
}
return ESP_OK;
}
esp_err_t adc_continuous_register_event_callbacks(adc_continuous_handle_t handle, const adc_continuous_evt_cbs_t *cbs, void *user_data)
{
ESP_RETURN_ON_FALSE(handle && cbs, ESP_ERR_INVALID_ARG, ADC_TAG, "invalid argument");
ESP_RETURN_ON_FALSE(handle->fsm == ADC_FSM_INIT, ESP_ERR_INVALID_STATE, ADC_TAG, "ADC continuous mode isn't in the init state, it's started already");
#if CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE
if (cbs->on_conv_done) {
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_conv_done), ESP_ERR_INVALID_ARG, ADC_TAG, "on_conv_done callback not in IRAM");
}
if (cbs->on_pool_ovf) {
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_pool_ovf), ESP_ERR_INVALID_ARG, ADC_TAG, "on_pool_ovf callback not in IRAM");
}
#endif
handle->cbs.on_conv_done = cbs->on_conv_done;
handle->cbs.on_pool_ovf = cbs->on_pool_ovf;
handle->user_data = user_data;
return ESP_OK;
}
esp_err_t adc_continuous_io_to_channel(int io_num, adc_unit_t *unit_id, adc_channel_t *channel)
{
return adc_io_to_channel(io_num, unit_id, channel);
}
esp_err_t adc_continuous_channel_to_io(adc_unit_t unit_id, adc_channel_t channel, int *io_num)
{
return adc_channel_to_io(unit_id, channel, io_num);
}

View File

@ -0,0 +1,87 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <esp_types.h>
#include "sdkconfig.h"
#include "sys/lock.h"
#include "esp_log.h"
#include "esp_err.h"
#include "esp_check.h"
#include "hal/adc_types.h"
#include "esp_private/adc_lock.h"
#include "esp_private/adc2_wifi.h"
static const char *TAG = "adc_lock";
static _lock_t adc1_lock;
static _lock_t adc2_lock;
esp_err_t adc_lock_acquire(adc_unit_t unit_mask)
{
if (unit_mask & ADC_UNIT_1) {
_lock_acquire(&adc1_lock);
}
if (unit_mask & ADC_UNIT_2) {
_lock_acquire(&adc2_lock);
}
return ESP_OK;
}
esp_err_t adc_lock_release(adc_unit_t unit_mask)
{
if (unit_mask & ADC_UNIT_2) {
ESP_RETURN_ON_FALSE(((uint32_t *)adc2_lock != NULL), ESP_ERR_INVALID_STATE, TAG, "adc2 lock release without acquiring");
_lock_release(&adc2_lock);
}
if (unit_mask & ADC_UNIT_1) {
ESP_RETURN_ON_FALSE(((uint32_t *)adc1_lock != NULL), ESP_ERR_INVALID_STATE, TAG, "adc1 lock release without acquiring");
_lock_release(&adc1_lock);
}
return ESP_OK;
}
esp_err_t adc_lock_try_acquire(adc_unit_t unit_mask)
{
if (unit_mask & ADC_UNIT_1) {
if (_lock_try_acquire(&adc1_lock) == -1) {
return ESP_ERR_TIMEOUT;
}
}
if (unit_mask & ADC_UNIT_2) {
if (_lock_try_acquire(&adc2_lock) == -1) {
return ESP_ERR_TIMEOUT;
}
}
return ESP_OK;
}
esp_err_t adc2_wifi_acquire(void)
{
#if CONFIG_IDF_TARGET_ESP32
/* Wi-Fi module will use adc2. Use locks to avoid conflicts. */
adc_lock_acquire(ADC_UNIT_2);
ESP_LOGD(TAG, "Wi-Fi takes adc2 lock.");
#endif
return ESP_OK;
}
esp_err_t adc2_wifi_release(void)
{
#if CONFIG_IDF_TARGET_ESP32
return adc_lock_release(ADC_UNIT_2);
#endif
return ESP_OK;
}

View File

@ -0,0 +1,260 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <esp_types.h>
#include <sys/lock.h>
#include "sdkconfig.h"
#include "stdatomic.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "driver/gpio.h"
#include "driver/rtc_io.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_private/adc_private.h"
#include "esp_private/adc_lock.h"
#include "hal/adc_types.h"
#include "hal/adc_oneshot_hal.h"
#include "hal/adc_ll.h"
#include "hal/adc_hal_conf.h"
#include "soc/adc_periph.h"
#if CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM
#define ADC_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define ADC_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
extern portMUX_TYPE rtc_spinlock;
static const char *TAG = "adc_oneshot";
typedef struct adc_oneshot_unit_ctx_t {
adc_oneshot_hal_ctx_t hal;
uint32_t unit_id;
adc_ulp_mode_t ulp_mode;
} adc_oneshot_unit_ctx_t;
typedef struct adc_oneshot_ctx_t {
_lock_t mutex;
adc_oneshot_unit_ctx_t *units[SOC_ADC_PERIPH_NUM];
int apb_periph_ref_cnts; //For the chips that ADC oneshot mode using APB_SARADC periph
} adc_oneshot_ctx_t;
static adc_oneshot_ctx_t s_ctx; //ADC oneshot mode context
static atomic_bool s_adc_unit_claimed[SOC_ADC_PERIPH_NUM] = {ATOMIC_VAR_INIT(false),
#if (SOC_ADC_PERIPH_NUM >= 2)
ATOMIC_VAR_INIT(false)
#endif
};
static bool s_adc_unit_claim(adc_unit_t unit);
static bool s_adc_unit_free(adc_unit_t unit);
static esp_err_t s_adc_io_init(adc_unit_t unit, adc_channel_t channel);
esp_err_t adc_oneshot_io_to_channel(int io_num, adc_unit_t *unit_id, adc_channel_t *channel)
{
return adc_io_to_channel(io_num, unit_id, channel);
}
esp_err_t adc_oneshot_channel_to_io(adc_unit_t unit_id, adc_channel_t channel, int *io_num)
{
return adc_channel_to_io(unit_id, channel, io_num);
}
esp_err_t adc_oneshot_new_unit(const adc_oneshot_unit_init_cfg_t *init_config, adc_oneshot_unit_handle_t *ret_unit)
{
esp_err_t ret = ESP_OK;
adc_oneshot_unit_ctx_t *unit = NULL;
ESP_GOTO_ON_FALSE(init_config && ret_unit, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument: null pointer");
ESP_GOTO_ON_FALSE(init_config->unit_id < SOC_ADC_PERIPH_NUM, ESP_ERR_INVALID_ARG, err, TAG, "invalid unit");
unit = heap_caps_calloc(1, sizeof(adc_oneshot_unit_ctx_t), ADC_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(unit, ESP_ERR_NO_MEM, err, TAG, "no mem for unit");
bool success_claim = s_adc_unit_claim(init_config->unit_id);
ESP_GOTO_ON_FALSE(success_claim, ESP_ERR_NOT_FOUND, err, TAG, "adc%d is already in use", init_config->unit_id + 1);
_lock_acquire(&s_ctx.mutex);
s_ctx.units[init_config->unit_id] = unit;
_lock_release(&s_ctx.mutex);
unit->unit_id = init_config->unit_id;
unit->ulp_mode = init_config->ulp_mode;
adc_oneshot_hal_cfg_t config = {
.unit = init_config->unit_id,
.work_mode = (init_config->ulp_mode == ADC_ULP_MODE_FSM) ? ADC_HAL_ULP_FSM_MODE : ADC_HAL_SINGLE_READ_MODE,
};
adc_oneshot_hal_init(&(unit->hal), &config);
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
//To enable the APB_SARADC periph if needed
_lock_acquire(&s_ctx.mutex);
s_ctx.apb_periph_ref_cnts++;
if (s_ctx.apb_periph_ref_cnts == 1) {
adc_apb_periph_claim();
}
_lock_release(&s_ctx.mutex);
#endif
adc_power_acquire();
ESP_LOGD(TAG, "new adc unit%d is created", unit->unit_id);
*ret_unit = unit;
return ESP_OK;
err:
if (unit) {
free(unit);
}
return ret;
}
esp_err_t adc_oneshot_config_channel(adc_oneshot_unit_handle_t handle, adc_channel_t channel, const adc_oneshot_chan_cfg_t *config)
{
ESP_RETURN_ON_FALSE(handle && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE(config->atten < SOC_ADC_ATTEN_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid attenuation");
ESP_RETURN_ON_FALSE(((config->bitwidth >= SOC_ADC_RTC_MIN_BITWIDTH && config->bitwidth <= SOC_ADC_RTC_MAX_BITWIDTH) || config->bitwidth == ADC_BITWIDTH_DEFAULT), ESP_ERR_INVALID_ARG, TAG, "invalid bitwidth");
ESP_RETURN_ON_FALSE(channel < SOC_ADC_CHANNEL_NUM(handle->unit_id), ESP_ERR_INVALID_ARG, TAG, "invalid channel");
s_adc_io_init(handle->unit_id, channel);
adc_oneshot_hal_ctx_t *hal = &(handle->hal);
adc_oneshot_hal_chan_cfg_t cfg = {
.atten = config->atten,
.bitwidth = (config->bitwidth == ADC_BITWIDTH_DEFAULT) ? SOC_ADC_RTC_MAX_BITWIDTH : config->bitwidth,
};
portENTER_CRITICAL(&rtc_spinlock);
adc_oneshot_hal_channel_config(hal, &cfg, channel);
if (handle->ulp_mode) {
adc_oneshot_hal_setup(hal, channel);
}
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
esp_err_t adc_oneshot_read(adc_oneshot_unit_handle_t handle, adc_channel_t chan, int *out_raw)
{
ESP_RETURN_ON_FALSE(handle && out_raw, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE(chan < SOC_ADC_CHANNEL_NUM(handle->unit_id), ESP_ERR_INVALID_ARG, TAG, "invalid channel");
if (adc_lock_try_acquire(handle->unit_id) != ESP_OK) {
return ESP_ERR_TIMEOUT;
}
portENTER_CRITICAL(&rtc_spinlock);
adc_oneshot_hal_setup(&(handle->hal), chan);
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
adc_atten_t atten = adc_ll_get_atten(handle->unit_id, chan);
adc_hal_calibration_init(handle->unit_id);
adc_set_hw_calibration_code(handle->unit_id, atten);
#endif
bool valid = false;
valid = adc_oneshot_hal_convert(&(handle->hal), out_raw);
portEXIT_CRITICAL(&rtc_spinlock);
adc_lock_release(handle->unit_id);
return valid ? ESP_OK : ESP_ERR_TIMEOUT;
}
esp_err_t adc_oneshot_read_isr(adc_oneshot_unit_handle_t handle, adc_channel_t chan, int *out_raw)
{
ESP_RETURN_ON_FALSE_ISR(handle && out_raw, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE_ISR(out_raw, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE_ISR(chan < SOC_ADC_CHANNEL_NUM(handle->unit_id), ESP_ERR_INVALID_ARG, TAG, "invalid channel");
portENTER_CRITICAL_SAFE(&rtc_spinlock);
adc_oneshot_hal_setup(&(handle->hal), chan);
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
adc_atten_t atten = adc_ll_get_atten(handle->unit_id, chan);
adc_hal_calibration_init(handle->unit_id);
adc_set_hw_calibration_code(handle->unit_id, atten);
#endif
adc_oneshot_hal_convert(&(handle->hal), out_raw);
portEXIT_CRITICAL_SAFE(&rtc_spinlock);
return ESP_OK;
}
esp_err_t adc_oneshot_del_unit(adc_oneshot_unit_handle_t handle)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
bool success_free = s_adc_unit_free(handle->unit_id);
ESP_RETURN_ON_FALSE(success_free, ESP_ERR_NOT_FOUND, TAG, "adc%d isn't in use", handle->unit_id + 1);
_lock_acquire(&s_ctx.mutex);
s_ctx.units[handle->unit_id] = NULL;
_lock_release(&s_ctx.mutex);
ESP_LOGD(TAG, "adc unit%d is deleted", handle->unit_id);
free(handle);
adc_power_release();
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
//To free the APB_SARADC periph if needed
_lock_acquire(&s_ctx.mutex);
s_ctx.apb_periph_ref_cnts--;
assert(s_ctx.apb_periph_ref_cnts >= 0);
if (s_ctx.apb_periph_ref_cnts == 0) {
adc_apb_periph_free();
}
_lock_release(&s_ctx.mutex);
#endif
return ESP_OK;
}
#define ADC_GET_IO_NUM(unit, channel) (adc_channel_io_map[unit][channel])
static esp_err_t s_adc_io_init(adc_unit_t unit, adc_channel_t channel)
{
ESP_RETURN_ON_FALSE(channel < SOC_ADC_CHANNEL_NUM(unit), ESP_ERR_INVALID_ARG, TAG, "invalid channel");
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
uint32_t io_num = ADC_GET_IO_NUM(unit, channel);
gpio_config_t cfg = {
.pin_bit_mask = BIT64(io_num),
.mode = GPIO_MODE_DISABLE,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
ESP_RETURN_ON_ERROR(gpio_config(&cfg), TAG, "IO config fail");
#else
gpio_num_t io_num = ADC_GET_IO_NUM(unit, channel);
ESP_RETURN_ON_ERROR(rtc_gpio_init(io_num), TAG, "IO config fail");
ESP_RETURN_ON_ERROR(rtc_gpio_set_direction(io_num, RTC_GPIO_MODE_DISABLED), TAG, "IO config fail");
ESP_RETURN_ON_ERROR(rtc_gpio_pulldown_dis(io_num), TAG, "IO config fail");
ESP_RETURN_ON_ERROR(rtc_gpio_pullup_dis(io_num), TAG, "IO config fail");
#endif
return ESP_OK;
}
static bool s_adc_unit_claim(adc_unit_t unit)
{
bool false_var = false;
return atomic_compare_exchange_strong(&s_adc_unit_claimed[unit], &false_var, true);
}
static bool s_adc_unit_free(adc_unit_t unit)
{
bool true_var = true;
return atomic_compare_exchange_strong(&s_adc_unit_claimed[unit], &true_var, false);
}

View File

@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#define COEFF_GROUP_NUM 4
#define TERM_MAX 5
/**
* @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
*/
extern const uint64_t adc1_error_coef_atten[COEFF_GROUP_NUM][TERM_MAX][2];
extern const uint64_t adc2_error_coef_atten[COEFF_GROUP_NUM][TERM_MAX][2];
/**
* Term sign
*/
extern const int32_t adc1_error_sign[COEFF_GROUP_NUM][TERM_MAX];
extern const int32_t adc2_error_sign[COEFF_GROUP_NUM][TERM_MAX];

View File

@ -6,12 +6,13 @@
#include <stdint.h>
#include "esp_types.h"
#include "driver/adc.h"
#include "hal/efuse_ll.h"
#include "esp_err.h"
#include "esp_check.h"
#include "assert.h"
#include "esp_adc_cal.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

View File

@ -11,11 +11,12 @@
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "driver/adc.h"
#include "hal/adc_ll.h"
#include "esp_efuse_rtc_calib.h"
#include "esp_adc_cal.h"
#include "../esp_adc_cal_internal.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";
@ -79,9 +80,12 @@ static esp_err_t prepare_calib_data_for(int version_num, adc_unit_t adc_num, adc
parsed_data_storage->version_num = version_num;
parsed_data_storage->adc_num = adc_num;
parsed_data_storage->atten_level = atten;
// V1 we don't have calibration data for ADC2, using the efuse data of ADC1
uint32_t voltage, digi;
ret = esp_efuse_rtc_calib_get_cal_voltage(version_num, atten, &digi, &voltage);
/**
* 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;
}
@ -129,7 +133,7 @@ esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num,
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 < 4, ESP_ADC_CAL_VAL_NOT_SUPPORTED, LOG_TAG, "Invalid attenuation");
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");

View File

@ -6,16 +6,17 @@
#include <stdint.h>
#include "esp_types.h"
#include "driver/adc.h"
#include "soc/efuse_periph.h"
#include "esp_err.h"
#include "esp_check.h"
#include "assert.h"
#include "esp_adc_cal.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";

View File

@ -11,11 +11,12 @@
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "driver/adc.h"
#include "hal/adc_types.h"
#include "esp_efuse_rtc_calib.h"
#include "esp_adc_cal.h"
#include "../esp_adc_cal_internal.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";

View File

@ -6,14 +6,18 @@
#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 "driver/adc.h"
#include "hal/adc_types.h"
#define CONFIG_ADC_SUPPRESS_DEPRECATE_WARN 1
#include "esp_adc_cal.h"
#include "esp_adc_cal_internal.h"
#include "esp_adc_cal_internal_legacy.h"
#include "driver/adc.h"
const __attribute__((unused)) static char *TAG = "ADC_CALI";
@ -41,6 +45,7 @@ esp_err_t esp_adc_cal_get_voltage(adc_channel_t channel,
return ret;
}
#if ESP_ADC_CAL_CURVE_FITTING_SUPPORTED
/*------------------------------------------------------------------------------
* Private API
@ -86,3 +91,5 @@ int32_t esp_adc_cal_get_reading_error(const esp_adc_error_calc_param_t *param, u
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

View File

@ -4,46 +4,24 @@
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __ESP_ADC_CAL_H__
#define __ESP_ADC_CAL_H__
#pragma once
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "driver/adc.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
/**
* @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;
#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
*
@ -128,8 +106,8 @@ uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_char
*/
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
#endif /* __ESP_ADC_CAL_H__ */

View File

@ -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

View File

@ -0,0 +1,435 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "sdkconfig.h"
#include "assert.h"
#include "esp_types.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "hal/adc_types.h"
#include "hal/efuse_ll.h"
#include "soc/soc_caps.h"
#include "esp_adc/adc_cali_scheme.h"
#include "adc_cali_interface.h"
/* ----------------------------- Configuration ------------------------------ */
#ifdef CONFIG_ADC_CALI_EFUSE_TP_ENABLE
#define EFUSE_TP_ENABLED 1
#else
#define EFUSE_TP_ENABLED 0
#endif
#ifdef CONFIG_ADC_CALI_EFUSE_VREF_ENABLE
#define EFUSE_VREF_ENABLED 1
#else
#define EFUSE_VREF_ENABLED 0
#endif
#ifdef CONFIG_ADC_CALI_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 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};
const __attribute__((unused)) static char *TAG = "adc_cali";
/* ----------------------- EFuse Access Functions --------------------------- */
static bool check_efuse_vref(void);
static bool check_efuse_tp(void);
static inline int decode_bits(uint32_t bits, uint32_t mask, bool is_twos_compl);
static uint32_t read_efuse_vref(void);
static uint32_t read_efuse_tp_low(adc_unit_t unit_id);
static uint32_t read_efuse_tp_high(adc_unit_t unit_id);
/* ----------------------- Characterization Functions ----------------------- */
static void characterize_using_two_point(adc_unit_t unit_id,
adc_atten_t atten,
uint32_t high,
uint32_t low,
uint32_t *coeff_a,
uint32_t *coeff_b);
static void characterize_using_vref(adc_unit_t unit_id,
adc_atten_t atten,
uint32_t vref,
uint32_t *coeff_a,
uint32_t *coeff_b);
/* ------------------------ Conversion Functions --------------------------- */
static uint32_t calculate_voltage_linear(uint32_t adc_reading, uint32_t coeff_a, uint32_t 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);
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;
}
/* ------------------------ Interface Functions --------------------------- */
static esp_err_t cali_raw_to_voltage(void *arg, int raw, int *voltage);
/* ------------------------ Context Structure--------------------------- */
typedef struct {
adc_unit_t unit_id; ///< ADC unit
adc_atten_t atten; ///< ADC attenuation
adc_bitwidth_t bitwidth; ///< 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)
adc_cali_line_fitting_efuse_val_t efuse_val; ///< Type of calibration value used in characterization
} cali_chars_line_fitting_t;
/* ------------------------- Public API ------------------------------------- */
esp_err_t adc_cali_create_scheme_line_fitting(const adc_cali_line_fitting_config_t *config, adc_cali_handle_t *ret_handle)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(config && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE(config->unit_id < SOC_ADC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid ADC unit");
ESP_RETURN_ON_FALSE(config->atten < SOC_ADC_ATTEN_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid ADC attenuation");
ESP_RETURN_ON_FALSE(((config->bitwidth >= SOC_ADC_RTC_MIN_BITWIDTH && config->bitwidth <= SOC_ADC_RTC_MAX_BITWIDTH) || config->bitwidth == ADC_BITWIDTH_DEFAULT), ESP_ERR_INVALID_ARG, TAG, "invalid bitwidth");
adc_cali_scheme_t *scheme = (adc_cali_scheme_t *)heap_caps_calloc(1, sizeof(adc_cali_scheme_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(scheme, ESP_ERR_NO_MEM, TAG, "no mem for adc calibration scheme");
cali_chars_line_fitting_t *chars = (cali_chars_line_fitting_t *)heap_caps_calloc(1, sizeof(cali_chars_line_fitting_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_GOTO_ON_FALSE(chars, ESP_ERR_NO_MEM, err, TAG, "no memory for the calibration characteristics");
//Check eFuse if enabled to do so
if (check_efuse_tp() && EFUSE_TP_ENABLED) {
//Characterize based on Two Point values
chars->efuse_val = ADC_CALI_LINE_FITTING_EFUSE_VAL_EFUSE_TP;
//Characterize based on Two Point values
uint32_t high = read_efuse_tp_high(config->unit_id);
uint32_t low = read_efuse_tp_low(config->unit_id);
characterize_using_two_point(config->unit_id, config->atten, high, low, &chars->coeff_a, &chars->coeff_b);
} else if (check_efuse_vref() && EFUSE_VREF_ENABLED) {
//Characterize based on eFuse Vref
chars->efuse_val = ADC_CALI_LINE_FITTING_EFUSE_VAL_EFUSE_VREF;
chars->vref = read_efuse_vref();
characterize_using_vref(config->unit_id, config->atten, chars->vref, &chars->coeff_a, &chars->coeff_b);
} else {
//Characterized based on default Vref
chars->efuse_val = ADC_CALI_LINE_FITTING_EFUSE_VAL_DEFAULT_VREF;
ESP_GOTO_ON_FALSE(config->default_vref, ESP_ERR_INVALID_ARG, err, TAG, "default vref didn't set");
chars->vref = config->default_vref;
characterize_using_vref(config->unit_id, config->atten, chars->vref, &chars->coeff_a, &chars->coeff_b);
}
chars->unit_id = config->unit_id;
chars->atten = config->atten;
chars->bitwidth = (config->bitwidth == ADC_BITWIDTH_DEFAULT) ? ADC_BITWIDTH_12 : config->bitwidth;
//Initialize fields for lookup table if necessary
if (LUT_ENABLED && config->atten == ADC_ATTEN_DB_11) {
chars->low_curve = (config->unit_id == ADC_UNIT_1) ? lut_adc1_low : lut_adc2_low;
chars->high_curve = (config->unit_id == ADC_UNIT_1) ? lut_adc1_high : lut_adc2_high;
} else {
chars->low_curve = NULL;
chars->high_curve = NULL;
}
scheme->raw_to_voltage = cali_raw_to_voltage;
scheme->ctx = chars;
*ret_handle = scheme;
return ESP_OK;
err:
if (scheme) {
free(scheme);
}
return ret;
}
esp_err_t adc_cali_scheme_line_fitting_check_efuse(adc_cali_line_fitting_efuse_val_t *cali_val)
{
ESP_RETURN_ON_FALSE(cali_val, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
if (check_efuse_tp()) {
*cali_val = ADC_CALI_LINE_FITTING_EFUSE_VAL_EFUSE_TP;
} else if (check_efuse_vref()) {
*cali_val = ADC_CALI_LINE_FITTING_EFUSE_VAL_EFUSE_VREF;
} else {
*cali_val = ADC_CALI_LINE_FITTING_EFUSE_VAL_DEFAULT_VREF;
}
return ESP_OK;
}
esp_err_t adc_cali_delete_scheme_line_fitting(adc_cali_handle_t handle)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
free(handle->ctx);
handle->ctx = NULL;
free(handle);
handle = NULL;
return ESP_OK;
}
/* ------------------------ Interface Functions --------------------------- */
static esp_err_t cali_raw_to_voltage(void *arg, int raw, int *voltage)
{
//pointers are checked in the upper layer
cali_chars_line_fitting_t *ctx = arg;
//Scale adc_rading if not 12 bits wide
raw = (raw << (ADC_BITWIDTH_12 - ctx->bitwidth));
if (raw > ADC_12_BIT_RES - 1) {
raw = ADC_12_BIT_RES - 1; //Set to 12bit res max
}
if (LUT_ENABLED && (ctx->atten == ADC_ATTEN_DB_11) && (raw >= 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(raw, ctx->vref, ctx->low_curve, ctx->high_curve);
if (raw <= 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(raw, ctx->coeff_a, ctx->coeff_b);
*voltage = interpolate_two_points(linear_voltage, lut_voltage, LUT_ADC_STEP_SIZE, (raw - LUT_LOW_THRESH));
} else {
*voltage = lut_voltage;
}
} else {
*voltage = calculate_voltage_linear(raw, ctx->coeff_a, ctx->coeff_b);
}
return ESP_OK;
}
/* ----------------------- 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 unit_id)
{
//ADC reading at 150mV stored in two's complement format
uint32_t ret;
uint32_t bits;
if (unit_id == 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 unit_id)
{
//ADC reading at 850mV stored in two's complement format
uint32_t ret;
uint32_t bits;
if (unit_id == 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 unit_id,
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 (unit_id == 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 unit_id,
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 (unit_id == 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;
}

View File

@ -0,0 +1,15 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
/**
* @file adc_cali_schemes.h
*
* @brief Supported calibration schemes
*/
#define ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED 1

View File

@ -0,0 +1,15 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
/**
* @file adc_cali_schemes.h
*
* @brief Supported calibration schemes
*/
//No scheme supported

View File

@ -8,9 +8,13 @@
The linker will link constructor (adc2_init_code_calibration) only when any sections inside the same file (adc2_cal_include) is used.
Don't put any other code into this file. */
#include "hal/adc_hal.h"
#include "freertos/FreeRTOS.h"
#include "hal/adc_types.h"
#include "hal/adc_hal_common.h"
#include "esp_private/adc2_wifi.h"
#include "esp_private/adc_cali.h"
#include "esp_private/adc_private.h"
extern portMUX_TYPE rtc_spinlock;
/**
* @brief Set initial code to ADC2 after calibration. ADC2 RTC and ADC2 PWDET controller share the initial code.
@ -18,9 +22,11 @@ Don't put any other code into this file. */
*/
static __attribute__((constructor)) void adc2_init_code_calibration(void)
{
const adc_unit_t adc_n = ADC_UNIT_2;
const adc_atten_t atten = ADC_ATTEN_DB_11;
adc_cal_offset(adc_n, atten);
adc_hal_calibration_init(ADC_UNIT_2);
adc_calc_hw_calibration_code(ADC_UNIT_2, ADC_ATTEN_DB_11);
portENTER_CRITICAL(&rtc_spinlock);
adc_set_hw_calibration_code(ADC_UNIT_2, ADC_ATTEN_DB_11);
portEXIT_CRITICAL(&rtc_spinlock);
}
/** Don't call `adc2_cal_include` in user code. */

View File

@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
/**
* @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 uint64_t adc1_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
*/
const int32_t adc1_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
};

View File

@ -0,0 +1,15 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
/**
* @file adc_cali_schemes.h
*
* @brief Supported calibration schemes
*/
#define ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED 1

View File

@ -0,0 +1,15 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
/**
* @file adc_cali_schemes.h
*
* @brief Supported calibration schemes
*/
//Now no scheme supported

View File

@ -8,9 +8,13 @@
The linker will link constructor (adc2_init_code_calibration) only when any sections inside the same file (adc2_cal_include) is used.
Don't put any other code into this file. */
#include "hal/adc_hal.h"
#include "freertos/FreeRTOS.h"
#include "hal/adc_types.h"
#include "hal/adc_hal_common.h"
#include "esp_private/adc2_wifi.h"
#include "esp_private/adc_cali.h"
#include "esp_private/adc_private.h"
extern portMUX_TYPE rtc_spinlock;
/**
* @brief Set initial code to ADC2 after calibration. ADC2 RTC and ADC2 PWDET controller share the initial code.
@ -18,9 +22,11 @@ Don't put any other code into this file. */
*/
static __attribute__((constructor)) void adc2_init_code_calibration(void)
{
const adc_unit_t adc_n = ADC_UNIT_2;
const adc_atten_t atten = ADC_ATTEN_DB_11;
adc_cal_offset(adc_n, atten);
adc_hal_calibration_init(ADC_UNIT_2);
adc_calc_hw_calibration_code(ADC_UNIT_2, ADC_ATTEN_DB_11);
portENTER_CRITICAL(&rtc_spinlock);
adc_set_hw_calibration_code(ADC_UNIT_2, ADC_ATTEN_DB_11);
portEXIT_CRITICAL(&rtc_spinlock);
}
/** Don't call `adc2_cal_include` in user code. */

View File

@ -0,0 +1,255 @@
/*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "assert.h"
#include "esp_types.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "esp_efuse.h"
#include "esp_efuse_table.h"
#include "esp_efuse_rtc_table.h"
#include "hal/adc_types.h"
#include "soc/efuse_periph.h"
#include "soc/soc_caps.h"
#include "esp_adc/adc_cali_scheme.h"
#include "adc_cali_interface.h"
const __attribute__((unused)) static char *TAG = "adc_cali";
/* ------------------------ 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_t;
typedef struct {
int adc_calib_high; // the reading of adc ...
int adc_calib_high_voltage; // ... at this voltage (mV)
} adc_calib_data_ver2_t;
typedef struct {
char version_num;
adc_unit_t unit_id;
adc_atten_t atten_level;
union {
adc_calib_data_ver1_t ver1;
adc_calib_data_ver2_t ver2;
} efuse_data;
} adc_calib_parsed_info_t;
/* ------------------------ Context Structure--------------------------- */
typedef struct {
adc_unit_t unit_id; ///< ADC unit
adc_atten_t atten; ///< ADC attenuation
uint32_t coeff_a; ///< Gradient of ADC-Voltage curve
uint32_t coeff_b; ///< Offset of ADC-Voltage curve
} cali_chars_line_fitting_t;
/* ----------------------- Characterization Functions ----------------------- */
static bool prepare_calib_data_for(adc_unit_t unit_id, adc_atten_t atten, adc_calib_parsed_info_t *parsed_data_storage);
/**
* (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 unit_id,
adc_atten_t atten,
uint32_t high,
uint32_t low,
uint32_t *coeff_a,
uint32_t *coeff_b);
/*
* 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_t *parsed_data, cali_chars_line_fitting_t *ctx);
/* ------------------------ Interface Functions --------------------------- */
static esp_err_t cali_raw_to_voltage(void *arg, int raw, int *voltage);
/* ------------------------- Public API ------------------------------------- */
esp_err_t adc_cali_create_scheme_line_fitting(const adc_cali_line_fitting_config_t *config, adc_cali_handle_t *ret_handle)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(config && ret_handle, ESP_ERR_INVALID_ARG, TAG, "invalid arg: null pointer");
ESP_RETURN_ON_FALSE(config->unit_id < SOC_ADC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid ADC unit");
ESP_RETURN_ON_FALSE(config->atten < SOC_ADC_ATTEN_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid ADC attenuation");
//S2 Oneshot read only supports 13 bits, DMA read only supports 12 bits
ESP_RETURN_ON_FALSE(((config->bitwidth == SOC_ADC_RTC_MAX_BITWIDTH || config->bitwidth == SOC_ADC_DIGI_MAX_BITWIDTH) || config->bitwidth == ADC_BITWIDTH_DEFAULT), ESP_ERR_INVALID_ARG, TAG, "invalid bitwidth");
// current version only accepts encoding ver 1 and ver 2.
uint8_t adc_encoding_version = esp_efuse_rtc_table_read_calib_version();
ESP_RETURN_ON_FALSE(((adc_encoding_version == 1) || (adc_encoding_version == 2)), ESP_ERR_NOT_SUPPORTED, TAG, "Calibration required eFuse bits not burnt");
adc_cali_scheme_t *scheme = (adc_cali_scheme_t *)heap_caps_calloc(1, sizeof(adc_cali_scheme_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(scheme, ESP_ERR_NO_MEM, TAG, "no mem for adc calibration scheme");
cali_chars_line_fitting_t *chars = (cali_chars_line_fitting_t *)heap_caps_calloc(1, sizeof(cali_chars_line_fitting_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_GOTO_ON_FALSE(chars, ESP_ERR_NO_MEM, err, TAG, "no memory for the calibration characteristics");
scheme->raw_to_voltage = cali_raw_to_voltage;
scheme->ctx = chars;
adc_calib_parsed_info_t efuse_parsed_data = {0};
bool success = prepare_calib_data_for(config->unit_id, config->atten, &efuse_parsed_data);
assert(success);
success = calculate_characterization_coefficients(&efuse_parsed_data, chars);
assert(success);
ESP_LOGD(TAG, "adc%d (atten leven %d) calibration done: A:%d B:%d\n", config->unit_id, config->atten, chars->coeff_a, chars->coeff_b);
chars->unit_id = config->unit_id;
chars->atten = config->atten;
*ret_handle = scheme;
return ESP_OK;
err:
if (scheme) {
free(scheme);
}
return ret;
}
esp_err_t adc_cali_delete_scheme_line_fitting(adc_cali_handle_t handle)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
free(handle->ctx);
handle->ctx = NULL;
free(handle);
handle = NULL;
return ESP_OK;
}
/* ------------------------ Interface Functions --------------------------- */
static esp_err_t cali_raw_to_voltage(void *arg, int raw, int *voltage)
{
//pointers are checked in the upper layer
cali_chars_line_fitting_t *ctx = arg;
*voltage = raw * ctx->coeff_a / coeff_a_scaling + ctx->coeff_b / coeff_b_scaling;
return ESP_OK;
}
/* ----------------------- Characterization Functions ----------------------- */
static bool prepare_calib_data_for(adc_unit_t unit_id, adc_atten_t atten, adc_calib_parsed_info_t *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->unit_id = unit_id;
parsed_data_storage->atten_level = atten;
switch (version_num) {
case 1:
// note: use the unit_id as in hal, which start from 0.
tag = esp_efuse_rtc_table_get_tag(version_num, unit_id, 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, unit_id, 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, unit_id, 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, unit_id, 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, unit_id, 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;
}
/**
* (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 unit_id,
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_t *parsed_data, cali_chars_line_fitting_t *ctx)
{
switch (parsed_data->version_num) {
case 1:
ESP_LOGD(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->unit_id, parsed_data->atten_level,
parsed_data->efuse_data.ver1.adc_calib_high, parsed_data->efuse_data.ver1.adc_calib_low,
&(ctx->coeff_a), &(ctx->coeff_b));
break;
case 2:
ESP_LOGD(TAG, "Calib V2, volt%dmV\n", parsed_data->efuse_data.ver2.adc_calib_high);
ctx->coeff_a = coeff_a_scaling * parsed_data->efuse_data.ver2.adc_calib_high_voltage /
parsed_data->efuse_data.ver2.adc_calib_high;
ctx->coeff_b = 0;
break;
default:
return false;
break;
}
return true;
}

View File

@ -0,0 +1,15 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
/**
* @file adc_cali_schemes.h
*
* @brief Supported calibration schemes
*/
#define ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED 1

View File

@ -0,0 +1,48 @@
/*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
/**
* @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 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 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 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 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
};

View File

@ -0,0 +1,15 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
/**
* @file adc_cali_schemes.h
*
* @brief Supported calibration schemes
*/
#define ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED 1

View File

@ -0,0 +1,59 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#include "esp_bit_defs.h"
#include "hal/adc_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief ADC calibration handle
*/
typedef struct adc_cali_scheme_t *adc_cali_handle_t;
/**
* @brief ADC calibration scheme
*/
typedef enum {
ADC_CALI_SCHEME_VER_LINE_FITTING = BIT(0), ///< Line fitting scheme
ADC_CALI_SCHEME_VER_CURVE_FITTING = BIT(1), ///< Curve fitting scheme
} adc_cali_scheme_ver_t;
/**
* @brief Check the supported ADC calibration scheme
*
* @param[out] scheme_mask Supported ADC calibration scheme(s)
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_NOT_SUPPORTED: No supported calibration scheme
*/
esp_err_t adc_cali_check_scheme(adc_cali_scheme_ver_t *scheme_mask);
/**
* @brief Convert ADC raw data to calibrated voltage
*
* @param[in] handle ADC calibration handle
* @param[in] raw ADC raw data
* @param[out] voltage Calibrated ADC voltage (in mV)
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_INVALID_STATE: Invalid state, scheme didn't registered
*/
esp_err_t adc_cali_raw_to_voltage(adc_cali_handle_t handle, int raw, int *voltage);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,135 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_adc/adc_cali.h"
#include "adc_cali_schemes.h"
#ifdef __cplusplus
extern "C" {
#endif
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
/*---------------------------------------------------------------
Curve Fitting Calibration Scheme
---------------------------------------------------------------*/
typedef struct {
adc_unit_t unit_id; ///< ADC unit
adc_atten_t atten; ///< ADC attenuation
adc_bitwidth_t bitwidth; ///< ADC raw output bitwidth
} adc_cali_curve_fitting_config_t;
/**
* @brief Create a Curve Fitting calibration scheme
*
* After creating, you'll get a handle to this scheme. Then you can use the driver APIS in `esp_adc/adc_cali.h` to do the
* ADC calibration via the handle you get.
*
* @param[in] config Initial configurations
* @param[out] handle ADC calibration handle
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_NO_MEM: No enough memory
* - ESP_ERR_NOT_SUPPORTED: Scheme required eFuse bits not burnt
*/
esp_err_t adc_cali_create_scheme_curve_fitting(const adc_cali_curve_fitting_config_t *config, adc_cali_handle_t *ret_handle);
/**
* @brief Delete the Curve Fitting calibration scheme handle
*
* @param[in] handle ADC calibration handle
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
*/
esp_err_t adc_cali_delete_scheme_curve_fitting(adc_cali_handle_t handle);
#endif // #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
/*---------------------------------------------------------------
Line Fitting Calibration Scheme
---------------------------------------------------------------*/
/**
* @brief Type of calibration value used in line fitting scheme characterization
*/
typedef enum {
ADC_CALI_LINE_FITTING_EFUSE_VAL_EFUSE_VREF = 0, ///< Characterization based on reference voltage stored in eFuse
ADC_CALI_LINE_FITTING_EFUSE_VAL_EFUSE_TP = 1, ///< Characterization based on Two Point values stored in eFuse
ADC_CALI_LINE_FITTING_EFUSE_VAL_DEFAULT_VREF = 2, ///< Characterization based on default reference voltage
} adc_cali_line_fitting_efuse_val_t;
typedef struct {
adc_unit_t unit_id; ///< ADC unit
adc_atten_t atten; ///< ADC attenuation
adc_bitwidth_t bitwidth; ///< ADC raw output bitwidth
#if CONFIG_IDF_TARGET_ESP32
/**
* @brief Default ADC reference voltage in mV.
*
* Use this when the ADC calibration value is `ADC_CALI_LINE_FITTING_EFUSE_VAL_DEFAULT_VREF`.
* If others, driver will use the calibration code burnt in the eFuse for calibration.
*/
uint32_t default_vref;
#endif
} adc_cali_line_fitting_config_t;
/**
* @brief Create a Line Fitting calibration scheme
*
* After creating, you'll get a handle to this scheme. Then you can use the driver APIS in `esp_adc/adc_cali.h` to do the
* ADC calibration via the handle you get.
*
* @param[in] config Initial configurations
* @param[out] handle ADC calibration handle
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_NO_MEM: No enough memory
* - ESP_ERR_NOT_SUPPORTED: Scheme required eFuse bits not burnt
*/
esp_err_t adc_cali_create_scheme_line_fitting(const adc_cali_line_fitting_config_t *config, adc_cali_handle_t *ret_handle);
/**
* @brief Delete the Line Fitting calibration scheme handle
*
* @param[in] handle ADC calibration handle
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
*/
esp_err_t adc_cali_delete_scheme_line_fitting(adc_cali_handle_t handle);
#if CONFIG_IDF_TARGET_ESP32
/**
* @brief Helper function to quickly check the ADC calibration code burnt on your eFuse
*
* @note If `cali_val` equals to `ESP_ADC_CALI_VAL_DEFAULT_VREF`, please set the `default_vref`
* when creating this scheme (See `ESP_ADC_CALI_SCHEME_VER_LINE_FITTING_init_t`)
*
* @param[out] cali_val See `esp_adc_cali_value_t`
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_NOT_SUPPORTED: Scheme required eFuse bits not burnt
*/
esp_err_t adc_cali_scheme_line_fitting_check_efuse(adc_cali_line_fitting_efuse_val_t *cali_val);
#endif // CONFIG_IDF_TARGET_ESP32
#endif // #if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,232 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "sdkconfig.h"
#include "hal/adc_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_ADC_DMA_SUPPORTED
/**
* @brief Driver Backgrounds
*
* --------------------------------------------------------------------------------------------------------
* | Conversion Frame |
* --------------------------------------------------------------------------------------------------------
* | Conversion Result | Conversion Result | Conversion Result | Conversion Result | ... |
* --------------------------------------------------------------------------------------------------------
*
* ADC continuous mode conversion is made up with multiple Conversion Frames.
* - Conversion Frame: One Conversion Frame contains multiple Conversion Results.
* Conversion Frame size is configured in `adc_continuous_handle_cfg_t:conv_frame_size`, in bytes.
* Each time driver see an interrupt event, this means one Conversion Frame is generated by the hardware.
* - Conversion Result: One Conversion Result contains multiple bytes (see `SOC_ADC_DIGI_RESULT_BYTES`). Its
* structure is `adc_digi_output_data_t`, including ADC Unit, ADC Channel and Raw Data.
*
* For example:
* conv_frame_size = 100,
* then one Conversion Frame contains (100 / `SOC_ADC_DIGI_RESULT_BYTES`) pieces of Conversion Results
*/
/**
* @brief ADC read max timeout value, it may make the ``adc_continuous_read`` block forever if the OS supports
*/
#define ADC_MAX_DELAY UINT32_MAX
/**
* @brief Type of adc continuous mode driver handle
*/
typedef struct adc_continuous_ctx_t *adc_continuous_handle_t;
/**
* @brief ADC continuous mode driver initial configurations
*/
typedef struct {
uint32_t max_store_buf_size; ///< Max length of the conversion Results that driver can store, in bytes.
uint32_t conv_frame_size; ///< Conversion frame size, in bytes. This should be in multiples of `SOC_ADC_DIGI_DATA_BYTES_PER_CONV`.
} adc_continuous_handle_cfg_t;
/**
* @brief ADC continuous mode driver configurations
*/
typedef struct {
uint32_t pattern_num; ///< Number of ADC channels that will be used
adc_digi_pattern_config_t *adc_pattern; ///< List of configs for each ADC channel that will be used
uint32_t sample_freq_hz; /*!< The expected ADC sampling frequency in Hz. Please refer to `soc/soc_caps.h` to know available sampling frequency range*/
adc_digi_convert_mode_t conv_mode; ///< ADC DMA conversion mode, see `adc_digi_convert_mode_t`.
adc_digi_output_format_t format; ///< ADC DMA conversion output format, see `adc_digi_output_format_t`.
} adc_continuous_config_t;
/**
* @brief Event data structure
* @note The `conv_frame_buffer` is maintained by the driver itself, so never free this piece of memory.
*/
typedef struct {
uint8_t *conv_frame_buffer; ///< Pointer to conversion result buffer for one conversion frame
uint32_t size; ///< Conversion frame size
} adc_continuous_evt_data_t;
/**
* @brief Prototype of ADC continuous mode event callback
*
* @param[in] handle ADC continuous mode driver handle
* @param[in] edata Pointer to ADC contunuous mode event data
* @param[in] user_data User registered context, registered when in `adc_continuous_register_event_callbacks()`
*
* @return Whether a high priority task is woken up by this function
*/
typedef bool (*adc_continuous_callback_t)(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data);
/**
* @brief Group of ADC continuous mode callbacks
*
* @note These callbacks are all running in an ISR environment.
* @note When CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM.
* Involved variables should be in internal RAM as well.
*/
typedef struct {
adc_continuous_callback_t on_conv_done; ///< Event callback, invoked when one conversion frame is done. See `@brief Driver Backgrounds` to konw `conversion frame` concept.
adc_continuous_callback_t on_pool_ovf; ///< Event callback, invoked when the internal pool is full.
} adc_continuous_evt_cbs_t;
/**
* @brief Initialize ADC continuous driver and get a handle to it
*
* @param[in] hdl_config Pointer to ADC initilization config. Refer to ``adc_continuous_handle_cfg_t``.
* @param[out] ret_handle ADC continuous mode driver handle
*
* @return
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid.
* - ESP_ERR_NOT_FOUND No free interrupt found with the specified flags
* - ESP_ERR_NO_MEM If out of memory
* - ESP_OK On success
*/
esp_err_t adc_continuous_new_handle(const adc_continuous_handle_cfg_t *hdl_config, adc_continuous_handle_t *ret_handle);
/**
* @brief Set ADC continuous mode required configurations
*
* @param[in] handle ADC continuous mode driver handle
* @param[in] config Refer to ``adc_digi_config_t``.
*
* @return
* - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment
* - ESP_ERR_INVALID_ARG: If the combination of arguments is invalid.
* - ESP_OK: On success
*/
esp_err_t adc_continuous_config(adc_continuous_handle_t handle, const adc_continuous_config_t *config);
/**
* @brief Register callbacks
*
* @note User can deregister a previously registered callback by calling this function and setting the to-be-deregistered callback member int
* the `cbs` structure to NULL.
* @note When CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM.
* Involved variables (including `user_data`) should be in internal RAM as well.
* @note You should only call this API when the ADC continuous mode driver isn't started. Check return value to know this.
*
* @param[in] handle ADC continuous mode driver handle
* @param[in] cbs Group of callback functions
* @param[in] user_data User data, which will be delivered to the callback functions directly
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid arguments
* - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment
*/
esp_err_t adc_continuous_register_event_callbacks(adc_continuous_handle_t handle, const adc_continuous_evt_cbs_t *cbs, void *user_data);
/**
* @brief Start the ADC under continuous mode. After this, the hardware starts working.
*
* @param[in] handle ADC continuous mode driver handle
*
* @return
* - ESP_ERR_INVALID_STATE Driver state is invalid.
* - ESP_OK On success
*/
esp_err_t adc_continuous_start(adc_continuous_handle_t handle);
/**
* @brief Read bytes from ADC under continuous mode.
*
* @param[in] handle ADC continuous mode driver handle
* @param[out] buf Conversion result buffer to read from ADC. Suggest convert to `adc_digi_output_data_t` for `ADC Conversion Results`.
* See `@brief Driver Backgrounds` to know this concept.
* @param[in] length_max Expected length of the Conversion Results read from the ADC, in bytes.
* @param[out] out_length Real length of the Conversion Results read from the ADC via this API, in bytes.
* @param[in] timeout_ms Time to wait for data via this API, in millisecond.
*
* @return
* - ESP_ERR_INVALID_STATE Driver state is invalid. Usually it means the ADC sampling rate is faster than the task processing rate.
* - ESP_ERR_TIMEOUT Operation timed out
* - ESP_OK On success
*/
esp_err_t adc_continuous_read(adc_continuous_handle_t handle, uint8_t *buf, uint32_t length_max, uint32_t *out_length, uint32_t timeout_ms);
/**
* @brief Stop the ADC. After this, the hardware stops working.
*
* @param[in] handle ADC continuous mode driver handle
*
* @return
* - ESP_ERR_INVALID_STATE Driver state is invalid.
* - ESP_OK On success
*/
esp_err_t adc_continuous_stop(adc_continuous_handle_t handle);
/**
* @brief Deinitialize the ADC continuous driver.
*
* @param[in] handle ADC continuous mode driver handle
*
* @return
* - ESP_ERR_INVALID_STATE Driver state is invalid.
* - ESP_OK On success
*/
esp_err_t adc_continuous_deinit(adc_continuous_handle_t handle);
/**
* @brief Get ADC channel from the given GPIO number
*
* @param[in] io_num GPIO number
* @param[out] unit_id ADC unit
* @param[out] channel ADC channel
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_NOT_FOUND: The IO is not a valid ADC pad
*/
esp_err_t adc_continuous_io_to_channel(int io_num, adc_unit_t *unit_id, adc_channel_t *channel);
/**
* @brief Get GPIO number from the given ADC channel
*
* @param[in] unit_id ADC unit
* @param[in] channel ADC channel
* @param[out] io_num GPIO number
*
* @param
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
*/
esp_err_t adc_continuous_channel_to_io(adc_unit_t unit_id, adc_channel_t channel, int *io_num);
#endif // #if SOC_ADC_DMA_SUPPORTED
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,130 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "hal/adc_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Type of ADC unit handle for oneshot mode
*/
typedef struct adc_oneshot_unit_ctx_t *adc_oneshot_unit_handle_t;
/**
* @brief ADC oneshot driver initial configurations
*/
typedef struct {
adc_unit_t unit_id; ///< ADC unit
adc_ulp_mode_t ulp_mode; ///< ADC controlled by ULP, see `adc_ulp_mode_t`
} adc_oneshot_unit_init_cfg_t;
/**
* @brief ADC channel configurations
*/
typedef struct {
adc_atten_t atten; ///< ADC attenuation
adc_bitwidth_t bitwidth; ///< ADC conversion result bits
} adc_oneshot_chan_cfg_t;
/**
* @brief Create a handle to a specific ADC unit
*
* @note This API is thread-safe. For more details, see ADC programming guide
*
* @param[in] init_config Driver initial configurations
* @param[out] ret_unit ADC unit handle
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid arguments
* - ESP_ERR_NO_MEM: No memory
* - ESP_ERR_NOT_FOUND: The ADC peripheral to be claimed is already in use
*/
esp_err_t adc_oneshot_new_unit(const adc_oneshot_unit_init_cfg_t *init_config, adc_oneshot_unit_handle_t *ret_unit);
/**
* @brief Set ADC oneshot mode required configurations
*
* @note This API is thread-safe. For more details, see ADC programming guide
*
* @param[in] handle ADC handle
* @param[in] channel ADC channel to be configured
* @param[in] config ADC configurations
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid arguments
*/
esp_err_t adc_oneshot_config_channel(adc_oneshot_unit_handle_t handle, adc_channel_t channel, const adc_oneshot_chan_cfg_t *config);
/**
* @brief Get one ADC conversion raw result
*
* @note This API is thread-safe. For more details, see ADC programming guide
* @note This API should NOT be called in an ISR context
*
* @param[in] handle ADC handle
* @param[in] chan ADC channel
* @param[out] out_raw ADC conversion raw result
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid arguments
* - ESP_ERR_TIMEOUT: Timeout, the ADC result is invalid
*/
esp_err_t adc_oneshot_read(adc_oneshot_unit_handle_t handle, adc_channel_t chan, int *out_raw);
/**
* @brief Delete the ADC unit handle
*
* @note This API is thread-safe. For more details, see ADC programming guide
*
* @param[in] handle ADC handle
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid arguments
* - ESP_ERR_NOT_FOUND: The ADC peripheral to be disclaimed isn't in use
*/
esp_err_t adc_oneshot_del_unit(adc_oneshot_unit_handle_t handle);
/**
* @brief Get ADC channel from the given GPIO number
*
* @param[in] io_num GPIO number
* @param[out] unit_id ADC unit
* @param[out] channel ADC channel
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_NOT_FOUND: The IO is not a valid ADC pad
*/
esp_err_t adc_oneshot_io_to_channel(int io_num, adc_unit_t *unit_id, adc_channel_t *channel);
/**
* @brief Get GPIO number from the given ADC channel
*
* @param[in] unit_id ADC unit
* @param[in] channel ADC channel
* @param[out] io_num GPIO number
*
* @param
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
*/
esp_err_t adc_oneshot_channel_to_io(adc_unit_t unit_id, adc_channel_t channel, int *io_num);
#ifdef __cplusplus
}
#endif

View File

@ -21,19 +21,20 @@ extern "C" {
* The WIFI module may have to wait for a short time for the current conversion (if exist) to finish.
*
* @return
* - ESP_OK success
* - ESP_ERR_TIMEOUT reserved for future use. Currently the function will wait until success.
* - ESP_OK success
* - ESP_ERR_TIMEOUT reserved for future use. Currently the function will wait until success.
*/
esp_err_t adc2_wifi_acquire(void);
/**
* @brief For WIFI module to let other tasks use the ADC2 when WIFI is not work.
*
* Other tasks will be forbidden to use ADC2 between ``adc2_wifi_acquire`` and ``adc2_wifi_release``.
* Call this function to release the occupation of ADC2 by WIFI.
*
* @return always return ESP_OK.
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_STATE: The lock(s) isn't acquired yet
*/
esp_err_t adc2_wifi_release(void);

View File

@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#include "hal/adc_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Acquire ADC lock by unit
*
* The lock acquiring sequence will be: ADC1, ADC2, ...
*
* @note If any of the locks are taken, this API will wait until the lock is successfully acquired.
*
* @param[in] unit_mask ADC unit mask
*
* @return
* - ESP_OK: On success
*/
esp_err_t adc_lock_acquire(adc_unit_t unit_mask);
/**
* @brief Release ADC lock by unit
*
* The lock releasing sequence will be: ..., ADC2, ADC1
*
* @param[in] unit_mask ADC unit mask
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_STATE: The lock(s) isn't acquired yet
*/
esp_err_t adc_lock_release(adc_unit_t unit_mask);
/**
* @brief Try to acquire ADC lock by unit
*
* The lock acquiring sequence will be: ADC1, ADC2, ...
*
* @note If any of the locks are taken, this API will return immediately with an error `ESP_ERR_TIMEOUT`
*
* @param[in] unit_mask ADC unit mask
*
* @return
* - ESP_OK: On success
* - ESP_ERR_TIMEOUT: Lock(s) is taken already
*/
esp_err_t adc_lock_try_acquire(adc_unit_t unit_mask);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,130 @@
/*
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// DO NOT USE THESE APIS IN ANY APPLICATIONS
#pragma once
#include "esp_err.h"
#include "hal/adc_types.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
/*------------------------------------------------------------------------------
* For those who use APB_SARADC periph
*----------------------------------------------------------------------------*/
/**
* @brief Claim the usage of the APB_SARADC periph
*
* Reference count inside
*/
void adc_apb_periph_claim(void);
/**
* @brief Free the usage of the APB_SARADC periph
*
* Reference count inside
*/
void adc_apb_periph_free(void);
/*------------------------------------------------------------------------------
* ADC Power
*----------------------------------------------------------------------------*/
/**
* @brief Acquire the ADC Power
*/
void adc_power_acquire(void);
/**
* @brief Release the ADC Power
*/
void adc_power_release(void);
/*---------------------------------------------------------------
ADC IOs
---------------------------------------------------------------*/
/**
* @brief Get ADC channel from the given GPIO number
*
* @param[in] io_num GPIO number
* @param[out] unit_id ADC unit
* @param[out] channel ADC channel
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_NOT_FOUND: The IO is not a valid ADC pad
*/
esp_err_t adc_io_to_channel(int io_num, adc_unit_t *unit_id, adc_channel_t *channel);
/**
* @brief Get GPIO number from the given ADC channel
*
* @param[in] unit_id ADC unit
* @param[in] channel ADC channel
* @param[out] io_num GPIO number
*
* @param
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
*/
esp_err_t adc_channel_to_io(adc_unit_t unit_id, adc_channel_t channel, int *io_num);
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
/*---------------------------------------------------------------
ADC Hardware Calibration
---------------------------------------------------------------*/
/**
* @brief Calculate the ADC HW calibration code. (Based on the pre-stored efuse or actual calibration)
*
* @param adc_n ADC unit to calibrate
* @param atten Attenuation to use
*/
void adc_calc_hw_calibration_code(adc_unit_t adc_n, adc_atten_t atten);
/**
* @brief Set the ADC HW calibration code.
*
* @param adc_n ADC unit to calibrate
* @param atten Attenuation to use
*/
void adc_set_hw_calibration_code(adc_unit_t adc_n, adc_atten_t atten);
#endif //#if SOC_ADC_CALIBRATION_V1_SUPPORTED
/*---------------------------------------------------------------
ADC Oneshot Read API ISR Version
---------------------------------------------------------------*/
typedef struct adc_oneshot_unit_ctx_t *adc_oneshot_unit_handle_t;
/**
* @brief ISR version to get one ADC conversion raw result
*
* @note This API only provide atomic register settings, without hardware resources protection. When other drivers are using
* SAR-ADCs, calling this API may get wrong ADC result.
* @note This API can be called in an ISR context.
* @note Strongly suggest using this function when there's no concurrent hardware usage to the ADC. You can refer to ADC Oneshot
* Programming Guide to know ADC Hardware Limitations
*
* @param[in] handle ADC handle
* @param[in] chan ADC channel
* @param[out] out_raw ADC conversion raw result
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid arguments
* - ESP_ERR_INVALID_STATE: Invalid state, the ADC result is invalid
*/
esp_err_t adc_oneshot_read_isr(adc_oneshot_unit_handle_t handle, adc_channel_t chan, int *out_raw);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_types.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct adc_cali_scheme_t adc_cali_scheme_t;
/**
* @brief ADC Calibration Scheme Interface and Context
*/
struct adc_cali_scheme_t {
/**
* @brief Convert ADC raw data to calibrated voltage
*
* @param[in] arg ///< ADC calibration scheme specific context
* @param[in] raw ///< ADC raw data
* @param[out] voltage ///< Calibrated ADC voltage (in mV)
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_INVALID_STATE: Invalid state, scheme didn't registered
*/
esp_err_t (*raw_to_voltage)(void *arg, int raw, int *voltage);
/**
* @brief ADC calibration specific contexts
* Can be customized to difference calibration schemes
*/
void *ctx;
};
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,5 @@
[mapping:esp_adc]
archive: libesp_adc.a
entries:
if ADC_ONESHOT_CTRL_FUNC_IN_IRAM = y:
adc_oneshot: adc_oneshot_read_isr (noflash)

View File

@ -0,0 +1,20 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(adc_test)
if(CONFIG_COMPILER_DUMP_RTL_FILES)
add_custom_target(check_test_app_sections ALL
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
--rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/esp_adc/
--elf-file ${CMAKE_BINARY_DIR}/adc_test.elf
find-refs
--from-sections=.iram0.text
--to-sections=.flash.text,.flash.rodata
--exit-code
DEPENDS ${elf}
)
endif()

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |

View File

@ -0,0 +1,11 @@
set(srcs "test_app_main.c"
"test_adc.c"
"test_adc_performance.c"
"test_adc_driver.c"
"test_adc_driver_iram.c"
"test_common_adc.c")
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRCS ${srcs}
WHOLE_ARCHIVE)

View File

@ -0,0 +1,260 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "unity.h"
#include "esp_log.h"
#include "soc/adc_periph.h"
#include "esp_adc/adc_oneshot.h"
#include "driver/gpio.h"
#include "driver/rtc_io.h"
#include "test_common_adc.h"
const __attribute__((unused)) static char *TAG = "TEST_ADC";
/*---------------------------------------------------------------
ADC General Macros
---------------------------------------------------------------*/
//ADC Channels
#if CONFIG_IDF_TARGET_ESP32
#define ADC1_TEST_CHAN0 ADC_CHANNEL_4
#define ADC1_TEST_CHAN1 ADC_CHANNEL_5
#define ADC2_TEST_CHAN0 ADC_CHANNEL_0
static const char *TAG_CH[2][10] = {{"ADC1_CH4", "ADC1_CH5"}, {"ADC2_CH0"}};
#else
#define ADC1_TEST_CHAN0 ADC_CHANNEL_2
#define ADC1_TEST_CHAN1 ADC_CHANNEL_3
#define ADC2_TEST_CHAN0 ADC_CHANNEL_0
static const char *TAG_CH[2][10] = {{"ADC1_CH2", "ADC1_CH3"}, {"ADC2_CH0"}};
#endif
/*---------------------------------------------------------------
ADC Oneshot High / Low test
---------------------------------------------------------------*/
TEST_CASE("ADC oneshot high/low test", "[adc_oneshot]")
{
static int adc_raw[2][10];
//-------------ADC1 Init---------------//
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_unit_init_cfg_t init_config1 = {
.unit_id = ADC_UNIT_1,
.ulp_mode = false,
};
TEST_ESP_OK(adc_oneshot_new_unit(&init_config1, &adc1_handle));
#if (SOC_ADC_PERIPH_NUM >= 2)
//-------------ADC2 Init---------------//
adc_oneshot_unit_handle_t adc2_handle;
adc_oneshot_unit_init_cfg_t init_config2 = {
.unit_id = ADC_UNIT_2,
.ulp_mode = false,
};
TEST_ESP_OK(adc_oneshot_new_unit(&init_config2, &adc2_handle));
#endif //#if (SOC_ADC_PERIPH_NUM >= 2)
//-------------ADC1 TEST Channel 0 Config---------------//
adc_oneshot_chan_cfg_t config = {
.bitwidth = ADC_BITWIDTH_DEFAULT,
.atten = ADC_ATTEN_DB_11,
};
TEST_ESP_OK(adc_oneshot_config_channel(adc1_handle, ADC1_TEST_CHAN0, &config));
//-------------ADC1 TEST Channel 1 Config---------------//
TEST_ESP_OK(adc_oneshot_config_channel(adc1_handle, ADC1_TEST_CHAN1, &config));
#if (SOC_ADC_PERIPH_NUM >= 2)
//-------------ADC2 TEST Channel 0 Config---------------//
TEST_ESP_OK(adc_oneshot_config_channel(adc2_handle, ADC2_TEST_CHAN0, &config));
#endif //#if (SOC_ADC_PERIPH_NUM >= 2)
test_adc_set_io_level(ADC_UNIT_1, ADC1_TEST_CHAN0, 0);
TEST_ESP_OK(adc_oneshot_read(adc1_handle, ADC1_TEST_CHAN0, &adc_raw[0][0]));
ESP_LOGI(TAG_CH[0][0], "raw data: %d", adc_raw[0][0]);
TEST_ASSERT_INT_WITHIN(ADC_TEST_LOW_THRESH, ADC_TEST_LOW_VAL, adc_raw[0][0]);
test_adc_set_io_level(ADC_UNIT_1, ADC1_TEST_CHAN1, 1);
TEST_ESP_OK(adc_oneshot_read(adc1_handle, ADC1_TEST_CHAN1, &adc_raw[0][1]));
ESP_LOGI(TAG_CH[0][1], "raw data: %d", adc_raw[0][1]);
TEST_ASSERT_INT_WITHIN(ADC_TEST_HIGH_THRESH, ADC_TEST_HIGH_VAL, adc_raw[0][1]);
#if (SOC_ADC_PERIPH_NUM >= 2)
test_adc_set_io_level(ADC_UNIT_2, ADC2_TEST_CHAN0, 0);
TEST_ESP_OK(adc_oneshot_read(adc2_handle, ADC2_TEST_CHAN0, &adc_raw[1][0]));
ESP_LOGI(TAG_CH[1][0], "raw data: %d", adc_raw[1][0]);
TEST_ASSERT_INT_WITHIN(ADC_TEST_LOW_THRESH, ADC_TEST_LOW_VAL, adc_raw[1][0]);
#endif //#if (SOC_ADC_PERIPH_NUM >= 2)
test_adc_set_io_level(ADC_UNIT_1, ADC1_TEST_CHAN0, 1);
TEST_ESP_OK(adc_oneshot_read(adc1_handle, ADC1_TEST_CHAN0, &adc_raw[0][0]));
ESP_LOGI(TAG_CH[0][0], "raw data: %d", adc_raw[0][0]);
TEST_ASSERT_INT_WITHIN(ADC_TEST_HIGH_THRESH, ADC_TEST_HIGH_VAL, adc_raw[0][0]);
test_adc_set_io_level(ADC_UNIT_1, ADC1_TEST_CHAN1, 0);
TEST_ESP_OK(adc_oneshot_read(adc1_handle, ADC1_TEST_CHAN1, &adc_raw[0][1]));
ESP_LOGI(TAG_CH[0][1], "raw data: %d", adc_raw[0][1]);
TEST_ASSERT_INT_WITHIN(ADC_TEST_LOW_THRESH, ADC_TEST_LOW_VAL, adc_raw[0][1]);
#if (SOC_ADC_PERIPH_NUM >= 2)
test_adc_set_io_level(ADC_UNIT_2, ADC2_TEST_CHAN0, 1);
TEST_ESP_OK(adc_oneshot_read(adc2_handle, ADC2_TEST_CHAN0, &adc_raw[1][0]));
ESP_LOGI(TAG_CH[1][0], "raw data: %d", adc_raw[1][0]);
TEST_ASSERT_INT_WITHIN(ADC_TEST_HIGH_THRESH, ADC_TEST_HIGH_VAL, adc_raw[1][0]);
#endif //#if (SOC_ADC_PERIPH_NUM >= 2)
TEST_ESP_OK(adc_oneshot_del_unit(adc1_handle));
#if (SOC_ADC_PERIPH_NUM >= 2)
TEST_ESP_OK(adc_oneshot_del_unit(adc2_handle));
#endif //#if (SOC_ADC_PERIPH_NUM >= 2)
}
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
/*---------------------------------------------------------------
ADC Oneshot with Light Sleep
---------------------------------------------------------------*/
#include <inttypes.h>
#include "esp_sleep.h"
#include "esp_private/regi2c_ctrl.h"
#include "soc/regi2c_saradc.h"
#define TEST_REGI2C_ANA_CALI_BYTE_NUM 8
static void s_adc_oneshot_with_sleep(adc_unit_t unit_id, adc_channel_t channel)
{
adc_atten_t atten[SOC_ADC_ATTEN_NUM] = {ADC_ATTEN_DB_0, ADC_ATTEN_DB_2_5, ADC_ATTEN_DB_6, ADC_ATTEN_DB_11};
//-------------ADC Init---------------//
adc_oneshot_unit_handle_t adc_handle;
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = unit_id,
.ulp_mode = false,
};
TEST_ESP_OK(adc_oneshot_new_unit(&init_config, &adc_handle));
//-------------ADC Channel Config---------------//
adc_oneshot_chan_cfg_t config = {
.bitwidth = SOC_ADC_RTC_MAX_BITWIDTH,
};
//-------------ADC Calibration Init---------------//
bool do_calibration = false;
adc_cali_handle_t cali_handle[SOC_ADC_ATTEN_NUM] = {};
for (int i = 0; i < SOC_ADC_ATTEN_NUM; i++) {
do_calibration = test_adc_calibration_init(unit_id, i, SOC_ADC_RTC_MAX_BITWIDTH, &cali_handle[i]);
}
if (!do_calibration) {
ESP_LOGW(TAG, "No efuse bits burnt, only test the regi2c analog register values");
}
for (int i = 0; i < SOC_ADC_ATTEN_NUM; i++) {
//-------------ADC Channel Config---------------//
config.atten = atten[i];
TEST_ESP_OK(adc_oneshot_config_channel(adc_handle, channel, &config));
printf("Test with atten: %d\n", atten[i]);
//---------------------------------Before Sleep-----------------------------------//
printf("Before Light Sleep\n");
int raw_expected = 0;
int cali_expected = 0;
uint8_t regi2c_cali_val_before[TEST_REGI2C_ANA_CALI_BYTE_NUM] = {};
//Read
TEST_ESP_OK(adc_oneshot_read(adc_handle, channel, &raw_expected));
if (do_calibration) {
TEST_ESP_OK(adc_cali_raw_to_voltage(cali_handle[i], raw_expected, &cali_expected));
}
//Print regi2c
printf("regi2c cali val is: ");
for (int j = 0; j < TEST_REGI2C_ANA_CALI_BYTE_NUM; j++) {
regi2c_cali_val_before[j] = regi2c_ctrl_read_reg(I2C_SAR_ADC, I2C_SAR_ADC_HOSTID, j);
printf("0x%x ", regi2c_cali_val_before[j]);
}
printf("\n");
//Print result
ESP_LOGI(TAG, "ADC%d Chan%d: raw data: %d", unit_id + 1, channel, raw_expected);
ESP_LOGI(TAG, "ADC%d Chan%d: cali data: %d", unit_id + 1, channel, cali_expected);
//---------------------------------Sleep-----------------------------------//
esp_sleep_enable_timer_wakeup(30 * 1000);
esp_light_sleep_start();
//---------------------------------After Sleep-----------------------------------//
printf("After Light Sleep\n");
int raw_after_sleep = 0;
int cali_after_sleep = 0;
uint8_t regi2c_cali_val_after[TEST_REGI2C_ANA_CALI_BYTE_NUM] = {};
//Print regi2c
printf("regi2c cali val is: ");
for (int i = 0; i < TEST_REGI2C_ANA_CALI_BYTE_NUM; i++) {
regi2c_cali_val_after[i] = regi2c_ctrl_read_reg(I2C_SAR_ADC, I2C_SAR_ADC_HOSTID, i);
printf("0x%x ", regi2c_cali_val_after[i]);
}
printf("\n");
//Read
TEST_ESP_OK(adc_oneshot_read(adc_handle, channel, &raw_after_sleep));
if (do_calibration) {
TEST_ESP_OK(adc_cali_raw_to_voltage(cali_handle[i], raw_after_sleep, &cali_after_sleep));
}
//Print result
ESP_LOGI(TAG, "ADC%d Chan%d: raw data: %d", unit_id + 1, channel, raw_after_sleep);
if (do_calibration) {
ESP_LOGI(TAG, "ADC%d Chan%d: cali data: %d", unit_id + 1, channel, cali_after_sleep);
}
//Compare
int32_t raw_diff = raw_expected - raw_after_sleep;
ESP_LOGI(TAG, "ADC%d Chan%d: raw difference: %d", unit_id + 1, channel, raw_diff);
if (do_calibration) {
int32_t cali_diff = cali_expected - cali_after_sleep;
ESP_LOGI(TAG, "ADC%d Chan%d: cali difference: %d", unit_id + 1, channel, cali_diff);
}
//Test Calibration registers
for (int i = 0; i < TEST_REGI2C_ANA_CALI_BYTE_NUM; i++) {
TEST_ASSERT_EQUAL(regi2c_cali_val_before[i], regi2c_cali_val_after[i]);
}
ESP_LOGI(TAG, "Cali register settings unchanged\n");
}
TEST_ESP_OK(adc_oneshot_del_unit(adc_handle));
for (int i = 0; i < SOC_ADC_ATTEN_NUM; i++) {
if (cali_handle[i]) {
test_adc_calibration_deinit(cali_handle[i]);
}
}
}
//ADC Channels
#if CONFIG_IDF_TARGET_ESP32
#define ADC1_SLEEP_TEST_CHAN ADC_CHANNEL_6
#define ADC2_SLEEP_TEST_CHAN ADC_CHANNEL_0
#else
#define ADC1_SLEEP_TEST_CHAN ADC_CHANNEL_2
#define ADC2_SLEEP_TEST_CHAN ADC_CHANNEL_0
#endif
TEST_CASE("test ADC1 Single Read with Light Sleep", "[adc][manul][ignore]")
{
s_adc_oneshot_with_sleep(ADC_UNIT_1, ADC1_SLEEP_TEST_CHAN);
}
TEST_CASE("test ADC2 Single Read with Light Sleep", "[adc][manul][ignore]")
{
s_adc_oneshot_with_sleep(ADC_UNIT_2, ADC2_SLEEP_TEST_CHAN);
}
#endif //#if SOC_ADC_CALIBRATION_V1_SUPPORTED

View File

@ -0,0 +1,128 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "unity.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gptimer.h"
#include "esp_rom_sys.h"
#include "esp_adc/adc_oneshot.h"
#include "test_common_adc.h"
const __attribute__((unused)) static char *TAG = "TEST_ADC";
/*---------------------------------------------------------------
ADC General Macros
---------------------------------------------------------------*/
//ADC Channels
#if CONFIG_IDF_TARGET_ESP32
#define ADC1_TEST_CHAN0 ADC_CHANNEL_4
#else
#define ADC1_TEST_CHAN0 ADC_CHANNEL_2
#endif
/*---------------------------------------------------------------
ADC work with ISR
---------------------------------------------------------------*/
typedef struct {
TaskHandle_t task_handle; //Task handle
adc_oneshot_unit_handle_t adc_handle; //ADC handle
bool level; //ADC level
} test_adc_isr_ctx_t;
static bool IRAM_ATTR s_alarm_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{
test_adc_isr_ctx_t *test_ctx = (test_adc_isr_ctx_t *)user_data;
BaseType_t high_task_wakeup;
int adc_raw = 0;
/**
* This test won't disable the cache, so having some code on Flash is OK.
* If you copy this test callback with cache disabled, do remeber to put all code in internal RAM.
*/
esp_rom_printf("alarm isr count=%llu\r\n", edata->count_value);
TEST_ESP_OK(adc_oneshot_read_isr(test_ctx->adc_handle, ADC1_TEST_CHAN0, &adc_raw));
esp_rom_printf("adc raw: %d\r\n", adc_raw);
if (test_ctx->level) {
TEST_ASSERT_INT_WITHIN(ADC_TEST_HIGH_THRESH, ADC_TEST_HIGH_VAL, adc_raw);
} else {
TEST_ASSERT_INT_WITHIN(ADC_TEST_LOW_THRESH, ADC_TEST_LOW_VAL, adc_raw);
}
// check the count value at alarm event
vTaskNotifyGiveFromISR(test_ctx->task_handle, &high_task_wakeup);
return high_task_wakeup == pdTRUE;
}
TEST_CASE("ADC oneshot fast work with ISR", "[adc_oneshot]")
{
static test_adc_isr_ctx_t isr_test_ctx = {};
isr_test_ctx.adc_handle = NULL;
isr_test_ctx.task_handle = xTaskGetCurrentTaskHandle();
//-------------ADC1 Init---------------//
adc_oneshot_unit_init_cfg_t init_config1 = {
.unit_id = ADC_UNIT_1,
.ulp_mode = false,
};
TEST_ESP_OK(adc_oneshot_new_unit(&init_config1, &isr_test_ctx.adc_handle));
//-------------ADC1 TEST Channel 0 Config---------------//
adc_oneshot_chan_cfg_t config = {
.bitwidth = ADC_BITWIDTH_DEFAULT,
.atten = ADC_ATTEN_DB_11,
};
TEST_ESP_OK(adc_oneshot_config_channel(isr_test_ctx.adc_handle, ADC1_TEST_CHAN0, &config));
//-------------GPTimer Init & Config---------------//
gptimer_handle_t timer = NULL;
gptimer_config_t timer_config = {
.resolution_hz = 1 * 1000 * 1000,
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
};
TEST_ESP_OK(gptimer_new_timer(&timer_config, &timer));
gptimer_event_callbacks_t cbs = {
.on_alarm = s_alarm_callback,
};
gptimer_alarm_config_t alarm_config = {
.reload_count = 0,
.alarm_count = 100000, // 100ms
};
TEST_ESP_OK(gptimer_set_alarm_action(timer, &alarm_config));
TEST_ESP_OK(gptimer_register_event_callbacks(timer, &cbs, &isr_test_ctx));
//ADC IO tile low
test_adc_set_io_level(ADC_UNIT_1, ADC1_TEST_CHAN0, 0);
isr_test_ctx.level = 0;
printf("start timer\r\n");
TEST_ESP_OK(gptimer_enable(timer));
TEST_ESP_OK(gptimer_start(timer));
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
TEST_ESP_OK(gptimer_stop(timer));
//ADC IO tile high
test_adc_set_io_level(ADC_UNIT_1, ADC1_TEST_CHAN0, 1);
isr_test_ctx.level = 1;
//Reset counter value to zero
TEST_ESP_OK(gptimer_set_raw_count(timer, 0));
printf("start timer\r\n");
TEST_ESP_OK(gptimer_start(timer));
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
//Tear Down
TEST_ESP_OK(gptimer_disable(timer));
TEST_ESP_OK(gptimer_del_timer(timer));
TEST_ESP_OK(adc_oneshot_del_unit(isr_test_ctx.adc_handle));
}

View File

@ -0,0 +1,266 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "unity.h"
#include "esp_log.h"
#include "esp_attr.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gptimer.h"
#include "esp_rom_sys.h"
#include "test_common_adc.h"
#include "esp_adc/adc_oneshot.h"
const __attribute__((unused)) static char *TAG = "TEST_ADC";
/*---------------------------------------------------------------
ADC General Macros
---------------------------------------------------------------*/
//ADC Channels
#if CONFIG_IDF_TARGET_ESP32
#define ADC1_TEST_CHAN0 ADC_CHANNEL_4
#else
#define ADC1_TEST_CHAN0 ADC_CHANNEL_2
#endif
typedef struct {
void *adc_handle; //ADC handle, could be oneshot / continuous handle
bool level; //ADC level
int adc_raw_high; //ADC reading raw when IO tie high
int cb_exe_times_high; //Callback running times when IO tie high and $ disabled
int adc_raw_low; //ADC reading raw when IO tie low
int cb_exe_times_low; //Callback running times when IO tie low and $ disabled
bool cache_disable_flag; //Indicating cache is disabled
} test_adc_iram_ctx_t;
extern void spi_flash_disable_interrupts_caches_and_other_cpu(void);
extern void spi_flash_enable_interrupts_caches_and_other_cpu(void);
__attribute__((unused))
static void s_test_cache_disable_period_us(test_adc_iram_ctx_t *ctx, uint32_t period_us);
#if CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM && CONFIG_GPTIMER_ISR_IRAM_SAFE
/*---------------------------------------------------------------
ADC oneshot work with cache safe ISR
---------------------------------------------------------------*/
static bool IRAM_ATTR NOINLINE_ATTR s_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{
test_adc_iram_ctx_t *test_ctx = (test_adc_iram_ctx_t *)user_data;
adc_oneshot_unit_handle_t oneshot_handle = *(adc_oneshot_unit_handle_t *)(test_ctx->adc_handle);
if (test_ctx->cache_disable_flag) {
if (test_ctx->level) {
adc_oneshot_read_isr(oneshot_handle, ADC1_TEST_CHAN0, &test_ctx->adc_raw_high);
esp_rom_printf(DRAM_STR("adc raw: %d\n"), test_ctx->adc_raw_high);
test_ctx->cb_exe_times_high++;
} else {
adc_oneshot_read_isr(oneshot_handle, ADC1_TEST_CHAN0, &test_ctx->adc_raw_low);
esp_rom_printf(DRAM_STR("adc raw: %d\n"), test_ctx->adc_raw_low);
test_ctx->cb_exe_times_low++;
}
}
return false;
}
TEST_CASE("ADC oneshot fast work with ISR and Flash", "[adc_oneshot]")
{
adc_oneshot_unit_handle_t oneshot_handle;
static DRAM_ATTR test_adc_iram_ctx_t isr_test_ctx = {};
isr_test_ctx.adc_handle = &oneshot_handle;
//-------------ADC1 Init---------------//
adc_oneshot_unit_init_cfg_t init_config1 = {
.unit_id = ADC_UNIT_1,
.ulp_mode = false,
};
TEST_ESP_OK(adc_oneshot_new_unit(&init_config1, &oneshot_handle));
//-------------ADC1 TEST Channel 0 Config---------------//
adc_oneshot_chan_cfg_t config = {
.bitwidth = ADC_BITWIDTH_DEFAULT,
.atten = ADC_ATTEN_DB_11,
};
TEST_ESP_OK(adc_oneshot_config_channel(oneshot_handle, ADC1_TEST_CHAN0, &config));
//-------------GPTimer Init & Config---------------//
gptimer_handle_t timer = NULL;
gptimer_config_t timer_config = {
.resolution_hz = 1 * 1000 * 1000,
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
};
TEST_ESP_OK(gptimer_new_timer(&timer_config, &timer));
gptimer_event_callbacks_t cbs = {
.on_alarm = s_alarm_cb,
};
gptimer_alarm_config_t alarm_config = {
.reload_count = 0,
.alarm_count = 100000, // 100ms
.flags.auto_reload_on_alarm = true,
};
TEST_ESP_OK(gptimer_set_alarm_action(timer, &alarm_config));
TEST_ESP_OK(gptimer_register_event_callbacks(timer, &cbs, &isr_test_ctx));
//ADC IO tile low
test_adc_set_io_level(ADC_UNIT_1, ADC1_TEST_CHAN0, 0);
isr_test_ctx.level = 0;
printf("start timer\r\n");
TEST_ESP_OK(gptimer_enable(timer));
TEST_ESP_OK(gptimer_start(timer));
s_test_cache_disable_period_us(&isr_test_ctx, 100 * 1000);
TEST_ESP_OK(gptimer_stop(timer));
//Checks
TEST_ASSERT_INT_WITHIN(ADC_TEST_LOW_THRESH, ADC_TEST_LOW_VAL, isr_test_ctx.adc_raw_low);
esp_rom_printf("callback runs %d times when $ disabled\n", isr_test_ctx.cb_exe_times_low);
TEST_ASSERT_GREATER_OR_EQUAL(1, isr_test_ctx.cb_exe_times_low);
//ADC IO tile high
test_adc_set_io_level(ADC_UNIT_1, ADC1_TEST_CHAN0, 1);
isr_test_ctx.level = 1;
//Reset counter value to zero
TEST_ESP_OK(gptimer_set_raw_count(timer, 0));
printf("start timer\r\n");
TEST_ESP_OK(gptimer_start(timer));
s_test_cache_disable_period_us(&isr_test_ctx, 100 * 1000);
TEST_ESP_OK(gptimer_stop(timer));
//Checks
TEST_ASSERT_INT_WITHIN(ADC_TEST_HIGH_THRESH, ADC_TEST_HIGH_VAL, isr_test_ctx.adc_raw_high);
esp_rom_printf("callback runs %d times when $ disabled\n", isr_test_ctx.cb_exe_times_high);
TEST_ASSERT_GREATER_OR_EQUAL(1, isr_test_ctx.cb_exe_times_high);
//Tear Down
TEST_ESP_OK(gptimer_disable(timer));
TEST_ESP_OK(gptimer_del_timer(timer));
TEST_ESP_OK(adc_oneshot_del_unit(oneshot_handle));
}
#endif //#if CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM && CONFIG_GPTIMER_ISR_IRAM_SAFE
#if CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE
#include "esp_adc/adc_continuous.h"
/*---------------------------------------------------------------
ADC continuous work with cache safe ISR
---------------------------------------------------------------*/
#if (SOC_ADC_DIGI_RESULT_BYTES == 2)
#define ADC_TEST_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE1
#else
#define ADC_TEST_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2
#endif
#define ADC_TEST_FREQ_HZ (50 * 1000)
#define ADC_TEST_PKG_SIZE 100
static bool IRAM_ATTR NOINLINE_ATTR s_conv_done_cb(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data)
{
test_adc_iram_ctx_t *test_ctx = (test_adc_iram_ctx_t *)user_data;
if (test_ctx->cache_disable_flag) {
int raw = 0;
for (int i = 0; i < edata->size; i += SOC_ADC_DIGI_RESULT_BYTES) {
adc_digi_output_data_t *p = (void*)&(edata->conv_frame_buffer[i]);
#if (SOC_ADC_DIGI_RESULT_BYTES == 2)
raw += p->type1.data;
#else
raw += p->type2.data;
#endif
}
if (test_ctx->level) {
test_ctx->adc_raw_high = raw / (edata->size / SOC_ADC_DIGI_RESULT_BYTES);
test_ctx->cb_exe_times_high++;
} else {
test_ctx->adc_raw_low = raw / (edata->size / SOC_ADC_DIGI_RESULT_BYTES);
test_ctx->cb_exe_times_low++;
}
}
return false;
}
TEST_CASE("ADC continuous work with ISR and Flash", "[adc_oneshot]")
{
adc_continuous_handle_t handle = NULL;
adc_continuous_handle_cfg_t adc_config = {
.max_store_buf_size = 1024,
.conv_frame_size = ADC_TEST_PKG_SIZE,
};
TEST_ESP_OK(adc_continuous_new_handle(&adc_config, &handle));
adc_continuous_config_t dig_cfg = {
.sample_freq_hz = ADC_TEST_FREQ_HZ,
.conv_mode = ADC_CONV_SINGLE_UNIT_1,
.format = ADC_TEST_OUTPUT_TYPE,
};
adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0};
adc_pattern[0].atten = ADC_ATTEN_DB_0;
adc_pattern[0].channel = ADC1_TEST_CHAN0;
adc_pattern[0].unit = ADC_UNIT_1;
adc_pattern[0].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH;
dig_cfg.adc_pattern = adc_pattern;
dig_cfg.pattern_num = 1;
TEST_ESP_OK(adc_continuous_config(handle, &dig_cfg));
static DRAM_ATTR test_adc_iram_ctx_t isr_test_ctx = {};
isr_test_ctx.adc_handle = &handle;
adc_continuous_evt_cbs_t cbs = {
.on_conv_done = s_conv_done_cb,
};
TEST_ESP_OK(adc_continuous_register_event_callbacks(handle, &cbs, &isr_test_ctx));
#if CONFIG_IDF_TARGET_ESP32
//This may need to be bigger, when the sampling freq is low
uint32_t overhead_us = 150;
#else
uint32_t overhead_us = 0;
#endif
uint32_t wait_time_us = (1000 * 1000 / ADC_TEST_FREQ_HZ * ADC_TEST_PKG_SIZE / SOC_ADC_DIGI_RESULT_BYTES) + overhead_us;
printf("period is %d us\n", wait_time_us);
//ADC IO tile low
test_adc_set_io_level(ADC_UNIT_1, ADC1_TEST_CHAN0, 0);
isr_test_ctx.level = 0;
TEST_ESP_OK(adc_continuous_start(handle));
s_test_cache_disable_period_us(&isr_test_ctx, wait_time_us);
TEST_ESP_OK(adc_continuous_stop(handle));
//Checks
TEST_ASSERT_INT_WITHIN(ADC_TEST_LOW_THRESH, ADC_TEST_LOW_VAL, isr_test_ctx.adc_raw_low);
esp_rom_printf("callback runs %d times when $ disabled\n", isr_test_ctx.cb_exe_times_low);
TEST_ASSERT_GREATER_OR_EQUAL(1, isr_test_ctx.cb_exe_times_low);
vTaskDelay(10);
printf("to set high\n");
//ADC IO tile high
test_adc_set_io_level(ADC_UNIT_1, ADC1_TEST_CHAN0, 1);
isr_test_ctx.level = 1;
TEST_ESP_OK(adc_continuous_start(handle));
s_test_cache_disable_period_us(&isr_test_ctx, wait_time_us);
TEST_ESP_OK(adc_continuous_stop(handle));
//Checks
// TEST_ASSERT_INT_WITHIN(ADC_TEST_HIGH_THRESH, ADC_TEST_HIGH_VAL_DMA, isr_test_ctx.adc_raw_high);
esp_rom_printf("callback runs %d times when $ disabled\n", isr_test_ctx.cb_exe_times_high);
TEST_ASSERT_GREATER_OR_EQUAL(1, isr_test_ctx.cb_exe_times_high);
TEST_ESP_OK(adc_continuous_deinit(handle));
}
#endif //#if CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE
static void IRAM_ATTR NOINLINE_ATTR s_test_cache_disable_period_us(test_adc_iram_ctx_t *ctx, uint32_t period_us)
{
spi_flash_disable_interrupts_caches_and_other_cpu();
ctx->cache_disable_flag = 1;
esp_rom_delay_us(period_us);
ctx->cache_disable_flag = 0;
spi_flash_enable_interrupts_caches_and_other_cpu();
}

View File

@ -0,0 +1,298 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "unity.h"
#include "test_utils.h"
#include "esp_log.h"
#include "esp_err.h"
#include "soc/adc_periph.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_adc/adc_oneshot.h"
#include "test_common_adc.h"
#if CONFIG_IDF_TARGET_ESP32 || SOC_ADC_CALIBRATION_V1_SUPPORTED
__attribute__((unused)) static const char *TAG = "TEST_ADC";
/*---------------------------------------------------------------
ADC Oneshot Average / STD_Deviation Test
---------------------------------------------------------------*/
#define TEST_COUNT (1<<SOC_ADC_RTC_MAX_BITWIDTH)
#define MAX_ARRAY_SIZE (1<<SOC_ADC_RTC_MAX_BITWIDTH)
static int s_adc_count[MAX_ARRAY_SIZE]={};
static int s_adc_offset = -1;
static int s_insert_point(uint32_t value)
{
const bool fixed_size = true;
if (s_adc_offset < 0) {
if (fixed_size) {
TEST_ASSERT_GREATER_OR_EQUAL(4096, MAX_ARRAY_SIZE);
s_adc_offset = 0; //Fixed to 0 because the array can hold all the data in 12 bits
} else {
s_adc_offset = MAX((int)value - MAX_ARRAY_SIZE/2, 0);
}
}
if (!fixed_size && (value < s_adc_offset || value >= s_adc_offset + MAX_ARRAY_SIZE)) {
TEST_ASSERT_GREATER_OR_EQUAL(s_adc_offset, value);
TEST_ASSERT_LESS_THAN(s_adc_offset + MAX_ARRAY_SIZE, value);
}
s_adc_count[value - s_adc_offset] ++;
return value - s_adc_offset;
}
static void s_reset_array(void)
{
memset(s_adc_count, 0, sizeof(s_adc_count));
s_adc_offset = -1;
}
static uint32_t s_get_average(void)
{
uint32_t sum = 0;
int count = 0;
for (int i = 0; i < MAX_ARRAY_SIZE; i++) {
sum += s_adc_count[i] * (s_adc_offset+i);
count += s_adc_count[i];
}
return sum/count;
}
static void s_print_summary(bool figure)
{
const int MAX_WIDTH=20;
int max_count = 0;
int start = -1;
int end = -1;
uint32_t sum = 0;
int count = 0;
for (int i = 0; i < MAX_ARRAY_SIZE; i++) {
if (s_adc_count[i] > max_count) {
max_count = s_adc_count[i];
}
if (s_adc_count[i] > 0 && start < 0) {
start = i;
}
if (s_adc_count[i] > 0) {
end = i;
}
count += s_adc_count[i];
sum += s_adc_count[i] * (s_adc_offset+i);
}
if (figure) {
for (int i = start; i <= end; i++) {
printf("%4d ", i+s_adc_offset);
int count = s_adc_count[i] * MAX_WIDTH / max_count;
for (int j = 0; j < count; j++) {
putchar('|');
}
printf(" %d\n", s_adc_count[i]);
}
}
float average = (float)sum/count;
float variation_square = 0;
for (int i = start; i <= end; i ++) {
if (s_adc_count[i] == 0) {
continue;
}
float delta = i + s_adc_offset - average;
variation_square += (delta * delta) * s_adc_count[i];
}
printf("%d points.\n", count);
printf("average: %.1f\n", (float)sum/count);
printf("std: %.2f\n", sqrt(variation_square/count));
}
#if CONFIG_IDF_TARGET_ESP32
#define TEST_STD_ADC1_CHANNEL0 ADC_CHANNEL_6
#else
#define TEST_STD_ADC1_CHANNEL0 ADC_CHANNEL_2
#endif
TEST_CASE("ADC1 oneshot raw average / std_deviation", "[adc_oneshot][ignore][manual]")
{
adc_atten_t atten[SOC_ADC_ATTEN_NUM] = {ADC_ATTEN_DB_0, ADC_ATTEN_DB_2_5, ADC_ATTEN_DB_6, ADC_ATTEN_DB_11};
adc_channel_t channel = TEST_STD_ADC1_CHANNEL0;
int raw = 0;
bool print_figure = false;
//-------------ADC1 Init---------------//
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_unit_init_cfg_t init_config1 = {
.unit_id = ADC_UNIT_1,
.ulp_mode = false,
};
TEST_ESP_OK(adc_oneshot_new_unit(&init_config1, &adc1_handle));
//-------------ADC Channel Config---------------//
adc_oneshot_chan_cfg_t config = {
.bitwidth = SOC_ADC_RTC_MAX_BITWIDTH,
};
//-------------ADC Calibration Init---------------//
bool do_calibration = false;
adc_cali_handle_t cali_handle[SOC_ADC_ATTEN_NUM] = {};
for (int i = 0; i < SOC_ADC_ATTEN_NUM; i++) {
do_calibration = test_adc_calibration_init(ADC_UNIT_1, i, ADC_BITWIDTH_DEFAULT, &cali_handle[i]);
}
if (!do_calibration) {
ESP_LOGW(TAG, "calibration fail, jump calibration\n");
}
for (int i = 0; i < SOC_ADC_ATTEN_NUM; i++) {
//-------------ADC1 Channel Config---------------//
config.atten = atten[i];
TEST_ESP_OK(adc_oneshot_config_channel(adc1_handle, channel, &config));
ESP_LOGI("TEST_ADC", "Test with atten: %d", atten[i]);
while (1) {
s_reset_array();
for (int i = 0; i < TEST_COUNT; i++) {
TEST_ESP_OK(adc_oneshot_read(adc1_handle, channel, &raw));
s_insert_point(raw);
}
s_print_summary(print_figure);
break;
}
if (do_calibration) {
uint32_t raw = s_get_average();
int voltage_mv = 0;
TEST_ESP_OK(adc_cali_raw_to_voltage(cali_handle[i], raw, &voltage_mv));
printf("Voltage = %d mV\n", voltage_mv);
}
}
TEST_ESP_OK(adc_oneshot_del_unit(adc1_handle));
for (int i = 0; i < SOC_ADC_ATTEN_NUM; i++) {
if (cali_handle[i]) {
test_adc_calibration_deinit(cali_handle[i]);
}
}
}
/*---------------------------------------------------------------
ADC Calibration Speed
---------------------------------------------------------------*/
#ifdef CONFIG_IDF_TARGET_ESP32
#define CPU_FREQ_MHZ CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ
#elif CONFIG_IDF_TARGET_ESP32S2
#define CPU_FREQ_MHZ CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ
#elif CONFIG_IDF_TARGET_ESP32S3
#define CPU_FREQ_MHZ CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ
#elif CONFIG_IDF_TARGET_ESP32C3
#define CPU_FREQ_MHZ CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ
#endif
#define RECORD_TIME_PREPARE() uint32_t __t1, __t2
#define RECORD_TIME_START() do {__t1 = esp_cpu_get_ccount();}while(0)
#define RECORD_TIME_END(p_time) do{__t2 = esp_cpu_get_ccount(); *p_time = (__t2-__t1);}while(0)
#define GET_US_BY_CCOUNT(t) ((double)t/CPU_FREQ_MHZ)
//ADC Channels
#if CONFIG_IDF_TARGET_ESP32
#define ADC1_CALI_SPEED_TEST_CHAN0 ADC_CHANNEL_6
#define ADC2_CALI_SPEED_TEST_CHAN0 ADC_CHANNEL_0
#else
#define ADC1_CALI_SPEED_TEST_CHAN0 ADC_CHANNEL_2
#define ADC2_CALI_SPEED_TEST_CHAN0 ADC_CHANNEL_0
#endif
#define TIMES_PER_ATTEN 10
static IRAM_ATTR NOINLINE_ATTR uint32_t get_cali_time_in_ccount(adc_cali_handle_t cali_handle, int adc_raw)
{
uint32_t time;
int voltage = 0;
RECORD_TIME_PREPARE();
RECORD_TIME_START();
adc_cali_raw_to_voltage(cali_handle, adc_raw, &voltage);
RECORD_TIME_END(&time);
return time;
}
static void s_adc_cali_speed(adc_unit_t unit_id, adc_channel_t channel)
{
//-------------ADC Calibration Init---------------//
bool do_calibration = false;
adc_cali_handle_t cali_handle[SOC_ADC_ATTEN_NUM] = {};
for (int i = 0; i < SOC_ADC_ATTEN_NUM; i++) {
do_calibration = test_adc_calibration_init(unit_id, i, SOC_ADC_RTC_MAX_BITWIDTH, &cali_handle[i]);
}
if (!do_calibration) {
ESP_LOGW(TAG, "no efuse burnt, jump test");
} else {
ESP_LOGI(TAG, "CPU FREQ is %dMHz", CPU_FREQ_MHZ);
adc_atten_t atten[SOC_ADC_ATTEN_NUM] = {ADC_ATTEN_DB_0, ADC_ATTEN_DB_2_5, ADC_ATTEN_DB_6, ADC_ATTEN_DB_11};
uint32_t adc_time_record[4][TIMES_PER_ATTEN] = {};
int adc_raw = 0;
//-------------ADC Init---------------//
adc_oneshot_unit_handle_t adc_handle;
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = unit_id,
.ulp_mode = false,
};
TEST_ESP_OK(adc_oneshot_new_unit(&init_config, &adc_handle));
//-------------ADC Channel Config---------------//
adc_oneshot_chan_cfg_t config = {
.bitwidth = SOC_ADC_RTC_MAX_BITWIDTH,
};
//atten0 ~ atten3
for (int i = 0; i < SOC_ADC_ATTEN_NUM; i++) {
//-------------ADC Channel Config---------------//
config.atten = atten[i];
TEST_ESP_OK(adc_oneshot_config_channel(adc_handle, channel, &config));
ESP_LOGI("TEST_ADC", "Test with atten: %d", atten[i]);
for (int j = 0; j < TIMES_PER_ATTEN; j++) {
TEST_ESP_OK(adc_oneshot_read(adc_handle, channel, &adc_raw));
adc_time_record[i][j] = get_cali_time_in_ccount(cali_handle[i], adc_raw);
IDF_LOG_PERFORMANCE("ADC1 Cali time", "%d us", (int)GET_US_BY_CCOUNT(adc_time_record[i][j]));
}
}
TEST_ESP_OK(adc_oneshot_del_unit(adc_handle));
for (int i = 0; i < SOC_ADC_ATTEN_NUM; i++) {
if (cali_handle[i]) {
test_adc_calibration_deinit(cali_handle[i]);
}
}
}
}
TEST_CASE("ADC1 Calibration Speed", "[adc][ignore][manual]")
{
s_adc_cali_speed(ADC_UNIT_1, ADC1_CALI_SPEED_TEST_CHAN0);
}
TEST_CASE("ADC2 Calibration Speed", "[adc][ignore][manual]")
{
s_adc_cali_speed(ADC_UNIT_2, ADC2_CALI_SPEED_TEST_CHAN0);
}
#endif //#if CONFIG_IDF_TARGET_ESP32 || SOC_ADC_CALIBRATION_V1_SUPPORTED

View File

@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
#define TEST_MEMORY_LEAK_THRESHOLD (-600)
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
unity_run_menu();
}

View File

@ -0,0 +1,94 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <stdlib.h>
#include "unity.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/rtc_io.h"
#include "soc/adc_periph.h"
#include "test_common_adc.h"
__attribute__((unused)) static const char *TAG = "TEST_ADC";
/*---------------------------------------------------------------
ADC Calibration
---------------------------------------------------------------*/
bool test_adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_bitwidth_t bitwidth, adc_cali_handle_t *out_handle)
{
esp_err_t ret = ESP_FAIL;
adc_cali_handle_t handle = NULL;
bool calibrated = false;
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = unit,
.atten = atten,
.bitwidth = bitwidth,
};
ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting");
adc_cali_line_fitting_config_t cali_config = {
.unit_id = unit,
.atten = atten,
.bitwidth = bitwidth,
};
ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
#endif
if (ret == ESP_OK) {
calibrated = true;
} else if (ret == ESP_ERR_NOT_SUPPORTED) {
ESP_LOGW(TAG, "calibration fail due to lack of eFuse bits");
} else {
TEST_ASSERT(false);
}
*out_handle = handle;
return calibrated;
}
void test_adc_calibration_deinit(adc_cali_handle_t handle)
{
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
ESP_LOGI(TAG, "deregister %s calibration scheme", "Curve Fitting");
TEST_ESP_OK(adc_cali_delete_scheme_curve_fitting(handle));
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
ESP_LOGI(TAG, "deregister %s calibration scheme", "Line Fitting");
TEST_ESP_OK(adc_cali_delete_scheme_line_fitting(handle));
#endif
}
/*---------------------------------------------------------------
ADC GPIO
---------------------------------------------------------------*/
#define ADC_GET_IO_NUM(unit, channel) (adc_channel_io_map[unit][channel])
void test_adc_set_io_level(adc_unit_t unit, adc_channel_t channel, bool level)
{
TEST_ASSERT(channel < SOC_ADC_CHANNEL_NUM(unit) && "invalid channel");
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
uint32_t io_num = ADC_GET_IO_NUM(unit, channel);
TEST_ESP_OK(gpio_set_pull_mode(io_num, (level ? GPIO_PULLUP_ONLY: GPIO_PULLDOWN_ONLY)));
#else
gpio_num_t io_num = ADC_GET_IO_NUM(unit, channel);
if (level) {
TEST_ESP_OK(rtc_gpio_pullup_en(io_num));
TEST_ESP_OK(rtc_gpio_pulldown_dis(io_num));
} else {
TEST_ESP_OK(rtc_gpio_pullup_dis(io_num));
TEST_ESP_OK(rtc_gpio_pulldown_en(io_num));
}
TEST_ESP_OK(gpio_set_pull_mode(io_num, (level ? GPIO_PULLUP_ONLY: GPIO_PULLDOWN_ONLY)));
#endif
}

View File

@ -0,0 +1,107 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "soc/soc_caps.h"
#include "esp_private/adc_private.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
/*---------------------------------------------------------------
ADC Calibration
---------------------------------------------------------------*/
/**
* @brief Initialise ADC Calibration
*
* @param[out] out_handle ADC calibration handle
*
* @return
* - True Calibration success
* - False Calibration fail
*/
bool test_adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_bitwidth_t bitwidth, adc_cali_handle_t *out_handle);
/**
* @brief De-initialise ADC Calibration
*
* @param[in] handle ADC calibration handle
*/
void test_adc_calibration_deinit(adc_cali_handle_t handle);
/*---------------------------------------------------------------
ADC GPIO
---------------------------------------------------------------*/
/**
* We use weak pulldown, `ADC_TEST_LOW_THRESH` may vary.
* If connect to GND, `ADC_TEST_LOW_THRESH` can be smaller
*/
#if CONFIG_IDF_TARGET_ESP32
#define ADC_TEST_LOW_VAL 0
#define ADC_TEST_LOW_THRESH 10
#define ADC_TEST_HIGH_VAL 4095
#define ADC_TEST_HIGH_VAL_DMA 4095
#define ADC_TEST_HIGH_THRESH 10
#elif CONFIG_IDF_TARGET_ESP32S2
#define ADC_TEST_LOW_VAL 0
#define ADC_TEST_LOW_THRESH 35
#define ADC_TEST_HIGH_VAL 8191
#define ADC_TEST_HIGH_VAL_DMA 4095
#define ADC_TEST_HIGH_THRESH 10
#elif CONFIG_IDF_TARGET_ESP32C3
#define ADC_TEST_LOW_VAL 0
#define ADC_TEST_LOW_THRESH 60
#define ADC_TEST_HIGH_VAL 4095
#define ADC_TEST_HIGH_VAL_DMA 4095
#define ADC_TEST_HIGH_THRESH 10
#elif CONFIG_IDF_TARGET_ESP32S3
#define ADC_TEST_LOW_VAL 0
#define ADC_TEST_LOW_THRESH 15
#define ADC_TEST_HIGH_VAL 4095
#define ADC_TEST_HIGH_VAL_DMA 4095
#define ADC_TEST_HIGH_THRESH 0
#elif CONFIG_IDF_TARGET_ESP32C2
#define ADC_TEST_LOW_VAL 2147
#define ADC_TEST_LOW_THRESH 50
#define ADC_TEST_HIGH_VAL 4095
#define ADC_TEST_HIGH_THRESH 0
#endif
/**
* @brief Set ADC IO level
*
* @param[in] unit ADC unit
* @param[in] channel ADC channel
* @param[in] level IO level. True: high; False: low
*/
void test_adc_set_io_level(adc_unit_t unit, adc_channel_t channel, bool level);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,21 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.esp32c3
@pytest.mark.esp32s3
@pytest.mark.esp32c2
@pytest.mark.generic
@pytest.mark.parametrize('config', [
'iram_safe',
'release',
], indirect=True)
def test_adc(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('*')
dut.expect_unity_test_output(timeout=120)

View File

@ -0,0 +1,6 @@
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM=y
CONFIG_GPTIMER_ISR_IRAM_SAFE=y
CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE=y
# silent the error check, as the error string are stored in rodata, causing RTL check failure
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y

View File

@ -0,0 +1,3 @@
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@ -0,0 +1,2 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n

View File

@ -1,12 +0,0 @@
idf_build_get_property(target IDF_TARGET)
set(srcs "esp_adc_cal_common.c")
set(src_target "${target}/esp_adc_cal.c")
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${src_target}")
list(APPEND srcs ${src_target})
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS include
REQUIRES driver
PRIV_REQUIRES efuse)

View File

@ -1,31 +0,0 @@
menu "ADC-Calibration"
config ADC_CAL_EFUSE_TP_ENABLE
depends on IDF_TARGET_ESP32
bool "Use Two Point Values"
default "y"
help
Some ESP32s have Two Point calibration values burned into eFuse BLOCK3.
This option will allow the ADC calibration component to characterize the
ADC-Voltage curve using Two Point values if they are available.
config ADC_CAL_EFUSE_VREF_ENABLE
depends on IDF_TARGET_ESP32
bool "Use eFuse Vref"
default "y"
help
Some ESP32s have Vref burned into eFuse BLOCK0. This option will allow
the ADC calibration component to characterize the ADC-Voltage curve using
eFuse Vref if it is available.
config ADC_CAL_LUT_ENABLE
depends on IDF_TARGET_ESP32
bool "Use Lookup Tables"
default "y"
help
This option will allow the ADC calibration component to use Lookup Tables
to correct for non-linear behavior in 11db attenuation. Other attenuations
do not exhibit non-linear behavior hence will not be affected by this option.
endmenu # ADC-Calibration

View File

@ -31,7 +31,7 @@ endif()
# [refactor-todo]: requires "driver" component for periph_ctrl header file
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include" "${idf_target}/include"
PRIV_REQUIRES nvs_flash driver efuse esp_timer
PRIV_REQUIRES nvs_flash driver efuse esp_timer esp_adc
LDFRAGMENTS "${ldfragments}"
EMBED_FILES ${embed_files}
)

View File

@ -5,8 +5,9 @@
*/
#include <stdbool.h>
#include "esp_attr.h"
#include "esp_private/regi2c_ctrl.h"
#include "driver/adc.h"
#include "esp_private/adc_private.h"
/*
* This file is used to override the hooks provided by the PHY lib for some system features.

View File

@ -30,7 +30,7 @@ endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include"
REQUIRES esp_event esp_phy
PRIV_REQUIRES driver esptool_py esp_netif esp_pm esp_timer nvs_flash
PRIV_REQUIRES driver esptool_py esp_netif esp_pm esp_timer nvs_flash esp_adc
wpa_supplicant hal lwip ${extra_priv_requires}
LDFRAGMENTS "${ldfragments}")

View File

@ -15,6 +15,7 @@
#include "esp_private/esp_clk.h"
#include "esp_wpa.h"
#include "esp_netif.h"
#include "esp_private/adc_private.h"
#include "esp_coexist_internal.h"
#include "esp_phy_init.h"
#include "phy.h"

View File

@ -44,7 +44,7 @@ if(NOT BOOTLOADER_BUILD)
"spi_flash_encrypt_hal_iram.c"
"sha_hal.c"
"adc_hal_common.c"
"adc_hal.c")
"adc_oneshot_hal.c")
if(CONFIG_SOC_SYSTIMER_SUPPORTED AND NOT CONFIG_HAL_SYSTIMER_USE_ROM_IMPL)
list(APPEND srcs "systimer_hal.c")
@ -82,6 +82,10 @@ if(NOT BOOTLOADER_BUILD)
list(APPEND srcs "emac_hal.c")
endif()
if(CONFIG_SOC_ADC_DMA_SUPPORTED)
list(APPEND srcs "adc_hal.c")
endif()
if(CONFIG_SOC_LCDCAM_SUPPORTED)
list(APPEND srcs "lcd_hal.c")
endif()
@ -96,7 +100,6 @@ if(NOT BOOTLOADER_BUILD)
"sdio_slave_hal.c"
"touch_sensor_hal.c"
"aes_hal.c"
"esp32/adc_hal.c"
"esp32/brownout_hal.c"
"esp32/touch_sensor_hal.c"
"esp32/gpio_hal_workaround.c")
@ -143,7 +146,6 @@ if(NOT BOOTLOADER_BUILD)
"spi_slave_hd_hal.c"
"xt_wdt_hal.c"
"aes_hal.c"
"esp32c3/adc_hal.c"
"esp32c3/brownout_hal.c"
"esp32c3/hmac_hal.c"
"esp32c3/rtc_cntl_hal.c")

View File

@ -25,16 +25,6 @@
#include "soc/spi_struct.h"
#endif
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
/*---------------------------------------------------------------
Single Read
---------------------------------------------------------------*/
/**
* For chips without RTC controller, Digital controller is used to trigger an ADC single read.
*/
#include "esp_rom_sys.h"
#endif //SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
/*---------------------------------------------------------------
Define all ADC DMA required operations here
---------------------------------------------------------------*/
@ -190,9 +180,12 @@ static void adc_hal_digi_sample_freq_config(adc_hal_dma_ctx_t *hal, uint32_t fre
adc_ll_digi_clk_sel(0); //use APB
#else
i2s_ll_rx_clk_set_src(hal->dev, I2S_CLK_SRC_DEFAULT); /*!< Clock from PLL_D2_CLK(160M)*/
uint32_t bck = I2S_BASE_CLK / (ADC_LL_CLKM_DIV_NUM_DEFAULT + ADC_LL_CLKM_DIV_B_DEFAULT / ADC_LL_CLKM_DIV_A_DEFAULT) / 2 / freq;
i2s_ll_set_raw_mclk_div(hal->dev, ADC_LL_CLKM_DIV_NUM_DEFAULT, ADC_LL_CLKM_DIV_A_DEFAULT, ADC_LL_CLKM_DIV_B_DEFAULT);
i2s_ll_rx_set_bck_div_num(hal->dev, bck);
uint32_t bclk_div = 16;
uint32_t bclk = freq * 2;
uint32_t mclk = bclk * bclk_div;
uint32_t mclk_div = I2S_BASE_CLK / mclk;
i2s_ll_rx_set_mclk(hal->dev, I2S_BASE_CLK, mclk, mclk_div);
i2s_ll_rx_set_bck_div_num(hal->dev, bclk_div);
#endif
}
@ -231,13 +224,8 @@ void adc_hal_digi_controller_config(adc_hal_dma_ctx_t *hal, const adc_hal_digi_c
#endif
if (cfg->conv_limit_en) {
adc_ll_digi_set_convert_limit_num(cfg->conv_limit_num);
adc_ll_digi_convert_limit_enable();
} else {
adc_ll_digi_convert_limit_disable();
}
adc_ll_digi_convert_limit_enable(ADC_LL_DEFAULT_CONV_LIMIT_EN);
adc_ll_digi_set_convert_limit_num(ADC_LL_DEFAULT_CONV_LIMIT_NUM);
adc_ll_digi_set_convert_mode(get_convert_mode(cfg->conv_mode));
//clock and sample frequency
@ -277,7 +265,7 @@ void adc_hal_digi_start(adc_hal_dma_ctx_t *hal, uint8_t *data_buf)
//reset the current descriptor address
hal->cur_desc_ptr = &hal->desc_dummy_head;
adc_hal_digi_dma_link_descriptors(hal->rx_desc, data_buf, hal->eof_num * ADC_HAL_DATA_LEN_PER_CONV, hal->desc_max_num);
adc_hal_digi_dma_link_descriptors(hal->rx_desc, data_buf, hal->eof_num * SOC_ADC_DIGI_DATA_BYTES_PER_CONV, hal->desc_max_num);
//start DMA
adc_dma_ll_rx_start(hal->dev, hal->dma_chan, (lldesc_t *)hal->rx_desc);
@ -334,67 +322,3 @@ void adc_hal_digi_stop(adc_hal_dma_ctx_t *hal)
//disconnect DMA and peripheral
adc_ll_digi_dma_disable();
}
/*---------------------------------------------------------------
Single Read
---------------------------------------------------------------*/
/**
* For chips without RTC controller, Digital controller is used to trigger an ADC single read.
*/
//--------------------Single Read-------------------------------//
static void adc_hal_onetime_start(adc_unit_t adc_n)
{
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
(void)adc_n;
/**
* There is a hardware limitation. If the APB clock frequency is high, the step of this reg signal: ``onetime_start`` may not be captured by the
* ADC digital controller (when its clock frequency is too slow). A rough estimate for this step should be at least 3 ADC digital controller
* clock cycle.
*
* This limitation will be removed in hardware future versions.
*
*/
uint32_t digi_clk = APB_CLK_FREQ / (ADC_LL_CLKM_DIV_NUM_DEFAULT + ADC_LL_CLKM_DIV_A_DEFAULT / ADC_LL_CLKM_DIV_B_DEFAULT + 1);
//Convert frequency to time (us). Since decimals are removed by this division operation. Add 1 here in case of the fact that delay is not enough.
uint32_t delay = (1000 * 1000) / digi_clk + 1;
//3 ADC digital controller clock cycle
delay = delay * 3;
//This coefficient (8) is got from test. When digi_clk is not smaller than ``APB_CLK_FREQ/8``, no delay is needed.
if (digi_clk >= APB_CLK_FREQ/8) {
delay = 0;
}
adc_oneshot_ll_start(false);
esp_rom_delay_us(delay);
adc_oneshot_ll_start(true);
//No need to delay here. Becuase if the start signal is not seen, there won't be a done intr.
#else
adc_oneshot_ll_start(adc_n);
#endif
}
esp_err_t adc_hal_convert(adc_unit_t adc_n, int channel, int *out_raw)
{
uint32_t event = (adc_n == ADC_UNIT_1) ? ADC_LL_EVENT_ADC1_ONESHOT_DONE : ADC_LL_EVENT_ADC2_ONESHOT_DONE;
adc_oneshot_ll_clear_event(event);
adc_oneshot_ll_disable_all_unit();
adc_oneshot_ll_enable(adc_n);
adc_oneshot_ll_set_channel(adc_n, channel);
adc_hal_onetime_start(adc_n);
while (adc_oneshot_ll_get_event(event) != true) {
;
}
*out_raw = adc_oneshot_ll_get_raw_result(adc_n);
if (adc_oneshot_ll_raw_check_valid(adc_n, *out_raw) == false) {
return ESP_ERR_INVALID_STATE;
}
//HW workaround: when enabling periph clock, this should be false
adc_oneshot_ll_disable_all_unit();
return ESP_OK;
}

Some files were not shown because too many files have changed in this diff Show More