Skip to content

Commit 5c6aa20

Browse files
committed
Merge branch 'feat/add_adc_mic_component' into 'master'
feat(adc_mic): add adc mic component Closes AEG-2189 See merge request ae_group/esp-iot-solution!1238
2 parents 72358cc + c744d74 commit 5c6aa20

File tree

22 files changed

+913
-2
lines changed

22 files changed

+913
-2
lines changed

.github/workflows/upload_component.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ jobs:
1616
uses: espressif/upload-components-ci-action@v1
1717
with:
1818
directories: >
19+
components/audio/adc_mic;
1920
components/audio/pwm_audio;
2021
components/avi_player;
2122
components/bluetooth/ble_conn_mgr;

.gitlab/ci/build.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,14 @@ build_example_utilities_xz_decompress_file:
944944
variables:
945945
EXAMPLE_DIR: examples/utilities/xz_decompress_file
946946

947+
build_components_audio_adc_mic_test_apps:
948+
extends:
949+
- .build_examples_template
950+
- .rules:build:component_audio_adc_mic_test_apps
951+
- .build_idf_version_greater_equal_v5_0
952+
variables:
953+
EXAMPLE_DIR: components/audio/adc_mic/test_apps
954+
947955
build_components_audio_pwm_audio_test_apps:
948956
extends:
949957
- .build_examples_template

.gitlab/ci/rules.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
- "pytest.ini"
1111

1212
# components folder, in the alphabetic order
13+
.patterns-components_audio_adc_mic: &patterns-components_audio_adc_mic
14+
- "components/audio/adc_mic/**/*"
15+
1316
.patterns-components_audio_dac_audio: &patterns-components_audio_dac_audio
1417
- "components/audio/dac_audio/**/*"
1518

@@ -1599,6 +1602,17 @@
15991602
changes: *patterns-example_relinker
16001603

16011604
# rules for ut
1605+
.rules:build:component_audio_adc_mic_test_apps:
1606+
rules:
1607+
- <<: *if-protected
1608+
- <<: *if-label-build
1609+
- <<: *if-label-target_test
1610+
- <<: *if-trigger-job
1611+
- <<: *if-dev-push
1612+
changes: *patterns-build_system
1613+
- <<: *if-dev-push
1614+
changes: *patterns-components_audio_adc_mic
1615+
16021616
.rules:build:component_audio_dac_audio_test:
16031617
rules:
16041618
- <<: *if-protected

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ The registered components in ESP-IoT-Solution are listed below:
5656

