Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 71 additions & 36 deletions cores/esp32/esp32-hal-adc.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_continuous.h"
#include "esp_adc/adc_cali_scheme.h"
#include "esp_heap_caps.h"

#if CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_REV_MIN_FULL >= 300
// NOTE: These weak definitions allow successful linkage if the real efuse calibration functions are missing.
Expand Down Expand Up @@ -403,43 +404,39 @@ adc_continuous_result_t *adc_result = NULL;
static bool adcContinuousDetachBus(void *adc_unit_number) {
adc_unit_t adc_unit = (adc_unit_t)adc_unit_number - 1;

// Guard against double-cleanup: check if already cleaned up
if (adc_handle[adc_unit].adc_continuous_handle == NULL) {
return true;
} else {
esp_err_t err = adc_continuous_deinit(adc_handle[adc_unit].adc_continuous_handle);
}

// Clean up ADC driver
esp_err_t err = adc_continuous_deinit(adc_handle[adc_unit].adc_continuous_handle);
if (err != ESP_OK) {
return false;
}
adc_handle[adc_unit].adc_continuous_handle = NULL;

// Clean up calibration handle if exists
if (adc_handle[adc_unit].adc_cali_handle != NULL) {
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
return false;
}
adc_handle[adc_unit].adc_continuous_handle = NULL;
if (adc_handle[adc_unit].adc_cali_handle != NULL) {
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
return false;
}
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
return false;
}
#else
log_e("ADC Calibration scheme is not supported!");
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
return false;
#endif
}
#else
log_e("ADC Calibration scheme is not supported!");
return false;
#endif
adc_handle[adc_unit].adc_cali_handle = NULL;

//set all used pins to INIT state
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(adc_unit); channel++) {
int io_pin;
adc_oneshot_channel_to_io(adc_unit, channel, &io_pin);
if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_CONT) {
if (!perimanClearPinBus(io_pin)) {
return false;
}
}
}
}

// Don't call perimanClearPinBus() here - the peripheral manager already handles it
// This callback is only responsible for cleaning up the ADC driver itself
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says "This callback is only responsible for cleaning up the ADC driver itself" but the callback doesn't handle freeing the adc_result buffer. Consider updating the comment to clarify that the caller (analogContinuousDeinit) is responsible for freeing adc_result, or alternatively document why this separation of responsibilities exists.

Suggested change
// This callback is only responsible for cleaning up the ADC driver itself
// This callback is only responsible for cleaning up the ADC driver and calibration handles.
// It does NOT free the adc_result buffer; the caller (e.g., analogContinuousDeinit) is responsible for freeing adc_result.

Copilot uses AI. Check for mistakes.
return true;
}

Expand Down Expand Up @@ -549,6 +546,14 @@ bool analogContinuous(const uint8_t pins[], size_t pins_count, uint32_t conversi
}
#endif

#if CONFIG_IDF_TARGET_ESP32P4
// Align conversion frame size to cache line size (required for DMA on targets with cache)
uint32_t alignment_remainder = adc_handle[adc_unit].conversion_frame_size % CONFIG_CACHE_L1_CACHE_LINE_SIZE;
if (alignment_remainder != 0) {
adc_handle[adc_unit].conversion_frame_size += (CONFIG_CACHE_L1_CACHE_LINE_SIZE - alignment_remainder);
}
#endif

adc_handle[adc_unit].buffer_size = adc_handle[adc_unit].conversion_frame_size * 2;

//Conversion frame size buffer cant be bigger than 4092 bytes
Expand Down Expand Up @@ -626,8 +631,21 @@ bool analogContinuousRead(adc_continuous_result_t **buffer, uint32_t timeout_ms)
uint32_t bytes_read = 0;
uint32_t read_raw[used_adc_channels];
uint32_t read_count[used_adc_channels];
uint8_t adc_read[adc_handle[ADC_UNIT_1].conversion_frame_size];
memset(adc_read, 0xcc, sizeof(adc_read));

// Allocate DMA buffer with cache line alignment (required for ESP32-P4 and other targets with cache)
size_t buffer_size = adc_handle[ADC_UNIT_1].conversion_frame_size;
#if CONFIG_IDF_TARGET_ESP32P4
uint8_t *adc_read = (uint8_t *)heap_caps_aligned_alloc(CONFIG_CACHE_L1_CACHE_LINE_SIZE, buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
#else
uint8_t *adc_read = (uint8_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
#endif
if (adc_read == NULL) {
log_e("Failed to allocate DMA buffer");
*buffer = NULL;
return false;
}

memset(adc_read, 0xcc, buffer_size);
memset(read_raw, 0, sizeof(read_raw));
memset(read_count, 0, sizeof(read_count));

Expand All @@ -638,6 +656,8 @@ bool analogContinuousRead(adc_continuous_result_t **buffer, uint32_t timeout_ms)
} else {
log_e("Reading data failed with error: %X", err);
}
free(adc_read);
adc_read = NULL;
*buffer = NULL;
return false;
}
Expand Down Expand Up @@ -676,6 +696,8 @@ bool analogContinuousRead(adc_continuous_result_t **buffer, uint32_t timeout_ms)
}
}

free(adc_read);
adc_read = NULL;
*buffer = adc_result;
return true;

Expand Down Expand Up @@ -708,16 +730,29 @@ bool analogContinuousStop() {
}

bool analogContinuousDeinit() {
if (adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL) {
esp_err_t err = adc_continuous_deinit(adc_handle[ADC_UNIT_1].adc_continuous_handle);
if (err != ESP_OK) {
return false;
if (adc_handle[ADC_UNIT_1].adc_continuous_handle == NULL) {
log_i("ADC Continuous was not initialized");
return true;
}

// Clear all used pins from peripheral manager
// This will trigger adcContinuousDetachBus() callback which cleans up the ADC driver
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(ADC_UNIT_1); channel++) {
int io_pin;
adc_oneshot_channel_to_io(ADC_UNIT_1, channel, &io_pin);
if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_CONT) {
if (!perimanClearPinBus(io_pin)) {
return false;
}
}
}

// Free the result buffer (callback doesn't do this)
if (adc_result != NULL) {
free(adc_result);
adc_handle[ADC_UNIT_1].adc_continuous_handle = NULL;
} else {
log_i("ADC Continuous was not initialized");
adc_result = NULL;
}

return true;
}

Expand Down
3 changes: 0 additions & 3 deletions tests/validation/periman/ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
platforms:
qemu: false
wokwi: false

targets:
esp32p4: false
Loading
Loading