Quellcode durchsuchen

Merge branch 'bugfix/fix_i2s_adc_mode' into 'master'

driver(i2s): fix broken i2s adc mode

See merge request idf/esp-idf!1653
Ivan Grokhotkov vor 8 Jahren
Ursprung
Commit
e381c6adde

+ 73 - 0
components/driver/adc1_i2s_private.h

@@ -0,0 +1,73 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef _DRIVER_ADC1_I2S_PRIVATE_H_
+#define _DRIVER_ADC1_I2S_PRIVATE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "esp_err.h"
+
+
+/**
+ * @brief Force power on for SAR ADC.
+ * This function should be called for the scenario in which ADC are controlled by digital function like DMA.
+ * When the ADC power is always on, RTC FSM can still be functional.
+ * This is an internal API for I2S module to call to enable I2S-ADC function.
+ * Note that adc_power_off() can still power down ADC.
+ */
+void adc_power_always_on();
+
+/**
+ * @brief For I2S dma to claim the usage of ADC1.
+ *
+ * Other tasks will be forbidden to use ADC1 between ``adc1_i2s_mode_acquire`` and ``adc1_i2s_release``.
+ * The I2S 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_err_t adc1_i2s_mode_acquire();
+
+/**
+ * @brief For ADC1 to claim the usage of ADC1.
+ *
+ * Other tasks will be forbidden to use ADC1 between ``adc1_adc_mode_acquire`` and ``adc1_i2s_release``.
+ * The ADC1 may have to wait for some time for the I2S read operation to finish.
+ *
+ * @return
+ *      - ESP_OK success
+ *      - ESP_ERR_TIMEOUT reserved for future use. Currently the function will wait until success.
+ */
+esp_err_t adc1_adc_mode_acquire();
+
+/**
+ * @brief to let other tasks use the ADC1 when I2S is not work.
+ *
+ * Other tasks will be forbidden to use ADC1 between ``adc1_adc/i2s_mode_acquire`` and ``adc1_i2s_release``.
+ * Call this function to release the occupation of ADC1
+ *
+ * @return always return ESP_OK.
+ */
+esp_err_t adc1_lock_release();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /*_DRIVER_ADC1_I2S_PRIVATE_H_*/
+

+ 47 - 15
components/driver/i2s.c

@@ -31,6 +31,7 @@
 #include "driver/i2s.h"
 #include "driver/rtc_io.h"
 #include "driver/dac.h"
+#include "adc1_i2s_private.h"
 
 #include "esp_intr.h"
 #include "esp_err.h"
@@ -83,12 +84,14 @@ typedef struct {
     int bits_per_sample;        /*!< Bits per sample*/
     i2s_mode_t mode;            /*!< I2S Working mode*/
     int use_apll;                /*!< I2S use APLL clock */
+    uint32_t sample_rate;              /*!< I2S sample rate */
 } i2s_obj_t;
 
 static i2s_obj_t *p_i2s_obj[I2S_NUM_MAX] = {0};
 static i2s_dev_t* I2S[I2S_NUM_MAX] = {&I2S0, &I2S1};
 static portMUX_TYPE i2s_spinlock[I2S_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED};
