Skip to content

Commit adf5b7a

Browse files
authored
Merge pull request #7602 from milindmovasha/espressif-analogbufio
Espressif analogbufio implementation
2 parents 2c84340 + 8996fda commit adf5b7a

File tree

5 files changed

+373
-0
lines changed

5 files changed

+373
-0
lines changed

locale/circuitpython.pot

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,10 @@ msgstr ""
188188
msgid "%q must be >= %d"
189189
msgstr ""
190190

191+
#: ports/espressif/common-hal/analogbufio/BufferedIn.c
192+
msgid "%q must be array of type 'H'"
193+
msgstr ""
194+
191195
#: shared-bindings/analogbufio/BufferedIn.c
192196
msgid "%q must be a bytearray or array of type 'H' or 'B'"
193197
msgstr ""
@@ -2173,6 +2177,11 @@ msgstr ""
21732177
msgid "Unable to allocate the heap."
21742178
msgstr ""
21752179

2180+
#: ports/espressif/common-hal/analogbufio/BufferedIn.c
2181+
#, c-format
2182+
msgid "Unable to configure ADC DMA controller, ErrorCode:%d"
2183+
msgstr ""
2184+
21762185
#: ports/espressif/common-hal/busio/I2C.c
21772186
msgid "Unable to create lock"
21782187
msgstr ""
@@ -2191,10 +2200,20 @@ msgstr ""
21912200
msgid "Unable to init parser"
21922201
msgstr ""
21932202

2203+
#: ports/espressif/common-hal/analogbufio/BufferedIn.c
2204+
#, c-format
2205+
msgid "Unable to initialize ADC DMA controller, ErrorCode:%d"
2206+
msgstr ""
2207+
21942208
#: shared-module/displayio/OnDiskBitmap.c
21952209
msgid "Unable to read color palette data"
21962210
msgstr ""
21972211

