From 622c07e0b2a9a5bbc0466d4e23da7b2ff7df2fc8 Mon Sep 17 00:00:00 2001 From: gaoxu Date: Mon, 30 Jun 2025 19:26:46 +0800 Subject: [PATCH 1/2] fix(adc): fix ESP32-P4 ADC2 wrong channel num --- components/hal/esp32p4/include/hal/adc_ll.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/hal/esp32p4/include/hal/adc_ll.h b/components/hal/esp32p4/include/hal/adc_ll.h index 5a21bafc3d..906607eabb 100644 --- a/components/hal/esp32p4/include/hal/adc_ll.h +++ b/components/hal/esp32p4/include/hal/adc_ll.h @@ -359,13 +359,14 @@ static inline void adc_ll_digi_set_pattern_table(adc_unit_t adc_n, uint32_t patt uint8_t offset = (pattern_index % 4) * 6; adc_ll_digi_pattern_table_t pattern = {0}; - pattern.val = (table.atten & 0x3) | ((table.channel & 0xF) << 2); if (table.unit == ADC_UNIT_1){ + pattern.val = (table.atten & 0x3) | ((table.channel & 0xF) << 2); tab = ADC.sar1_patt_tab[index].sar1_patt_tab; //Read old register value tab &= (~(0xFC0000 >> offset)); //Clear old data tab |= ((uint32_t)(pattern.val & 0x3F) << 18) >> offset; //Fill in the new data ADC.sar1_patt_tab[index].sar1_patt_tab = tab; //Write back } else { + pattern.val = (table.atten & 0x3) | (((table.channel + 2) & 0xF) << 2); tab = ADC.sar2_patt_tab[index].sar2_patt_tab; //Read old register value tab &= (~(0xFC0000 >> offset)); //clear old data tab |= ((uint32_t)(pattern.val & 0x3F) << 18) >> offset; //Fill in the new data From 3c7e54c422224c134d188ee96ffcfd51400152f8 Mon Sep 17 00:00:00 2001 From: gaoxu Date: Thu, 28 Aug 2025 11:51:57 +0800 Subject: [PATCH 2/2] feat(adc): add adc_continuous_parse_data api --- components/esp_adc/adc_continuous.c | 94 ++++++++++++++++++- components/esp_adc/adc_continuous_internal.h | 1 + .../esp_adc/include/esp_adc/adc_continuous.h | 64 ++++++++++++- components/hal/esp32/include/hal/adc_ll.h | 2 + components/hal/esp32c3/include/hal/adc_ll.h | 2 + components/hal/esp32c5/include/hal/adc_ll.h | 2 + components/hal/esp32c6/include/hal/adc_ll.h | 2 + components/hal/esp32c61/include/hal/adc_ll.h | 2 + components/hal/esp32h2/include/hal/adc_ll.h | 2 + components/hal/esp32p4/include/hal/adc_ll.h | 3 + components/hal/esp32s2/include/hal/adc_ll.h | 2 + components/hal/esp32s3/include/hal/adc_ll.h | 2 + .../peripherals/adc/adc_continuous.rst | 65 +++++++++++++ .../peripherals/adc/adc_continuous.rst | 65 +++++++++++++ .../main/continuous_read_main.c | 49 +++++----- 15 files changed, 324 insertions(+), 33 deletions(-) diff --git a/components/esp_adc/adc_continuous.c b/components/esp_adc/adc_continuous.c index fe7173e704..07bbb836bc 100644 --- a/components/esp_adc/adc_continuous.c +++ b/components/esp_adc/adc_continuous.c @@ -477,17 +477,16 @@ esp_err_t adc_continuous_config(adc_continuous_handle_t handle, const adc_contin } ESP_RETURN_ON_FALSE(config->sample_freq_hz <= SOC_ADC_SAMPLE_FREQ_THRES_HIGH && config->sample_freq_hz >= SOC_ADC_SAMPLE_FREQ_THRES_LOW, ESP_ERR_INVALID_ARG, ADC_TAG, "ADC sampling frequency out of range"); - #if CONFIG_IDF_TARGET_ESP32 - ESP_RETURN_ON_FALSE(config->format == ADC_DIGI_OUTPUT_FORMAT_TYPE1, ESP_ERR_INVALID_ARG, ADC_TAG, "Please use type1"); + handle->format = ADC_DIGI_OUTPUT_FORMAT_TYPE1; #elif CONFIG_IDF_TARGET_ESP32S2 if (config->conv_mode == ADC_CONV_BOTH_UNIT || config->conv_mode == ADC_CONV_ALTER_UNIT) { - ESP_RETURN_ON_FALSE(config->format == ADC_DIGI_OUTPUT_FORMAT_TYPE2, ESP_ERR_INVALID_ARG, ADC_TAG, "Please use type2"); + handle->format = ADC_DIGI_OUTPUT_FORMAT_TYPE2; } else if (config->conv_mode == ADC_CONV_SINGLE_UNIT_1 || config->conv_mode == ADC_CONV_SINGLE_UNIT_2) { - ESP_RETURN_ON_FALSE(config->format == ADC_DIGI_OUTPUT_FORMAT_TYPE1, ESP_ERR_INVALID_ARG, ADC_TAG, "Please use type1"); + handle->format = ADC_DIGI_OUTPUT_FORMAT_TYPE1; } #else - ESP_RETURN_ON_FALSE(config->format == ADC_DIGI_OUTPUT_FORMAT_TYPE2, ESP_ERR_INVALID_ARG, ADC_TAG, "Please use type2"); + handle->format = ADC_DIGI_OUTPUT_FORMAT_TYPE2; #endif uint32_t clk_src_freq_hz = 0; @@ -585,3 +584,88 @@ esp_err_t adc_continuous_channel_to_io(adc_unit_t unit_id, adc_channel_t channel { return adc_channel_to_io(unit_id, channel, io_num); } + +esp_err_t adc_continuous_parse_data(adc_continuous_handle_t handle, + const uint8_t *raw_data, + uint32_t raw_data_size, + adc_continuous_data_t *parsed_data, + uint32_t *num_parsed_samples) +{ + // Parameter validation + ESP_RETURN_ON_FALSE(handle && raw_data && parsed_data && num_parsed_samples, ESP_ERR_INVALID_ARG, ADC_TAG, "invalid argument"); + + // Buffer size validation + if (raw_data_size == 0 || raw_data_size % SOC_ADC_DIGI_RESULT_BYTES != 0) { + *num_parsed_samples = 0; + return ESP_ERR_INVALID_SIZE; + } + + // Calculate number of samples + uint32_t samples_to_parse = raw_data_size / SOC_ADC_DIGI_RESULT_BYTES; + + for (uint32_t i = 0; i < samples_to_parse; i++) { + adc_digi_output_data_t *p = (adc_digi_output_data_t*)&raw_data[i * SOC_ADC_DIGI_RESULT_BYTES]; +#if CONFIG_IDF_TARGET_ESP32 + parsed_data[i].unit = ADC_UNIT_1; + parsed_data[i].channel = p->type1.channel; + parsed_data[i].raw_data = p->type1.data; + parsed_data[i].valid = (parsed_data[i].channel < SOC_ADC_CHANNEL_NUM(parsed_data[i].unit)); +#elif CONFIG_IDF_TARGET_ESP32S2 + if (handle->format == ADC_DIGI_OUTPUT_FORMAT_TYPE2) { + parsed_data[i].unit = p->type2.unit ? ADC_UNIT_2 : ADC_UNIT_1; + parsed_data[i].channel = p->type2.channel; + parsed_data[i].raw_data = p->type2.data; + parsed_data[i].valid = (parsed_data[i].channel < SOC_ADC_CHANNEL_NUM(parsed_data[i].unit)); + } else if (handle->format == ADC_DIGI_OUTPUT_FORMAT_TYPE1) { + parsed_data[i].unit = handle->use_adc1 ? ADC_UNIT_1 : ADC_UNIT_2; + parsed_data[i].channel = p->type1.channel; + parsed_data[i].raw_data = p->type1.data; + parsed_data[i].valid = (parsed_data[i].channel < SOC_ADC_CHANNEL_NUM(parsed_data[i].unit)); + } +#else +#if CONFIG_SOC_ADC_PERIPH_NUM == 1 + parsed_data[i].unit = ADC_UNIT_1; +#else + parsed_data[i].unit = p->type2.unit ? ADC_UNIT_2 : ADC_UNIT_1; +#endif + parsed_data[i].channel = (parsed_data[i].unit == ADC_UNIT_2) ? p->type2.channel - ADC_LL_UNIT2_CHANNEL_SUBSTRATION : p->type2.channel; + parsed_data[i].raw_data = p->type2.data; + parsed_data[i].valid = (parsed_data[i].channel < SOC_ADC_CHANNEL_NUM(parsed_data[i].unit)); +#endif + } + + *num_parsed_samples = samples_to_parse; + return ESP_OK; +} + +esp_err_t adc_continuous_read_parse(adc_continuous_handle_t handle, + adc_continuous_data_t *parsed_data, + uint32_t max_samples, + uint32_t *num_samples, + uint32_t timeout_ms) +{ + // Parameter validation + ESP_RETURN_ON_FALSE(handle && parsed_data && num_samples, ESP_ERR_INVALID_ARG, ADC_TAG, "invalid argument"); + + // Allocate raw data buffer based on max_samples + uint32_t raw_buffer_size = max_samples * SOC_ADC_DIGI_RESULT_BYTES; + uint8_t *raw_data = malloc(raw_buffer_size); + if (raw_data == NULL) { + *num_samples = 0; + return ESP_ERR_NO_MEM; + } + + uint32_t out_length = 0; + esp_err_t read_ret = adc_continuous_read(handle, raw_data, raw_buffer_size, &out_length, timeout_ms); + if (read_ret != ESP_OK) { + free(raw_data); + *num_samples = 0; + return read_ret; + } + + esp_err_t parse_ret = adc_continuous_parse_data(handle, raw_data, out_length, parsed_data, num_samples); + + free(raw_data); + + return parse_ret; +} diff --git a/components/esp_adc/adc_continuous_internal.h b/components/esp_adc/adc_continuous_internal.h index 55f0cdc4df..9e15fc8b2f 100644 --- a/components/esp_adc/adc_continuous_internal.h +++ b/components/esp_adc/adc_continuous_internal.h @@ -87,6 +87,7 @@ struct adc_continuous_ctx_t { adc_atten_t adc1_atten; //Attenuation for ADC1. On this chip each ADC can only support one attenuation. adc_atten_t adc2_atten; //Attenuation for ADC2. On this chip each ADC can only support one attenuation. adc_hal_digi_ctrlr_cfg_t hal_digi_ctrlr_cfg; //Hal digital controller configuration + adc_digi_output_format_t format; //ADC DMA conversion output format adc_continuous_evt_cbs_t cbs; //Callbacks void *user_data; //User context #if CONFIG_PM_ENABLE diff --git a/components/esp_adc/include/esp_adc/adc_continuous.h b/components/esp_adc/include/esp_adc/adc_continuous.h index 422e46e4db..37cd2cbdf4 100644 --- a/components/esp_adc/include/esp_adc/adc_continuous.h +++ b/components/esp_adc/include/esp_adc/adc_continuous.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -65,7 +65,7 @@ typedef struct { adc_digi_pattern_config_t *adc_pattern; ///< List of configs for each ADC channel that will be used uint32_t sample_freq_hz; /*!< The expected ADC sampling frequency in Hz. Please refer to `soc/soc_caps.h` to know available sampling frequency range*/ adc_digi_convert_mode_t conv_mode; ///< ADC DMA conversion mode, see `adc_digi_convert_mode_t`. - adc_digi_output_format_t format; ///< ADC DMA conversion output format, see `adc_digi_output_format_t`. + adc_digi_output_format_t format __attribute__((deprecated)); ///< ADC DMA conversion output format, see `adc_digi_output_format_t`. } adc_continuous_config_t; /** @@ -238,6 +238,66 @@ esp_err_t adc_continuous_io_to_channel(int io_num, adc_unit_t * const unit_id, a */ esp_err_t adc_continuous_channel_to_io(adc_unit_t unit_id, adc_channel_t channel, int * const io_num); +/** + * @brief Parsed ADC continuous mode data structure + */ +typedef struct { + adc_unit_t unit; ///< ADC unit (ADC_UNIT_1 or ADC_UNIT_2) + adc_channel_t channel; ///< ADC channel number (0-9) + uint32_t raw_data; ///< ADC raw data value (0-4095, 12-bit resolution) + bool valid; ///< Whether the data is valid +} adc_continuous_data_t; + +/** + * @brief Parse ADC continuous mode raw data + * + * @param[in] handle ADC continuous mode driver handle + * @param[in] raw_data Raw data buffer obtained from adc_continuous_read() + * @param[in] raw_data_size Size of raw data buffer in bytes + * @param[out] parsed_data Parsed data array + * @param[out] num_parsed_samples Number of samples actually parsed and stored in parsed_data + * + * @note The function will parse all available samples from raw_data. User should ensure + * parsed_data array is large enough to hold raw_data_size/SOC_ADC_DIGI_RESULT_BYTES samples. + * The function includes comprehensive bounds checking to prevent buffer overflow and integer overflow. + * + * @return + * - ESP_OK: Success + * - ESP_ERR_INVALID_ARG: Invalid arguments + * - ESP_ERR_INVALID_SIZE: raw_data_size is not aligned to SOC_ADC_DIGI_RESULT_BYTES, + * integer overflow detected, or buffer overflow detected + */ +esp_err_t adc_continuous_parse_data(adc_continuous_handle_t handle, + const uint8_t *raw_data, + uint32_t raw_data_size, + adc_continuous_data_t *parsed_data, + uint32_t *num_parsed_samples); + +/** + * @brief Read and parse ADC continuous mode data in one call + * + * @param[in] handle ADC continuous mode driver handle + * @param[out] parsed_data Parsed data array + * @param[in] max_samples Maximum number of samples that can be stored in parsed_data array + * @param[out] num_samples Number of samples actually parsed and stored in parsed_data + * @param[in] timeout_ms Timeout in milliseconds + * + * @note This function automatically handles raw data buffer allocation and cleanup. + * User only needs to provide parsed_data array and specify max_samples. + * + * @return + * - ESP_OK: Success + * - ESP_ERR_INVALID_ARG: Invalid arguments + * - ESP_ERR_INVALID_SIZE: Buffer size issues or overflow detected + * - ESP_ERR_TIMEOUT: Operation timed out + * - ESP_ERR_NO_MEM: Memory allocation failed + */ +esp_err_t adc_continuous_read_parse(adc_continuous_handle_t handle, + adc_continuous_data_t *parsed_data, + uint32_t max_samples, + uint32_t *num_samples, + uint32_t timeout_ms); + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32/include/hal/adc_ll.h b/components/hal/esp32/include/hal/adc_ll.h index 580ee81b4f..a89523fdff 100644 --- a/components/hal/esp32/include/hal/adc_ll.h +++ b/components/hal/esp32/include/hal/adc_ll.h @@ -27,6 +27,8 @@ extern "C" { #define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (0) +#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0 + /*--------------------------------------------------------------- Oneshot ---------------------------------------------------------------*/ diff --git a/components/hal/esp32c3/include/hal/adc_ll.h b/components/hal/esp32c3/include/hal/adc_ll.h index e50a829ad5..063689f5e0 100644 --- a/components/hal/esp32c3/include/hal/adc_ll.h +++ b/components/hal/esp32c3/include/hal/adc_ll.h @@ -40,6 +40,8 @@ extern "C" { #define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1) +#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0 + /*--------------------------------------------------------------- Oneshot ---------------------------------------------------------------*/ diff --git a/components/hal/esp32c5/include/hal/adc_ll.h b/components/hal/esp32c5/include/hal/adc_ll.h index c38e227aa3..e9126c40f9 100644 --- a/components/hal/esp32c5/include/hal/adc_ll.h +++ b/components/hal/esp32c5/include/hal/adc_ll.h @@ -41,6 +41,8 @@ extern "C" { #define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1) #define ADC_LL_ADC_FE_ON_MODEM_DOMAIN (1) + +#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0 /*--------------------------------------------------------------- Oneshot ---------------------------------------------------------------*/ diff --git a/components/hal/esp32c6/include/hal/adc_ll.h b/components/hal/esp32c6/include/hal/adc_ll.h index 48baef59ca..9965752c36 100644 --- a/components/hal/esp32c6/include/hal/adc_ll.h +++ b/components/hal/esp32c6/include/hal/adc_ll.h @@ -40,6 +40,8 @@ extern "C" { #define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1) #define ADC_LL_ADC_FE_ON_MODEM_DOMAIN (1) + +#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0 /*--------------------------------------------------------------- Oneshot ---------------------------------------------------------------*/ diff --git a/components/hal/esp32c61/include/hal/adc_ll.h b/components/hal/esp32c61/include/hal/adc_ll.h index 3a9056c817..fe55dda19c 100644 --- a/components/hal/esp32c61/include/hal/adc_ll.h +++ b/components/hal/esp32c61/include/hal/adc_ll.h @@ -41,6 +41,8 @@ extern "C" { #define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1) #define ADC_LL_ADC_FE_ON_MODEM_DOMAIN (1) + +#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0 /*--------------------------------------------------------------- Oneshot ---------------------------------------------------------------*/ diff --git a/components/hal/esp32h2/include/hal/adc_ll.h b/components/hal/esp32h2/include/hal/adc_ll.h index 15a3971a86..297f03e2ff 100644 --- a/components/hal/esp32h2/include/hal/adc_ll.h +++ b/components/hal/esp32h2/include/hal/adc_ll.h @@ -40,6 +40,8 @@ extern "C" { #define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1) #define ADC_LL_ADC_FE_ON_MODEM_DOMAIN (1) + +#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0 /*--------------------------------------------------------------- Oneshot ---------------------------------------------------------------*/ diff --git a/components/hal/esp32p4/include/hal/adc_ll.h b/components/hal/esp32p4/include/hal/adc_ll.h index 906607eabb..ff334307d9 100644 --- a/components/hal/esp32p4/include/hal/adc_ll.h +++ b/components/hal/esp32p4/include/hal/adc_ll.h @@ -34,6 +34,9 @@ extern "C" { #define LP_ADC_FORCE_XPD_SAR_PD 2 // Force power down #define LP_ADC_FORCE_XPD_SAR_PU 3 // Force power up +// ESP32P4 ADC2 channel is 2-7, so we need to subtract 2 to get the correct channel +#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 2 + #define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (((ADC_UNIT) == ADC_UNIT_1) ? 0 : 1) /*--------------------------------------------------------------- diff --git a/components/hal/esp32s2/include/hal/adc_ll.h b/components/hal/esp32s2/include/hal/adc_ll.h index e5ed95c572..a6e1000970 100644 --- a/components/hal/esp32s2/include/hal/adc_ll.h +++ b/components/hal/esp32s2/include/hal/adc_ll.h @@ -38,6 +38,8 @@ extern "C" { #define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (0) +#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0 + /*--------------------------------------------------------------- Oneshot ---------------------------------------------------------------*/ diff --git a/components/hal/esp32s3/include/hal/adc_ll.h b/components/hal/esp32s3/include/hal/adc_ll.h index bb55c09b43..8985ce67a4 100644 --- a/components/hal/esp32s3/include/hal/adc_ll.h +++ b/components/hal/esp32s3/include/hal/adc_ll.h @@ -40,6 +40,8 @@ extern "C" { #define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (0) +#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0 + /*--------------------------------------------------------------- Oneshot ---------------------------------------------------------------*/ diff --git a/docs/en/api-reference/peripherals/adc/adc_continuous.rst b/docs/en/api-reference/peripherals/adc/adc_continuous.rst index 8f5dfecbf7..f7b8088927 100644 --- a/docs/en/api-reference/peripherals/adc/adc_continuous.rst +++ b/docs/en/api-reference/peripherals/adc/adc_continuous.rst @@ -313,6 +313,71 @@ where: To do further calibration to convert the ADC raw result to voltage in mV, please refer to :doc:`adc_calibration`. +Parse ADC Raw Data +~~~~~~~~~~~~~~~~~~~~~ + +The raw data read from ADC continuous mode needs to be further parsed to obtain usable ADC conversion results. The function :cpp:func:`adc_continuous_parse_data` provides the functionality to parse raw data into structured ADC data. + +.. note:: + + Input buffer requirements: + + - **Length alignment**: `raw_data_size` must be a multiple of :c:macro:`SOC_ADC_DIGI_RESULT_BYTES` + - **Buffer size**: Ensure the `raw_data` buffer is large enough to hold `raw_data_size` bytes of data + +.. code:: c + + // Read raw data + uint32_t ret_num = 0; + esp_err_t ret = adc_continuous_read(handle, result, EXAMPLE_READ_LEN, &ret_num, 0); + if (ret == ESP_OK) { + // Parse raw data + adc_continuous_data_t parsed_data[ret_num / SOC_ADC_DIGI_RESULT_BYTES]; + uint32_t num_parsed_samples = 0; + + esp_err_t parse_ret = adc_continuous_parse_data(handle, result, ret_num, parsed_data, &num_parsed_samples); + if (parse_ret == ESP_OK) { + for (int i = 0; i < num_parsed_samples; i++) { + if (parsed_data[i].valid) { + ESP_LOGI(TAG, "ADC%d, Channel: %d, Value: %"PRIu32, + parsed_data[i].unit + 1, + parsed_data[i].channel, + parsed_data[i].raw_data); + } + } + } + } + +The parsed data structure :cpp:type:`adc_continuous_data_t` contains the following information: + +- :cpp:member:`adc_continuous_data_t::unit`:ADC unit (ADC_UNIT_1 or ADC_UNIT_2) +- :cpp:member:`adc_continuous_data_t::channel`:ADC channel number (0-9) +- :cpp:member:`adc_continuous_data_t::raw_data`:ADC raw data value (0-4095, 12-bit resolution) +- :cpp:member:`adc_continuous_data_t::valid`:Whether the data is valid + +Read and Parse ADC Data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To simplify the usage flow, the function :cpp:func:`adc_continuous_read_parse` is provided, which merges the read and parse operations into a single function call. + +.. code:: c + + // Using the read and parse function + adc_continuous_data_t parsed_data[64]; // User specifies maximum number of samples + uint32_t num_samples = 0; + + esp_err_t ret = adc_continuous_read_parse(handle, parsed_data, 64, &num_samples, 1000); + if (ret == ESP_OK) { + for (int i = 0; i < num_samples; i++) { + if (parsed_data[i].valid) { + ESP_LOGI(TAG, "ADC%d, Channel: %d, Value: %"PRIu32, + parsed_data[i].unit + 1, + parsed_data[i].channel, + parsed_data[i].raw_data); + } + } + } + .. _adc-continuous-hardware-limitations: .. _hardware_limitations_adc_continuous: diff --git a/docs/zh_CN/api-reference/peripherals/adc/adc_continuous.rst b/docs/zh_CN/api-reference/peripherals/adc/adc_continuous.rst index 1df126a789..4bab568dfe 100644 --- a/docs/zh_CN/api-reference/peripherals/adc/adc_continuous.rst +++ b/docs/zh_CN/api-reference/peripherals/adc/adc_continuous.rst @@ -313,6 +313,71 @@ ADC 连续转换模式驱动使用内部缓冲池保存转换结果,缓冲池 若需进一步校准,将 ADC 原始结果转换为以 mV 为单位的电压数据,请参考 :doc:`adc_calibration`。 +解析 ADC 原始数据 +~~~~~~~~~~~~~~~~~~~~~ + +ADC 连续转换模式读取的原始数据需要进一步解析才能获得可用的 ADC 转换结果。函数 :cpp:func:`adc_continuous_parse_data` 提供了将原始数据解析为结构化 ADC 数据的功能。 + +.. note:: + + 输入缓冲区要求: + + - **长度对齐**:`raw_data_size` 必须是 :c:macro:`SOC_ADC_DIGI_RESULT_BYTES` 的整数倍 + - **缓冲区大小**:确保 `raw_data` 缓冲区足够大以容纳 `raw_data_size` 字节的数据 + +.. code:: c + + // 读取原始数据 + uint32_t ret_num = 0; + esp_err_t ret = adc_continuous_read(handle, result, EXAMPLE_READ_LEN, &ret_num, 0); + if (ret == ESP_OK) { + // 解析原始数据 + adc_continuous_data_t parsed_data[ret_num / SOC_ADC_DIGI_RESULT_BYTES]; + uint32_t num_parsed_samples = 0; + + esp_err_t parse_ret = adc_continuous_parse_data(handle, result, ret_num, parsed_data, &num_parsed_samples); + if (parse_ret == ESP_OK) { + for (int i = 0; i < num_parsed_samples; i++) { + if (parsed_data[i].valid) { + ESP_LOGI(TAG, "ADC%d, Channel: %d, Value: %"PRIu32, + parsed_data[i].unit + 1, + parsed_data[i].channel, + parsed_data[i].raw_data); + } + } + } + } + +解析后的数据结构 :cpp:type:`adc_continuous_data_t` 包含以下信息: + +- :cpp:member:`adc_continuous_data_t::unit`:ADC 单元(ADC_UNIT_1 或 ADC_UNIT_2) +- :cpp:member:`adc_continuous_data_t::channel`:ADC 通道号(0-9) +- :cpp:member:`adc_continuous_data_t::raw_data`:ADC 原始数据值(0-4095,12位分辨率) +- :cpp:member:`adc_continuous_data_t::valid`:数据是否有效 + +读取并解析 ADC 数据 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +为了简化使用流程,提供了 :cpp:func:`adc_continuous_read_parse` 函数,该函数将读取和解析操作合并为一个函数调用。 + +.. code:: c + + // 使用读取并解析函数 + adc_continuous_data_t parsed_data[64]; // 用户指定最大样本数 + uint32_t num_samples = 0; + + esp_err_t ret = adc_continuous_read_parse(handle, parsed_data, 64, &num_samples, 1000); + if (ret == ESP_OK) { + for (int i = 0; i < num_samples; i++) { + if (parsed_data[i].valid) { + ESP_LOGI(TAG, "ADC%d, Channel: %d, Value: %"PRIu32, + parsed_data[i].unit + 1, + parsed_data[i].channel, + parsed_data[i].raw_data); + } + } + } + .. _adc-continuous-hardware-limitations: .. _hardware_limitations_adc_continuous: diff --git a/examples/peripherals/adc/continuous_read/main/continuous_read_main.c b/examples/peripherals/adc/continuous_read/main/continuous_read_main.c index 307b8da979..514fb8c5ca 100644 --- a/examples/peripherals/adc/continuous_read/main/continuous_read_main.c +++ b/examples/peripherals/adc/continuous_read/main/continuous_read_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,22 +14,10 @@ #include "esp_adc/adc_continuous.h" #define EXAMPLE_ADC_UNIT ADC_UNIT_1 -#define _EXAMPLE_ADC_UNIT_STR(unit) #unit -#define EXAMPLE_ADC_UNIT_STR(unit) _EXAMPLE_ADC_UNIT_STR(unit) #define EXAMPLE_ADC_CONV_MODE ADC_CONV_SINGLE_UNIT_1 -#define EXAMPLE_ADC_ATTEN ADC_ATTEN_DB_0 +#define EXAMPLE_ADC_ATTEN ADC_ATTEN_DB_12 #define EXAMPLE_ADC_BIT_WIDTH SOC_ADC_DIGI_MAX_BITWIDTH -#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 -#define EXAMPLE_ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE1 -#define EXAMPLE_ADC_GET_CHANNEL(p_data) ((p_data)->type1.channel) -#define EXAMPLE_ADC_GET_DATA(p_data) ((p_data)->type1.data) -#else -#define EXAMPLE_ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2 -#define EXAMPLE_ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel) -#define EXAMPLE_ADC_GET_DATA(p_data) ((p_data)->type2.data) -#endif - #define EXAMPLE_READ_LEN 256 #if CONFIG_IDF_TARGET_ESP32 @@ -63,7 +51,6 @@ static void continuous_adc_init(adc_channel_t *channel, uint8_t channel_num, adc adc_continuous_config_t dig_cfg = { .sample_freq_hz = 20 * 1000, .conv_mode = EXAMPLE_ADC_CONV_MODE, - .format = EXAMPLE_ADC_OUTPUT_TYPE, }; adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0}; @@ -114,23 +101,33 @@ void app_main(void) */ ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - char unit[] = EXAMPLE_ADC_UNIT_STR(EXAMPLE_ADC_UNIT); - while (1) { ret = adc_continuous_read(handle, result, EXAMPLE_READ_LEN, &ret_num, 0); if (ret == ESP_OK) { ESP_LOGI("TASK", "ret is %x, ret_num is %"PRIu32" bytes", ret, ret_num); - for (int i = 0; i < ret_num; i += SOC_ADC_DIGI_RESULT_BYTES) { - adc_digi_output_data_t *p = (adc_digi_output_data_t*)&result[i]; - uint32_t chan_num = EXAMPLE_ADC_GET_CHANNEL(p); - uint32_t data = EXAMPLE_ADC_GET_DATA(p); - /* Check the channel number validation, the data is invalid if the channel num exceed the maximum channel */ - if (chan_num < SOC_ADC_CHANNEL_NUM(EXAMPLE_ADC_UNIT)) { - ESP_LOGI(TAG, "Unit: %s, Channel: %"PRIu32", Value: %"PRIx32, unit, chan_num, data); - } else { - ESP_LOGW(TAG, "Invalid data [%s_%"PRIu32"_%"PRIx32"]", unit, chan_num, data); + + adc_continuous_data_t parsed_data[ret_num / SOC_ADC_DIGI_RESULT_BYTES]; + uint32_t num_parsed_samples = 0; + + esp_err_t parse_ret = adc_continuous_parse_data(handle, result, ret_num, parsed_data, &num_parsed_samples); + if (parse_ret == ESP_OK) { + for (int i = 0; i < num_parsed_samples; i++) { + if (parsed_data[i].valid) { + ESP_LOGI(TAG, "ADC%d, Channel: %d, Value: %"PRIu32, + parsed_data[i].unit + 1, + parsed_data[i].channel, + parsed_data[i].raw_data); + } else { + ESP_LOGW(TAG, "Invalid data [ADC%d_Ch%d_%"PRIu32"]", + parsed_data[i].unit + 1, + parsed_data[i].channel, + parsed_data[i].raw_data); + } } + } else { + ESP_LOGE(TAG, "Data parsing failed: %s", esp_err_to_name(parse_ret)); } + /** * Because printing is slow, so every time you call `ulTaskNotifyTake`, it will immediately return. * To avoid a task watchdog timeout, add a delay here. When you replace the way you process the data,