diff --git a/ports/espressif/common-hal/analogbufio/BufferedIn.c b/ports/espressif/common-hal/analogbufio/BufferedIn.c new file mode 100644 index 0000000000..e26afb283e --- /dev/null +++ b/ports/espressif/common-hal/analogbufio/BufferedIn.c @@ -0,0 +1,283 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2023 Milind Movasha + * + * SPDX-License-Identifier: BSD-3-Clause + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "common-hal/analogbufio/BufferedIn.h" +#include "shared-bindings/analogbufio/BufferedIn.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared/runtime/interrupt_char.h" +#include "py/runtime.h" +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "driver/adc.h" + +//#define DEBUG_ANALOGBUFIO + +#define NUM_SAMPLES_PER_INTERRUPT 256 +#define NUM_ADC_CHANNELS 1 +#define DMA_BUFFER_SIZE 1024 +#define ATTENUATION ADC_ATTEN_DB_0 +#define ADC_READ_TIMEOUT_MS 2000 + +#if defined(CONFIG_IDF_TARGET_ESP32) +#define ADC_RESULT_BYTE 2 +#define ADC_CONV_LIMIT_EN 1 //For ESP32, this should always be set to 1 +#elif defined(CONFIG_IDF_TARGET_ESP32S2) +#define ADC_RESULT_BYTE 2 +#define ADC_CONV_LIMIT_EN 0 +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32H2) +#define ADC_RESULT_BYTE 4 +#define ADC_CONV_LIMIT_EN 0 +#elif defined(CONFIG_IDF_TARGET_ESP32S3) +#define ADC_RESULT_BYTE 4 +#define ADC_CONV_LIMIT_EN 0 +#endif + +static adc_digi_convert_mode_t convert_mode = ADC_CONV_SINGLE_UNIT_2; +static adc_digi_output_format_t output_format = ADC_DIGI_OUTPUT_FORMAT_TYPE1; +static uint8_t adc_channel = 0; + +void common_hal_analogbufio_bufferedin_construct(analogbufio_bufferedin_obj_t *self, const mcu_pin_obj_t *pin, uint32_t sample_rate) { + uint16_t adc1_chan_mask = 0; + uint16_t adc2_chan_mask = 0; + + if( self->pin != NULL) { + mp_raise_ValueError(translate("ADC DMA already initialized")); + } + + output_format = ADC_DIGI_OUTPUT_FORMAT_TYPE1; + if(pin->adc_index == ADC_UNIT_1) { + convert_mode = ADC_CONV_SINGLE_UNIT_1; + } else { + convert_mode = ADC_CONV_SINGLE_UNIT_2; + } + + if (pin->adc_index == NO_ADC || pin->adc_channel == NO_ADC_CHANNEL) { + raise_ValueError_invalid_pin(); + } + + adc_channel = pin->adc_channel; + + /* + * Chip version Conversion Mode Output Format Type + * ESP32 1 TYPE1 + * ESP32S2 1,2,BOTH,ALTER TYPE1, TYPE2 + * ESP32C3 ALTER TYPE2 + * ESP32S3 1,2,BOTH,ALTER TYPE2 + * ESP32H3 1,2,BOTH,ALTER TYPE2 + */ + +#if defined(CONFIG_IDF_TARGET_ESP32) + if(pin->adc_index != ADC_UNIT_1) { + mp_raise_ValueError(translate("ESP32 only supports ADC1 unit")); + } +#endif +#if defined(CONFIG_IDF_TARGET_ESP32S2) +#endif +#if defined(CONFIG_IDF_TARGET_ESP32C3) + //ESP32C3 only supports alter mode + convert_mode = ADC_CONV_ALTER_UNIT; + output_format = ADC_DIGI_OUTPUT_FORMAT_TYPE2; +#endif +#if defined(CONFIG_IDF_TARGET_ESP32S3) +#endif +#if defined(CONFIG_IDF_TARGET_ESP32H2) +#endif + + self->pin = pin; + common_hal_mcu_pin_claim(pin); + + if(pin->adc_index == ADC_UNIT_1) { + adc1_chan_mask = 1 << pin->adc_channel; + } else { + adc2_chan_mask = 1 << pin->adc_channel; + } + + adc_digi_init_config_t adc_dma_config = { + .max_store_buf_size = DMA_BUFFER_SIZE, + .conv_num_each_intr = NUM_SAMPLES_PER_INTERRUPT, + .adc1_chan_mask = adc1_chan_mask, + .adc2_chan_mask = adc2_chan_mask, + }; + +#if defined(DEBUG_ANALOGBUFIO) + 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); +#endif //DEBUG_ANALOGBUFIO + esp_err_t err = adc_digi_initialize(&adc_dma_config); + if(ESP_OK != err) { + common_hal_analogbufio_bufferedin_deinit(self); + mp_raise_ValueError_varg(translate("Unable to initialize ADC DMA controller, ErrorCode:%d"),err); + } + + adc_digi_configuration_t dig_cfg = { + .conv_limit_en = ADC_CONV_LIMIT_EN, + .conv_limit_num = 250, + .pattern_num = NUM_ADC_CHANNELS, + .sample_freq_hz = sample_rate, + .conv_mode = convert_mode, + .format = output_format, + }; + +#if defined(DEBUG_ANALOGBUFIO) + 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); +#endif //DEBUG_ANALOGBUFIO + + adc_digi_pattern_config_t adc_pattern[NUM_ADC_CHANNELS] = {0}; + adc_pattern[0].atten = ATTENUATION; + adc_pattern[0].channel = pin->adc_channel; + if(pin->adc_index == ADC_UNIT_1) { + adc_pattern[0].unit = 0; + } else { + adc_pattern[0].unit = 1; + } + adc_pattern[0].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH; + + dig_cfg.adc_pattern = adc_pattern; +#if defined(DEBUG_ANALOGBUFIO) + 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); +#endif //DEBUG_ANALOGBUFIO + + err = adc_digi_controller_configure(&dig_cfg); + if(ESP_OK != err) { + common_hal_analogbufio_bufferedin_deinit(self); + mp_raise_ValueError_varg(translate("Unable to configure ADC DMA controller, ErrorCode:%d"),err); + } + err = adc_digi_start(); + if(ESP_OK != err) { + common_hal_analogbufio_bufferedin_deinit(self); + mp_raise_ValueError_varg(translate("Unable to start ADC DMA controller, ErrorCode:%d"),err); + } +} + +bool common_hal_analogbufio_bufferedin_deinited(analogbufio_bufferedin_obj_t *self) { + return self->pin == NULL; +} + +void common_hal_analogbufio_bufferedin_deinit(analogbufio_bufferedin_obj_t *self) { + if (common_hal_analogbufio_bufferedin_deinited(self)) { + return; + } + + adc_digi_stop(); + adc_digi_deinitialize(); + + // Release ADC Pin + reset_pin_number(self->pin->number); + self->pin = NULL; +} + +static bool check_valid_data(const adc_digi_output_data_t *data) +{ + unsigned int unit = data->type2.unit; + if(output_format == ADC_DIGI_OUTPUT_FORMAT_TYPE2) { + if (data->type2.channel >= SOC_ADC_CHANNEL_NUM(unit)) return false; + if (adc_channel != data->type2.channel) return false; + } else { + if( convert_mode == ADC_CONV_SINGLE_UNIT_1 ) { + unit = 0; + } else { + unit = 1; + } + if (data->type1.channel >= SOC_ADC_CHANNEL_NUM(unit)) return false; + if (adc_channel != data->type1.channel) return false; + } + if (unit > 2) return false; + return true; +} + + +uint32_t common_hal_analogbufio_bufferedin_readinto(analogbufio_bufferedin_obj_t *self, uint8_t *buffer, uint32_t len, uint8_t bytes_per_sample) { + uint8_t result[NUM_SAMPLES_PER_INTERRUPT] __attribute__ ((aligned (4))) = {0}; + uint32_t captured_samples = 0; + uint32_t captured_bytes = 0; + esp_err_t ret; + uint32_t ret_num = 0; + +#if defined(DEBUG_ANALOGBUFIO) + mp_printf(&mp_plat_print,"Required bytes: %d\n",len); +#endif //DEBUG_ANALOGBUFIO + + while(captured_bytes < len) { + ret_num = 0; + ret = adc_digi_read_bytes(result, NUM_SAMPLES_PER_INTERRUPT, &ret_num, ADC_READ_TIMEOUT_MS); + + if (ret == ESP_OK) { + for(uint32_t i=0; itype1.data; + } else { + *(uint16_t *)(void *)&buffer[captured_bytes] = ((adc_digi_output_data_t *) (void *)&result[i])->type2.data; + } + } else { + if(output_format == ADC_DIGI_OUTPUT_FORMAT_TYPE1) { + *(uint32_t *)(void *)&buffer[captured_bytes] = ((adc_digi_output_data_t *) (void *)&result[i])->type1.data; + } else { + *(uint32_t *)(void *)&buffer[captured_bytes] = ((adc_digi_output_data_t *) (void *)&result[i])->type2.data; + } + } + captured_bytes += ADC_RESULT_BYTE; + captured_samples++; + } else { + return captured_samples; + } + } else { +#if defined(DEBUG_ANALOGBUFIO) + if(ADC_RESULT_BYTE == 2) { + mp_printf(&mp_plat_print,"Invalid sample received: 0x%0x\n",*(uint16_t *)(void *)&result[i]); + } else { + mp_printf(&mp_plat_print,"Invalid sample received: 0x%0x\n",*(uint32_t *)(void *)&result[i]); + } +#endif //DEBUG_ANALOGBUFIO + return captured_samples; + } + } + } else if (ret == ESP_ERR_TIMEOUT) { +#if defined(DEBUG_ANALOGBUFIO) + mp_printf(&mp_plat_print,"ADC Timeout\n"); +#endif //DEBUG_ANALOGBUFIO + return captured_samples; + } else { +#if defined(DEBUG_ANALOGBUFIO) + mp_printf(&mp_plat_print,"adc_digi_read_bytes failed error code:%d\n",ret); +#endif //DEBUG_ANALOGBUFIO + return captured_samples; + } + } +#if defined(DEBUG_ANALOGBUFIO) + mp_printf(&mp_plat_print,"Captured bytes: %d\n",captured_bytes); +#endif //DEBUG_ANALOGBUFIO + return captured_samples; +} diff --git a/ports/espressif/common-hal/analogbufio/BufferedIn.h b/ports/espressif/common-hal/analogbufio/BufferedIn.h new file mode 100644 index 0000000000..0c9d7df3d7 --- /dev/null +++ b/ports/espressif/common-hal/analogbufio/BufferedIn.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2023 Milind Movasha + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_ESP32_COMMON_HAL_ANALOGBUFIO_BUFFEREDIN_H +#define MICROPY_INCLUDED_ESP32_COMMON_HAL_ANALOGBUFIO_BUFFEREDIN_H + +#include "common-hal/microcontroller/Pin.h" +#include "py/obj.h" + +// This is the analogbufio object +typedef struct { + mp_obj_base_t base; + const mcu_pin_obj_t *pin; + uint8_t chan; +} analogbufio_bufferedin_obj_t; + +#endif // MICROPY_INCLUDED_ESP32_COMMON_HAL_ANALOGBUFIO_BUFFEREDIN_H diff --git a/ports/espressif/common-hal/analogbufio/__init__.c b/ports/espressif/common-hal/analogbufio/__init__.c new file mode 100644 index 0000000000..b6c74b985b --- /dev/null +++ b/ports/espressif/common-hal/analogbufio/__init__.c @@ -0,0 +1 @@ +// No analogbufio module functions. diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index 460e651705..c82d622570 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -13,6 +13,7 @@ CIRCUITPY_FULL_BUILD ?= 1 # These modules are implemented in ports//common-hal: CIRCUITPY_ALARM ?= 1 CIRCUITPY_AUDIOBUSIO ?= 1 +CIRCUITPY_ANALOGBUFIO ?= 1 CIRCUITPY_AUDIOBUSIO_I2SOUT ?= 1 CIRCUITPY_AUDIOBUSIO_PDMIN ?= 0 CIRCUITPY_AUDIOCORE ?= 1