diff --git a/cores/esp32/esp32-hal-adc.c b/cores/esp32/esp32-hal-adc.c index 50a3d5e676e..dc3c1815535 100644 --- a/cores/esp32/esp32-hal-adc.c +++ b/cores/esp32/esp32-hal-adc.c @@ -13,261 +13,301 @@ // limitations under the License. #include "esp32-hal-adc.h" -#include "driver/adc.h" -#include "esp_adc_cal.h" -#if SOC_DAC_SUPPORTED //ESP32, ESP32S2 -#include "soc/dac_channel.h" -#include "soc/sens_reg.h" -#include "soc/rtc_io_reg.h" -#endif +#if SOC_ADC_SUPPORTED +#include "esp32-hal.h" +#include "esp32-hal-periman.h" +#include "esp_adc/adc_oneshot.h" +#include "esp_adc/adc_cali_scheme.h" -#define DEFAULT_VREF 1100 +static uint8_t __analogAttenuation = ADC_11db; +static uint8_t __analogWidth = SOC_ADC_RTC_MAX_BITWIDTH; +static uint8_t __analogReturnedWidth = SOC_ADC_RTC_MAX_BITWIDTH; -static uint8_t __analogAttenuation = 3;//11db -static uint8_t __analogWidth = ADC_WIDTH_MAX - 1; //3 for ESP32/ESP32C3; 4 for ESP32S2 -static uint8_t __analogReturnedWidth = SOC_ADC_RTC_MAX_BITWIDTH; //12 for ESP32/ESP32C3; 13 for ESP32S2 -static uint8_t __analogClockDiv = 1; -static adc_attenuation_t __pin_attenuation[SOC_GPIO_PIN_COUNT]; +adc_oneshot_unit_handle_t adc_handle[SOC_ADC_PERIPH_NUM]; +adc_cali_handle_t adc_cali_handle[SOC_ADC_PERIPH_NUM]; -static uint16_t __analogVRef = 0; -#if CONFIG_IDF_TARGET_ESP32 -static uint8_t __analogVRefPin = 0; -#endif +static bool adcDetachBus(void * pin){ + adc_channel_t adc_channel; + adc_unit_t adc_unit; + uint8_t used_channels = 0; + + adc_oneshot_io_to_channel((int)(pin-1), &adc_unit, &adc_channel); + for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(adc_unit); channel++){ + int io_pin; + adc_oneshot_channel_to_io(adc_unit, channel, &io_pin); + if(perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_ONESHOT){ + used_channels++; + } + } + + if(used_channels == 1){ //only 1 channel is used + esp_err_t err = adc_oneshot_del_unit(adc_handle[adc_unit]); + if(err != ESP_OK){ + return false; + } + adc_handle[adc_unit] = NULL; + } + return true; +} + +esp_err_t __analogChannelConfig(adc_bitwidth_t width, adc_attenuation_t atten, int8_t pin){ + esp_err_t err = ESP_OK; + adc_oneshot_chan_cfg_t config = { + .bitwidth = width, + .atten = (atten & 3), + }; + if(pin == -1){ //Reconfigure all used analog pins/channels + for(int adc_unit = 0 ; adc_unit < SOC_ADC_PERIPH_NUM; adc_unit++){ + if(adc_handle[adc_unit] != NULL){ + for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(adc_unit); channel++){ + int io_pin; + adc_oneshot_channel_to_io( adc_unit, channel, &io_pin); + if(perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_ONESHOT){ + err = adc_oneshot_config_channel(adc_handle[adc_unit], channel, &config); + if(err != ESP_OK){ + log_e("adc_oneshot_config_channel failed with error: %d", err); + return err; + } + } + } + //ADC calibration reconfig only if all channels are updated + if(adc_cali_handle[adc_unit] != NULL){ + #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED + log_d("Deleting ADC_UNIT_%d cali handle",adc_unit); + err = adc_cali_delete_scheme_curve_fitting(adc_cali_handle[adc_unit]); + if(err != ESP_OK){ + log_e("adc_cali_delete_scheme_curve_fitting failed with error: %d", err); + return err; + } + adc_cali_curve_fitting_config_t cali_config = { + .unit_id = adc_unit, + .atten = atten, + .bitwidth = width, + }; + log_d("Creating ADC_UNIT_%d curve cali handle",adc_unit); + err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle[adc_unit]); + if(err != ESP_OK){ + log_e("adc_cali_create_scheme_curve_fitting failed with error: %d", err); + return err; + } + #else //ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED + log_d("Deleting ADC_UNIT_%d line cali handle",adc_unit); + err = adc_cali_delete_scheme_line_fitting(adc_cali_handle[adc_unit]); + if(err != ESP_OK){ + log_e("adc_cali_delete_scheme_line_fitting failed with error: %d", err); + return err; + } + adc_cali_line_fitting_config_t cali_config = { + .unit_id = adc_unit, + .atten = atten, + .bitwidth = width, + }; + log_d("Creating ADC_UNIT_%d line cali handle",adc_unit); + err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_cali_handle[adc_unit]); + if(err != ESP_OK){ + log_e("adc_cali_create_scheme_line_fitting failed with error: %d", err); + return err; + } + #endif + } + } + } + + //make it default for next channels + __analogWidth = width; + __analogAttenuation = atten; + } + else{ //Reconfigure single channel + if(perimanGetPinBusType(pin) == ESP32_BUS_TYPE_ADC_ONESHOT){ + adc_channel_t channel; + adc_unit_t adc_unit; -static inline uint16_t mapResolution(uint16_t value) -{ - uint8_t from = __analogWidth + 9; - if (from == __analogReturnedWidth) { + adc_oneshot_io_to_channel(pin, &adc_unit, &channel); + if(err != ESP_OK){ + log_e("Pin %u is not ADC pin!", pin); + return err; + } + err = adc_oneshot_config_channel(adc_handle[adc_unit], channel, &config); + if(err != ESP_OK){ + log_e("adc_oneshot_config_channel failed with error: %d", err); + return err; + } + } + else { + log_e("Pin is not configured as analog channel"); + } + } + return ESP_OK; +} + +static inline uint16_t mapResolution(uint16_t value){ + uint8_t from = __analogWidth; + if (from == __analogReturnedWidth){ return value; } - if (from > __analogReturnedWidth) { + if (from > __analogReturnedWidth){ return value >> (from - __analogReturnedWidth); } return value << (__analogReturnedWidth - from); } -void __analogSetClockDiv(uint8_t clockDiv){ - if(!clockDiv){ - clockDiv = 1; +void __analogSetAttenuation(adc_attenuation_t attenuation){ + if(__analogChannelConfig(__analogWidth, attenuation, -1) != ESP_OK){ + log_e("__analogChannelConfig failed!"); } - __analogClockDiv = clockDiv; -#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 - adc_set_clk_div(__analogClockDiv); -#endif -} - -void __analogSetAttenuation(adc_attenuation_t attenuation) -{ - __analogAttenuation = attenuation & 3; } #if CONFIG_IDF_TARGET_ESP32 void __analogSetWidth(uint8_t bits){ - if(bits < 9){ - bits = 9; - } else if(bits > 12){ - bits = 12; + if(bits < SOC_ADC_RTC_MIN_BITWIDTH){ + bits = SOC_ADC_RTC_MIN_BITWIDTH; + } + else if(bits > SOC_ADC_RTC_MAX_BITWIDTH){ + bits = SOC_ADC_RTC_MAX_BITWIDTH; + } + if(__analogChannelConfig(bits, __analogAttenuation, -1) != ESP_OK){ + log_e("__analogChannelConfig failed!"); } - __analogWidth = bits - 9; - adc1_config_width(__analogWidth); } #endif -void __analogInit(){ - static bool initialized = false; - if(initialized){ - return; - } - initialized = true; - __analogSetClockDiv(__analogClockDiv); -#if CONFIG_IDF_TARGET_ESP32 - __analogSetWidth(__analogWidth + 9);//in bits -#endif - for(int i=0; i 3){ - return ; + if(err != ESP_OK){ + log_e("adc_oneshot_new_unit failed with error: %d", err); + return err; + } } - if(channel > (SOC_ADC_MAX_CHANNEL_NUM - 1)){ - adc2_config_channel_atten(channel - SOC_ADC_MAX_CHANNEL_NUM, attenuation); - } else { - adc1_config_channel_atten(channel, attenuation); + + if(!perimanSetPinBus(pin, ESP32_BUS_TYPE_ADC_ONESHOT, (void *)(pin+1))){ + adcDetachBus((void *)(pin+1)); + return err; } - __analogInit(); - if((__pin_attenuation[pin] != ADC_ATTENDB_MAX) || (attenuation != __analogAttenuation)){ - __pin_attenuation[pin] = attenuation; + + adc_oneshot_chan_cfg_t config = { + .bitwidth = __analogWidth, + .atten = __analogAttenuation, + }; + + err = adc_oneshot_config_channel(adc_handle[adc_unit], channel, &config); + if(err != ESP_OK){ + log_e("adc_oneshot_config_channel failed with error: %d", err); + return err; } + perimanSetBusDeinit(ESP32_BUS_TYPE_ADC_ONESHOT, adcDetachBus); + return ESP_OK; } -bool __adcAttachPin(uint8_t pin){ - int8_t channel = digitalPinToAnalogChannel(pin); - if(channel < 0){ - log_e("Pin %u is not ADC pin!", pin); - return false; - } - __analogInit(); - int8_t pad = digitalPinToTouchChannel(pin); - if(pad >= 0){ -#if CONFIG_IDF_TARGET_ESP32 - uint32_t touch = READ_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG); - if(touch & (1 << pad)){ - touch &= ~((1 << (pad + SENS_TOUCH_PAD_OUTEN2_S)) - | (1 << (pad + SENS_TOUCH_PAD_OUTEN1_S)) - | (1 << (pad + SENS_TOUCH_PAD_WORKEN_S))); - WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, touch); - } -#endif - } -#if SOC_DAC_SUPPORTED - else if(pin == DAC_CHANNEL_1_GPIO_NUM){ - CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC | RTC_IO_PDAC1_DAC_XPD_FORCE);//stop dac1 - } else if(pin == DAC_CHANNEL_2_GPIO_NUM){ - CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_XPD_DAC | RTC_IO_PDAC2_DAC_XPD_FORCE);//stop dac2 +void __analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation){ + if(__analogChannelConfig(__analogWidth, attenuation, pin) != ESP_OK) + { + log_e("__analogChannelConfig failed!"); } -#endif - - pinMode(pin, ANALOG); - __analogSetPinAttenuation(pin, (__pin_attenuation[pin] != ADC_ATTENDB_MAX)?__pin_attenuation[pin]:__analogAttenuation); - return true; } -void __analogReadResolution(uint8_t bits) -{ +void __analogReadResolution(uint8_t bits){ if(!bits || bits > 16){ return; } __analogReturnedWidth = bits; + #if CONFIG_IDF_TARGET_ESP32 - __analogSetWidth(bits); // hadware from 9 to 12 + __analogSetWidth(bits); // hardware analog resolution from 9 to 12 #endif } -uint16_t __analogRead(uint8_t pin) -{ - int8_t channel = digitalPinToAnalogChannel(pin); +uint16_t __analogRead(uint8_t pin){ int value = 0; - esp_err_t r = ESP_OK; - if(channel < 0){ + adc_channel_t channel; + adc_unit_t adc_unit; + + esp_err_t err = ESP_OK; + err = adc_oneshot_io_to_channel(pin, &adc_unit, &channel); + if(err != ESP_OK){ log_e("Pin %u is not ADC pin!", pin); return value; } - __adcAttachPin(pin); - if(channel > (SOC_ADC_MAX_CHANNEL_NUM - 1)){ - channel -= SOC_ADC_MAX_CHANNEL_NUM; - r = adc2_get_raw( channel, __analogWidth, &value); - if ( r == ESP_OK ) { - return mapResolution(value); - } else if ( r == ESP_ERR_INVALID_STATE ) { - log_e("GPIO%u: %s: ADC2 not initialized yet.", pin, esp_err_to_name(r)); - } else if ( r == ESP_ERR_TIMEOUT ) { - log_e("GPIO%u: %s: ADC2 is in use by Wi-Fi. Please see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html#adc-limitations for more info", pin, esp_err_to_name(r)); - } else { - log_e("GPIO%u: %s", pin, esp_err_to_name(r)); + + if(perimanGetPinBus(pin, ESP32_BUS_TYPE_ADC_ONESHOT) == NULL){ + log_d("Calling __analogInit! pin = %d", pin); + err = __analogInit(pin, channel, adc_unit); + if(err != ESP_OK){ + log_e("Analog initialization failed!"); + return value; } - } else { - value = adc1_get_raw(channel); - return mapResolution(value); } + + adc_oneshot_read(adc_handle[adc_unit], channel, &value); return mapResolution(value); } uint32_t __analogReadMilliVolts(uint8_t pin){ - int8_t channel = digitalPinToAnalogChannel(pin); - if(channel < 0){ + int value = 0; + adc_channel_t channel; + adc_unit_t adc_unit; + esp_err_t err = ESP_OK; + + adc_oneshot_io_to_channel(pin, &adc_unit, &channel); + if(err != ESP_OK){ log_e("Pin %u is not ADC pin!", pin); - return 0; + return value; } - if(!__analogVRef){ - if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) { - log_d("eFuse Two Point: Supported"); - __analogVRef = DEFAULT_VREF; + if(perimanGetPinBus(pin, ESP32_BUS_TYPE_ADC_ONESHOT) == NULL){ + err = __analogInit(pin, channel, adc_unit); + if(err != ESP_OK){ + log_e("Analog initialization failed!"); + return value; } - if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK) { - log_d("eFuse Vref: Supported"); - __analogVRef = DEFAULT_VREF; - } - if(!__analogVRef){ - __analogVRef = DEFAULT_VREF; - - #if CONFIG_IDF_TARGET_ESP32 - if(__analogVRefPin){ - esp_adc_cal_characteristics_t chars; - if(adc_vref_to_gpio(ADC_UNIT_2, __analogVRefPin) == ESP_OK){ - __analogVRef = __analogRead(__analogVRefPin); - esp_adc_cal_characterize(1, __analogAttenuation, __analogWidth, DEFAULT_VREF, &chars); - __analogVRef = esp_adc_cal_raw_to_voltage(__analogVRef, &chars); - log_d("Vref to GPIO%u: %u", __analogVRefPin, __analogVRef); - } - } - #endif - } - } - uint8_t unit = 1; - if(channel > (SOC_ADC_MAX_CHANNEL_NUM - 1)){ - unit = 2; - } - - uint16_t adc_reading = __analogRead(pin); - - uint8_t atten = __analogAttenuation; - if (__pin_attenuation[pin] != ADC_ATTENDB_MAX){ - atten = __pin_attenuation[pin]; } - esp_adc_cal_characteristics_t chars = {}; - esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, __analogWidth, __analogVRef, &chars); - - static bool print_chars_info = true; - if(print_chars_info) - { - if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { - log_i("ADC%u: Characterized using Two Point Value: %u\n", unit, chars.vref); - } - else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { - log_i("ADC%u: Characterized using eFuse Vref: %u\n", unit, chars.vref); - } - #if CONFIG_IDF_TARGET_ESP32 - else if(__analogVRef != DEFAULT_VREF){ - log_i("ADC%u: Characterized using Vref to GPIO%u: %u\n", unit, __analogVRefPin, chars.vref); - } + if(adc_cali_handle[adc_unit] == NULL){ + log_d("Creating cali handle for ADC_%d", adc_unit); + #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED + adc_cali_curve_fitting_config_t cali_config = { + .unit_id = adc_unit, + .atten = __analogAttenuation, + .bitwidth = __analogWidth, + }; + err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle[adc_unit]); + #else //ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED + adc_cali_line_fitting_config_t cali_config = { + .unit_id = adc_unit, + .bitwidth = __analogWidth, + .atten = __analogAttenuation, + }; + err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_cali_handle[adc_unit]); #endif - else { - log_i("ADC%u: Characterized using Default Vref: %u\n", unit, chars.vref); + if(err != ESP_OK){ + log_e("adc_cali_create_scheme_x failed!"); + return value; } - print_chars_info = false; } - return esp_adc_cal_raw_to_voltage((uint32_t)adc_reading, &chars); -} - -#if CONFIG_IDF_TARGET_ESP32 -void __analogSetVRefPin(uint8_t pin){ - if(pin <25 || pin > 27){ - pin = 0; + err = adc_oneshot_get_calibrated_result(adc_handle[adc_unit], adc_cali_handle[adc_unit], channel, &value); + if(err != ESP_OK){ + log_e("adc_oneshot_get_calibrated_result failed!"); + return 0; } - __analogVRefPin = pin; + return value; } -#endif - extern uint16_t analogRead(uint8_t pin) __attribute__ ((weak, alias("__analogRead"))); extern uint32_t analogReadMilliVolts(uint8_t pin) __attribute__ ((weak, alias("__analogReadMilliVolts"))); extern void analogReadResolution(uint8_t bits) __attribute__ ((weak, alias("__analogReadResolution"))); -extern void analogSetClockDiv(uint8_t clockDiv) __attribute__ ((weak, alias("__analogSetClockDiv"))); extern void analogSetAttenuation(adc_attenuation_t attenuation) __attribute__ ((weak, alias("__analogSetAttenuation"))); extern void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation) __attribute__ ((weak, alias("__analogSetPinAttenuation"))); -extern bool adcAttachPin(uint8_t pin) __attribute__ ((weak, alias("__adcAttachPin"))); - #if CONFIG_IDF_TARGET_ESP32 -extern void analogSetVRefPin(uint8_t pin) __attribute__ ((weak, alias("__analogSetVRefPin"))); extern void analogSetWidth(uint8_t bits) __attribute__ ((weak, alias("__analogSetWidth"))); #endif + +#endif diff --git a/cores/esp32/esp32-hal-adc.h b/cores/esp32/esp32-hal-adc.h index c226384a422..f25e1abce41 100644 --- a/cores/esp32/esp32-hal-adc.h +++ b/cores/esp32/esp32-hal-adc.h @@ -17,8 +17,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef MAIN_ESP32_HAL_ADC_H_ -#define MAIN_ESP32_HAL_ADC_H_ +#pragma once + +#include "soc/soc_caps.h" +#if SOC_ADC_SUPPORTED #ifdef __cplusplus extern "C" { @@ -53,13 +55,6 @@ uint32_t analogReadMilliVolts(uint8_t pin); */ void analogReadResolution(uint8_t bits); -/* - * Set the divider for the ADC clock. - * Default is 1 - * Range is 1 - 255 - * */ -void analogSetClockDiv(uint8_t clockDiv); - /* * Set the attenuation for all channels * Default is 11db @@ -72,11 +67,6 @@ void analogSetAttenuation(adc_attenuation_t attenuation); * */ void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation); -/* - * Attach pin to ADC (will also clear any other analog mode that could be on) - * */ -bool adcAttachPin(uint8_t pin); - #if CONFIG_IDF_TARGET_ESP32 /* * Sets the sample bits and read resolution @@ -85,15 +75,10 @@ bool adcAttachPin(uint8_t pin); * */ void analogSetWidth(uint8_t bits); -/* - * Set pin to use for ADC calibration if the esp is not already calibrated (25, 26 or 27) - * */ -void analogSetVRefPin(uint8_t pin); - #endif #ifdef __cplusplus } #endif -#endif /* MAIN_ESP32_HAL_ADC_H_ */ +#endif /* SOC_ADC_SUPPORTED */ diff --git a/libraries/ESP32/examples/FreeRTOS/BasicMultiThreading/BasicMultiThreading.ino b/libraries/ESP32/examples/FreeRTOS/BasicMultiThreading/BasicMultiThreading.ino index 745d5b6a46c..2cfe185a20c 100644 --- a/libraries/ESP32/examples/FreeRTOS/BasicMultiThreading/BasicMultiThreading.ino +++ b/libraries/ESP32/examples/FreeRTOS/BasicMultiThreading/BasicMultiThreading.ino @@ -92,7 +92,7 @@ void TaskBlink(void *pvParameters){ // This is a task. void TaskAnalogRead(void *pvParameters){ // This is a task. (void) pvParameters; // Check if the given analog pin is usable - if not - delete this task - if(!adcAttachPin(ANALOG_INPUT_PIN)){ + if(digitalPinToAnalogChannel(ANALOG_INPUT_PIN) == -1){ Serial.printf("TaskAnalogRead cannot work because the given pin %d cannot be used for ADC - the task will delete itself.\n", ANALOG_INPUT_PIN); analog_read_task_handle = NULL; // Prevent calling vTaskDelete on non-existing task vTaskDelete(NULL); // Delete this task