5757
| Component | Version |
5858
| --- | --- |
59+
| [adc_mic](https://components.espressif.com/components/espressif/adc_mic) | [![Component Registry](https://components.espressif.com/components/espressif/adc_mic/badge.svg)](https://components.espressif.com/components/espressif/adc_mic) |
5960
| [aht20](https://components.espressif.com/components/espressif/aht20) | [![Component Registry](https://components.espressif.com/components/espressif/aht20/badge.svg)](https://components.espressif.com/components/espressif/aht20) |
6061
| [apds9960](https://components.espressif.com/components/espressif/apds9960) | [![Component Registry](https://components.espressif.com/components/espressif/apds9960/badge.svg)](https://components.espressif.com/components/espressif/apds9960) |
6162
| [at24c02](https://components.espressif.com/components/espressif/at24c02) | [![Component Registry](https://components.espressif.com/components/espressif/at24c02/badge.svg)](https://components.espressif.com/components/espressif/at24c02) |

README_CN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ ESP-IoT-Solution 中注册的组件如下:
5656

5757
| 组件 | 版本 |
5858
| --- | --- |
59+
| [adc_mic](https://components.espressif.com/components/espressif/adc_mic) | [![Component Registry](https://components.espressif.com/components/espressif/adc_mic/badge.svg)](https://components.espressif.com/components/espressif/adc_mic) |
5960
| [aht20](https://components.espressif.com/components/espressif/aht20) | [![Component Registry](https://components.espressif.com/components/espressif/aht20/badge.svg)](https://components.espressif.com/components/espressif/aht20) |
6061
| [apds9960](https://components.espressif.com/components/espressif/apds9960) | [![Component Registry](https://components.espressif.com/components/espressif/apds9960/badge.svg)](https://components.espressif.com/components/espressif/apds9960) |
6162
| [at24c02](https://components.espressif.com/components/espressif/at24c02) | [![Component Registry](https://components.espressif.com/components/espressif/at24c02/badge.svg)](https://components.espressif.com/components/espressif/at24c02) |

components/.build-rules.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
components/audio/adc_mic/test_apps:
2+
enable:
3+
- if: SOC_ADC_DMA_SUPPORTED == 1
4+
disable:
5+
- if: IDF_TARGET in ["esp32h2"] and (IDF_VERSION_MAJOR == 5 and IDF_VERSION_MINOR == 0)
6+
17
components/audio/pwm_audio/test_apps:
28
enable:
39
- if: INCLUDE_DEFAULT == 1
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
idf_component_register(SRCS "adc_mic.c"
2+
INCLUDE_DIRS "."
3+
REQUIRES "esp_adc")
4+
5+
include(package_manager)
6+
cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})

components/audio/adc_mic/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[![Component Registry](https://components.espressif.com/components/espressif/adc_mic/badge.svg)](https://components.espressif.com/components/espressif/adc_mic)
2+
3+
# Component: ADC Mic
4+
5+
The **ADC MIC** can collect analog microphone data through the ADC.
6+
7+
Currently supported features:
8+
* Supports single-channel/multi-channel ADC audio sampling.
9+
* Compliant with the esp_codec_dev architecture.
10+
11+
## Add component to your project
12+
13+
Please use the component manager command `add-dependency` to add the `adc_mic` to your project's dependency, during the `CMake` step the component will be downloaded automatically
14+
15+
```
16+
idf.py add-dependency "espressif/adc_mic=*"
17+
```
18+
19+
## Known Issues
20+
21+
* In IDF versions lower than ``5.2``, the internal ring buffer does not automatically clear old data when full. Please ensure old data is cleared in time or manually refresh the buffer.

components/audio/adc_mic/adc_mic.c

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <string.h>
8+
#include "esp_log.h"
9+
#include "esp_check.h"
10+
#include "freertos/FreeRTOS.h"
11+
#include "freertos/task.h"
12+
#include "freertos/queue.h"
13+
#include "audio_codec_data_if.h"
14+
#include "adc_mic.h"
15+
#include "esp_codec_dev_defaults.h"
16+
#include "esp_idf_version.h"
17+
#include "soc/soc_caps.h"
18+
19+
static const char *TAG = "adc_if";
20+
typedef struct {
21+
audio_codec_data_if_t base;
22+
adc_continuous_handle_t handle;
23+
adc_unit_t unit_id; /*!< ADC unit */
24+
adc_atten_t atten; /*!< ADC attenuation */
25+
bool if_config_by_user; /*!< If the ADC is configured by the user, the internal initialization will not be performed. */
26+
bool is_open;
27+
bool enable;
28+
uint8_t *adc_channel;
29+
uint8_t adc_channel_num;
30+
uint32_t conv_frame_size;
31+
#if SOC_ADC_DIGI_RESULT_BYTES != 2
32+
uint32_t *conv_data;
33+
#endif
34+
} adc_data_t;
35+
36+
static esp_err_t adc_channel_config(adc_data_t *adc_data, uint8_t *channel, uint8_t channel_num, int sample_freq_hz, adc_atten_t atten)
37+
{
38+
adc_digi_convert_mode_t conv_mode = adc_data->unit_id == ADC_UNIT_1 ? ADC_CONV_SINGLE_UNIT_1 : ADC_CONV_SINGLE_UNIT_2;
39+
adc_continuous_config_t dig_cfg = {
40+
.sample_freq_hz = sample_freq_hz * channel_num,
41+
.conv_mode = conv_mode,
42+
};
43+
44+
/**
45+
* @brief For ESP32 and ESP32-S2, only `type1` can obtain 12-bit data.
46+
*
47+
*/
48+
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
49+
dig_cfg.format = ADC_DIGI_OUTPUT_FORMAT_TYPE1;
50+
#else
51+
dig_cfg.format = ADC_DIGI_OUTPUT_FORMAT_TYPE2;
52+
#endif
53+
54+
adc_digi_pattern_config_t *adc_pattern = calloc(channel_num, sizeof(adc_digi_pattern_config_t));
55+
ESP_RETURN_ON_FALSE(adc_pattern != NULL, ESP_ERR_NO_MEM, TAG, "adc_pattern is NULL");
56+
dig_cfg.pattern_num = channel_num;
57+
for (int i = 0; i < dig_cfg.pattern_num; i++) {
58+
adc_pattern[i].atten = atten;
59+
adc_pattern[i].channel = channel[i];
60+
adc_pattern[i].unit = adc_data->unit_id;
61+
adc_pattern[i].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH;
62+
}
63+
dig_cfg.adc_pattern = adc_pattern;
64+
esp_err_t ret = adc_continuous_config(adc_data->handle, &dig_cfg);
65+
ESP_RETURN_ON_ERROR(ret, TAG, "adc_continuous_config failed");
66+
free(adc_pattern);
67+
return ESP_OK;
68+
}
69+
70+
static bool _adc_data_is_open(const audio_codec_data_if_t *h)
71+
{
72+
adc_data_t *adc_data = (adc_data_t *) h;
73+
if (adc_data) {
74+
return adc_data->is_open;
75+
}
76+
return false;
77+
}
78+
79+
static int _adc_data_enable(const audio_codec_data_if_t *h, esp_codec_dev_type_t dev_type, bool enable)
80+
{
81+
adc_data_t *adc_data = (adc_data_t *) h;
82+
ESP_RETURN_ON_FALSE(adc_data != NULL, ESP_CODEC_DEV_INVALID_ARG, TAG, "adc_data is NULL");
83+
ESP_RETURN_ON_FALSE(adc_data->is_open, ESP_CODEC_DEV_WRONG_STATE, TAG, "adc_data is not open");
84+
ESP_RETURN_ON_FALSE(dev_type == ESP_CODEC_DEV_TYPE_IN, ESP_CODEC_DEV_INVALID_ARG, TAG, "Invalid device type");
85+
esp_err_t ret = ESP_OK;
86+
if (enable) {
87+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0)
88+
ret = adc_continuous_flush_pool(adc_data->handle);
89+
ESP_RETURN_ON_FALSE(ret == ESP_OK, ESP_CODEC_DEV_DRV_ERR, TAG, "adc_continuous_flush_pool failed");
90+
#endif
91+
ret = adc_continuous_start(adc_data->handle);
92+
ESP_RETURN_ON_FALSE(ret == ESP_OK, ESP_CODEC_DEV_DRV_ERR, TAG, "adc_continuous_start failed");
93+
} else {
94+
ret = adc_continuous_stop(adc_data->handle);
95+
ESP_RETURN_ON_FALSE(ret == ESP_OK, ESP_CODEC_DEV_DRV_ERR, TAG, "adc_continuous_stop failed");
96+
}
97+
98+
adc_data->enable = enable;
99+
100+
return ret;
101+
}
102+
103+
/**
104+
* @brief Read ADC data from the continuous ADC interface.
105+
*
106+
* This function reads ADC data from the continuous ADC interface and processes the raw values.
107+
* The data is left-shifted to amplify the audio signal. The function ensures that the requested
108+
* read size is valid and properly aligned with the ADC data format.
109+
*
110+
* @param h Pointer to the audio codec data interface handle.
111+
* @param data Pointer to the buffer where the ADC data will be stored.
112+
* @param size Number of bytes to read, must be a multiple of SOC_ADC_DIGI_DATA_BYTES_PER_CONV.
113+
* @return int Returns ESP_CODEC_DEV_OK on success, or an error code on failure.
114+
*/
115+
static int _adc_data_read(const audio_codec_data_if_t *h, uint8_t *data, int size)
116+
{
117+
adc_data_t *adc_data = (adc_data_t *) h;
118+
ESP_RETURN_ON_FALSE(adc_data != NULL, ESP_CODEC_DEV_INVALID_ARG, TAG, "adc_data is NULL");
119+
ESP_RETURN_ON_FALSE(adc_data->is_open, ESP_CODEC_DEV_WRONG_STATE, TAG, "adc_data is not open");
120+
121+
if (size % SOC_ADC_DIGI_DATA_BYTES_PER_CONV != 0) {
122+
ESP_LOGE(TAG, "Invalid size, must be multiple of %d", SOC_ADC_DIGI_DATA_BYTES_PER_CONV);
123+
}
124+
125+
ESP_LOGV(TAG, "adc mic read %d bytes", size);
126+
127+
// Note: must be 16bit
128+
uint32_t ret_num = 0;
129+
int left_size = size;
130+
int cnt = 0;
131+
#if SOC_ADC_DIGI_RESULT_BYTES == 2
132+
while (left_size > 0) {
133+
int request_size = left_size > adc_data->conv_frame_size ? adc_data->conv_frame_size : left_size;
134+
adc_continuous_read(adc_data->handle, &data[cnt], request_size, &ret_num, portMAX_DELAY);
135+
adc_digi_output_data_t *buffer = (adc_digi_output_data_t *)&data[cnt];
136+
uint16_t *p = (uint16_t *)&data[cnt];
137+
size_t item_count = ret_num / sizeof(adc_digi_output_data_t);
138+
for (int i = 0; i < item_count; i++) {
139+
uint16_t raw_value = buffer[i].val;
140+
// Left shift to amplify audio.
141+
p[i] = (raw_value << 4) - 32768;
142+
}
143+
144+
cnt += ret_num;
145+
left_size -= ret_num;
146+
}
147+
#else
148+
while (left_size > 0) {
149+
int request_size = left_size > adc_data->conv_frame_size ? adc_data->conv_frame_size : left_size;
150+
adc_continuous_read(adc_data->handle, (uint8_t *)adc_data->conv_data, request_size * 2, &ret_num, portMAX_DELAY);
151+
adc_digi_output_data_t *buffer = (adc_digi_output_data_t *)adc_data->conv_data;
152+
uint16_t *p = (uint16_t *)&data[cnt];
153+
size_t item_count = ret_num / sizeof(adc_digi_output_data_t);
154+
for (int i = 0; i < item_count; i++) {
155+
uint16_t raw_value = buffer[i].val & 0xFFFF;
156+
// Left shift to amplify audio.
157+
p[i] = (raw_value << 4) - 32768;
158+
}
159+
cnt += ret_num / 2;
160+
left_size -= ret_num / 2;
161+
}
162+
#endif
163+
164+
return ESP_CODEC_DEV_OK;
165+
}
166+
167+
static int _adc_data_close(const audio_codec_data_if_t *h)
168+
{
169+
adc_data_t *adc_data = (adc_data_t *) h;
170+
ESP_RETURN_ON_FALSE(adc_data != NULL, ESP_CODEC_DEV_INVALID_ARG, TAG, "adc_data is NULL");
171+
ESP_RETURN_ON_FALSE(adc_data->enable == false, ESP_CODEC_DEV_WRONG_STATE, TAG, "adc_data is enable, please disable it first");
172+
173+
if (!adc_data->if_config_by_user) {
174+
adc_continuous_deinit(adc_data->handle);
175+
}
176+
177+
#if SOC_ADC_DIGI_RESULT_BYTES != 2
178+
if (adc_data->conv_data) {
179+
free(adc_data->conv_data);
180+
}
181+
#endif
182+
if (adc_data->adc_channel) {
183+
free(adc_data->adc_channel);
184+
}
185+
186+
// adc_data will be deleted by the esp_code_dev interface.
187+
return ESP_CODEC_DEV_OK;
188+
}
189+
190+
/**
191+
* @brief Configure the format of ADC data.
192+
*
193+
* This function sets the sample format for the ADC data interface. It ensures that the ADC is properly configured
194+
* with the expected sample rate, channel count, and bit depth. Before setting the format, the ADC should be stopped.
195+
*
196+
* @note adc_data only supports 16-bit samples
197+
*
198+
* @param h Pointer to the audio codec data interface handle.
199+
* @param dev_type Type of the codec device (not used in this function).
200+
* @param fs Pointer to the sample format structure, specifying sample rate, channels, and bit depth.
201+
* @return int Returns ESP_CODEC_DEV_OK on success, or an error code on failure.
202+
*/
203+
static int _adc_data_set_fmt(const audio_codec_data_if_t *h, esp_codec_dev_type_t dev_type, esp_codec_dev_sample_info_t *fs)
204+
{
205+
adc_data_t *adc_data = (adc_data_t *) h;
206+
ESP_RETURN_ON_FALSE(adc_data != NULL, ESP_CODEC_DEV_INVALID_ARG, TAG, "adc_data is NULL");
207+
ESP_RETURN_ON_FALSE(adc_data->is_open, ESP_CODEC_DEV_WRONG_STATE, TAG, "adc_data is not open");
208+
ESP_RETURN_ON_FALSE(fs->bits_per_sample == 16, ESP_CODEC_DEV_INVALID_ARG, TAG, "adc_data only support 16-bit samples");
209+
ESP_RETURN_ON_FALSE(fs->channel == adc_data->adc_channel_num, ESP_CODEC_DEV_INVALID_ARG, TAG, "channel num not match");
210+
211+
esp_err_t ret = adc_channel_config(adc_data, adc_data->adc_channel, adc_data->adc_channel_num, fs->sample_rate, adc_data->atten);
212+
return ret == ESP_OK ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
213+
}
214+
215+
const audio_codec_data_if_t *audio_codec_new_adc_data(audio_codec_adc_cfg_t *adc_cfg)
216+
{
217+
ESP_LOGI(TAG, "ADC MIC Version: %d.%d.%d", ADC_MIC_VER_MAJOR, ADC_MIC_VER_MINOR, ADC_MIC_VER_PATCH);
218+
ESP_RETURN_ON_FALSE(adc_cfg != NULL, NULL, TAG, "adc_cfg is NULL");
219+
esp_err_t ret = ESP_OK;
220+
221+
adc_data_t *adc_data = (adc_data_t *)calloc(1, sizeof(adc_data_t));
222+
ESP_RETURN_ON_FALSE(adc_data != NULL, NULL, TAG, "calloc failed");
223+
224+
adc_data->conv_frame_size = adc_cfg->conv_frame_size;
225+
/**
226+
* @brief If the conversion result takes up 4 bytes, we need to use a temporary
227+
* buffer for the conversion instead of copying it directly into the provided
228+
* buffer, as this would result in some efficiency loss.
229+
*/
230+
#if SOC_ADC_DIGI_RESULT_BYTES != 2
231+
adc_data->conv_data = calloc(1, adc_data->conv_frame_size * 2);
232+
ESP_GOTO_ON_FALSE(adc_data->conv_data != NULL, ESP_CODEC_DEV_NO_MEM, err, TAG, "calloc failed");
233+
#endif
234+
235+
adc_data->adc_channel = malloc(adc_cfg->adc_channel_num * sizeof(uint8_t));
236+
ESP_GOTO_ON_FALSE(adc_data->adc_channel != NULL, ESP_CODEC_DEV_NO_MEM, err, TAG, "malloc failed");
237+
adc_data->adc_channel_num = adc_cfg->adc_channel_num;
238+
239+
if (adc_cfg->handle == NULL) {
240+
adc_continuous_handle_cfg_t adc_config = {
241+
.max_store_buf_size = adc_cfg->max_store_buf_size,
242+
.conv_frame_size = adc_cfg->conv_frame_size,
243+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0)
244+
.flags.flush_pool = true,
245+
#endif
246+
};
247+
248+
ret = adc_continuous_new_handle(&adc_config, &adc_data->handle);
249+
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_CODEC_DEV_DRV_ERR, err, TAG, "adc_continuous_new_handle failed");
250+
adc_data->if_config_by_user = false;
251+
} else {
252+
adc_data->handle = *adc_cfg->handle;
253+
adc_data->if_config_by_user = true;
254+
}
255+
256+
adc_data->unit_id = adc_cfg->unit_id;
257+
adc_data->atten = adc_cfg->atten;
258+
259+
memcpy(adc_data->adc_channel, adc_cfg->adc_channel_list, adc_cfg->adc_channel_num * sizeof(uint8_t));
260+
adc_channel_config(adc_data, adc_data->adc_channel, adc_data->adc_channel_num, adc_cfg->sample_rate_hz, adc_cfg->atten);
261+
262+
adc_data->base.open = NULL;
263+
adc_data->base.is_open = _adc_data_is_open;
264+
adc_data->base.enable = _adc_data_enable;
265+
adc_data->base.read = _adc_data_read;
266+
adc_data->base.write = NULL;
267+
adc_data->base.set_fmt = _adc_data_set_fmt;
268+
adc_data->base.close = _adc_data_close;
269+
270+
adc_data->is_open = true;
271+
return &adc_data->base;
272+
273+
err:
274+
if (adc_data) {
275+
free(adc_data);
276+
}
277+
return NULL;
278+
}

0 commit comments

Comments
 (0)