Skip to content

Commit b4aaeb0

Browse files
committed
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.
1 parent a28ff38 commit b4aaeb0

5 files changed

Lines changed: 85 additions & 77 deletions

File tree

src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.c

Lines changed: 21 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "esp_log.h"
2121
#include "esp_check.h"
2222
#include "esp_heap_caps.h"
23+
#include "esp_memory_utils.h"
2324
#include "esp_clk_tree.h"
2425
#include "esp_private/regi2c_ctrl.h"
2526
#include "esp_private/sar_periph_ctrl.h"
@@ -36,16 +37,7 @@
3637
#include "esp_cache.h"
3738
#endif
3839

39-
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
40-
#define ADC_GET_CHANNEL(p_data) ((p_data)->type1.channel)
41-
#define ADC_GET_DATA(p_data) ((p_data)->type1.data)
42-
#else
43-
#define ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel)
44-
#define ADC_GET_DATA(p_data) ((p_data)->type2.data)
45-
#endif
46-
4740
#define ESP32_ADC_DMA_DESC_ALIGN 4
48-
#define ESP32_ADC_FRAME_BYTES (SIMPLEFOC_ESP32_ADC_NUM_CHANNELS * SOC_ADC_DIGI_RESULT_BYTES)
4941

5042
typedef enum {
5143
ESP32_ADC_STATE_IDLE = 0,
@@ -58,7 +50,6 @@ typedef struct {
5850
adc_hal_dma_ctx_t hal;
5951
adc_hal_digi_ctrlr_cfg_t hal_cfg;
6052
adc_digi_pattern_config_t patterns[SIMPLEFOC_ESP32_ADC_NUM_CHANNELS];
61-
uint8_t *rx_buf;
6253
uint32_t rx_desc_size;
6354
esp32_adc_digi_dma_ctx_t dma_ctx;
6455
adc_channel_t channels[SIMPLEFOC_ESP32_ADC_NUM_CHANNELS];
@@ -146,7 +137,7 @@ static void esp32_adc_rearm(esp32_adc_digi_ctx_t *adc)
146137
{
147138
esp32_adc_digi_dma_reset(&adc->dma_ctx);
148139
adc_hal_digi_reset();
149-
adc_hal_digi_dma_link(&adc->hal, adc->rx_buf);
140+
adc_hal_digi_dma_link(&adc->hal, (uint8_t *)adc->adc_buffer);
150141
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
151142
esp_cache_msync(adc->hal.rx_desc, adc->rx_desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
152143
#endif
@@ -199,56 +190,27 @@ static esp_err_t esp32_adc_gpio_init(adc_unit_t unit, uint32_t chan_mask)
199190
return ESP_OK;
200191
}
201192

202-
static void IRAM_ATTR esp32_adc_process_frame(esp32_adc_digi_ctx_t *adc, const uint8_t *frame, uint32_t size)
203-
{
204-
if (frame == NULL || size < ESP32_ADC_FRAME_BYTES || adc->adc_buffer == NULL) {
205-
return;
206-
}
207-
208-
adc_digi_output_data_t *p = (adc_digi_output_data_t *)frame;
209-
const int n = adc->no_adc_channels < SIMPLEFOC_ESP32_ADC_NUM_CHANNELS
210-
? adc->no_adc_channels
211-
: SIMPLEFOC_ESP32_ADC_NUM_CHANNELS;
212-
for (int i = 0; i < n; i++) {
213-
adc->adc_buffer[i] = (int)ADC_GET_DATA(p);
214-
p++;
215-
}
216-
}
217-
218193
static void IRAM_ATTR esp32_adc_dma_done(void *arg)
219194
{
220195
esp32_adc_digi_ctx_t *adc = (esp32_adc_digi_ctx_t *)arg;
221-
adc_hal_dma_desc_status_t status;
222-
uint8_t *finished_buffer = NULL;
223-
uint32_t finished_size = 0;
224-
225-
while (1) {
226-
status = adc_hal_get_reading_result(&adc->hal, adc->dma_ctx.eof_desc_addr,
227-
&finished_buffer, &finished_size);
228-
if (status != ADC_HAL_DMA_DESC_VALID) {
229-
break;
230-
}
231-
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
232-
esp_cache_msync(finished_buffer, finished_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
233-
#endif
234-
esp32_adc_process_frame(adc, finished_buffer, finished_size);
235-
}
236196

237197
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
198+
if (adc->adc_buffer != NULL) {
199+
esp_cache_msync((void *)adc->adc_buffer, ESP32_ADC_DIGI_FRAME_BYTES, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
200+
}
238201
esp_cache_msync(adc->hal.rx_desc, adc->rx_desc_size,
239202
ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
240203
#endif
241204

242205
#if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED
243206
if (adc->trigger == SIMPLEFOC_ESP32_ADC_TRIG_ETM) {
244207
esp32_adc_rearm(adc);
245-
} else
246-
#endif
247-
{
248-
adc_hal_digi_enable(false);
249-
adc_hal_digi_connect(false);
250-
adc->state = ESP32_ADC_STATE_IDLE;
208+
return;
251209
}
210+
#endif
211+
adc_hal_digi_enable(false);
212+
adc_hal_digi_connect(false);
213+
adc->state = ESP32_ADC_STATE_IDLE;
252214
}
253215

254216
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)
315277
};
316278
adc_hal_dma_ctx_config(&adc->hal, &dma_cfg);
317279

318-
adc->rx_buf = heap_caps_calloc(1, ESP32_ADC_FRAME_BYTES,
319-
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
320-
if (adc->rx_buf == NULL) {
321-
return ESP_ERR_NO_MEM;
322-
}
323-
324280
adc->hal.rx_desc = heap_caps_aligned_calloc(ESP32_ADC_DMA_DESC_ALIGN, 1,
325281
sizeof(dma_descriptor_t),
326282
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)
341297
return ESP_OK;
342298
}
343299

300+
int esp32_adc_digi_read_raw(const void *adc_buffer, int index)
301+
{
302+
return esp32_adc_digi_raw_at(adc_buffer, index);
303+
}
304+
344305
bool esp32_adc_digi_supported(void)
345306
{
346307
return true;
@@ -366,6 +327,11 @@ esp_err_t esp32_adc_digi_init(const esp32_adc_digi_config_t *cfg)
366327
ESP_LOGE(TAG, "need 1..%d ADC channels for hw trigger", SIMPLEFOC_ESP32_ADC_NUM_CHANNELS);
367328
return ESP_ERR_INVALID_ARG;
368329
}
330+
if (!esp_ptr_dma_capable(cfg->adc_buffer) ||
331+
!esp_ptr_internal(cfg->adc_buffer)) {
332+
ESP_LOGE(TAG, "adc_buffer must be INTERNAL|DMA capable (heap_caps_calloc)");
333+
return ESP_ERR_INVALID_ARG;
334+
}
369335
if (s_adc_initialized) {
370336
return ESP_OK;
371337
}
@@ -409,13 +375,9 @@ esp_err_t esp32_adc_digi_deinit(void)
409375
esp32_adc_digi_set_trigger(SIMPLEFOC_ESP32_ADC_TRIG_SOFTWARE);
410376
esp32_adc_digi_dma_stop(&s_adc.dma_ctx);
411377
esp32_adc_digi_dma_deinit(&s_adc.dma_ctx);
412-
adc_hal_digi_deinit(&s_adc.hal);
378+
adc_hal_digi_deinit();
413379
adc_lock_release(s_adc.unit);
414380
sar_periph_ctrl_adc_continuous_power_release();
415-
if (s_adc.rx_buf) {
416-
heap_caps_free(s_adc.rx_buf);
417-
s_adc.rx_buf = NULL;
418-
}
419381
if (s_adc.hal.rx_desc) {
420382
heap_caps_free(s_adc.hal.rx_desc);
421383
s_adc.hal.rx_desc = NULL;

src/current_sense/hardware_specific/esp32/esp32_adc_digi_driver.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515

1616
#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED
1717

18+
#ifdef __cplusplus
19+
extern "C" {
20+
#endif
21+
1822
typedef struct {
1923
adc_channel_t channels[SIMPLEFOC_ESP32_ADC_NUM_CHANNELS];
2024
adc_unit_t unit;
@@ -34,11 +38,17 @@ esp_err_t esp32_adc_digi_deinit(void);
3438
esp_err_t esp32_adc_digi_set_trigger(esp32_adc_digi_trigger_t mode);
3539
esp_err_t esp32_adc_digi_trigger_software(void);
3640

41+
int esp32_adc_digi_read_raw(const void *adc_buffer, int index);
42+
3743
bool esp32_adc_digi_supported(void);
3844

3945
#if SIMPLEFOC_ESP32_ADC_ETM_SUPPORTED
4046
esp_err_t esp32_adc_digi_set_etm_source(const esp32_adc_digi_etm_config_t *cfg);
4147
bool esp32_adc_digi_etm_supported(void);
4248
#endif
4349

50+
#ifdef __cplusplus
51+
}
52+
#endif
53+
4454
#endif /* SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED */

src/current_sense/hardware_specific/esp32/esp32_adc_digi_internal.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
#include <stdint.h>
1010
#include "sdkconfig.h"
1111
#include "esp_err.h"
12+
#include "hal/adc_types.h"
1213
#include "hal/dma_types.h"
14+
#include "soc/soc_caps.h"
1315

1416
/*
1517
* Low-side current sense on ESP32 uses the SAR **digital** controller (pattern
@@ -27,7 +29,7 @@
2729
* - ESP32-S3+: GDMA (esp32_adc_dma_gdma.c)
2830
*/
2931

30-
#if defined(ESP_H) && defined(ARDUINO_ARCH_ESP32)
32+
#if defined(ARDUINO_ARCH_ESP32)
3133

3234
#define SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED 1
3335

@@ -50,6 +52,25 @@
5052
#define SIMPLEFOC_ESP32_ADC_NUM_CHANNELS 2
5153
#define SIMPLEFOC_ESP32_ADC_CONVERT_LIMIT 2
5254

55+
#define ESP32_ADC_DIGI_FRAME_BYTES \
56+
(SIMPLEFOC_ESP32_ADC_NUM_CHANNELS * SOC_ADC_DIGI_RESULT_BYTES)
57+
58+
/*
59+
* DMA writes adc_digi_output_data_t[N] into adc_buffer (zero-copy).
60+
* int adc_buffer[3] must be DMA-capable and >= ESP32_ADC_DIGI_FRAME_BYTES used bytes.
61+
*/
62+
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
63+
#define ESP32_ADC_DIGI_SAMPLE_RAW(p) ((int32_t)((p)->type1.data))
64+
#else
65+
#define ESP32_ADC_DIGI_SAMPLE_RAW(p) ((int32_t)((p)->type2.data))
66+
#endif
67+
68+
static inline int esp32_adc_digi_raw_at(const void *adc_buffer, int index)
69+
{
70+
const adc_digi_output_data_t *samples = (const adc_digi_output_data_t *)adc_buffer;
71+
return (int)ESP32_ADC_DIGI_SAMPLE_RAW(&samples[index]);
72+
}
73+
5374
typedef struct esp32_adc_digi_dma_ctx esp32_adc_digi_dma_ctx_t;
5475

5576
typedef void (*esp32_adc_digi_dma_done_fn_t)(void *user);

src/current_sense/hardware_specific/esp32/esp32_mcpwm_mcu.cpp

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
#include "soc/mcpwm_reg.h"
1515
#include "soc/mcpwm_struct.h"
1616

17+
#include "esp32_adc_digi_internal.h"
1718
#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED
1819
#include "esp32_adc_digi_lowside.h"
20+
#include "esp_heap_caps.h"
1921
#endif
2022

2123
// #define SIMPLEFOC_ESP32_INTERRUPT_DEBUG
@@ -38,9 +40,9 @@
3840
* Low-side current sense on ESP32 MCPWM.
3941
*
4042
* ADC path (see esp32_adc_digi_internal.h):
41-
* - LEGACY: MCPWM ISR calls adcRead() one phase per interrupt (~10 us each).
43+
* - ADC_READ: default; MCPWM ISR + adcRead(), one phase per interrupt (~10 us each).
4244
* - DIGI_SW: ESP32 / S2 — digi+DMA; ISR only calls esp32_adc_digi_trigger_software().
43-
* - DIGI_ETM: S3+ — same digi+DMA; MCPWM TEZ starts ADC via ETM (no ADC ISR).
45+
* - DIGI_ETM: S3 / C6+ — digi+DMA; MCPWM TEZ starts ADC via ETM (no ADC ISR).
4446
*/
4547

4648
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)
5254
continue;
5355
}
5456
if (pin == p->pins[i]) {
57+
if (p->adc_lowside_path != ESP32_ADC_LOWSIDE_ADC_READ) {
58+
return esp32_adc_digi_raw_at(p->adc_buffer, no_channel) * p->adc_voltage_conv;
59+
}
5560
return p->adc_buffer[no_channel] * p->adc_voltage_conv;
5661
}
5762
no_channel++;
@@ -71,7 +76,16 @@ void *IRAM_ATTR _configureADCLowSide(const void *driver_params, const int pinA,
7176
return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED;
7277
}
7378

79+
#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED
80+
ESP32CurrentSenseParams *params = (ESP32CurrentSenseParams *)heap_caps_calloc(
81+
1, sizeof(ESP32CurrentSenseParams), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
82+
if (params == NULL) {
83+
SIMPLEFOC_ESP32_CS_DEBUG("ERROR: DMA-capable current sense alloc failed");
84+
return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED;
85+
}
86+
#else
7487
ESP32CurrentSenseParams *params = new ESP32CurrentSenseParams{};
88+
#endif
7589
int no_adc_channels = 0;
7690

7791
int adc_pins[3] = { pinA, pinB, pinC };
@@ -90,7 +104,7 @@ void *IRAM_ATTR _configureADCLowSide(const void *driver_params, const int pinA,
90104
params->no_adc_channels = no_adc_channels;
91105

92106
#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED
93-
if (esp32_adc_lowside_configure(params) != ESP32_ADC_LOWSIDE_LEGACY) {
107+
if (esp32_adc_lowside_configure(params) != ESP32_ADC_LOWSIDE_ADC_READ) {
94108
t->user_data = params;
95109
return params;
96110
}
@@ -100,8 +114,8 @@ void *IRAM_ATTR _configureADCLowSide(const void *driver_params, const int pinA,
100114
return params;
101115
}
102116

103-
static bool IRAM_ATTR _mcpwm_legacy_adc_callback(mcpwm_timer_handle_t tim, const mcpwm_timer_event_data_t *edata,
104-
void *user_data)
117+
static bool IRAM_ATTR _mcpwm_adc_read_timer_callback(mcpwm_timer_handle_t tim, const mcpwm_timer_event_data_t *edata,
118+
void *user_data)
105119
{
106120
(void)tim;
107121
(void)edata;
@@ -120,8 +134,8 @@ static bool IRAM_ATTR _mcpwm_legacy_adc_callback(mcpwm_timer_handle_t tim, const
120134
return true;
121135
}
122136

123-
static bool IRAM_ATTR _mcpwm_legacy_comparator_adc_callback(mcpwm_cmpr_handle_t cmpr,
124-
const mcpwm_compare_event_data_t *edata, void *user_data)
137+
static bool IRAM_ATTR _mcpwm_adc_read_comparator_callback(mcpwm_cmpr_handle_t cmpr,
138+
const mcpwm_compare_event_data_t *edata, void *user_data)
125139
{
126140
if (edata->direction != MCPWM_TIMER_DIRECTION_UP) {
127141
return true;
@@ -148,13 +162,13 @@ void IRAM_ATTR _startADC3PinConversionLowSide()
148162
#endif
149163
}
150164

151-
static void *esp32_mcpwm_sync_legacy_lowside(void *driver_params, ESP32CurrentSenseParams *cs)
165+
static void *esp32_mcpwm_sync_adc_read_lowside(void *driver_params, ESP32CurrentSenseParams *cs)
152166
{
153167
ESP32MCPWMDriverParams *p = (ESP32MCPWMDriverParams *)driver_params;
154168
mcpwm_timer_t *t = (mcpwm_timer_t *)p->timers[0];
155169
int group_id = p->group_id;
156170

157-
SIMPLEFOC_ESP32_CS_DEBUG("Legacy path: MCPWM ISR + adcRead() (one phase per interrupt)");
171+
SIMPLEFOC_ESP32_CS_DEBUG("ADC_READ: MCPWM ISR + adcRead() (one phase per interrupt)");
158172

159173
#ifndef SIMPLEFOC_CS_PRETRIGGER_US
160174
#define SIMPLEFOC_CS_PRETRIGGER_US 5
@@ -178,7 +192,7 @@ static void *esp32_mcpwm_sync_legacy_lowside(void *driver_params, ESP32CurrentSe
178192
"Failed to set pretrigger compare value");
179193

180194
mcpwm_comparator_event_callbacks_t cmp_cbs = {
181-
.on_reach = _mcpwm_legacy_comparator_adc_callback,
195+
.on_reach = _mcpwm_adc_read_comparator_callback,
182196
};
183197
CHECK_CS_ERR(mcpwm_comparator_register_event_callbacks((mcpwm_cmpr_handle_t)cs->pretrig_comparator, &cmp_cbs,
184198
cs),
@@ -195,7 +209,7 @@ static void *esp32_mcpwm_sync_legacy_lowside(void *driver_params, ESP32CurrentSe
195209
}
196210

197211
auto cbs = mcpwm_timer_event_callbacks_t{
198-
.on_full = _mcpwm_legacy_adc_callback,
212+
.on_full = _mcpwm_adc_read_timer_callback,
199213
};
200214
t->fsm = MCPWM_TIMER_FSM_INIT;
201215
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)
217231
}
218232

219233
#if SIMPLEFOC_ESP32_ADC_DIGI_SUPPORTED
220-
if (cs->adc_lowside_path != ESP32_ADC_LOWSIDE_LEGACY) {
234+
if (cs->adc_lowside_path != ESP32_ADC_LOWSIDE_ADC_READ) {
221235
void *r = esp32_adc_lowside_sync_mcpwm(driver_params, cs);
222236
if (r == SIMPLEFOC_CURRENT_SENSE_INIT_FAILED) {
223237
return r;
@@ -226,7 +240,7 @@ void *IRAM_ATTR _driverSyncLowSide(void *driver_params, void *cs_params)
226240
}
227241
#endif
228242

229-
return esp32_mcpwm_sync_legacy_lowside(driver_params, cs);
243+
return esp32_mcpwm_sync_adc_read_lowside(driver_params, cs);
230244
}
231245

232246
#endif

src/current_sense/hardware_specific/esp32/esp32_mcu.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313

1414
/*
1515
* Low-side ADC backend (set during LowsideCurrentSense::init):
16-
* LEGACY — MCPWM ISR + adcRead(), one phase per interrupt.
16+
* ADC_READ — default; MCPWM ISR + adcRead(), one phase per interrupt.
1717
* DIGI_SW — digi controller + DMA; ISR only starts conversion (ESP32, S2, …).
18-
* DIGI_ETM — same hardware as DIGI_SW; MCPWM TEZ starts ADC via ETM (S3+).
18+
* DIGI_ETM — digi + DMA; MCPWM TEZ starts ADC via ETM (S3, C6, …).
1919
*/
2020
enum ESP32AdcLowsidePath : uint8_t {
21-
ESP32_ADC_LOWSIDE_LEGACY = 0,
21+
ESP32_ADC_LOWSIDE_ADC_READ = 0,
2222
ESP32_ADC_LOWSIDE_DIGI_SW,
2323
ESP32_ADC_LOWSIDE_DIGI_ETM,
2424
};
@@ -27,11 +27,12 @@ enum ESP32AdcLowsidePath : uint8_t {
2727
typedef struct ESP32CurrentSenseParams {
2828
int pins[3];
2929
float adc_voltage_conv;
30+
/* ADC_READ: plain counts; DIGI_*: DMA lands adc_digi_output_data_t[0..N] here (zero-copy) */
3031
int adc_buffer[3] = {};
3132
int buffer_index = 0;
3233
int no_adc_channels = 0;
3334
void* pretrig_comparator = nullptr;
34-
ESP32AdcLowsidePath adc_lowside_path = ESP32_ADC_LOWSIDE_LEGACY;
35+
ESP32AdcLowsidePath adc_lowside_path = ESP32_ADC_LOWSIDE_ADC_READ;
3536
} ESP32CurrentSenseParams;
3637

3738
// macros for debugging wuing the simplefoc debug system

0 commit comments

Comments
 (0)