-
+static int _i2s_adc_unit = -1;
+static int _i2s_adc_channel = -1;
 /**
  * @brief Pre define APLL parameters, save compute time
  *        | bits_per_sample | rate | sdm0 | sdm1 | sdm2 | odir
@@ -321,12 +324,11 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t b
         return ESP_ERR_INVALID_ARG;
     }
 
-
     if (p_i2s_obj[i2s_num] == NULL) {
         ESP_LOGE(I2S_TAG, "Not initialized yet");
         return ESP_FAIL;
     }
-
+    p_i2s_obj[i2s_num]->sample_rate = rate;
     double clkmdiv = (double)I2S_BASE_CLK / (rate * factor);
 
     if (clkmdiv > 256) {
@@ -645,6 +647,18 @@ esp_err_t i2s_start(i2s_port_t i2s_num)
 {
     //start DMA link
     I2S_ENTER_CRITICAL();
+    i2s_reset_fifo(i2s_num);
+    //reset dma
+    I2S[i2s_num]->lc_conf.in_rst = 1;
+    I2S[i2s_num]->lc_conf.in_rst = 0;
+    I2S[i2s_num]->lc_conf.out_rst = 1;
+    I2S[i2s_num]->lc_conf.out_rst = 0;
+
+    I2S[i2s_num]->conf.tx_reset = 1;
+    I2S[i2s_num]->conf.tx_reset = 0;
+    I2S[i2s_num]->conf.rx_reset = 1;
+    I2S[i2s_num]->conf.rx_reset = 0;
+
     esp_intr_disable(p_i2s_obj[i2s_num]->i2s_isr_handle);
     I2S[i2s_num]->int_clr.val = 0xFFFFFFFF;
     if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) {
@@ -677,17 +691,6 @@ esp_err_t i2s_stop(i2s_port_t i2s_num)
         i2s_disable_rx_intr(i2s_num);
     }
     I2S[i2s_num]->int_clr.val = I2S[i2s_num]->int_st.val; //clear pending interrupt
-    i2s_reset_fifo(i2s_num);
-    //reset dma
-    I2S[i2s_num]->lc_conf.in_rst = 1;
-    I2S[i2s_num]->lc_conf.in_rst = 0;
-    I2S[i2s_num]->lc_conf.out_rst = 1;
-    I2S[i2s_num]->lc_conf.out_rst = 0;
-
-    I2S[i2s_num]->conf.tx_reset = 1;
-    I2S[i2s_num]->conf.tx_reset = 0;
-    I2S[i2s_num]->conf.rx_reset = 1;
-    I2S[i2s_num]->conf.rx_reset = 0;
     I2S_EXIT_CRITICAL();
     return 0;
 }
@@ -714,10 +717,18 @@ esp_err_t i2s_set_dac_mode(i2s_dac_mode_t dac_mode)
     return ESP_OK;
 }
 
+static esp_err_t _i2s_adc_mode_recover()
+{
+    I2S_CHECK(((_i2s_adc_unit != -1) && (_i2s_adc_channel != -1)), "i2s ADC recover error, not initialized...", ESP_ERR_INVALID_ARG);
+    return adc_i2s_mode_init(_i2s_adc_unit, _i2s_adc_channel);
+}
+
 esp_err_t i2s_set_adc_mode(adc_unit_t adc_unit, adc1_channel_t adc_channel)
 {
     I2S_CHECK((adc_unit < ADC_UNIT_2), "i2s ADC unit error, only support ADC1 for now", ESP_ERR_INVALID_ARG);
     // For now, we only support SAR ADC1.
+    _i2s_adc_unit = adc_unit;
+    _i2s_adc_channel = adc_channel;
     return adc_i2s_mode_init(adc_unit, adc_channel);
 }
 
@@ -856,7 +867,7 @@ static esp_err_t i2s_param_config(i2s_port_t i2s_num, const i2s_config_t *i2s_co
         //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_on();
+        adc_power_always_on();
     }
     // configure I2S data port interface.
     i2s_reset_fifo(i2s_num);
@@ -1144,6 +1155,27 @@ int i2s_write_bytes(i2s_port_t i2s_num, const char *src, size_t size, TickType_t
     return bytes_writen;
 }
 
+esp_err_t i2s_adc_enable(i2s_port_t i2s_num)
+{
+    I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG);
+    I2S_CHECK((p_i2s_obj[i2s_num] != NULL), "Not initialized yet", ESP_ERR_INVALID_STATE);
+    I2S_CHECK((p_i2s_obj[i2s_num]->mode & I2S_MODE_ADC_BUILT_IN), "i2s built-in adc not enabled", ESP_ERR_INVALID_STATE);
+
+    adc1_i2s_mode_acquire();
+    _i2s_adc_mode_recover();
+    return i2s_set_clk(i2s_num, p_i2s_obj[i2s_num]->sample_rate, p_i2s_obj[i2s_num]->bits_per_sample, p_i2s_obj[i2s_num]->channel_num);
+}
+
+esp_err_t i2s_adc_disable(i2s_port_t i2s_num)
+{
+    I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG);
+    I2S_CHECK((p_i2s_obj[i2s_num] != NULL), "Not initialized yet", ESP_ERR_INVALID_STATE);
+    I2S_CHECK((p_i2s_obj[i2s_num]->mode & I2S_MODE_ADC_BUILT_IN), "i2s built-in adc not enabled", ESP_ERR_INVALID_STATE);
+
+    adc1_lock_release();
+    return ESP_OK;
+}
+
 int i2s_read_bytes(i2s_port_t i2s_num, char* dest, size_t size, TickType_t ticks_to_wait)
 {
     char *data_ptr;

+ 2 - 1
components/driver/include/driver/adc.h

@@ -204,12 +204,13 @@ int adc1_get_voltage(adc1_channel_t channel) __attribute__((deprecated));
 /** @endcond */
 
 /**
- * @brief Power on SAR ADC
+ * @brief Enable ADC power
  */
 void adc_power_on();
 
 /**
  * @brief Power off SAR ADC
+ * This function will force power down for ADC
  */
 void adc_power_off();
 

