adc_i2s: solve the i2s_adc issue when using wifi

This commit is contained in:
Cao Sen Miao
2020-12-03 13:58:24 +08:00
committed by bot
parent 34408026bb
commit 4e6e34e3ad
3 changed files with 89 additions and 18 deletions

View File

@@ -74,6 +74,14 @@ In ADC2, there're two locks used for different cases:
adc2_spinlock should be acquired first, then adc2_wifi_lock or rtc_spinlock. adc2_spinlock should be acquired first, then adc2_wifi_lock or rtc_spinlock.
*/ */
// 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);
static void adc_power_off_internal(void);
#ifdef CONFIG_IDF_TARGET_ESP32 #ifdef CONFIG_IDF_TARGET_ESP32
//prevent ADC2 being used by wifi and other tasks at the same time. //prevent ADC2 being used by wifi and other tasks at the same time.
static _lock_t adc2_wifi_lock; static _lock_t adc2_wifi_lock;
@@ -122,36 +130,60 @@ static uint32_t get_calibration_offset(adc_ll_num_t adc_n, adc_channel_t chan)
} }
#endif #endif
void adc_power_always_on(void) void adc_power_acquire(void)
{ {
bool powered_on = false;
ADC_ENTER_CRITICAL(); ADC_ENTER_CRITICAL();
adc_hal_set_power_manage(ADC_POWER_SW_ON); s_adc_power_on_cnt++;
if (s_adc_power_on_cnt == 1) {
adc_power_on_internal();
powered_on = true;
}
ADC_EXIT_CRITICAL(); ADC_EXIT_CRITICAL();
if (powered_on) {
ESP_LOGV(ADC_TAG, "%s: ADC powered on", __func__);
}
} }
void adc_power_on(void) void adc_power_release(void)
{
bool powered_off = false;
ADC_ENTER_CRITICAL();
s_adc_power_on_cnt--;
/* Sanity check */
if (s_adc_power_on_cnt < 0) {
ADC_EXIT_CRITICAL();
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();
powered_off = true;
}
ADC_EXIT_CRITICAL();
if (powered_off) {
ESP_LOGV(ADC_TAG, "%s: ADC powered off", __func__);
}
}
static void adc_power_on_internal(void)
{ {
ADC_ENTER_CRITICAL(); ADC_ENTER_CRITICAL();
/* The power FSM controlled mode saves more power, while the ADC noise may get increased. */
#ifndef CONFIG_ADC_FORCE_XPD_FSM
/* Set the power always on to increase precision. */ /* Set the power always on to increase precision. */
adc_hal_set_power_manage(ADC_POWER_SW_ON); adc_hal_set_power_manage(ADC_POWER_SW_ON);
#else
/* Use the FSM to turn off the power while not used to save power. */
if (adc_hal_get_power_manage() != ADC_POWER_BY_FSM) {
adc_hal_set_power_manage(ADC_POWER_SW_ON);
}
#endif
ADC_EXIT_CRITICAL(); ADC_EXIT_CRITICAL();
} }
void adc_power_off(void) void adc_power_on(void) __attribute__((alias("adc_power_on_internal")));
static void adc_power_off_internal(void)
{ {
ADC_ENTER_CRITICAL(); ADC_ENTER_CRITICAL();
adc_hal_set_power_manage(ADC_POWER_SW_OFF); adc_hal_set_power_manage(ADC_POWER_SW_OFF);
ADC_EXIT_CRITICAL(); ADC_EXIT_CRITICAL();
} }
void adc_power_off(void) __attribute__((alias("adc_power_off_internal")));
esp_err_t adc_set_clk_div(uint8_t clk_div) esp_err_t adc_set_clk_div(uint8_t clk_div)
{ {
ADC_ENTER_CRITICAL(); ADC_ENTER_CRITICAL();
@@ -337,7 +369,7 @@ int adc1_get_raw(adc1_channel_t channel)
int adc_value; int adc_value;
ADC_CHANNEL_CHECK(ADC_NUM_1, channel); ADC_CHANNEL_CHECK(ADC_NUM_1, channel);
adc1_rtc_mode_acquire(); adc1_rtc_mode_acquire();
adc_power_on(); adc_power_acquire();
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
// Get calibration value before going into critical section // Get calibration value before going into critical section
@@ -359,6 +391,7 @@ int adc1_get_raw(adc1_channel_t channel)
#endif #endif
ADC_EXIT_CRITICAL(); ADC_EXIT_CRITICAL();
adc_power_release();
adc1_lock_release(); adc1_lock_release();
return adc_value; return adc_value;
} }
@@ -371,7 +404,7 @@ int adc1_get_voltage(adc1_channel_t channel) //Deprecated. Use adc1_get_raw()
#if SOC_ULP_SUPPORTED #if SOC_ULP_SUPPORTED
void adc1_ulp_enable(void) void adc1_ulp_enable(void)
{ {
adc_power_on(); adc_power_acquire();
ADC_ENTER_CRITICAL(); ADC_ENTER_CRITICAL();
adc_hal_set_controller(ADC_NUM_1, ADC_CTRL_ULP); adc_hal_set_controller(ADC_NUM_1, ADC_CTRL_ULP);
@@ -492,7 +525,7 @@ esp_err_t adc2_get_raw(adc2_channel_t channel, adc_bits_width_t width_bit, int *
ADC_CHECK(width_bit == ADC_WIDTH_BIT_13, "WIDTH ERR: ESP32S2 support 13 bit width", ESP_ERR_INVALID_ARG); ADC_CHECK(width_bit == ADC_WIDTH_BIT_13, "WIDTH ERR: ESP32S2 support 13 bit width", ESP_ERR_INVALID_ARG);
#endif #endif
adc_power_on(); //in critical section with whole rtc module adc_power_acquire(); //in critical section with whole rtc module
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
// Get calibration value before going into critical section // Get calibration value before going into critical section
@@ -503,6 +536,7 @@ esp_err_t adc2_get_raw(adc2_channel_t channel, adc_bits_width_t width_bit, int *
if ( ADC2_WIFI_LOCK_TRY_ACQUIRE() == -1 ) { //try the lock, return if failed (wifi using). if ( ADC2_WIFI_LOCK_TRY_ACQUIRE() == -1 ) { //try the lock, return if failed (wifi using).
ADC2_EXIT_CRITICAL(); ADC2_EXIT_CRITICAL();
adc_power_release();
return ESP_ERR_TIMEOUT; return ESP_ERR_TIMEOUT;
} }
#ifdef CONFIG_ADC_DISABLE_DAC #ifdef CONFIG_ADC_DISABLE_DAC
@@ -544,8 +578,12 @@ esp_err_t adc2_get_raw(adc2_channel_t channel, adc_bits_width_t width_bit, int *
if (adc_value < 0) { if (adc_value < 0) {
ESP_LOGD( ADC_TAG, "ADC2 ARB: Return data is invalid." ); ESP_LOGD( ADC_TAG, "ADC2 ARB: Return data is invalid." );
adc_power_release();
return ESP_ERR_INVALID_STATE; return ESP_ERR_INVALID_STATE;
} }
//in critical section with whole rtc module
adc_power_release();
*raw_out = adc_value; *raw_out = adc_value;
return ESP_OK; return ESP_OK;
} }
@@ -558,7 +596,9 @@ esp_err_t adc2_vref_to_gpio(gpio_num_t gpio)
esp_err_t adc_vref_to_gpio(adc_unit_t adc_unit, gpio_num_t gpio) esp_err_t adc_vref_to_gpio(adc_unit_t adc_unit, gpio_num_t gpio)
{ {
#ifdef CONFIG_IDF_TARGET_ESP32 #ifdef CONFIG_IDF_TARGET_ESP32
adc_power_acquire();
if (adc_unit & ADC_UNIT_1) { if (adc_unit & ADC_UNIT_1) {
adc_power_release();
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
} }
#endif #endif
@@ -571,6 +611,7 @@ esp_err_t adc_vref_to_gpio(adc_unit_t adc_unit, gpio_num_t gpio)
} }
} }
if (ch == ADC2_CHANNEL_MAX) { if (ch == ADC2_CHANNEL_MAX) {
adc_power_release();
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
} }

