diff options
Diffstat (limited to 'ports/esp32')
75 files changed, 1508 insertions, 1047 deletions
diff --git a/ports/esp32/README.md b/ports/esp32/README.md index d9115072a4..d8b55e45f3 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -31,7 +31,7 @@ manage the ESP32 microcontroller, as well as a way to manage the required build environment and toolchains needed to build the firmware. The ESP-IDF changes quickly and MicroPython only supports certain versions. -Currently MicroPython supports v5.2, v5.2.2, v5.3 and v5.4. +Currently MicroPython supports v5.2, v5.2.2, v5.3, v5.4 and v5.4.1. To install the ESP-IDF the full instructions can be found at the [Espressif Getting Started guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html#installation-step-by-step). @@ -49,10 +49,10 @@ The steps to take are summarised below. To check out a copy of the IDF use git clone: ```bash -$ git clone -b v5.2.2 --recursive https://github.com/espressif/esp-idf.git +$ git clone -b v5.4.1 --recursive https://github.com/espressif/esp-idf.git ``` -You can replace `v5.2.2` with any other supported version. +You can replace `v5.4.1` with any other supported version. (You don't need a full recursive clone; see the `ci_esp32_setup` function in `tools/ci.sh` in this repository for more detailed set-up commands.) @@ -61,7 +61,7 @@ MicroPython and update the submodules using: ```bash $ cd esp-idf -$ git checkout v5.2.2 +$ git checkout v5.4.1 $ git submodule update --init --recursive ``` @@ -221,6 +221,30 @@ import machine antenna = machine.Pin(16, machine.Pin.OUT, value=0) ``` +Partition table and filesystem size +----------------------------------- + +ESP32 firmware contains a bootloader, partition table and main application +firmware, which are all stored in (external) SPI flash. The user filesystem +is also stored in the same SPI flash. By default, MicroPython does not have +a fixed entry in the ESP32 partition table for the filesystem. Instead it +will automatically determine the size of the SPI flash upon boot, and then -- +so long as there is no existing partition called "vfs" or "ffat" -- it will +create a partition for the filesystem called "vfs" which takes all of the +remaining flash between the end of the last defined partition up until the +end of flash. + +This means that firmware built for, say, a 4MiB flash will work on any +device that has at least 4MiB flash. The user "vfs" filesystem will then +take up as much space as possible. + +This auto-detection behaviour can be overridden: if the ESP32 partition +table does contain an entry called "vfs" or "ffat" then these are used for +the user filesystem and no automatic "vfs" partition is added. This is +useful in cases where only the MicroPython ESP32 application is flashed to +the device (and not the bootloader or partition table), eg when deploying +.uf2 files. + Defining a custom ESP32 board ----------------------------- diff --git a/ports/esp32/adc.c b/ports/esp32/adc.c index 83af6dce0b..d0e5e81330 100644 --- a/ports/esp32/adc.c +++ b/ports/esp32/adc.c @@ -26,83 +26,73 @@ */ #include "py/mphal.h" +#include "py/mperrno.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 +static esp_err_t ensure_adc_calibration(machine_adc_block_obj_t *self, adc_atten_t atten); -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; +void adc_is_init_guard(machine_adc_block_obj_t *self) { + if (!self->handle) { + mp_raise_OSError(MP_EPERM); } - 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; +} - 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_is_init_guard(self->block); + + 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; + adc_is_init_guard(self); + + 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 5688e0a29a..ebf7fcc21a 100644 --- a/ports/esp32/adc.h +++ b/ports/esp32/adc.h @@ -29,17 +29,45 @@ #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_bitwidth_t bitwidth; + adc_cali_handle_t calib[ADC_ATTEN_COUNT]; } machine_adc_block_obj_t; typedef struct _machine_adc_obj_t { @@ -51,11 +79,18 @@ typedef struct _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/ARDUINO_NANO_ESP32/board.json b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json index 9d0016017f..936a498b5e 100644 --- a/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json @@ -17,8 +17,8 @@ "ABX00092_01.iso_1000x750.jpg" ], "mcu": "esp32s3", - "product": "Arduino Nano ESP32", + "product": "Nano ESP32", "thumbnail": "", - "url": "https://store.arduino.cc/products/arduino-nano-esp32", + "url": "https://docs.arduino.cc/hardware/nano-esp32/", "vendor": "Arduino" } diff --git a/ports/esp32/boards/ESP32_GENERIC/board.md b/ports/esp32/boards/ESP32_GENERIC/board.md index 9346d18d84..06972e436f 100644 --- a/ports/esp32/boards/ESP32_GENERIC/board.md +++ b/ports/esp32/boards/ESP32_GENERIC/board.md @@ -1,5 +1,5 @@ The following files are firmware that should work on most ESP32-based boards -with 4MiB of flash, including WROOM WROVER, SOLO, PICO, and MINI modules. +with 4MiB or more of flash, including WROOM WROVER, SOLO, PICO, and MINI modules. This board has multiple variants available: diff --git a/ports/esp32/boards/ESP32_GENERIC_C3/board.md b/ports/esp32/boards/ESP32_GENERIC_C3/board.md index 1604b879c5..6097eb85df 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C3/board.md +++ b/ports/esp32/boards/ESP32_GENERIC_C3/board.md @@ -1,6 +1,6 @@ The following files are firmware images that should work on most -ESP32-C3-based boards with 4MiB of flash, including WROOM and MINI modules, -that use the revision 3 silicon (or newer). +ESP32-C3-based boards with 4MiB or more of flash, including WROOM and MINI +modules, that use the revision 3 silicon (or newer). USB serial/JTAG support is enabled on pin 18 and 19. Note that this is not a full USB stack, the C3 just provides a CDC/ACM class serial diff --git a/ports/esp32/boards/ESP32_GENERIC_S2/board.md b/ports/esp32/boards/ESP32_GENERIC_S2/board.md index 3e46c02467..d065ecbbb9 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S2/board.md +++ b/ports/esp32/boards/ESP32_GENERIC_S2/board.md @@ -1,5 +1,5 @@ The following files are firmware that should work on most ESP32-S2-based -boards with 4MiB of flash, including WROOM, WROVER, and MINI modules. +boards with 4MiB or more of flash, including WROOM, WROVER, and MINI modules. This firmware supports configurations with and without SPIRAM (also known as PSRAM) and will auto-detect a connected SPIRAM chip at startup and allocate diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/board.json b/ports/esp32/boards/ESP32_GENERIC_S3/board.json index fd0c9edce0..7a546d35fc 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S3/board.json +++ b/ports/esp32/boards/ESP32_GENERIC_S3/board.json @@ -21,7 +21,6 @@ "url": "https://www.espressif.com/en/products/modules", "vendor": "Espressif", "variants": { - "SPIRAM_OCT": "Support for Octal-SPIRAM", - "FLASH_4M": "4MiB flash" + "SPIRAM_OCT": "Support for Octal-SPIRAM" } } diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/board.md b/ports/esp32/boards/ESP32_GENERIC_S3/board.md index 16930c9193..68ce0917e0 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S3/board.md +++ b/ports/esp32/boards/ESP32_GENERIC_S3/board.md @@ -1,9 +1,7 @@ The following files are firmware that should work on most ESP32-S3-based -boards with 4/8MiB of flash, including WROOM and MINI modules. +boards with 4MiB or more of flash, including WROOM and MINI modules. This firmware supports configurations with and without SPIRAM (also known as PSRAM) and will auto-detect a connected SPIRAM chip at startup and allocate the MicroPython heap accordingly. However if your board has Octal SPIRAM, then use the "spiram-oct" variant. - -If your board has 4MiB flash (including ESP32-S3FH4R2 based ones with embedded flash), then use the "flash-4m" build. diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake deleted file mode 100644 index e832cdb60f..0000000000 --- a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake +++ /dev/null @@ -1,4 +0,0 @@ -set(SDKCONFIG_DEFAULTS - ${SDKCONFIG_DEFAULTS} - boards/ESP32_GENERIC_S3/sdkconfig.flash_4m -) diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board index 9839b0d300..369330682f 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board +++ b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board @@ -1,8 +1,2 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y - -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m deleted file mode 100644 index c967c46ae6..0000000000 --- a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_8MB= - -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiB.csv" diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json new file mode 100644 index 0000000000..d2ada7cccd --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json @@ -0,0 +1,23 @@ +{ + "deploy": [ + "../deploy_nativeusb.md" + ], + "deploy_options": { + "flash_offset": "0" + }, + "docs": "", + "features": [ + "BLE", + "External Flash", + "RGB LED", + "WiFi" + ], + "images": [ + "GAR-PYBSTICK26-C3-mUSB-00.JPG" + ], + "mcu": "esp32c3", + "product": "PYBSTICK26_ESP32C3", + "thumbnail": "", + "url": "https://shop.mchobby.be/fr/pybstick/2505-pybstick26-esp32-c3-micropython-et-arduino-3232100025059.html", + "vendor": "McHobby" +} diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake new file mode 100644 index 0000000000..81cfff1d77 --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake @@ -0,0 +1,7 @@ +set(IDF_TARGET esp32c3) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble + boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board +) diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h new file mode 100644 index 0000000000..c9796d1f65 --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h @@ -0,0 +1,10 @@ +#define MICROPY_HW_BOARD_NAME "PYBSTICK26_ESP32C3" +#define MICROPY_HW_MCU_NAME "ESP32C3" +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "pybstick26_esp32c3" + +#define MICROPY_HW_I2C0_SCL (1) +#define MICROPY_HW_I2C0_SDA (0) + +#define MICROPY_HW_SPI1_MOSI (7) +#define MICROPY_HW_SPI1_MISO (2) +#define MICROPY_HW_SPI1_SCK (6) diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv new file mode 100644 index 0000000000..d786654903 --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv @@ -0,0 +1,13 @@ +GP0,GPIO0 +GP1,GPIO1 +GP2,GPIO2 +GP3,GPIO3 +GP4,GPIO4 +GP5,GPIO5 +GP6,GPIO6 +GP7,GPIO7 +GP8,GPIO8 +GP9,GPIO9 +GP10,GPIO10 +GP20,GPIO20 +GP21,GPIO21 diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board new file mode 100644 index 0000000000..70e5f1f0d1 --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board @@ -0,0 +1,4 @@ +CONFIG_ESP32C3_REV_MIN_3=y + +# Workaround for https://github.com/espressif/esp-idf/issues/14456 +CONFIG_ESP_SYSTEM_HW_STACK_GUARD=n diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json index d68a9baadd..7521c01a6b 100644 --- a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json @@ -19,8 +19,8 @@ "lilygo-ttgo-lora-32-v1-6.jpg" ], "mcu": "esp32", - "product": "LILYGO TTGO LoRa32", + "product": "TTGO LoRa32", "thumbnail": "", - "url": "http://www.lilygo.cn/prod_view.aspx?TypeId=50060&Id=1270&FId=t3:50060:3", + "url": "https://www.lilygo.cc/products/lora3", "vendor": "LILYGO" } diff --git a/ports/esp32/boards/M5STACK_ATOM/board.json b/ports/esp32/boards/M5STACK_ATOM/board.json index 3a1e7ce359..c3908a4779 100644 --- a/ports/esp32/boards/M5STACK_ATOM/board.json +++ b/ports/esp32/boards/M5STACK_ATOM/board.json @@ -18,8 +18,8 @@ "m5stack_atom.jpg" ], "mcu": "esp32", - "product": "M5 Stack Atom", + "product": "Atom", "thumbnail": "", - "url": "https://m5stack.com/", - "vendor": "M5 Stack" + "url": "https://shop.m5stack.com/products/atom-matrix-esp32-development-kit", + "vendor": "M5Stack" } diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json index fe0e97f9f9..26b1a4d54a 100644 --- a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json +++ b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json @@ -20,5 +20,5 @@ "product": "AtomS3 Lite", "thumbnail": "", "url": "https://shop.m5stack.com/products/atoms3-lite-esp32s3-dev-kit", - "vendor": "M5 Stack" + "vendor": "M5Stack" } diff --git a/ports/esp32/boards/M5STACK_NANOC6/board.json b/ports/esp32/boards/M5STACK_NANOC6/board.json index 087851ae5e..ddf3406c83 100644 --- a/ports/esp32/boards/M5STACK_NANOC6/board.json +++ b/ports/esp32/boards/M5STACK_NANOC6/board.json @@ -18,7 +18,7 @@ "m5stack_nanoc6.jpg" ], "mcu": "esp32c6", - "product": "M5Stack NanoC6", + "product": "NanoC6", "url": "https://shop.m5stack.com/products/m5stack-nanoc6-dev-kit", "vendor": "M5Stack" } diff --git a/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json b/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json index 3eb9a5e0a7..198f1d4b6d 100644 --- a/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json +++ b/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json @@ -18,8 +18,8 @@ "ESP32-EVB_Rev_K1.png" ], "mcu": "esp32", - "product": "Olimex ESP32 EVB", + "product": "ESP32 EVB", "thumbnail": "", - "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB/open-source-hardware", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB", + "vendor": "Olimex" } diff --git a/ports/esp32/boards/OLIMEX_ESP32_POE/board.json b/ports/esp32/boards/OLIMEX_ESP32_POE/board.json index cda26178d7..1b00d213c7 100644 --- a/ports/esp32/boards/OLIMEX_ESP32_POE/board.json +++ b/ports/esp32/boards/OLIMEX_ESP32_POE/board.json @@ -19,8 +19,8 @@ "ESP32-POE-ISO-1.jpg" ], "mcu": "esp32", - "product": "Olimex ESP32 POE", + "product": "ESP32 POE", "thumbnail": "", - "url": "https://www.olimex.com/", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-POE", + "vendor": "Olimex" } diff --git a/ports/esp32/boards/SIL_WESP32/board.json b/ports/esp32/boards/SIL_WESP32/board.json index 53a50f3286..7126fa7ea1 100644 --- a/ports/esp32/boards/SIL_WESP32/board.json +++ b/ports/esp32/boards/SIL_WESP32/board.json @@ -18,7 +18,7 @@ "wesp32-top.jpg" ], "mcu": "esp32", - "product": "SIL WESP32", + "product": "wESP32", "thumbnail": "", "url": "https://wesp32.com/", "vendor": "Silicognition" diff --git a/ports/esp32/boards/SIL_WESP32/sdkconfig.board b/ports/esp32/boards/SIL_WESP32/sdkconfig.board index 741a4db439..36155cd5e3 100644 --- a/ports/esp32/boards/SIL_WESP32/sdkconfig.board +++ b/ports/esp32/boards/SIL_WESP32/sdkconfig.board @@ -1,9 +1,6 @@ -# 16 MB flash +# 8 MB+ flash -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB= -CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y -CONFIG_ESPTOOLPY_FLASHSIZE="16MB" +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y # Fast flash @@ -14,7 +11,7 @@ CONFIG_ESP32_REV_MIN_1=y # OTA CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB-ota.csv" +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiBplus-ota.csv" # Network name diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json new file mode 100644 index 0000000000..8bce1a0013 --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json @@ -0,0 +1,23 @@ +{ + "deploy": [ + "../deploy.md" + ], + "deploy_options": { + "flash_offset": "0x1000" + }, + "docs": "", + "features": [ + "BLE", + "External Flash", + "WiFi", + "USB-C" + ], + "images": [ + "19177-Sparkfun_IoT_Redboard-ESP32.jpg" + ], + "mcu": "esp32", + "product": "ESP32 / WROOM", + "thumbnail": "", + "url": "https://www.sparkfun.com/sparkfun-iot-redboard-esp32-development-board.html", + "vendor": "SparkFun" +} diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py new file mode 100644 index 0000000000..73446ecac9 --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py @@ -0,0 +1,2 @@ +include("$(PORT_DIR)/boards/manifest.py") +require("sdcard") diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake new file mode 100644 index 0000000000..0ffcc38c87 --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake @@ -0,0 +1,7 @@ +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble + boards/sdkconfig.240mhz +) + +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h new file mode 100644 index 0000000000..4fe888ac17 --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h @@ -0,0 +1,14 @@ +// Board and hardware specific configuration + +#define MICROPY_HW_BOARD_NAME "SparkFun IoT RedBoard ESP32" +#define MICROPY_HW_MCU_NAME "ESP32" + +// Enable UART REPL for modules that have an external USB-UART and don't use native USB. +#define MICROPY_HW_ENABLE_UART_REPL (1) + +#define MICROPY_HW_I2C0_SCL (22) +#define MICROPY_HW_I2C0_SDA (21) + +#define MICROPY_HW_SPI1_SCK (18) +#define MICROPY_HW_SPI1_MOSI (23) +#define MICROPY_HW_SPI1_MISO (19) diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv new file mode 100644 index 0000000000..d37c51af8f --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv @@ -0,0 +1,22 @@ +TX,GPIO1 +RX,GPIO3 +ALERT,GPIO4 +TCK,GPIO13 +TMS,GPIO14 +CS,GPIO5 +PICO,GPIO23 +POCI,GPIO19 +SCK,GPIO18 +SDA,GPIO21 +SCL,GPIO22 +A0,GPIO36 +A3,GPIO39 +A4,GPIO32 +A5,GPIO33 +A6,GPIO34 +A7,GPIO35 +LED,GPIO18 +LED_BLUE,GPIO18 +BLUE_LED,GPIO18 +RGB_LED,GPIO2 +NEOPIXEL,GPIO2 diff --git a/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board index 9ab58f215f..162de0da14 100644 --- a/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board +++ b/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board @@ -3,11 +3,6 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_SPIRAM_MEMTEST= -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv" - # LWIP CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS2" # end of LWIP diff --git a/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board index 3ca0c4b243..3092e35598 100644 --- a/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board +++ b/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB= -CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS3" diff --git a/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board index d0355a94dc..25b8d7689d 100644 --- a/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board +++ b/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board @@ -3,11 +3,6 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_AFTER_NORESET=y CONFIG_SPIRAM_MEMTEST= -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS3Neo" diff --git a/ports/esp32/boards/UM_NANOS3/sdkconfig.board b/ports/esp32/boards/UM_NANOS3/sdkconfig.board index 2a39c64337..e06f7a4245 100644 --- a/ports/esp32/boards/UM_NANOS3/sdkconfig.board +++ b/ports/esp32/boards/UM_NANOS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMNanoS3" diff --git a/ports/esp32/boards/UM_OMGS3/sdkconfig.board b/ports/esp32/boards/UM_OMGS3/sdkconfig.board index 84a8ce449e..8a0bf0b13a 100644 --- a/ports/esp32/boards/UM_OMGS3/sdkconfig.board +++ b/ports/esp32/boards/UM_OMGS3/sdkconfig.board @@ -2,12 +2,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_AFTER_NORESET=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMOMGS3" diff --git a/ports/esp32/boards/UM_PROS3/sdkconfig.board b/ports/esp32/boards/UM_PROS3/sdkconfig.board index 5752e03e60..75ca58d80b 100644 --- a/ports/esp32/boards/UM_PROS3/sdkconfig.board +++ b/ports/esp32/boards/UM_PROS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB= -CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMProS3" diff --git a/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board b/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board index ef3f38af47..7d244fdc16 100644 --- a/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board +++ b/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board @@ -2,12 +2,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_AFTER_NORESET=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMRGBTouchMini" diff --git a/ports/esp32/boards/UM_TINYC6/sdkconfig.board b/ports/esp32/boards/UM_TINYC6/sdkconfig.board index 7917467b12..213d28d8b4 100644 --- a/ports/esp32/boards/UM_TINYC6/sdkconfig.board +++ b/ports/esp32/boards/UM_TINYC6/sdkconfig.board @@ -2,9 +2,4 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_AFTER_NORESET=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" diff --git a/ports/esp32/boards/UM_TINYS3/sdkconfig.board b/ports/esp32/boards/UM_TINYS3/sdkconfig.board index d1d19761a0..2474c5fb27 100644 --- a/ports/esp32/boards/UM_TINYS3/sdkconfig.board +++ b/ports/esp32/boards/UM_TINYS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMTinyS3" diff --git a/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board b/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board index 1380e15ce8..10121d235d 100644 --- a/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board +++ b/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMTinyWATCHS3" diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index 530db42711..30740af434 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -50,7 +50,6 @@ CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=2 CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP=n -CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK=y # UDP CONFIG_LWIP_PPP_SUPPORT=y @@ -98,7 +97,7 @@ CONFIG_ULP_COPROC_RESERVE_MEM=2040 # For cmake build CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiB.csv" +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiBplus.csv" # To reduce iRAM usage CONFIG_ESP32_WIFI_IRAM_OPT=n @@ -118,7 +117,6 @@ CONFIG_ADC_CAL_LUT_ENABLE=y CONFIG_UART_ISR_IN_IRAM=y # IDF 5 deprecated -CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y CONFIG_ETH_USE_SPI_ETHERNET=y diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index c54f5a5403..09b1203913 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -80,9 +80,7 @@ list(APPEND MICROPY_SOURCE_DRIVERS ${MICROPY_DIR}/drivers/dht/dht.c ) -list(APPEND GIT_SUBMODULES lib/tinyusb) if(MICROPY_PY_TINYUSB) - set(TINYUSB_SRC "${MICROPY_DIR}/lib/tinyusb/src") string(TOUPPER OPT_MCU_${IDF_TARGET} tusb_mcu) list(APPEND MICROPY_DEF_TINYUSB @@ -90,12 +88,6 @@ if(MICROPY_PY_TINYUSB) ) list(APPEND MICROPY_SOURCE_TINYUSB - ${TINYUSB_SRC}/tusb.c - ${TINYUSB_SRC}/common/tusb_fifo.c - ${TINYUSB_SRC}/device/usbd.c - ${TINYUSB_SRC}/device/usbd_control.c - ${TINYUSB_SRC}/class/cdc/cdc_device.c - ${TINYUSB_SRC}/portable/synopsys/dwc2/dcd_dwc2.c ${MICROPY_DIR}/shared/tinyusb/mp_usbd.c ${MICROPY_DIR}/shared/tinyusb/mp_usbd_cdc.c ${MICROPY_DIR}/shared/tinyusb/mp_usbd_descriptor.c @@ -103,13 +95,8 @@ if(MICROPY_PY_TINYUSB) ) list(APPEND MICROPY_INC_TINYUSB - ${TINYUSB_SRC} ${MICROPY_DIR}/shared/tinyusb/ ) - - list(APPEND MICROPY_LINK_TINYUSB - -Wl,--wrap=dcd_event_handler - ) endif() list(APPEND MICROPY_SOURCE_PORT @@ -251,6 +238,7 @@ endif() # Set compile options for this port. target_compile_definitions(${MICROPY_TARGET} PUBLIC + ${MICROPY_DEF_COMPONENT} ${MICROPY_DEF_CORE} ${MICROPY_DEF_BOARD} ${MICROPY_DEF_TINYUSB} @@ -263,15 +251,12 @@ target_compile_definitions(${MICROPY_TARGET} PUBLIC # Disable some warnings to keep the build output clean. target_compile_options(${MICROPY_TARGET} PUBLIC + ${MICROPY_COMPILE_COMPONENT} -Wno-clobbered -Wno-deprecated-declarations -Wno-missing-field-initializers ) -target_link_options(${MICROPY_TARGET} PUBLIC - ${MICROPY_LINK_TINYUSB} -) - # Additional include directories needed for private NimBLE headers. target_include_directories(${MICROPY_TARGET} PUBLIC ${IDF_PATH}/components/bt/host/nimble/nimble diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c index be1725c370..02acaa22da 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,32 @@ 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)); + self->block->handle = NULL; + } +} + 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 +189,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 +208,46 @@ 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; } 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 a373603b7e..6b06b432c2 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, ADC_WIDTH_MAX, {0}}, #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, ADC_WIDTH_MAX, {0}}, #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/machine_i2c.c b/ports/esp32/machine_i2c.c index 12b86e4aa0..259101ee7e 100644 --- a/ports/esp32/machine_i2c.c +++ b/ports/esp32/machine_i2c.c @@ -35,9 +35,14 @@ #if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SOFTI2C #ifndef MICROPY_HW_I2C0_SCL +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 +#define MICROPY_HW_I2C0_SCL (GPIO_NUM_9) +#define MICROPY_HW_I2C0_SDA (GPIO_NUM_8) +#else #define MICROPY_HW_I2C0_SCL (GPIO_NUM_18) #define MICROPY_HW_I2C0_SDA (GPIO_NUM_19) #endif +#endif #ifndef MICROPY_HW_I2C1_SCL #if CONFIG_IDF_TARGET_ESP32 diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index d0c4ee1a7e..4ab79f0a26 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -325,14 +325,17 @@ static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_ mp_raise_ValueError(MP_ERROR_TEXT("bad wake value")); } + #if SOC_TOUCH_SENSOR_SUPPORTED if (machine_rtc_config.wake_on_touch) { // not compatible mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } + #endif if (!RTC_IS_VALID_EXT_PIN(index)) { mp_raise_ValueError(MP_ERROR_TEXT("invalid pin for wake")); } + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin == -1) { machine_rtc_config.ext0_pin = index; } else if (machine_rtc_config.ext0_pin != index) { @@ -341,10 +344,13 @@ static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_ machine_rtc_config.ext0_level = trigger == GPIO_INTR_LOW_LEVEL ? 0 : 1; machine_rtc_config.ext0_wake_types = wake; + #endif } else { + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin == index) { machine_rtc_config.ext0_pin = -1; } + #endif if (handler == mp_const_none) { handler = MP_OBJ_NULL; diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 7826769556..7cb84640ee 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -6,7 +6,8 @@ * Copyright (c) 2016-2021 Damien P. George * Copyright (c) 2018 Alan Dragomirecky * Copyright (c) 2020 Antoine Aubert - * Copyright (c) 2021 Ihor Nehrutsa + * Copyright (c) 2021, 2023-2025 Ihor Nehrutsa + * Copyright (c) 2024 Yoann Darche * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,79 +33,41 @@ #include <math.h> #include "py/mphal.h" -#include "driver/ledc.h" #include "esp_err.h" +#include "driver/ledc.h" +#include "soc/ledc_periph.h" #include "soc/gpio_sig_map.h" - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) #include "esp_clk_tree.h" -#endif - -#define PWM_DBG(...) -// #define PWM_DBG(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, "\n"); - -// Total number of channels -#define PWM_CHANNEL_MAX (LEDC_SPEED_MODE_MAX * LEDC_CHANNEL_MAX) - -typedef struct _chan_t { - // Which channel has which GPIO pin assigned? - // (-1 if not assigned) - gpio_num_t pin; - // Which channel has which timer assigned? - // (-1 if not assigned) - int timer_idx; -} chan_t; +#include "py/mpprint.h" -// List of PWM channels -static chan_t chans[PWM_CHANNEL_MAX]; +#define debug_printf(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, " | %d at %s\n", __LINE__, __FILE__); -// channel_idx is an index (end-to-end sequential numbering) for all channels -// available on the chip and described in chans[] -#define CHANNEL_IDX(mode, channel) (mode * LEDC_CHANNEL_MAX + channel) -#define CHANNEL_IDX_TO_MODE(channel_idx) (channel_idx / LEDC_CHANNEL_MAX) -#define CHANNEL_IDX_TO_CHANNEL(channel_idx) (channel_idx % LEDC_CHANNEL_MAX) +#define FADE 0 -// Total number of timers -#define PWM_TIMER_MAX (LEDC_SPEED_MODE_MAX * LEDC_TIMER_MAX) +// 10-bit user interface resolution compatible with esp8266 PWM.duty() +#define UI_RES_10_BIT (10) +#define DUTY_10 UI_RES_10_BIT +// Maximum duty value on 10-bit resolution is 1024 but reduced to 1023 in UI +#define MAX_10_DUTY (1U << UI_RES_10_BIT) -// List of timer configs -static ledc_timer_config_t timers[PWM_TIMER_MAX]; +// 16-bit user interface resolution used in PWM.duty_u16() +#define UI_RES_16_BIT (16) +#define DUTY_16 UI_RES_16_BIT +// Maximum duty value on 16-bit resolution is 65536 but reduced to 65535 in UI +#define MAX_16_DUTY (1U << UI_RES_16_BIT) -// timer_idx is an index (end-to-end sequential numbering) for all timers -// available on the chip and configured in timers[] -#define TIMER_IDX(mode, timer) (mode * LEDC_TIMER_MAX + timer) -#define TIMER_IDX_TO_MODE(timer_idx) (timer_idx / LEDC_TIMER_MAX) -#define TIMER_IDX_TO_TIMER(timer_idx) (timer_idx % LEDC_TIMER_MAX) +// ns user interface used in PWM.duty_ns() +#define DUTY_NS (1) -// Params for PWM operation // 5khz is default frequency -#define PWM_FREQ (5000) - -// 10-bit resolution (compatible with esp8266 PWM) -#define PWM_RES_10_BIT (LEDC_TIMER_10_BIT) - -// Maximum duty value on 10-bit resolution -#define MAX_DUTY_U10 ((1 << PWM_RES_10_BIT) - 1) -// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html#supported-range-of-frequency-and-duty-resolutions -// duty() uses 10-bit resolution or less -// duty_u16() and duty_ns() use 16-bit resolution or less - -// Possible highest resolution in device -#if (LEDC_TIMER_BIT_MAX - 1) < LEDC_TIMER_16_BIT -#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1) -#else -#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit for ESP32, but 16 bit is used -#endif -// Duty resolution of user interface in `duty_u16()` and `duty_u16` parameter in constructor/initializer -#define UI_RES_16_BIT (16) -// Maximum duty value on highest user interface resolution -#define UI_MAX_DUTY ((1 << UI_RES_16_BIT) - 1) -// How much to shift from the HIGHEST_PWM_RES duty resolution to the user interface duty resolution UI_RES_16_BIT -#define UI_RES_SHIFT (UI_RES_16_BIT - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3 - -#if SOC_LEDC_SUPPORT_REF_TICK +#define PWM_FREQ (5000) +// default duty 50% +#define PWM_DUTY ((1U << UI_RES_16_BIT) / 2) + +// All chips except esp32 and esp32s2 do not have timer-specific clock sources, which means clock source for all timers must be the same one. +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 // If the PWM frequency is less than EMPIRIC_FREQ, then LEDC_REF_CLK_HZ(1 MHz) source is used, else LEDC_APB_CLK_HZ(80 MHz) source is used -#define EMPIRIC_FREQ (10) // Hz +#define EMPIRIC_FREQ (10) // Hz #endif // Config of timer upon which we run all PWM'ed GPIO pins @@ -113,490 +76,675 @@ static bool pwm_inited = false; // MicroPython PWM object struct typedef struct _machine_pwm_obj_t { mp_obj_base_t base; - gpio_num_t pin; - bool active; - int mode; - int channel; - int timer; - int duty_x; // PWM_RES_10_BIT if duty(), HIGHEST_PWM_RES if duty_u16(), -HIGHEST_PWM_RES if duty_ns() - int duty_u10; // stored values from previous duty setters - int duty_u16; // - / - - int duty_ns; // - / - + int8_t pin; + int8_t mode; + int8_t channel; + int8_t timer; + bool lightsleep; + int32_t freq; + int8_t duty_scale; // DUTY_10 if duty(), DUTY_16 if duty_u16(), DUTY_NS if duty_ns() + int duty_ui; // saved values of UI duty + int channel_duty; // saved values of UI duty, calculated to raw channel->duty + bool output_invert; + bool output_invert_prev; } machine_pwm_obj_t; -static bool is_timer_in_use(int current_channel_idx, int timer_idx); -static void set_duty_u16(machine_pwm_obj_t *self, int duty); -static void set_duty_u10(machine_pwm_obj_t *self, int duty); -static void set_duty_ns(machine_pwm_obj_t *self, int ns); +typedef struct _chans_t { + int8_t pin; // Which channel has which GPIO pin assigned? (-1 if not assigned) + int8_t timer; // Which channel has which timer assigned? (-1 if not assigned) + bool lightsleep; // Is light sleep enable has been set for this pin +} chans_t; + +// List of PWM channels +static chans_t chans[LEDC_SPEED_MODE_MAX][LEDC_CHANNEL_MAX]; + +typedef struct _timers_t { + int32_t freq; + int8_t duty_resolution; + ledc_clk_cfg_t clk_cfg; +} timers_t; + +// List of PWM timers +static timers_t timers[LEDC_SPEED_MODE_MAX][LEDC_TIMER_MAX]; + +// register-unregister channel +static void register_channel(int mode, int channel, int pin, int timer) { + chans[mode][channel].pin = pin; + chans[mode][channel].timer = timer; +} + +static void unregister_channel(int mode, int channel) { + register_channel(mode, channel, -1, -1); + chans[mode][channel].lightsleep = false; +} + +static void unregister_timer(int mode, int timer) { + timers[mode][timer].freq = -1; // unused timer freq is -1 + timers[mode][timer].duty_resolution = 0; + timers[mode][timer].clk_cfg = LEDC_AUTO_CLK; +} static void pwm_init(void) { - // Initial condition: no channels assigned - for (int i = 0; i < PWM_CHANNEL_MAX; ++i) { - chans[i].pin = -1; - chans[i].timer_idx = -1; + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + // Initial condition: no channels assigned + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + unregister_channel(mode, channel); + } + // Initial condition: no timers assigned + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + unregister_timer(mode, timer); + } } +} - // Prepare all timers config - // Initial condition: no timers assigned - for (int i = 0; i < PWM_TIMER_MAX; ++i) { - timers[i].duty_resolution = HIGHEST_PWM_RES; - // unset timer is -1 - timers[i].freq_hz = -1; - timers[i].speed_mode = TIMER_IDX_TO_MODE(i); - timers[i].timer_num = TIMER_IDX_TO_TIMER(i); - timers[i].clk_cfg = LEDC_AUTO_CLK; // will reinstall later according to the EMPIRIC_FREQ +// Returns true if the timer is in use in addition to current channel +static bool is_timer_in_use(int mode, int current_channel, int timer) { + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if ((channel != current_channel) && (chans[mode][channel].timer == timer) && (chans[mode][channel].pin >= 0)) { + return true; + } } + return false; } // Deinit channel and timer if the timer is unused -static void pwm_deinit(int channel_idx) { - // Valid channel? - if ((channel_idx >= 0) && (channel_idx < PWM_CHANNEL_MAX)) { +static void pwm_deinit(int mode, int channel, int level) { + // Is valid channel? + if ((mode >= 0) && (mode < LEDC_SPEED_MODE_MAX) && (channel >= 0) && (channel < LEDC_CHANNEL_MAX)) { // Clean up timer if necessary - int timer_idx = chans[channel_idx].timer_idx; - if (timer_idx != -1) { - if (!is_timer_in_use(channel_idx, timer_idx)) { - check_esp_err(ledc_timer_pause(TIMER_IDX_TO_MODE(timer_idx), TIMER_IDX_TO_TIMER(timer_idx))); - ledc_timer_config_t timer_config = { + int timer = chans[mode][channel].timer; + if (timer >= 0) { + if (!is_timer_in_use(mode, channel, timer)) { + check_esp_err(ledc_timer_pause(mode, timer)); + ledc_timer_config_t ledc_timer = { .deconfigure = true, - .speed_mode = TIMER_IDX_TO_MODE(timer_idx), - .timer_num = TIMER_IDX_TO_TIMER(timer_idx), + .speed_mode = mode, + .timer_num = timer, }; - check_esp_err(ledc_timer_config(&timer_config)); - // Flag it unused - timers[chans[channel_idx].timer_idx].freq_hz = -1; + ledc_timer_config(&ledc_timer); + unregister_timer(mode, timer); } } - int pin = chans[channel_idx].pin; - if (pin != -1) { - int mode = CHANNEL_IDX_TO_MODE(channel_idx); - int channel = CHANNEL_IDX_TO_CHANNEL(channel_idx); - // Mark it unused, and tell the hardware to stop routing - check_esp_err(ledc_stop(mode, channel, 0)); - // Disable ledc signal for the pin - // esp_rom_gpio_connect_out_signal(pin, SIG_GPIO_OUT_IDX, false, false); - if (mode == LEDC_LOW_SPEED_MODE) { - esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT0_IDX + channel, false, true); - } else { - #if LEDC_SPEED_MODE_MAX > 1 - #if CONFIG_IDF_TARGET_ESP32 - esp_rom_gpio_connect_out_signal(pin, LEDC_HS_SIG_OUT0_IDX + channel, false, true); - #else - #error Add supported CONFIG_IDF_TARGET_ESP32_xxx - #endif - #endif + int pin = chans[mode][channel].pin; + if (pin >= 0) { + // Disable LEDC output, and set idle level + check_esp_err(ledc_stop(mode, channel, level)); + if (chans[mode][channel].lightsleep) { + // Enable SLP_SEL to change GPIO status automantically in lightsleep. + check_esp_err(gpio_sleep_sel_en(pin)); + chans[mode][channel].lightsleep = false; } } - chans[channel_idx].pin = -1; - chans[channel_idx].timer_idx = -1; + unregister_channel(mode, channel); } } // This called from Ctrl-D soft reboot void machine_pwm_deinit_all(void) { if (pwm_inited) { - for (int channel_idx = 0; channel_idx < PWM_CHANNEL_MAX; ++channel_idx) { - pwm_deinit(channel_idx); + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + pwm_deinit(mode, channel, 0); + } } + #if FADE + ledc_fade_func_uninstall(); + #endif pwm_inited = false; } } -static void configure_channel(machine_pwm_obj_t *self) { - ledc_channel_config_t cfg = { - .channel = self->channel, - .duty = (1 << (timers[TIMER_IDX(self->mode, self->timer)].duty_resolution)) / 2, - .gpio_num = self->pin, - .intr_type = LEDC_INTR_DISABLE, - .speed_mode = self->mode, - .timer_sel = self->timer, - }; - if (ledc_channel_config(&cfg) != ESP_OK) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("PWM not supported on Pin(%d)"), self->pin); - } -} - -static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_config_t *timer) { - esp_err_t err; - if (freq != timer->freq_hz) { - // Configure the new frequency and resolution - timer->freq_hz = freq; - - #if SOC_LEDC_SUPPORT_PLL_DIV_CLOCK - timer->clk_cfg = LEDC_USE_PLL_DIV_CLK; - #elif SOC_LEDC_SUPPORT_APB_CLOCK - timer->clk_cfg = LEDC_USE_APB_CLK; - #elif SOC_LEDC_SUPPORT_XTAL_CLOCK - timer->clk_cfg = LEDC_USE_XTAL_CLK; - #else - #error No supported PWM / LEDC clocks. - #endif - #if SOC_LEDC_SUPPORT_REF_TICK - if (freq < EMPIRIC_FREQ) { - timer->clk_cfg = LEDC_USE_REF_TICK; - } - #endif - uint32_t src_clk_freq = 0; - err = esp_clk_tree_src_get_freq_hz(timer->clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_freq); - if (err != ESP_OK) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unable to query source clock frequency %d"), (int)timer->clk_cfg); - } - - timer->duty_resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer->freq_hz); - - // Set frequency - err = ledc_timer_config(timer); - if (err != ESP_OK) { - if (err == ESP_FAIL) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unreachable frequency %d"), freq); - } else { - check_esp_err(err); - } - } - // Reset the timer if low speed - if (self->mode == LEDC_LOW_SPEED_MODE) { - check_esp_err(ledc_timer_rst(self->mode, self->timer)); - } - } - - // Save the same duty cycle when frequency is changed - if (self->duty_x == HIGHEST_PWM_RES) { - set_duty_u16(self, self->duty_u16); - } else if (self->duty_x == PWM_RES_10_BIT) { - set_duty_u10(self, self->duty_u10); - } else if (self->duty_x == -HIGHEST_PWM_RES) { - set_duty_ns(self, self->duty_ns); +static void pwm_is_active(machine_pwm_obj_t *self) { + if (self->timer < 0) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("PWM is inactive")); } } // Calculate the duty parameters based on an ns value static int ns_to_duty(machine_pwm_obj_t *self, int ns) { - ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; - int64_t duty = ((int64_t)ns * UI_MAX_DUTY * timer.freq_hz + 500000000LL) / 1000000000LL; + pwm_is_active(self); + int64_t duty = ((int64_t)ns * (int64_t)MAX_16_DUTY * self->freq + 500000000LL) / 1000000000LL; if ((ns > 0) && (duty == 0)) { duty = 1; - } else if (duty > UI_MAX_DUTY) { - duty = UI_MAX_DUTY; + } else if (duty > MAX_16_DUTY) { + duty = MAX_16_DUTY; } return duty; } static int duty_to_ns(machine_pwm_obj_t *self, int duty) { - ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; - int64_t ns = ((int64_t)duty * 1000000000LL + (int64_t)timer.freq_hz * UI_MAX_DUTY / 2) / ((int64_t)timer.freq_hz * UI_MAX_DUTY); - return ns; + pwm_is_active(self); + return ((int64_t)duty * 1000000000LL + (int64_t)self->freq * (int64_t)(MAX_16_DUTY / 2)) / ((int64_t)self->freq * (int64_t)MAX_16_DUTY); } -#define get_duty_raw(self) ledc_get_duty(self->mode, self->channel) +// Reconfigure PWM pin output as input/output. +static void reconfigure_pin(machine_pwm_obj_t *self) { + #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 0) + // This allows to read the pin level. + gpio_set_direction(self->pin, GPIO_MODE_INPUT_OUTPUT); + #endif + esp_rom_gpio_connect_out_signal(self->pin, ledc_periph_signal[self->mode].sig_out0_idx + self->channel, self->output_invert, 0); +} -static void pwm_is_active(machine_pwm_obj_t *self) { - if (self->active == false) { - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("PWM inactive")); +static void apply_duty(machine_pwm_obj_t *self) { + pwm_is_active(self); + + int duty = 0; + if (self->duty_scale == DUTY_16) { + duty = self->duty_ui; + } else if (self->duty_scale == DUTY_10) { + duty = self->duty_ui << (UI_RES_16_BIT - UI_RES_10_BIT); + } else if (self->duty_scale == DUTY_NS) { + duty = ns_to_duty(self, self->duty_ui); + } + self->channel_duty = duty >> (UI_RES_16_BIT - timers[self->mode][self->timer].duty_resolution); + if ((chans[self->mode][self->channel].pin == -1) || (self->output_invert_prev != self->output_invert)) { + self->output_invert_prev = self->output_invert; + // New PWM assignment + ledc_channel_config_t cfg = { + .channel = self->channel, + .duty = self->channel_duty, + .gpio_num = self->pin, + .intr_type = LEDC_INTR_DISABLE, + .speed_mode = self->mode, + .timer_sel = self->timer, + .hpoint = 0, + .flags.output_invert = self->output_invert, + }; + check_esp_err(ledc_channel_config(&cfg)); + reconfigure_pin(self); + } else { + #if FADE + check_esp_err(ledc_set_duty_and_update(self->mode, self->channel, self->channel_duty, 0)); + #else + check_esp_err(ledc_set_duty(self->mode, self->channel, self->channel_duty)); + check_esp_err(ledc_update_duty(self->mode, self->channel)); + #endif + } + if (self->lightsleep) { + // Disable SLP_SEL to change GPIO status automantically in lightsleep. + check_esp_err(gpio_sleep_sel_dis(self->pin)); + chans[self->mode][self->channel].lightsleep = true; } + register_channel(self->mode, self->channel, self->pin, self->timer); +} + +static uint32_t find_suitable_duty_resolution(uint32_t src_clk_freq, uint32_t timer_freq) { + unsigned int resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer_freq); + if (resolution > UI_RES_16_BIT) { + // limit resolution to user interface + resolution = UI_RES_16_BIT; + } + // Note: On ESP32, ESP32S2, ESP32S3, ESP32C3, ESP32C2, ESP32C6, ESP32H2, ESP32P4, due to a hardware bug, + // 100% duty cycle (i.e. 2**duty_res) is not reachable when the binded timer selects the maximum duty + // resolution. For example, the max duty resolution on ESP32C3 is 14-bit width, then set duty to (2**14) + // will mess up the duty calculation in hardware. + // Reduce the resolution from 14 to 13 bits to resolve the hardware bug. + if (resolution >= SOC_LEDC_TIMER_BIT_WIDTH) { + resolution -= 1; + } + return resolution; } static uint32_t get_duty_u16(machine_pwm_obj_t *self) { pwm_is_active(self); - int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution; - int duty = ledc_get_duty(self->mode, self->channel); - if (resolution <= UI_RES_16_BIT) { - duty <<= (UI_RES_16_BIT - resolution); + int duty = ledc_get_duty(self->mode, self->channel) << (UI_RES_16_BIT - timers[self->mode][self->timer].duty_resolution); + if (duty != MAX_16_DUTY) { + return duty; } else { - duty >>= (resolution - UI_RES_16_BIT); + return MAX_16_DUTY - 1; } - return duty; } static uint32_t get_duty_u10(machine_pwm_obj_t *self) { - pwm_is_active(self); - return get_duty_u16(self) >> 6; // Scale down from 16 bit to 10 bit resolution + // Scale down from 16 bit to 10 bit resolution + return get_duty_u16(self) >> (UI_RES_16_BIT - UI_RES_10_BIT); } static uint32_t get_duty_ns(machine_pwm_obj_t *self) { - pwm_is_active(self); return duty_to_ns(self, get_duty_u16(self)); } -static void set_duty_u16(machine_pwm_obj_t *self, int duty) { - pwm_is_active(self); - if ((duty < 0) || (duty > UI_MAX_DUTY)) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), UI_MAX_DUTY); +static void check_duty_u16(machine_pwm_obj_t *self, int duty) { + if ((duty < 0) || (duty > MAX_16_DUTY - 1)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), MAX_16_DUTY); } - ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; - int channel_duty; - if (timer.duty_resolution <= UI_RES_16_BIT) { - channel_duty = duty >> (UI_RES_16_BIT - timer.duty_resolution); - } else { - channel_duty = duty << (timer.duty_resolution - UI_RES_16_BIT); + if (duty == MAX_16_DUTY - 1) { + duty = MAX_16_DUTY; } - int max_duty = (1 << timer.duty_resolution) - 1; - if (channel_duty < 0) { - channel_duty = 0; - } else if (channel_duty > max_duty) { - channel_duty = max_duty; - } - check_esp_err(ledc_set_duty(self->mode, self->channel, channel_duty)); - check_esp_err(ledc_update_duty(self->mode, self->channel)); + self->duty_scale = DUTY_16; + self->duty_ui = duty; +} - /* - // Bug: Sometimes duty is not set right now. - // Not a bug. It's a feature. The duty is applied at the beginning of the next signal period. - // Bug: It has been experimentally established that the duty is set during 2 signal periods, but 1 period is expected. - // See https://github.com/espressif/esp-idf/issues/7288 - if (duty != get_duty_u16(self)) { - PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, channel_duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), channel_duty, timer.duty_resolution, timer.freq_hz); - esp_rom_delay_us(2 * 1000000 / timer.freq_hz); - if (duty != get_duty_u16(self)) { - PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, channel_duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), channel_duty, timer.duty_resolution, timer.freq_hz); - } - } - */ +static void set_duty_u16(machine_pwm_obj_t *self, int duty) { + check_duty_u16(self, duty); + apply_duty(self); +} - self->duty_x = HIGHEST_PWM_RES; - self->duty_u16 = duty; +static void check_duty_u10(machine_pwm_obj_t *self, int duty) { + if ((duty < 0) || (duty > MAX_10_DUTY - 1)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_10_DUTY - 1); + } + if (duty == MAX_10_DUTY - 1) { + duty = MAX_10_DUTY; + } + self->duty_scale = DUTY_10; + self->duty_ui = duty; } static void set_duty_u10(machine_pwm_obj_t *self, int duty) { - pwm_is_active(self); - if ((duty < 0) || (duty > MAX_DUTY_U10)) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_DUTY_U10); + check_duty_u10(self, duty); + apply_duty(self); +} + +static void check_duty_ns(machine_pwm_obj_t *self, int ns) { + if ((ns < 0) || (ns > duty_to_ns(self, MAX_16_DUTY))) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_ns must be from 0 to %d ns"), duty_to_ns(self, MAX_16_DUTY)); } - set_duty_u16(self, duty << (UI_RES_16_BIT - PWM_RES_10_BIT)); - self->duty_x = PWM_RES_10_BIT; - self->duty_u10 = duty; + self->duty_scale = DUTY_NS; + self->duty_ui = ns; } static void set_duty_ns(machine_pwm_obj_t *self, int ns) { - pwm_is_active(self); - if ((ns < 0) || (ns > duty_to_ns(self, UI_MAX_DUTY))) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_ns must be from 0 to %d ns"), duty_to_ns(self, UI_MAX_DUTY)); + check_duty_ns(self, ns); + apply_duty(self); +} + +#if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) +// This check if a clock is already set in the timer list, if yes, return the LEDC_XXX value +static ledc_clk_cfg_t find_clock_in_use() { + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + if (timers[mode][timer].clk_cfg != LEDC_AUTO_CLK) { + return timers[mode][timer].clk_cfg; + } + } } - set_duty_u16(self, ns_to_duty(self, ns)); - self->duty_x = -HIGHEST_PWM_RES; - self->duty_ns = ns; + return LEDC_AUTO_CLK; } -/******************************************************************************/ +// Check if a timer is already set with a different clock source +static bool is_timer_with_different_clock(int mode, int current_timer, ledc_clk_cfg_t clk_cfg) { + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + if ((timer != current_timer) && (clk_cfg != LEDC_AUTO_CLK) && (timers[mode][timer].clk_cfg != LEDC_AUTO_CLK) && (timers[mode][timer].clk_cfg != clk_cfg)) { + return true; + } + } + return false; +} +#endif -#define SAME_FREQ_ONLY (true) -#define SAME_FREQ_OR_FREE (false) -#define ANY_MODE (-1) +static void check_freq_ranges(machine_pwm_obj_t *self, int freq, int upper_freq) { + if ((freq <= 0) || (freq > upper_freq)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("frequency must be from 1Hz to %dMHz"), upper_freq / 1000000); + } +} -// Return timer_idx. Use TIMER_IDX_TO_MODE(timer_idx) and TIMER_IDX_TO_TIMER(timer_idx) to get mode and timer -static int find_timer(unsigned int freq, bool same_freq_only, int mode) { - int free_timer_idx_found = -1; - // Find a free PWM Timer using the same freq - for (int timer_idx = 0; timer_idx < PWM_TIMER_MAX; ++timer_idx) { - if ((mode == ANY_MODE) || (mode == TIMER_IDX_TO_MODE(timer_idx))) { - if (timers[timer_idx].freq_hz == freq) { - // A timer already uses the same freq. Use it now. - return timer_idx; - } - if (!same_freq_only && (free_timer_idx_found == -1) && (timers[timer_idx].freq_hz == -1)) { - free_timer_idx_found = timer_idx; - // Continue to check if a channel with the same freq is in use. +// Set timer frequency +static void set_freq(machine_pwm_obj_t *self, unsigned int freq) { + self->freq = freq; + if ((timers[self->mode][self->timer].freq != freq) || (self->lightsleep)) { + ledc_timer_config_t timer = {}; + timer.speed_mode = self->mode; + timer.timer_num = self->timer; + timer.freq_hz = freq; + timer.deconfigure = false; + + timer.clk_cfg = LEDC_AUTO_CLK; + #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) + ledc_clk_cfg_t clk_cfg = find_clock_in_use(); + if (clk_cfg != LEDC_AUTO_CLK) { + timer.clk_cfg = clk_cfg; + } else + #endif + if (self->lightsleep) { + timer.clk_cfg = LEDC_USE_RC_FAST_CLK; // 8 or 20 MHz + } else { + #if SOC_LEDC_SUPPORT_APB_CLOCK + timer.clk_cfg = LEDC_USE_APB_CLK; // 80 MHz + #elif SOC_LEDC_SUPPORT_PLL_DIV_CLOCK + timer.clk_cfg = LEDC_USE_PLL_DIV_CLK; // 60 or 80 or 96 MHz + #elif SOC_LEDC_SUPPORT_XTAL_CLOCK + timer.clk_cfg = LEDC_USE_XTAL_CLK; // 40 MHz + #else + #error No supported PWM / LEDC clocks. + #endif + + #ifdef EMPIRIC_FREQ // ESP32 and ESP32S2 only + if (freq < EMPIRIC_FREQ) { + timer.clk_cfg = LEDC_USE_REF_TICK; // 1 MHz } + #endif } - } + #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) + // Check for clock source conflict + clk_cfg = find_clock_in_use(); + if ((clk_cfg != LEDC_AUTO_CLK) && (clk_cfg != timer.clk_cfg)) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("one or more active timers use a different clock source, not supported by the current SoC.")); + } + #endif - return free_timer_idx_found; + uint32_t src_clk_freq = 0; + check_esp_err(esp_clk_tree_src_get_freq_hz(timer.clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &src_clk_freq)); + // machine.freq(20_000_000) reduces APB_CLK_FREQ to 20MHz and the highest PWM frequency to 10MHz + check_freq_ranges(self, freq, src_clk_freq / 2); + + // Configure the new resolution + timer.duty_resolution = find_suitable_duty_resolution(src_clk_freq, self->freq); + // Configure timer - Set frequency + if ((timers[self->mode][self->timer].duty_resolution == timer.duty_resolution) && (timers[self->mode][self->timer].clk_cfg == timer.clk_cfg)) { + check_esp_err(ledc_set_freq(self->mode, self->timer, freq)); + } else { + check_esp_err(ledc_timer_config(&timer)); + } + // Reset the timer if low speed + if (self->mode == LEDC_LOW_SPEED_MODE) { + check_esp_err(ledc_timer_rst(self->mode, self->timer)); + } + timers[self->mode][self->timer].freq = freq; + timers[self->mode][self->timer].duty_resolution = timer.duty_resolution; + timers[self->mode][self->timer].clk_cfg = timer.clk_cfg; + } } -// Return true if the timer is in use in addition to current channel -static bool is_timer_in_use(int current_channel_idx, int timer_idx) { - for (int i = 0; i < PWM_CHANNEL_MAX; ++i) { - if ((i != current_channel_idx) && (chans[i].timer_idx == timer_idx)) { +static bool is_free_channels(int mode, int pin) { + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if ((chans[mode][channel].pin < 0) || (chans[mode][channel].pin == pin)) { return true; } } + return false; +} +static bool is_free_timers(int mode, int32_t freq) { + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + if ((timers[mode][timer].freq < 0) || (timers[mode][timer].freq == freq)) { + return true; + } + } return false; } -// Find a free PWM channel, also spot if our pin is already mentioned. -// Return channel_idx. Use CHANNEL_IDX_TO_MODE(channel_idx) and CHANNEL_IDX_TO_CHANNEL(channel_idx) to get mode and channel -static int find_channel(int pin, int mode) { - int avail_idx = -1; - int channel_idx; - for (channel_idx = 0; channel_idx < PWM_CHANNEL_MAX; ++channel_idx) { - if ((mode == ANY_MODE) || (mode == CHANNEL_IDX_TO_MODE(channel_idx))) { - if (chans[channel_idx].pin == pin) { - break; +// Find self channel or free channel +static void find_channel(machine_pwm_obj_t *self, int *ret_mode, int *ret_channel, int32_t freq) { + // Try to find self channel first + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + #if SOC_LEDC_SUPPORT_HS_MODE + if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) { + continue; + } + #endif + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if (chans[mode][channel].pin == self->pin) { + *ret_mode = mode; + *ret_channel = channel; + return; } - if ((avail_idx == -1) && (chans[channel_idx].pin == -1)) { - avail_idx = channel_idx; + } + } + // Find free channel + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + #if SOC_LEDC_SUPPORT_HS_MODE + if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) { + continue; + } + #endif + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if ((chans[mode][channel].pin < 0) && is_free_timers(mode, freq)) { + *ret_mode = mode; + *ret_channel = channel; + return; } } } - if (channel_idx >= PWM_CHANNEL_MAX) { - channel_idx = avail_idx; +} + +// Returns timer with the same frequency, freq == -1 means free timer +static void find_timer(machine_pwm_obj_t *self, int freq, int *ret_mode, int *ret_timer) { + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + #if SOC_LEDC_SUPPORT_HS_MODE + if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) { + continue; + } + #endif + if (is_free_channels(mode, self->pin)) { + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + if (timers[mode][timer].freq == freq) { + *ret_mode = mode; + *ret_timer = timer; + return; + } + } + } } - return channel_idx; } -/******************************************************************************/ +// Try to find a timer with the same frequency in the current mode, otherwise in the next mode. +// If no existing timer and channel was found, then try to find free timer in any mode. +// If the mode or channel is changed, release the channel and register a new channel in the next mode. +static void select_timer(machine_pwm_obj_t *self, int freq) { + // mode, channel, timer may be -1(not defined) or actual values + int mode = -1; + int timer = -1; + // Check if an already running timer with the required frequency is running + find_timer(self, freq, &mode, &timer); + if (timer < 0) { + // Try to reuse self timer + if ((self->mode >= 0) && (self->channel >= 0)) { + if (!is_timer_in_use(self->mode, self->channel, self->timer)) { + mode = self->mode; + timer = self->timer; + } + } + // If no existing timer and channel was found, then try to find free timer in any mode + if (timer < 0) { + find_timer(self, -1, &mode, &timer); + } + } + if (timer < 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("out of %sPWM timers:%d"), self->lightsleep ? "light sleep capable " : "", self->lightsleep ? LEDC_TIMER_MAX : LEDC_SPEED_MODE_MAX *LEDC_TIMER_MAX); + } + // If the timer is found, then register + if (self->timer != timer) { + unregister_channel(self->mode, self->channel); + // Rregister the channel to the timer + self->mode = mode; + self->timer = timer; + register_channel(self->mode, self->channel, -1, self->timer); + } + #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) + if (is_timer_with_different_clock(self->mode, self->timer, timers[self->mode][self->timer].clk_cfg)) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("one or more active timers use a different clock source, not supported by the current SoC.")); + } + #endif +} + +static void set_freq_duty(machine_pwm_obj_t *self, unsigned int freq) { + select_timer(self, freq); + set_freq(self, freq); + apply_duty(self); +} + +// ****************************************************************************** // MicroPython bindings for PWM static void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "PWM(Pin(%u)", self->pin); - if (self->active) { + if (self->timer >= 0) { mp_printf(print, ", freq=%u", ledc_get_freq(self->mode, self->timer)); - - if (self->duty_x == PWM_RES_10_BIT) { + if (self->duty_scale == DUTY_10) { mp_printf(print, ", duty=%d", get_duty_u10(self)); - } else if (self->duty_x == -HIGHEST_PWM_RES) { + } else if (self->duty_scale == DUTY_NS) { mp_printf(print, ", duty_ns=%d", get_duty_ns(self)); } else { mp_printf(print, ", duty_u16=%d", get_duty_u16(self)); } - int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution; - mp_printf(print, ", resolution=%d", resolution); - - mp_printf(print, ", (duty=%.2f%%, resolution=%.3f%%)", 100.0 * get_duty_raw(self) / (1 << resolution), 100.0 * 1 / (1 << resolution)); // percents - - mp_printf(print, ", mode=%d, channel=%d, timer=%d", self->mode, self->channel, self->timer); + if (self->output_invert) { + mp_printf(print, ", invert=True"); + } + if (self->lightsleep) { + mp_printf(print, ", lightsleep=True"); + } + mp_printf(print, ")"); + + #if MICROPY_ERROR_REPORTING > MICROPY_ERROR_REPORTING_NORMAL + mp_printf(print, " # duty=%.2f%%", 100.0 * get_duty_u16(self) / MAX_16_DUTY); + mp_printf(print, ", raw_duty=%d, resolution=%d", ledc_get_duty(self->mode, self->channel), timers[self->mode][self->timer].duty_resolution); + mp_printf(print, ", mode=%d, timer=%d, channel=%d", self->mode, self->timer, self->channel); + int clk_cfg = timers[self->mode][self->timer].clk_cfg; + mp_printf(print, ", clk_cfg=%d=", clk_cfg); + if (clk_cfg == LEDC_USE_RC_FAST_CLK) { + mp_printf(print, "RC_FAST_CLK"); + } + #if SOC_LEDC_SUPPORT_APB_CLOCK + else if (clk_cfg == LEDC_USE_APB_CLK) { + mp_printf(print, "APB_CLK"); + } + #endif + #if SOC_LEDC_SUPPORT_XTAL_CLOCK + else if (clk_cfg == LEDC_USE_XTAL_CLK) { + mp_printf(print, "XTAL_CLK"); + } + #endif + #if SOC_LEDC_SUPPORT_REF_TICK + else if (clk_cfg == LEDC_USE_REF_TICK) { + mp_printf(print, "REF_TICK"); + } + #endif + #if SOC_LEDC_SUPPORT_PLL_DIV_CLOCK + else if (clk_cfg == LEDC_USE_PLL_DIV_CLK) { + mp_printf(print, "PLL_CLK"); + } + #endif + else if (clk_cfg == LEDC_AUTO_CLK) { + mp_printf(print, "AUTO_CLK"); + } else { + mp_printf(print, "UNKNOWN"); + } + #endif + } else { + mp_printf(print, ")"); } - mp_printf(print, ")"); } // This called from pwm.init() method +// +// Check the current mode. +// If the frequency is changed, try to find a timer with the same frequency +// in the current mode, otherwise in the new mode. +// If the mode is changed, release the channel and select a new channel in the new mode. +// Then set the frequency with the same duty. static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns }; - static const mp_arg_t allowed_args[] = { + + enum { ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_lightsleep }; + mp_arg_t allowed_args[] = { { MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_duty, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_duty_u16, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_duty_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = self->output_invert} }, + { MP_QSTR_lightsleep, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = self->lightsleep} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - int channel_idx = find_channel(self->pin, ANY_MODE); - if (channel_idx == -1) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM channels:%d"), PWM_CHANNEL_MAX); // in all modes + self->lightsleep = args[ARG_lightsleep].u_bool; + + int freq = args[ARG_freq].u_int; + if (freq != -1) { + check_freq_ranges(self, freq, 40000000); } int duty = args[ARG_duty].u_int; int duty_u16 = args[ARG_duty_u16].u_int; int duty_ns = args[ARG_duty_ns].u_int; - if (((duty != -1) && (duty_u16 != -1)) || ((duty != -1) && (duty_ns != -1)) || ((duty_u16 != -1) && (duty_ns != -1))) { - mp_raise_ValueError(MP_ERROR_TEXT("only one of parameters 'duty', 'duty_u16' or 'duty_ns' is allowed")); + if (duty_u16 >= 0) { + check_duty_u16(self, duty_u16); + } else if (duty_ns >= 0) { + check_duty_ns(self, duty_ns); + } else if (duty >= 0) { + check_duty_u10(self, duty); + } else if (self->duty_scale == 0) { + self->duty_scale = DUTY_16; + self->duty_ui = PWM_DUTY; + } + + self->output_invert = args[ARG_invert].u_bool; + + // Check the current mode and channel + int mode = -1; + int channel = -1; + find_channel(self, &mode, &channel, freq); + if (channel < 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("out of %sPWM channels:%d"), self->lightsleep ? "light sleep capable " : "", self->lightsleep ? LEDC_CHANNEL_MAX : LEDC_SPEED_MODE_MAX *LEDC_CHANNEL_MAX); } + self->mode = mode; + self->channel = channel; - int freq = args[ARG_freq].u_int; // Check if freq wasn't passed as an argument - if (freq == -1) { + if ((freq == -1) && (mode >= 0) && (channel >= 0)) { // Check if already set, otherwise use the default freq. // It is possible in case: // pwm = PWM(pin, freq=1000, duty=256) // pwm = PWM(pin, duty=128) - if (chans[channel_idx].timer_idx != -1) { - freq = timers[chans[channel_idx].timer_idx].freq_hz; + if (chans[mode][channel].timer >= 0) { + freq = timers[mode][chans[mode][channel].timer].freq; } if (freq <= 0) { freq = PWM_FREQ; } } - if ((freq <= 0) || (freq > 40000000)) { - mp_raise_ValueError(MP_ERROR_TEXT("frequency must be from 1Hz to 40MHz")); - } - - int timer_idx; - int current_timer_idx = chans[channel_idx].timer_idx; - bool current_in_use = is_timer_in_use(channel_idx, current_timer_idx); - if (current_in_use) { - timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, CHANNEL_IDX_TO_MODE(channel_idx)); - } else { - timer_idx = chans[channel_idx].timer_idx; - } - - if (timer_idx == -1) { - timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, ANY_MODE); - } - if (timer_idx == -1) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM timers:%d"), PWM_TIMER_MAX); // in all modes - } - - int mode = TIMER_IDX_TO_MODE(timer_idx); - if (CHANNEL_IDX_TO_MODE(channel_idx) != mode) { - // unregister old channel - chans[channel_idx].pin = -1; - chans[channel_idx].timer_idx = -1; - // find new channel - channel_idx = find_channel(self->pin, mode); - if (CHANNEL_IDX_TO_MODE(channel_idx) != mode) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM channels:%d"), PWM_CHANNEL_MAX); // in current mode - } - } - self->mode = mode; - self->timer = TIMER_IDX_TO_TIMER(timer_idx); - self->channel = CHANNEL_IDX_TO_CHANNEL(channel_idx); - - // New PWM assignment - if ((chans[channel_idx].pin == -1) || (chans[channel_idx].timer_idx != timer_idx)) { - configure_channel(self); - chans[channel_idx].pin = self->pin; - } - chans[channel_idx].timer_idx = timer_idx; - self->active = true; - - // Set timer frequency - set_freq(self, freq, &timers[timer_idx]); + set_freq_duty(self, freq); +} - // Set duty cycle? - if (duty_u16 != -1) { - set_duty_u16(self, duty_u16); - } else if (duty_ns != -1) { - set_duty_ns(self, duty_ns); - } else if (duty != -1) { - set_duty_u10(self, duty); - } else if (self->duty_x == 0) { - set_duty_u10(self, (1 << PWM_RES_10_BIT) / 2); // 50% - } +static void self_reset(machine_pwm_obj_t *self) { + self->mode = -1; + self->channel = -1; + self->timer = -1; + self->freq = -1; + self->duty_scale = 0; + self->duty_ui = 0; + self->channel_duty = -1; + self->output_invert = false; + self->output_invert_prev = false; + self->lightsleep = false; } // This called from PWM() constructor static mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 2, true); - gpio_num_t pin_id = machine_pin_get_id(args[0]); - - // create PWM object from the given pin - machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type); - self->pin = pin_id; - self->active = false; - self->mode = -1; - self->channel = -1; - self->timer = -1; - self->duty_x = 0; // start the PWM subsystem if it's not already running if (!pwm_inited) { pwm_init(); + #if FADE + ledc_fade_func_install(0); + #endif pwm_inited = true; } - // start the PWM running for this channel + // create PWM object from the given pin + machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type); + self_reset(self); + + self->pin = machine_pin_get_id(args[0]); + + // Process the remaining parameters. mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); mp_machine_pwm_init_helper(self, n_args - 1, args + 1, &kw_args); - return MP_OBJ_FROM_PTR(self); } // This called from pwm.deinit() method static void mp_machine_pwm_deinit(machine_pwm_obj_t *self) { - int channel_idx = CHANNEL_IDX(self->mode, self->channel); - pwm_deinit(channel_idx); - self->active = false; - self->mode = -1; - self->channel = -1; - self->timer = -1; - self->duty_x = 0; + pwm_deinit(self->mode, self->channel, self->output_invert); + self_reset(self); } // Set and get methods of PWM class @@ -608,57 +756,11 @@ static mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) { static void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { pwm_is_active(self); - if ((freq <= 0) || (freq > 40000000)) { - mp_raise_ValueError(MP_ERROR_TEXT("frequency must be from 1Hz to 40MHz")); - } - if (freq == timers[TIMER_IDX(self->mode, self->timer)].freq_hz) { + check_freq_ranges(self, freq, 40000000); + if (freq == timers[self->mode][self->timer].freq) { return; } - - int current_timer_idx = chans[CHANNEL_IDX(self->mode, self->channel)].timer_idx; - bool current_in_use = is_timer_in_use(CHANNEL_IDX(self->mode, self->channel), current_timer_idx); - - // Check if an already running timer with the same freq is running - int new_timer_idx = find_timer(freq, SAME_FREQ_ONLY, self->mode); - - // If no existing timer was found, and the current one is in use, then find a new one - if ((new_timer_idx == -1) && current_in_use) { - // Have to find a new timer - new_timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, self->mode); - - if (new_timer_idx == -1) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM timers:%d"), PWM_TIMER_MAX); // in current mode - } - } - - if ((new_timer_idx != -1) && (new_timer_idx != current_timer_idx)) { - // Bind the channel to the new timer - chans[self->channel].timer_idx = new_timer_idx; - - if (ledc_bind_channel_timer(self->mode, self->channel, TIMER_IDX_TO_TIMER(new_timer_idx)) != ESP_OK) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("failed to bind timer to channel")); - } - - if (!current_in_use) { - // Free the old timer - check_esp_err(ledc_timer_pause(self->mode, self->timer)); - ledc_timer_config_t timer_config = { - .deconfigure = true, - .speed_mode = self->mode, - .timer_num = self->timer, - }; - check_esp_err(ledc_timer_config(&timer_config)); - // Flag it unused - timers[current_timer_idx].freq_hz = -1; - } - - current_timer_idx = new_timer_idx; - } - self->mode = TIMER_IDX_TO_MODE(current_timer_idx); - self->timer = TIMER_IDX_TO_TIMER(current_timer_idx); - - // Set the frequency - set_freq(self, freq, &timers[current_timer_idx]); + set_freq_duty(self, freq); } static mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) { diff --git a/ports/esp32/machine_rtc.c b/ports/esp32/machine_rtc.c index a2f4bb132d..ee6b1ad5cb 100644 --- a/ports/esp32/machine_rtc.c +++ b/ports/esp32/machine_rtc.c @@ -82,8 +82,12 @@ _USER_MEM_ATTR uint8_t rtc_user_mem_data[MICROPY_HW_RTC_USER_MEM_MAX]; static const machine_rtc_obj_t machine_rtc_obj = {{&machine_rtc_type}}; machine_rtc_config_t machine_rtc_config = { + #if SOC_PM_SUPPORT_EXT1_WAKEUP .ext1_pins = 0, + #endif + #if SOC_PM_SUPPORT_EXT0_WAKEUP .ext0_pin = -1 + #endif }; static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { diff --git a/ports/esp32/machine_rtc.h b/ports/esp32/machine_rtc.h index ce2a5482a2..72717c5936 100644 --- a/ports/esp32/machine_rtc.h +++ b/ports/esp32/machine_rtc.h @@ -31,13 +31,25 @@ #include "modmachine.h" typedef struct { + #if SOC_PM_SUPPORT_EXT1_WAKEUP uint64_t ext1_pins; // set bit == pin# + #endif + #if SOC_PM_SUPPORT_EXT0_WAKEUP int8_t ext0_pin; // just the pin#, -1 == None + #endif + #if SOC_TOUCH_SENSOR_SUPPORTED bool wake_on_touch : 1; + #endif + #if SOC_ULP_SUPPORTED bool wake_on_ulp : 1; + #endif + #if SOC_PM_SUPPORT_EXT0_WAKEUP bool ext0_level : 1; wake_type_t ext0_wake_types; + #endif + #if SOC_PM_SUPPORT_EXT1_WAKEUP bool ext1_level : 1; + #endif } machine_rtc_config_t; extern machine_rtc_config_t machine_rtc_config; diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c index 82b432bbac..a104288f6e 100644 --- a/ports/esp32/machine_timer.c +++ b/ports/esp32/machine_timer.c @@ -50,12 +50,13 @@ const mp_obj_type_t machine_timer_type; static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +static mp_obj_t machine_timer_deinit(mp_obj_t self_in); void machine_timer_deinit_all(void) { // Disable, deallocate and remove all timers from list machine_timer_obj_t **t = &MP_STATE_PORT(machine_timer_obj_head); while (*t != NULL) { - machine_timer_disable(*t); + machine_timer_deinit(*t); machine_timer_obj_t *next = (*t)->next; m_del_obj(machine_timer_obj_t, *t); *t = next; @@ -96,6 +97,7 @@ machine_timer_obj_t *machine_timer_create(mp_uint_t timer) { self = mp_obj_malloc(machine_timer_obj_t, &machine_timer_type); self->group = group; self->index = index; + self->handle = NULL; // Add the timer to the linked-list of timers self->next = MP_STATE_PORT(machine_timer_obj_head); @@ -131,9 +133,8 @@ void machine_timer_disable(machine_timer_obj_t *self) { } if (self->handle) { - // Free the interrupt handler. - esp_intr_free(self->handle); - self->handle = NULL; + // Disable the interrupt + ESP_ERROR_CHECK(esp_intr_disable(self->handle)); } // We let the disabled timer stay in the list, as it might be @@ -150,12 +151,16 @@ static void machine_timer_isr(void *self_in) { if (self->repeat) { timer_ll_enable_alarm(self->hal_context.dev, self->index, true); } - mp_sched_schedule(self->callback, self); - mp_hal_wake_main_task_from_isr(); + self->handler(self); } } -void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) { +static void machine_timer_isr_handler(machine_timer_obj_t *self) { + mp_sched_schedule(self->callback, self); + mp_hal_wake_main_task_from_isr(); +} + +void machine_timer_enable(machine_timer_obj_t *self) { // Initialise the timer. timer_hal_init(&self->hal_context, self->group, self->index); timer_ll_enable_counter(self->hal_context.dev, self->index, false); @@ -167,10 +172,17 @@ void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) { // Allocate and enable the alarm interrupt. timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), false); timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index)); - ESP_ERROR_CHECK( - esp_intr_alloc(timer_group_periph_signals.groups[self->group].timer_irq_id[self->index], - TIMER_FLAGS, timer_isr, self, &self->handle) - ); + if (self->handle) { + ESP_ERROR_CHECK(esp_intr_enable(self->handle)); + } else { + ESP_ERROR_CHECK(esp_intr_alloc( + timer_group_periph_signals.groups[self->group].timer_irq_id[self->index], + TIMER_FLAGS, + machine_timer_isr, + self, + &self->handle + )); + } timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), true); // Enable the alarm to trigger at the given period. @@ -224,16 +236,22 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n } self->repeat = args[ARG_mode].u_int; + self->handler = machine_timer_isr_handler; self->callback = args[ARG_callback].u_obj; - self->handle = NULL; - machine_timer_enable(self, machine_timer_isr); + machine_timer_enable(self); return mp_const_none; } static mp_obj_t machine_timer_deinit(mp_obj_t self_in) { - machine_timer_disable(self_in); + machine_timer_obj_t *self = self_in; + + machine_timer_disable(self); + if (self->handle) { + ESP_ERROR_CHECK(esp_intr_free(self->handle)); + self->handle = NULL; + } return mp_const_none; } @@ -246,6 +264,9 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init) static mp_obj_t machine_timer_value(mp_obj_t self_in) { machine_timer_obj_t *self = self_in; + if (self->handle == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("timer not set")); + } uint64_t result = timer_ll_get_counter_value(self->hal_context.dev, self->index); return MP_OBJ_NEW_SMALL_INT((mp_uint_t)(result / (TIMER_SCALE / 1000))); // value in ms } diff --git a/ports/esp32/machine_timer.h b/ports/esp32/machine_timer.h index 914bedd86b..10fe2f39c9 100644 --- a/ports/esp32/machine_timer.h +++ b/ports/esp32/machine_timer.h @@ -55,12 +55,13 @@ typedef struct _machine_timer_obj_t { mp_obj_t callback; intr_handle_t handle; + void (*handler)(struct _machine_timer_obj_t *timer); struct _machine_timer_obj_t *next; } machine_timer_obj_t; machine_timer_obj_t *machine_timer_create(mp_uint_t timer); -void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)); +void machine_timer_enable(machine_timer_obj_t *self); void machine_timer_disable(machine_timer_obj_t *self); #endif // MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index 73089ef463..982d9a7e27 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -59,6 +59,7 @@ #define UART_IRQ_BREAK (1 << UART_BREAK) #define MP_UART_ALLOWED_FLAGS (UART_IRQ_RX | UART_IRQ_RXIDLE | UART_IRQ_BREAK) #define RXIDLE_TIMER_MIN (5000) // 500 us +#define UART_QUEUE_SIZE (3) enum { RXIDLE_INACTIVE, @@ -109,30 +110,19 @@ static const char *_parity_name[] = {"None", "1", "0"}; { MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_IRQ_RXIDLE) }, \ { MP_ROM_QSTR(MP_QSTR_IRQ_BREAK), MP_ROM_INT(UART_IRQ_BREAK) }, \ -static void uart_timer_callback(void *self_in) { - machine_timer_obj_t *self = self_in; - - uint32_t intr_status = timer_ll_get_intr_status(self->hal_context.dev); - - if (intr_status & TIMER_LL_EVENT_ALARM(self->index)) { - timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index)); - if (self->repeat) { - timer_ll_enable_alarm(self->hal_context.dev, self->index, true); - } - } - +static void uart_timer_callback(machine_timer_obj_t *timer) { // The UART object is referred here by the callback field. - machine_uart_obj_t *uart = (machine_uart_obj_t *)self->callback; - if (uart->rxidle_state == RXIDLE_ALERT) { + machine_uart_obj_t *self = (machine_uart_obj_t *)timer->callback; + if (self->rxidle_state == RXIDLE_ALERT) { // At the first call, just switch the state - uart->rxidle_state = RXIDLE_ARMED; - } else if (uart->rxidle_state == RXIDLE_ARMED) { + self->rxidle_state = RXIDLE_ARMED; + } else if (self->rxidle_state == RXIDLE_ARMED) { // At the second call, run the irq callback and stop the timer - uart->rxidle_state = RXIDLE_STANDBY; - uart->mp_irq_flags = UART_IRQ_RXIDLE; - mp_irq_handler(uart->mp_irq_obj); + self->rxidle_state = RXIDLE_STANDBY; + self->mp_irq_flags = UART_IRQ_RXIDLE; + mp_irq_handler(self->mp_irq_obj); mp_hal_wake_main_task_from_isr(); - machine_timer_disable(uart->rxidle_timer); + machine_timer_disable(self->rxidle_timer); } } @@ -149,9 +139,7 @@ static void uart_event_task(void *self_in) { if (self->mp_irq_trigger & UART_IRQ_RXIDLE) { if (self->rxidle_state != RXIDLE_INACTIVE) { if (self->rxidle_state == RXIDLE_STANDBY) { - self->rxidle_timer->repeat = true; - self->rxidle_timer->handle = NULL; - machine_timer_enable(self->rxidle_timer, uart_timer_callback); + machine_timer_enable(self->rxidle_timer); } } self->rxidle_state = RXIDLE_ALERT; @@ -174,6 +162,13 @@ static void uart_event_task(void *self_in) { } } +static inline void uart_event_task_create(machine_uart_obj_t *self) { + if (xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 2048, self, + ESP_TASKD_EVENT_PRIO, (TaskHandle_t *)&self->uart_event_task, MP_TASK_COREID) != pdPASS) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("failed to create UART event task")); + } +} + static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); uint32_t baudrate; @@ -250,7 +245,7 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, // wait for all data to be transmitted before changing settings uart_wait_tx_done(self->uart_num, pdMS_TO_TICKS(1000)); - if (args[ARG_txbuf].u_int >= 0 || args[ARG_rxbuf].u_int >= 0) { + if ((args[ARG_txbuf].u_int >= 0 && args[ARG_txbuf].u_int != self->txbuf) || (args[ARG_rxbuf].u_int >= 0 && args[ARG_rxbuf].u_int != self->rxbuf)) { // must reinitialise driver to change the tx/rx buffer size #if MICROPY_HW_ENABLE_UART_REPL if (self->uart_num == MICROPY_HW_UART_REPL) { @@ -275,9 +270,12 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, check_esp_err(uart_get_word_length(self->uart_num, &uartcfg.data_bits)); check_esp_err(uart_get_parity(self->uart_num, &uartcfg.parity)); check_esp_err(uart_get_stop_bits(self->uart_num, &uartcfg.stop_bits)); - check_esp_err(uart_driver_delete(self->uart_num)); + mp_machine_uart_deinit(self); check_esp_err(uart_param_config(self->uart_num, &uartcfg)); - check_esp_err(uart_driver_install(self->uart_num, self->rxbuf, self->txbuf, 0, NULL, 0)); + check_esp_err(uart_driver_install(self->uart_num, self->rxbuf, self->txbuf, UART_QUEUE_SIZE, &self->uart_queue, 0)); + if (self->mp_irq_obj != NULL && self->mp_irq_obj->handler != mp_const_none) { + uart_event_task_create(self); + } } // set baudrate @@ -437,7 +435,8 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->timeout_char = 0; self->invert = 0; self->flowcontrol = 0; - self->uart_event_task = 0; + self->uart_event_task = NULL; + self->uart_queue = NULL; self->rxidle_state = RXIDLE_INACTIVE; switch (uart_num) { @@ -470,12 +469,13 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg { // Remove any existing configuration check_esp_err(uart_driver_delete(self->uart_num)); + self->uart_queue = NULL; // init the peripheral // Setup check_esp_err(uart_param_config(self->uart_num, &uartcfg)); - check_esp_err(uart_driver_install(uart_num, self->rxbuf, self->txbuf, 3, &self->uart_queue, 0)); + check_esp_err(uart_driver_install(uart_num, self->rxbuf, self->txbuf, UART_QUEUE_SIZE, &self->uart_queue, 0)); } mp_map_t kw_args; @@ -489,7 +489,12 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg } static void mp_machine_uart_deinit(machine_uart_obj_t *self) { + if (self->uart_event_task != NULL) { + vTaskDelete(self->uart_event_task); + self->uart_event_task = NULL; + } check_esp_err(uart_driver_delete(self->uart_num)); + self->uart_queue = NULL; } static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) { @@ -535,11 +540,11 @@ static void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger } self->rxidle_period = period; self->rxidle_timer->period = period; + self->rxidle_timer->handler = uart_timer_callback; // The Python callback is not used. So use this // data field to hold a reference to the UART object. self->rxidle_timer->callback = self; self->rxidle_timer->repeat = true; - self->rxidle_timer->handle = NULL; self->rxidle_state = RXIDLE_STANDBY; } } @@ -568,6 +573,12 @@ static const mp_irq_methods_t uart_irq_methods = { }; static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) { + #if MICROPY_HW_ENABLE_UART_REPL + if (self->uart_num == MICROPY_HW_UART_REPL) { + mp_raise_ValueError(MP_ERROR_TEXT("UART does not support IRQs")); + } + #endif + if (self->mp_irq_obj == NULL) { self->mp_irq_trigger = 0; self->mp_irq_obj = mp_irq_new(&uart_irq_methods, MP_OBJ_FROM_PTR(self)); @@ -597,9 +608,8 @@ static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args uart_irq_configure_timer(self, trigger); // Start a task for handling events - if (handler != mp_const_none && self->uart_event_task == NULL) { - xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 2048, self, - ESP_TASKD_EVENT_PRIO, (TaskHandle_t *)&self->uart_event_task, MP_TASK_COREID); + if (handler != mp_const_none && self->uart_event_task == NULL && self->uart_queue != NULL) { + uart_event_task_create(self); } else if (handler == mp_const_none && self->uart_event_task != NULL) { vTaskDelete(self->uart_event_task); self->uart_event_task = NULL; diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 4f0c27ee07..f048aa85f5 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -38,6 +38,7 @@ #include "nvs_flash.h" #include "esp_task.h" #include "esp_event.h" +#include "esp_flash.h" #include "esp_log.h" #include "esp_memory_utils.h" #include "esp_psram.h" @@ -214,18 +215,45 @@ void boardctrl_startup(void) { nvs_flash_erase(); nvs_flash_init(); } + + // Query the physical size of the SPI flash and store it in the size + // variable of the global, default SPI flash handle. + esp_flash_get_physical_size(NULL, &esp_flash_default_chip->size); + + // If there is no filesystem partition (no "vfs" or "ffat"), add a "vfs" partition + // that extends from the end of the application partition up to the end of flash. + if (esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "vfs") == NULL + && esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "ffat") == NULL) { + // No "vfs" or "ffat" partition, so try to create one. + + // Find the end of the last partition that exists in the partition table. + size_t offset = 0; + esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL); + while (iter != NULL) { + const esp_partition_t *part = esp_partition_get(iter); + offset = MAX(offset, part->address + part->size); + iter = esp_partition_next(iter); + } + + // If we found the application partition and there is some space between the end of + // that and the end of flash, create a "vfs" partition taking up all of that space. + if (offset > 0 && esp_flash_default_chip->size > offset) { + size_t size = esp_flash_default_chip->size - offset; + esp_partition_register_external(esp_flash_default_chip, offset, size, "vfs", ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL); + } + } } -void app_main(void) { +void MICROPY_ESP_IDF_ENTRY(void) { // Hook for a board to run code at start up. - // This defaults to initialising NVS. + // This defaults to initialising NVS and detecting the flash size. MICROPY_BOARD_STARTUP(); // Create and transfer control to the MicroPython task. xTaskCreatePinnedToCore(mp_task, "mp_task", MICROPY_TASK_STACK_SIZE / sizeof(StackType_t), NULL, MP_TASK_PRIORITY, &mp_main_task_handle, MP_TASK_COREID); } -void nlr_jump_fail(void *val) { +MP_WEAK void nlr_jump_fail(void *val) { printf("NLR jump failed, val=%p\n", val); esp_restart(); } diff --git a/ports/esp32/main/idf_component.yml b/ports/esp32/main/idf_component.yml index ccbde1f27e..f7773f4f4e 100644 --- a/ports/esp32/main/idf_component.yml +++ b/ports/esp32/main/idf_component.yml @@ -5,5 +5,10 @@ dependencies: rules: - if: "target in [esp32s2, esp32s3]" version: "~1.0.0" + espressif/lan867x: + version: "~1.0.0" + rules: + - if: "target == esp32" + - if: "idf_version >=5.3" idf: version: ">=5.2.0" diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 164b191826..fcd6ed9fa8 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -30,7 +30,6 @@ #include <time.h> #include <sys/time.h> #include "driver/gpio.h" -#include "driver/adc.h" #include "esp_heap_caps.h" #include "multi_heap.h" @@ -48,22 +47,30 @@ #include "../multi_heap_platform.h" #include "../heap_private.h" +#if SOC_TOUCH_SENSOR_SUPPORTED static mp_obj_t esp32_wake_on_touch(const mp_obj_t wake) { + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin != -1) { mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } + #endif machine_rtc_config.wake_on_touch = mp_obj_is_true(wake); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_1(esp32_wake_on_touch_obj, esp32_wake_on_touch); +#endif +#if SOC_PM_SUPPORT_EXT0_WAKEUP static mp_obj_t esp32_wake_on_ext0(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + #if SOC_TOUCH_SENSOR_SUPPORTED if (machine_rtc_config.wake_on_touch) { mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } + #endif + enum {ARG_pin, ARG_level}; const mp_arg_t allowed_args[] = { { MP_QSTR_pin, MP_ARG_OBJ, {.u_obj = mp_obj_new_int(machine_rtc_config.ext0_pin)} }, @@ -90,7 +97,9 @@ static mp_obj_t esp32_wake_on_ext0(size_t n_args, const mp_obj_t *pos_args, mp_m return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_wake_on_ext0_obj, 0, esp32_wake_on_ext0); +#endif +#if SOC_PM_SUPPORT_EXT1_WAKEUP static mp_obj_t esp32_wake_on_ext1(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum {ARG_pins, ARG_level}; const mp_arg_t allowed_args[] = { @@ -126,15 +135,20 @@ static mp_obj_t esp32_wake_on_ext1(size_t n_args, const mp_obj_t *pos_args, mp_m return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_wake_on_ext1_obj, 0, esp32_wake_on_ext1); +#endif +#if SOC_ULP_SUPPORTED static mp_obj_t esp32_wake_on_ulp(const mp_obj_t wake) { + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin != -1) { mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } + #endif machine_rtc_config.wake_on_ulp = mp_obj_is_true(wake); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_1(esp32_wake_on_ulp_obj, esp32_wake_on_ulp); +#endif #if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP static mp_obj_t esp32_gpio_deep_sleep_hold(const mp_obj_t enable) { @@ -212,13 +226,64 @@ static mp_obj_t esp32_idf_heap_info(const mp_obj_t cap_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(esp32_idf_heap_info_obj, esp32_idf_heap_info); +#if CONFIG_FREERTOS_USE_TRACE_FACILITY +static mp_obj_t esp32_idf_task_info(void) { + const size_t task_count_max = uxTaskGetNumberOfTasks(); + TaskStatus_t *task_array = m_new(TaskStatus_t, task_count_max); + uint32_t total_time; + const size_t task_count = uxTaskGetSystemState(task_array, task_count_max, &total_time); + + mp_obj_list_t *task_list = MP_OBJ_TO_PTR(mp_obj_new_list(task_count, NULL)); + for (size_t i = 0; i < task_count; i++) { + mp_obj_t task_data[] = { + mp_obj_new_int_from_uint((mp_uint_t)task_array[i].xHandle), + mp_obj_new_str(task_array[i].pcTaskName, strlen(task_array[i].pcTaskName)), + MP_OBJ_NEW_SMALL_INT(task_array[i].eCurrentState), + MP_OBJ_NEW_SMALL_INT(task_array[i].uxCurrentPriority), + #if CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS + mp_obj_new_int_from_uint(task_array[i].ulRunTimeCounter), + #else + mp_const_none, + #endif + mp_obj_new_int_from_uint(task_array[i].usStackHighWaterMark), + #if CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID + MP_OBJ_NEW_SMALL_INT(task_array[i].xCoreID), + #else + mp_const_none, + #endif + }; + task_list->items[i] = mp_obj_new_tuple(7, task_data); + } + + m_del(TaskStatus_t, task_array, task_count_max); + mp_obj_t task_stats[] = { + #if CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS + MP_OBJ_NEW_SMALL_INT(total_time), + #else + mp_const_none, + #endif + task_list + }; + return mp_obj_new_tuple(2, task_stats); +} +static MP_DEFINE_CONST_FUN_OBJ_0(esp32_idf_task_info_obj, esp32_idf_task_info); +#endif + static const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_esp32) }, + #if SOC_TOUCH_SENSOR_SUPPORTED { MP_ROM_QSTR(MP_QSTR_wake_on_touch), MP_ROM_PTR(&esp32_wake_on_touch_obj) }, + #endif + #if SOC_PM_SUPPORT_EXT0_WAKEUP { MP_ROM_QSTR(MP_QSTR_wake_on_ext0), MP_ROM_PTR(&esp32_wake_on_ext0_obj) }, + #endif + #if SOC_PM_SUPPORT_EXT1_WAKEUP { MP_ROM_QSTR(MP_QSTR_wake_on_ext1), MP_ROM_PTR(&esp32_wake_on_ext1_obj) }, + #endif + #if SOC_ULP_SUPPORTED { MP_ROM_QSTR(MP_QSTR_wake_on_ulp), MP_ROM_PTR(&esp32_wake_on_ulp_obj) }, + #endif #if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP { MP_ROM_QSTR(MP_QSTR_gpio_deep_sleep_hold), MP_ROM_PTR(&esp32_gpio_deep_sleep_hold_obj) }, #endif @@ -228,6 +293,9 @@ static const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_mcu_temperature), MP_ROM_PTR(&esp32_mcu_temperature_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_idf_heap_info), MP_ROM_PTR(&esp32_idf_heap_info_obj) }, + #if CONFIG_FREERTOS_USE_TRACE_FACILITY + { MP_ROM_QSTR(MP_QSTR_idf_task_info), MP_ROM_PTR(&esp32_idf_task_info_obj) }, + #endif { MP_ROM_QSTR(MP_QSTR_NVS), MP_ROM_PTR(&esp32_nvs_type) }, { MP_ROM_QSTR(MP_QSTR_Partition), MP_ROM_PTR(&esp32_partition_type) }, diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 0c1b94d02d..0d7ea44c66 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -178,7 +178,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { machine_sleep_helper(MACHINE_WAKE_SLEEP, n_args, args); }; -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { machine_sleep_helper(MACHINE_WAKE_DEEPSLEEP, n_args, args); mp_machine_reset(); }; @@ -221,7 +221,7 @@ static mp_int_t mp_machine_reset_cause(void) { #include "esp32s3/rom/usb/chip_usb_dw_wrapper.h" #endif -NORETURN static void machine_bootloader_rtc(void) { +MP_NORETURN static void machine_bootloader_rtc(void) { #if CONFIG_IDF_TARGET_ESP32S3 && MICROPY_HW_USB_CDC usb_usj_mode(); usb_dc_prepare_persist(); @@ -233,7 +233,7 @@ NORETURN static void machine_bootloader_rtc(void) { #endif #ifdef MICROPY_BOARD_ENTER_BOOTLOADER -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args); for (;;) { } @@ -254,7 +254,7 @@ static mp_obj_t machine_wake_reason(size_t n_args, const mp_obj_t *pos_args, mp_ } static MP_DEFINE_CONST_FUN_OBJ_KW(machine_wake_reason_obj, 0, machine_wake_reason); -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { esp_restart(); } diff --git a/ports/esp32/modnetwork.h b/ports/esp32/modnetwork.h index 387f961976..ba69d5cc0f 100644 --- a/ports/esp32/modnetwork.h +++ b/ports/esp32/modnetwork.h @@ -28,7 +28,32 @@ #include "esp_netif.h" -enum { PHY_LAN8710, PHY_LAN8720, PHY_IP101, PHY_RTL8201, PHY_DP83848, PHY_KSZ8041, PHY_KSZ8081, PHY_KSZ8851SNL = 100, PHY_DM9051, PHY_W5500 }; +// lan867x component requires newer IDF version +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && CONFIG_IDF_TARGET_ESP32 +#define PHY_LAN867X_ENABLED (1) +#else +#define PHY_LAN867X_ENABLED (0) +#endif + +// PHY_GENERIC support requires newer IDF version +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) && CONFIG_IDF_TARGET_ESP32 +#define PHY_GENERIC_ENABLED (1) +#else +#define PHY_GENERIC_ENABLED (0) +#endif + +enum { + // PHYs supported by the internal Ethernet MAC: + PHY_LAN8710, PHY_LAN8720, PHY_IP101, PHY_RTL8201, PHY_DP83848, PHY_KSZ8041, PHY_KSZ8081, + #if PHY_LAN867X_ENABLED + PHY_LAN8670, + #endif + #if PHY_GENERIC_ENABLED + PHY_GENERIC, + #endif + // PHYs which are actually SPI Ethernet MAC+PHY chips: + PHY_KSZ8851SNL = 100, PHY_DM9051, PHY_W5500 +}; #define IS_SPI_PHY(NUM) (NUM >= 100) enum { ETH_INITIALIZED, ETH_STARTED, ETH_STOPPED, ETH_CONNECTED, ETH_DISCONNECTED, ETH_GOT_IP }; @@ -52,7 +77,7 @@ extern const mp_obj_type_t esp_network_wlan_type; MP_DECLARE_CONST_FUN_OBJ_0(esp_network_initialize_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_get_wlan_obj); MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_get_lan_obj); -MP_DECLARE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj); +extern const struct _mp_obj_type_t esp_network_ppp_lwip_type; MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_ifconfig_obj); MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_ipconfig_obj); MP_DECLARE_CONST_FUN_OBJ_KW(esp_nic_ipconfig_obj); @@ -61,7 +86,7 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_phy_mode_obj); mp_obj_t esp_ifname(esp_netif_t *netif); -NORETURN void esp_exceptions_helper(esp_err_t e); +MP_NORETURN void esp_exceptions_helper(esp_err_t e); static inline void esp_exceptions(esp_err_t e) { if (e != ESP_OK) { diff --git a/ports/esp32/modnetwork_globals.h b/ports/esp32/modnetwork_globals.h index 1aad785ee3..12252ddbc5 100644 --- a/ports/esp32/modnetwork_globals.h +++ b/ports/esp32/modnetwork_globals.h @@ -8,7 +8,7 @@ { MP_ROM_QSTR(MP_QSTR_LAN), MP_ROM_PTR(&esp_network_get_lan_obj) }, #endif #if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT) -{ MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&esp_network_ppp_make_new_obj) }, +{ MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&esp_network_ppp_lwip_type) }, #endif { MP_ROM_QSTR(MP_QSTR_phy_mode), MP_ROM_PTR(&esp_network_phy_mode_obj) }, { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_network_ipconfig_obj) }, @@ -47,6 +47,12 @@ { MP_ROM_QSTR(MP_QSTR_PHY_DP83848), MP_ROM_INT(PHY_DP83848) }, { MP_ROM_QSTR(MP_QSTR_PHY_KSZ8041), MP_ROM_INT(PHY_KSZ8041) }, { MP_ROM_QSTR(MP_QSTR_PHY_KSZ8081), MP_ROM_INT(PHY_KSZ8081) }, +#if PHY_LAN867X_ENABLED +{ MP_ROM_QSTR(MP_QSTR_PHY_LAN8670), MP_ROM_INT(PHY_LAN8670) }, +#endif +#if PHY_GENERIC_ENABLED +{ MP_ROM_QSTR(MP_QSTR_PHY_GENERIC), MP_ROM_INT(PHY_GENERIC) }, +#endif #if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL { MP_ROM_QSTR(MP_QSTR_PHY_KSZ8851SNL), MP_ROM_INT(PHY_KSZ8851SNL) }, diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c index 7ea5e855d3..2050d1d04d 100644 --- a/ports/esp32/modsocket.c +++ b/ports/esp32/modsocket.c @@ -611,7 +611,7 @@ static MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); // XXX this can end up waiting a very long time if the content is dribbled in one character // at a time, as the timeout resets each time a recvfrom succeeds ... this is probably not // good behaviour. -static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, +static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, mp_int_t flags, struct sockaddr *from, socklen_t *from_len, int *errcode) { socket_obj_t *sock = MP_OBJ_TO_PTR(self_in); @@ -645,7 +645,7 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, if (release_gil) { MP_THREAD_GIL_EXIT(); } - int r = lwip_recvfrom(sock->fd, buf, size, 0, from, from_len); + int r = lwip_recvfrom(sock->fd, buf, size, flags, from, from_len); if (release_gil) { MP_THREAD_GIL_ENTER(); } @@ -655,7 +655,7 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, if (r >= 0) { return r; } - if (errno != EWOULDBLOCK) { + if (errno != EWOULDBLOCK || (flags & MSG_DONTWAIT)) { *errcode = errno; return MP_STREAM_ERROR; } @@ -666,14 +666,17 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, return MP_STREAM_ERROR; } -mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, +mp_obj_t _socket_recvfrom(size_t n_args, const mp_obj_t *args, struct sockaddr *from, socklen_t *from_len) { - size_t len = mp_obj_get_int(len_in); + mp_obj_t self_in = args[0]; + size_t len = mp_obj_get_int(args[1]); + int flags = n_args > 2 ? mp_obj_get_int(args[2]) : 0; + vstr_t vstr; vstr_init_len(&vstr, len); int errcode; - mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, from, from_len, &errcode); + mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, flags, from, from_len, &errcode); if (ret == MP_STREAM_ERROR) { mp_raise_OSError(errcode); } @@ -682,17 +685,17 @@ mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, return mp_obj_new_bytes_from_vstr(&vstr); } -static mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { - return _socket_recvfrom(self_in, len_in, NULL, NULL); +static mp_obj_t socket_recv(size_t n_args, const mp_obj_t *args) { + return _socket_recvfrom(n_args, args, NULL, NULL); } -static MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recv_obj, 2, 3, socket_recv); -static mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { +static mp_obj_t socket_recvfrom(size_t n_args, const mp_obj_t *args) { struct sockaddr from; socklen_t fromlen = sizeof(from); mp_obj_t tuple[2]; - tuple[0] = _socket_recvfrom(self_in, len_in, &from, &fromlen); + tuple[0] = _socket_recvfrom(n_args, args, &from, &fromlen); uint8_t *ip = (uint8_t *)&((struct sockaddr_in *)&from)->sin_addr; mp_uint_t port = lwip_ntohs(((struct sockaddr_in *)&from)->sin_port); @@ -700,7 +703,7 @@ static mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { return mp_obj_new_tuple(2, tuple); } -static MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recvfrom_obj, 2, 3, socket_recvfrom); int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) { int sentlen = 0; @@ -789,7 +792,7 @@ static mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) { static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, socket_makefile); static mp_uint_t socket_stream_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { - return _socket_read_data(self_in, buf, size, NULL, NULL, errcode); + return _socket_read_data(self_in, buf, size, 0, NULL, NULL, errcode); } static mp_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { @@ -1010,6 +1013,8 @@ static const mp_rom_map_elem_t mp_module_socket_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SO_BINDTODEVICE), MP_ROM_INT(SO_BINDTODEVICE) }, { MP_ROM_QSTR(MP_QSTR_IP_ADD_MEMBERSHIP), MP_ROM_INT(IP_ADD_MEMBERSHIP) }, { MP_ROM_QSTR(MP_QSTR_TCP_NODELAY), MP_ROM_INT(TCP_NODELAY) }, + { MP_ROM_QSTR(MP_QSTR_MSG_PEEK), MP_ROM_INT(MSG_PEEK) }, + { MP_ROM_QSTR(MP_QSTR_MSG_DONTWAIT), MP_ROM_INT(MSG_DONTWAIT) }, }; static MP_DEFINE_CONST_DICT(mp_module_socket_globals, mp_module_socket_globals_table); diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index b5b7d63a56..a6f103cdef 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -62,7 +62,8 @@ #define MICROPY_STACK_CHECK_MARGIN (1024) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) -#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) // Debugging Note: Increase the error reporting level to view + // __FUNCTION__, __LINE__, __FILE__ in check_esp_err() exceptions #define MICROPY_WARNINGS (1) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #define MICROPY_STREAMS_POSIX_API (1) @@ -123,6 +124,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) @@ -391,3 +393,8 @@ void boardctrl_startup(void); #ifndef MICROPY_PY_STRING_TX_GIL_THRESHOLD #define MICROPY_PY_STRING_TX_GIL_THRESHOLD (20) #endif + +// Code can override this to provide a custom ESP-IDF entry point. +#ifndef MICROPY_ESP_IDF_ENTRY +#define MICROPY_ESP_IDF_ENTRY app_main +#endif diff --git a/ports/esp32/mpnimbleport.c b/ports/esp32/mpnimbleport.c index ce4b77727a..77185883fd 100644 --- a/ports/esp32/mpnimbleport.c +++ b/ports/esp32/mpnimbleport.c @@ -58,7 +58,7 @@ void mp_bluetooth_nimble_port_start(void) { nimble_port_freertos_init(ble_host_task); } -void mp_bluetooth_nimble_port_shutdown(void) { +int mp_bluetooth_nimble_port_shutdown(void) { DEBUG_printf("mp_bluetooth_nimble_port_shutdown\n"); #if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK @@ -79,6 +79,8 @@ void mp_bluetooth_nimble_port_shutdown(void) { // Mark stack as shutdown. mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; + + return 0; } #endif diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c index 962b5780d0..67917e33e5 100644 --- a/ports/esp32/mpthreadport.c +++ b/ports/esp32/mpthreadport.c @@ -41,10 +41,16 @@ #define MP_THREAD_DEFAULT_STACK_SIZE (MP_THREAD_MIN_STACK_SIZE + MICROPY_STACK_CHECK_MARGIN) #define MP_THREAD_PRIORITY (ESP_TASK_PRIO_MIN + 1) +typedef enum { + MP_THREAD_RUN_STATE_NEW, + MP_THREAD_RUN_STATE_RUNNING, + MP_THREAD_RUN_STATE_FINISHED, +} mp_thread_run_state_t; + // this structure forms a linked list, one node per active thread typedef struct _mp_thread_t { TaskHandle_t id; // system id of thread - int ready; // whether the thread is ready and running + mp_thread_run_state_t run_state; // current run state of the thread void *arg; // thread Python args, a GC root pointer void *stack; // pointer to the stack size_t stack_len; // number of words in the stack @@ -60,18 +66,16 @@ void mp_thread_init(void *stack, uint32_t stack_len) { mp_thread_set_state(&mp_state_ctx.thread); // create the first entry in the linked list of all threads thread_entry0.id = xTaskGetCurrentTaskHandle(); - thread_entry0.ready = 1; + thread_entry0.run_state = MP_THREAD_RUN_STATE_RUNNING; thread_entry0.arg = NULL; thread_entry0.stack = stack; thread_entry0.stack_len = stack_len; thread_entry0.next = NULL; + thread = &thread_entry0; mp_thread_mutex_init(&thread_mutex); // memory barrier to ensure above data is committed __sync_synchronize(); - - // vTaskPreDeletionHook needs the thread ready after thread_mutex is ready - thread = &thread_entry0; } void mp_thread_gc_others(void) { @@ -82,7 +86,7 @@ void mp_thread_gc_others(void) { if (th->id == xTaskGetCurrentTaskHandle()) { continue; } - if (!th->ready) { + if (th->run_state != MP_THREAD_RUN_STATE_RUNNING) { continue; } gc_collect_root(th->stack, th->stack_len); @@ -106,7 +110,7 @@ void mp_thread_start(void) { mp_thread_mutex_lock(&thread_mutex, 1); for (mp_thread_t *th = thread; th != NULL; th = th->next) { if (th->id == xTaskGetCurrentTaskHandle()) { - th->ready = 1; + th->run_state = MP_THREAD_RUN_STATE_RUNNING; break; } } @@ -116,12 +120,22 @@ void mp_thread_start(void) { static void *(*ext_thread_entry)(void *) = NULL; static void freertos_entry(void *arg) { + // Run the Python code. if (ext_thread_entry) { ext_thread_entry(arg); } - vTaskDelete(NULL); - for (;;) {; + + // Remove the thread from the linked-list of active threads. + mp_thread_mutex_lock(&thread_mutex, 1); + for (mp_thread_t **th = &thread; *th != NULL; th = &(*th)->next) { + if ((*th)->id == xTaskGetCurrentTaskHandle()) { + *th = (*th)->next; + } } + mp_thread_mutex_unlock(&thread_mutex); + + // Delete this FreeRTOS task (this call to vTaskDelete will not return). + vTaskDelete(NULL); } mp_uint_t mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_size, int priority, char *name) { @@ -147,7 +161,7 @@ mp_uint_t mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_s } // add thread to linked list of all threads - th->ready = 0; + th->run_state = MP_THREAD_RUN_STATE_NEW; th->arg = arg; th->stack = pxTaskGetStackStart(th->id); th->stack_len = *stack_size / sizeof(uintptr_t); @@ -167,33 +181,7 @@ void mp_thread_finish(void) { mp_thread_mutex_lock(&thread_mutex, 1); for (mp_thread_t *th = thread; th != NULL; th = th->next) { if (th->id == xTaskGetCurrentTaskHandle()) { - th->ready = 0; - break; - } - } - mp_thread_mutex_unlock(&thread_mutex); -} - -// This is called either from vTaskDelete() or from the FreeRTOS idle task, so -// may not be within Python context. Therefore MP_STATE_THREAD may not be valid -// and it does not have the GIL. -void vTaskPreDeletionHook(void *tcb) { - if (thread == NULL) { - // threading not yet initialised - return; - } - mp_thread_t *prev = NULL; - mp_thread_mutex_lock(&thread_mutex, 1); - for (mp_thread_t *th = thread; th != NULL; prev = th, th = th->next) { - // unlink the node from the list - if ((void *)th->id == tcb) { - if (prev != NULL) { - prev->next = th->next; - } else { - // move the start pointer - thread = th->next; - } - // The "th" memory will eventually be reclaimed by the GC. + th->run_state = MP_THREAD_RUN_STATE_FINISHED; break; } } @@ -221,32 +209,22 @@ void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) { } void mp_thread_deinit(void) { - for (;;) { - // Find a task to delete - TaskHandle_t id = NULL; - mp_thread_mutex_lock(&thread_mutex, 1); - for (mp_thread_t *th = thread; th != NULL; th = th->next) { - // Don't delete the current task - if (th->id != xTaskGetCurrentTaskHandle()) { - id = th->id; - break; - } - } - mp_thread_mutex_unlock(&thread_mutex); + // The current task should be thread_entry0 and should be the last in the linked list. + assert(thread_entry0.id == xTaskGetCurrentTaskHandle()); + assert(thread_entry0.next == NULL); - if (id == NULL) { - // No tasks left to delete - break; - } else { - // Call FreeRTOS to delete the task (it will call vTaskPreDeletionHook) - vTaskDelete(id); + // Delete all tasks except the main one. + mp_thread_mutex_lock(&thread_mutex, 1); + for (mp_thread_t *th = thread; th != NULL; th = th->next) { + if (th != &thread_entry0) { + vTaskDelete(th->id); } } -} - -#else + thread = &thread_entry0; + mp_thread_mutex_unlock(&thread_mutex); -void vTaskPreDeletionHook(void *tcb) { + // Give the idle task a chance to run, to clean up any deleted tasks. + vTaskDelay(1); } #endif // MICROPY_PY_THREAD diff --git a/ports/esp32/network_common.c b/ports/esp32/network_common.c index fce8a0304c..bd34f1b41f 100644 --- a/ports/esp32/network_common.c +++ b/ports/esp32/network_common.c @@ -45,7 +45,7 @@ #include "lwip/sockets.h" #include "lwip/dns.h" -NORETURN void esp_exceptions_helper(esp_err_t e) { +MP_NORETURN void esp_exceptions_helper(esp_err_t e) { switch (e) { case ESP_ERR_WIFI_NOT_INIT: mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Not Initialized")); @@ -77,6 +77,8 @@ NORETURN void esp_exceptions_helper(esp_err_t e) { mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Would Block")); case ESP_ERR_WIFI_NOT_CONNECT: mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Not Connected")); + case ESP_ERR_NO_MEM: + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("WiFi Out of Memory")); default: mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("Wifi Unknown Error 0x%04x"), e); } diff --git a/ports/esp32/network_lan.c b/ports/esp32/network_lan.c index e1a8c95785..309ee0b14a 100644 --- a/ports/esp32/network_lan.c +++ b/ports/esp32/network_lan.c @@ -45,6 +45,10 @@ #include "modnetwork.h" #include "extmod/modnetwork.h" +#if PHY_LAN867X_ENABLED +#include "esp_eth_phy_lan867x.h" +#endif + typedef struct _lan_if_obj_t { base_if_obj_t base; bool initialized; @@ -156,6 +160,12 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar args[ARG_phy_type].u_int != PHY_RTL8201 && args[ARG_phy_type].u_int != PHY_KSZ8041 && args[ARG_phy_type].u_int != PHY_KSZ8081 && + #if PHY_LAN867X_ENABLED + args[ARG_phy_type].u_int != PHY_LAN8670 && + #endif + #if PHY_GENERIC_ENABLED + args[ARG_phy_type].u_int != PHY_GENERIC && + #endif #if CONFIG_ETH_USE_SPI_ETHERNET #if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL args[ARG_phy_type].u_int != PHY_KSZ8851SNL && @@ -231,7 +241,17 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar case PHY_KSZ8081: self->phy = esp_eth_phy_new_ksz80xx(&phy_config); break; + #if PHY_LAN867X_ENABLED + case PHY_LAN8670: + self->phy = esp_eth_phy_new_lan867x(&phy_config); + break; + #endif + #if PHY_GENERIC_ENABLED + case PHY_GENERIC: + self->phy = esp_eth_phy_new_generic(&phy_config); + break; #endif + #endif // CONFIG_IDF_TARGET_ESP32 #if CONFIG_ETH_USE_SPI_ETHERNET #if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL case PHY_KSZ8851SNL: { diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index 4dd5a3718c..8b700c98ef 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2018 "Eric Poulsen" <eric@zyxod.com> + * Copyright (c) 2024 Damien P. George * * Based on the ESP IDF example code which is Public Domain / CC0 * @@ -26,172 +27,265 @@ * THE SOFTWARE. */ +// This file is intended to closely match extmod/network_ppp_lwip.c. Changes can +// and should probably be applied to both files. Compare them directly by using: +// git diff --no-index extmod/network_ppp_lwip.c ports/esp32/network_ppp.c + #include "py/runtime.h" #include "py/mphal.h" -#include "py/objtype.h" #include "py/stream.h" -#include "shared/netutils/netutils.h" -#include "modmachine.h" -#include "ppp_set_auth.h" -#include "netif/ppp/ppp.h" -#include "netif/ppp/pppos.h" -#include "lwip/err.h" -#include "lwip/sockets.h" -#include "lwip/sys.h" -#include "lwip/netdb.h" +#if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT) + #include "lwip/dns.h" +#include "netif/ppp/ppp.h" #include "netif/ppp/pppapi.h" +#include "netif/ppp/pppos.h" -#if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT) +// Includes for port-specific changes compared to network_ppp_lwip.c +#include "shared/netutils/netutils.h" +#include "ppp_set_auth.h" -#define PPP_CLOSE_TIMEOUT_MS (4000) +// Enable this to see the serial data going between the PPP layer. +#define PPP_TRACE_IN_OUT (0) -typedef struct _ppp_if_obj_t { +typedef enum { + STATE_INACTIVE, + STATE_ACTIVE, + STATE_ERROR, + STATE_CONNECTING, + STATE_CONNECTED, +} network_ppp_state_t; + +typedef struct _network_ppp_obj_t { mp_obj_base_t base; - bool active; - bool connected; - volatile bool clean_close; - ppp_pcb *pcb; + network_ppp_state_t state; + int error_code; mp_obj_t stream; - SemaphoreHandle_t inactiveWaitSem; - volatile TaskHandle_t client_task_handle; - struct netif pppif; -} ppp_if_obj_t; + ppp_pcb *pcb; + struct netif netif; +} network_ppp_obj_t; -const mp_obj_type_t ppp_if_type; +const mp_obj_type_t esp_network_ppp_lwip_type; + +static mp_obj_t network_ppp___del__(mp_obj_t self_in); + +static void network_ppp_stream_uart_irq_disable(network_ppp_obj_t *self) { + if (self->stream == mp_const_none) { + return; + } -static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { - ppp_if_obj_t *self = ctx; - struct netif *pppif = ppp_netif(self->pcb); + // Disable UART IRQ. + mp_obj_t dest[3]; + mp_load_method(self->stream, MP_QSTR_irq, dest); + dest[2] = mp_const_none; + mp_call_method_n_kw(1, 0, dest); +} +static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { + network_ppp_obj_t *self = ctx; switch (err_code) { case PPPERR_NONE: - #if CONFIG_LWIP_IPV6 - self->connected = (pppif->ip_addr.u_addr.ip4.addr != 0); - #else - self->connected = (pppif->ip_addr.addr != 0); - #endif // CONFIG_LWIP_IPV6 + self->state = STATE_CONNECTED; break; case PPPERR_USER: - self->clean_close = true; - break; - case PPPERR_CONNECT: - self->connected = false; + if (self->state >= STATE_ERROR) { + // Indicate that we are no longer connected and thus + // only need to free the PPP PCB, not close it. + self->state = STATE_ACTIVE; + } + // Clean up the PPP PCB. + network_ppp___del__(MP_OBJ_FROM_PTR(self)); break; default: + self->state = STATE_ERROR; + self->error_code = err_code; break; } } -static mp_obj_t ppp_make_new(mp_obj_t stream) { +static mp_obj_t network_ppp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + mp_obj_t stream = all_args[0]; + if (stream != mp_const_none) { mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); } - ppp_if_obj_t *self = mp_obj_malloc_with_finaliser(ppp_if_obj_t, &ppp_if_type); + network_ppp_obj_t *self = mp_obj_malloc_with_finaliser(network_ppp_obj_t, type); + self->state = STATE_INACTIVE; self->stream = stream; - self->active = false; - self->connected = false; - self->clean_close = false; - self->client_task_handle = NULL; + self->pcb = NULL; return MP_OBJ_FROM_PTR(self); } -MP_DEFINE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj, ppp_make_new); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) -static u32_t ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t len, void *ctx) -#else -static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) -#endif -{ - ppp_if_obj_t *self = ctx; - - mp_obj_t stream = self->stream; - if (stream == mp_const_none) { - return 0; +static mp_obj_t network_ppp___del__(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->state >= STATE_ACTIVE) { + if (self->state >= STATE_ERROR) { + // Still connected over the stream. + // Force the connection to close, with nocarrier=1. + self->state = STATE_INACTIVE; + pppapi_close(self->pcb, 1); + } + network_ppp_stream_uart_irq_disable(self); + // Free PPP PCB and reset state. + self->state = STATE_INACTIVE; + pppapi_free(self->pcb); + self->pcb = NULL; } - - int err; - return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE); + return mp_const_none; } +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp___del___obj, network_ppp___del__); -static void pppos_client_task(void *self_in) { - ppp_if_obj_t *self = (ppp_if_obj_t *)self_in; - uint8_t buf[256]; +static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); - int len = 0; - while (ulTaskNotifyTake(pdTRUE, len <= 0) == 0) { - mp_obj_t stream = self->stream; - if (stream == mp_const_none) { - len = 0; - } else { - int err; - len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0); - if (len > 0) { - pppos_input_tcpip(self->pcb, (u8_t *)buf, len); - } - } + if (self->state <= STATE_ERROR) { + return MP_OBJ_NEW_SMALL_INT(-MP_EPERM); } - self->client_task_handle = NULL; - vTaskDelete(NULL); - for (;;) { + mp_int_t total_len = 0; + mp_obj_t stream = self->stream; + while (stream != mp_const_none) { + uint8_t buf[256]; + int err; + mp_uint_t len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0); + if (len == 0) { + break; + } + #if PPP_TRACE_IN_OUT + mp_printf(&mp_plat_print, "ppp_in(n=%u,data=", len); + for (size_t i = 0; i < len; ++i) { + mp_printf(&mp_plat_print, "%02x:", buf[i]); + } + mp_printf(&mp_plat_print, ")\n"); + #endif + pppos_input(self->pcb, (u8_t *)buf, len); + total_len += len; } + + return MP_OBJ_NEW_SMALL_INT(total_len); } +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_poll_obj, 1, 2, network_ppp_poll); -static mp_obj_t ppp_active(size_t n_args, const mp_obj_t *args) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); +static void network_ppp_stream_uart_irq_enable(network_ppp_obj_t *self) { + if (self->stream == mp_const_none) { + return; + } - if (n_args > 1) { - if (mp_obj_is_true(args[1])) { - if (self->active) { - return mp_const_true; - } + // Enable UART IRQ to call PPP.poll() when incoming data is ready. + mp_obj_t dest[4]; + mp_load_method(self->stream, MP_QSTR_irq, dest); + dest[2] = mp_obj_new_bound_meth(MP_OBJ_FROM_PTR(&network_ppp_poll_obj), MP_OBJ_FROM_PTR(self)); + dest[3] = mp_load_attr(self->stream, MP_QSTR_IRQ_RXIDLE); + mp_call_method_n_kw(2, 0, dest); +} - self->pcb = pppapi_pppos_create(&self->pppif, ppp_output_callback, ppp_status_cb, self); +static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + if (n_args != 1 && kwargs->used != 0) { + mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed")); + } + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); - if (self->pcb == NULL) { - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("init failed")); - } - self->active = true; - } else { - if (!self->active) { - return mp_const_false; + if (kwargs->used != 0) { + for (size_t i = 0; i < kwargs->alloc; i++) { + if (mp_map_slot_is_filled(kwargs, i)) { + switch (mp_obj_str_get_qstr(kwargs->table[i].key)) { + case MP_QSTR_stream: { + if (kwargs->table[i].value != mp_const_none) { + mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + } + if (self->state >= STATE_ACTIVE) { + network_ppp_stream_uart_irq_disable(self); + } + self->stream = kwargs->table[i].value; + if (self->state >= STATE_ACTIVE) { + network_ppp_stream_uart_irq_enable(self); + } + break; + } + default: + break; + } } + } + return mp_const_none; + } - if (self->client_task_handle != NULL) { // is connecting or connected? - // Wait for PPPERR_USER, with timeout - pppapi_close(self->pcb, 0); - uint32_t t0 = mp_hal_ticks_ms(); - while (!self->clean_close && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) { - mp_hal_delay_ms(10); - } + if (n_args != 2) { + mp_raise_TypeError(MP_ERROR_TEXT("can query only one param")); + } - // Shutdown task - xTaskNotifyGive(self->client_task_handle); - t0 = mp_hal_ticks_ms(); - while (self->client_task_handle != NULL && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) { - mp_hal_delay_ms(10); + mp_obj_t val = mp_const_none; + + switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_stream: { + val = self->stream; + break; + } + case MP_QSTR_ifname: { + if (self->pcb != NULL) { + struct netif *pppif = ppp_netif(self->pcb); + char ifname[NETIF_NAMESIZE + 1] = {0}; + netif_index_to_name(netif_get_index(pppif), ifname); + if (ifname[0] != 0) { + val = mp_obj_new_str_from_cstr((char *)ifname); } } - - // Release PPP - pppapi_free(self->pcb); - self->pcb = NULL; - self->active = false; - self->connected = false; - self->clean_close = false; + break; } + default: + mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); + } + + return val; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_config_obj, 1, network_ppp_config); + +static mp_obj_t network_ppp_status(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->state == STATE_ERROR) { + return MP_OBJ_NEW_SMALL_INT(-self->error_code); + } else { + return MP_OBJ_NEW_SMALL_INT(self->state); + } +} +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_status_obj, network_ppp_status); + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +static u32_t network_ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t len, void *ctx) +#else +static u32_t network_ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) +#endif +{ + network_ppp_obj_t *self = ctx; + #if PPP_TRACE_IN_OUT + mp_printf(&mp_plat_print, "ppp_out(n=%u,data=", len); + for (size_t i = 0; i < len; ++i) { + mp_printf(&mp_plat_print, "%02x:", ((const uint8_t *)data)[i]); + } + mp_printf(&mp_plat_print, ")\n"); + #endif + mp_obj_t stream = self->stream; + if (stream == mp_const_none) { + return 0; } - return mp_obj_new_bool(self->active); + int err; + // The return value from this output callback is the number of bytes written out. + // If it's less than the requested number of bytes then lwIP will propagate out an error. + return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE); } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_active_obj, 1, 2, ppp_active); -static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { - enum { ARG_authmode, ARG_username, ARG_password }; +static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + enum { ARG_security, ARG_user, ARG_key, ARG_authmode, ARG_username, ARG_password }; static const mp_arg_t allowed_args[] = { + { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_user, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + // Deprecated arguments for backwards compatibility { MP_QSTR_authmode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PPPAUTHTYPE_NONE} }, { MP_QSTR_username, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_password, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, @@ -200,17 +294,34 @@ static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw mp_arg_val_t parsed_args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, parsed_args); - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + // Use deprecated arguments as defaults + if (parsed_args[ARG_security].u_int == -1) { + parsed_args[ARG_security].u_int = parsed_args[ARG_authmode].u_int; + } + if (parsed_args[ARG_user].u_obj == mp_const_none) { + parsed_args[ARG_user].u_obj = parsed_args[ARG_username].u_obj; + } + if (parsed_args[ARG_key].u_obj == mp_const_none) { + parsed_args[ARG_key].u_obj = parsed_args[ARG_password].u_obj; + } + + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if (self->state == STATE_INACTIVE) { + self->pcb = pppapi_pppos_create(&self->netif, network_ppp_output_callback, network_ppp_status_cb, self); + if (self->pcb == NULL) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("pppos_create failed")); + } + self->state = STATE_ACTIVE; - if (!self->active) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("must be active")); + network_ppp_stream_uart_irq_enable(self); } - if (self->client_task_handle != NULL) { + if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) { mp_raise_OSError(MP_EALREADY); } - switch (parsed_args[ARG_authmode].u_int) { + switch (parsed_args[ARG_security].u_int) { case PPPAUTHTYPE_NONE: case PPPAUTHTYPE_PAP: case PPPAUTHTYPE_CHAP: @@ -219,39 +330,49 @@ static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw mp_raise_ValueError(MP_ERROR_TEXT("invalid auth")); } - if (parsed_args[ARG_authmode].u_int != PPPAUTHTYPE_NONE) { - const char *username_str = mp_obj_str_get_str(parsed_args[ARG_username].u_obj); - const char *password_str = mp_obj_str_get_str(parsed_args[ARG_password].u_obj); - pppapi_set_auth(self->pcb, parsed_args[ARG_authmode].u_int, username_str, password_str); + if (parsed_args[ARG_security].u_int != PPPAUTHTYPE_NONE) { + const char *user_str = mp_obj_str_get_str(parsed_args[ARG_user].u_obj); + const char *key_str = mp_obj_str_get_str(parsed_args[ARG_key].u_obj); + pppapi_set_auth(self->pcb, parsed_args[ARG_security].u_int, user_str, key_str); } - if (pppapi_set_default(self->pcb) != ESP_OK) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("set default failed")); + + if (pppapi_set_default(self->pcb) != ERR_OK) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ppp_set_default failed")); } ppp_set_usepeerdns(self->pcb, true); - if (pppapi_connect(self->pcb, 0) != ESP_OK) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("connect failed")); + if (pppapi_connect(self->pcb, 0) != ERR_OK) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ppp_connect failed")); } - if (xTaskCreatePinnedToCore(pppos_client_task, "ppp", 2048, self, 1, (TaskHandle_t *)&self->client_task_handle, MP_TASK_COREID) != pdPASS) { - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("failed to create worker task")); - } + self->state = STATE_CONNECTING; + + // Do a poll in case there is data waiting on the input stream. + network_ppp_poll(1, args); return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_KW(ppp_connect_obj, 1, ppp_connect_py); +static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_connect_obj, 1, network_ppp_connect); -static mp_obj_t ppp_delete(mp_obj_t self_in) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_obj_t args[] = {self, mp_const_false}; - ppp_active(2, args); +static mp_obj_t network_ppp_disconnect(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) { + // Initiate close and wait for PPPERR_USER callback. + pppapi_close(self->pcb, 0); + } return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_1(ppp_delete_obj, ppp_delete); +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_disconnect_obj, network_ppp_disconnect); + +static mp_obj_t network_ppp_isconnected(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(self->state == STATE_CONNECTED); +} +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_isconnected_obj, network_ppp_isconnected); -static mp_obj_t ppp_ifconfig(size_t n_args, const mp_obj_t *args) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); +static mp_obj_t network_ppp_ifconfig(size_t n_args, const mp_obj_t *args) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (n_args == 1) { // get const ip_addr_t *dns; @@ -282,11 +403,11 @@ static mp_obj_t ppp_ifconfig(size_t n_args, const mp_obj_t *args) { return mp_const_none; } } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_ifconfig_obj, 1, 2, ppp_ifconfig); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_ifconfig_obj, 1, 2, network_ppp_ifconfig); -static mp_obj_t ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { +static mp_obj_t network_ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (kwargs->used == 0) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (self->pcb == NULL) { mp_raise_ValueError(MP_ERROR_TEXT("PPP not active")); } @@ -318,94 +439,64 @@ static mp_obj_t ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwar } return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_KW(ppp_ipconfig_obj, 1, ppp_ipconfig); +static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_ipconfig_obj, 1, network_ppp_ipconfig); -static mp_obj_t ppp_status(mp_obj_t self_in) { - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(ppp_status_obj, ppp_status); - -static mp_obj_t ppp_isconnected(mp_obj_t self_in) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_bool(self->connected); -} -static MP_DEFINE_CONST_FUN_OBJ_1(ppp_isconnected_obj, ppp_isconnected); - -static mp_obj_t ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { - if (n_args != 1 && kwargs->used != 0) { - mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed")); - } - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); - - if (kwargs->used != 0) { - for (size_t i = 0; i < kwargs->alloc; i++) { - if (mp_map_slot_is_filled(kwargs, i)) { - switch (mp_obj_str_get_qstr(kwargs->table[i].key)) { - case MP_QSTR_stream: { - if (kwargs->table[i].value != mp_const_none) { - mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); - } - self->stream = kwargs->table[i].value; - break; - } - default: - break; - } +static mp_obj_t network_ppp_active(size_t n_args, const mp_obj_t *args) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (n_args > 1) { + if (mp_obj_is_true(args[1])) { + if (self->state >= STATE_ACTIVE) { + return mp_const_true; } - } - return mp_const_none; - } - if (n_args != 2) { - mp_raise_TypeError(MP_ERROR_TEXT("can query only one param")); - } - - mp_obj_t val = mp_const_none; + self->pcb = pppapi_pppos_create(&self->netif, network_ppp_output_callback, network_ppp_status_cb, self); + if (self->pcb == NULL) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("pppos_create failed")); + } + self->state = STATE_ACTIVE; - switch (mp_obj_str_get_qstr(args[1])) { - case MP_QSTR_stream: { - val = self->stream; - break; - } - case MP_QSTR_ifname: { - if (self->pcb != NULL) { - struct netif *pppif = ppp_netif(self->pcb); - char ifname[NETIF_NAMESIZE + 1] = {0}; - netif_index_to_name(netif_get_index(pppif), ifname); - if (ifname[0] != 0) { - val = mp_obj_new_str_from_cstr((char *)ifname); - } + network_ppp_stream_uart_irq_enable(self); + } else { + if (self->state < STATE_ACTIVE) { + return mp_const_false; } - break; + + network_ppp___del__(MP_OBJ_FROM_PTR(self)); } - default: - mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); } - - return val; + return mp_obj_new_bool(self->state >= STATE_ACTIVE); } -static MP_DEFINE_CONST_FUN_OBJ_KW(ppp_config_obj, 1, ppp_config); - -static const mp_rom_map_elem_t ppp_if_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&ppp_active_obj) }, - { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&ppp_connect_obj) }, - { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&ppp_isconnected_obj) }, - { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&ppp_status_obj) }, - { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&ppp_config_obj) }, - { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&ppp_ifconfig_obj) }, - { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&ppp_ipconfig_obj) }, - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&ppp_delete_obj) }, +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_active_obj, 1, 2, network_ppp_active); + +static const mp_rom_map_elem_t network_ppp_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&network_ppp___del___obj) }, + { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_ppp_config_obj) }, + { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_ppp_status_obj) }, + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&network_ppp_connect_obj) }, + { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&network_ppp_disconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_ppp_isconnected_obj) }, + { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_ppp_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_ppp_ipconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&network_ppp_poll_obj) }, + + { MP_ROM_QSTR(MP_QSTR_SEC_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) }, + { MP_ROM_QSTR(MP_QSTR_SEC_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) }, + { MP_ROM_QSTR(MP_QSTR_SEC_CHAP), MP_ROM_INT(PPPAUTHTYPE_CHAP) }, + + // Deprecated interface for backwards compatibility + { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&network_ppp_active_obj) }, { MP_ROM_QSTR(MP_QSTR_AUTH_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) }, { MP_ROM_QSTR(MP_QSTR_AUTH_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) }, { MP_ROM_QSTR(MP_QSTR_AUTH_CHAP), MP_ROM_INT(PPPAUTHTYPE_CHAP) }, }; -static MP_DEFINE_CONST_DICT(ppp_if_locals_dict, ppp_if_locals_dict_table); +static MP_DEFINE_CONST_DICT(network_ppp_locals_dict, network_ppp_locals_dict_table); MP_DEFINE_CONST_OBJ_TYPE( - ppp_if_type, + esp_network_ppp_lwip_type, MP_QSTR_PPP, MP_TYPE_FLAG_NONE, - locals_dict, &ppp_if_locals_dict + make_new, network_ppp_make_new, + locals_dict, &network_ppp_locals_dict ); #endif diff --git a/ports/esp32/partitions-16MiB-ota.csv b/ports/esp32/partitions-16MiB-ota.csv deleted file mode 100644 index a6f83bc46b..0000000000 --- a/ports/esp32/partitions-16MiB-ota.csv +++ /dev/null @@ -1,10 +0,0 @@ -# Partition table for MicroPython with OTA support using 16MB flash -# Notes: the offset of the partition table itself is set in -# $IDF_PATH/components/partition_table/Kconfig.projbuild. -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x4000, -otadata, data, ota, 0xd000, 0x2000, -phy_init, data, phy, 0xf000, 0x1000, -ota_0, app, ota_0, 0x10000, 0x270000, -ota_1, app, ota_1, 0x280000, 0x270000, -vfs, data, fat, 0x4f0000, 0xb10000, diff --git a/ports/esp32/partitions-16MiB.csv b/ports/esp32/partitions-16MiB.csv deleted file mode 100644 index ae926c7b94..0000000000 --- a/ports/esp32/partitions-16MiB.csv +++ /dev/null @@ -1,7 +0,0 @@ -# Notes: the offset of the partition table itself is set in -# $IDF_PATH/components/partition_table/Kconfig.projbuild. -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x6000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x1F0000, -vfs, data, fat, 0x200000, 0xE00000, diff --git a/ports/esp32/partitions-2MiB.csv b/ports/esp32/partitions-2MiB.csv index ea6626825c..5449201a7a 100644 --- a/ports/esp32/partitions-2MiB.csv +++ b/ports/esp32/partitions-2MiB.csv @@ -4,4 +4,3 @@ nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 0x160000, -vfs, data, fat, 0x170000, 0x50000, diff --git a/ports/esp32/partitions-32MiB.csv b/ports/esp32/partitions-32MiB.csv deleted file mode 100644 index 31591c9949..0000000000 --- a/ports/esp32/partitions-32MiB.csv +++ /dev/null @@ -1,7 +0,0 @@ -# Notes: the offset of the partition table itself is set in -# $IDF_PATH/components/partition_table/Kconfig.projbuild. -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x6000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x1F0000, -vfs, data, fat, 0x200000, 0x1E00000, diff --git a/ports/esp32/partitions-4MiB-ota.csv b/ports/esp32/partitions-4MiB-ota.csv index 094ad76660..9cbb422799 100644 --- a/ports/esp32/partitions-4MiB-ota.csv +++ b/ports/esp32/partitions-4MiB-ota.csv @@ -7,4 +7,3 @@ otadata, data, ota, 0xd000, 0x2000, phy_init, data, phy, 0xf000, 0x1000, ota_0, app, ota_0, 0x10000, 0x180000, ota_1, app, ota_1, 0x190000, 0x180000, -vfs, data, fat, 0x310000, 0x0f0000, diff --git a/ports/esp32/partitions-4MiB-romfs.csv b/ports/esp32/partitions-4MiB-romfs.csv index dd02506e54..29033ff3ab 100644 --- a/ports/esp32/partitions-4MiB-romfs.csv +++ b/ports/esp32/partitions-4MiB-romfs.csv @@ -5,4 +5,3 @@ nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 0x1D0000, romfs, data, 0x8f, 0x1E0000, 0x20000, -vfs, data, fat, 0x200000, 0x200000, diff --git a/ports/esp32/partitions-4MiB.csv b/ports/esp32/partitions-4MiBplus.csv index 53f0f16744..460e5cc0e9 100644 --- a/ports/esp32/partitions-4MiB.csv +++ b/ports/esp32/partitions-4MiBplus.csv @@ -1,7 +1,10 @@ +# This partition table is for devices with 4MiB or more of flash. +# The first 2MiB is used for bootloader, nvs, phy_init and firmware. +# The remaining flash is for the user filesystem(s). + # Notes: the offset of the partition table itself is set in # $IDF_PATH/components/partition_table/Kconfig.projbuild. # Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 0x1F0000, -vfs, data, fat, 0x200000, 0x200000, diff --git a/ports/esp32/partitions-8MiB.csv b/ports/esp32/partitions-8MiB.csv deleted file mode 100644 index 582d3b50e5..0000000000 --- a/ports/esp32/partitions-8MiB.csv +++ /dev/null @@ -1,7 +0,0 @@ -# Notes: the offset of the partition table itself is set in -# $IDF_PATH/components/partition_table/Kconfig.projbuild. -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x6000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x1F0000, -vfs, data, fat, 0x200000, 0x600000, diff --git a/ports/esp32/partitions-32MiB-ota.csv b/ports/esp32/partitions-8MiBplus-ota.csv index 7366a2ad8d..09a8e6d702 100644 --- a/ports/esp32/partitions-32MiB-ota.csv +++ b/ports/esp32/partitions-8MiBplus-ota.csv @@ -1,4 +1,7 @@ -# Partition table for MicroPython with OTA support using 32MB flash +# This partition table is for devices with 8MiB or more of flash and OTA support. +# The first 5056kiB is used for bootloader, nvs, phy_init and firmware. +# The remaining flash is for the user filesystem(s). + # Notes: the offset of the partition table itself is set in # $IDF_PATH/components/partition_table/Kconfig.projbuild. # Name, Type, SubType, Offset, Size, Flags @@ -7,4 +10,3 @@ otadata, data, ota, 0xd000, 0x2000, phy_init, data, phy, 0xf000, 0x1000, ota_0, app, ota_0, 0x10000, 0x270000, ota_1, app, ota_1, 0x280000, 0x270000, -vfs, data, fat, 0x4f0000, 0x1B10000, diff --git a/ports/esp32/tools/metrics_esp32.py b/ports/esp32/tools/metrics_esp32.py index 5e65a78c97..9efaae63a9 100755 --- a/ports/esp32/tools/metrics_esp32.py +++ b/ports/esp32/tools/metrics_esp32.py @@ -37,7 +37,7 @@ import sys import subprocess from dataclasses import dataclass -IDF_VERS = ("v5.2.2",) +IDF_VERS = ("v5.4.1",) BUILDS = ( ("ESP32_GENERIC", ""), |