driver(i2s): fix broken i2s adc mode

1. Move i2s reset code from i2s_stop to i2s_start.
2. add RTC API to set sw mode for ADC
3. add description for adc_power_always_on()
4. add lock for i2s dma and RTC ADC functions.
5. add ADC read task in example

reported from bbs:  https://esp32.com/viewtopic.php?f=13&t=3490&p=17522#p17522
reported from github: https://github.com/espressif/esp-idf/issues/1333
This commit is contained in:
Wangjialin
2017-12-08 20:07:19 +08:00
parent 475ffe78e8
commit 451f69cc2e
7 changed files with 242 additions and 32 deletions

View File

@@ -34,6 +34,7 @@
#include "sys/lock.h"
#include "driver/rtc_cntl.h"
#include "driver/gpio.h"
#include "adc1_i2s_private.h"
#ifndef NDEBUG
// Enable built-in checks in queue.h in debug builds
@@ -99,6 +100,9 @@ static _lock_t adc2_wifi_lock = NULL;
//prevent ADC2 being used by tasks (regardless of WIFI)
portMUX_TYPE adc2_spinlock = portMUX_INITIALIZER_UNLOCKED;
//prevent ADC1 being used by I2S dma and other tasks at the same time.
static _lock_t adc1_i2s_lock = NULL;
typedef struct {
TimerHandle_t timer;
uint32_t filtered_val[TOUCH_PAD_MAX];
@@ -108,12 +112,6 @@ typedef struct {
} touch_pad_filter_t;
static touch_pad_filter_t *s_touch_pad_filter = NULL;
typedef enum {
ADC_FORCE_FSM = 0x0,
ADC_FORCE_DISABLE = 0x2,
ADC_FORCE_ENABLE = 0x3,
} adc_force_mode_t;
//Reg,Mux,Fun,IE,Up,Down,Rtc_number
const rtc_gpio_desc_t rtc_gpio_desc[GPIO_PIN_COUNT] = {
{RTC_IO_TOUCH_PAD1_REG, RTC_IO_TOUCH_PAD1_MUX_SEL_M, RTC_IO_TOUCH_PAD1_FUN_SEL_S, RTC_IO_TOUCH_PAD1_FUN_IE_M, RTC_IO_TOUCH_PAD1_RUE_M, RTC_IO_TOUCH_PAD1_RDE_M, RTC_IO_TOUCH_PAD1_SLP_SEL_M, RTC_IO_TOUCH_PAD1_SLP_IE_M, RTC_IO_TOUCH_PAD1_HOLD_M, RTC_CNTL_TOUCH_PAD1_HOLD_FORCE_M, RTC_IO_TOUCH_PAD1_DRV_V, RTC_IO_TOUCH_PAD1_DRV_S, RTCIO_GPIO0_CHANNEL}, //0
@@ -1025,10 +1023,21 @@ static esp_err_t adc_set_atten(adc_unit_t adc_unit, adc_channel_t channel, adc_a
return ESP_OK;
}
void adc_power_always_on()
{
portENTER_CRITICAL(&rtc_spinlock);
SENS.sar_meas_wait2.force_xpd_sar = SENS_FORCE_XPD_SAR_PU;
portEXIT_CRITICAL(&rtc_spinlock);
}
void adc_power_on()
{
portENTER_CRITICAL(&rtc_spinlock);
SENS.sar_meas_wait2.force_xpd_sar = ADC_FORCE_FSM;
if (SENS.sar_meas_wait2.force_xpd_sar & SENS_FORCE_XPD_SAR_SW_M) {
SENS.sar_meas_wait2.force_xpd_sar = SENS_FORCE_XPD_SAR_PU;
} else {
SENS.sar_meas_wait2.force_xpd_sar = SENS_FORCE_XPD_SAR_FSM;
}
portEXIT_CRITICAL(&rtc_spinlock);
}
@@ -1037,7 +1046,7 @@ void adc_power_off()
portENTER_CRITICAL(&rtc_spinlock);
//Bit1 0:Fsm 1: SW mode
//Bit0 0:SW mode power down 1: SW mode power on
SENS.sar_meas_wait2.force_xpd_sar = ADC_FORCE_DISABLE;
SENS.sar_meas_wait2.force_xpd_sar = SENS_FORCE_XPD_SAR_PD;
portEXIT_CRITICAL(&rtc_spinlock);
}
@@ -1161,7 +1170,7 @@ esp_err_t adc_i2s_mode_init(adc_unit_t adc_unit, adc_channel_t channel)
uint8_t table_len = 1;
//POWER ON SAR
adc_power_on();
adc_power_always_on();
adc_gpio_init(adc_unit, channel);
adc_set_i2s_data_len(adc_unit, table_len);
adc_set_i2s_data_pattern(adc_unit, 0, channel, ADC_WIDTH_BIT_12, ADC_ATTEN_DB_11);
@@ -1246,12 +1255,55 @@ esp_err_t adc1_config_width(adc_bits_width_t width_bit)
return ESP_OK;
}
esp_err_t adc1_i2s_mode_acquire()
{
//lazy initialization
//for i2s, block until acquire the lock
_lock_acquire( &adc1_i2s_lock );
ESP_LOGD( RTC_MODULE_TAG, "i2s mode takes adc1 lock." );
portENTER_CRITICAL(&rtc_spinlock);
SENS.sar_meas_wait2.force_xpd_sar = SENS_FORCE_XPD_SAR_PU;
//switch SARADC into DIG channel
SENS.sar_read_ctrl.sar1_dig_force = 1;
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
esp_err_t adc1_adc_mode_acquire()
{
//lazy initialization
//for adc1, block until acquire the lock
_lock_acquire( &adc1_i2s_lock );
ESP_LOGD( RTC_MODULE_TAG, "adc mode takes adc1 lock." );
portENTER_CRITICAL(&rtc_spinlock);
// for now the WiFi would use ADC2 and set xpd_sar force on.
// so we can not reset xpd_sar to fsm mode directly.
// We should handle this after the synchronization mechanism is established.
//switch SARADC into RTC channel
SENS.sar_read_ctrl.sar1_dig_force = 0;
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
esp_err_t adc1_lock_release()
{
RTC_MODULE_CHECK((uint32_t*)adc1_i2s_lock != NULL, "adc1 lock release called before acquire", ESP_ERR_INVALID_STATE );
// for now the WiFi would use ADC2 and set xpd_sar force on.
// so we can not reset xpd_sar to fsm mode directly.
// We should handle this after the synchronization mechanism is established.
_lock_release( &adc1_i2s_lock );
ESP_LOGD( RTC_MODULE_TAG, "returns adc1 lock." );
return ESP_OK;
}
int adc1_get_raw(adc1_channel_t channel)
{
uint16_t adc_value;
RTC_MODULE_CHECK(channel < ADC1_CHANNEL_MAX, "ADC Channel Err", ESP_ERR_INVALID_ARG);
adc_power_on();
adc1_adc_mode_acquire();
adc_power_on();
portENTER_CRITICAL(&rtc_spinlock);
//Adc Controler is Rtc module,not ulp coprocessor
@@ -1274,6 +1326,7 @@ int adc1_get_raw(adc1_channel_t channel)
while (SENS.sar_meas_start1.meas1_done_sar == 0);
adc_value = SENS.sar_meas_start1.meas1_data_sar;
portEXIT_CRITICAL(&rtc_spinlock);
adc1_lock_release();
return adc_value;
}
@@ -1467,6 +1520,8 @@ esp_err_t adc2_vref_to_gpio(gpio_num_t gpio)
rtc_gpio_input_disable(gpio);
rtc_gpio_pullup_dis(gpio);
rtc_gpio_pulldown_dis(gpio);
//force fsm
adc_power_always_on(); //Select power source of ADC
RTCCNTL.bias_conf.dbg_atten = 0; //Check DBG effect outside sleep mode
//set dtest (MUX_SEL : 0 -> RTC; 1-> vdd_sar2)
@@ -1475,8 +1530,6 @@ esp_err_t adc2_vref_to_gpio(gpio_num_t gpio)
RTCCNTL.test_mux.ent_rtc = 1;
//set sar2_en_test
SENS.sar_start_force.sar2_en_test = 1;
//force fsm
SENS.sar_meas_wait2.force_xpd_sar = ADC_FORCE_ENABLE; //Select power source of ADC
//set sar2 en force
SENS.sar_meas_start2.sar2_en_pad_force = 1; //Pad bitmap controlled by SW
//set en_pad for channels 7,8,9 (bits 0x380)