Skip to content

Commit

Permalink
Merge branch 'bugfix/adc_i2s_v3.0' into 'release/v3.0'
Browse files Browse the repository at this point in the history
driver(i2s): fix broken i2s adc mode

See merge request idf/esp-idf!2405
  • Loading branch information
projectgus committed May 16, 2018
2 parents 94c4f32 + 945d2e6 commit 561f8ff
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 32 deletions.
73 changes: 73 additions & 0 deletions 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_*/

62 changes: 47 additions & 15 deletions components/driver/i2s.c
Expand Up @@ -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"
Expand Down Expand Up @@ -84,12 +85,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
Expand Down Expand Up @@ -322,12 +325,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_ERR_INVALID_ARG;
}

p_i2s_obj[i2s_num]->sample_rate = rate;
double clkmdiv = (double)I2S_BASE_CLK / (rate * factor);

if (clkmdiv > 256) {
Expand Down Expand Up @@ -652,6 +654,18 @@ esp_err_t i2s_start(i2s_port_t i2s_num)
I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG);
//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) {
Expand Down Expand Up @@ -685,17 +699,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 ESP_OK;
}
Expand All @@ -722,10 +725,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);
}

Expand Down Expand Up @@ -864,7 +875,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);
Expand Down Expand Up @@ -1171,6 +1182,27 @@ esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *by
return ESP_OK;
}

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;
}

esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, size_t src_bits, size_t aim_bits, size_t *bytes_written, TickType_t ticks_to_wait)
{
char *data_ptr;
Expand Down
3 changes: 2 additions & 1 deletion components/driver/include/driver/adc.h
Expand Up @@ -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();

Expand Down
27 changes: 27 additions & 0 deletions components/driver/include/driver/i2s.h
Expand Up @@ -353,6 +353,8 @@ int i2s_read_bytes(i2s_port_t i2s_num, void *dest, size_t size, TickType_t ticks
*
* @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.
* @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
* - ESP_OK Success
Expand Down Expand Up @@ -485,6 +487,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
Expand Down

0 comments on commit 561f8ff

Please sign in to comment.