2022-07-20 23:47:09 -04:00
|
|
|
/*
|
2024-01-04 23:01:28 -05:00
|
|
|
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
|
2022-07-20 23:47:09 -04:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ADC is shared by multiple components, including:
|
|
|
|
* - esp_phy
|
|
|
|
* - esp_wifi
|
|
|
|
* - driver
|
|
|
|
*
|
|
|
|
* However, usages of above components are different.
|
|
|
|
* Therefore, we put the common used parts into `esp_hw_support`, including:
|
2024-03-28 03:19:45 -04:00
|
|
|
* - adc power maintenance
|
2022-07-20 23:47:09 -04:00
|
|
|
* - adc hw calibration settings
|
|
|
|
* - adc locks, to prevent concurrently using adc hw
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <esp_types.h>
|
|
|
|
#include "sdkconfig.h"
|
|
|
|
#include "sys/lock.h"
|
|
|
|
#include "esp_log.h"
|
|
|
|
#include "esp_check.h"
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
|
|
#include "hal/adc_types.h"
|
|
|
|
#include "hal/adc_hal_common.h"
|
2023-07-28 00:06:14 -04:00
|
|
|
#include "hal/adc_ll.h"
|
2022-07-20 23:47:09 -04:00
|
|
|
#include "esp_private/adc_share_hw_ctrl.h"
|
2023-01-09 04:10:04 -05:00
|
|
|
#include "esp_private/sar_periph_ctrl.h"
|
2024-01-04 23:01:28 -05:00
|
|
|
#include "esp_private/periph_ctrl.h"
|
|
|
|
#include "soc/periph_defs.h"
|
2022-07-20 23:47:09 -04:00
|
|
|
//For calibration
|
|
|
|
#if CONFIG_IDF_TARGET_ESP32S2
|
|
|
|
#include "esp_efuse_rtc_table.h"
|
|
|
|
#elif SOC_ADC_CALIBRATION_V1_SUPPORTED
|
|
|
|
#include "esp_efuse_rtc_calib.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
static const char *TAG = "adc_share_hw_ctrl";
|
|
|
|
extern portMUX_TYPE rtc_spinlock;
|
|
|
|
|
|
|
|
|
|
|
|
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
|
|
|
|
/*---------------------------------------------------------------
|
|
|
|
ADC Hardware Calibration
|
|
|
|
---------------------------------------------------------------*/
|
|
|
|
#if CONFIG_IDF_TARGET_ESP32S2
|
|
|
|
#define esp_efuse_rtc_calib_get_ver() esp_efuse_rtc_table_read_calib_version()
|
|
|
|
|
|
|
|
static inline uint32_t esp_efuse_rtc_calib_get_init_code(int version, uint32_t adc_unit, int atten)
|
|
|
|
{
|
2022-09-05 07:25:23 -04:00
|
|
|
int tag = esp_efuse_rtc_table_get_tag(version, adc_unit, atten, RTCCALIB_V2_PARAM_VINIT);
|
2022-07-20 23:47:09 -04:00
|
|
|
return esp_efuse_rtc_table_get_parsed_efuse_value(tag, false);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static uint32_t s_adc_cali_param[SOC_ADC_PERIPH_NUM][SOC_ADC_ATTEN_NUM] = {};
|
|
|
|
|
|
|
|
void adc_calc_hw_calibration_code(adc_unit_t adc_n, adc_atten_t atten)
|
|
|
|
{
|
|
|
|
if (s_adc_cali_param[adc_n][atten]) {
|
2024-03-07 10:43:45 -05:00
|
|
|
ESP_EARLY_LOGV(TAG, "Use calibrated val ADC%d atten=%d: %04" PRIX32, adc_n + 1, atten, s_adc_cali_param[adc_n][atten]);
|
2022-07-20 23:47:09 -04:00
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if we can fetch the values from eFuse.
|
|
|
|
int version = esp_efuse_rtc_calib_get_ver();
|
|
|
|
|
|
|
|
uint32_t init_code = 0;
|
|
|
|
|
2023-06-21 01:31:16 -04:00
|
|
|
if ((version >= ESP_EFUSE_ADC_CALIB_VER_MIN) &&
|
|
|
|
(version <= ESP_EFUSE_ADC_CALIB_VER_MAX)) {
|
2023-06-25 23:52:25 -04:00
|
|
|
// Guarantee the calibration version before calling efuse function
|
2022-07-20 23:47:09 -04:00
|
|
|
init_code = esp_efuse_rtc_calib_get_init_code(version, adc_n, atten);
|
2023-02-02 01:46:44 -05:00
|
|
|
}
|
|
|
|
#if SOC_ADC_SELF_HW_CALI_SUPPORTED
|
|
|
|
else {
|
2022-07-20 23:47:09 -04:00
|
|
|
ESP_EARLY_LOGD(TAG, "Calibration eFuse is not configured, use self-calibration for ICode");
|
2023-01-09 04:10:04 -05:00
|
|
|
sar_periph_ctrl_adc_oneshot_power_acquire();
|
2022-07-20 23:47:09 -04:00
|
|
|
portENTER_CRITICAL(&rtc_spinlock);
|
2023-02-13 02:53:31 -05:00
|
|
|
adc_ll_pwdet_set_cct(ADC_LL_PWDET_CCT_DEFAULT);
|
2022-07-20 23:47:09 -04:00
|
|
|
const bool internal_gnd = true;
|
|
|
|
init_code = adc_hal_self_calibration(adc_n, atten, internal_gnd);
|
|
|
|
portEXIT_CRITICAL(&rtc_spinlock);
|
2023-01-09 04:10:04 -05:00
|
|
|
sar_periph_ctrl_adc_oneshot_power_release();
|
2022-07-20 23:47:09 -04:00
|
|
|
}
|
2023-02-02 01:46:44 -05:00
|
|
|
#else
|
|
|
|
else {
|
|
|
|
ESP_EARLY_LOGD(TAG, "ICode self-calibration isn't supported");
|
|
|
|
}
|
|
|
|
#endif //SOC_ADC_SELF_HW_CALI_SUPPORTED
|
2022-07-20 23:47:09 -04:00
|
|
|
|
|
|
|
s_adc_cali_param[adc_n][atten] = init_code;
|
2024-03-07 10:43:45 -05:00
|
|
|
ESP_EARLY_LOGV(TAG, "Calib(V%d) ADC%d atten=%d: %04" PRIX32, version, adc_n + 1, atten, init_code);
|
2022-07-20 23:47:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void IRAM_ATTR adc_set_hw_calibration_code(adc_unit_t adc_n, adc_atten_t atten)
|
|
|
|
{
|
|
|
|
adc_hal_set_calibration_param(adc_n, s_adc_cali_param[adc_n][atten]);
|
|
|
|
}
|
2023-05-05 07:56:44 -04:00
|
|
|
|
|
|
|
#if SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
|
|
|
|
static int s_adc_cali_chan_compens[SOC_ADC_MAX_CHANNEL_NUM][SOC_ADC_ATTEN_NUM] = {};
|
|
|
|
void adc_load_hw_calibration_chan_compens(adc_unit_t adc_n, adc_channel_t chan, adc_atten_t atten)
|
|
|
|
{
|
|
|
|
int version = esp_efuse_rtc_calib_get_ver();
|
2023-06-25 23:52:25 -04:00
|
|
|
if ((version >= ESP_EFUSE_ADC_CALIB_VER_MIN) &&
|
|
|
|
(version <= ESP_EFUSE_ADC_CALIB_VER_MAX)) {
|
|
|
|
// Guarantee the calibration version before calling efuse function
|
|
|
|
s_adc_cali_chan_compens[chan][atten] = esp_efuse_rtc_calib_get_chan_compens(version, adc_n, chan, atten);
|
|
|
|
}
|
|
|
|
// No warning when version doesn't match because should has warned in adc_calc_hw_calibration_code
|
2023-05-05 07:56:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int IRAM_ATTR adc_get_hw_calibration_chan_compens(adc_unit_t adc_n, adc_channel_t chan, adc_atten_t atten)
|
|
|
|
{
|
|
|
|
return s_adc_cali_chan_compens[chan][atten];
|
|
|
|
}
|
|
|
|
#endif // SOC_ADC_CALIB_CHAN_COMPENS_SUPPORTED
|
2022-07-20 23:47:09 -04:00
|
|
|
#endif //#if SOC_ADC_CALIBRATION_V1_SUPPORTED
|
|
|
|
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------
|
|
|
|
ADC Hardware Locks
|
|
|
|
---------------------------------------------------------------*/
|
|
|
|
static _lock_t adc1_lock;
|
|
|
|
static _lock_t adc2_lock;
|
|
|
|
|
|
|
|
esp_err_t adc_lock_acquire(adc_unit_t adc_unit)
|
|
|
|
{
|
|
|
|
if (adc_unit == ADC_UNIT_1) {
|
|
|
|
_lock_acquire(&adc1_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (adc_unit == ADC_UNIT_2) {
|
|
|
|
_lock_acquire(&adc2_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ESP_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
esp_err_t adc_lock_release(adc_unit_t adc_unit)
|
|
|
|
{
|
|
|
|
if (adc_unit == ADC_UNIT_2) {
|
|
|
|
ESP_RETURN_ON_FALSE(((uint32_t *)adc2_lock != NULL), ESP_ERR_INVALID_STATE, TAG, "adc2 lock release without acquiring");
|
|
|
|
_lock_release(&adc2_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (adc_unit == ADC_UNIT_1) {
|
|
|
|
ESP_RETURN_ON_FALSE(((uint32_t *)adc1_lock != NULL), ESP_ERR_INVALID_STATE, TAG, "adc1 lock release without acquiring");
|
|
|
|
_lock_release(&adc1_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ESP_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
esp_err_t adc_lock_try_acquire(adc_unit_t adc_unit)
|
|
|
|
{
|
|
|
|
if (adc_unit == ADC_UNIT_1) {
|
|
|
|
if (_lock_try_acquire(&adc1_lock) == -1) {
|
|
|
|
return ESP_ERR_TIMEOUT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (adc_unit == ADC_UNIT_2) {
|
|
|
|
if (_lock_try_acquire(&adc2_lock) == -1) {
|
|
|
|
return ESP_ERR_TIMEOUT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ESP_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
esp_err_t adc2_wifi_acquire(void)
|
|
|
|
{
|
|
|
|
#if CONFIG_IDF_TARGET_ESP32
|
|
|
|
/* Wi-Fi module will use adc2. Use locks to avoid conflicts. */
|
|
|
|
adc_lock_acquire(ADC_UNIT_2);
|
|
|
|
ESP_LOGD(TAG, "Wi-Fi takes adc2 lock.");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return ESP_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
esp_err_t adc2_wifi_release(void)
|
|
|
|
{
|
|
|
|
#if CONFIG_IDF_TARGET_ESP32
|
|
|
|
return adc_lock_release(ADC_UNIT_2);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return ESP_OK;
|
|
|
|
}
|
2024-01-04 23:01:28 -05:00
|
|
|
|
|
|
|
static portMUX_TYPE s_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
|
|
* For those who use APB_SARADC periph
|
|
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
static int s_adc_digi_ctrlr_cnt;
|
|
|
|
|
|
|
|
void adc_apb_periph_claim(void)
|
|
|
|
{
|
|
|
|
portENTER_CRITICAL(&s_spinlock);
|
|
|
|
s_adc_digi_ctrlr_cnt++;
|
|
|
|
if (s_adc_digi_ctrlr_cnt == 1) {
|
2024-03-28 03:19:45 -04:00
|
|
|
ADC_BUS_CLK_ATOMIC() {
|
|
|
|
adc_ll_enable_bus_clock(true);
|
2024-07-13 02:06:57 -04:00
|
|
|
#if SOC_RCC_IS_INDEPENDENT
|
|
|
|
adc_ll_enable_func_clock(true);
|
|
|
|
#endif
|
2024-03-28 03:19:45 -04:00
|
|
|
adc_ll_reset_register();
|
|
|
|
}
|
2024-01-04 23:01:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
portEXIT_CRITICAL(&s_spinlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void adc_apb_periph_free(void)
|
|
|
|
{
|
|
|
|
portENTER_CRITICAL(&s_spinlock);
|
|
|
|
s_adc_digi_ctrlr_cnt--;
|
|
|
|
if (s_adc_digi_ctrlr_cnt == 0) {
|
2024-03-28 03:19:45 -04:00
|
|
|
ADC_BUS_CLK_ATOMIC() {
|
|
|
|
adc_ll_enable_bus_clock(false);
|
2024-07-13 02:06:57 -04:00
|
|
|
#if SOC_RCC_IS_INDEPENDENT
|
|
|
|
adc_ll_enable_func_clock(false);
|
|
|
|
#endif
|
2024-03-28 03:19:45 -04:00
|
|
|
}
|
2024-01-04 23:01:28 -05:00
|
|
|
} else if (s_adc_digi_ctrlr_cnt < 0) {
|
|
|
|
portEXIT_CRITICAL(&s_spinlock);
|
|
|
|
ESP_LOGE(TAG, "%s called, but `s_adc_digi_ctrlr_cnt == 0`", __func__);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
portEXIT_CRITICAL(&s_spinlock);
|
|
|
|
}
|