esp_adc: support selecting clock source for oneshot driver

This commit is contained in:
Armando 2023-02-02 16:50:53 +08:00
parent d0e4d36fb6
commit 5653018cd1
17 changed files with 67 additions and 43 deletions

View File

@ -28,6 +28,7 @@
#include "hal/adc_hal_conf.h"
#include "esp_private/periph_ctrl.h"
#include "driver/adc_types_legacy.h"
#include "clk_tree.h"
#if SOC_DAC_SUPPORTED
#include "hal/dac_types.h"
@ -280,8 +281,8 @@ esp_err_t adc1_config_channel_atten(adc1_channel_t channel, adc_atten_t atten)
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
if (!clk_src_freq_hz) {
esp_err_t ret = clk_tree_src_get_freq_hz(ADC_DIGI_CLK_SRC_DEFAULT, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz);
assert(ret == ESP_OK);
//should never fail
clk_tree_src_get_freq_hz(ADC_DIGI_CLK_SRC_DEFAULT, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz);
}
#endif //#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
@ -439,8 +440,8 @@ esp_err_t adc2_config_channel_atten(adc2_channel_t channel, adc_atten_t atten)
ESP_RETURN_ON_FALSE(atten <= SOC_ADC_ATTEN_NUM, ESP_ERR_INVALID_ARG, ADC_TAG, "ADC2 Atten Err");
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
if (!clk_src_freq_hz) {
esp_err_t ret = clk_tree_src_get_freq_hz(ADC_DIGI_CLK_SRC_DEFAULT, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz);
assert(ret == ESP_OK);
//should never fail
clk_tree_src_get_freq_hz(ADC_DIGI_CLK_SRC_DEFAULT, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz);
}
#endif //#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
@ -750,6 +751,12 @@ 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");
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
if (!clk_src_freq_hz) {
//should never fail
clk_tree_src_get_freq_hz(ADC_DIGI_CLK_SRC_DEFAULT, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz);
}
#endif //#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
esp_err_t ret = ESP_OK;
s_atten1_single[channel] = atten;
@ -814,6 +821,12 @@ esp_err_t adc2_get_raw(adc2_channel_t channel, adc_bits_width_t width_bit, int *
if (width_bit != ADC_WIDTH_BIT_12) {
return ESP_ERR_INVALID_ARG;
}
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
if (!clk_src_freq_hz) {
//should never fail
clk_tree_src_get_freq_hz(ADC_DIGI_CLK_SRC_DEFAULT, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz);
}
#endif //#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
esp_err_t ret = ESP_OK;

View File

@ -22,6 +22,7 @@
#include "esp_private/adc_private.h"
#include "esp_private/adc_share_hw_ctrl.h"
#include "esp_private/sar_periph_ctrl.h"
#include "clk_tree.h"
#include "driver/gpio.h"
#include "esp_adc/adc_continuous.h"
#include "hal/adc_types.h"
@ -533,10 +534,15 @@ esp_err_t adc_continuous_config(adc_continuous_handle_t handle, const adc_contin
ESP_RETURN_ON_FALSE(config->format == ADC_DIGI_OUTPUT_FORMAT_TYPE2, ESP_ERR_INVALID_ARG, ADC_TAG, "Please use type2");
#endif
uint32_t clk_src_freq_hz = 0;
clk_tree_src_get_freq_hz(ADC_DIGI_CLK_SRC_DEFAULT, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz);
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));
handle->hal_digi_ctrlr_cfg.clk_src = ADC_DIGI_CLK_SRC_DEFAULT;
handle->hal_digi_ctrlr_cfg.clk_src_freq_hz = clk_src_freq_hz;
const int atten_uninitialized = 999;
handle->adc1_atten = atten_uninitialized;

View File

@ -15,6 +15,7 @@
#include "driver/gpio.h"
#include "driver/rtc_io.h"
#include "esp_adc/adc_oneshot.h"
#include "clk_tree.h"
#include "esp_private/adc_private.h"
#include "esp_private/adc_share_hw_ctrl.h"
#include "esp_private/sar_periph_ctrl.h"
@ -97,9 +98,18 @@ esp_err_t adc_oneshot_new_unit(const adc_oneshot_unit_init_cfg_t *init_config, a
unit->unit_id = init_config->unit_id;
unit->ulp_mode = init_config->ulp_mode;
adc_oneshot_clk_src_t clk_src = ADC_DIGI_CLK_SRC_DEFAULT;
if (init_config->clk_src) {
clk_src = init_config->clk_src;
}
uint32_t clk_src_freq_hz = 0;
ESP_GOTO_ON_ERROR(clk_tree_src_get_freq_hz(clk_src, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz), err, TAG, "clock source not supported");
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,
.clk_src = clk_src,
.clk_src_freq_hz = clk_src_freq_hz,
};
adc_oneshot_hal_init(&(unit->hal), &config);