View File

@@ -867,6 +867,13 @@ static esp_err_t i2s_param_config(i2s_port_t i2s_num, const i2s_config_t *i2s_co
periph_module_enable(i2s_periph_signal[i2s_num].module); periph_module_enable(i2s_periph_signal[i2s_num].module);
#if SOC_I2S_SUPPORTS_ADC_DAC #if SOC_I2S_SUPPORTS_ADC_DAC
if(i2s_config->mode & I2S_MODE_ADC_BUILT_IN) {
//in ADC built-in mode, we need to call i2s_set_adc_mode to
//initialize the specific ADC channel.
//in the current stage, we only support ADC1 and single channel mode.
//In default data mode, the ADC data is in 12-bit resolution mode.
adc_power_acquire();
}
#endif #endif
// configure I2S data port interface. // configure I2S data port interface.
i2s_hal_config_param(&(p_i2s_obj[i2s_num]->hal), i2s_config); i2s_hal_config_param(&(p_i2s_obj[i2s_num]->hal), i2s_config);

View File

@@ -151,14 +151,32 @@ typedef struct adc_digi_init_config_s {
/** /**
* @brief Enable ADC power * @brief Enable ADC power
* @deprecated Use adc_power_acquire and adc_power_release instead.
*/ */
void adc_power_on(void); void adc_power_on(void) __attribute__((deprecated));
/** /**
* @brief Power off SAR ADC * @brief Power off SAR ADC
* This function will force power down for 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); 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);
/** /**
* @brief Initialize ADC pad * @brief Initialize ADC pad
@@ -250,6 +268,8 @@ esp_err_t adc1_config_width(adc_bits_width_t width_bit);
* the input of GPIO36 and GPIO39 will be pulled down for about 80ns. * 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. * 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. * Please refer to section 3.11 of 'ECO_and_Workarounds_for_Bugs_in_ESP32' for the description of this issue.
* As a workaround, call adc_power_acquire() in the app. This will result in higher power consumption (by ~1mA),
* but will remove the glitches on GPIO36 and GPIO39.
* *
* @note Call ``adc1_config_width()`` before the first time this * @note Call ``adc1_config_width()`` before the first time this
* function is called. * function is called.
@@ -375,6 +395,9 @@ esp_err_t adc2_config_channel_atten(adc2_channel_t channel, adc_atten_t atten);
* the input of GPIO36 and GPIO39 will be pulled down for about 80ns. * 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. * 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. * Please refer to section 3.11 of 'ECO_and_Workarounds_for_Bugs_in_ESP32' for the description of this issue.
* As a workaround, call adc_power_acquire() in the app. This will result in higher power consumption (by ~1mA),
* but will remove the glitches on GPIO36 and GPIO39.
*
* *
* @note ESP32: * @note ESP32:
* For a given channel, ``adc2_config_channel_atten()`` * For a given channel, ``adc2_config_channel_atten()``