Browse Source

drivers: adc: esp32: Add support for single-shot conversion

Allow single-shot adc conversion on all supported targets.

Signed-off-by: Marek Matej <marek.matej@espressif.com>
pull/53341/head
Marek Matej 3 years ago committed by Fabio Baltieri
parent
commit
937ea00e7a
  1. 6
      boards/riscv/esp32c3_devkitm/esp32c3_devkitm.yaml
  2. 2
      boards/xtensa/esp32/esp32-pinctrl.dtsi
  3. 2
      boards/xtensa/esp32/esp32.yaml
  4. 7
      boards/xtensa/esp32s2_saola/esp32s2_saola.yaml
  5. 1
      drivers/adc/CMakeLists.txt
  6. 2
      drivers/adc/Kconfig
  7. 10
      drivers/adc/Kconfig.esp32
  8. 334
      drivers/adc/adc_esp32.c
  9. 2
      drivers/adc/adc_shell.c
  10. 49
      dts/bindings/adc/espressif,esp32-adc.yaml
  11. 18
      dts/riscv/espressif/esp32c3.dtsi
  12. 18
      dts/xtensa/espressif/esp32.dtsi
  13. 18
      dts/xtensa/espressif/esp32s2.dtsi
  14. 7
      samples/drivers/adc/boards/esp32.conf

6
boards/riscv/esp32c3_devkitm/esp32c3_devkitm.yaml

@ -4,6 +4,12 @@ type: mcu @@ -4,6 +4,12 @@ type: mcu
arch: riscv
toolchain:
- zephyr
supported:
- adc
- gpio
- i2c
- watchdog
- uart
testing:
ignore_tags:
- net

2
boards/xtensa/esp32/esp32-pinctrl.dtsi

@ -73,5 +73,5 @@ @@ -73,5 +73,5 @@
output-high;
};
};
};

2
boards/xtensa/esp32/esp32.yaml

@ -5,6 +5,8 @@ arch: xtensa @@ -5,6 +5,8 @@ arch: xtensa
toolchain:
- zephyr
supported:
- adc
- dac
- gpio
- i2c
- watchdog

7
boards/xtensa/esp32s2_saola/esp32s2_saola.yaml

@ -4,6 +4,13 @@ type: mcu @@ -4,6 +4,13 @@ type: mcu
arch: xtensa
toolchain:
- zephyr
supported:
- adc
- dac
- gpio
- i2c
- watchdog
- uart
testing:
ignore_tags:
- net

1
drivers/adc/CMakeLists.txt

@ -30,3 +30,4 @@ zephyr_library_sources_ifdef(CONFIG_ADC_GD32 adc_gd32.c) @@ -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)

2
drivers/adc/Kconfig

@ -58,6 +58,8 @@ source "drivers/adc/Kconfig.sam0" @@ -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"

10
drivers/adc/Kconfig.esp32

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
# Copyright (c) 2022 Wolter HV <wolterhv@gmx.de>
#
# 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

334
drivers/adc/adc_esp32.c

@ -0,0 +1,334 @@ @@ -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 <errno.h>
#include <hal/adc_hal.h>
#include <hal/adc_types.h>
#include <esp_adc_cal.h>
#include <esp_heap_caps.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/adc.h>
#include "driver/periph_ctrl.h"
#include <zephyr/logging/log.h>
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)

2
drivers/adc/adc_shell.c

@ -13,6 +13,8 @@ @@ -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)

49
dts/bindings/adc/espressif,esp32-adc.yaml

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
# Copyright (c) 2022 Wolter HV <wolterhv@gmx.de>
#
# 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

18
dts/riscv/espressif/esp32c3.dtsi

@ -231,6 +231,24 @@ @@ -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";
};
};
};

18
dts/xtensa/espressif/esp32.dtsi

@ -364,6 +364,24 @@ @@ -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";
};
};
};

18
dts/xtensa/espressif/esp32s2.dtsi

@ -270,6 +270,24 @@ @@ -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";
};
};
};

7
samples/drivers/adc/boards/esp32.conf

@ -0,0 +1,7 @@ @@ -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
Loading…
Cancel
Save