+ 27 - 0
components/driver/include/driver/i2s.h

@@ -293,6 +293,8 @@ int i2s_write_bytes(i2s_port_t i2s_num, const char *src, size_t size, TickType_t
  *
  * Format of the data in source buffer is determined by the I2S
  * configuration (see i2s_config_t).
+ * @note If the built-in ADC mode is enabled, we should call i2s_adc_start and i2s_adc_stop around the whole reading process,
+ *       to prevent the data getting corrupted.
  *
  * @return  Number of bytes read, or ESP_FAIL (-1) for parameter error. If a timeout occurred, bytes read will be less than total size.
  */
@@ -417,6 +419,31 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t b
  */
 esp_err_t i2s_set_adc_mode(adc_unit_t adc_unit, adc1_channel_t adc_channel);
 
+/**
+ * @brief Start to use I2S built-in ADC mode
+ * @note This function would acquire the lock of ADC to prevent the data getting corrupted
+ *       during the I2S peripheral is being used to do fully continuous ADC sampling.
+ *
+ * @param i2s_num i2s port index
+ * @return
+ *     - ESP_OK Success
+ *     - ESP_ERR_INVALID_ARG Parameter error
+ *     - ESP_ERR_INVALID_STATE driver state error
+ *     - ESP_FAIL Internal driver error
+ */
+esp_err_t i2s_adc_enable(i2s_port_t i2s_num);
+
+/**
+ * @brief Stop to use I2S built-in ADC mode
+ * @param i2s_num i2s port index
+ * @note This function would release the lock of ADC so that other tasks can use ADC.
+ * @return
+ *     - ESP_OK Success
+ *     - ESP_ERR_INVALID_ARG Parameter error
+ *     - ESP_ERR_INVALID_STATE driver state error
+ */
+esp_err_t i2s_adc_disable(i2s_port_t i2s_num);
+
 #ifdef __cplusplus
 }
 #endif

+ 66 - 13
components/driver/rtc_module.c

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

+ 1 - 0
components/soc/esp32/include/soc/sens_reg.h

@@ -96,6 +96,7 @@
 #define SENS_FORCE_XPD_SAR_M  ((SENS_FORCE_XPD_SAR_V)<<(SENS_FORCE_XPD_SAR_S))
 #define SENS_FORCE_XPD_SAR_V  0x3
 #define SENS_FORCE_XPD_SAR_S  18
+#define SENS_FORCE_XPD_SAR_SW_M (BIT1)
 #define SENS_FORCE_XPD_SAR_FSM 0 // Use FSM to control power down
 #define SENS_FORCE_XPD_SAR_PD  2 // Force power down
 #define SENS_FORCE_XPD_SAR_PU  3 // Force power up

+ 26 - 3
examples/peripherals/i2s_adc_dac/main/app_main.c

@@ -9,8 +9,11 @@
 #include "driver/i2s.h"
 #include "driver/adc.h"
 #include "audio_example_file.h"
+#include "esp_adc_cal.h"
 
 static const char* TAG = "ad/da";
+#define V_REF   1100
+#define ADC1_TEST_CHANNEL (ADC1_CHANNEL_7)
 
 #define PARTITION_NAME   "storage"
 
