mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-01 03:25:15 +00:00
feat(adc): add adc_continuous_parse_data api
This commit is contained in:
@@ -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");
|
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
|
#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
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||||
if (config->conv_mode == ADC_CONV_BOTH_UNIT || config->conv_mode == ADC_CONV_ALTER_UNIT) {
|
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) {
|
} 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
|
#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
|
#endif
|
||||||
|
|
||||||
uint32_t clk_src_freq_hz = 0;
|
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);
|
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;
|
||||||
|
}
|
||||||
|
@@ -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 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_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_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
|
adc_continuous_evt_cbs_t cbs; //Callbacks
|
||||||
void *user_data; //User context
|
void *user_data; //User context
|
||||||
#if CONFIG_PM_ENABLE
|
#if CONFIG_PM_ENABLE
|
||||||
|
@@ -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
|
* 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
|
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*/
|
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_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;
|
} 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);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -27,6 +27,8 @@ extern "C" {
|
|||||||
|
|
||||||
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (0)
|
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (0)
|
||||||
|
|
||||||
|
#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0
|
||||||
|
|
||||||
/*---------------------------------------------------------------
|
/*---------------------------------------------------------------
|
||||||
Oneshot
|
Oneshot
|
||||||
---------------------------------------------------------------*/
|
---------------------------------------------------------------*/
|
||||||
|
@@ -40,6 +40,8 @@ extern "C" {
|
|||||||
|
|
||||||
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1)
|
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1)
|
||||||
|
|
||||||
|
#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0
|
||||||
|
|
||||||
/*---------------------------------------------------------------
|
/*---------------------------------------------------------------
|
||||||
Oneshot
|
Oneshot
|
||||||
---------------------------------------------------------------*/
|
---------------------------------------------------------------*/
|
||||||
|
@@ -41,6 +41,8 @@ extern "C" {
|
|||||||
|
|
||||||
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1)
|
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1)
|
||||||
#define ADC_LL_ADC_FE_ON_MODEM_DOMAIN (1)
|
#define ADC_LL_ADC_FE_ON_MODEM_DOMAIN (1)
|
||||||
|
|
||||||
|
#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0
|
||||||
/*---------------------------------------------------------------
|
/*---------------------------------------------------------------
|
||||||
Oneshot
|
Oneshot
|
||||||
---------------------------------------------------------------*/
|
---------------------------------------------------------------*/
|
||||||
|
@@ -40,6 +40,8 @@ extern "C" {
|
|||||||
|
|
||||||
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1)
|
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1)
|
||||||
#define ADC_LL_ADC_FE_ON_MODEM_DOMAIN (1)
|
#define ADC_LL_ADC_FE_ON_MODEM_DOMAIN (1)
|
||||||
|
|
||||||
|
#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0
|
||||||
/*---------------------------------------------------------------
|
/*---------------------------------------------------------------
|
||||||
Oneshot
|
Oneshot
|
||||||
---------------------------------------------------------------*/
|
---------------------------------------------------------------*/
|
||||||
|
@@ -41,6 +41,8 @@ extern "C" {
|
|||||||
|
|
||||||
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1)
|
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1)
|
||||||
#define ADC_LL_ADC_FE_ON_MODEM_DOMAIN (1)
|
#define ADC_LL_ADC_FE_ON_MODEM_DOMAIN (1)
|
||||||
|
|
||||||
|
#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0
|
||||||
/*---------------------------------------------------------------
|
/*---------------------------------------------------------------
|
||||||
Oneshot
|
Oneshot
|
||||||
---------------------------------------------------------------*/
|
---------------------------------------------------------------*/
|
||||||
|
@@ -40,6 +40,8 @@ extern "C" {
|
|||||||
|
|
||||||
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1)
|
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (1)
|
||||||
#define ADC_LL_ADC_FE_ON_MODEM_DOMAIN (1)
|
#define ADC_LL_ADC_FE_ON_MODEM_DOMAIN (1)
|
||||||
|
|
||||||
|
#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0
|
||||||
/*---------------------------------------------------------------
|
/*---------------------------------------------------------------
|
||||||
Oneshot
|
Oneshot
|
||||||
---------------------------------------------------------------*/
|
---------------------------------------------------------------*/
|
||||||
|
@@ -34,6 +34,9 @@ extern "C" {
|
|||||||
#define LP_ADC_FORCE_XPD_SAR_PD 2 // Force power down
|
#define LP_ADC_FORCE_XPD_SAR_PD 2 // Force power down
|
||||||
#define LP_ADC_FORCE_XPD_SAR_PU 3 // Force power up
|
#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)
|
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (((ADC_UNIT) == ADC_UNIT_1) ? 0 : 1)
|
||||||
|
|
||||||
/*---------------------------------------------------------------
|
/*---------------------------------------------------------------
|
||||||
|
@@ -38,6 +38,8 @@ extern "C" {
|
|||||||
|
|
||||||
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (0)
|
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (0)
|
||||||
|
|
||||||
|
#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0
|
||||||
|
|
||||||
/*---------------------------------------------------------------
|
/*---------------------------------------------------------------
|
||||||
Oneshot
|
Oneshot
|
||||||
---------------------------------------------------------------*/
|
---------------------------------------------------------------*/
|
||||||
|
@@ -40,6 +40,8 @@ extern "C" {
|
|||||||
|
|
||||||
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (0)
|
#define ADC_LL_NEED_APB_PERIPH_CLAIM(ADC_UNIT) (0)
|
||||||
|
|
||||||
|
#define ADC_LL_UNIT2_CHANNEL_SUBSTRATION 0
|
||||||
|
|
||||||
/*---------------------------------------------------------------
|
/*---------------------------------------------------------------
|
||||||
Oneshot
|
Oneshot
|
||||||
---------------------------------------------------------------*/
|
---------------------------------------------------------------*/
|
||||||
|
@@ -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`.
|
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:
|
.. _adc-continuous-hardware-limitations:
|
||||||
|
|
||||||
.. _hardware_limitations_adc_continuous:
|
.. _hardware_limitations_adc_continuous:
|
||||||
|
@@ -313,6 +313,71 @@ ADC 连续转换模式驱动使用内部缓冲池保存转换结果,缓冲池
|
|||||||
|
|
||||||
若需进一步校准,将 ADC 原始结果转换为以 mV 为单位的电压数据,请参考 :doc:`adc_calibration`。
|
若需进一步校准,将 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:
|
.. _adc-continuous-hardware-limitations:
|
||||||
|
|
||||||
.. _hardware_limitations_adc_continuous:
|
.. _hardware_limitations_adc_continuous:
|
||||||
|
@@ -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
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -14,22 +14,10 @@
|
|||||||
#include "esp_adc/adc_continuous.h"
|
#include "esp_adc/adc_continuous.h"
|
||||||
|
|
||||||
#define EXAMPLE_ADC_UNIT ADC_UNIT_1
|
#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_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
|
#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
|
#define EXAMPLE_READ_LEN 256
|
||||||
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
#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 = {
|
adc_continuous_config_t dig_cfg = {
|
||||||
.sample_freq_hz = 20 * 1000,
|
.sample_freq_hz = 20 * 1000,
|
||||||
.conv_mode = EXAMPLE_ADC_CONV_MODE,
|
.conv_mode = EXAMPLE_ADC_CONV_MODE,
|
||||||
.format = EXAMPLE_ADC_OUTPUT_TYPE,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0};
|
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);
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
|
|
||||||
char unit[] = EXAMPLE_ADC_UNIT_STR(EXAMPLE_ADC_UNIT);
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
ret = adc_continuous_read(handle, result, EXAMPLE_READ_LEN, &ret_num, 0);
|
ret = adc_continuous_read(handle, result, EXAMPLE_READ_LEN, &ret_num, 0);
|
||||||
if (ret == ESP_OK) {
|
if (ret == ESP_OK) {
|
||||||
ESP_LOGI("TASK", "ret is %x, ret_num is %"PRIu32" bytes", ret, ret_num);
|
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];
|
adc_continuous_data_t parsed_data[ret_num / SOC_ADC_DIGI_RESULT_BYTES];
|
||||||
uint32_t chan_num = EXAMPLE_ADC_GET_CHANNEL(p);
|
uint32_t num_parsed_samples = 0;
|
||||||
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 */
|
esp_err_t parse_ret = adc_continuous_parse_data(handle, result, ret_num, parsed_data, &num_parsed_samples);
|
||||||
if (chan_num < SOC_ADC_CHANNEL_NUM(EXAMPLE_ADC_UNIT)) {
|
if (parse_ret == ESP_OK) {
|
||||||
ESP_LOGI(TAG, "Unit: %s, Channel: %"PRIu32", Value: %"PRIx32, unit, chan_num, data);
|
for (int i = 0; i < num_parsed_samples; i++) {
|
||||||
} else {
|
if (parsed_data[i].valid) {
|
||||||
ESP_LOGW(TAG, "Invalid data [%s_%"PRIu32"_%"PRIx32"]", unit, chan_num, data);
|
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.
|
* 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,
|
* To avoid a task watchdog timeout, add a delay here. When you replace the way you process the data,
|
||||||
|
Reference in New Issue
Block a user