diff --git a/boards/riscv/esp32c3_devkitm/esp32c3_devkitm.yaml b/boards/riscv/esp32c3_devkitm/esp32c3_devkitm.yaml index 09f0cc51361..e6a09fad904 100644 --- a/boards/riscv/esp32c3_devkitm/esp32c3_devkitm.yaml +++ b/boards/riscv/esp32c3_devkitm/esp32c3_devkitm.yaml @@ -4,6 +4,12 @@ type: mcu arch: riscv toolchain: - zephyr +supported: + - adc + - gpio + - i2c + - watchdog + - uart testing: ignore_tags: - net diff --git a/boards/xtensa/esp32/esp32-pinctrl.dtsi b/boards/xtensa/esp32/esp32-pinctrl.dtsi index b2d38c8ef54..81ac4b4325a 100644 --- a/boards/xtensa/esp32/esp32-pinctrl.dtsi +++ b/boards/xtensa/esp32/esp32-pinctrl.dtsi @@ -73,5 +73,5 @@ output-high; }; }; - }; + diff --git a/boards/xtensa/esp32/esp32.yaml b/boards/xtensa/esp32/esp32.yaml index b3fdcf7f222..74c72e24347 100644 --- a/boards/xtensa/esp32/esp32.yaml +++ b/boards/xtensa/esp32/esp32.yaml @@ -5,6 +5,8 @@ arch: xtensa toolchain: - zephyr supported: + - adc + - dac - gpio - i2c - watchdog diff --git a/boards/xtensa/esp32s2_saola/esp32s2_saola.yaml b/boards/xtensa/esp32s2_saola/esp32s2_saola.yaml index bf09c41d930..00e7c85264a 100644 --- a/boards/xtensa/esp32s2_saola/esp32s2_saola.yaml +++ b/boards/xtensa/esp32s2_saola/esp32s2_saola.yaml @@ -4,6 +4,13 @@ type: mcu arch: xtensa toolchain: - zephyr +supported: + - adc + - dac + - gpio + - i2c + - watchdog + - uart testing: ignore_tags: - net diff --git a/drivers/adc/CMakeLists.txt b/drivers/adc/CMakeLists.txt index 81ca6757f43..7b1c0fe96bd 100644 --- a/drivers/adc/CMakeLists.txt +++ b/drivers/adc/CMakeLists.txt @@ -30,3 +30,4 @@ zephyr_library_sources_ifdef(CONFIG_ADC_GD32 adc_gd32.c) zephyr_library_sources_ifdef(CONFIG_ADC_ADS1119 adc_ads1119.c) zephyr_library_sources_ifdef(CONFIG_ADC_RPI_PICO adc_rpi_pico.c) zephyr_library_sources_ifdef(CONFIG_ADC_XMC4XXX adc_xmc4xxx.c) +zephyr_library_sources_ifdef(CONFIG_ADC_ESP32 adc_esp32.c) diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index c17a81ada67..cbcd81eda7c 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -58,6 +58,8 @@ source "drivers/adc/Kconfig.sam0" source "drivers/adc/Kconfig.stm32" +source "drivers/adc/Kconfig.esp32" + source "drivers/adc/Kconfig.xec" source "drivers/adc/Kconfig.lmp90xxx" diff --git a/drivers/adc/Kconfig.esp32 b/drivers/adc/Kconfig.esp32 new file mode 100644 index 00000000000..8fc8c9cabce --- /dev/null +++ b/drivers/adc/Kconfig.esp32 @@ -0,0 +1,10 @@ +# Copyright (c) 2022 Wolter HV +# +# SPDX-License-Identifier: Apache-2.0 + +config ADC_ESP32 + bool "ESP32 ADC driver" + default y + depends on DT_HAS_ESPRESSIF_ESP32_ADC_ENABLED + help + Enable the driver implementation for the ESP32 ADC diff --git a/drivers/adc/adc_esp32.c b/drivers/adc/adc_esp32.c new file mode 100644 index 00000000000..01bd54c32b5 --- /dev/null +++ b/drivers/adc/adc_esp32.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT espressif_esp32_adc + +#include +#include +#include +#include +#include + +#include +#include +#include +#include "driver/periph_ctrl.h" + +#include +LOG_MODULE_REGISTER(adc_esp32, CONFIG_ADC_LOG_LEVEL); + +#if CONFIG_SOC_ESP32 +#define ADC_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_VREF +#define ADC_RESOLUTION_MIN SOC_ADC_DIGI_MIN_BITWIDTH +#define ADC_RESOLUTION_MAX SOC_ADC_DIGI_MAX_BITWIDTH + +/* Due to significant measurement discrepancy in higher voltage range, we + * clip the value instead of yet another correction. The IDF implementation + * for ESP32-S2 is doing it, so we copy that approach in Zephyr driver + */ +#define ADC_CLIP_MVOLT_11DB 2550 + +#elif CONFIG_SOC_ESP32S2 +#define ADC_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP +#define ADC_RESOLUTION_MIN SOC_ADC_DIGI_MAX_BITWIDTH +#define ADC_RESOLUTION_MAX SOC_ADC_MAX_BITWIDTH + +#elif CONFIG_SOC_ESP32C3 +#define ADC_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP +#define ADC_RESOLUTION_MIN SOC_ADC_DIGI_MAX_BITWIDTH +#define ADC_RESOLUTION_MAX SOC_ADC_DIGI_MAX_BITWIDTH + +#endif + +/* Convert resolution in bits to esp32 enum values */ +#define WIDTH_MASK(r) ((((r) - 9) < ADC_WIDTH_MAX) ? ((r) - 9) : (ADC_WIDTH_MAX - 1)) + +/* Validate if resolution in bits is within allowed values */ +#define VALID_RESOLUTION(r) ((r) >= ADC_RESOLUTION_MIN && (r) <= ADC_RESOLUTION_MAX) +#define INVALID_RESOLUTION(r) (!VALID_RESOLUTION(r)) + +/* Default internal reference voltage */ +#define ADC_ESP32_DEFAULT_VREF_INTERNAL (1100) + +struct adc_esp32_conf { + adc_unit_t unit; + uint8_t channel_count; +}; + +struct adc_esp32_data { + adc_atten_t attenuation[ADC_CHANNEL_MAX]; + uint8_t resolution[ADC_CHANNEL_MAX]; + esp_adc_cal_characteristics_t chars[ADC_CHANNEL_MAX]; + uint16_t meas_ref_internal; + uint16_t *buffer; + uint16_t *buffer_repeat; + bool calibrate; +}; + +/* Convert zephyr,gain property to the ESP32 attenuation */ +static inline int gain_to_atten(enum adc_gain gain, adc_atten_t *atten) +{ + switch (gain) { + case ADC_GAIN_1: + *atten = ADC_ATTEN_DB_0; + break; + case ADC_GAIN_4_5: + *atten = ADC_ATTEN_DB_2_5; + break; + case ADC_GAIN_1_2: + *atten = ADC_ATTEN_DB_6; + break; + case ADC_GAIN_1_4: + *atten = ADC_ATTEN_DB_11; + break; + default: + return -ENOTSUP; + } + return 0; +} + +/* Convert voltage by inverted attenuation to support zephyr gain values */ +static void atten_to_gain(adc_atten_t atten, uint32_t *val_mv) +{ + if (!val_mv) { + return; + } + switch (atten) { + case ADC_ATTEN_DB_2_5: + *val_mv = (*val_mv * 4) / 5; /* 1/ADC_GAIN_4_5 */ + break; + case ADC_ATTEN_DB_6: + *val_mv = *val_mv >> 1; /* 1/ADC_GAIN_1_2 */ + break; + case ADC_ATTEN_DB_11: + *val_mv = *val_mv / 4; /* 1/ADC_GAIN_1_4 */ + break; + case ADC_ATTEN_DB_0: /* 1/ADC_GAIN_1 */ + default: + break; + } +} + +static bool adc_calibration_init(const struct device *dev) +{ + struct adc_esp32_data *data = dev->data; + + switch (esp_adc_cal_check_efuse(ADC_CALI_SCHEME)) { + case ESP_ERR_NOT_SUPPORTED: + LOG_WRN("Skip software calibration - Not supported!"); + break; + case ESP_ERR_INVALID_VERSION: + LOG_WRN("Skip software calibration - Invalid version!"); + break; + case ESP_OK: + LOG_DBG("Software calibration possible"); + return true; + default: + LOG_ERR("Invalid arg"); + break; + } + return false; +} + +static int adc_esp32_read(const struct device *dev, const struct adc_sequence *seq) +{ + const struct adc_esp32_conf *conf = dev->config; + struct adc_esp32_data *data = dev->data; + int reading; + uint32_t cal, cal_mv; + + uint8_t channel_id = find_lsb_set(seq->channels) - 1; + + if (seq->buffer_size < 2) { + LOG_ERR("Sequence buffer space too low '%d'", seq->buffer_size); + return -ENOMEM; + } + + if (seq->channels > BIT(channel_id)) { + LOG_ERR("Multi-channel readings not supported"); + return -ENOTSUP; + } + + if (INVALID_RESOLUTION(seq->resolution)) { + LOG_ERR("unsupported resolution (%d)", seq->resolution); + return -ENOTSUP; + } + + if (seq->calibrate) { + /* TODO: Does this mean actual Vref measurement on selected GPIO ?*/ + LOG_ERR("calibration is not supported"); + return -ENOTSUP; + } + + data->resolution[channel_id] = seq->resolution; + +#if CONFIG_SOC_ESP32C3 + /* NOTE: nothing to set on ESP32C3 SoC */ + if (conf->unit == ADC_UNIT_1) { + adc1_config_width(ADC_WIDTH_BIT_DEFAULT); + } +#else + adc_set_data_width(conf->unit, WIDTH_MASK(data->resolution[channel_id])); +#endif /* CONFIG_SOC_ESP32C3 */ + + /* Read raw value */ + if (conf->unit == ADC_UNIT_1) { + reading = adc1_get_raw(channel_id); + } + if (conf->unit == ADC_UNIT_2) { + if (adc2_get_raw(channel_id, ADC_WIDTH_BIT_DEFAULT, &reading)) { + LOG_ERR("Conversion timeout on '%s' channel %d", dev->name, channel_id); + return -ETIMEDOUT; + } + } + + /* Calibration scheme is available */ + if (data->calibrate) { + data->chars[channel_id].bit_width = WIDTH_MASK(data->resolution[channel_id]); + /* Get corrected voltage output */ + cal = cal_mv = esp_adc_cal_raw_to_voltage(reading, &data->chars[channel_id]); + +#if CONFIG_SOC_ESP32 + if (data->attenuation[channel_id] == ADC_ATTEN_DB_11) { + if (cal > ADC_CLIP_MVOLT_11DB) { + cal = ADC_CLIP_MVOLT_11DB; + } + } +#endif /* CONFIG_SOC_ESP32 */ + + /* Fit according to selected attenuation */ + atten_to_gain(data->attenuation[channel_id], &cal); + if (data->meas_ref_internal > 0) { + cal = (cal << data->resolution[channel_id]) / data->meas_ref_internal; + } + } else { + LOG_DBG("Using uncalibrated values!"); + /* Uncalibrated raw value */ + cal = reading; + } + + /* Store result */ + data->buffer = (uint16_t *) seq->buffer; + data->buffer[0] = cal; + + return 0; +} + +#ifdef CONFIG_ADC_ASYNC +static int adc_esp32_read_async(const struct device *dev, + const struct adc_sequence *sequence, + struct k_poll_signal *async) +{ + (void)(dev); + (void)(sequence); + (void)(async); + + return -ENOTSUP; +} +#endif /* CONFIG_ADC_ASYNC */ + +static int adc_esp32_channel_setup(const struct device *dev, const struct adc_channel_cfg *cfg) +{ + const struct adc_esp32_conf *conf = (const struct adc_esp32_conf *)dev->config; + struct adc_esp32_data *data = (struct adc_esp32_data *) dev->data; + int err; + + if (cfg->channel_id >= conf->channel_count) { + LOG_ERR("Unsupported channel id '%d'", cfg->channel_id); + return -ENOTSUP; + } + + if (cfg->reference != ADC_REF_INTERNAL) { + LOG_ERR("Unsupported channel reference '%d'", cfg->reference); + return -ENOTSUP; + } + + if (cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { + LOG_ERR("Unsupported acquisition_time '%d'", cfg->acquisition_time); + return -ENOTSUP; + } + + if (cfg->differential) { + LOG_ERR("Differential channels are not supported"); + return -ENOTSUP; + } + + if (gain_to_atten(cfg->gain, &data->attenuation[cfg->channel_id])) { + LOG_ERR("Unsupported gain value '%d'", cfg->gain); + return -ENOTSUP; + } + + /* Prepare channel */ + if (conf->unit == ADC_UNIT_1) { + adc1_config_channel_atten(cfg->channel_id, data->attenuation[cfg->channel_id]); + } + if (conf->unit == ADC_UNIT_2) { + adc2_config_channel_atten(cfg->channel_id, data->attenuation[cfg->channel_id]); + } + + if (data->calibrate) { + esp_adc_cal_value_t cal = esp_adc_cal_characterize(conf->unit, + data->attenuation[cfg->channel_id], + WIDTH_MASK(data->resolution[cfg->channel_id]), + data->meas_ref_internal, + &data->chars[cfg->channel_id]); + if (cal >= ESP_ADC_CAL_VAL_NOT_SUPPORTED) { + LOG_ERR("Calibration error or not supported"); + return -EIO; + } + LOG_DBG("Using ADC calibration method %d", cal); + } + + return 0; +} + +static int adc_esp32_init(const struct device *dev) +{ + struct adc_esp32_data *data = (struct adc_esp32_data *) dev->data; + + for (uint8_t i = 0; i < ARRAY_SIZE(data->resolution); i++) { + data->resolution[i] = ADC_RESOLUTION_MAX; + } + for (uint8_t i = 0; i < ARRAY_SIZE(data->attenuation); i++) { + data->attenuation[i] = ADC_ATTEN_DB_0; + } + + /* Default reference voltage. This could be calibrated externaly */ + data->meas_ref_internal = ADC_ESP32_DEFAULT_VREF_INTERNAL; + + /* Check if calibration is possible */ + data->calibrate = adc_calibration_init(dev); + + return 0; +} + +static const struct adc_driver_api api_esp32_driver_api = { + .channel_setup = adc_esp32_channel_setup, + .read = adc_esp32_read, +#ifdef CONFIG_ADC_ASYNC + .read_async = adc_esp32_read_async, +#endif /* CONFIG_ADC_ASYNC */ + .ref_internal = ADC_ESP32_DEFAULT_VREF_INTERNAL, +}; + +#define ESP32_ADC_INIT(inst) \ + \ + static const struct adc_esp32_conf adc_esp32_conf_##inst = { \ + .unit = DT_PROP(DT_DRV_INST(inst), unit), \ + .channel_count = DT_PROP(DT_DRV_INST(inst), channel_count), \ + }; \ + \ + static struct adc_esp32_data adc_esp32_data_##inst = { \ + }; \ + \ +DEVICE_DT_INST_DEFINE(inst, &adc_esp32_init, NULL, \ + &adc_esp32_data_##inst, \ + &adc_esp32_conf_##inst, \ + POST_KERNEL, \ + CONFIG_ADC_INIT_PRIORITY, \ + &api_esp32_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(ESP32_ADC_INIT) diff --git a/drivers/adc/adc_shell.c b/drivers/adc/adc_shell.c index 86fa75916ca..342420719d5 100644 --- a/drivers/adc/adc_shell.c +++ b/drivers/adc/adc_shell.c @@ -13,6 +13,8 @@ #if DT_HAS_COMPAT_STATUS_OKAY(atmel_sam_afec) #define DT_DRV_COMPAT atmel_sam_afec +#elif DT_HAS_COMPAT_STATUS_OKAY(espressif_esp32_adc) +#define DT_DRV_COMPAT espressif_esp32_adc #elif DT_HAS_COMPAT_STATUS_OKAY(atmel_sam0_adc) #define DT_DRV_COMPAT atmel_sam0_adc #elif DT_HAS_COMPAT_STATUS_OKAY(ite_it8xxx2_adc) diff --git a/dts/bindings/adc/espressif,esp32-adc.yaml b/dts/bindings/adc/espressif,esp32-adc.yaml new file mode 100644 index 00000000000..14a264bbc70 --- /dev/null +++ b/dts/bindings/adc/espressif,esp32-adc.yaml @@ -0,0 +1,49 @@ +# Copyright (c) 2022 Wolter HV +# +# SPDX-License-Identifier: Apache-2.0 + +description: | + Espressif ESP32 ADC + Possible available resolutions depends on the used chip. + - ESP32 < 9,10,11,12 > + - ESP32-S2 < 12 > + - ESP32-C3 < 12 > + For chips with configurable resolution feature (ESP32), + maximum resolution will be used if not set explicitly. + + Zephyr API is using gain unit to characterize ADC input. + To achieve compatibility we choose to select those gain, + which coresponds to the ESP32 ADC attenuation feature. + + ESP32,attenuation ~ zephyr,gain + ----------------- ----------- + 0 dB ADC_GAIN_1 + 2.5 dB ADC_GAIN_4_5 + 6 dB ADC_GAIN_1_2 + 11 dB ADC_GAIN_1_4 + + In case unsupported gain is selected the adc_channel_setup() + would return ENOTSUP error. + +compatible: "espressif,esp32-adc" + +include: [adc-controller.yaml] + +properties: + + unit: + type: int + required: true + description: ADC unit number. + Possible values are 1,2,.. depending on chip. + + channel-count: + type: int + required: true + description: The maximum channels supported on each unit. + + "#io-channel-cells": + const: 1 + +io-channel-cells: + - input diff --git a/dts/riscv/espressif/esp32c3.dtsi b/dts/riscv/espressif/esp32c3.dtsi index a31c13c80f0..ffb3bad0a8d 100644 --- a/dts/riscv/espressif/esp32c3.dtsi +++ b/dts/riscv/espressif/esp32c3.dtsi @@ -231,6 +231,24 @@ status = "disabled"; }; + adc0: adc@60040000 { + compatible = "espressif,esp32-adc"; + reg = <0x60040000 4>; + unit = <1>; + channel-count = <5>; + #io-channel-cells = <1>; + status = "disabled"; + }; + + adc1: adc@60040004 { + compatible = "espressif,esp32-adc"; + reg = <0x60040004 4>; + unit = <2>; + channel-count = <2>; + #io-channel-cells = <1>; + status = "disabled"; + }; + }; }; diff --git a/dts/xtensa/espressif/esp32.dtsi b/dts/xtensa/espressif/esp32.dtsi index bf00f02ae31..74e843f978d 100644 --- a/dts/xtensa/espressif/esp32.dtsi +++ b/dts/xtensa/espressif/esp32.dtsi @@ -364,6 +364,24 @@ #io-channel-cells = <1>; status = "disabled"; }; - }; + adc0: adc@3ff48800 { + compatible = "espressif,esp32-adc"; + reg = <0x3ff48800 10>; + unit = <1>; + channel-count = <8>; + #io-channel-cells = <1>; + status = "disabled"; + }; + + adc1: adc@3ff48890 { + compatible = "espressif,esp32-adc"; + reg = <0x3ff48890 10>; + unit = <2>; + channel-count = <10>; + #io-channel-cells = <1>; + status = "disabled"; + }; + + }; }; diff --git a/dts/xtensa/espressif/esp32s2.dtsi b/dts/xtensa/espressif/esp32s2.dtsi index 5607d9bd8d9..556303a826a 100644 --- a/dts/xtensa/espressif/esp32s2.dtsi +++ b/dts/xtensa/espressif/esp32s2.dtsi @@ -270,6 +270,24 @@ reg = <0x3f408800 0x4>; status = "disabled"; }; + + adc0: adc@3f440018 { + compatible = "espressif,esp32-adc"; + reg = <0x3f440018 100>; + unit = <1>; + channel-count = <10>; + #io-channel-cells = <1>; + status = "disabled"; + }; + + adc1: adc@3f440028 { + compatible = "espressif,esp32-adc"; + reg = <0x3f440028 100>; + unit = <2>; + channel-count = <10>; + #io-channel-cells = <1>; + status = "disabled"; + }; }; }; diff --git a/samples/drivers/adc/boards/esp32.conf b/samples/drivers/adc/boards/esp32.conf new file mode 100644 index 00000000000..ec0dbd60800 --- /dev/null +++ b/samples/drivers/adc/boards/esp32.conf @@ -0,0 +1,7 @@ +#CONFIG_SHELL=y +#CONFIG_LOG=y +#CONFIG_ADC_ESP32=y + +#CONFIG_LOG_MODE_DEFERRED=y +#CONFIG_LOG_MODE_MINIMAL=y +#CONFIG_ADC_LOG_LEVEL_DBG=y