From d411aa9be92ede56009ff32d56efe759f380e0f4 Mon Sep 17 00:00:00 2001 From: Felipe Neves Date: Mon, 1 Jun 2026 21:44:14 -0300 Subject: [PATCH 1/7] Add ESP32 digital ADC DMA backends for low-side sensing. Port chip-specific DMA shims (I2S on ESP32, SPI3 on S2, GDMA on S3/C6) and shared timing constants aligned with espFoC isensor_adc. --- .../esp32/esp32_adc_digi_internal.h | 87 +++++++++++ .../esp32/esp32_adc_dma_esp32.c | 131 ++++++++++++++++ .../esp32/esp32_adc_dma_esp32s2.c | 136 ++++++++++++++++ .../esp32/esp32_adc_dma_gdma.c | 146 ++++++++++++++++++ 4 files changed, 500 insertions(+) create mode 100644 src/current_sense/hardware_specific/esp32/esp32_adc_digi_internal.h create mode 100644 src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32.c create mode 100644 src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32s2.c create mode 100644 src/current_sense/hardware_specific/esp32/esp32_adc_dma_gdma.c diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_internal.h b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_internal.h new file mode 100644 index 00000000..c794af66 --- /dev/null +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_internal.h @@ -0,0 +1,87 @@ +/* + * MIT License + * + * Copyright (c) 2021 Felipe Neves + */ +#pragma once + +#include +#include +#include "sdkconfig.h" +#include "esp_err.h" +#include "hal/dma_types.h" + +/* + * Low-side current sense on ESP32 uses the SAR **digital** controller (pattern + * sequencer) plus a chip-specific DMA shim — same approach as espFoC isensor_adc. + * + * Triggering (when to start one pattern conversion): + * - ESP32 / ESP32-S2: **software** only. MCPWM ISR (comparator or timer event) + * must call esp32_adc_digi_trigger_software() at the desired PWM phase. + * - ESP32-S3 and newer (C3, C6, …): optional **ETM** wires MCPWM timer TEZ/TEP + * to ADC_TASK_START0 with no CPU in the trigger path. + * + * DMA backend (where SOC moves ADC results): + * - ESP32: I2S peripheral (see esp32_adc_dma_esp32.c) + * - ESP32-S2: SPI3 (esp32_adc_dma_esp32s2.c) + * - ESP32-S3+: GDMA (esp32_adc_dma_gdma.c) + */ + +#if defined(ESP_H) && defined(ARDUINO_ARCH_ESP32) + +#define SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED 1 + +/* ETM is not available on original ESP32 or ESP32-S2. */ +#if SOC_ETM_SUPPORTED && !CONFIG_IDF_TARGET_ESP32 && !CONFIG_IDF_TARGET_ESP32S2 +#define SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED 1 +#else +#define SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED 0 +#endif + +#else + +#define SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED 0 +#define SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED 0 + +#endif + +/* Pattern / DMA sizing — kept aligned with espFoC isensor_adc_internal.h */ +#define SIMPLEFOC_ESP32_ADC_PATTERN_HZ 80000 +#define SIMPLEFOC_ESP32_ADC_NUM_CHANNELS 2 +#define SIMPLEFOC_ESP32_ADC_CONVERT_LIMIT 2 + +typedef struct esp32_adc_digi_dma_ctx esp32_adc_digi_dma_ctx_t; + +typedef void (*esp32_adc_digi_dma_done_fn_t)(void *user); + +struct esp32_adc_digi_dma_ctx { + esp32_adc_digi_dma_done_fn_t on_done; + void *on_done_arg; + volatile intptr_t eof_desc_addr; +}; + +esp_err_t esp32_adc_digi_dma_init(esp32_adc_digi_dma_ctx_t *ctx, + esp32_adc_digi_dma_done_fn_t on_done, + void *user); +esp_err_t esp32_adc_digi_dma_deinit(esp32_adc_digi_dma_ctx_t *ctx); +esp_err_t esp32_adc_digi_dma_start(esp32_adc_digi_dma_ctx_t *ctx, dma_descriptor_t *desc); +esp_err_t esp32_adc_digi_dma_stop(esp32_adc_digi_dma_ctx_t *ctx); +esp_err_t esp32_adc_digi_dma_reset(esp32_adc_digi_dma_ctx_t *ctx); + +#if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED + +typedef enum { + SIMPLEFOC_ESP32_ADC_MCPWM_EVT_TIMER_TEZ = 0, + SIMPLEFOC_ESP32_ADC_MCPWM_EVT_TIMER_TEP, +} esp32_adc_digi_mcpwm_event_t; + +typedef struct { + uint8_t mcpwm_timer; + esp32_adc_digi_mcpwm_event_t event; +} esp32_adc_digi_etm_config_t; + +esp_err_t esp32_adc_digi_etm_connect(const esp32_adc_digi_etm_config_t *cfg); +esp_err_t esp32_adc_digi_etm_enable(bool enable); +void esp32_adc_digi_etm_disconnect(void); + +#endif /* SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED */ diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32.c b/src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32.c new file mode 100644 index 00000000..a350eee7 --- /dev/null +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32.c @@ -0,0 +1,131 @@ +/* + * MIT License + * + * Copyright (c) 2021 Felipe Neves + * + * I2S DMA backend for ADC digi on ESP32 (i2s_ll only). + */ + +#include "sdkconfig.h" + +#if CONFIG_IDF_TARGET_ESP32 + +#include "esp_err.h" +#include "esp_intr_alloc.h" +#include "esp_attr.h" +#include "hal/i2s_ll.h" +#include "hal/adc_hal.h" +#include "soc/i2s_periph.h" +#include "esp_private/i2s_platform.h" +#include "esp32_adc_digi_internal.h" + +#define ADC_DMA_I2S_HOST ADC_HAL_DMA_I2S_HOST +#define ADC_DMA_INTR_MASK BIT(9) + +static i2s_dev_t *s_i2s_dev; +static esp32_adc_digi_dma_ctx_t *s_ctx; +static intr_handle_t s_intr; +static bool s_inited; + +static void IRAM_ATTR esp32_adc_i2s_isr(void *arg) +{ + (void)arg; + if ((i2s_ll_get_intr_status(s_i2s_dev) & ADC_DMA_INTR_MASK) == 0) { + return; + } + i2s_ll_clear_intr_status(s_i2s_dev, ADC_DMA_INTR_MASK); + + if (s_ctx != NULL) { + uint32_t desc_addr = 0; + i2s_ll_rx_get_eof_des_addr(s_i2s_dev, &desc_addr); + s_ctx->eof_desc_addr = (intptr_t)desc_addr; + if (s_ctx->on_done != NULL) { + s_ctx->on_done(s_ctx->on_done_arg); + } + } +} + +esp_err_t esp32_adc_digi_dma_init(esp32_adc_digi_dma_ctx_t *ctx, + esp32_adc_digi_dma_done_fn_t on_done, + void *user) +{ + if (ctx == NULL || on_done == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (s_inited) { + ctx->on_done = on_done; + ctx->on_done_arg = user; + s_ctx = ctx; + return ESP_OK; + } + + esp_err_t err = i2s_platform_acquire_occupation(I2S_CTLR_HP, ADC_DMA_I2S_HOST, "simplefoc_adc_digi"); + if (err != ESP_OK) { + return err; + } + s_i2s_dev = I2S_LL_GET_HW(ADC_DMA_I2S_HOST); + + err = esp_intr_alloc(i2s_periph_signal[ADC_DMA_I2S_HOST].irq, ESP_INTR_FLAG_IRAM, + esp32_adc_i2s_isr, NULL, &s_intr); + if (err != ESP_OK) { + i2s_platform_release_occupation(I2S_CTLR_HP, ADC_DMA_I2S_HOST); + return err; + } + + ctx->on_done = on_done; + ctx->on_done_arg = user; + s_ctx = ctx; + s_inited = true; + return ESP_OK; +} + +esp_err_t esp32_adc_digi_dma_deinit(esp32_adc_digi_dma_ctx_t *ctx) +{ + (void)ctx; + if (!s_inited) { + return ESP_OK; + } + esp32_adc_digi_dma_stop(ctx); + if (s_intr != NULL) { + esp_intr_free(s_intr); + s_intr = NULL; + } + i2s_platform_release_occupation(I2S_CTLR_HP, ADC_DMA_I2S_HOST); + s_ctx = NULL; + s_inited = false; + return ESP_OK; +} + +esp_err_t esp32_adc_digi_dma_start(esp32_adc_digi_dma_ctx_t *ctx, dma_descriptor_t *desc) +{ + (void)ctx; + i2s_ll_clear_intr_status(s_i2s_dev, ADC_DMA_INTR_MASK); + i2s_ll_enable_intr(s_i2s_dev, ADC_DMA_INTR_MASK, true); + i2s_ll_enable_dma(s_i2s_dev, true); + i2s_ll_rx_start_link(s_i2s_dev, (uint32_t)desc); + return ESP_OK; +} + +esp_err_t esp32_adc_digi_dma_stop(esp32_adc_digi_dma_ctx_t *ctx) +{ + (void)ctx; + if (!s_inited) { + return ESP_OK; + } + i2s_ll_enable_intr(s_i2s_dev, ADC_DMA_INTR_MASK, false); + i2s_ll_clear_intr_status(s_i2s_dev, ADC_DMA_INTR_MASK); + i2s_ll_rx_stop_link(s_i2s_dev); + return ESP_OK; +} + +esp_err_t esp32_adc_digi_dma_reset(esp32_adc_digi_dma_ctx_t *ctx) +{ + (void)ctx; + if (!s_inited) { + return ESP_OK; + } + i2s_ll_rx_reset_dma(s_i2s_dev); + return ESP_OK; +} + +#endif diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32s2.c b/src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32s2.c new file mode 100644 index 00000000..6363503e --- /dev/null +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32s2.c @@ -0,0 +1,136 @@ +/* + * MIT License + * + * Copyright (c) 2021 Felipe Neves + * + * SPI3 DMA backend for ADC digi on ESP32-S2 (spi_ll + spicommon). + */ + +#include "sdkconfig.h" + +#if CONFIG_IDF_TARGET_ESP32S2 + +#include "esp_err.h" +#include "esp_intr_alloc.h" +#include "esp_attr.h" +#include "hal/spi_ll.h" +#include "esp_private/spi_common_internal.h" +#include "esp32_adc_digi_internal.h" + +#define ADC_DMA_SPI_HOST SPI3_HOST +#define ADC_DMA_INTR_MASK SPI_LL_INTR_IN_SUC_EOF + +static spi_dev_t *s_spi_dev; +static spi_dma_ctx_t *s_spi_dma; +static esp32_adc_digi_dma_ctx_t *s_ctx; +static intr_handle_t s_intr; +static bool s_inited; + +static void IRAM_ATTR esp32_adc_spi_isr(void *arg) +{ + (void)arg; + if (!spi_ll_get_intr(s_spi_dev, ADC_DMA_INTR_MASK)) { + return; + } + spi_ll_clear_intr(s_spi_dev, ADC_DMA_INTR_MASK); + + if (s_ctx != NULL) { + s_ctx->eof_desc_addr = spi_dma_ll_get_in_suc_eof_desc_addr(s_spi_dev, + s_spi_dma->rx_dma_chan.chan_id); + if (s_ctx->on_done != NULL) { + s_ctx->on_done(s_ctx->on_done_arg); + } + } +} + +esp_err_t esp32_adc_digi_dma_init(esp32_adc_digi_dma_ctx_t *ctx, + esp32_adc_digi_dma_done_fn_t on_done, + void *user) +{ + if (ctx == NULL || on_done == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (s_inited) { + ctx->on_done = on_done; + ctx->on_done_arg = user; + s_ctx = ctx; + return ESP_OK; + } + + if (!spicommon_periph_claim(ADC_DMA_SPI_HOST, "simplefoc_adc_digi")) { + return ESP_FAIL; + } + + esp_err_t err = spicommon_dma_chan_alloc(ADC_DMA_SPI_HOST, SPI_DMA_CH_AUTO, &s_spi_dma); + if (err != ESP_OK) { + spicommon_periph_free(ADC_DMA_SPI_HOST); + return err; + } + + s_spi_dev = SPI_LL_GET_HW(ADC_DMA_SPI_HOST); + err = esp_intr_alloc(spicommon_irqdma_source_for_host(ADC_DMA_SPI_HOST), ESP_INTR_FLAG_IRAM, + esp32_adc_spi_isr, NULL, &s_intr); + if (err != ESP_OK) { + spicommon_dma_chan_free(s_spi_dma); + spicommon_periph_free(ADC_DMA_SPI_HOST); + return err; + } + + ctx->on_done = on_done; + ctx->on_done_arg = user; + s_ctx = ctx; + s_inited = true; + return ESP_OK; +} + +esp_err_t esp32_adc_digi_dma_deinit(esp32_adc_digi_dma_ctx_t *ctx) +{ + (void)ctx; + if (!s_inited) { + return ESP_OK; + } + esp32_adc_digi_dma_stop(ctx); + if (s_intr != NULL) { + esp_intr_free(s_intr); + s_intr = NULL; + } + spicommon_dma_chan_free(s_spi_dma); + spicommon_periph_free(ADC_DMA_SPI_HOST); + s_spi_dma = NULL; + s_ctx = NULL; + s_inited = false; + return ESP_OK; +} + +esp_err_t esp32_adc_digi_dma_start(esp32_adc_digi_dma_ctx_t *ctx, dma_descriptor_t *desc) +{ + (void)ctx; + spi_ll_clear_intr(s_spi_dev, ADC_DMA_INTR_MASK); + spi_ll_enable_intr(s_spi_dev, ADC_DMA_INTR_MASK); + spi_dma_ll_rx_start(s_spi_dev, s_spi_dma->rx_dma_chan.chan_id, (lldesc_t *)desc); + return ESP_OK; +} + +esp_err_t esp32_adc_digi_dma_stop(esp32_adc_digi_dma_ctx_t *ctx) +{ + (void)ctx; + if (!s_inited) { + return ESP_OK; + } + spi_ll_disable_intr(s_spi_dev, ADC_DMA_INTR_MASK); + spi_ll_clear_intr(s_spi_dev, ADC_DMA_INTR_MASK); + spi_dma_ll_rx_stop(s_spi_dev, s_spi_dma->rx_dma_chan.chan_id); + return ESP_OK; +} + +esp_err_t esp32_adc_digi_dma_reset(esp32_adc_digi_dma_ctx_t *ctx) +{ + (void)ctx; + if (!s_inited) { + return ESP_OK; + } + spi_dma_ll_rx_reset(s_spi_dev, s_spi_dma->rx_dma_chan.chan_id); + return ESP_OK; +} + +#endif diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_dma_gdma.c b/src/current_sense/hardware_specific/esp32/esp32_adc_dma_gdma.c new file mode 100644 index 00000000..55684621 --- /dev/null +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_dma_gdma.c @@ -0,0 +1,146 @@ +/* + * MIT License + * + * Copyright (c) 2021 Felipe Neves + * + * GDMA RX backend for ADC digi (ESP32-S3, C3, C6, …) using gdma_hal + gdma_ll only. + */ + +#include "sdkconfig.h" + +#if !CONFIG_IDF_TARGET_ESP32 && !CONFIG_IDF_TARGET_ESP32S2 + +#include "esp_err.h" +#include "esp_intr_alloc.h" +#include "esp_attr.h" +#include "hal/gdma_hal.h" +#include "hal/gdma_hal_ahb.h" +#include "hal/gdma_ll.h" +#include "hal/gdma_types.h" +#include "soc/gdma_periph.h" +#include "esp32_adc_digi_internal.h" + +#define ESP32_ADC_GDMA_GROUP 0 +#define ESP32_ADC_GDMA_PAIR 2 +#define ESP32_ADC_GDMA_RX_EOF GDMA_LL_EVENT_RX_SUC_EOF + +static gdma_hal_context_t s_gdma_hal; +static esp32_adc_digi_dma_ctx_t *s_ctx; +static intr_handle_t s_intr; +static bool s_inited; + +static void IRAM_ATTR esp32_adc_gdma_isr(void *arg) +{ + (void)arg; + gdma_hal_context_t *hal = &s_gdma_hal; + const int ch = ESP32_ADC_GDMA_PAIR; + + uint32_t st = hal->read_intr_status(hal, ch, GDMA_CHANNEL_DIRECTION_RX, true); + if ((st & ESP32_ADC_GDMA_RX_EOF) == 0) { + return; + } + hal->clear_intr(hal, ch, GDMA_CHANNEL_DIRECTION_RX, ESP32_ADC_GDMA_RX_EOF); + + if (s_ctx != NULL) { + s_ctx->eof_desc_addr = (intptr_t)hal->get_eof_desc_addr(hal, ch, GDMA_CHANNEL_DIRECTION_RX, true); + if (s_ctx->on_done != NULL) { + s_ctx->on_done(s_ctx->on_done_arg); + } + } +} + +esp_err_t esp32_adc_digi_dma_init(esp32_adc_digi_dma_ctx_t *ctx, + esp32_adc_digi_dma_done_fn_t on_done, + void *user) +{ + if (ctx == NULL || on_done == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (s_inited) { + ctx->on_done = on_done; + ctx->on_done_arg = user; + s_ctx = ctx; + return ESP_OK; + } + + int __DECLARE_RCC_ATOMIC_ENV __attribute__((unused)); + + gdma_ll_enable_bus_clock(ESP32_ADC_GDMA_GROUP, true); + gdma_ll_reset_register(ESP32_ADC_GDMA_GROUP); + + gdma_hal_config_t hal_cfg = { + .group_id = ESP32_ADC_GDMA_GROUP, + }; + gdma_ahb_hal_init(&s_gdma_hal, &hal_cfg); + + const int ch = ESP32_ADC_GDMA_PAIR; + s_gdma_hal.reset(&s_gdma_hal, ch, GDMA_CHANNEL_DIRECTION_RX); + s_gdma_hal.connect_peri(&s_gdma_hal, ch, GDMA_CHANNEL_DIRECTION_RX, + GDMA_TRIG_PERIPH_ADC, 0); + s_gdma_hal.set_strategy(&s_gdma_hal, ch, GDMA_CHANNEL_DIRECTION_RX, + true, false, false); + s_gdma_hal.enable_burst(&s_gdma_hal, ch, GDMA_CHANNEL_DIRECTION_RX, false, false); + s_gdma_hal.enable_intr(&s_gdma_hal, ch, GDMA_CHANNEL_DIRECTION_RX, + ESP32_ADC_GDMA_RX_EOF, true); + + int irq = gdma_periph_signals.groups[ESP32_ADC_GDMA_GROUP].pairs[ch].rx_irq_id; + esp_err_t err = esp_intr_alloc(irq, ESP_INTR_FLAG_IRAM, esp32_adc_gdma_isr, NULL, &s_intr); + if (err != ESP_OK) { + return err; + } + + ctx->on_done = on_done; + ctx->on_done_arg = user; + s_ctx = ctx; + s_inited = true; + return ESP_OK; +} + +esp_err_t esp32_adc_digi_dma_deinit(esp32_adc_digi_dma_ctx_t *ctx) +{ + (void)ctx; + if (!s_inited) { + return ESP_OK; + } + esp32_adc_digi_dma_stop(ctx); + if (s_intr != NULL) { + esp_intr_free(s_intr); + s_intr = NULL; + } + s_ctx = NULL; + s_inited = false; + return ESP_OK; +} + +esp_err_t esp32_adc_digi_dma_start(esp32_adc_digi_dma_ctx_t *ctx, dma_descriptor_t *desc) +{ + (void)ctx; + if (desc == NULL) { + return ESP_ERR_INVALID_ARG; + } + s_gdma_hal.start_with_desc(&s_gdma_hal, ESP32_ADC_GDMA_PAIR, + GDMA_CHANNEL_DIRECTION_RX, (intptr_t)desc); + return ESP_OK; +} + +esp_err_t esp32_adc_digi_dma_stop(esp32_adc_digi_dma_ctx_t *ctx) +{ + (void)ctx; + if (!s_inited) { + return ESP_OK; + } + s_gdma_hal.stop(&s_gdma_hal, ESP32_ADC_GDMA_PAIR, GDMA_CHANNEL_DIRECTION_RX); + return ESP_OK; +} + +esp_err_t esp32_adc_digi_dma_reset(esp32_adc_digi_dma_ctx_t *ctx) +{ + (void)ctx; + if (!s_inited) { + return ESP_OK; + } + s_gdma_hal.reset(&s_gdma_hal, ESP32_ADC_GDMA_PAIR, GDMA_CHANNEL_DIRECTION_RX); + return ESP_OK; +} + +#endif From 7a86f8270d1d1f8f2d43106950dc3400efe7e777 Mon Sep 17 00:00:00 2001 From: Felipe Neves Date: Mon, 1 Jun 2026 21:44:18 -0300 Subject: [PATCH 2/7] Add ETM path to start ADC from MCPWM on ESP32-S3 and newer. Wire MCPWM timer TEZ/TEP to ADC_TASK_START0 so conversions are not kicked from CPU ISRs on chips that lack ETM (ESP32, ESP32-S2). --- .../hardware_specific/esp32/esp32_adc_etm.c | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 src/current_sense/hardware_specific/esp32/esp32_adc_etm.c diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_etm.c b/src/current_sense/hardware_specific/esp32/esp32_adc_etm.c new file mode 100644 index 00000000..03a832c3 --- /dev/null +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_etm.c @@ -0,0 +1,159 @@ +/* + * MIT License + * + * Copyright (c) 2021 Felipe Neves + * + * ETM wiring: MCPWM timer event -> ADC digi START task. + */ + +#include "sdkconfig.h" +#include "esp32_adc_digi_internal.h" + +#if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED + +#include +#include "esp_log.h" +#include "esp_check.h" +#include "esp_etm.h" +#include "esp_heap_caps.h" +#include "esp_private/etm_interface.h" +#include "soc/soc_caps.h" +#include "soc/soc_etm_source.h" +#include "esp32_adc_digi_internal.h" + +static const char *TAG = "esp32_adc_etm"; + +typedef struct { + esp_etm_event_t base; +} esp32_adc_etm_event_t; + +typedef struct { + esp_etm_task_t base; +} esp32_adc_etm_task_t; + +typedef struct { + esp_etm_channel_handle_t chan; + esp_etm_event_handle_t event; + esp_etm_task_handle_t task; + bool connected; + bool enabled; +} esp32_adc_etm_ctx_t; + +static esp32_adc_etm_ctx_t s_etm; + +static esp_err_t esp32_adc_etm_del_event(esp_etm_event_t *event) +{ + esp32_adc_etm_event_t *ev = __containerof(event, esp32_adc_etm_event_t, base); + free(ev); + return ESP_OK; +} + +static esp_err_t esp32_adc_etm_del_task(esp_etm_task_t *task) +{ + esp32_adc_etm_task_t *tk = __containerof(task, esp32_adc_etm_task_t, base); + free(tk); + return ESP_OK; +} + +static uint32_t esp32_adc_mcpwm_event_id(const esp32_adc_digi_etm_config_t *cfg) +{ + switch (cfg->event) { + case SIMPLEFOC_ESP32_ADC_MCPWM_EVT_TIMER_TEZ: + return (uint32_t)(MCPWM_EVT_TIMER0_TEZ + cfg->mcpwm_timer); + case SIMPLEFOC_ESP32_ADC_MCPWM_EVT_TIMER_TEP: + return (uint32_t)(MCPWM_EVT_TIMER0_TEP + cfg->mcpwm_timer); + default: + return 0; + } +} + +static esp_err_t esp32_adc_etm_create_event(uint32_t event_id, esp_etm_event_handle_t *out) +{ + esp32_adc_etm_event_t *ev = heap_caps_calloc(1, sizeof(*ev), MALLOC_CAP_DEFAULT); + ESP_RETURN_ON_FALSE(ev, ESP_ERR_NO_MEM, TAG, "no mem for etm event"); + ev->base.event_id = event_id; + ev->base.trig_periph = ETM_TRIG_PERIPH_MCPWM; + ev->base.del = esp32_adc_etm_del_event; + *out = &ev->base; + return ESP_OK; +} + +static esp_err_t esp32_adc_etm_create_task(uint32_t task_id, esp_etm_task_handle_t *out) +{ + esp32_adc_etm_task_t *tk = heap_caps_calloc(1, sizeof(*tk), MALLOC_CAP_DEFAULT); + ESP_RETURN_ON_FALSE(tk, ESP_ERR_NO_MEM, TAG, "no mem for etm task"); + tk->base.task_id = task_id; + tk->base.trig_periph = ETM_TRIG_PERIPH_MCPWM; + tk->base.del = esp32_adc_etm_del_task; + *out = &tk->base; + return ESP_OK; +} + +static void esp32_adc_etm_teardown(void) +{ + if (s_etm.enabled) { + esp_etm_channel_disable(s_etm.chan); + s_etm.enabled = false; + } + if (s_etm.connected) { + esp_etm_channel_connect(s_etm.chan, NULL, NULL); + s_etm.connected = false; + } + if (s_etm.event) { + esp_etm_del_event(s_etm.event); + s_etm.event = NULL; + } + if (s_etm.task) { + esp_etm_del_task(s_etm.task); + s_etm.task = NULL; + } + if (s_etm.chan) { + esp_etm_del_channel(s_etm.chan); + s_etm.chan = NULL; + } +} + +esp_err_t esp32_adc_digi_etm_connect(const esp32_adc_digi_etm_config_t *cfg) +{ + ESP_RETURN_ON_FALSE(cfg, ESP_ERR_INVALID_ARG, TAG, "cfg is NULL"); + ESP_RETURN_ON_FALSE(cfg->mcpwm_timer < SOC_MCPWM_TIMERS_PER_GROUP, ESP_ERR_INVALID_ARG, TAG, + "invalid mcpwm timer %u", cfg->mcpwm_timer); + + uint32_t event_id = esp32_adc_mcpwm_event_id(cfg); + ESP_RETURN_ON_FALSE(event_id != 0, ESP_ERR_INVALID_ARG, TAG, "invalid mcpwm event"); + + esp32_adc_etm_teardown(); + + ESP_RETURN_ON_ERROR(esp32_adc_etm_create_event(event_id, &s_etm.event), TAG, "event create failed"); + ESP_RETURN_ON_ERROR(esp32_adc_etm_create_task(ADC_TASK_START0, &s_etm.task), TAG, "task create failed"); + + esp_etm_channel_config_t chan_cfg = {}; + ESP_RETURN_ON_ERROR(esp_etm_new_channel(&chan_cfg, &s_etm.chan), TAG, "channel alloc failed"); + ESP_RETURN_ON_ERROR(esp_etm_channel_connect(s_etm.chan, s_etm.event, s_etm.task), TAG, "connect failed"); + s_etm.connected = true; + + ESP_LOGI(TAG, "ETM MCPWM timer%u evt=%u -> ADC_TASK_START0", cfg->mcpwm_timer, (unsigned)cfg->event); + return ESP_OK; +} + +esp_err_t esp32_adc_digi_etm_enable(bool enable) +{ + if (!s_etm.chan || !s_etm.connected) { + return ESP_ERR_INVALID_STATE; + } + if (enable == s_etm.enabled) { + return ESP_OK; + } + esp_err_t err = enable ? esp_etm_channel_enable(s_etm.chan) : esp_etm_channel_disable(s_etm.chan); + if (err == ESP_OK) { + s_etm.enabled = enable; + } + return err; +} + +void esp32_adc_digi_etm_disconnect(void) +{ + esp32_adc_etm_teardown(); +} + +#endif /* SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED */ From 670dd676a5a8fda5f9ad55316c84197d71f922fe Mon Sep 17 00:00:00 2001 From: Felipe Neves Date: Mon, 1 Jun 2026 21:44:18 -0300 Subject: [PATCH 3/7] Add digital ADC controller driver with software and ETM triggers. Configure adc_hal digi pattern/DMA, process frames into a shared buffer, and re-arm after each conversion without blocking MCPWM callbacks. --- .../esp32/esp32_adc_digi_driver.c | 484 ++++++++++++++++++ .../esp32/esp32_adc_digi_driver.h | 44 ++ 2 files changed, 528 insertions(+) create mode 100644 src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.c create mode 100644 src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.h diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.c b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.c new file mode 100644 index 00000000..ca126cba --- /dev/null +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.c @@ -0,0 +1,484 @@ +/* + * MIT License + * + * Copyright (c) 2021 Felipe Neves + * + * ADC digi + DMA for SimpleFOC low-side current sense. + * + * Triggering is selected with esp32_adc_digi_set_trigger(): + * - SOFTWARE: always available (ESP32, S2, S3, …). + * - ETM: ESP32-S3 and newer only; see esp32_adc_etm.c. + */ + +#include "sdkconfig.h" +#include "esp32_adc_digi_internal.h" + +#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED + +#include +#include +#include "esp_log.h" +#include "esp_check.h" +#include "esp_heap_caps.h" +#include "esp_clk_tree.h" +#include "esp_private/regi2c_ctrl.h" +#include "esp_private/sar_periph_ctrl.h" +#include "esp_private/adc_share_hw_ctrl.h" +#include "esp_private/gpio.h" +#include "soc/adc_periph.h" +#include "soc/soc_caps.h" +#include "soc/clk_tree_defs.h" +#include "hal/adc_hal.h" +#include "hal/adc_ll.h" +#include "esp32_adc_digi_driver.h" + +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE +#include "esp_cache.h" +#endif + +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 +#define ADC_GET_CHANNEL(p_data) ((p_data)->type1.channel) +#define ADC_GET_DATA(p_data) ((p_data)->type1.data) +#else +#define ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel) +#define ADC_GET_DATA(p_data) ((p_data)->type2.data) +#endif + +#define ESP32_ADC_DMA_DESC_ALIGN 4 +#define ESP32_ADC_FRAME_BYTES (SIMPLEFOC_ESP32_ADC_NUM_CHANNELS * SOC_ADC_DIGI_RESULT_BYTES) + +typedef enum { + ESP32_ADC_STATE_IDLE = 0, + ESP32_ADC_STATE_BUSY, +} esp32_adc_digi_state_t; + +static const char *TAG = "SF_ESP32_ADC_DIGI"; + +typedef struct { + adc_hal_dma_ctx_t hal; + adc_hal_digi_ctrlr_cfg_t hal_cfg; + adc_digi_pattern_config_t patterns[SIMPLEFOC_ESP32_ADC_NUM_CHANNELS]; + uint8_t *rx_buf; + uint32_t rx_desc_size; + esp32_adc_digi_dma_ctx_t dma_ctx; + adc_channel_t channels[SIMPLEFOC_ESP32_ADC_NUM_CHANNELS]; + adc_unit_t unit; + adc_atten_t atten; + int *adc_buffer; + int no_adc_channels; + esp32_adc_digi_trigger_t trigger; + esp32_adc_digi_state_t state; + bool started; +#if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED + esp32_adc_digi_etm_config_t etm_cfg; +#endif +} esp32_adc_digi_ctx_t; + +DRAM_ATTR static esp32_adc_digi_ctx_t s_adc; +static bool s_adc_initialized; + +static adc_bitwidth_t esp32_adc_digi_bitwidth(void) +{ +#if CONFIG_IDF_TARGET_ESP32 + return ADC_BITWIDTH_12; +#else + return SOC_ADC_DIGI_MAX_BITWIDTH; +#endif +} + +static adc_atten_t esp32_adc_default_atten(void) +{ +#if CONFIG_IDF_TARGET_ESP32 + return ADC_ATTEN_DB_11; +#else + return ADC_ATTEN_DB_12; +#endif +} + +static void esp32_adc_digi_apply_convert_limit(void) +{ + adc_ll_digi_set_convert_limit_num(SIMPLEFOC_ESP32_ADC_CONVERT_LIMIT); + adc_ll_digi_convert_limit_enable(true); +} + +static void esp32_adc_apply_max_clock(esp32_adc_digi_ctx_t *adc) +{ +#if CONFIG_IDF_TARGET_ESP32 + adc_ll_set_sample_cycle(ADC_LL_SAMPLE_CYCLE_DEFAULT); + adc_ll_digi_set_clk_div(2); +#elif CONFIG_IDF_TARGET_ESP32S2 + ESP_ERROR_CHECK(esp_clk_tree_enable_src(SOC_MOD_CLK_APLL, true)); + uint32_t clk_hz = 0; + ESP_ERROR_CHECK(esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_APLL, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_hz)); + adc->hal_cfg.clk_src = ADC_DIGI_CLK_SRC_APLL; + adc->hal_cfg.clk_src_freq_hz = clk_hz; + adc_ll_digi_clk_sel(ADC_DIGI_CLK_SRC_APLL); + adc_ll_digi_controller_clk_div(0, 1, 0); + adc_ll_digi_set_clk_div(1); + adc_ll_set_sample_cycle(ADC_LL_SAMPLE_CYCLE_DEFAULT); +#elif CONFIG_IDF_TARGET_ESP32S3 + ESP_ERROR_CHECK(esp_clk_tree_enable_src(SOC_MOD_CLK_PLL_D2, true)); + uint32_t clk_hz = 0; + ESP_ERROR_CHECK(esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_PLL_D2, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_hz)); + adc->hal_cfg.clk_src = ADC_DIGI_CLK_SRC_PLL_F240M; + adc->hal_cfg.clk_src_freq_hz = clk_hz; + adc_ll_digi_clk_sel(ADC_DIGI_CLK_SRC_PLL_F240M); + adc_ll_digi_controller_clk_div(0, 1, 0); + adc_ll_digi_set_clk_div(1); + adc_ll_set_sample_cycle(ADC_LL_SAMPLE_CYCLE_DEFAULT); +#else + const soc_module_clk_t fast_src = SOC_MOD_CLK_PLL_F80M; + ESP_ERROR_CHECK(esp_clk_tree_enable_src(fast_src, true)); + uint32_t clk_hz = 0; + ESP_ERROR_CHECK(esp_clk_tree_src_get_freq_hz(fast_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_hz)); + adc->hal_cfg.clk_src = ADC_DIGI_CLK_SRC_PLL_F80M; + adc->hal_cfg.clk_src_freq_hz = clk_hz; + adc_ll_digi_clk_sel(ADC_DIGI_CLK_SRC_PLL_F80M); + adc_ll_digi_controller_clk_div(0, 1, 0); + adc_ll_digi_set_clk_div(1); + adc_ll_set_sample_cycle(ADC_LL_SAMPLE_CYCLE_DEFAULT); +#endif + ESP_LOGI(TAG, "ADC fast clock: src=%d freq=%" PRIu32 " Hz", + (int)adc->hal_cfg.clk_src, adc->hal_cfg.clk_src_freq_hz); +} + +static void esp32_adc_rearm(esp32_adc_digi_ctx_t *adc) +{ + esp32_adc_digi_dma_reset(&adc->dma_ctx); + adc_hal_digi_reset(); + adc_hal_digi_dma_link(&adc->hal, adc->rx_buf); +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + esp_cache_msync(adc->hal.rx_desc, adc->rx_desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); +#endif + esp32_adc_digi_dma_start(&adc->dma_ctx, adc->hal.rx_desc); + adc_hal_digi_connect(true); + if (adc->trigger == SIMPLEFOC_ESP32_ADC_TRIG_SOFTWARE) { + adc_hal_digi_enable(true); + } else { + adc_hal_digi_enable(false); + } + adc->state = ESP32_ADC_STATE_BUSY; +} + +static esp_err_t esp32_adc_apply_trigger_mode(esp32_adc_digi_ctx_t *adc) +{ +#if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED + if (adc->trigger == SIMPLEFOC_ESP32_ADC_TRIG_ETM) { + esp_err_t err = esp32_adc_digi_etm_connect(&adc->etm_cfg); + if (err != ESP_OK) { + return err; + } + err = esp32_adc_digi_etm_enable(true); + if (err != ESP_OK) { + return err; + } + adc_hal_digi_enable(false); + return ESP_OK; + } + esp32_adc_digi_etm_enable(false); + esp32_adc_digi_etm_disconnect(); +#else + if (adc->trigger != SIMPLEFOC_ESP32_ADC_TRIG_SOFTWARE) { + return ESP_ERR_NOT_SUPPORTED; + } +#endif + return ESP_OK; +} + +static esp_err_t esp32_adc_gpio_init(adc_unit_t unit, uint32_t chan_mask) +{ + while (chan_mask) { + int ch = __builtin_ctz(chan_mask); + int8_t io = adc_channel_io_map[unit][ch]; + if (io < 0) { + return ESP_ERR_INVALID_ARG; + } + gpio_config_as_analog(io); + chan_mask &= ~(1U << ch); + } + return ESP_OK; +} + +static void IRAM_ATTR esp32_adc_process_frame(esp32_adc_digi_ctx_t *adc, const uint8_t *frame, uint32_t size) +{ + if (frame == NULL || size < ESP32_ADC_FRAME_BYTES || adc->adc_buffer == NULL) { + return; + } + + adc_digi_output_data_t *p = (adc_digi_output_data_t *)frame; + const int n = adc->no_adc_channels < SIMPLEFOC_ESP32_ADC_NUM_CHANNELS + ? adc->no_adc_channels + : SIMPLEFOC_ESP32_ADC_NUM_CHANNELS; + for (int i = 0; i < n; i++) { + adc->adc_buffer[i] = (int)ADC_GET_DATA(p); + p++; + } +} + +static void IRAM_ATTR esp32_adc_dma_done(void *arg) +{ + esp32_adc_digi_ctx_t *adc = (esp32_adc_digi_ctx_t *)arg; + adc_hal_dma_desc_status_t status; + uint8_t *finished_buffer = NULL; + uint32_t finished_size = 0; + + while (1) { + status = adc_hal_get_reading_result(&adc->hal, adc->dma_ctx.eof_desc_addr, + &finished_buffer, &finished_size); + if (status != ADC_HAL_DMA_DESC_VALID) { + break; + } +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + esp_cache_msync(finished_buffer, finished_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); +#endif + esp32_adc_process_frame(adc, finished_buffer, finished_size); + } + +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + esp_cache_msync(adc->hal.rx_desc, adc->rx_desc_size, + ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); +#endif + +#if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED + if (adc->trigger == SIMPLEFOC_ESP32_ADC_TRIG_ETM) { + esp32_adc_rearm(adc); + } else +#endif + { + adc_hal_digi_enable(false); + adc_hal_digi_connect(false); + adc->state = ESP32_ADC_STATE_IDLE; + } +} + +static esp_err_t esp32_adc_hw_start(esp32_adc_digi_ctx_t *adc) +{ + ANALOG_CLOCK_ENABLE(); + + ADC_BUS_CLK_ATOMIC() { + adc_ll_reset_register(); + } + + sar_periph_ctrl_adc_continuous_power_acquire(); + adc_lock_acquire(adc->unit); + +#if SOC_ADC_CALIBRATION_V1_SUPPORTED + adc_hal_calibration_init(adc->unit); + adc_set_hw_calibration_code(adc->unit, adc->atten); +#endif + + adc_hal_set_controller(adc->unit, ADC_HAL_CONTINUOUS_READ_MODE); + +#if !CONFIG_IDF_TARGET_ESP32 + ESP_ERROR_CHECK(esp_clk_tree_enable_src((soc_module_clk_t)adc->hal_cfg.clk_src, true)); +#endif + + adc_hal_digi_init(&adc->hal); + adc_hal_digi_controller_config(&adc->hal, &adc->hal_cfg); + esp32_adc_apply_max_clock(adc); + esp32_adc_digi_apply_convert_limit(); + + esp32_adc_digi_dma_stop(&adc->dma_ctx); + adc->started = true; + esp32_adc_rearm(adc); + return ESP_OK; +} + +static esp_err_t esp32_adc_setup_hal(esp32_adc_digi_ctx_t *adc) +{ + uint32_t clk_hz = 0; + esp_clk_tree_src_get_freq_hz(ADC_DIGI_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_hz); + + adc->hal_cfg.adc_pattern = adc->patterns; + adc->hal_cfg.adc_pattern_len = SIMPLEFOC_ESP32_ADC_NUM_CHANNELS; + adc->hal_cfg.sample_freq_hz = SIMPLEFOC_ESP32_ADC_PATTERN_HZ; + adc->hal_cfg.conv_mode = ADC_CONV_SINGLE_UNIT_1; + adc->hal_cfg.clk_src = ADC_DIGI_CLK_SRC_DEFAULT; + adc->hal_cfg.clk_src_freq_hz = clk_hz; + + uint32_t chan_mask = 0; + for (int i = 0; i < SIMPLEFOC_ESP32_ADC_NUM_CHANNELS; i++) { + adc->patterns[i].atten = adc->atten; + adc->patterns[i].channel = adc->channels[i] & 0x7; + adc->patterns[i].unit = adc->unit; + adc->patterns[i].bit_width = esp32_adc_digi_bitwidth(); + chan_mask |= BIT(adc->patterns[i].channel); + ESP_LOGI(TAG, "pattern[%d] unit=%d ch=%d atten=%d", i, + (int)adc->patterns[i].unit, (int)adc->patterns[i].channel, + (int)adc->patterns[i].atten); + } + + adc_hal_dma_config_t dma_cfg = { + .eof_desc_num = 1, + .eof_step = 1, + .eof_num = SIMPLEFOC_ESP32_ADC_NUM_CHANNELS, + }; + adc_hal_dma_ctx_config(&adc->hal, &dma_cfg); + + adc->rx_buf = heap_caps_calloc(1, ESP32_ADC_FRAME_BYTES, + MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT); + if (adc->rx_buf == NULL) { + return ESP_ERR_NO_MEM; + } + + adc->hal.rx_desc = heap_caps_aligned_calloc(ESP32_ADC_DMA_DESC_ALIGN, 1, + sizeof(dma_descriptor_t), + MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT); + if (adc->hal.rx_desc == NULL) { + return ESP_ERR_NO_MEM; + } + +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + uint32_t line = 4; + adc->rx_desc_size = (sizeof(dma_descriptor_t) + line - 1) & ~(line - 1); +#else + adc->rx_desc_size = sizeof(dma_descriptor_t); +#endif + + ESP_RETURN_ON_ERROR(esp32_adc_gpio_init(adc->unit, chan_mask), TAG, "gpio init failed"); + ESP_RETURN_ON_ERROR(esp32_adc_digi_dma_init(&adc->dma_ctx, esp32_adc_dma_done, adc), + TAG, "dma init failed"); + return ESP_OK; +} + +bool esp32_adc_digi_supported(void) +{ + return true; +} + +#if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED +bool esp32_adc_digi_etm_supported(void) +{ + return true; +} +#endif + +esp_err_t esp32_adc_digi_init(const esp32_adc_digi_config_t *cfg) +{ + if (cfg == NULL || cfg->adc_buffer == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (cfg->unit != ADC_UNIT_1) { + ESP_LOGE(TAG, "only ADC1 supported"); + return ESP_ERR_NOT_SUPPORTED; + } + if (cfg->no_adc_channels < 1 || cfg->no_adc_channels > SIMPLEFOC_ESP32_ADC_NUM_CHANNELS) { + ESP_LOGE(TAG, "need 1..%d ADC channels for hw trigger", SIMPLEFOC_ESP32_ADC_NUM_CHANNELS); + return ESP_ERR_INVALID_ARG; + } + if (s_adc_initialized) { + return ESP_OK; + } + + memset(&s_adc, 0, sizeof(s_adc)); + s_adc.unit = cfg->unit; + s_adc.atten = esp32_adc_default_atten(); + s_adc.channels[0] = cfg->channels[0]; + s_adc.channels[1] = cfg->channels[1]; + s_adc.adc_buffer = cfg->adc_buffer; + s_adc.no_adc_channels = cfg->no_adc_channels; + s_adc.trigger = SIMPLEFOC_ESP32_ADC_TRIG_SOFTWARE; + s_adc.state = ESP32_ADC_STATE_IDLE; +#if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED + s_adc.etm_cfg.mcpwm_timer = 0; + s_adc.etm_cfg.event = SIMPLEFOC_ESP32_ADC_MCPWM_EVT_TIMER_TEZ; +#endif + + adc_apb_periph_claim(); + + esp_err_t err = esp32_adc_setup_hal(&s_adc); + if (err != ESP_OK) { + ESP_LOGE(TAG, "ADC HAL setup failed: %d", err); + return err; + } + + err = esp32_adc_hw_start(&s_adc); + if (err != ESP_OK) { + return err; + } + + s_adc_initialized = true; + return ESP_OK; +} + +esp_err_t esp32_adc_digi_deinit(void) +{ + if (!s_adc_initialized) { + return ESP_OK; + } + esp32_adc_digi_set_trigger(SIMPLEFOC_ESP32_ADC_TRIG_SOFTWARE); + esp32_adc_digi_dma_stop(&s_adc.dma_ctx); + esp32_adc_digi_dma_deinit(&s_adc.dma_ctx); + adc_hal_digi_deinit(&s_adc.hal); + adc_lock_release(s_adc.unit); + sar_periph_ctrl_adc_continuous_power_release(); + if (s_adc.rx_buf) { + heap_caps_free(s_adc.rx_buf); + s_adc.rx_buf = NULL; + } + if (s_adc.hal.rx_desc) { + heap_caps_free(s_adc.hal.rx_desc); + s_adc.hal.rx_desc = NULL; + } + s_adc_initialized = false; + return ESP_OK; +} + +esp_err_t esp32_adc_digi_set_trigger(esp32_adc_digi_trigger_t mode) +{ + if (!s_adc_initialized) { + return ESP_ERR_INVALID_STATE; + } +#if !SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED + if (mode != SIMPLEFOC_ESP32_ADC_TRIG_SOFTWARE) { + return ESP_ERR_NOT_SUPPORTED; + } +#endif + s_adc.trigger = mode; + esp_err_t err = esp32_adc_apply_trigger_mode(&s_adc); + if (err != ESP_OK) { + s_adc.trigger = SIMPLEFOC_ESP32_ADC_TRIG_SOFTWARE; + return err; + } + if (s_adc.started && s_adc.state != ESP32_ADC_STATE_BUSY) { + esp32_adc_rearm(&s_adc); + } + return ESP_OK; +} + +#if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED +esp_err_t esp32_adc_digi_set_etm_source(const esp32_adc_digi_etm_config_t *cfg) +{ + if (!s_adc_initialized || cfg == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (cfg->mcpwm_timer >= SOC_MCPWM_TIMERS_PER_GROUP) { + return ESP_ERR_INVALID_ARG; + } + s_adc.etm_cfg = *cfg; + if (s_adc.trigger == SIMPLEFOC_ESP32_ADC_TRIG_ETM) { + return esp32_adc_apply_trigger_mode(&s_adc); + } + return ESP_OK; +} +#endif /* SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED */ + +esp_err_t esp32_adc_digi_trigger_software(void) +{ + if (!s_adc_initialized) { + return ESP_ERR_INVALID_STATE; + } + if (s_adc.trigger != SIMPLEFOC_ESP32_ADC_TRIG_SOFTWARE) { + return ESP_ERR_INVALID_STATE; + } + if (s_adc.state == ESP32_ADC_STATE_BUSY) { + return ESP_OK; + } + if (!s_adc.started) { + return esp32_adc_hw_start(&s_adc); + } + esp32_adc_rearm(&s_adc); + return ESP_OK; +} + +#endif diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.h b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.h new file mode 100644 index 00000000..59be2f1c --- /dev/null +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.h @@ -0,0 +1,44 @@ +/* + * MIT License + * + * Copyright (c) 2021 Felipe Neves + * + * Digital ADC + DMA driver for SimpleFOC ESP32 low-side current sense. + * See esp32_adc_digi_internal.h for chip-specific trigger/DMA behaviour. + */ +#pragma once + +#include "sdkconfig.h" +#include "esp_err.h" +#include "hal/adc_types.h" +#include "esp32_adc_digi_internal.h" + +#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED + +typedef struct { + adc_channel_t channels[SIMPLEFOC_ESP32_ADC_NUM_CHANNELS]; + adc_unit_t unit; + int *adc_buffer; + int no_adc_channels; +} esp32_adc_digi_config_t; + +typedef enum { + SIMPLEFOC_ESP32_ADC_TRIG_SOFTWARE = 0, +#if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED + SIMPLEFOC_ESP32_ADC_TRIG_ETM, +#endif +} esp32_adc_digi_trigger_t; + +esp_err_t esp32_adc_digi_init(const esp32_adc_digi_config_t *cfg); +esp_err_t esp32_adc_digi_deinit(void); +esp_err_t esp32_adc_digi_set_trigger(esp32_adc_digi_trigger_t mode); +esp_err_t esp32_adc_digi_trigger_software(void); + +bool esp32_adc_digi_supported(void); + +#if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED +esp_err_t esp32_adc_digi_set_etm_source(const esp32_adc_digi_etm_config_t *cfg); +bool esp32_adc_digi_etm_supported(void); +#endif + +#endif /* SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED */ From 723adda4e7c0ed0cfb4171357e06f359c8bf9f72 Mon Sep 17 00:00:00 2001 From: Felipe Neves Date: Mon, 1 Jun 2026 21:44:18 -0300 Subject: [PATCH 4/7] Add low-side glue between SimpleFOC and the digi ADC driver. Select LEGACY, DIGI_SW, or DIGI_ETM paths and keep MCPWM sync logic out of the core driver files. --- .../esp32/esp32_adc_digi_lowside.cpp | 206 ++++++++++++++++++ .../esp32/esp32_adc_digi_lowside.h | 21 ++ 2 files changed, 227 insertions(+) create mode 100644 src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.cpp create mode 100644 src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.h diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.cpp b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.cpp new file mode 100644 index 00000000..ab1bbf06 --- /dev/null +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.cpp @@ -0,0 +1,206 @@ +#include "esp32_adc_digi_lowside.h" + +#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED && defined(SOC_MCPWM_SUPPORTED) && !defined(SIMPLEFOC_ESP32_USELEDC) + +#include "esp32_adc_digi_driver.h" +#include "../../../drivers/hardware_specific/esp32/esp32_driver_mcpwm.h" +#include "../../../drivers/hardware_specific/esp32/mcpwm_private.h" +#include "driver/mcpwm_prelude.h" + +#ifndef SIMPLEFOC_CS_PRETRIGGER_US +#define SIMPLEFOC_CS_PRETRIGGER_US 5 +#endif + +static bool esp32_pin_to_adc1_channel(int pin, adc_channel_t *out_ch) +{ + int8_t ch = digitalPinToAnalogChannel(pin); + if (ch < 0 || ch >= SOC_ADC_MAX_CHANNEL_NUM) { + return false; + } + *out_ch = (adc_channel_t)ch; + return true; +} + +static esp_err_t esp32_adc_digi_bind_params(ESP32CurrentSenseParams *params) +{ + if (params->no_adc_channels < 1 || params->no_adc_channels > SIMPLEFOC_ESP32_ADC_NUM_CHANNELS) { + return ESP_ERR_INVALID_ARG; + } + + adc_channel_t channels[SIMPLEFOC_ESP32_ADC_NUM_CHANNELS] = {}; + for (int i = 0; i < params->no_adc_channels; i++) { + if (!esp32_pin_to_adc1_channel(params->pins[i], &channels[i])) { + return ESP_ERR_INVALID_ARG; + } + } + + esp32_adc_digi_config_t cfg = { + .channels = { channels[0], channels[1] }, + .unit = ADC_UNIT_1, + .adc_buffer = params->adc_buffer, + .no_adc_channels = params->no_adc_channels, + }; + + return esp32_adc_digi_init(&cfg); +} + +ESP32AdcLowsidePath esp32_adc_lowside_configure(ESP32CurrentSenseParams *params) +{ + if (params == NULL || !esp32_adc_digi_supported()) { + return ESP32_ADC_LOWSIDE_LEGACY; + } + + if (esp32_adc_digi_bind_params(params) != ESP_OK) { + SIMPLEFOC_ESP32_CS_DEBUG("WARN: ADC digi+DMA init failed, using legacy adcRead path"); + return ESP32_ADC_LOWSIDE_LEGACY; + } + + params->adc_lowside_path = ESP32_ADC_LOWSIDE_DIGI_SW; + SIMPLEFOC_ESP32_CS_DEBUG("ADC digi+DMA ready (software trigger via MCPWM ISR on ESP32/S2)"); + return ESP32_ADC_LOWSIDE_DIGI_SW; +} + +bool esp32_adc_lowside_uses_mcpwm_isr(const ESP32CurrentSenseParams *params) +{ + if (params == NULL) { + return false; + } + return params->adc_lowside_path == ESP32_ADC_LOWSIDE_DIGI_SW; +} + +void esp32_adc_lowside_start_conversion(void) +{ + esp32_adc_digi_trigger_software(); +} + +#if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED +static void *esp32_adc_lowside_sync_etm(void *driver_params, ESP32CurrentSenseParams *cs) +{ + ESP32MCPWMDriverParams *p = (ESP32MCPWMDriverParams *)driver_params; + mcpwm_timer_t *t = (mcpwm_timer_t *)p->timers[0]; + int group_id = p->group_id; + + esp32_adc_digi_etm_config_t etm = { + .mcpwm_timer = (uint8_t)t->timer_id, + .event = SIMPLEFOC_ESP32_ADC_MCPWM_EVT_TIMER_TEZ, + }; + + if (esp32_adc_digi_set_etm_source(&etm) != ESP_OK) { + SIMPLEFOC_ESP32_CS_DEBUG("ERROR: ETM source setup failed"); + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; + } + if (esp32_adc_digi_set_trigger(SIMPLEFOC_ESP32_ADC_TRIG_ETM) != ESP_OK) { + SIMPLEFOC_ESP32_CS_DEBUG("ERROR: ETM ADC trigger enable failed"); + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; + } + + cs->adc_lowside_path = ESP32_ADC_LOWSIDE_DIGI_ETM; + SIMPLEFOC_ESP32_CS_DEBUG("MCPWM" + String(group_id) + " timer " + String(t->timer_id) + + " -> ETM -> ADC (TEZ), no MCPWM ADC ISR"); + return cs; +} +#endif + +static bool IRAM_ATTR esp32_adc_mcpwm_sw_trigger_cb(mcpwm_cmpr_handle_t cmpr, + const mcpwm_compare_event_data_t *edata, + void *user_data) +{ + (void)cmpr; + (void)user_data; + if (edata->direction != MCPWM_TIMER_DIRECTION_UP) { + return true; + } + esp32_adc_digi_trigger_software(); + return true; +} + +static bool IRAM_ATTR esp32_adc_mcpwm_sw_trigger_timer_cb(mcpwm_timer_handle_t tim, + const mcpwm_timer_event_data_t *edata, + void *user_data) +{ + (void)tim; + (void)edata; + (void)user_data; + esp32_adc_digi_trigger_software(); + return true; +} + +static void *esp32_adc_lowside_sync_mcpwm_sw(void *driver_params, ESP32CurrentSenseParams *cs) +{ + ESP32MCPWMDriverParams *p = (ESP32MCPWMDriverParams *)driver_params; + mcpwm_timer_t *t = (mcpwm_timer_t *)p->timers[0]; + int group_id = p->group_id; + + SIMPLEFOC_ESP32_CS_DEBUG("MCPWM comparator -> software ADC digi trigger (ESP32/S2 style)"); + + mcpwm_comparator_config_t cmp_config = {}; + cmp_config.flags.update_cmp_on_tez = true; + for (int i = 2; i >= 0; i--) { + if (p->oper[i] == nullptr) { + continue; + } + if (mcpwm_new_comparator(p->oper[i], &cmp_config, (mcpwm_cmpr_handle_t *)&cs->pretrig_comparator) == ESP_OK) { + break; + } + } + + if (cs->pretrig_comparator) { + uint32_t pwm_duty_cycle = p->mcpwm_period * (0.75f - ((float)p->pwm_frequency * SIMPLEFOC_CS_PRETRIGGER_US) / 1e6f / 2.0f); + if (mcpwm_comparator_set_compare_value((mcpwm_cmpr_handle_t)cs->pretrig_comparator, pwm_duty_cycle) != ESP_OK) { + SIMPLEFOC_ESP32_CS_DEBUG("ERROR: comparator compare value"); + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; + } + mcpwm_comparator_event_callbacks_t cmp_cbs = { + .on_reach = esp32_adc_mcpwm_sw_trigger_cb, + }; + if (mcpwm_comparator_register_event_callbacks((mcpwm_cmpr_handle_t)cs->pretrig_comparator, &cmp_cbs, cs) != ESP_OK) { + SIMPLEFOC_ESP32_CS_DEBUG("ERROR: comparator callback"); + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; + } + _notifyLowSideUsingComparator(group_id); + SIMPLEFOC_ESP32_CS_DEBUG("Comparator pre-trigger -> esp32_adc_digi_trigger_software()"); + return cs; + } + + SIMPLEFOC_ESP32_CS_DEBUG("WARN: no comparator; MCPWM on_full -> software ADC digi trigger"); + if (t->on_full != nullptr) { + SIMPLEFOC_ESP32_CS_DEBUG("ERROR: timer on_full already in use"); + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; + } + + auto cbs = mcpwm_timer_event_callbacks_t{ + .on_full = esp32_adc_mcpwm_sw_trigger_timer_cb, + }; + t->fsm = MCPWM_TIMER_FSM_INIT; + if (mcpwm_timer_register_event_callbacks(t, &cbs, cs) != ESP_OK) { + SIMPLEFOC_ESP32_CS_DEBUG("ERROR: timer callback"); + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; + } + t->fsm = MCPWM_TIMER_FSM_ENABLE; + if (esp_intr_enable(t->intr) != ESP_OK) { + SIMPLEFOC_ESP32_CS_DEBUG("ERROR: enable timer intr"); + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; + } + return cs; +} + +void *esp32_adc_lowside_sync_mcpwm(void *driver_params, ESP32CurrentSenseParams *cs) +{ + if (cs == NULL || driver_params == NULL) { + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; + } + +#if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED + if (esp32_adc_digi_etm_supported() && cs->adc_lowside_path != ESP32_ADC_LOWSIDE_LEGACY) { + return esp32_adc_lowside_sync_etm(driver_params, cs); + } +#endif + + if (cs->adc_lowside_path == ESP32_ADC_LOWSIDE_DIGI_SW) { + return esp32_adc_lowside_sync_mcpwm_sw(driver_params, cs); + } + + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; +} + +#endif diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.h b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.h new file mode 100644 index 00000000..cafcf67c --- /dev/null +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.h @@ -0,0 +1,21 @@ +/* + * MIT License + * + * Copyright (c) 2021 Felipe Neves + * + * Glue between SimpleFOC LowsideCurrentSense (ESP32 MCPWM) and esp32_adc_digi_*. + */ +#pragma once + +#include "esp32_adc_digi_internal.h" + +#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED && defined(SOC_MCPWM_SUPPORTED) && !defined(SIMPLEFOC_ESP32_USELEDC) + +#include "esp32_mcu.h" + +ESP32AdcLowsidePath esp32_adc_lowside_configure(ESP32CurrentSenseParams *params); +bool esp32_adc_lowside_uses_mcpwm_isr(const ESP32CurrentSenseParams *params); +void *esp32_adc_lowside_sync_mcpwm(void *driver_params, ESP32CurrentSenseParams *cs); +void esp32_adc_lowside_start_conversion(void); + +#endif From a28ff38696753ece42bcd9bdeb550f6844a31df7 Mon Sep 17 00:00:00 2001 From: Felipe Neves Date: Mon, 1 Jun 2026 21:44:18 -0300 Subject: [PATCH 5/7] Integrate digi ADC low-side path into ESP32 MCPWM current sense. Prefer digi+DMA on init; use ETM on S3/C6 or MCPWM ISR software trigger on ESP32/S2, with legacy adcRead() as fallback. --- .../esp32/esp32_mcpwm_mcu.cpp | 342 +++++++++--------- .../hardware_specific/esp32/esp32_mcu.h | 16 +- 2 files changed, 179 insertions(+), 179 deletions(-) diff --git a/src/current_sense/hardware_specific/esp32/esp32_mcpwm_mcu.cpp b/src/current_sense/hardware_specific/esp32/esp32_mcpwm_mcu.cpp index 41ded6f2..08985e68 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_mcpwm_mcu.cpp +++ b/src/current_sense/hardware_specific/esp32/esp32_mcpwm_mcu.cpp @@ -2,7 +2,6 @@ #if defined(ESP_H) && defined(ARDUINO_ARCH_ESP32) && defined(SOC_MCPWM_SUPPORTED) && !defined(SIMPLEFOC_ESP32_USELEDC) -// check the version of the ESP-IDF #include "esp_idf_version.h" #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) @@ -11,21 +10,21 @@ #include "../../../drivers/hardware_specific/esp32/esp32_driver_mcpwm.h" #include "../../../drivers/hardware_specific/esp32/mcpwm_private.h" - #include "driver/mcpwm_prelude.h" #include "soc/mcpwm_reg.h" #include "soc/mcpwm_struct.h" +#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED +#include "esp32_adc_digi_lowside.h" +#endif - -// adding a debug toggle pin to measure the time of the interrupt with oscilloscope // #define SIMPLEFOC_ESP32_INTERRUPT_DEBUG #ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG #include "driver/gpio.h" #ifndef DEBUGPIN -#ifdef CONFIG_IDF_TARGET_ESP32S3 +#ifdef CONFIG_IDF_TARGET_ESP32S3 #define DEBUGPIN 16 #else #define DEBUGPIN 19 @@ -35,212 +34,199 @@ #define GPIO_NUM (gpio_num_t)((int)GPIO_NUM_0 + DEBUGPIN) #endif -#ifndef SIMPLEFOC_CS_PRETRIGGER_US -#define SIMPLEFOC_CS_PRETRIGGER_US 5 // 5 us because ADC read takes around 10us -#endif - - - /** - * Low side adc reading implementation -*/ - - -// function reading an ADC value and returning the read voltage -float IRAM_ATTR _readADCVoltageLowSide(const int pin, const void* cs_params){ - ESP32CurrentSenseParams* p = (ESP32CurrentSenseParams*)cs_params; - int no_channel = 0; - for(int i=0; i < 3; i++){ - if(!_isset(p->pins[i])) continue; - if(pin == p->pins[i]) // found in the buffer - return p->adc_buffer[no_channel] * p->adc_voltage_conv; - else no_channel++; - } - SIMPLEFOC_DEBUG("ERROR: ADC pin not found in the buffer!"); - // not found - return 0; + * Low-side current sense on ESP32 MCPWM. + * + * ADC path (see esp32_adc_digi_internal.h): + * - LEGACY: MCPWM ISR calls adcRead() one phase per interrupt (~10 us each). + * - DIGI_SW: ESP32 / S2 — digi+DMA; ISR only calls esp32_adc_digi_trigger_software(). + * - DIGI_ETM: S3+ — same digi+DMA; MCPWM TEZ starts ADC via ETM (no ADC ISR). + */ + +float IRAM_ATTR _readADCVoltageLowSide(const int pin, const void *cs_params) +{ + ESP32CurrentSenseParams *p = (ESP32CurrentSenseParams *)cs_params; + int no_channel = 0; + for (int i = 0; i < 3; i++) { + if (!_isset(p->pins[i])) { + continue; + } + if (pin == p->pins[i]) { + return p->adc_buffer[no_channel] * p->adc_voltage_conv; + } + no_channel++; + } + SIMPLEFOC_DEBUG("ERROR: ADC pin not found in the buffer!"); + return 0; } +void *IRAM_ATTR _configureADCLowSide(const void *driver_params, const int pinA, const int pinB, const int pinC) +{ + ESP32MCPWMDriverParams *p = (ESP32MCPWMDriverParams *)driver_params; + mcpwm_timer_t *t = (mcpwm_timer_t *)p->timers[0]; -// function configuring low-side current sensing -void* IRAM_ATTR _configureADCLowSide(const void* driver_params, const int pinA,const int pinB,const int pinC){ - // check if driver timer is already running - // fail if it is - // the easiest way that I've found to check if timer is running - // is to start it and stop it - ESP32MCPWMDriverParams *p = (ESP32MCPWMDriverParams*)driver_params; - mcpwm_timer_t* t = (mcpwm_timer_t*) p->timers[0]; - - // check if low side callback is already set - // if it is, return error - if(t->on_full != nullptr){ - SIMPLEFOC_ESP32_CS_DEBUG("ERROR: Low side callback is already set. Cannot set it again for timer: "+String(t->timer_id)+", group: "+String(t->group->group_id)); - return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; - } - - - ESP32CurrentSenseParams* params = new ESP32CurrentSenseParams{}; - int no_adc_channels = 0; - - // initialize the ADC pins - // fail if the pin is not an ADC pin - int adc_pins[3] = {pinA, pinB, pinC}; - for (int i = 0; i < 3; i++){ - if(_isset(adc_pins[i])){ - if(!adcInit(adc_pins[i])){ - SIMPLEFOC_ESP32_CS_DEBUG("ERROR: Failed to initialise ADC pin: "+String(adc_pins[i]) + String(", maybe not an ADC pin?")); + if (t->on_full != nullptr) { + SIMPLEFOC_ESP32_CS_DEBUG("ERROR: Low side callback is already set. Cannot set it again for timer: " + + String(t->timer_id) + ", group: " + String(t->group->group_id)); return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; - } - params->pins[no_adc_channels++] = adc_pins[i]; } - } - - t->user_data = params; - params->adc_voltage_conv = (_ADC_VOLTAGE)/(_ADC_RESOLUTION); - params->no_adc_channels = no_adc_channels; - return params; -} - -static bool IRAM_ATTR _mcpwmTriggerADCCallback(mcpwm_timer_handle_t tim, const mcpwm_timer_event_data_t* edata, void* user_data){ - ESP32CurrentSenseParams *p = (ESP32CurrentSenseParams*)user_data; -#ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG // debugging toggle pin to measure the time of the interrupt with oscilloscope - gpio_set_level(GPIO_NUM,1); //cca 250ns for on+off -#endif - // sample the phase currents one at a time - // ESP's adc read takes around 10us which is very long - // so we are sampling one phase per call - p->adc_buffer[p->buffer_index] = adcRead(p->pins[p->buffer_index]); + ESP32CurrentSenseParams *params = new ESP32CurrentSenseParams{}; + int no_adc_channels = 0; + + int adc_pins[3] = { pinA, pinB, pinC }; + for (int i = 0; i < 3; i++) { + if (_isset(adc_pins[i])) { + if (!adcInit(adc_pins[i])) { + SIMPLEFOC_ESP32_CS_DEBUG("ERROR: Failed to initialise ADC pin: " + String(adc_pins[i]) + + ", maybe not an ADC pin?"); + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; + } + params->pins[no_adc_channels++] = adc_pins[i]; + } + } - // increment buffer index - p->buffer_index++; - if(p->buffer_index >= p->no_adc_channels){ - p->buffer_index = 0; - } + params->adc_voltage_conv = (_ADC_VOLTAGE) / (_ADC_RESOLUTION); + params->no_adc_channels = no_adc_channels; -#ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG // debugging toggle pin to measure the time of the interrupt with oscilloscope - gpio_set_level(GPIO_NUM,0); //cca 250ns for on+off +#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED + if (esp32_adc_lowside_configure(params) != ESP32_ADC_LOWSIDE_LEGACY) { + t->user_data = params; + return params; + } #endif - return true; -} -// Comparator on_reach callback: sample ADC pre-trigger -// In center-aligned mode, comparator fires twice per cycle (up and down) -// Only sample on down-count to get one sample per PWM period -static bool IRAM_ATTR _mcpwmComparatorADCCallback(mcpwm_cmpr_handle_t cmpr, const mcpwm_compare_event_data_t* edata, void* user_data){ - // Only trigger on down-count direction - if(edata->direction != MCPWM_TIMER_DIRECTION_UP){ - return true; - } + t->user_data = params; + return params; +} - ESP32CurrentSenseParams *p = (ESP32CurrentSenseParams*)user_data; +static bool IRAM_ATTR _mcpwm_legacy_adc_callback(mcpwm_timer_handle_t tim, const mcpwm_timer_event_data_t *edata, + void *user_data) +{ + (void)tim; + (void)edata; + ESP32CurrentSenseParams *p = (ESP32CurrentSenseParams *)user_data; #ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG - gpio_set_level(GPIO_NUM,1); + gpio_set_level(GPIO_NUM, 1); #endif - - // sample the phase currents one at a time - // ESP's adc read takes around 10us which is very long - // so we are sampling one phase per call - p->adc_buffer[p->buffer_index] = adcRead(p->pins[p->buffer_index]); - - // increment buffer index - p->buffer_index++; - if(p->buffer_index >= p->no_adc_channels){ - p->buffer_index = 0; - } - + p->adc_buffer[p->buffer_index] = adcRead(p->pins[p->buffer_index]); + p->buffer_index++; + if (p->buffer_index >= p->no_adc_channels) { + p->buffer_index = 0; + } #ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG - gpio_set_level(GPIO_NUM,0); + gpio_set_level(GPIO_NUM, 0); #endif - return true; + return true; } -void* IRAM_ATTR _driverSyncLowSide(void* driver_params, void* cs_params){ +static bool IRAM_ATTR _mcpwm_legacy_comparator_adc_callback(mcpwm_cmpr_handle_t cmpr, + const mcpwm_compare_event_data_t *edata, void *user_data) +{ + if (edata->direction != MCPWM_TIMER_DIRECTION_UP) { + return true; + } + ESP32CurrentSenseParams *p = (ESP32CurrentSenseParams *)user_data; #ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG - pinMode(DEBUGPIN, OUTPUT); + gpio_set_level(GPIO_NUM, 1); #endif - ESP32MCPWMDriverParams *p = (ESP32MCPWMDriverParams*)driver_params; - mcpwm_timer_t* t = (mcpwm_timer_t*) p->timers[0]; - int group_id = p->group_id; - - - ESP32CurrentSenseParams *cs = (ESP32CurrentSenseParams*)cs_params; - if(!cs){ - SIMPLEFOC_ESP32_CS_DEBUG("ERROR: cs_params is null"); - return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; - } - - SIMPLEFOC_ESP32_CS_DEBUG("Configuring sync with comparator..."); - - // Create a spare comparator on the first operator for ADC pre-trigger - // This comparator will fire ~5 µs before on_reach (peak) - mcpwm_comparator_config_t cmp_config = {0}; - cmp_config.flags.update_cmp_on_tez = true; - for(int i=2; i>=0; i--){ // start from the end as first operators are more likely to be used fully - if(p->oper[i] == nullptr) continue; - if(mcpwm_new_comparator(p->oper[i], &cmp_config, (mcpwm_cmpr_handle_t*)&cs->pretrig_comparator) == ESP_OK){ - break; + p->adc_buffer[p->buffer_index] = adcRead(p->pins[p->buffer_index]); + p->buffer_index++; + if (p->buffer_index >= p->no_adc_channels) { + p->buffer_index = 0; } - } - - // if comparator creation failed, fall back to on_full callback - if (cs->pretrig_comparator){ - // Calculate pwm duty cycle ticks for pre-trigger channel - // TODO: verify the timing it seems to be correct between 15 and 20kHz (but needs better testing) - uint32_t pwm_duty_cycle = p->mcpwm_period * (0.75f - ((float)p->pwm_frequency*SIMPLEFOC_CS_PRETRIGGER_US)/1e6f/2.0f); - // set up the comparator duty cycle - CHECK_CS_ERR(mcpwm_comparator_set_compare_value((mcpwm_cmpr_handle_t)cs->pretrig_comparator, pwm_duty_cycle), - "Failed to set pretrigger compare value"); - - // Register comparator on_reach callback for ADC sampling - mcpwm_comparator_event_callbacks_t cmp_cbs = { - .on_reach = _mcpwmComparatorADCCallback - }; - // register the callback - CHECK_CS_ERR(mcpwm_comparator_register_event_callbacks((mcpwm_cmpr_handle_t)cs->pretrig_comparator, &cmp_cbs, cs_params), - "Failed to register comparator callback"); +#ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG + gpio_set_level(GPIO_NUM, 0); +#endif + return true; +} + +void IRAM_ATTR _startADC3PinConversionLowSide() +{ +#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED + esp32_adc_lowside_start_conversion(); +#endif +} + +static void *esp32_mcpwm_sync_legacy_lowside(void *driver_params, ESP32CurrentSenseParams *cs) +{ + ESP32MCPWMDriverParams *p = (ESP32MCPWMDriverParams *)driver_params; + mcpwm_timer_t *t = (mcpwm_timer_t *)p->timers[0]; + int group_id = p->group_id; + + SIMPLEFOC_ESP32_CS_DEBUG("Legacy path: MCPWM ISR + adcRead() (one phase per interrupt)"); - SIMPLEFOC_ESP32_CS_DEBUG("MCPWM"+String(group_id)+" Timer "+String(t->timer_id)+" pretrigger comparator configured."); - // notify the driver code that low side is uses one comparator - _notifyLowSideUsingComparator(group_id); +#ifndef SIMPLEFOC_CS_PRETRIGGER_US +#define SIMPLEFOC_CS_PRETRIGGER_US 5 +#endif - }else{ + mcpwm_comparator_config_t cmp_config = {}; + cmp_config.flags.update_cmp_on_tez = true; + for (int i = 2; i >= 0; i--) { + if (p->oper[i] == nullptr) { + continue; + } + if (mcpwm_new_comparator(p->oper[i], &cmp_config, (mcpwm_cmpr_handle_t *)&cs->pretrig_comparator) == ESP_OK) { + break; + } + } - SIMPLEFOC_ESP32_CS_DEBUG("WARN: Failed to create comparator!"); - SIMPLEFOC_ESP32_CS_DEBUG("Configuring sync with on_full callback (less accurate)..."); + if (cs->pretrig_comparator) { + uint32_t pwm_duty_cycle = + p->mcpwm_period * (0.75f - ((float)p->pwm_frequency * SIMPLEFOC_CS_PRETRIGGER_US) / 1e6f / 2.0f); + CHECK_CS_ERR(mcpwm_comparator_set_compare_value((mcpwm_cmpr_handle_t)cs->pretrig_comparator, pwm_duty_cycle), + "Failed to set pretrigger compare value"); + + mcpwm_comparator_event_callbacks_t cmp_cbs = { + .on_reach = _mcpwm_legacy_comparator_adc_callback, + }; + CHECK_CS_ERR(mcpwm_comparator_register_event_callbacks((mcpwm_cmpr_handle_t)cs->pretrig_comparator, &cmp_cbs, + cs), + "Failed to register comparator callback"); + _notifyLowSideUsingComparator(group_id); + SIMPLEFOC_ESP32_CS_DEBUG("MCPWM" + String(group_id) + " timer " + String(t->timer_id) + " comparator + adcRead"); + return cs; + } - // check if low side callback is already set - // if it is, return error - if(t->on_full != nullptr){ - SIMPLEFOC_ESP32_CS_DEBUG("ERROR: Low side callback is already set. Cannot set it again for timer: "+String(t->timer_id)+", group: "+String(t->group->group_id)); - return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; + SIMPLEFOC_ESP32_CS_DEBUG("WARN: comparator unavailable; MCPWM on_full + adcRead"); + if (t->on_full != nullptr) { + SIMPLEFOC_ESP32_CS_DEBUG("ERROR: on_full already set"); + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; } - // set the callback for the low side current sensing - // mcpwm_timer_event_callbacks_t can be used to set the callback - // for three timer events - // - on_full - low-side - // - on_empty - high-side - // - on_sync - sync event (not used with simplefoc) + auto cbs = mcpwm_timer_event_callbacks_t{ - .on_full = _mcpwmTriggerADCCallback, + .on_full = _mcpwm_legacy_adc_callback, }; - SIMPLEFOC_ESP32_CS_DEBUG("Timer "+String(t->timer_id)+" enable interrupt callback."); - // set the timer state to init (so that we can call the `mcpwm_timer_register_event_callbacks` ) - // this is a hack, as this function is not supposed to be called when the timer is running - // the timer does not really go to the init state! t->fsm = MCPWM_TIMER_FSM_INIT; - // set the callback - CHECK_CS_ERR(mcpwm_timer_register_event_callbacks(t, &cbs, cs_params), "Failed to set low side callback"); - // set the timer state to enabled again + CHECK_CS_ERR(mcpwm_timer_register_event_callbacks(t, &cbs, cs), "Failed to set low side callback"); t->fsm = MCPWM_TIMER_FSM_ENABLE; - CHECK_CS_ERR(esp_intr_enable(t->intr), "Failed to enable low-side interrupts!"); + CHECK_CS_ERR(esp_intr_enable(t->intr), "Failed to enable low-side interrupts"); + return cs; +} + +void *IRAM_ATTR _driverSyncLowSide(void *driver_params, void *cs_params) +{ +#ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG + pinMode(DEBUGPIN, OUTPUT); +#endif - SIMPLEFOC_ESP32_CS_DEBUG("MCPWM"+String(group_id)+" Timer "+String(t->timer_id)+" on_full callback configured."); - } + ESP32CurrentSenseParams *cs = (ESP32CurrentSenseParams *)cs_params; + if (!cs) { + SIMPLEFOC_ESP32_CS_DEBUG("ERROR: cs_params is null"); + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; + } +#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED + if (cs->adc_lowside_path != ESP32_ADC_LOWSIDE_LEGACY) { + void *r = esp32_adc_lowside_sync_mcpwm(driver_params, cs); + if (r == SIMPLEFOC_CURRENT_SENSE_INIT_FAILED) { + return r; + } + return cs; + } +#endif - return cs_params; + return esp32_mcpwm_sync_legacy_lowside(driver_params, cs); } - #endif diff --git a/src/current_sense/hardware_specific/esp32/esp32_mcu.h b/src/current_sense/hardware_specific/esp32/esp32_mcu.h index f6524d5c..1817d2e8 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_mcu.h +++ b/src/current_sense/hardware_specific/esp32/esp32_mcu.h @@ -2,6 +2,7 @@ #define ESP32_MCU_CURRENT_SENSING_H #include "../../hardware_api.h" +#include #if defined(ESP_H) && defined(ARDUINO_ARCH_ESP32) @@ -10,6 +11,18 @@ #include "esp32_adc_driver.h" +/* + * Low-side ADC backend (set during LowsideCurrentSense::init): + * LEGACY — MCPWM ISR + adcRead(), one phase per interrupt. + * DIGI_SW — digi controller + DMA; ISR only starts conversion (ESP32, S2, …). + * DIGI_ETM — same hardware as DIGI_SW; MCPWM TEZ starts ADC via ETM (S3+). + */ +enum ESP32AdcLowsidePath : uint8_t { + ESP32_ADC_LOWSIDE_LEGACY = 0, + ESP32_ADC_LOWSIDE_DIGI_SW, + ESP32_ADC_LOWSIDE_DIGI_ETM, +}; + // esp32 current sense parameters typedef struct ESP32CurrentSenseParams { int pins[3]; @@ -17,7 +30,8 @@ typedef struct ESP32CurrentSenseParams { int adc_buffer[3] = {}; int buffer_index = 0; int no_adc_channels = 0; - void* pretrig_comparator = nullptr; // MCPWM comparator handle for ADC pre-trigger + void* pretrig_comparator = nullptr; + ESP32AdcLowsidePath adc_lowside_path = ESP32_ADC_LOWSIDE_LEGACY; } ESP32CurrentSenseParams; // macros for debugging wuing the simplefoc debug system From b4aaeb0724f4fbe64548db3d97452c06273c2a35 Mon Sep 17 00:00:00 2001 From: Felipe Neves Date: Mon, 1 Jun 2026 22:01:47 -0300 Subject: [PATCH 6/7] Zero-copy DMA into adc_buffer for digi low-side sense. Link adc_hal DMA directly to ESP32CurrentSenseParams::adc_buffer, trim the EOF ISR to cache sync and rearm only, and decode samples on read. Allocate params from DMA-capable heap. --- .../esp32/esp32_adc_digi_driver.c | 80 +++++-------------- .../esp32/esp32_adc_digi_driver.h | 10 +++ .../esp32/esp32_adc_digi_internal.h | 23 +++++- .../esp32/esp32_mcpwm_mcu.cpp | 40 +++++++--- .../hardware_specific/esp32/esp32_mcu.h | 9 ++- 5 files changed, 85 insertions(+), 77 deletions(-) diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.c b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.c index ca126cba..77ae348b 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.c +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.c @@ -20,6 +20,7 @@ #include "esp_log.h" #include "esp_check.h" #include "esp_heap_caps.h" +#include "esp_memory_utils.h" #include "esp_clk_tree.h" #include "esp_private/regi2c_ctrl.h" #include "esp_private/sar_periph_ctrl.h" @@ -36,16 +37,7 @@ #include "esp_cache.h" #endif -#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 -#define ADC_GET_CHANNEL(p_data) ((p_data)->type1.channel) -#define ADC_GET_DATA(p_data) ((p_data)->type1.data) -#else -#define ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel) -#define ADC_GET_DATA(p_data) ((p_data)->type2.data) -#endif - #define ESP32_ADC_DMA_DESC_ALIGN 4 -#define ESP32_ADC_FRAME_BYTES (SIMPLEFOC_ESP32_ADC_NUM_CHANNELS * SOC_ADC_DIGI_RESULT_BYTES) typedef enum { ESP32_ADC_STATE_IDLE = 0, @@ -58,7 +50,6 @@ typedef struct { adc_hal_dma_ctx_t hal; adc_hal_digi_ctrlr_cfg_t hal_cfg; adc_digi_pattern_config_t patterns[SIMPLEFOC_ESP32_ADC_NUM_CHANNELS]; - uint8_t *rx_buf; uint32_t rx_desc_size; esp32_adc_digi_dma_ctx_t dma_ctx; adc_channel_t channels[SIMPLEFOC_ESP32_ADC_NUM_CHANNELS]; @@ -146,7 +137,7 @@ static void esp32_adc_rearm(esp32_adc_digi_ctx_t *adc) { esp32_adc_digi_dma_reset(&adc->dma_ctx); adc_hal_digi_reset(); - adc_hal_digi_dma_link(&adc->hal, adc->rx_buf); + adc_hal_digi_dma_link(&adc->hal, (uint8_t *)adc->adc_buffer); #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE esp_cache_msync(adc->hal.rx_desc, adc->rx_desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); #endif @@ -199,42 +190,14 @@ static esp_err_t esp32_adc_gpio_init(adc_unit_t unit, uint32_t chan_mask) return ESP_OK; } -static void IRAM_ATTR esp32_adc_process_frame(esp32_adc_digi_ctx_t *adc, const uint8_t *frame, uint32_t size) -{ - if (frame == NULL || size < ESP32_ADC_FRAME_BYTES || adc->adc_buffer == NULL) { - return; - } - - adc_digi_output_data_t *p = (adc_digi_output_data_t *)frame; - const int n = adc->no_adc_channels < SIMPLEFOC_ESP32_ADC_NUM_CHANNELS - ? adc->no_adc_channels - : SIMPLEFOC_ESP32_ADC_NUM_CHANNELS; - for (int i = 0; i < n; i++) { - adc->adc_buffer[i] = (int)ADC_GET_DATA(p); - p++; - } -} - static void IRAM_ATTR esp32_adc_dma_done(void *arg) { esp32_adc_digi_ctx_t *adc = (esp32_adc_digi_ctx_t *)arg; - adc_hal_dma_desc_status_t status; - uint8_t *finished_buffer = NULL; - uint32_t finished_size = 0; - - while (1) { - status = adc_hal_get_reading_result(&adc->hal, adc->dma_ctx.eof_desc_addr, - &finished_buffer, &finished_size); - if (status != ADC_HAL_DMA_DESC_VALID) { - break; - } -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - esp_cache_msync(finished_buffer, finished_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); -#endif - esp32_adc_process_frame(adc, finished_buffer, finished_size); - } #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + if (adc->adc_buffer != NULL) { + esp_cache_msync((void *)adc->adc_buffer, ESP32_ADC_DIGI_FRAME_BYTES, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + } esp_cache_msync(adc->hal.rx_desc, adc->rx_desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); #endif @@ -242,13 +205,12 @@ static void IRAM_ATTR esp32_adc_dma_done(void *arg) #if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED if (adc->trigger == SIMPLEFOC_ESP32_ADC_TRIG_ETM) { esp32_adc_rearm(adc); - } else -#endif - { - adc_hal_digi_enable(false); - adc_hal_digi_connect(false); - adc->state = ESP32_ADC_STATE_IDLE; + return; } +#endif + adc_hal_digi_enable(false); + adc_hal_digi_connect(false); + adc->state = ESP32_ADC_STATE_IDLE; } static esp_err_t esp32_adc_hw_start(esp32_adc_digi_ctx_t *adc) @@ -315,12 +277,6 @@ static esp_err_t esp32_adc_setup_hal(esp32_adc_digi_ctx_t *adc) }; adc_hal_dma_ctx_config(&adc->hal, &dma_cfg); - adc->rx_buf = heap_caps_calloc(1, ESP32_ADC_FRAME_BYTES, - MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT); - if (adc->rx_buf == NULL) { - return ESP_ERR_NO_MEM; - } - adc->hal.rx_desc = heap_caps_aligned_calloc(ESP32_ADC_DMA_DESC_ALIGN, 1, sizeof(dma_descriptor_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT); @@ -341,6 +297,11 @@ static esp_err_t esp32_adc_setup_hal(esp32_adc_digi_ctx_t *adc) return ESP_OK; } +int esp32_adc_digi_read_raw(const void *adc_buffer, int index) +{ + return esp32_adc_digi_raw_at(adc_buffer, index); +} + bool esp32_adc_digi_supported(void) { return true; @@ -366,6 +327,11 @@ esp_err_t esp32_adc_digi_init(const esp32_adc_digi_config_t *cfg) ESP_LOGE(TAG, "need 1..%d ADC channels for hw trigger", SIMPLEFOC_ESP32_ADC_NUM_CHANNELS); return ESP_ERR_INVALID_ARG; } + if (!esp_ptr_dma_capable(cfg->adc_buffer) || + !esp_ptr_internal(cfg->adc_buffer)) { + ESP_LOGE(TAG, "adc_buffer must be INTERNAL|DMA capable (heap_caps_calloc)"); + return ESP_ERR_INVALID_ARG; + } if (s_adc_initialized) { return ESP_OK; } @@ -409,13 +375,9 @@ esp_err_t esp32_adc_digi_deinit(void) esp32_adc_digi_set_trigger(SIMPLEFOC_ESP32_ADC_TRIG_SOFTWARE); esp32_adc_digi_dma_stop(&s_adc.dma_ctx); esp32_adc_digi_dma_deinit(&s_adc.dma_ctx); - adc_hal_digi_deinit(&s_adc.hal); + adc_hal_digi_deinit(); adc_lock_release(s_adc.unit); sar_periph_ctrl_adc_continuous_power_release(); - if (s_adc.rx_buf) { - heap_caps_free(s_adc.rx_buf); - s_adc.rx_buf = NULL; - } if (s_adc.hal.rx_desc) { heap_caps_free(s_adc.hal.rx_desc); s_adc.hal.rx_desc = NULL; diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.h b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.h index 59be2f1c..42f989ab 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.h +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.h @@ -15,6 +15,10 @@ #if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { adc_channel_t channels[SIMPLEFOC_ESP32_ADC_NUM_CHANNELS]; adc_unit_t unit; @@ -34,6 +38,8 @@ esp_err_t esp32_adc_digi_deinit(void); esp_err_t esp32_adc_digi_set_trigger(esp32_adc_digi_trigger_t mode); esp_err_t esp32_adc_digi_trigger_software(void); +int esp32_adc_digi_read_raw(const void *adc_buffer, int index); + bool esp32_adc_digi_supported(void); #if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED @@ -41,4 +47,8 @@ esp_err_t esp32_adc_digi_set_etm_source(const esp32_adc_digi_etm_config_t *cfg); bool esp32_adc_digi_etm_supported(void); #endif +#ifdef __cplusplus +} +#endif + #endif /* SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED */ diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_internal.h b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_internal.h index c794af66..09d3249d 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_internal.h +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_internal.h @@ -9,7 +9,9 @@ #include #include "sdkconfig.h" #include "esp_err.h" +#include "hal/adc_types.h" #include "hal/dma_types.h" +#include "soc/soc_caps.h" /* * Low-side current sense on ESP32 uses the SAR **digital** controller (pattern @@ -27,7 +29,7 @@ * - ESP32-S3+: GDMA (esp32_adc_dma_gdma.c) */ -#if defined(ESP_H) && defined(ARDUINO_ARCH_ESP32) +#if defined(ARDUINO_ARCH_ESP32) #define SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED 1 @@ -50,6 +52,25 @@ #define SIMPLEFOC_ESP32_ADC_NUM_CHANNELS 2 #define SIMPLEFOC_ESP32_ADC_CONVERT_LIMIT 2 +#define ESP32_ADC_DIGI_FRAME_BYTES \ + (SIMPLEFOC_ESP32_ADC_NUM_CHANNELS * SOC_ADC_DIGI_RESULT_BYTES) + +/* + * DMA writes adc_digi_output_data_t[N] into adc_buffer (zero-copy). + * int adc_buffer[3] must be DMA-capable and >= ESP32_ADC_DIGI_FRAME_BYTES used bytes. + */ +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 +#define ESP32_ADC_DIGI_SAMPLE_RAW(p) ((int32_t)((p)->type1.data)) +#else +#define ESP32_ADC_DIGI_SAMPLE_RAW(p) ((int32_t)((p)->type2.data)) +#endif + +static inline int esp32_adc_digi_raw_at(const void *adc_buffer, int index) +{ + const adc_digi_output_data_t *samples = (const adc_digi_output_data_t *)adc_buffer; + return (int)ESP32_ADC_DIGI_SAMPLE_RAW(&samples[index]); +} + typedef struct esp32_adc_digi_dma_ctx esp32_adc_digi_dma_ctx_t; typedef void (*esp32_adc_digi_dma_done_fn_t)(void *user); diff --git a/src/current_sense/hardware_specific/esp32/esp32_mcpwm_mcu.cpp b/src/current_sense/hardware_specific/esp32/esp32_mcpwm_mcu.cpp index 08985e68..8e4c3bbd 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_mcpwm_mcu.cpp +++ b/src/current_sense/hardware_specific/esp32/esp32_mcpwm_mcu.cpp @@ -14,8 +14,10 @@ #include "soc/mcpwm_reg.h" #include "soc/mcpwm_struct.h" +#include "esp32_adc_digi_internal.h" #if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED #include "esp32_adc_digi_lowside.h" +#include "esp_heap_caps.h" #endif // #define SIMPLEFOC_ESP32_INTERRUPT_DEBUG @@ -38,9 +40,9 @@ * Low-side current sense on ESP32 MCPWM. * * ADC path (see esp32_adc_digi_internal.h): - * - LEGACY: MCPWM ISR calls adcRead() one phase per interrupt (~10 us each). + * - ADC_READ: default; MCPWM ISR + adcRead(), one phase per interrupt (~10 us each). * - DIGI_SW: ESP32 / S2 — digi+DMA; ISR only calls esp32_adc_digi_trigger_software(). - * - DIGI_ETM: S3+ — same digi+DMA; MCPWM TEZ starts ADC via ETM (no ADC ISR). + * - DIGI_ETM: S3 / C6+ — digi+DMA; MCPWM TEZ starts ADC via ETM (no ADC ISR). */ float IRAM_ATTR _readADCVoltageLowSide(const int pin, const void *cs_params) @@ -52,6 +54,9 @@ float IRAM_ATTR _readADCVoltageLowSide(const int pin, const void *cs_params) continue; } if (pin == p->pins[i]) { + if (p->adc_lowside_path != ESP32_ADC_LOWSIDE_ADC_READ) { + return esp32_adc_digi_raw_at(p->adc_buffer, no_channel) * p->adc_voltage_conv; + } return p->adc_buffer[no_channel] * p->adc_voltage_conv; } no_channel++; @@ -71,7 +76,16 @@ void *IRAM_ATTR _configureADCLowSide(const void *driver_params, const int pinA, return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; } +#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED + ESP32CurrentSenseParams *params = (ESP32CurrentSenseParams *)heap_caps_calloc( + 1, sizeof(ESP32CurrentSenseParams), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); + if (params == NULL) { + SIMPLEFOC_ESP32_CS_DEBUG("ERROR: DMA-capable current sense alloc failed"); + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; + } +#else ESP32CurrentSenseParams *params = new ESP32CurrentSenseParams{}; +#endif int no_adc_channels = 0; int adc_pins[3] = { pinA, pinB, pinC }; @@ -90,7 +104,7 @@ void *IRAM_ATTR _configureADCLowSide(const void *driver_params, const int pinA, params->no_adc_channels = no_adc_channels; #if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED - if (esp32_adc_lowside_configure(params) != ESP32_ADC_LOWSIDE_LEGACY) { + if (esp32_adc_lowside_configure(params) != ESP32_ADC_LOWSIDE_ADC_READ) { t->user_data = params; return params; } @@ -100,8 +114,8 @@ void *IRAM_ATTR _configureADCLowSide(const void *driver_params, const int pinA, return params; } -static bool IRAM_ATTR _mcpwm_legacy_adc_callback(mcpwm_timer_handle_t tim, const mcpwm_timer_event_data_t *edata, - void *user_data) +static bool IRAM_ATTR _mcpwm_adc_read_timer_callback(mcpwm_timer_handle_t tim, const mcpwm_timer_event_data_t *edata, + void *user_data) { (void)tim; (void)edata; @@ -120,8 +134,8 @@ static bool IRAM_ATTR _mcpwm_legacy_adc_callback(mcpwm_timer_handle_t tim, const return true; } -static bool IRAM_ATTR _mcpwm_legacy_comparator_adc_callback(mcpwm_cmpr_handle_t cmpr, - const mcpwm_compare_event_data_t *edata, void *user_data) +static bool IRAM_ATTR _mcpwm_adc_read_comparator_callback(mcpwm_cmpr_handle_t cmpr, + const mcpwm_compare_event_data_t *edata, void *user_data) { if (edata->direction != MCPWM_TIMER_DIRECTION_UP) { return true; @@ -148,13 +162,13 @@ void IRAM_ATTR _startADC3PinConversionLowSide() #endif } -static void *esp32_mcpwm_sync_legacy_lowside(void *driver_params, ESP32CurrentSenseParams *cs) +static void *esp32_mcpwm_sync_adc_read_lowside(void *driver_params, ESP32CurrentSenseParams *cs) { ESP32MCPWMDriverParams *p = (ESP32MCPWMDriverParams *)driver_params; mcpwm_timer_t *t = (mcpwm_timer_t *)p->timers[0]; int group_id = p->group_id; - SIMPLEFOC_ESP32_CS_DEBUG("Legacy path: MCPWM ISR + adcRead() (one phase per interrupt)"); + SIMPLEFOC_ESP32_CS_DEBUG("ADC_READ: MCPWM ISR + adcRead() (one phase per interrupt)"); #ifndef SIMPLEFOC_CS_PRETRIGGER_US #define SIMPLEFOC_CS_PRETRIGGER_US 5 @@ -178,7 +192,7 @@ static void *esp32_mcpwm_sync_legacy_lowside(void *driver_params, ESP32CurrentSe "Failed to set pretrigger compare value"); mcpwm_comparator_event_callbacks_t cmp_cbs = { - .on_reach = _mcpwm_legacy_comparator_adc_callback, + .on_reach = _mcpwm_adc_read_comparator_callback, }; CHECK_CS_ERR(mcpwm_comparator_register_event_callbacks((mcpwm_cmpr_handle_t)cs->pretrig_comparator, &cmp_cbs, cs), @@ -195,7 +209,7 @@ static void *esp32_mcpwm_sync_legacy_lowside(void *driver_params, ESP32CurrentSe } auto cbs = mcpwm_timer_event_callbacks_t{ - .on_full = _mcpwm_legacy_adc_callback, + .on_full = _mcpwm_adc_read_timer_callback, }; t->fsm = MCPWM_TIMER_FSM_INIT; CHECK_CS_ERR(mcpwm_timer_register_event_callbacks(t, &cbs, cs), "Failed to set low side callback"); @@ -217,7 +231,7 @@ void *IRAM_ATTR _driverSyncLowSide(void *driver_params, void *cs_params) } #if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED - if (cs->adc_lowside_path != ESP32_ADC_LOWSIDE_LEGACY) { + if (cs->adc_lowside_path != ESP32_ADC_LOWSIDE_ADC_READ) { void *r = esp32_adc_lowside_sync_mcpwm(driver_params, cs); if (r == SIMPLEFOC_CURRENT_SENSE_INIT_FAILED) { return r; @@ -226,7 +240,7 @@ void *IRAM_ATTR _driverSyncLowSide(void *driver_params, void *cs_params) } #endif - return esp32_mcpwm_sync_legacy_lowside(driver_params, cs); + return esp32_mcpwm_sync_adc_read_lowside(driver_params, cs); } #endif diff --git a/src/current_sense/hardware_specific/esp32/esp32_mcu.h b/src/current_sense/hardware_specific/esp32/esp32_mcu.h index 1817d2e8..7ff7fb1e 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_mcu.h +++ b/src/current_sense/hardware_specific/esp32/esp32_mcu.h @@ -13,12 +13,12 @@ /* * Low-side ADC backend (set during LowsideCurrentSense::init): - * LEGACY — MCPWM ISR + adcRead(), one phase per interrupt. + * ADC_READ — default; MCPWM ISR + adcRead(), one phase per interrupt. * DIGI_SW — digi controller + DMA; ISR only starts conversion (ESP32, S2, …). - * DIGI_ETM — same hardware as DIGI_SW; MCPWM TEZ starts ADC via ETM (S3+). + * DIGI_ETM — digi + DMA; MCPWM TEZ starts ADC via ETM (S3, C6, …). */ enum ESP32AdcLowsidePath : uint8_t { - ESP32_ADC_LOWSIDE_LEGACY = 0, + ESP32_ADC_LOWSIDE_ADC_READ = 0, ESP32_ADC_LOWSIDE_DIGI_SW, ESP32_ADC_LOWSIDE_DIGI_ETM, }; @@ -27,11 +27,12 @@ enum ESP32AdcLowsidePath : uint8_t { typedef struct ESP32CurrentSenseParams { int pins[3]; float adc_voltage_conv; + /* ADC_READ: plain counts; DIGI_*: DMA lands adc_digi_output_data_t[0..N] here (zero-copy) */ int adc_buffer[3] = {}; int buffer_index = 0; int no_adc_channels = 0; void* pretrig_comparator = nullptr; - ESP32AdcLowsidePath adc_lowside_path = ESP32_ADC_LOWSIDE_LEGACY; + ESP32AdcLowsidePath adc_lowside_path = ESP32_ADC_LOWSIDE_ADC_READ; } ESP32CurrentSenseParams; // macros for debugging wuing the simplefoc debug system From a76ec5832e273a096947e0066076d769a0e65ccb Mon Sep 17 00:00:00 2001 From: Felipe Neves Date: Mon, 1 Jun 2026 22:01:47 -0300 Subject: [PATCH 7/7] Rename low-side ADC path labels from legacy to ADC_READ. Wrap ESP32 digi ADC sources in ARDUINO_ARCH_ESP32 guards so AVR CI builds do not pull sdkconfig.h. Select DMA backend per chip (I2S / SPI3 / GDMA) via internal.h so classic ESP32 and S2 do not compile GDMA. Include esp32_mcu.h before MCPWM guard in lowside.cpp. --- .../esp32/esp32_adc_digi_driver.c | 22 ++++++++++--- .../esp32/esp32_adc_digi_driver.h | 4 ++- .../esp32/esp32_adc_digi_internal.h | 32 ++++++++++--------- .../esp32/esp32_adc_digi_lowside.cpp | 15 +++++---- .../esp32/esp32_adc_digi_lowside.h | 6 +++- .../esp32/esp32_adc_dma_esp32.c | 10 +++--- .../esp32/esp32_adc_dma_esp32s2.c | 10 +++--- .../esp32/esp32_adc_dma_gdma.c | 10 +++--- .../hardware_specific/esp32/esp32_adc_etm.c | 6 ++-- 9 files changed, 73 insertions(+), 42 deletions(-) diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.c b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.c index 77ae348b..2afba529 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.c +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.c @@ -10,7 +10,8 @@ * - ETM: ESP32-S3 and newer only; see esp32_adc_etm.c. */ -#include "sdkconfig.h" +#if defined(ARDUINO_ARCH_ESP32) + #include "esp32_adc_digi_internal.h" #if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED @@ -118,13 +119,25 @@ static void esp32_adc_apply_max_clock(esp32_adc_digi_ctx_t *adc) adc_ll_digi_set_clk_div(1); adc_ll_set_sample_cycle(ADC_LL_SAMPLE_CYCLE_DEFAULT); #else +#if defined(ADC_DIGI_CLK_SRC_PLL_F80M) const soc_module_clk_t fast_src = SOC_MOD_CLK_PLL_F80M; + const soc_periph_adc_digi_clk_src_t adc_clk = ADC_DIGI_CLK_SRC_PLL_F80M; +#elif defined(ADC_DIGI_CLK_SRC_PLL_F96M) + const soc_module_clk_t fast_src = SOC_MOD_CLK_PLL_F96M; + const soc_periph_adc_digi_clk_src_t adc_clk = ADC_DIGI_CLK_SRC_PLL_F96M; +#elif defined(ADC_DIGI_CLK_SRC_APB) + const soc_module_clk_t fast_src = SOC_MOD_CLK_APB; + const soc_periph_adc_digi_clk_src_t adc_clk = ADC_DIGI_CLK_SRC_APB; +#else + const soc_module_clk_t fast_src = (soc_module_clk_t)ADC_DIGI_CLK_SRC_DEFAULT; + const soc_periph_adc_digi_clk_src_t adc_clk = ADC_DIGI_CLK_SRC_DEFAULT; +#endif ESP_ERROR_CHECK(esp_clk_tree_enable_src(fast_src, true)); uint32_t clk_hz = 0; ESP_ERROR_CHECK(esp_clk_tree_src_get_freq_hz(fast_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_hz)); - adc->hal_cfg.clk_src = ADC_DIGI_CLK_SRC_PLL_F80M; + adc->hal_cfg.clk_src = adc_clk; adc->hal_cfg.clk_src_freq_hz = clk_hz; - adc_ll_digi_clk_sel(ADC_DIGI_CLK_SRC_PLL_F80M); + adc_ll_digi_clk_sel(adc_clk); adc_ll_digi_controller_clk_div(0, 1, 0); adc_ll_digi_set_clk_div(1); adc_ll_set_sample_cycle(ADC_LL_SAMPLE_CYCLE_DEFAULT); @@ -443,4 +456,5 @@ esp_err_t esp32_adc_digi_trigger_software(void) return ESP_OK; } -#endif +#endif /* SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED */ +#endif /* ARDUINO_ARCH_ESP32 */ diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.h b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.h index 42f989ab..8abee475 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.h +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.h @@ -8,7 +8,8 @@ */ #pragma once -#include "sdkconfig.h" +#if defined(ARDUINO_ARCH_ESP32) + #include "esp_err.h" #include "hal/adc_types.h" #include "esp32_adc_digi_internal.h" @@ -52,3 +53,4 @@ bool esp32_adc_digi_etm_supported(void); #endif #endif /* SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED */ +#endif /* ARDUINO_ARCH_ESP32 */ diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_internal.h b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_internal.h index 09d3249d..2d06f1fb 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_internal.h +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_internal.h @@ -5,6 +5,13 @@ */ #pragma once +#if !defined(ARDUINO_ARCH_ESP32) + +#define SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED 0 +#define SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED 0 + +#else + #include #include #include "sdkconfig.h" @@ -29,25 +36,22 @@ * - ESP32-S3+: GDMA (esp32_adc_dma_gdma.c) */ -#if defined(ARDUINO_ARCH_ESP32) - #define SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED 1 -/* ETM is not available on original ESP32 or ESP32-S2. */ -#if SOC_ETM_SUPPORTED && !CONFIG_IDF_TARGET_ESP32 && !CONFIG_IDF_TARGET_ESP32S2 -#define SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED 1 -#else -#define SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED 0 +#if CONFIG_IDF_TARGET_ESP32 +#define SIMPLEFOC_ESP32_ADC_USE_I2S_DMA 1 +#elif CONFIG_IDF_TARGET_ESP32S2 +#define SIMPLEFOC_ESP32_ADC_USE_SPI3_DMA 1 +#elif SOC_GDMA_SUPPORTED +#define SIMPLEFOC_ESP32_ADC_USE_GDMA_DMA 1 #endif +#if SOC_ETM_SUPPORTED && defined(SIMPLEFOC_ESP32_ADC_USE_GDMA_DMA) +#define SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED 1 #else - -#define SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED 0 #define SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED 0 - #endif -/* Pattern / DMA sizing — kept aligned with espFoC isensor_adc_internal.h */ #define SIMPLEFOC_ESP32_ADC_PATTERN_HZ 80000 #define SIMPLEFOC_ESP32_ADC_NUM_CHANNELS 2 #define SIMPLEFOC_ESP32_ADC_CONVERT_LIMIT 2 @@ -55,10 +59,6 @@ #define ESP32_ADC_DIGI_FRAME_BYTES \ (SIMPLEFOC_ESP32_ADC_NUM_CHANNELS * SOC_ADC_DIGI_RESULT_BYTES) -/* - * DMA writes adc_digi_output_data_t[N] into adc_buffer (zero-copy). - * int adc_buffer[3] must be DMA-capable and >= ESP32_ADC_DIGI_FRAME_BYTES used bytes. - */ #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 #define ESP32_ADC_DIGI_SAMPLE_RAW(p) ((int32_t)((p)->type1.data)) #else @@ -106,3 +106,5 @@ esp_err_t esp32_adc_digi_etm_enable(bool enable); void esp32_adc_digi_etm_disconnect(void); #endif /* SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED */ + +#endif /* ARDUINO_ARCH_ESP32 */ diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.cpp b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.cpp index ab1bbf06..07f9ce2a 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.cpp +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.cpp @@ -1,6 +1,9 @@ -#include "esp32_adc_digi_lowside.h" +#include "esp32_mcu.h" + +#if defined(ESP_H) && defined(ARDUINO_ARCH_ESP32) && defined(SOC_MCPWM_SUPPORTED) \ + && !defined(SIMPLEFOC_ESP32_USELEDC) -#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED && defined(SOC_MCPWM_SUPPORTED) && !defined(SIMPLEFOC_ESP32_USELEDC) +#include "esp32_adc_digi_lowside.h" #include "esp32_adc_digi_driver.h" #include "../../../drivers/hardware_specific/esp32/esp32_driver_mcpwm.h" @@ -47,12 +50,12 @@ static esp_err_t esp32_adc_digi_bind_params(ESP32CurrentSenseParams *params) ESP32AdcLowsidePath esp32_adc_lowside_configure(ESP32CurrentSenseParams *params) { if (params == NULL || !esp32_adc_digi_supported()) { - return ESP32_ADC_LOWSIDE_LEGACY; + return ESP32_ADC_LOWSIDE_ADC_READ; } if (esp32_adc_digi_bind_params(params) != ESP_OK) { - SIMPLEFOC_ESP32_CS_DEBUG("WARN: ADC digi+DMA init failed, using legacy adcRead path"); - return ESP32_ADC_LOWSIDE_LEGACY; + SIMPLEFOC_ESP32_CS_DEBUG("WARN: ADC digi+DMA init failed, using ADC_READ (adcRead) path"); + return ESP32_ADC_LOWSIDE_ADC_READ; } params->adc_lowside_path = ESP32_ADC_LOWSIDE_DIGI_SW; @@ -191,7 +194,7 @@ void *esp32_adc_lowside_sync_mcpwm(void *driver_params, ESP32CurrentSenseParams } #if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED - if (esp32_adc_digi_etm_supported() && cs->adc_lowside_path != ESP32_ADC_LOWSIDE_LEGACY) { + if (esp32_adc_digi_etm_supported() && cs->adc_lowside_path != ESP32_ADC_LOWSIDE_ADC_READ) { return esp32_adc_lowside_sync_etm(driver_params, cs); } #endif diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.h b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.h index cafcf67c..1e45a1a5 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.h +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_digi_lowside.h @@ -7,9 +7,12 @@ */ #pragma once +#if defined(ESP_H) && defined(ARDUINO_ARCH_ESP32) && defined(SOC_MCPWM_SUPPORTED) \ + && !defined(SIMPLEFOC_ESP32_USELEDC) + #include "esp32_adc_digi_internal.h" -#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED && defined(SOC_MCPWM_SUPPORTED) && !defined(SIMPLEFOC_ESP32_USELEDC) +#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED #include "esp32_mcu.h" @@ -18,4 +21,5 @@ bool esp32_adc_lowside_uses_mcpwm_isr(const ESP32CurrentSenseParams *params); void *esp32_adc_lowside_sync_mcpwm(void *driver_params, ESP32CurrentSenseParams *cs); void esp32_adc_lowside_start_conversion(void); +#endif /* SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED */ #endif diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32.c b/src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32.c index a350eee7..8f69ae5b 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32.c +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32.c @@ -6,9 +6,11 @@ * I2S DMA backend for ADC digi on ESP32 (i2s_ll only). */ -#include "sdkconfig.h" +#if defined(ARDUINO_ARCH_ESP32) -#if CONFIG_IDF_TARGET_ESP32 +#include "esp32_adc_digi_internal.h" + +#if defined(SIMPLEFOC_ESP32_ADC_USE_I2S_DMA) && SIMPLEFOC_ESP32_ADC_USE_I2S_DMA #include "esp_err.h" #include "esp_intr_alloc.h" @@ -17,7 +19,6 @@ #include "hal/adc_hal.h" #include "soc/i2s_periph.h" #include "esp_private/i2s_platform.h" -#include "esp32_adc_digi_internal.h" #define ADC_DMA_I2S_HOST ADC_HAL_DMA_I2S_HOST #define ADC_DMA_INTR_MASK BIT(9) @@ -128,4 +129,5 @@ esp_err_t esp32_adc_digi_dma_reset(esp32_adc_digi_dma_ctx_t *ctx) return ESP_OK; } -#endif +#endif /* SIMPLEFOC_ESP32_ADC_USE_I2S_DMA */ +#endif /* ARDUINO_ARCH_ESP32 */ diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32s2.c b/src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32s2.c index 6363503e..18e8a020 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32s2.c +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_dma_esp32s2.c @@ -6,16 +6,17 @@ * SPI3 DMA backend for ADC digi on ESP32-S2 (spi_ll + spicommon). */ -#include "sdkconfig.h" +#if defined(ARDUINO_ARCH_ESP32) -#if CONFIG_IDF_TARGET_ESP32S2 +#include "esp32_adc_digi_internal.h" + +#if defined(SIMPLEFOC_ESP32_ADC_USE_SPI3_DMA) && SIMPLEFOC_ESP32_ADC_USE_SPI3_DMA #include "esp_err.h" #include "esp_intr_alloc.h" #include "esp_attr.h" #include "hal/spi_ll.h" #include "esp_private/spi_common_internal.h" -#include "esp32_adc_digi_internal.h" #define ADC_DMA_SPI_HOST SPI3_HOST #define ADC_DMA_INTR_MASK SPI_LL_INTR_IN_SUC_EOF @@ -133,4 +134,5 @@ esp_err_t esp32_adc_digi_dma_reset(esp32_adc_digi_dma_ctx_t *ctx) return ESP_OK; } -#endif +#endif /* SIMPLEFOC_ESP32_ADC_USE_SPI3_DMA */ +#endif /* ARDUINO_ARCH_ESP32 */ diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_dma_gdma.c b/src/current_sense/hardware_specific/esp32/esp32_adc_dma_gdma.c index 55684621..f4d94320 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_adc_dma_gdma.c +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_dma_gdma.c @@ -6,9 +6,11 @@ * GDMA RX backend for ADC digi (ESP32-S3, C3, C6, …) using gdma_hal + gdma_ll only. */ -#include "sdkconfig.h" +#if defined(ARDUINO_ARCH_ESP32) -#if !CONFIG_IDF_TARGET_ESP32 && !CONFIG_IDF_TARGET_ESP32S2 +#include "esp32_adc_digi_internal.h" + +#if defined(SIMPLEFOC_ESP32_ADC_USE_GDMA_DMA) && SIMPLEFOC_ESP32_ADC_USE_GDMA_DMA #include "esp_err.h" #include "esp_intr_alloc.h" @@ -18,7 +20,6 @@ #include "hal/gdma_ll.h" #include "hal/gdma_types.h" #include "soc/gdma_periph.h" -#include "esp32_adc_digi_internal.h" #define ESP32_ADC_GDMA_GROUP 0 #define ESP32_ADC_GDMA_PAIR 2 @@ -143,4 +144,5 @@ esp_err_t esp32_adc_digi_dma_reset(esp32_adc_digi_dma_ctx_t *ctx) return ESP_OK; } -#endif +#endif /* SIMPLEFOC_ESP32_ADC_USE_GDMA_DMA */ +#endif /* ARDUINO_ARCH_ESP32 */ diff --git a/src/current_sense/hardware_specific/esp32/esp32_adc_etm.c b/src/current_sense/hardware_specific/esp32/esp32_adc_etm.c index 03a832c3..a2605d9b 100644 --- a/src/current_sense/hardware_specific/esp32/esp32_adc_etm.c +++ b/src/current_sense/hardware_specific/esp32/esp32_adc_etm.c @@ -6,7 +6,8 @@ * ETM wiring: MCPWM timer event -> ADC digi START task. */ -#include "sdkconfig.h" +#if defined(ARDUINO_ARCH_ESP32) + #include "esp32_adc_digi_internal.h" #if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED @@ -19,8 +20,6 @@ #include "esp_private/etm_interface.h" #include "soc/soc_caps.h" #include "soc/soc_etm_source.h" -#include "esp32_adc_digi_internal.h" - static const char *TAG = "esp32_adc_etm"; typedef struct { @@ -157,3 +156,4 @@ void esp32_adc_digi_etm_disconnect(void) } #endif /* SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED */ +#endif /* ARDUINO_ARCH_ESP32 */