diff --git a/components/driver/Kconfig b/components/driver/Kconfig index f0bcbcfe72..da23ea059f 100644 --- a/components/driver/Kconfig +++ b/components/driver/Kconfig @@ -538,4 +538,14 @@ menu "Driver Configurations" endmenu # Parallel IO Configuration + menu "LEDC Configuration" + config LEDC_CTRL_FUNC_IN_IRAM + bool "Place LEDC control functions into IRAM" + default n + help + Place LEDC control functions (ledc_update_duty and ledc_stop) into IRAM, + so that these functions can be IRAM-safe and able to be called in an IRAM context. + Enabling this option can improve driver performance as well. + endmenu # LEDC Configuration + endmenu # Driver configurations diff --git a/components/driver/ledc/include/driver/ledc.h b/components/driver/ledc/include/driver/ledc.h index 580288af0f..bce4e0de4e 100644 --- a/components/driver/ledc/include/driver/ledc.h +++ b/components/driver/ledc/include/driver/ledc.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -141,6 +141,9 @@ esp_err_t ledc_timer_config(const ledc_timer_config_t *timer_conf); * @note ledc_set_duty, ledc_set_duty_with_hpoint and ledc_update_duty are not thread-safe, do not call these functions to * control one LEDC channel in different tasks at the same time. * A thread-safe version of API is ledc_set_duty_and_update + * @note If `CONFIG_LEDC_CTRL_FUNC_IN_IRAM` is enabled, this function will be placed in the IRAM by linker, + * makes it possible to execute even when the Cache is disabled. + * @note This function is allowed to run within ISR context. * @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode. * @param channel LEDC channel (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t * @@ -171,6 +174,9 @@ esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc * @brief LEDC stop. * Disable LEDC output, and set idle level * + * @note If `CONFIG_LEDC_CTRL_FUNC_IN_IRAM` is enabled, this function will be placed in the IRAM by linker, + * makes it possible to execute even when the Cache is disabled. + * @note This function is allowed to run within ISR context. * @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode. * @param channel LEDC channel (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t * @param idle_level Set output idle level after LEDC stops. diff --git a/components/driver/ledc/ledc.c b/components/driver/ledc/ledc.c index 15b5f96a3a..73d5005437 100644 --- a/components/driver/ledc/ledc.c +++ b/components/driver/ledc/ledc.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -26,6 +26,8 @@ static __attribute__((unused)) const char *LEDC_TAG = "ledc"; #define LEDC_CHECK(a, str, ret_val) ESP_RETURN_ON_FALSE(a, ret_val, LEDC_TAG, "%s", str) #define LEDC_ARG_CHECK(a, param) ESP_RETURN_ON_FALSE(a, ESP_ERR_INVALID_ARG, LEDC_TAG, param " argument is invalid") +#define LEDC_CHECK_ISR(a, str, ret_val) ESP_RETURN_ON_FALSE_ISR(a, ret_val, LEDC_TAG, "%s", str) +#define LEDC_ARG_CHECK_ISR(a, param) ESP_RETURN_ON_FALSE_ISR(a, ESP_ERR_INVALID_ARG, LEDC_TAG, param " argument is invalid") #define LEDC_CLK_NOT_FOUND 0 #define LEDC_SLOW_CLK_UNINIT -1 @@ -705,26 +707,26 @@ static void _ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - LEDC_ARG_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "speed_mode"); - LEDC_ARG_CHECK(channel < LEDC_CHANNEL_MAX, "channel"); - LEDC_CHECK(p_ledc_obj[speed_mode] != NULL, LEDC_NOT_INIT, ESP_ERR_INVALID_STATE); - portENTER_CRITICAL(&ledc_spinlock); + LEDC_ARG_CHECK_ISR(speed_mode < LEDC_SPEED_MODE_MAX, "speed_mode"); + LEDC_ARG_CHECK_ISR(channel < LEDC_CHANNEL_MAX, "channel"); + LEDC_CHECK_ISR(p_ledc_obj[speed_mode] != NULL, LEDC_NOT_INIT, ESP_ERR_INVALID_STATE); + portENTER_CRITICAL_SAFE(&ledc_spinlock); _ledc_update_duty(speed_mode, channel); - portEXIT_CRITICAL(&ledc_spinlock); + portEXIT_CRITICAL_SAFE(&ledc_spinlock); return ESP_OK; } esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level) { - LEDC_ARG_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "speed_mode"); - LEDC_ARG_CHECK(channel < LEDC_CHANNEL_MAX, "channel"); - LEDC_CHECK(p_ledc_obj[speed_mode] != NULL, LEDC_NOT_INIT, ESP_ERR_INVALID_STATE); - portENTER_CRITICAL(&ledc_spinlock); + LEDC_ARG_CHECK_ISR(speed_mode < LEDC_SPEED_MODE_MAX, "speed_mode"); + LEDC_ARG_CHECK_ISR(channel < LEDC_CHANNEL_MAX, "channel"); + LEDC_CHECK_ISR(p_ledc_obj[speed_mode] != NULL, LEDC_NOT_INIT, ESP_ERR_INVALID_STATE); + portENTER_CRITICAL_SAFE(&ledc_spinlock); ledc_hal_set_idle_level(&(p_ledc_obj[speed_mode]->ledc_hal), channel, idle_level); ledc_hal_set_sig_out_en(&(p_ledc_obj[speed_mode]->ledc_hal), channel, false); ledc_hal_set_duty_start(&(p_ledc_obj[speed_mode]->ledc_hal), channel, false); ledc_ls_channel_update(speed_mode, channel); - portEXIT_CRITICAL(&ledc_spinlock); + portEXIT_CRITICAL_SAFE(&ledc_spinlock); return ESP_OK; } diff --git a/components/driver/linker.lf b/components/driver/linker.lf index 1f6f247879..578f167970 100644 --- a/components/driver/linker.lf +++ b/components/driver/linker.lf @@ -27,3 +27,7 @@ entries: dac_continuous: dac_continuous_write_asynchronously (noflash) if MCPWM_CTRL_FUNC_IN_IRAM = y: mcpwm_cmpr: mcpwm_comparator_set_compare_value (noflash) + if LEDC_CTRL_FUNC_IN_IRAM = y: + ledc: ledc_stop (noflash) + ledc: ledc_update_duty (noflash) + ledc: _ledc_update_duty (noflash) diff --git a/components/driver/test_apps/ledc/sdkconfig.ci.iram_safe b/components/driver/test_apps/ledc/sdkconfig.ci.iram_safe index a3eee573c8..c20ccf5cf6 100644 --- a/components/driver/test_apps/ledc/sdkconfig.ci.iram_safe +++ b/components/driver/test_apps/ledc/sdkconfig.ci.iram_safe @@ -6,3 +6,4 @@ CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y # ledc driver uses assert in the ISR code path CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y +CONFIG_LEDC_CTRL_FUNC_IN_IRAM=y diff --git a/components/hal/esp32/include/hal/ledc_ll.h b/components/hal/esp32/include/hal/ledc_ll.h index 9953191522..0e2996bccf 100644 --- a/components/hal/esp32/include/hal/ledc_ll.h +++ b/components/hal/esp32/include/hal/ledc_ll.h @@ -408,6 +408,7 @@ static inline void ledc_ll_set_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode * * @return None */ +__attribute__((always_inline)) static inline void ledc_ll_set_sig_out_en(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, bool sig_out_en) { hw->channel_group[speed_mode].channel[channel_num].conf0.sig_out_en = sig_out_en; @@ -438,6 +439,7 @@ static inline void ledc_ll_set_duty_start(ledc_dev_t *hw, ledc_mode_t speed_mode * * @return None */ +__attribute__((always_inline)) static inline void ledc_ll_set_idle_level(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t idle_level) { hw->channel_group[speed_mode].channel[channel_num].conf0.idle_lv = idle_level & 0x1; diff --git a/components/hal/esp32c2/include/hal/ledc_ll.h b/components/hal/esp32c2/include/hal/ledc_ll.h index 9d2da4b87f..6c1f7b1447 100644 --- a/components/hal/esp32c2/include/hal/ledc_ll.h +++ b/components/hal/esp32c2/include/hal/ledc_ll.h @@ -393,6 +393,7 @@ static inline void ledc_ll_set_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode * * @return None */ +__attribute__((always_inline)) static inline void ledc_ll_set_sig_out_en(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, bool sig_out_en) { hw->channel_group[speed_mode].channel[channel_num].conf0.sig_out_en = sig_out_en; @@ -423,6 +424,7 @@ static inline void ledc_ll_set_duty_start(ledc_dev_t *hw, ledc_mode_t speed_mode * * @return None */ +__attribute__((always_inline)) static inline void ledc_ll_set_idle_level(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t idle_level) { hw->channel_group[speed_mode].channel[channel_num].conf0.idle_lv = idle_level & 0x1; diff --git a/components/hal/esp32c3/include/hal/ledc_ll.h b/components/hal/esp32c3/include/hal/ledc_ll.h index 7fb7c0b31f..8da610d174 100644 --- a/components/hal/esp32c3/include/hal/ledc_ll.h +++ b/components/hal/esp32c3/include/hal/ledc_ll.h @@ -393,6 +393,7 @@ static inline void ledc_ll_set_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode * * @return None */ +__attribute__((always_inline)) static inline void ledc_ll_set_sig_out_en(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, bool sig_out_en) { hw->channel_group[speed_mode].channel[channel_num].conf0.sig_out_en = sig_out_en; @@ -423,6 +424,7 @@ static inline void ledc_ll_set_duty_start(ledc_dev_t *hw, ledc_mode_t speed_mode * * @return None */ +__attribute__((always_inline)) static inline void ledc_ll_set_idle_level(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t idle_level) { hw->channel_group[speed_mode].channel[channel_num].conf0.idle_lv = idle_level & 0x1; diff --git a/components/hal/esp32c6/include/hal/ledc_ll.h b/components/hal/esp32c6/include/hal/ledc_ll.h index d636311c51..74e3a63834 100644 --- a/components/hal/esp32c6/include/hal/ledc_ll.h +++ b/components/hal/esp32c6/include/hal/ledc_ll.h @@ -491,6 +491,7 @@ static inline void ledc_ll_get_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode * * @return None */ +__attribute__((always_inline)) static inline void ledc_ll_set_sig_out_en(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, bool sig_out_en) { hw->channel_group[speed_mode].channel[channel_num].conf0.sig_out_en = sig_out_en; @@ -521,6 +522,7 @@ static inline void ledc_ll_set_duty_start(ledc_dev_t *hw, ledc_mode_t speed_mode * * @return None */ +__attribute__((always_inline)) static inline void ledc_ll_set_idle_level(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t idle_level) { hw->channel_group[speed_mode].channel[channel_num].conf0.idle_lv = idle_level & 0x1; diff --git a/components/hal/esp32h2/include/hal/ledc_ll.h b/components/hal/esp32h2/include/hal/ledc_ll.h index 0d644590a1..d773079e66 100644 --- a/components/hal/esp32h2/include/hal/ledc_ll.h +++ b/components/hal/esp32h2/include/hal/ledc_ll.h @@ -489,6 +489,7 @@ static inline void ledc_ll_get_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode * * @return None */ +__attribute__((always_inline)) static inline void ledc_ll_set_sig_out_en(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, bool sig_out_en) { hw->channel_group[speed_mode].channel[channel_num].conf0.sig_out_en = sig_out_en; @@ -519,6 +520,7 @@ static inline void ledc_ll_set_duty_start(ledc_dev_t *hw, ledc_mode_t speed_mode * * @return None */ +__attribute__((always_inline)) static inline void ledc_ll_set_idle_level(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t idle_level) { hw->channel_group[speed_mode].channel[channel_num].conf0.idle_lv = idle_level & 0x1; diff --git a/components/hal/esp32s2/include/hal/ledc_ll.h b/components/hal/esp32s2/include/hal/ledc_ll.h index 0eb780ec9b..49d5ae5899 100644 --- a/components/hal/esp32s2/include/hal/ledc_ll.h +++ b/components/hal/esp32s2/include/hal/ledc_ll.h @@ -425,6 +425,7 @@ static inline void ledc_ll_set_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode * * @return None */ +__attribute__((always_inline)) static inline void ledc_ll_set_sig_out_en(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, bool sig_out_en) { hw->channel_group[speed_mode].channel[channel_num].conf0.sig_out_en = sig_out_en; @@ -455,6 +456,7 @@ static inline void ledc_ll_set_duty_start(ledc_dev_t *hw, ledc_mode_t speed_mode * * @return None */ +__attribute__((always_inline)) static inline void ledc_ll_set_idle_level(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t idle_level) { hw->channel_group[speed_mode].channel[channel_num].conf0.idle_lv = idle_level & 0x1; diff --git a/components/hal/esp32s3/include/hal/ledc_ll.h b/components/hal/esp32s3/include/hal/ledc_ll.h index 2e44509563..85d22b13c4 100644 --- a/components/hal/esp32s3/include/hal/ledc_ll.h +++ b/components/hal/esp32s3/include/hal/ledc_ll.h @@ -394,6 +394,7 @@ static inline void ledc_ll_set_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode * * @return None */ +__attribute__((always_inline)) static inline void ledc_ll_set_sig_out_en(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, bool sig_out_en) { hw->channel_group[speed_mode].channel[channel_num].conf0.sig_out_en = sig_out_en; @@ -424,6 +425,7 @@ static inline void ledc_ll_set_duty_start(ledc_dev_t *hw, ledc_mode_t speed_mode * * @return None */ +__attribute__((always_inline)) static inline void ledc_ll_set_idle_level(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t idle_level) { hw->channel_group[speed_mode].channel[channel_num].conf0.idle_lv = idle_level & 0x1;