@@ -36,6 +39,10 @@ static const char* TAG = "ad/da";
 #define EXAMPLE_I2S_FORMAT        (I2S_CHANNEL_FMT_RIGHT_LEFT)
 //I2S channel number
 #define EXAMPLE_I2S_CHANNEL_NUM   ((EXAMPLE_I2S_FORMAT < I2S_CHANNEL_FMT_ONLY_RIGHT) ? (2) : (1))
+//I2S built-in ADC unit
+#define I2S_ADC_UNIT              ADC_UNIT_1
+//I2S built-in ADC channel
+#define I2S_ADC_CHANNEL           ADC1_CHANNEL_0
 
 //flash record size, for recording 5 seconds' data
 #define FLASH_RECORD_SIZE         (EXAMPLE_I2S_CHANNEL_NUM * EXAMPLE_I2S_SAMPLE_RATE * EXAMPLE_I2S_SAMPLE_BITS / 8 * 5)
@@ -45,6 +52,7 @@ static const char* TAG = "ad/da";
 //flash read / write address
 #define FLASH_ADDR                (0x200000)
 
+
 /**
  * @brief I2S ADC/DAC mode init.
  */
@@ -66,7 +74,7 @@ void example_i2s_init()
 	 //init DAC pad
 	 i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
 	 //init ADC pad
-	 i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_0);
+	 i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL);
 }
 
 /*
@@ -193,10 +201,8 @@ void example_i2s_adc_dac(void*arg)
         ESP_LOGE(TAG, "Partition error: can't find partition name: %s\n", PARTITION_NAME);
         vTaskDelete(NULL);
     }
-
     //1. Erase flash
     example_erase_flash();
-    example_i2s_init();
     int i2s_read_len = EXAMPLE_I2S_READ_LEN;
     int flash_wr_size = 0;
 
@@ -204,6 +210,7 @@ void example_i2s_adc_dac(void*arg)
 #if RECORD_IN_FLASH_EN
     char* i2s_read_buff = (char*) calloc(i2s_read_len, sizeof(char));
     uint8_t* flash_write_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char));
+    i2s_adc_enable(EXAMPLE_I2S_NUM);
     while (flash_wr_size < FLASH_RECORD_SIZE) {
         //read data from I2S bus, in this case, from ADC.
         i2s_read_bytes(EXAMPLE_I2S_NUM, (char*) i2s_read_buff, i2s_read_len, portMAX_DELAY);
@@ -213,6 +220,7 @@ void example_i2s_adc_dac(void*arg)
         flash_wr_size += i2s_read_len;
         ets_printf("Sound recording %u%%\n", flash_wr_size * 100 / FLASH_RECORD_SIZE);
     }
+    i2s_adc_disable(EXAMPLE_I2S_NUM);
     free(i2s_read_buff);
     i2s_read_buff = NULL;
     free(flash_write_buff);
@@ -256,10 +264,25 @@ void example_i2s_adc_dac(void*arg)
     vTaskDelete(NULL);
 }
 
+void adc_read_task(void* arg)
+{
+    adc1_config_width(ADC_WIDTH_12Bit);
+    adc1_config_channel_atten(ADC1_TEST_CHANNEL, ADC_ATTEN_11db);
+    esp_adc_cal_characteristics_t characteristics;
+    esp_adc_cal_get_characteristics(V_REF, ADC_ATTEN_11db, ADC_WIDTH_12Bit, &characteristics);
+    while(1) {
+        uint32_t voltage = adc1_to_voltage(ADC1_TEST_CHANNEL, &characteristics);
+        ESP_LOGI(TAG, "%d mV", voltage);
+        vTaskDelay(200 / portTICK_RATE_MS);
+    }
+}
+
 esp_err_t app_main()
 {
+    example_i2s_init();
     esp_log_level_set("I2S", ESP_LOG_INFO);
     xTaskCreate(example_i2s_adc_dac, "example_i2s_adc_dac", 1024 * 2, NULL, 5, NULL);
+    xTaskCreate(adc_read_task, "ADC read task", 2048, NULL, 5, NULL);
     return ESP_OK;
 }