/* * 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 "bindings/espidf/__init__.h" #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" // #define DEBUG_ANALOGBUFIO #define NUM_SAMPLES_PER_INTERRUPT 256 #define NUM_ADC_CHANNELS 1 #define DMA_BUFFER_SIZE 1024 #define ATTENUATION ADC_ATTEN_DB_11 #define ADC_READ_TIMEOUT_MS 2000 #define ADC_PIN_MAX_VALUE 0xfff #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_ESP32C6) || 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 #else #error No known CONFIG_IDF_TARGET_xxx found #endif static void start_dma(analogbufio_bufferedin_obj_t *self, adc_digi_convert_mode_t *convert_mode, adc_digi_output_format_t *output_format); static void stop_dma(analogbufio_bufferedin_obj_t *self); void common_hal_analogbufio_bufferedin_construct(analogbufio_bufferedin_obj_t *self, const mcu_pin_obj_t *pin, uint32_t sample_rate) { self->pin = pin; self->sample_rate = sample_rate; if (pin->adc_index == NO_ADC || pin->adc_channel == NO_ADC_CHANNEL) { raise_ValueError_invalid_pin(); } #if defined(CONFIG_IDF_TARGET_ESP32) if (pin->adc_index != ADC_UNIT_1) { /* * ESP32 only supports ADC1 unit * https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf * Table 29-3 */ raise_ValueError_invalid_pin(); } #endif // C3 and S3 have errata related to ADC2 and continuous mode. #if (defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)) && !defined(CONFIG_ADC_CONTINUOUS_FORCE_USE_ADC2_ON_C3_S3) if (pin->adc_index != ADC_UNIT_1) { raise_ValueError_invalid_pin(); } #endif common_hal_mcu_pin_claim(pin); } static void start_dma(analogbufio_bufferedin_obj_t *self, adc_digi_convert_mode_t *convert_mode, adc_digi_output_format_t *output_format) { const mcu_pin_obj_t *pin = self->pin; uint32_t sample_rate = self->sample_rate; *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; } /* * 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_ESP32C3) /* ESP32C3 only supports alter mode */ *convert_mode = ADC_CONV_ALTER_UNIT; #endif #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32H2) *output_format = ADC_DIGI_OUTPUT_FORMAT_TYPE2; #endif adc_continuous_handle_cfg_t adc_dma_config = { .max_store_buf_size = DMA_BUFFER_SIZE, .conv_frame_size = NUM_SAMPLES_PER_INTERRUPT * SOC_ADC_DIGI_DATA_BYTES_PER_CONV }; #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_continuous_new_handle(&adc_dma_config, &self->handle); if (ESP_OK != err) { stop_dma(self); CHECK_ESP_RESULT(err); } adc_continuous_config_t dig_cfg = { .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_continuous_config(self->handle, &dig_cfg); if (ESP_OK != err) { stop_dma(self); CHECK_ESP_RESULT(err); } err = adc_continuous_start(self->handle); if (ESP_OK != err) { stop_dma(self); CHECK_ESP_RESULT(err); } self->started = true; } static void stop_dma(analogbufio_bufferedin_obj_t *self) { if (self->started) { adc_continuous_stop(self->handle); self->started = false; } if (self->handle != NULL) { adc_continuous_deinit(self->handle); self->handle = NULL; } } 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; } // Release ADC Pin reset_pin_number(self->pin->number); self->pin = NULL; } 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) { unsigned int unit; #if SOC_ADC_PERIPH_NUM == 1 unit = 0; #else unit = data->type2.unit; #endif if (output_format == ADC_DIGI_OUTPUT_FORMAT_TYPE2) { if (data->type2.channel >= SOC_ADC_CHANNEL_NUM(unit)) { return false; } if (pin->adc_channel != data->type2.channel) { return false; } } else { if (convert_mode == ADC_CONV_SINGLE_UNIT_1) { unit = 0; } else { unit = 1; } #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) if (data->type1.channel >= SOC_ADC_CHANNEL_NUM(unit)) { return false; } if (pin->adc_channel != data->type1.channel) { return false; } #endif } 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 * SOC_ADC_DIGI_DATA_BYTES_PER_CONV] __attribute__ ((aligned(4))) = {0}; uint32_t captured_samples = 0; uint32_t captured_bytes = 0; esp_err_t ret; uint32_t ret_num = 0; uint32_t adc_reading = 0; adc_digi_convert_mode_t convert_mode = ADC_CONV_SINGLE_UNIT_2; adc_digi_output_format_t output_format = ADC_DIGI_OUTPUT_FORMAT_TYPE1; if (bytes_per_sample != 2) { mp_raise_ValueError_varg(translate("%q must be array of type 'H'"), MP_QSTR_buffer); } start_dma(self, &convert_mode, &output_format); #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_continuous_read(self->handle, result, NUM_SAMPLES_PER_INTERRUPT * SOC_ADC_DIGI_DATA_BYTES_PER_CONV, &ret_num, ADC_READ_TIMEOUT_MS); if (ret == ESP_OK) { for (uint32_t i = 0; i < ret_num; i += ADC_RESULT_BYTE) { adc_digi_output_data_t *pResult = (adc_digi_output_data_t *)(void *)&result[i]; if (check_valid_data(pResult, self->pin, convert_mode, output_format)) { if (captured_bytes < len) { uint16_t *pBuffer = (uint16_t *)(void *)&buffer[captured_bytes]; if (output_format == ADC_DIGI_OUTPUT_FORMAT_TYPE1) { #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) adc_reading = pResult->type1.data; #endif } else { adc_reading = pResult->type2.data; } adc_reading = adc_reading * ((1 << 16) - 1) / ADC_PIN_MAX_VALUE; *pBuffer = (uint16_t)adc_reading; captured_bytes += sizeof(uint16_t); captured_samples++; } else { stop_dma(self); return captured_samples; } } else { #if !defined(CONFIG_IDF_TARGET_ESP32C3) // For all chips except for ESP32C3 we would receive samples only from one unit // For ESP32C3 we may receive sample from alternating units and need to ignore them #if defined(DEBUG_ANALOGBUFIO) mp_printf(&mp_plat_print, "Invalid sample received: 0x%x\n", pResult->val); #endif // DEBUG_ANALOGBUFIO stop_dma(self); return captured_samples; #endif } } } else if (ret == ESP_ERR_TIMEOUT) { #if defined(DEBUG_ANALOGBUFIO) mp_printf(&mp_plat_print, "ADC Timeout\n"); #endif // DEBUG_ANALOGBUFIO stop_dma(self); 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 stop_dma(self); return captured_samples; } } stop_dma(self); #if defined(DEBUG_ANALOGBUFIO) mp_printf(&mp_plat_print, "Captured bytes: %d\n", captured_bytes); #endif // DEBUG_ANALOGBUFIO return captured_samples; }