2212+
#: ports/espressif/common-hal/analogbufio/BufferedIn.c
2213+
#, c-format
2214+
msgid "Unable to start ADC DMA controller, ErrorCode:%d"
2215+
msgstr ""
2216+
21982217
#: ports/espressif/common-hal/mdns/Server.c
21992218
#: ports/raspberrypi/common-hal/mdns/Server.c
22002219
msgid "Unable to start mDNS query"
Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* SPDX-FileCopyrightText: Copyright (c) 2023 Milind Movasha
7+
*
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*
10+
*
11+
* Permission is hereby granted, free of charge, to any person obtaining a copy
12+
* of this software and associated documentation files (the "Software"), to deal
13+
* in the Software without restriction, including without limitation the rights
14+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15+
* copies of the Software, and to permit persons to whom the Software is
16+
* furnished to do so, subject to the following conditions:
17+
*
18+
* The above copyright notice and this permission notice shall be included in
19+
* all copies or substantial portions of the Software.
20+
*
21+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27+
* THE SOFTWARE.
28+
*/
29+
30+
#include <stdio.h>
31+
#include "common-hal/analogbufio/BufferedIn.h"
32+
#include "shared-bindings/analogbufio/BufferedIn.h"
33+
#include "shared-bindings/microcontroller/Pin.h"
34+
#include "shared/runtime/interrupt_char.h"
35+
#include "py/runtime.h"
36+
#include <string.h>
37+
#include <stdio.h>
38+
#include "sdkconfig.h"
39+
#include "freertos/FreeRTOS.h"
40+
#include "freertos/task.h"
41+
#include "freertos/semphr.h"
42+
#include "driver/adc.h"
43+
44+
// #define DEBUG_ANALOGBUFIO
45+
46+
#define NUM_SAMPLES_PER_INTERRUPT 256
47+
#define NUM_ADC_CHANNELS 1
48+
#define DMA_BUFFER_SIZE 1024
49+
#define ATTENUATION ADC_ATTEN_DB_0
50+
#define ADC_READ_TIMEOUT_MS 2000
51+
52+
#if defined(CONFIG_IDF_TARGET_ESP32)
53+
#define ADC_RESULT_BYTE 2
54+
#define ADC_CONV_LIMIT_EN 1 // For ESP32, this should always be set to 1
55+
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
56+
#define ADC_RESULT_BYTE 2
57+
#define ADC_CONV_LIMIT_EN 0
58+
#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32H2)
59+
#define ADC_RESULT_BYTE 4
60+
#define ADC_CONV_LIMIT_EN 0
61+
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
62+
#define ADC_RESULT_BYTE 4
63+
#define ADC_CONV_LIMIT_EN 0
64+
#endif
65+
66+
static void start_dma(analogbufio_bufferedin_obj_t *self, adc_digi_convert_mode_t *convert_mode, adc_digi_output_format_t *output_format);
67+
static void stop_dma(analogbufio_bufferedin_obj_t *self);
68+
69+
void common_hal_analogbufio_bufferedin_construct(analogbufio_bufferedin_obj_t *self, const mcu_pin_obj_t *pin, uint32_t sample_rate) {
70+
self->pin = pin;
71+
self->sample_rate = sample_rate;
72+
}
73+
74+
static void start_dma(analogbufio_bufferedin_obj_t *self, adc_digi_convert_mode_t *convert_mode, adc_digi_output_format_t *output_format) {
75+
uint16_t adc1_chan_mask = 0;
76+
uint16_t adc2_chan_mask = 0;
77+
78+
const mcu_pin_obj_t *pin = self->pin;
79+
uint32_t sample_rate = self->sample_rate;
80+
81+
*output_format = ADC_DIGI_OUTPUT_FORMAT_TYPE1;
82+
if (pin->adc_index == ADC_UNIT_1) {
83+
*convert_mode = ADC_CONV_SINGLE_UNIT_1;
84+
} else {
85+
*convert_mode = ADC_CONV_SINGLE_UNIT_2;
86+
}
87+
88+
if (pin->adc_index == NO_ADC || pin->adc_channel == NO_ADC_CHANNEL) {
89+
raise_ValueError_invalid_pin();
90+
}
91+
92+
/*
93+
* Chip version Conversion Mode Output Format Type
94+
* ESP32 1 TYPE1
95+
* ESP32S2 1,2,BOTH,ALTER TYPE1, TYPE2
96+
* ESP32C3 ALTER TYPE2
97+
* ESP32S3 1,2,BOTH,ALTER TYPE2
98+
* ESP32H3 1,2,BOTH,ALTER TYPE2
99+
*/
100+
101+
#if defined(CONFIG_IDF_TARGET_ESP32)
102+
if (pin->adc_index != ADC_UNIT_1) {
103+
/*
104+
* ESP32 only supports ADC1 unit
105+
* https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf
106+
* Table 29-3
107+
*/
108+
raise_ValueError_invalid_pin();
109+
}
110+
#endif
111+
112+
#if defined(CONFIG_IDF_TARGET_ESP32C3)
113+
/* ESP32C3 only supports alter mode */
114+
*convert_mode = ADC_CONV_ALTER_UNIT;
115+
#endif
116+
117+
#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32H2)
118+
*output_format = ADC_DIGI_OUTPUT_FORMAT_TYPE2;
119+
#endif
120+
121+
common_hal_mcu_pin_claim(pin);
122+
123+
if (pin->adc_index == ADC_UNIT_1) {
124+
adc1_chan_mask = 1 << pin->adc_channel;
125+
} else {
126+
adc2_chan_mask = 1 << pin->adc_channel;
127+
}
128+
129+
adc_digi_init_config_t adc_dma_config = {
130+
.max_store_buf_size = DMA_BUFFER_SIZE,
131+
.conv_num_each_intr = NUM_SAMPLES_PER_INTERRUPT,
132+
.adc1_chan_mask = adc1_chan_mask,
133+
.adc2_chan_mask = adc2_chan_mask,
134+
};
135+
136+
#if defined(DEBUG_ANALOGBUFIO)
137+
mp_printf(&mp_plat_print,"pin:%d, ADC channel:%d, ADC index:%d, adc1_chan_mask:0x%x, adc2_chan_mask:0x%x\n",pin->number,pin->adc_channel,pin->adc_index,adc1_chan_mask,adc2_chan_mask);
138+
#endif // DEBUG_ANALOGBUFIO
139+
esp_err_t err = adc_digi_initialize(&adc_dma_config);
140+
if (ESP_OK != err) {
141+
stop_dma(self);
142+
common_hal_analogbufio_bufferedin_deinit(self);
143+
mp_raise_ValueError_varg(translate("Unable to initialize ADC DMA controller, ErrorCode:%d"),err);
144+
}
145+
146+
adc_digi_configuration_t dig_cfg = {
147+
.conv_limit_en = ADC_CONV_LIMIT_EN,
148+
.conv_limit_num = 250,
149+
.pattern_num = NUM_ADC_CHANNELS,
150+
.sample_freq_hz = sample_rate,
151+
.conv_mode = *convert_mode,
152+
.format = *output_format,
153+
};
154+
155+
#if defined(DEBUG_ANALOGBUFIO)
156+
mp_printf(&mp_plat_print,"conversion_mode:%d, format:%d, conv_limit_en:%d, sample_rate:%d\n",*convert_mode,*output_format,ADC_CONV_LIMIT_EN,sample_rate);
157+
#endif // DEBUG_ANALOGBUFIO
158+
159+
adc_digi_pattern_config_t adc_pattern[NUM_ADC_CHANNELS] = {0};
160+
adc_pattern[0].atten = ATTENUATION;
161+
adc_pattern[0].channel = pin->adc_channel;
162+
if (pin->adc_index == ADC_UNIT_1) {
163+
adc_pattern[0].unit = 0;
164+
} else {
165+
adc_pattern[0].unit = 1;
166+
}
167+
adc_pattern[0].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH;
168+
169+
dig_cfg.adc_pattern = adc_pattern;
170+
#if defined(DEBUG_ANALOGBUFIO)
171+
mp_printf(&mp_plat_print,"adc_pattern[0].channel:%d, adc_pattern[0].unit:%d, adc_pattern[0].atten:%d\n",adc_pattern[0].channel,adc_pattern[0].unit,adc_pattern[0].atten);
172+
#endif // DEBUG_ANALOGBUFIO
173+
174+
err = adc_digi_controller_configure(&dig_cfg);
175+
if (ESP_OK != err) {
176+
stop_dma(self);
177+
common_hal_analogbufio_bufferedin_deinit(self);
178+
mp_raise_ValueError_varg(translate("Unable to configure ADC DMA controller, ErrorCode:%d"),err);
179+
}
180+
err = adc_digi_start();
181+
if (ESP_OK != err) {
182+
stop_dma(self);
183+
common_hal_analogbufio_bufferedin_deinit(self);
184+
mp_raise_ValueError_varg(translate("Unable to start ADC DMA controller, ErrorCode:%d"),err);
185+
}
186+
}
187+
188+
static void stop_dma(analogbufio_bufferedin_obj_t *self) {
189+
adc_digi_stop();
190+
adc_digi_deinitialize();
191+
// Release ADC Pin
192+
reset_pin_number(self->pin->number);
193+
}
194+
195+
bool common_hal_analogbufio_bufferedin_deinited(analogbufio_bufferedin_obj_t *self) {
196+
return self->pin == NULL;
197+
}
198+
199+
void common_hal_analogbufio_bufferedin_deinit(analogbufio_bufferedin_obj_t *self) {
200+
if (common_hal_analogbufio_bufferedin_deinited(self)) {
201+
return;
202+
}
203+
self->pin = NULL;
204+
}
205+
206+
static bool check_valid_data(const adc_digi_output_data_t *data, const mcu_pin_obj_t *pin, adc_digi_convert_mode_t convert_mode, adc_digi_output_format_t output_format) {
207+
unsigned int unit = data->type2.unit;
208+
if (output_format == ADC_DIGI_OUTPUT_FORMAT_TYPE2) {
209+
if (data->type2.channel >= SOC_ADC_CHANNEL_NUM(unit)) {
210+
return false;
211+
}
212+
if (pin->adc_channel != data->type2.channel) {
213+
return false;
214+
}
215+
} else {
216+
if (convert_mode == ADC_CONV_SINGLE_UNIT_1) {
217+
unit = 0;
218+
} else {
219+
unit = 1;
220+
}
221+
#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2)
222+
if (data->type1.channel >= SOC_ADC_CHANNEL_NUM(unit)) {
223+
return false;
224+
}
225+
if (pin->adc_channel != data->type1.channel) {
226+
return false;
227+
}
228+
#endif
229+
}
230+
if (unit > 2) {
231+
return false;
232+
}
233+
return true;
234+
}
235+
236+
uint32_t common_hal_analogbufio_bufferedin_readinto(analogbufio_bufferedin_obj_t *self, uint8_t *buffer, uint32_t len, uint8_t bytes_per_sample) {
237+
uint8_t result[NUM_SAMPLES_PER_INTERRUPT] __attribute__ ((aligned(4))) = {0};
238+
uint32_t captured_samples = 0;
239+
uint32_t captured_bytes = 0;
240+
esp_err_t ret;
241+
uint32_t ret_num = 0;
242+
adc_digi_convert_mode_t convert_mode = ADC_CONV_SINGLE_UNIT_2;
243+
adc_digi_output_format_t output_format = ADC_DIGI_OUTPUT_FORMAT_TYPE1;
244+
245+
if (bytes_per_sample != 2) {
246+
mp_raise_ValueError_varg(translate("%q must be array of type 'H'"), MP_QSTR_buffer);
247+
}
248+
249+
start_dma(self, &convert_mode, &output_format);
250+
251+
#if defined(DEBUG_ANALOGBUFIO)
252+
mp_printf(&mp_plat_print,"Required bytes: %d\n",len);
253+
#endif // DEBUG_ANALOGBUFIO
254+
255+
while (captured_bytes < len) {
256+
ret_num = 0;
257+
ret = adc_digi_read_bytes(result, NUM_SAMPLES_PER_INTERRUPT, &ret_num, ADC_READ_TIMEOUT_MS);
258+
259+
if (ret == ESP_OK) {
260+
for (uint32_t i = 0; i < ret_num; i += ADC_RESULT_BYTE) {
261+
adc_digi_output_data_t *pResult = (adc_digi_output_data_t *)(void *)&result[i];
262+
if (check_valid_data(pResult, self->pin, convert_mode, output_format)) {
263+
if (captured_bytes < len) {
264+
uint16_t *pBuffer = (uint16_t *)(void *)&buffer[captured_bytes];
265+
if (output_format == ADC_DIGI_OUTPUT_FORMAT_TYPE1) {
266+
#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2)
267+
*pBuffer = pResult->type1.data;
268+
#endif
269+
} else {
270+
*pBuffer = pResult->type2.data;
271+
}
272+
captured_bytes += sizeof(uint16_t);
273+
captured_samples++;
274+
} else {
275+
stop_dma(self);
276+
return captured_samples;
277+
}
278+
} else {
279+
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
280+
// For all chips except for ESP32C3 we would receive samples only from one unit
281+
// For ESP32C3 we may receive sample from alternating units and need to ignore them
282+
#if defined(DEBUG_ANALOGBUFIO)
283+
mp_printf(&mp_plat_print,"Invalid sample received: 0x%x\n",pResult->val);
284+
#endif // DEBUG_ANALOGBUFIO
285+
stop_dma(self);
286+
return captured_samples;
287+
#endif
288+
}
289+
}
290+
} else if (ret == ESP_ERR_TIMEOUT) {
291+
#if defined(DEBUG_ANALOGBUFIO)
292+
mp_printf(&mp_plat_print,"ADC Timeout\n");
293+
#endif // DEBUG_ANALOGBUFIO
294+
stop_dma(self);
295+
return captured_samples;
296+
} else {
297+
#if defined(DEBUG_ANALOGBUFIO)
298+
mp_printf(&mp_plat_print,"adc_digi_read_bytes failed error code:%d\n",ret);
299+
#endif // DEBUG_ANALOGBUFIO
300+
stop_dma(self);
301+
return captured_samples;
302+
}
303+
}
304+
305+
stop_dma(self);
306+
#if defined(DEBUG_ANALOGBUFIO)
307+
mp_printf(&mp_plat_print,"Captured bytes: %d\n",captured_bytes);
308+
#endif // DEBUG_ANALOGBUFIO
309+
return captured_samples;
310+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* SPDX-FileCopyrightText: Copyright (c) 2023 Milind Movasha
7+
*
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*
10+
* Permission is hereby granted, free of charge, to any person obtaining a copy
11+
* of this software and associated documentation files (the "Software"), to deal
12+
* in the Software without restriction, including without limitation the rights
13+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14+
* copies of the Software, and to permit persons to whom the Software is
15+
* furnished to do so, subject to the following conditions:
16+
*
17+
* The above copyright notice and this permission notice shall be included in
18+
* all copies or substantial portions of the Software.
19+
*
20+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26+
* THE SOFTWARE.
27+
*/
28+
29+
#ifndef MICROPY_INCLUDED_ESP32_COMMON_HAL_ANALOGBUFIO_BUFFEREDIN_H
30+
#define MICROPY_INCLUDED_ESP32_COMMON_HAL_ANALOGBUFIO_BUFFEREDIN_H
31+
32+
#include "common-hal/microcontroller/Pin.h"
33+
#include "py/obj.h"
34+
35+
// This is the analogbufio object
36+
typedef struct {
37+
mp_obj_base_t base;
38+
const mcu_pin_obj_t *pin;
39+
uint32_t sample_rate;
40+
} analogbufio_bufferedin_obj_t;
41+
42+
#endif // MICROPY_INCLUDED_ESP32_COMMON_HAL_ANALOGBUFIO_BUFFEREDIN_H
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// No analogbufio module functions.

0 commit comments

Comments
 (0)