View File

@ -27,6 +27,7 @@ typedef struct adc_oneshot_unit_ctx_t *adc_oneshot_unit_handle_t;
*/
typedef struct {
adc_unit_t unit_id; ///< ADC unit
adc_oneshot_clk_src_t clk_src; ///< Clock source
adc_ulp_mode_t ulp_mode; ///< ADC controlled by ULP, see `adc_ulp_mode_t`
} adc_oneshot_unit_init_cfg_t;
@ -51,6 +52,7 @@ typedef struct {
* - 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_FAIL: Clock source isn't initialised correctly
*/
esp_err_t adc_oneshot_new_unit(const adc_oneshot_unit_init_cfg_t *init_config, adc_oneshot_unit_handle_t *ret_unit);

View File

@ -25,11 +25,14 @@ if(NOT BOOTLOADER_BUILD)
"sleep_gpio.c"
"sleep_mac_bb.c"
"regi2c_ctrl.c"
"adc_share_hw_ctrl.c"
"port/${target}/io_mux.c"
"port/${target}/clk_tree.c"
"port/clk_tree_common.c")
if(CONFIG_SOC_ADC_SUPPORTED)
list(APPEND srcs "adc_share_hw_ctrl.c")
endif()
if(CONFIG_SOC_PM_SUPPORT_CPU_PD)
list(APPEND srcs "sleep_cpu.c")
endif()

View File

