Skip to content

ADC refactoring #7827

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 14, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
416 changes: 228 additions & 188 deletions cores/esp32/esp32-hal-adc.c
Original file line number Diff line number Diff line change
@@ -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<SOC_GPIO_PIN_COUNT; i++){
__pin_attenuation[i] = ADC_ATTENDB_MAX;
}
}
esp_err_t __analogInit(uint8_t pin, adc_channel_t channel, adc_unit_t adc_unit){
esp_err_t err = ESP_OK;
if(adc_handle[adc_unit] == NULL) {
adc_oneshot_unit_init_cfg_t init_config1 = {
.unit_id = adc_unit,
.ulp_mode = ADC_ULP_MODE_DISABLE,
};
err = adc_oneshot_new_unit(&init_config1, &adc_handle[adc_unit]);

void __analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation)
{
int8_t channel = digitalPinToAnalogChannel(pin);
if(channel < 0 || attenuation > 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
25 changes: 5 additions & 20 deletions cores/esp32/esp32-hal-adc.h
Original file line number Diff line number Diff line change
@@ -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 */
Original file line number Diff line number Diff line change
@@ -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