From d4ce7177faa93fc0b74ece2c24cb27735d30812b Mon Sep 17 00:00:00 2001 From: purewack Date: Sun, 22 Dec 2024 13:30:45 +0000 Subject: [PATCH 1/5] esp32: Customise ULP module inclusion. Add the ability to disable the built in ULP module using mpconfigport.h Add the ability to compile custom ULP image into the build by defining the cmake variables: ulp_app_name ulp_sources ulp_exp_dep_srcs Signed-off-by: Damian Nowacki (purewack) --- ports/esp32/esp32_common.cmake | 8 ++++++++ ports/esp32/esp32_ulp.c | 4 ++-- ports/esp32/modesp32.c | 2 +- ports/esp32/mpconfigport.h | 3 +++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index b6bfcdacc3cb4..45b56f36b1932 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -202,6 +202,14 @@ idf_component_register( ${IDF_COMPONENTS} ) +# If a ULP binary image is to be embedded into the build, you can define the +# appropriate variables to have ESP-IDF build it for your board using your +# mpconfigboard.cmake +if(DEFINED ulp_app_name) +message("ULP embedded APP: ${ulp_app_name} ${ulp_sources} ${ulp_exp_dep_srcs}") +ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}") +endif() + # Set the MicroPython target as the current (main) IDF component target. set(MICROPY_TARGET ${COMPONENT_TARGET}) diff --git a/ports/esp32/esp32_ulp.c b/ports/esp32/esp32_ulp.c index 17d210952f465..392f351a7c1b0 100644 --- a/ports/esp32/esp32_ulp.c +++ b/ports/esp32/esp32_ulp.c @@ -26,7 +26,7 @@ #include "py/runtime.h" -#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#if MICROPY_PY_ULP && (CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3) #if CONFIG_IDF_TARGET_ESP32 #include "esp32/ulp.h" @@ -104,4 +104,4 @@ MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &esp32_ulp_locals_dict ); -#endif // CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#endif // MICROPY_PY_ULP && (CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3) diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 164b1918261f4..af972029f5163 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -232,7 +232,7 @@ static const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_NVS), MP_ROM_PTR(&esp32_nvs_type) }, { MP_ROM_QSTR(MP_QSTR_Partition), MP_ROM_PTR(&esp32_partition_type) }, { MP_ROM_QSTR(MP_QSTR_RMT), MP_ROM_PTR(&esp32_rmt_type) }, - #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + #if MICROPY_PY_ULP && (CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3) { MP_ROM_QSTR(MP_QSTR_ULP), MP_ROM_PTR(&esp32_ulp_type) }, #endif diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index b5b7d63a56333..43a7d27f8f3bb 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -87,6 +87,9 @@ #define MICROPY_GC_SPLIT_HEAP_AUTO (1) // extended modules +#ifndef MICROPY_PY_ULP +#define MICROPY_PY_ULP (1) +#endif #ifndef MICROPY_PY_ESPNOW #define MICROPY_PY_ESPNOW (1) #endif From 23d114c443f1220de1f196a99c1747be5a01dbf5 Mon Sep 17 00:00:00 2001 From: purewack Date: Thu, 2 Jan 2025 11:44:00 +0000 Subject: [PATCH 2/5] esp32: ADC driver update to the new esp_adc --- ports/esp32/adc.c | 89 ++++++++++++------------------ ports/esp32/adc.h | 49 ++++++++++++++--- ports/esp32/boards/sdkconfig.base | 2 +- ports/esp32/machine_adc.c | 91 ++++++++++++++++--------------- ports/esp32/machine_adc_block.c | 14 ++--- ports/esp32/modesp32.c | 1 - ports/esp32/mpconfigport.h | 3 + 7 files changed, 135 insertions(+), 114 deletions(-) diff --git a/ports/esp32/adc.c b/ports/esp32/adc.c index 83af6dce0b2a5..d188b042dbfd8 100644 --- a/ports/esp32/adc.c +++ b/ports/esp32/adc.c @@ -27,82 +27,65 @@ #include "py/mphal.h" #include "adc.h" -#include "driver/adc.h" +#include "esp_adc/adc_oneshot.h" #include "esp_adc/adc_cali_scheme.h" -#define DEFAULT_VREF 1100 -void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits) { - if (bits < SOC_ADC_RTC_MIN_BITWIDTH && bits > SOC_ADC_RTC_MAX_BITWIDTH) { - // Invalid value for the current chip, raise exception in the switch below. - bits = -1; - } - switch (bits) { - case 9: - self->width = ADC_BITWIDTH_9; - break; - case 10: - self->width = ADC_BITWIDTH_10; - break; - case 11: - self->width = ADC_BITWIDTH_11; - break; - case 12: - self->width = ADC_BITWIDTH_12; - break; - case 13: - self->width = ADC_BITWIDTH_13; - break; - default: - mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); - } - self->bits = bits; +static esp_err_t ensure_adc_calibration(machine_adc_block_obj_t *self, adc_atten_t atten); - if (self->unit_id == ADC_UNIT_1) { - adc1_config_width(self->width); - } + +esp_err_t apply_self_adc_channel_atten(const machine_adc_obj_t *self, uint8_t atten){ + adc_oneshot_chan_cfg_t config = { + .atten = atten, + .bitwidth = self->block->bitwidth, + }; + esp_err_t ret = adc_oneshot_config_channel(self->block->handle, self->channel_id, &config); + return ret; } mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id) { - int raw = 0; - if (self->unit_id == ADC_UNIT_1) { - raw = adc1_get_raw(channel_id); - } else { - #if (SOC_ADC_PERIPH_NUM >= 2) - check_esp_err(adc2_get_raw(channel_id, self->width, &raw)); - #endif - } - return raw; + int reading = 0; + adc_oneshot_read(self->handle, channel_id, &reading); + return reading; +} + +/* +During testing, it turned out that the function `adc_cali_raw_to_voltage` does not account for the lower resolution, +instead it expects the full resolution value as an argument, hence the scaling applied here +*/ +mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) { + int raw = madcblock_read_helper(self, channel_id); + int uv = 0; + + check_esp_err(ensure_adc_calibration(self, atten)); + check_esp_err(adc_cali_raw_to_voltage(self->calib[atten], (raw << (ADC_WIDTH_MAX-self->bitwidth)), &uv)); + return (mp_int_t)uv * 1000; } static esp_err_t ensure_adc_calibration(machine_adc_block_obj_t *self, adc_atten_t atten) { - if (self->handle[atten] != NULL) { + if (self->calib[atten] != NULL) { return ESP_OK; } + esp_err_t ret = ESP_OK; + #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED adc_cali_curve_fitting_config_t cali_config = { .unit_id = self->unit_id, .atten = atten, - .bitwidth = self->width, + .bitwidth = self->bitwidth, }; - return adc_cali_create_scheme_curve_fitting(&cali_config, &self->handle[atten]); + ret = adc_cali_create_scheme_curve_fitting(&cali_config, &self->calib[atten]); #else adc_cali_line_fitting_config_t cali_config = { .unit_id = self->unit_id, .atten = atten, - .bitwidth = self->width, + .bitwidth = self->bitwidth, }; - return adc_cali_create_scheme_line_fitting(&cali_config, &self->handle[atten]); + ret = adc_cali_create_scheme_line_fitting(&cali_config, &self->calib[atten]); #endif -} -mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) { - int raw = madcblock_read_helper(self, channel_id); - int uv; - - check_esp_err(ensure_adc_calibration(self, atten)); - check_esp_err(adc_cali_raw_to_voltage(self->handle[atten], raw, &uv)); - - return (mp_int_t)uv * 1000; + + return ret; } + diff --git a/ports/esp32/adc.h b/ports/esp32/adc.h index 5688e0a29a706..e006adfc5158d 100644 --- a/ports/esp32/adc.h +++ b/ports/esp32/adc.h @@ -29,33 +29,68 @@ #define MICROPY_INCLUDED_ESP32_ADC_H #include "py/runtime.h" -#include "esp_adc_cal.h" +#include "esp_adc/adc_oneshot.h" #include "esp_adc/adc_cali_scheme.h" -#define ADC_ATTEN_MAX SOC_ADC_ATTEN_NUM +#define ADC_ATTEN_COUNT SOC_ADC_ATTEN_NUM +#define ADC_ATTEN_MIN ADC_ATTEN_DB_0 +#define ADC_ATTEN_MAX ADC_ATTEN_DB_11 + +/* +https://github.com/espressif/esp-idf/issues/13128 +https://github.com/espressif/esp-idf/blob/release/v5.2/components/soc/esp32s3/include/soc/soc_caps.h +https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32s2/03-errata-description/index.html +https://docs.espressif.com/projects/esp-idf/en/v4.2/esp32/api-reference/peripherals/adc.html + +Looks like only the original esp32 is capable of bitwidth adjustment, all others are stuck at 12 bits, +except the S2, which is locked at 13 bits, otherwise attenuation doesn't work. +*/ +#if CONFIG_IDF_TARGET_ESP32S2 + +#define ADC_WIDTH_MIN ADC_BITWIDTH_13 +#define ADC_WIDTH_MAX ADC_BITWIDTH_13 + +#elif CONFIG_IDF_TARGET_ESP32 + +#define ADC_WIDTH_MIN ADC_BITWIDTH_9 +#define ADC_WIDTH_MAX ADC_BITWIDTH_12 + +#else + +#define ADC_WIDTH_MIN ADC_BITWIDTH_12 +#define ADC_WIDTH_MAX ADC_BITWIDTH_12 + +#endif typedef struct _machine_adc_block_obj_t { mp_obj_base_t base; adc_unit_t unit_id; - mp_int_t bits; - adc_bits_width_t width; - adc_cali_handle_t handle[ADC_ATTEN_MAX]; + adc_oneshot_unit_handle_t handle; + adc_cali_handle_t calib[ADC_ATTEN_COUNT]; + adc_bitwidth_t bitwidth; } machine_adc_block_obj_t; typedef struct _machine_adc_obj_t { mp_obj_base_t base; machine_adc_block_obj_t *block; adc_channel_t channel_id; - gpio_num_t gpio_id; + gpio_num_t gpio_id; } machine_adc_obj_t; extern machine_adc_block_obj_t madcblock_obj[]; -void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits); +esp_err_t apply_self_adc_channel_atten(const machine_adc_obj_t *self, uint8_t atten); + mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id); mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten); const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id); void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +mp_int_t mp_machine_adc_atten_get_helper(const machine_adc_obj_t *self); +void mp_machine_adc_atten_set_helper(const machine_adc_obj_t *self, mp_int_t atten); + +mp_int_t mp_machine_adc_width_get_helper(const machine_adc_obj_t *self); +void mp_machine_adc_block_width_set_helper(machine_adc_block_obj_t *self, mp_int_t width); + #endif // MICROPY_INCLUDED_ESP32_ADC_H diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index e20835c70c42d..d030e3824f0ea 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -115,7 +115,7 @@ CONFIG_ADC_CAL_LUT_ENABLE=y CONFIG_UART_ISR_IN_IRAM=y # IDF 5 deprecated -CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y +CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=n CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y CONFIG_ETH_USE_SPI_ETHERNET=y diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c index be1725c3709a6..7abe56b7804c2 100644 --- a/ports/esp32/machine_adc.c +++ b/ports/esp32/machine_adc.c @@ -30,7 +30,7 @@ #include "py/mphal.h" #include "adc.h" -#include "driver/adc.h" +#include "esp_adc/adc_oneshot.h" #define ADCBLOCK1 (&madcblock_obj[0]) #define ADCBLOCK2 (&madcblock_obj[1]) @@ -126,20 +126,8 @@ static const machine_adc_obj_t madc_obj[] = { #endif }; -// These values are initialised to 0, which means the corresponding ADC channel is not initialised. -// The madc_atten_get/madc_atten_set functions store (atten+1) here so that the uninitialised state -// can be distinguished from the initialised state. static uint8_t madc_obj_atten[MP_ARRAY_SIZE(madc_obj)]; -static inline adc_atten_t madc_atten_get(const machine_adc_obj_t *self) { - uint8_t value = madc_obj_atten[self - &madc_obj[0]]; - return value == 0 ? ADC_ATTEN_MAX : value - 1; -} - -static inline void madc_atten_set(const machine_adc_obj_t *self, adc_atten_t atten) { - madc_obj_atten[self - &madc_obj[0]] = atten + 1; -} - const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id) { for (int i = 0; i < MP_ARRAY_SIZE(madc_obj); i++) { const machine_adc_obj_t *adc = &madc_obj[i]; @@ -152,22 +140,7 @@ const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_ static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { const machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, madc_atten_get(self)); -} - -static void madc_atten_helper(const machine_adc_obj_t *self, mp_int_t atten) { - esp_err_t err = ESP_FAIL; - if (self->block->unit_id == ADC_UNIT_1) { - err = adc1_config_channel_atten(self->channel_id, atten); - } else { - #if SOC_ADC_PERIPH_NUM >= 2 - err = adc2_config_channel_atten(self->channel_id, atten); - #endif - } - if (err != ESP_OK) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid atten")); - } - madc_atten_set(self, atten); + mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, mp_machine_adc_atten_get_helper(self)); } void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -182,18 +155,30 @@ void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_pos_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mp_int_t atten = args[ARG_atten].u_int; - if (atten != -1) { - madc_atten_helper(self, atten); - } else if (madc_atten_get(self) == ADC_ATTEN_MAX) { - madc_atten_helper(self, ADC_ATTEN_DB_0); + + if(!self->block->handle){ + adc_oneshot_unit_init_cfg_t init_config = { + .unit_id = self->block->unit_id + }; + check_esp_err(adc_oneshot_new_unit(&init_config, &self->block->handle)); } + + mp_int_t atten = args[ARG_atten].u_int; + mp_machine_adc_atten_set_helper(self, atten != -1 ? atten : ADC_ATTEN_MAX); + mp_machine_adc_block_width_set_helper(self->block, ADC_WIDTH_MAX); + apply_self_adc_channel_atten(self,mp_machine_adc_atten_get_helper(self)); + } static void mp_machine_adc_init_helper(machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { madc_init_helper(self, n_pos_args, pos_args, kw_args); } +static void mp_machine_adc_deinit(machine_adc_obj_t *self){ + if(self->block->handle) + check_esp_err(adc_oneshot_del_unit(self->block->handle)); +} + static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_pos_args, size_t n_kw_args, const mp_obj_t *args) { mp_arg_check_num(n_pos_args, n_kw_args, 1, MP_OBJ_FUN_ARGS_MAX, true); gpio_num_t gpio_id = machine_pin_get_id(args[0]); @@ -202,10 +187,6 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_pos_ mp_raise_ValueError(MP_ERROR_TEXT("invalid pin")); } - if (self->block->width == -1) { - madcblock_bits_helper(self->block, self->block->bits); - } - mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw_args, args + n_pos_args); madc_init_helper(self, n_pos_args - 1, args + 1, &kw_args); @@ -225,20 +206,44 @@ static mp_int_t mp_machine_adc_read(machine_adc_obj_t *self) { static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) { mp_uint_t raw = madcblock_read_helper(self->block, self->channel_id); // Scale raw reading to 16 bit value using a Taylor expansion (for 8 <= bits <= 16) - mp_int_t bits = self->block->bits; + mp_int_t bits = mp_machine_adc_width_get_helper(self); mp_uint_t u16 = raw << (16 - bits) | raw >> (2 * bits - 16); return u16; } static mp_int_t mp_machine_adc_read_uv(machine_adc_obj_t *self) { - adc_atten_t atten = madc_atten_get(self); - return madcblock_read_uv_helper(self->block, self->channel_id, atten); + return madcblock_read_uv_helper(self->block, self->channel_id, mp_machine_adc_atten_get_helper(self)); } + +mp_int_t mp_machine_adc_atten_get_helper(const machine_adc_obj_t *self){ + uint8_t value = madc_obj_atten[self - &madc_obj[0]]; + return value == 0 ? ADC_ATTEN_MAX : value - 1; +} +void mp_machine_adc_atten_set_helper(const machine_adc_obj_t *self, mp_int_t atten) { + if(atten < ADC_ATTEN_MIN || atten > ADC_ATTEN_MAX) + mp_raise_ValueError(MP_ERROR_TEXT("invalid attenuation")); + + madc_obj_atten[self - &madc_obj[0]] = atten + 1; +} static void mp_machine_adc_atten_set(machine_adc_obj_t *self, mp_int_t atten) { - madc_atten_helper(self, atten); + mp_machine_adc_atten_set_helper(self,atten); + apply_self_adc_channel_atten(self,mp_machine_adc_atten_get_helper(self)); +} + + +mp_int_t mp_machine_adc_width_get_helper(const machine_adc_obj_t *self){ + return self->block->bitwidth; +} +void mp_machine_adc_block_width_set_helper(machine_adc_block_obj_t *self, mp_int_t width) { + if(width < ADC_WIDTH_MIN || width > ADC_WIDTH_MAX) + mp_raise_ValueError(MP_ERROR_TEXT("invalid bit-width")); + + self->bitwidth = width; + DEBUG_printf("wd%d\n",width); } static void mp_machine_adc_width_set(machine_adc_obj_t *self, mp_int_t width) { - madcblock_bits_helper(self->block, width); + mp_machine_adc_block_width_set_helper(self->block,width); + apply_self_adc_channel_atten(self,mp_machine_adc_atten_get_helper(self)); } diff --git a/ports/esp32/machine_adc_block.c b/ports/esp32/machine_adc_block.c index a373603b7e396..1a47e3bd7f1ad 100644 --- a/ports/esp32/machine_adc_block.c +++ b/ports/esp32/machine_adc_block.c @@ -29,25 +29,21 @@ #include "py/mphal.h" #include "adc.h" -#include "driver/adc.h" +#include "esp_adc/adc_oneshot.h" machine_adc_block_obj_t madcblock_obj[] = { - {{&machine_adc_block_type}, ADC_UNIT_1, SOC_ADC_RTC_MAX_BITWIDTH, -1, {0}}, + {{&machine_adc_block_type}, ADC_UNIT_1, NULL}, #if SOC_ADC_PERIPH_NUM > 1 - {{&machine_adc_block_type}, ADC_UNIT_2, SOC_ADC_RTC_MAX_BITWIDTH, -1, {0}}, + {{&machine_adc_block_type}, ADC_UNIT_2, NULL}, #endif }; static void mp_machine_adc_block_print(const mp_print_t *print, machine_adc_block_obj_t *self) { - mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bits); + mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bitwidth); } static void mp_machine_adc_block_bits_set(machine_adc_block_obj_t *self, mp_int_t bits) { - if (bits != -1) { - madcblock_bits_helper(self, bits); - } else if (self->width == -1) { - madcblock_bits_helper(self, self->bits); - } + mp_machine_adc_block_width_set_helper(self, bits); } static machine_adc_block_obj_t *mp_machine_adc_block_get(mp_int_t unit) { diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index af972029f5163..7788e0a666c03 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -30,7 +30,6 @@ #include #include #include "driver/gpio.h" -#include "driver/adc.h" #include "esp_heap_caps.h" #include "multi_heap.h" diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 43a7d27f8f3bb..4a8bd7e82ea51 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -12,6 +12,8 @@ #include "driver/i2s_std.h" #include "esp_wifi_types.h" +#define MICROPY_DEBUG_PRINTERS (1) + #ifndef MICROPY_CONFIG_ROM_LEVEL #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) #endif @@ -126,6 +128,7 @@ #define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/esp32/machine_adc.c" #define MICROPY_PY_MACHINE_ADC_ATTEN_WIDTH (1) #define MICROPY_PY_MACHINE_ADC_INIT (1) +#define MICROPY_PY_MACHINE_ADC_DEINIT (1) #define MICROPY_PY_MACHINE_ADC_READ (1) #define MICROPY_PY_MACHINE_ADC_READ_UV (1) #define MICROPY_PY_MACHINE_ADC_BLOCK (1) From 5e9f6ff38704083e1863791dce5e46c4fda25995 Mon Sep 17 00:00:00 2001 From: purewack Date: Thu, 2 Jan 2025 19:54:59 +0000 Subject: [PATCH 3/5] esp32/ulp: set ULP type to RISCV by default if supported Possible override if you prefer to still use FSM ULP by specifying: `set(PREFER_FSM_ULP ON)` and appending: `boards/sdkconfig.ulp_fsm` to your `mpconfigboard.cmake` of your target board No RTC pin configuration yet, RISCV can configure its own RTC pins. FSM - I have not found a way of doing so yet without explicitly setting each pin required using ``` rtc_gpio_init(pin); rtc_gpio_set_direction(pin, RTC_GPIO_MODE_OUTPUT_ONLY); rtc_gpio_pulldown_dis(pin); rtc_gpio_pullup_dis(pin); ``` --- ports/esp32/boards/sdkconfig.base | 4 +- ports/esp32/boards/sdkconfig.ulp_fsm | 2 + ports/esp32/esp32_common.cmake | 14 +++- ports/esp32/esp32_ulp.c | 86 +++++++++++++++++++++++-- ports/esp32/main_esp32s3/CMakeLists.txt | 6 ++ ports/esp32/modesp32.c | 2 +- ports/esp32/mpconfigport.h | 3 - 7 files changed, 102 insertions(+), 15 deletions(-) create mode 100644 ports/esp32/boards/sdkconfig.ulp_fsm diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index d030e3824f0ea..45ea0ed7ab7f8 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -90,7 +90,9 @@ CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC=y # Only on: ESP32, ESP32S2, ESP32S3 CONFIG_ULP_COPROC_ENABLED=y CONFIG_ULP_COPROC_TYPE_FSM=y -CONFIG_ULP_COPROC_RESERVE_MEM=2040 +# Allow SoCs that support RISCV to have it enabled by default, has no effect otherwise +CONFIG_ULP_COPROC_TYPE_RISCV=y +CONFIG_ULP_COPROC_RESERVE_MEM=4096 # For cmake build CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y diff --git a/ports/esp32/boards/sdkconfig.ulp_fsm b/ports/esp32/boards/sdkconfig.ulp_fsm new file mode 100644 index 0000000000000..d59d06c504d58 --- /dev/null +++ b/ports/esp32/boards/sdkconfig.ulp_fsm @@ -0,0 +1,2 @@ +CONFIG_ULP_COPROC_TYPE_FSM=y +CONFIG_ULP_COPROC_TYPE_RISCV=n diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 45b56f36b1932..b21b373e8d83a 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -205,9 +205,17 @@ idf_component_register( # If a ULP binary image is to be embedded into the build, you can define the # appropriate variables to have ESP-IDF build it for your board using your # mpconfigboard.cmake -if(DEFINED ulp_app_name) -message("ULP embedded APP: ${ulp_app_name} ${ulp_sources} ${ulp_exp_dep_srcs}") -ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}") +if(DEFINED ulp_embedded_sources) + list(APPEND MICROPY_DEF_CORE ULP_EMBEDDED_APP=1) + set(ulp_app_name + "ulp_embedded" + ) + set(ulp_depentants + ${ulp_depentants} + "esp32_ulp.c" + ) + message("embedded ULP App sources: " ${ulp_sources} ", deps: " ${ulp_depentants}) + ulp_embed_binary(${ulp_app_name} ${ulp_embedded_sources} ${ulp_depentants}) endif() # Set the MicroPython target as the current (main) IDF component target. diff --git a/ports/esp32/esp32_ulp.c b/ports/esp32/esp32_ulp.c index 392f351a7c1b0..6b91014a399b4 100644 --- a/ports/esp32/esp32_ulp.c +++ b/ports/esp32/esp32_ulp.c @@ -26,14 +26,28 @@ #include "py/runtime.h" -#if MICROPY_PY_ULP && (CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3) +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + +#if CONFIG_IDF_TARGET_ULP_PREFER_FSM || CONFIG_IDF_TARGET_ESP32 + #define TYPE_RISCV 0 +#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + #define TYPE_RISCV 1 +#endif #if CONFIG_IDF_TARGET_ESP32 -#include "esp32/ulp.h" + #include "esp32/ulp.h" +#elif TYPE_RISCV + #include "ulp_riscv.h" #elif CONFIG_IDF_TARGET_ESP32S2 -#include "esp32s2/ulp.h" + #include "esp32s2/ulp.h" #elif CONFIG_IDF_TARGET_ESP32S3 -#include "esp32s3/ulp.h" + #include "esp32s3/ulp.h" +#endif + +#ifdef ULP_EMBEDDED_APP + #include "ulp_embedded.h" + extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_embedded_bin_start"); + extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_embedded_bin_end"); #endif typedef struct _esp32_ulp_obj_t { @@ -49,6 +63,10 @@ static mp_obj_t esp32_ulp_make_new(const mp_obj_type_t *type, size_t n_args, siz // check arguments mp_arg_check_num(n_args, n_kw, 0, 0, false); + ulp_set_wakeup_period(0, 1000*10);//default wakeup 10ms + #ifdef ULP_EMBEDDED_APP + DEBUG_printf("embedded app size %d\n", (ulp_main_bin_end - ulp_main_bin_start)); + #endif // return constant object return (mp_obj_t)&esp32_ulp_obj; } @@ -69,8 +87,12 @@ static mp_obj_t esp32_ulp_load_binary(mp_obj_t self_in, mp_obj_t load_addr_in, m mp_buffer_info_t bufinfo; mp_get_buffer_raise(program_binary_in, &bufinfo, MP_BUFFER_READ); - + + #if TYPE_RISCV + int _errno = ulp_riscv_load_binary(bufinfo.buf, bufinfo.len / sizeof(uint32_t)); + #else int _errno = ulp_load_binary(load_addr, bufinfo.buf, bufinfo.len / sizeof(uint32_t)); + #endif if (_errno != ESP_OK) { mp_raise_OSError(_errno); } @@ -79,8 +101,12 @@ static mp_obj_t esp32_ulp_load_binary(mp_obj_t self_in, mp_obj_t load_addr_in, m static MP_DEFINE_CONST_FUN_OBJ_3(esp32_ulp_load_binary_obj, esp32_ulp_load_binary); static mp_obj_t esp32_ulp_run(mp_obj_t self_in, mp_obj_t entry_point_in) { + #if TYPE_RISCV + int _errno = ulp_riscv_run(); + #else mp_uint_t entry_point = mp_obj_get_int(entry_point_in); int _errno = ulp_run(entry_point / sizeof(uint32_t)); + #endif if (_errno != ESP_OK) { mp_raise_OSError(_errno); } @@ -88,10 +114,56 @@ static mp_obj_t esp32_ulp_run(mp_obj_t self_in, mp_obj_t entry_point_in) { } static MP_DEFINE_CONST_FUN_OBJ_2(esp32_ulp_run_obj, esp32_ulp_run); +#ifdef ULP_EMBEDDED_APP +static mp_obj_t esp32_ulp_load_and_run_embedded(mp_obj_t self_in) { + int _errno; + #if TYPE_RISCV + _errno = ulp_riscv_load_binary(ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start)); + if (_errno != ESP_OK) mp_raise_OSError(_errno); + + _errno = ulp_riscv_run(); + if (_errno != ESP_OK) mp_raise_OSError(_errno); + #else + _errno = ulp_load_binary(0, ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t)); + if (_errno != ESP_OK) mp_raise_OSError(_errno); + + _errno = ulp_run(&ulp_entry - RTC_SLOW_MEM); + if (_errno != ESP_OK) mp_raise_OSError(_errno); + #endif + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(esp32_ulp_load_and_run_embedded_obj, esp32_ulp_load_and_run_embedded); +#endif + +static mp_obj_t esp32_ulp_pause(mp_obj_t self_in) { + #if TYPE_RISCV + ulp_riscv_timer_stop(); + #else + ulp_timer_stop(); + #endif + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(esp32_ulp_pause_obj, esp32_ulp_pause); + +static mp_obj_t esp32_ulp_resume(mp_obj_t self_in) { + #if TYPE_RISCV + ulp_riscv_timer_resume(); + #else + ulp_timer_resume(); + #endif + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(esp32_ulp_resume_obj, esp32_ulp_resume); + static const mp_rom_map_elem_t esp32_ulp_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_set_wakeup_period), MP_ROM_PTR(&esp32_ulp_set_wakeup_period_obj) }, { MP_ROM_QSTR(MP_QSTR_load_binary), MP_ROM_PTR(&esp32_ulp_load_binary_obj) }, { MP_ROM_QSTR(MP_QSTR_run), MP_ROM_PTR(&esp32_ulp_run_obj) }, + #ifdef ULP_EMBEDDED_APP + { MP_ROM_QSTR(MP_QSTR_run_embedded), MP_ROM_PTR(&esp32_ulp_load_and_run_embedded_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&esp32_ulp_pause_obj) }, + { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&esp32_ulp_resume_obj) }, { MP_ROM_QSTR(MP_QSTR_RESERVE_MEM), MP_ROM_INT(CONFIG_ULP_COPROC_RESERVE_MEM) }, }; static MP_DEFINE_CONST_DICT(esp32_ulp_locals_dict, esp32_ulp_locals_dict_table); @@ -102,6 +174,6 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_TYPE_FLAG_NONE, make_new, esp32_ulp_make_new, locals_dict, &esp32_ulp_locals_dict - ); +); -#endif // MICROPY_PY_ULP && (CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3) +#endif //CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 diff --git a/ports/esp32/main_esp32s3/CMakeLists.txt b/ports/esp32/main_esp32s3/CMakeLists.txt index bc5ab939c3ce3..bf8cf5c126b61 100644 --- a/ports/esp32/main_esp32s3/CMakeLists.txt +++ b/ports/esp32/main_esp32s3/CMakeLists.txt @@ -10,4 +10,10 @@ endif() set(MICROPY_PY_TINYUSB ON) +if(PREFER_FSM_ULP) +list(APPEND MICROPY_DEF_CORE + CONFIG_IDF_TARGET_ULP_PREFER_FSM=1 +) +endif() + include(${MICROPY_PORT_DIR}/esp32_common.cmake) diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 7788e0a666c03..dc2197ca8c6f2 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -231,7 +231,7 @@ static const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_NVS), MP_ROM_PTR(&esp32_nvs_type) }, { MP_ROM_QSTR(MP_QSTR_Partition), MP_ROM_PTR(&esp32_partition_type) }, { MP_ROM_QSTR(MP_QSTR_RMT), MP_ROM_PTR(&esp32_rmt_type) }, - #if MICROPY_PY_ULP && (CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3) + #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 { MP_ROM_QSTR(MP_QSTR_ULP), MP_ROM_PTR(&esp32_ulp_type) }, #endif diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 4a8bd7e82ea51..f091bb320ca23 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -89,9 +89,6 @@ #define MICROPY_GC_SPLIT_HEAP_AUTO (1) // extended modules -#ifndef MICROPY_PY_ULP -#define MICROPY_PY_ULP (1) -#endif #ifndef MICROPY_PY_ESPNOW #define MICROPY_PY_ESPNOW (1) #endif From f2e08b8c1b6089eebf45cf0ed207fbb2dd636c6d Mon Sep 17 00:00:00 2001 From: purewack Date: Fri, 3 Jan 2025 16:21:41 +0000 Subject: [PATCH 4/5] esp32: ulp read and write RTC memory. Unified interface for the ULP. `ULP.run(buffer)` will load & run the binary. (`ULP.run(entry, buffer)` on esp32) `ULP.pause()` and `ULP.resume()` functions added to start and stop the *wakeup* timer. If your ULP code is in an infinite loop, it will not be stopped on FSM as there is no ulp_halt() function. `ULP.read(address)` and `ULP.write(address,value)` will allow interacting with variables if an embedded application is used, variable addresses will abtomatically be saved and added to the ULP class, meaning you can do `ulp.read(ulp.VAR_TEST)` and `ulp.write(ulp.VAR_TEST, 12345)` addresses are relative from RTC_SLOW_MEM, but you can use the full address too. --- ports/esp32/esp32_common.cmake | 17 +++- ports/esp32/esp32_ulp.c | 113 +++++++++++++++++------- ports/esp32/esp32_ulp_qstr.py | 80 +++++++++++++++++ ports/esp32/main_esp32s2/CMakeLists.txt | 6 ++ 4 files changed, 180 insertions(+), 36 deletions(-) create mode 100644 ports/esp32/esp32_ulp_qstr.py diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index b21b373e8d83a..314ca25c38a37 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -202,6 +202,9 @@ idf_component_register( ${IDF_COMPONENTS} ) +# Set the MicroPython target as the current (main) IDF component target. +set(MICROPY_TARGET ${COMPONENT_TARGET}) + # If a ULP binary image is to be embedded into the build, you can define the # appropriate variables to have ESP-IDF build it for your board using your # mpconfigboard.cmake @@ -216,10 +219,18 @@ if(DEFINED ulp_embedded_sources) ) message("embedded ULP App sources: " ${ulp_sources} ", deps: " ${ulp_depentants}) ulp_embed_binary(${ulp_app_name} ${ulp_embedded_sources} ${ulp_depentants}) -endif() -# Set the MicroPython target as the current (main) IDF component target. -set(MICROPY_TARGET ${COMPONENT_TARGET}) + set(ULP_LD_DIR ${CMAKE_BINARY_DIR}/esp-idf/main_${IDF_TARGET}/ulp_embedded) + add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/esp32_ulpconst_qstr.h + COMMAND python ${MICROPY_PORT_DIR}/esp32_ulp_qstr.py ${ULP_LD_DIR}/ulp_embedded.ld + DEPENDS ${ULP_LD_DIR}/ulp_embedded.ld + COMMENT "Parsing ULP headers" + VERBATIM + ) + + add_library(ULP_CONST INTERFACE ${CMAKE_BINARY_DIR}/esp32_ulpconst_qstr.h) +endif() # Define mpy-cross flags, for use with frozen code. if(CONFIG_IDF_TARGET_ARCH STREQUAL "xtensa") diff --git a/ports/esp32/esp32_ulp.c b/ports/esp32/esp32_ulp.c index 6b91014a399b4..5b43474c1d93e 100644 --- a/ports/esp32/esp32_ulp.c +++ b/ports/esp32/esp32_ulp.c @@ -64,55 +64,73 @@ static mp_obj_t esp32_ulp_make_new(const mp_obj_type_t *type, size_t n_args, siz mp_arg_check_num(n_args, n_kw, 0, 0, false); ulp_set_wakeup_period(0, 1000*10);//default wakeup 10ms - #ifdef ULP_EMBEDDED_APP - DEBUG_printf("embedded app size %d\n", (ulp_main_bin_end - ulp_main_bin_start)); - #endif // return constant object return (mp_obj_t)&esp32_ulp_obj; } -static mp_obj_t esp32_ulp_set_wakeup_period(mp_obj_t self_in, mp_obj_t period_index_in, mp_obj_t period_us_in) { - mp_uint_t period_index = mp_obj_get_int(period_index_in); + +static mp_obj_t esp32_ulp_set_wakeup_period( + mp_obj_t self_in, +#if CONFIG_IDF_TARGET_ESP32 + mp_obj_t period_index_in, +#endif + mp_obj_t period_us_in +) { + mp_uint_t period_index = 0; +#if CONFIG_IDF_TARGET_ESP32 + period_index = mp_obj_get_int(period_index_in); +#endif + mp_uint_t period_us = mp_obj_get_int(period_us_in); int _errno = ulp_set_wakeup_period(period_index, period_us); - if (_errno != ESP_OK) { - mp_raise_OSError(_errno); - } + if (_errno != ESP_OK) mp_raise_OSError(_errno); return mp_const_none; } +#if CONFIG_IDF_TARGET_ESP32 static MP_DEFINE_CONST_FUN_OBJ_3(esp32_ulp_set_wakeup_period_obj, esp32_ulp_set_wakeup_period); +#else +static MP_DEFINE_CONST_FUN_OBJ_2(esp32_ulp_set_wakeup_period_obj, esp32_ulp_set_wakeup_period); +#endif -static mp_obj_t esp32_ulp_load_binary(mp_obj_t self_in, mp_obj_t load_addr_in, mp_obj_t program_binary_in) { - mp_uint_t load_addr = mp_obj_get_int(load_addr_in); +static mp_obj_t esp32_ulp_load_and_run( + mp_obj_t self_in, +#if !TYPE_RISCV + mp_obj_t entry_point_in, +#endif + mp_obj_t program_binary_in +){ mp_buffer_info_t bufinfo; mp_get_buffer_raise(program_binary_in, &bufinfo, MP_BUFFER_READ); - + int _errno; #if TYPE_RISCV - int _errno = ulp_riscv_load_binary(bufinfo.buf, bufinfo.len / sizeof(uint32_t)); - #else - int _errno = ulp_load_binary(load_addr, bufinfo.buf, bufinfo.len / sizeof(uint32_t)); + ulp_riscv_timer_stop(); + ulp_riscv_halt(); + ulp_riscv_reset(); + _errno = ulp_riscv_load_binary(bufinfo.buf, bufinfo.len); + if (_errno != ESP_OK) mp_raise_OSError(_errno); + + _errno = ulp_riscv_run(); + if (_errno != ESP_OK) mp_raise_OSError(_errno); + ulp_riscv_timer_resume(); + #else + ulp_timer_stop(); + _errno = ulp_load_binary(0, bufinfo.buf, bufinfo.len / sizeof(uint32_t)); + if (_errno != ESP_OK) mp_raise_OSError(_errno); + + mp_uint_t entry_point = mp_obj_get_int(entry_point_in); + _errno = ulp_run(entry_point / sizeof(uint32_t)); + if (_errno != ESP_OK) mp_raise_OSError(_errno); + ulp_timer_resume(); #endif - if (_errno != ESP_OK) { - mp_raise_OSError(_errno); - } - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_3(esp32_ulp_load_binary_obj, esp32_ulp_load_binary); -static mp_obj_t esp32_ulp_run(mp_obj_t self_in, mp_obj_t entry_point_in) { - #if TYPE_RISCV - int _errno = ulp_riscv_run(); - #else - mp_uint_t entry_point = mp_obj_get_int(entry_point_in); - int _errno = ulp_run(entry_point / sizeof(uint32_t)); - #endif - if (_errno != ESP_OK) { - mp_raise_OSError(_errno); - } return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_2(esp32_ulp_run_obj, esp32_ulp_run); +#if TYPE_RISCV +static MP_DEFINE_CONST_FUN_OBJ_2(esp32_ulp_load_and_run_obj, esp32_ulp_load_and_run); +#else +static MP_DEFINE_CONST_FUN_OBJ_3(esp32_ulp_load_and_run_obj, esp32_ulp_load_and_run); +#endif #ifdef ULP_EMBEDDED_APP static mp_obj_t esp32_ulp_load_and_run_embedded(mp_obj_t self_in) { @@ -138,6 +156,7 @@ static MP_DEFINE_CONST_FUN_OBJ_1(esp32_ulp_load_and_run_embedded_obj, esp32_ulp_ static mp_obj_t esp32_ulp_pause(mp_obj_t self_in) { #if TYPE_RISCV ulp_riscv_timer_stop(); + ulp_riscv_halt(); #else ulp_timer_stop(); #endif @@ -155,16 +174,44 @@ static mp_obj_t esp32_ulp_resume(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(esp32_ulp_resume_obj, esp32_ulp_resume); + +static mp_obj_t esp32_ulp_read(mp_obj_t self_in, mp_obj_t address) { + uint32_t addr = mp_obj_get_int(address); + if(addr < (uintptr_t)RTC_SLOW_MEM ) addr += (uintptr_t)RTC_SLOW_MEM; + if(addr > ((uintptr_t)(RTC_SLOW_MEM)+CONFIG_ULP_COPROC_RESERVE_MEM)) + mp_raise_ValueError(MP_ERROR_TEXT("invalid address")); + + uint32_t val = *(uint32_t *)addr; + return mp_obj_new_int(val); +} +static MP_DEFINE_CONST_FUN_OBJ_2(esp32_ulp_read_obj, esp32_ulp_read); + +static mp_obj_t esp32_ulp_write(mp_obj_t self_in, mp_obj_t address, mp_obj_t value) { + uintptr_t addr = mp_obj_get_int(address); + if(addr < (uintptr_t)RTC_SLOW_MEM ) addr += (uintptr_t)RTC_SLOW_MEM; + if(addr > ((uintptr_t)(RTC_SLOW_MEM)+CONFIG_ULP_COPROC_RESERVE_MEM)) + mp_raise_ValueError(MP_ERROR_TEXT("invalid address")); + + *(uint32_t *)addr = mp_obj_get_int(value); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(esp32_ulp_write_obj, esp32_ulp_write); + + static const mp_rom_map_elem_t esp32_ulp_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_set_wakeup_period), MP_ROM_PTR(&esp32_ulp_set_wakeup_period_obj) }, - { MP_ROM_QSTR(MP_QSTR_load_binary), MP_ROM_PTR(&esp32_ulp_load_binary_obj) }, - { MP_ROM_QSTR(MP_QSTR_run), MP_ROM_PTR(&esp32_ulp_run_obj) }, + { MP_ROM_QSTR(MP_QSTR_run), MP_ROM_PTR(&esp32_ulp_load_and_run_obj) }, #ifdef ULP_EMBEDDED_APP { MP_ROM_QSTR(MP_QSTR_run_embedded), MP_ROM_PTR(&esp32_ulp_load_and_run_embedded_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&esp32_ulp_pause_obj) }, { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&esp32_ulp_resume_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&esp32_ulp_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&esp32_ulp_write_obj) }, { MP_ROM_QSTR(MP_QSTR_RESERVE_MEM), MP_ROM_INT(CONFIG_ULP_COPROC_RESERVE_MEM) }, + #ifdef ULP_EMBEDDED_APP + #include "genhdr/esp32_ulpconst_qstr.h" + #endif }; static MP_DEFINE_CONST_DICT(esp32_ulp_locals_dict, esp32_ulp_locals_dict_table); diff --git a/ports/esp32/esp32_ulp_qstr.py b/ports/esp32/esp32_ulp_qstr.py new file mode 100644 index 0000000000000..0c2466d8f716a --- /dev/null +++ b/ports/esp32/esp32_ulp_qstr.py @@ -0,0 +1,80 @@ +""" +This script reads in the given CMSIS device include file (eg stm32f405xx.h), +extracts relevant peripheral constants, and creates qstrs, mpz's and constants +for the stm module. +""" + +# Use a pattern rule here so that make will only call make-stmconst.py once to +# make both modstm_const.h and modstm_qstr.h +# $(HEADER_BUILD)/%_const.h $(BUILD)/%_qstr.h: $(CMSIS_MCU_HDR) make-stmconst.py | $(HEADER_BUILD) +# $(ECHO) "GEN stmconst $@" +# $(Q)$(PYTHON) make-stmconst.py --qstr $(GEN_STMCONST_QSTR) --mpz $(GEN_STMCONST_MPZ) $(CMSIS_MCU_HDR) > $(GEN_STMCONST_HDR) + +from __future__ import print_function + +import argparse +import re + +# Python 2/3 compatibility +import platform + +def parse_file(filename): + + regex_pattern = '(\w+)\s\=\s*(?:"([^"]*)"|(\S+))' + + with open(filename, 'r') as file: + content = file.read() + + shared_variables = {} + + matches = re.findall(regex_pattern, content) + + for m in matches: + + variable = m[0] + address = m[2] + # Check if the key starts with "ulp_" + if variable.startswith("ulp_var_"): + # Remove "ulp_" from the beginning of the key + variable = variable[4:].upper() + + shared_variables[variable.strip()] = address.strip() + + return shared_variables + +def main(): + cmd_parser = argparse.ArgumentParser(description="Extract ULP constants from a C header file.") + cmd_parser.add_argument("file", nargs=1, help="input file") + cmd_parser.add_argument( + "-q", + "--qstr", + dest="qstr_filename", + default="build/esp32_ulpconst_qstr.h", + help="Specified the name of the generated qstr header file", + ) + # cmd_parser.add_argument( + # "--mpz", + # dest="mpz_filename", + # default="build/esp32_ulpconst_mpz.h", + # help="the destination file of the generated mpz header", + # ) + args = cmd_parser.parse_args() + + shared_variables = parse_file(args.file[0]) + print(shared_variables) + + with open("../../genhdr/esp32_ulpconst_qstr.h", "wt") as h_file: + for key, value in shared_variables.items(): + line = ( + "{MP_ROM_QSTR(MP_QSTR_%s), MP_ROM_INT(0x%08x)}," + % (key, int(value, 16)-0x5000_0000) + ) + print(line) + h_file.write(line + '\n') + + #{MP_ROM_QSTR(MP_QSTR_VOLTAGE), MP_ROM_PTR( & mpz_50000130)}, + + return shared_variables + +if __name__ == "__main__": + main() diff --git a/ports/esp32/main_esp32s2/CMakeLists.txt b/ports/esp32/main_esp32s2/CMakeLists.txt index bc5ab939c3ce3..bf8cf5c126b61 100644 --- a/ports/esp32/main_esp32s2/CMakeLists.txt +++ b/ports/esp32/main_esp32s2/CMakeLists.txt @@ -10,4 +10,10 @@ endif() set(MICROPY_PY_TINYUSB ON) +if(PREFER_FSM_ULP) +list(APPEND MICROPY_DEF_CORE + CONFIG_IDF_TARGET_ULP_PREFER_FSM=1 +) +endif() + include(${MICROPY_PORT_DIR}/esp32_common.cmake) From f536032da778d11a527a2f338b7f44e1b449de97 Mon Sep 17 00:00:00 2001 From: purewack Date: Fri, 3 Jan 2025 19:06:35 +0000 Subject: [PATCH 5/5] esp32: ULP RTC io options Allow initialization of ADC and RTC pins using `ULP.rtc_init(pin)` `ULP.rtc_deinit(pin)` `ULP.adc_init(unit,channel)` During testing, it turns out that this needs to be done, so that the FSM can utilize IO. RISCV is already able to do this from its own code, however, you can still use this if you do not configure RISCV pins explicitly in the ulp code. --- ports/esp32/esp32_ulp.c | 57 ++++++++++++++++++++++++++++++++++++++++ ports/esp32/modmachine.c | 10 +++++++ 2 files changed, 67 insertions(+) diff --git a/ports/esp32/esp32_ulp.c b/ports/esp32/esp32_ulp.c index 5b43474c1d93e..e16f699c40313 100644 --- a/ports/esp32/esp32_ulp.c +++ b/ports/esp32/esp32_ulp.c @@ -50,6 +50,11 @@ extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_embedded_bin_end"); #endif +#include "hal/adc_types.h" +#include "driver/rtc_io.h" +#include "ulp_adc.h" +#include "py/mphal.h" + typedef struct _esp32_ulp_obj_t { mp_obj_base_t base; } esp32_ulp_obj_t; @@ -198,6 +203,55 @@ static mp_obj_t esp32_ulp_write(mp_obj_t self_in, mp_obj_t address, mp_obj_t val static MP_DEFINE_CONST_FUN_OBJ_3(esp32_ulp_write_obj, esp32_ulp_write); +/* +RTC pin functionality, can be moved to Pin class if needed +*/ + +static mp_obj_t esp32_ulp_rtc_init(mp_obj_t self_in, mp_obj_t pin_in) { + gpio_num_t gpio_id = mp_obj_get_int(pin_in); + + if(!rtc_gpio_is_valid_gpio(gpio_id)) mp_raise_ValueError(MP_ERROR_TEXT("invalid pin")); + rtc_gpio_init(gpio_id); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(esp32_ulp_rtc_init_obj, esp32_ulp_rtc_init); + + +static mp_obj_t esp32_ulp_rtc_deinit(mp_obj_t self_in, mp_obj_t pin_in) { + gpio_num_t gpio_id = mp_obj_get_int(pin_in); + + if(!rtc_gpio_is_valid_gpio(gpio_id)) mp_raise_ValueError(MP_ERROR_TEXT("invalid pin")); + rtc_gpio_deinit(gpio_id); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(esp32_ulp_rtc_deinit_obj, esp32_ulp_rtc_deinit); + + +static mp_obj_t esp32_ulp_adc_init(mp_obj_t self_in, mp_obj_t unit_in, mp_obj_t channel_in) { + int unit = mp_obj_get_int(unit_in); + int channel = mp_obj_get_int(channel_in); + + if(unit < 0 || unit > SOC_ADC_PERIPH_NUM) mp_raise_ValueError(MP_ERROR_TEXT("invalid ADC unit")); + + ulp_adc_cfg_t cfg = { + .adc_n = unit, + .channel = channel, + #if CONFIG_IDF_TARGET_ESP32S2 + .width = ADC_BITWIDTH_13, + #else + .width = ADC_BITWIDTH_12, + #endif + .atten = ADC_ATTEN_DB_11, + .ulp_mode = ADC_ULP_MODE_RISCV, + }; + int _errno = ulp_adc_init(&cfg); + if(_errno != 0) mp_raise_ValueError(MP_ERROR_TEXT("ADC unit already in use or invalid channel")); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(esp32_ulp_adc_init_obj, esp32_ulp_adc_init); + + static const mp_rom_map_elem_t esp32_ulp_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_set_wakeup_period), MP_ROM_PTR(&esp32_ulp_set_wakeup_period_obj) }, { MP_ROM_QSTR(MP_QSTR_run), MP_ROM_PTR(&esp32_ulp_load_and_run_obj) }, @@ -208,6 +262,9 @@ static const mp_rom_map_elem_t esp32_ulp_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&esp32_ulp_resume_obj) }, { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&esp32_ulp_read_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&esp32_ulp_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_rtc_init), MP_ROM_PTR(&esp32_ulp_rtc_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_rtc_deinit), MP_ROM_PTR(&esp32_ulp_rtc_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_adc_input), MP_ROM_PTR(&esp32_ulp_adc_init_obj) }, { MP_ROM_QSTR(MP_QSTR_RESERVE_MEM), MP_ROM_INT(CONFIG_ULP_COPROC_RESERVE_MEM) }, #ifdef ULP_EMBEDDED_APP #include "genhdr/esp32_ulpconst_qstr.h" diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 0c1b94d02d940..6c2a453676e35 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -38,6 +38,7 @@ #include "modmachine.h" #include "machine_rtc.h" +#include "driver/rtc_io.h" #if MICROPY_HW_ENABLE_SDCARD #define MICROPY_PY_MACHINE_SDCARD_ENTRY { MP_ROM_QSTR(MP_QSTR_SDCard), MP_ROM_PTR(&machine_sdcard_type) }, @@ -158,6 +159,15 @@ static void machine_sleep_helper(wake_type_t wake_type, size_t n_args, const mp_ #if SOC_ULP_SUPPORTED if (machine_rtc_config.wake_on_ulp) { + #if CONFIG_IDF_TARGET_ESP32 + /* Disconnect GPIO12 and GPIO15 to remove current drain through + * pullup/pulldown resistors on modules which have these (e.g. ESP32-WROVER) + * GPIO12 may be pulled high to select flash voltage. + */ + rtc_gpio_isolate(GPIO_NUM_12); + rtc_gpio_isolate(GPIO_NUM_15); + #endif // CONFIG_IDF_TARGET_ESP32 + if (esp_sleep_enable_ulp_wakeup() != ESP_OK) { mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("esp_sleep_enable_ulp_wakeup() failed")); }