diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 2974028b50..195295e5a6 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -1058,6 +1058,7 @@ CONF_DISABLE_MBEDTLS_PEER_CERT = "disable_mbedtls_peer_cert" CONF_DISABLE_MBEDTLS_PKCS7 = "disable_mbedtls_pkcs7" CONF_DISABLE_REGI2C_IN_IRAM = "disable_regi2c_in_iram" CONF_DISABLE_FATFS = "disable_fatfs" +CONF_LEDC_IN_IRAM = "ledc_in_iram" # VFS requirement tracking # Components that need VFS features can call require_vfs_*() functions @@ -1071,6 +1072,7 @@ KEY_MBEDTLS_PEER_CERT_REQUIRED = "mbedtls_peer_cert_required" KEY_MBEDTLS_PKCS7_REQUIRED = "mbedtls_pkcs7_required" KEY_FATFS_REQUIRED = "fatfs_required" KEY_MBEDTLS_SHA512_REQUIRED = "mbedtls_sha512_required" +KEY_LEDC_IRAM_REQUIRED = "ledc_iram_required" def require_vfs_select() -> None: @@ -1168,6 +1170,17 @@ def require_fatfs() -> None: CORE.data[KEY_ESP32][KEY_FATFS_REQUIRED] = True +def require_ledc_iram() -> None: + """Mark that LEDC IRAM safety is required by a component. + + Call this from components that use the LEDC (PWM) driver. When flash cache is + disabled (e.g., during NVS writes by WiFi, BLE, Zigbee, or power management), + the LEDC control functions must be in IRAM to avoid crashes. + This sets CONFIG_LEDC_CTRL_FUNC_IN_IRAM. + """ + CORE.data[KEY_ESP32][KEY_LEDC_IRAM_REQUIRED] = True + + def _parse_idf_component(value: str) -> ConfigType: """Parse IDF component shorthand syntax like 'owner/component^version'""" # Match operator followed by version-like string (digit or *) @@ -1268,6 +1281,7 @@ FRAMEWORK_SCHEMA = cv.Schema( cv.Optional(CONF_DISABLE_MBEDTLS_PEER_CERT, default=True): cv.boolean, cv.Optional(CONF_DISABLE_MBEDTLS_PKCS7, default=True): cv.boolean, cv.Optional(CONF_DISABLE_REGI2C_IN_IRAM, default=True): cv.boolean, + cv.Optional(CONF_LEDC_IN_IRAM, default=False): cv.boolean, cv.Optional(CONF_DISABLE_FATFS, default=True): cv.boolean, } ), @@ -2068,6 +2082,16 @@ async def to_code(config): if advanced[CONF_DISABLE_REGI2C_IN_IRAM]: add_idf_sdkconfig_option("CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM", False) + # Place LEDC control functions in IRAM for cache safety + # When flash cache is disabled (during NVS writes by WiFi, BLE, Zigbee, Thread, + # power management, etc.), LEDC operations will crash if these functions are in flash. + # Components using LEDC call require_ledc_iram() to force this. + if ( + CORE.data[KEY_ESP32].get(KEY_LEDC_IRAM_REQUIRED, False) + or advanced[CONF_LEDC_IN_IRAM] + ): + add_idf_sdkconfig_option("CONFIG_LEDC_CTRL_FUNC_IN_IRAM", True) + # Disable FATFS support # Components that need FATFS (SD card, etc.) can call require_fatfs() if CORE.data[KEY_ESP32].get(KEY_FATFS_REQUIRED, False): diff --git a/esphome/components/ledc/output.py b/esphome/components/ledc/output.py index 95df1fba23..e82795fa4c 100644 --- a/esphome/components/ledc/output.py +++ b/esphome/components/ledc/output.py @@ -1,6 +1,7 @@ from esphome import automation, pins import esphome.codegen as cg from esphome.components import output +from esphome.components.esp32 import require_ledc_iram import esphome.config_validation as cv from esphome.const import ( CONF_CHANNEL, @@ -57,6 +58,7 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( async def to_code(config): + require_ledc_iram() gpio = await cg.gpio_pin_expression(config[CONF_PIN]) var = cg.new_Pvariable(config[CONF_ID], gpio) await cg.register_component(var, config)