@ -166,19 +166,19 @@ static adc_ll_digi_convert_mode_t get_convert_mode(adc_digi_convert_mode_t conve
* - Enable clock and select clock source for ADC digital controller.
* For esp32, use I2S clock
*/
static void adc_hal_digi_sample_freq_config(adc_hal_dma_ctx_t *hal, uint32_t freq)
static void adc_hal_digi_sample_freq_config(adc_hal_dma_ctx_t *hal, adc_continuous_clk_src_t clk_src, uint32_t clk_src_freq_hz, uint32_t sample_freq_hz)
{
#if !CONFIG_IDF_TARGET_ESP32
uint32_t interval = APB_CLK_FREQ / (ADC_LL_CLKM_DIV_NUM_DEFAULT + ADC_LL_CLKM_DIV_A_DEFAULT / ADC_LL_CLKM_DIV_B_DEFAULT + 1) / 2 / freq;
uint32_t interval = clk_src_freq_hz / (ADC_LL_CLKM_DIV_NUM_DEFAULT + ADC_LL_CLKM_DIV_A_DEFAULT / ADC_LL_CLKM_DIV_B_DEFAULT + 1) / 2 / sample_freq_hz;
//set sample interval
adc_ll_digi_set_trigger_interval(interval);
//Here we set the clock divider factor to make the digital clock to 5M Hz
adc_ll_digi_controller_clk_div(ADC_LL_CLKM_DIV_NUM_DEFAULT, ADC_LL_CLKM_DIV_B_DEFAULT, ADC_LL_CLKM_DIV_A_DEFAULT);
adc_ll_digi_clk_sel(ADC_DIGI_CLK_SRC_DEFAULT); // use default clock source for ADC digital controller
adc_ll_digi_clk_sel(clk_src);
#else
i2s_ll_rx_clk_set_src(hal->dev, I2S_CLK_SRC_DEFAULT); /*!< Clock from PLL_D2_CLK(160M)*/
uint32_t bclk_div = 16;
uint32_t bclk = freq * 2;
uint32_t bclk = sample_freq_hz * 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);
@ -226,7 +226,7 @@ void adc_hal_digi_controller_config(adc_hal_dma_ctx_t *hal, const adc_hal_digi_c
adc_ll_digi_set_convert_mode(get_convert_mode(cfg->conv_mode));
//clock and sample frequency
adc_hal_digi_sample_freq_config(hal, cfg->sample_freq_hz);
adc_hal_digi_sample_freq_config(hal, cfg->clk_src, cfg->clk_src_freq_hz, cfg->sample_freq_hz);
}
static void adc_hal_digi_dma_link_descriptors(dma_descriptor_t *desc, uint8_t *data_buf, uint32_t size, uint32_t num)

View File

@ -12,6 +12,7 @@
#include "hal/adc_hal_conf.h"
#include "hal/adc_ll.h"
#include "hal/assert.h"
#include "hal/log.h"
#if SOC_DAC_SUPPORTED
#include "hal/dac_ll.h"
@ -35,6 +36,8 @@ void adc_oneshot_hal_init(adc_oneshot_hal_ctx_t *hal, const adc_oneshot_hal_cfg_
{
hal->unit = config->unit;
hal->work_mode = config->work_mode;
hal->clk_src = config->clk_src;
hal->clk_src_freq_hz = config->clk_src_freq_hz;
}
void adc_oneshot_hal_channel_config(adc_oneshot_hal_ctx_t *hal, const adc_oneshot_hal_chan_cfg_t *config, adc_channel_t chan)
@ -57,7 +60,7 @@ void adc_oneshot_hal_setup(adc_oneshot_hal_ctx_t *hal, adc_channel_t chan)
#endif
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
adc_ll_digi_clk_sel(ADC_DIGI_CLK_SRC_DEFAULT);
adc_ll_digi_clk_sel(hal->clk_src);
#else
adc_ll_set_sar_clk_div(unit, ADC_HAL_SAR_CLK_DIV_DEFAULT(unit));
if (unit == ADC_UNIT_2) {
@ -77,25 +80,29 @@ void adc_oneshot_hal_setup(adc_oneshot_hal_ctx_t *hal, adc_channel_t chan)
#endif //#if SOC_ADC_ARBITER_SUPPORTED
}
static void adc_hal_onetime_start(adc_unit_t unit)
static void adc_hal_onetime_start(adc_unit_t unit, uint32_t clk_src_freq_hz)
{
#if SOC_ADC_DIG_CTRL_SUPPORTED && !SOC_ADC_RTC_CTRL_SUPPORTED
(void)unit;
uint32_t delay = 0;
/**
* 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.
*/
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);
uint32_t digi_clk = clk_src_freq_hz / (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;
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.
HAL_EARLY_LOGD("adc_hal", "clk_src_freq_hz: %d, digi_clk: %d, delay: %d", clk_src_freq_hz, digi_clk, delay);
//This coefficient (8) is got from test, and verified from DT. When digi_clk is not smaller than ``APB_CLK_FREQ/8``, no delay is needed.
if (digi_clk >= APB_CLK_FREQ/8) {
delay = 0;
}
HAL_EARLY_LOGD("adc_hal", "delay: %d", delay);
adc_oneshot_ll_start(false);
esp_rom_delay_us(delay);
adc_oneshot_ll_start(true);
@ -120,7 +127,7 @@ bool adc_oneshot_hal_convert(adc_oneshot_hal_ctx_t *hal, int *out_raw)
adc_oneshot_ll_disable_all_unit();
adc_oneshot_ll_enable(hal->unit);
adc_hal_onetime_start(hal->unit);
adc_hal_onetime_start(hal->unit, hal->clk_src_freq_hz);
while (!adc_oneshot_ll_get_event(event)) {
;
}

View File

@ -28,7 +28,7 @@
extern "C" {
#endif
#define ADC_LL_CLKM_DIV_NUM_DEFAULT 15
#define ADC_LL_CLKM_DIV_NUM_DEFAULT 19
#define ADC_LL_CLKM_DIV_B_DEFAULT 1
#define ADC_LL_CLKM_DIV_A_DEFAULT 0
#define ADC_LL_DEFAULT_CONV_LIMIT_EN 0

View File

@ -84,6 +84,8 @@ typedef struct adc_hal_digi_ctrlr_cfg_t {
uint32_t sample_freq_hz; //ADC sample frequency
adc_digi_convert_mode_t conv_mode; //controller work mode
uint32_t bit_width; //output data width
adc_continuous_clk_src_t clk_src; ///< Clock source
uint32_t clk_src_freq_hz; ///< Clock source frequency in hz
} adc_hal_digi_ctrlr_cfg_t;

View File

@ -20,6 +20,8 @@ typedef struct sens_dev_t *adc_oneshot_soc_handle_t;
typedef struct adc_oneshot_hal_cfg_t {
adc_unit_t unit; ///< ADC unit
adc_hal_work_mode_t work_mode; ///< ADC work mode
adc_oneshot_clk_src_t clk_src; ///< Clock source
uint32_t clk_src_freq_hz; ///< Clock source frequency in hz
} adc_oneshot_hal_cfg_t;
/**
@ -27,20 +29,20 @@ typedef struct adc_oneshot_hal_cfg_t {
*/
typedef struct adc_oneshot_hal_chan_cfg_t {
adc_atten_t atten; ///< ADC attenuation
adc_bitwidth_t bitwidth; ///< ADC conversion result bits
adc_bitwidth_t bitwidth; ///< ADC conversion result bits
} adc_oneshot_hal_chan_cfg_t;
/**
* Context of the ADC unit, should be maintained by both the driver and the HAL.
*/
typedef struct adc_oneshot_hal_ctx_t {
/* This should be configured by driver in initialisation, dou't touch again */
/* These should be configured by driver, dou't modify these directly in hal*/
adc_oneshot_soc_handle_t dev; ///< ADC SoC layer handle
/* These should be malloced by the driver first */
adc_unit_t unit; ///< ADC unit
adc_hal_work_mode_t work_mode; ///< ADC work mode
adc_oneshot_hal_chan_cfg_t chan_configs[SOC_ADC_MAX_CHANNEL_NUM]; ///< ADC configurations per channel
adc_oneshot_clk_src_t clk_src; ///< Clock source
uint32_t clk_src_freq_hz; ///< Clock source frequency in hz
} adc_oneshot_hal_ctx_t;
/**

View File

@ -167,14 +167,6 @@ config SOC_ADC_SELF_HW_CALI_SUPPORTED
bool
default y
config SOC_ADC_DIG_CLK_XTAL_SUPPORTED
bool
default y
config SOC_ADC_DIG_CLK_PLL_F80M_SUPPORTED
bool
default y
config SOC_BROWNOUT_RESET_SUPPORTED
bool
default y

View File

@ -247,10 +247,6 @@ config SOC_ADC_SELF_HW_CALI_SUPPORTED
bool
default y
config SOC_ADC_DIG_CLK_APB_SUPPORTED
bool
default y
config SOC_APB_BACKUP_DMA
bool
default y

View File

@ -76,7 +76,6 @@
#define SOC_AES_SUPPORT_AES_128 (1)
#define SOC_AES_SUPPORT_AES_256 (1)
// TODO: IDF-6215 (Copy from esp32c6, need check)
/*-------------------------- ADC CAPS -------------------------------*/
/*!< SAR ADC Module*/
#define SOC_ADC_DIG_CTRL_SUPPORTED 1

View File

@ -263,10 +263,6 @@ config SOC_ADC_SELF_HW_CALI_SUPPORTED
bool
default y
config SOC_ADC_RTC_CLK_RC_FAST_SUPPORTED
bool
default y
config SOC_BROWNOUT_RESET_SUPPORTED
bool
default y

View File

@ -299,10 +299,6 @@ config SOC_ADC_SELF_HW_CALI_SUPPORTED
bool
default y
config SOC_ADC_RTC_CLK_RC_FAST_SUPPORTED
bool
default y
config SOC_APB_BACKUP_DMA
bool
default y

View File

@ -111,7 +111,6 @@
#define SOC_ADC_CALIBRATION_V1_SUPPORTED (1) /*!< support HW offset calibration version 1*/
#define SOC_ADC_SELF_HW_CALI_SUPPORTED (1) /*!< support HW offset self calibration */
/*-------------------------- APB BACKUP DMA CAPS -------------------------------*/
#define SOC_APB_BACKUP_DMA (1)

View File

@ -42,6 +42,7 @@ The ADC oneshot mode driver is implemented based on {IDF_TARGET_NAME} SAR ADC mo
To install an ADC instance, set up the required initial configuration structure :cpp:type:`adc_oneshot_unit_init_cfg_t`:
- :cpp:member:`adc_oneshot_unit_init_cfg_t::unit_id` selects the ADC. Please refer to the `datasheet <{IDF_TARGET_TRM_EN_URL}>`__ to know dedicated analog IOs for this ADC.
- :cpp:member:`adc_oneshot_unit_init_cfg_t::clk_src` selects the source clock of the ADC. If it's set to 0, driver will fallback to use a default clock source, see :cpp:type:`adc_oneshot_clk_src_t` to know the details.
- :cpp:member:`adc_oneshot_unit_init_cfg_t::ulp_mode` sets if the ADC will be working under ULP mode.
.. todo::