summaryrefslogtreecommitdiffstatshomepage
path: root/ports/esp32
diff options
context:
space:
mode:
Diffstat (limited to 'ports/esp32')
-rw-r--r--ports/esp32/README.md32
-rw-r--r--ports/esp32/adc.c94
-rw-r--r--ports/esp32/adc.h47
-rw-r--r--ports/esp32/boards/ARDUINO_NANO_ESP32/board.json4
-rw-r--r--ports/esp32/boards/ESP32_GENERIC/board.md2
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_C3/board.md4
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S2/board.md2
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/board.json3
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/board.md4
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake4
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board6
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m4
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json23
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake7
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h10
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv13
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board4
-rw-r--r--ports/esp32/boards/LILYGO_TTGO_LORA32/board.json4
-rw-r--r--ports/esp32/boards/M5STACK_ATOM/board.json6
-rw-r--r--ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json2
-rw-r--r--ports/esp32/boards/M5STACK_NANOC6/board.json2
-rw-r--r--ports/esp32/boards/OLIMEX_ESP32_EVB/board.json6
-rw-r--r--ports/esp32/boards/OLIMEX_ESP32_POE/board.json6
-rw-r--r--ports/esp32/boards/SIL_WESP32/board.json2
-rw-r--r--ports/esp32/boards/SIL_WESP32/sdkconfig.board9
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json23
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py2
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake7
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h14
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv22
-rw-r--r--ports/esp32/boards/UM_FEATHERS2/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_FEATHERS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_NANOS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_OMGS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_PROS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_TINYC6/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_TINYS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/sdkconfig.base4
-rw-r--r--ports/esp32/esp32_common.cmake19
-rw-r--r--ports/esp32/machine_adc.c95
-rw-r--r--ports/esp32/machine_adc_block.c14
-rw-r--r--ports/esp32/machine_i2c.c5
-rw-r--r--ports/esp32/machine_pin.c6
-rw-r--r--ports/esp32/machine_pwm.c974
-rw-r--r--ports/esp32/machine_rtc.c4
-rw-r--r--ports/esp32/machine_rtc.h12
-rw-r--r--ports/esp32/machine_timer.c49
-rw-r--r--ports/esp32/machine_timer.h3
-rw-r--r--ports/esp32/machine_uart.c74
-rw-r--r--ports/esp32/main.c34
-rw-r--r--ports/esp32/main/idf_component.yml5
-rw-r--r--ports/esp32/modesp32.c70
-rw-r--r--ports/esp32/modmachine.c8
-rw-r--r--ports/esp32/modnetwork.h31
-rw-r--r--ports/esp32/modnetwork_globals.h8
-rw-r--r--ports/esp32/modsocket.c31
-rw-r--r--ports/esp32/mpconfigport.h9
-rw-r--r--ports/esp32/mpnimbleport.c4
-rw-r--r--ports/esp32/mpthreadport.c96
-rw-r--r--ports/esp32/network_common.c4
-rw-r--r--ports/esp32/network_lan.c20
-rw-r--r--ports/esp32/network_ppp.c511
-rw-r--r--ports/esp32/partitions-16MiB-ota.csv10
-rw-r--r--ports/esp32/partitions-16MiB.csv7
-rw-r--r--ports/esp32/partitions-2MiB.csv1
-rw-r--r--ports/esp32/partitions-32MiB.csv7
-rw-r--r--ports/esp32/partitions-4MiB-ota.csv1
-rw-r--r--ports/esp32/partitions-4MiB-romfs.csv1
-rw-r--r--ports/esp32/partitions-4MiBplus.csv (renamed from ports/esp32/partitions-4MiB.csv)5
-rw-r--r--ports/esp32/partitions-8MiB.csv7
-rw-r--r--ports/esp32/partitions-8MiBplus-ota.csv (renamed from ports/esp32/partitions-32MiB-ota.csv)6
-rwxr-xr-xports/esp32/tools/metrics_esp32.py2
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", ""),