Kaynağa Gözat

Merge branch 'refactor/ledc_driver_refactor' into 'master'

ledc: Improvement made on the LEDC driver

See merge request espressif/esp-idf!22971
Song Ruo Jing 2 yıl önce
ebeveyn
işleme
6700630ccb

+ 8 - 2
components/driver/ledc/include/driver/ledc.h

@@ -51,7 +51,7 @@ typedef struct {
 } ledc_channel_config_t;
 
 /**
- * @brief Configuration parameters of LEDC Timer timer for ledc_timer_config function
+ * @brief Configuration parameters of LEDC timer for ledc_timer_config function
  */
 typedef struct {
     ledc_mode_t speed_mode;                /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */
@@ -65,6 +65,11 @@ typedef struct {
                                                 as its clock source. 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. */
+    bool deconfigure;                      /*!< Set this field to de-configure a LEDC timer which has been configured before
+                                                Note that it will not check whether the timer wants to be de-configured
+                                                is binded to any channel. Also, the timer has to be paused first before
+                                                it can be de-configured.
+                                                When this field is set, duty_resolution, freq_hz, clk_cfg fields are ignored. */
 } ledc_timer_config_t;
 
 typedef intr_handle_t ledc_isr_handle_t;
@@ -124,6 +129,7 @@ esp_err_t ledc_channel_config(const ledc_channel_config_t *ledc_conf);
  *     - ESP_OK Success
  *     - ESP_ERR_INVALID_ARG Parameter error
  *     - ESP_FAIL Can not find a proper pre-divider number base on the given frequency and the current duty_resolution.
+ *     - ESP_ERR_INVALID_STATE Timer cannot be de-configured because timer is not configured or is not paused
  */
 esp_err_t ledc_timer_config(const ledc_timer_config_t *timer_conf);
 
@@ -453,7 +459,7 @@ esp_err_t ledc_fade_start(ledc_mode_t speed_mode, ledc_channel_t channel, ledc_f
  * @brief Stop LEDC fading. The duty of the channel is garanteed to be fixed at most one PWM cycle after the function returns.
  * @note  This API can be called if a new fixed duty or a new fade want to be set while the last fade operation is still running in progress.
  * @note  Call this API will abort the fading operation only if it was started by calling ledc_fade_start with LEDC_FADE_NO_WAIT mode.
- * @note  If a fade was started with LEDC_FADE_WAIT_DONE mode, calling this API afterwards HAS no use in stopping the fade. Fade will continue until it reachs the target duty.
+ * @note  If a fade was started with LEDC_FADE_WAIT_DONE mode, calling this API afterwards has no use in stopping the fade. Fade will continue until it reachs the target duty.
  * @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 number
  *

+ 110 - 44
components/driver/ledc/ledc.c

@@ -29,6 +29,7 @@ static __attribute__((unused)) const char *LEDC_TAG = "ledc";
 
 #define LEDC_CLK_NOT_FOUND  0
 #define LEDC_SLOW_CLK_UNINIT -1
+#define LEDC_TIMER_SPECIFIC_CLK_UNINIT -1
 
 // Precision degree only affects RC_FAST, other clock sources' frequences are fixed values
 // For targets that do not support RC_FAST calibration, can only use its approx. value. Precision degree other than
@@ -64,13 +65,20 @@ typedef struct {
 } ledc_fade_t;
 
 typedef struct {
-    ledc_hal_context_t ledc_hal;        /*!< LEDC hal context*/
+    ledc_hal_context_t ledc_hal;                        /*!< LEDC hal context */
+    ledc_slow_clk_sel_t glb_clk;                        /*!< LEDC global clock selection */
+    bool timer_is_stopped[LEDC_TIMER_MAX];              /*!< Indicates whether each timer has been stopped */
+    bool glb_clk_is_acquired[LEDC_TIMER_MAX];           /*!< Tracks whether the global clock is being acquired by each timer */
+#if SOC_LEDC_HAS_TIMER_SPECIFIC_MUX
+    ledc_clk_src_t timer_specific_clk[LEDC_TIMER_MAX];  /*!< Tracks the timer-specific clock selection for each timer */
+#endif
 } ledc_obj_t;
 
 static ledc_obj_t *p_ledc_obj[LEDC_SPEED_MODE_MAX] = {0};
 static ledc_fade_t *s_ledc_fade_rec[LEDC_SPEED_MODE_MAX][LEDC_CHANNEL_MAX];
 static ledc_isr_handle_t s_ledc_fade_isr_handle = NULL;
 static portMUX_TYPE ledc_spinlock = portMUX_INITIALIZER_UNLOCKED;
+static _lock_t s_ledc_mutex[LEDC_SPEED_MODE_MAX];
 
 #define LEDC_VAL_NO_CHANGE        (-1)
 #define LEDC_DUTY_NUM_MAX         LEDC_LL_DUTY_NUM_MAX            // Maximum steps per hardware fade
@@ -172,8 +180,10 @@ static void _ledc_op_lock_release(ledc_mode_t mode, ledc_channel_t channel)
 static uint32_t ledc_get_max_duty(ledc_mode_t speed_mode, ledc_channel_t channel)
 {
     // The arguments are checked before internally calling this function.
+    ledc_timer_t timer_sel;
+    ledc_hal_get_channel_timer(&(p_ledc_obj[speed_mode]->ledc_hal), channel, &timer_sel);
     uint32_t max_duty;
-    ledc_hal_get_max_duty(&(p_ledc_obj[speed_mode]->ledc_hal), channel, &max_duty);
+    ledc_hal_get_max_duty(&(p_ledc_obj[speed_mode]->ledc_hal), timer_sel, &max_duty);
     return max_duty;
 }
 
@@ -186,9 +196,10 @@ esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_
     portENTER_CRITICAL(&ledc_spinlock);
     ledc_hal_set_clock_divider(&(p_ledc_obj[speed_mode]->ledc_hal), timer_sel, clock_divider);
 #if SOC_LEDC_HAS_TIMER_SPECIFIC_MUX
-    /* Clock source can only be configured on boards which support timer-specific
-     * source clock. */
+    /* Clock source can only be configured on targets which support timer-specific source clock. */
     ledc_hal_set_clock_source(&(p_ledc_obj[speed_mode]->ledc_hal), timer_sel, clk_src);
+    // TODO: acquire clk_src, and release old clk_src if initialized and different than new one [clk_tree]
+    p_ledc_obj[speed_mode]->timer_specific_clk[timer_sel] = clk_src;
 #endif
     ledc_hal_set_duty_resolution(&(p_ledc_obj[speed_mode]->ledc_hal), timer_sel, duty_resolution);
     ledc_ls_timer_update(speed_mode, timer_sel);
@@ -205,12 +216,8 @@ int duty_val, ledc_duty_direction_t duty_direction, uint32_t duty_num, uint32_t
     if (duty_val >= 0) {
         ledc_hal_set_duty_int_part(&(p_ledc_obj[speed_mode]->ledc_hal), channel, duty_val);
     }
-    ledc_hal_set_duty_direction(&(p_ledc_obj[speed_mode]->ledc_hal), channel, duty_direction);
-    ledc_hal_set_duty_num(&(p_ledc_obj[speed_mode]->ledc_hal), channel, duty_num);
-    ledc_hal_set_duty_cycle(&(p_ledc_obj[speed_mode]->ledc_hal), channel, duty_cycle);
-    ledc_hal_set_duty_scale(&(p_ledc_obj[speed_mode]->ledc_hal), channel, duty_scale);
+    ledc_hal_set_fade_param(&(p_ledc_obj[speed_mode]->ledc_hal), channel, 0, duty_direction, duty_cycle, duty_scale, duty_num);
 #if SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
-    ledc_hal_set_duty_range_wr_addr(&(p_ledc_obj[speed_mode]->ledc_hal), channel, 0);
     ledc_hal_set_range_number(&(p_ledc_obj[speed_mode]->ledc_hal), channel, 1);
 #endif
     return ESP_OK;
@@ -245,6 +252,7 @@ esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, ledc_timer_t timer_sel)
     LEDC_ARG_CHECK(timer_sel < LEDC_TIMER_MAX, "timer_select");
     LEDC_CHECK(p_ledc_obj[speed_mode] != NULL, LEDC_NOT_INIT, ESP_ERR_INVALID_STATE);
     portENTER_CRITICAL(&ledc_spinlock);
+    p_ledc_obj[speed_mode]->timer_is_stopped[timer_sel] = true;
     ledc_hal_timer_pause(&(p_ledc_obj[speed_mode]->ledc_hal), timer_sel);
     portEXIT_CRITICAL(&ledc_spinlock);
     return ESP_OK;
@@ -256,6 +264,7 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, ledc_timer_t timer_sel)
     LEDC_ARG_CHECK(timer_sel < LEDC_TIMER_MAX, "timer_select");
     LEDC_CHECK(p_ledc_obj[speed_mode] != NULL, LEDC_NOT_INIT, ESP_ERR_INVALID_STATE);
     portENTER_CRITICAL(&ledc_spinlock);
+    p_ledc_obj[speed_mode]->timer_is_stopped[timer_sel] = false;
     ledc_hal_timer_resume(&(p_ledc_obj[speed_mode]->ledc_hal), timer_sel);
     portEXIT_CRITICAL(&ledc_spinlock);
     return ESP_OK;
@@ -271,6 +280,31 @@ esp_err_t ledc_isr_register(void (*fn)(void *), void *arg, int intr_alloc_flags,
     return ret;
 }
 
+static bool ledc_speed_mode_ctx_create(ledc_mode_t speed_mode)
+{
+    bool new_ctx = false;
+
+    // Prevent p_ledc_obj malloc concurrently
+    _lock_acquire(&s_ledc_mutex[speed_mode]);
+    if (!p_ledc_obj[speed_mode]) {
+        ledc_obj_t *ledc_new_mode_obj = (ledc_obj_t *) heap_caps_calloc(1, sizeof(ledc_obj_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
+        if (ledc_new_mode_obj) {
+            new_ctx = true;
+            ledc_hal_init(&(ledc_new_mode_obj->ledc_hal), speed_mode);
+            ledc_new_mode_obj->glb_clk = LEDC_SLOW_CLK_UNINIT;
+#if SOC_LEDC_HAS_TIMER_SPECIFIC_MUX
+            memset(ledc_new_mode_obj->timer_specific_clk, LEDC_TIMER_SPECIFIC_CLK_UNINIT, sizeof(ledc_clk_src_t) * LEDC_TIMER_MAX);
+#endif
+            p_ledc_obj[speed_mode] = ledc_new_mode_obj;
+            // Enable APB access to LEDC registers
+            periph_module_enable(PERIPH_LEDC_MODULE);
+        }
+    }
+    _lock_release(&s_ledc_mutex[speed_mode]);
+
+    return new_ctx;
+}
+
 static inline uint32_t ledc_calculate_divisor(uint32_t src_clk_freq, int freq_hz, uint32_t precision)
 {
     /**
@@ -501,16 +535,31 @@ static esp_err_t ledc_set_timer_div(ledc_mode_t speed_mode, ledc_timer_t timer_n
 #endif
         // Arriving here, variable glb_clk must have been assigned to one of the ledc_slow_clk_sel_t enum values
         assert(glb_clk != LEDC_SLOW_CLK_UNINIT);
+
+        portENTER_CRITICAL(&ledc_spinlock);
+        if (p_ledc_obj[speed_mode]->glb_clk != LEDC_SLOW_CLK_UNINIT && p_ledc_obj[speed_mode]->glb_clk != glb_clk) {
+            for (int i = 0; i < LEDC_TIMER_MAX; i++) {
+                if (i != timer_num && p_ledc_obj[speed_mode]->glb_clk_is_acquired[i]) {
+                    portEXIT_CRITICAL(&ledc_spinlock);
+                    ESP_RETURN_ON_FALSE(false, ESP_FAIL, LEDC_TAG,
+                    "timer clock conflict, already is %d but attempt to %d", p_ledc_obj[speed_mode]->glb_clk, glb_clk);
+                }
+            }
+        }
+        p_ledc_obj[speed_mode]->glb_clk_is_acquired[timer_num] = true;
+        if (p_ledc_obj[speed_mode]->glb_clk != glb_clk) {
+            // TODO: release old glb_clk (if not UNINIT), and acquire new glb_clk [clk_tree]
+            p_ledc_obj[speed_mode]->glb_clk = glb_clk;
+            ledc_hal_set_slow_clk_sel(&(p_ledc_obj[speed_mode]->ledc_hal), glb_clk);
+        }
+        portEXIT_CRITICAL(&ledc_spinlock);
+
         ESP_LOGD(LEDC_TAG, "In slow speed mode, global clk set: %d", glb_clk);
 
         /* keep ESP_PD_DOMAIN_RC_FAST on during light sleep */
 #if !CONFIG_IDF_TARGET_ESP32H2 // TODO: IDF-6267 Remove when H2 light sleep supported
         esp_sleep_periph_use_8m(glb_clk == LEDC_SLOW_CLK_RC_FAST);
 #endif
-
-        portENTER_CRITICAL(&ledc_spinlock);
-        ledc_hal_set_slow_clk_sel(&(p_ledc_obj[speed_mode]->ledc_hal), glb_clk);
-        portEXIT_CRITICAL(&ledc_spinlock);
     }
 
     /* The divisor is correct, we can write in the hardware. */
@@ -522,6 +571,28 @@ error:
     return ESP_FAIL;
 }
 
+static esp_err_t ledc_timer_del(ledc_mode_t speed_mode, ledc_timer_t timer_sel)
+{
+    LEDC_CHECK(p_ledc_obj[speed_mode] != NULL, LEDC_NOT_INIT, ESP_ERR_INVALID_STATE);
+    bool is_configured = true;
+    bool is_deleted = false;
+    portENTER_CRITICAL(&ledc_spinlock);
+    if (p_ledc_obj[speed_mode]->glb_clk_is_acquired[timer_sel] == false
+#if SOC_LEDC_HAS_TIMER_SPECIFIC_MUX
+        && p_ledc_obj[speed_mode]->timer_specific_clk[timer_sel] == LEDC_TIMER_SPECIFIC_CLK_UNINIT
+#endif
+    ) {
+        is_configured = false;
+    } else if (p_ledc_obj[speed_mode]->timer_is_stopped[timer_sel] == true) {
+        is_deleted = true;
+        p_ledc_obj[speed_mode]->glb_clk_is_acquired[timer_sel] = false;
+        // TODO: release timer specific clk and global clk if possible [clk_tree]
+    }
+    portEXIT_CRITICAL(&ledc_spinlock);
+    ESP_RETURN_ON_FALSE(is_configured && is_deleted, ESP_ERR_INVALID_STATE, LEDC_TAG, "timer hasn't been configured, or it is still running, please stop it with ledc_timer_pause first");
+    return ESP_OK;
+}
+
 esp_err_t ledc_timer_config(const ledc_timer_config_t *timer_conf)
 {
     LEDC_ARG_CHECK(timer_conf != NULL, "timer_conf");
@@ -530,28 +601,24 @@ esp_err_t ledc_timer_config(const ledc_timer_config_t *timer_conf)
     uint32_t timer_num = timer_conf->timer_num;
     uint32_t speed_mode = timer_conf->speed_mode;
     LEDC_ARG_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "speed_mode");
+    LEDC_ARG_CHECK(timer_num < LEDC_TIMER_MAX, "timer_num");
+    if (timer_conf->deconfigure) {
+        return ledc_timer_del(speed_mode, timer_num);
+    }
     LEDC_ARG_CHECK(!((timer_conf->clk_cfg == LEDC_USE_RC_FAST_CLK) && (speed_mode != LEDC_LOW_SPEED_MODE)), "Only low speed channel support RC_FAST_CLK");
-    periph_module_enable(PERIPH_LEDC_MODULE);
     if (freq_hz == 0 || duty_resolution == 0 || duty_resolution >= LEDC_TIMER_BIT_MAX) {
         ESP_LOGE(LEDC_TAG, "freq_hz=%"PRIu32" duty_resolution=%"PRIu32, freq_hz, duty_resolution);
         return ESP_ERR_INVALID_ARG;
     }
-    if (timer_num > LEDC_TIMER_3) {
-        ESP_LOGE(LEDC_TAG, "invalid timer #%"PRIu32, timer_num);
-        return ESP_ERR_INVALID_ARG;
-    }
 
-    if (p_ledc_obj[speed_mode] == NULL) {
-        p_ledc_obj[speed_mode] = (ledc_obj_t *) heap_caps_calloc(1, sizeof(ledc_obj_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
-        if (p_ledc_obj[speed_mode] == NULL) {
-            return ESP_ERR_NO_MEM;
-        }
-        ledc_hal_init(&(p_ledc_obj[speed_mode]->ledc_hal), speed_mode);
+    if (!ledc_speed_mode_ctx_create(speed_mode) && !p_ledc_obj[speed_mode]) {
+        return ESP_ERR_NO_MEM;
     }
 
     esp_err_t ret = ledc_set_timer_div(speed_mode, timer_num, timer_conf->clk_cfg, freq_hz, duty_resolution);
     if (ret == ESP_OK) {
-        /* Reset the timer. */
+        /* Make sure timer is running and reset the timer. */
+        ledc_timer_resume(speed_mode, timer_num);
         ledc_timer_rst(speed_mode, timer_num);
     }
     return ret;
@@ -585,23 +652,26 @@ esp_err_t ledc_channel_config(const ledc_channel_config_t *ledc_conf)
     LEDC_ARG_CHECK(timer_select < LEDC_TIMER_MAX, "timer_select");
     LEDC_ARG_CHECK(intr_type < LEDC_INTR_MAX, "intr_type");
 
-    periph_module_enable(PERIPH_LEDC_MODULE);
     esp_err_t ret = ESP_OK;
 
-    if (p_ledc_obj[speed_mode] == NULL) {
-        p_ledc_obj[speed_mode] = (ledc_obj_t *) heap_caps_calloc(1, sizeof(ledc_obj_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
-        if (p_ledc_obj[speed_mode] == NULL) {
-            return ESP_ERR_NO_MEM;
-        }
-        ledc_hal_init(&(p_ledc_obj[speed_mode]->ledc_hal), speed_mode);
+    bool new_speed_mode_ctx_created = ledc_speed_mode_ctx_create(speed_mode);
+    if (!new_speed_mode_ctx_created && !p_ledc_obj[speed_mode]) {
+        return ESP_ERR_NO_MEM;
+    }
 #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32H2)
-        // On such targets, the default ledc core(global) clock does not connect to any clock source
-        // Set channel configurations and update bits before core clock is on could lead to error
-        // Therefore, we should connect the core clock to a real clock source to make it on before any ledc register operation
-        // It can be switched to the other desired clock sources to meet the output pwm freq requirement later at timer configuration
-        ledc_hal_set_slow_clk_sel(&(p_ledc_obj[speed_mode]->ledc_hal), LEDC_LL_GLOBAL_CLK_DEFAULT);
-#endif
+    // On such targets, the default ledc core(global) clock does not connect to any clock source
+    // Set channel configurations and update bits before core clock is on could lead to error
+    // Therefore, we should connect the core clock to a real clock source to make it on before any ledc register operation
+    // It can be switched to the other desired clock sources to meet the output pwm freq requirement later at timer configuration
+    // So we consider the glb_clk still as LEDC_SLOW_CLK_UNINIT
+    else if (new_speed_mode_ctx_created) {
+        portENTER_CRITICAL(&ledc_spinlock);
+        if (p_ledc_obj[speed_mode]->glb_clk == LEDC_SLOW_CLK_UNINIT) {
+            ledc_hal_set_slow_clk_sel(&(p_ledc_obj[speed_mode]->ledc_hal), LEDC_LL_GLOBAL_CLK_DEFAULT);
+        }
+        portEXIT_CRITICAL(&ledc_spinlock);
     }
+#endif
 
     /*set channel parameters*/
     /*   channel parameters decide how the waveform looks like in one period*/
@@ -781,7 +851,7 @@ uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num)
         ESP_LOGW(LEDC_TAG, "LEDC timer not configured, call ledc_timer_config to set timer frequency");
         return 0;
     }
-    return ((uint64_t) src_clk_freq << 8) / precision / clock_divider;
+    return ((uint64_t) src_clk_freq << LEDC_LL_FRACTIONAL_BITS) / precision / clock_divider;
 }
 
 static inline void IRAM_ATTR ledc_calc_fade_end_channel(uint32_t *fade_end_status, uint32_t *channel)
@@ -1264,11 +1334,7 @@ static esp_err_t _ledc_set_multi_fade(ledc_mode_t speed_mode, ledc_channel_t cha
     ledc_hal_set_duty_int_part(&(p_ledc_obj[speed_mode]->ledc_hal), channel, start_duty);
     for (int i = 0; i < list_len; i++) {
         ledc_fade_param_config_t fade_param = fade_params_list[i];
-        ledc_hal_set_duty_direction(&(p_ledc_obj[speed_mode]->ledc_hal), channel, fade_param.dir);
-        ledc_hal_set_duty_cycle(&(p_ledc_obj[speed_mode]->ledc_hal), channel, fade_param.cycle_num);
-        ledc_hal_set_duty_scale(&(p_ledc_obj[speed_mode]->ledc_hal), channel, fade_param.scale);
-        ledc_hal_set_duty_num(&(p_ledc_obj[speed_mode]->ledc_hal), channel, fade_param.step_num);
-        ledc_hal_set_duty_range_wr_addr(&(p_ledc_obj[speed_mode]->ledc_hal), channel, i);
+        ledc_hal_set_fade_param(&(p_ledc_obj[speed_mode]->ledc_hal), channel, i, fade_param.dir, fade_param.cycle_num, fade_param.scale, fade_param.step_num);
     }
     ledc_hal_set_range_number(&(p_ledc_obj[speed_mode]->ledc_hal), channel, list_len);
     portEXIT_CRITICAL(&ledc_spinlock);

+ 8 - 3
components/driver/test_apps/ledc/main/test_ledc.c

@@ -546,8 +546,8 @@ static void timer_frequency_test(ledc_channel_t channel, ledc_timer_bit_t timer_
     };
     TEST_ESP_OK(ledc_channel_config(&ledc_ch_config));
     TEST_ESP_OK(ledc_timer_config(&ledc_time_config));
-    frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 100, 100, 20);
-    frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 5000, 5000, 50);
+    frequency_set_get(speed_mode, timer, 100, 100, 20);
+    frequency_set_get(speed_mode, timer, 5000, 5000, 50);
     // Try a frequency that couldn't be exactly achieved, requires rounding
     uint32_t theoretical_freq = 9000;
     uint32_t clk_src_freq = 0;
@@ -557,7 +557,12 @@ static void timer_frequency_test(ledc_channel_t channel, ledc_timer_bit_t timer_
     } else if (clk_src_freq == 96 * 1000 * 1000) {
         theoretical_freq = 9009;
     }
-    frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 9000, theoretical_freq, 50);
+    frequency_set_get(speed_mode, timer, 9000, theoretical_freq, 50);
+
+    // Pause and de-configure the timer so that it won't affect the following test cases
+    TEST_ESP_OK(ledc_timer_pause(speed_mode, timer));
+    ledc_time_config.deconfigure = 1;
+    TEST_ESP_OK(ledc_timer_config(&ledc_time_config));
 }
 
 TEST_CASE("LEDC set and get frequency", "[ledc][timeout=60]")

+ 32 - 24
components/hal/esp32/include/hal/ledc_ll.h

@@ -226,33 +226,32 @@ static inline void ledc_ll_get_duty_resolution(ledc_dev_t *hw, ledc_mode_t speed
 }
 
 /**
- * @brief Update channel configure when select low speed mode
+ * @brief Get LEDC max duty
  *
  * @param hw Beginning address of the peripheral registers
  * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
- * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
+ * @param timer_sel LEDC timer index (0-3), select from ledc_timer_t
+ * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-static inline void ledc_ll_ls_channel_update(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num)
+static inline void ledc_ll_get_max_duty(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t *max_duty)
 {
-    hw->channel_group[speed_mode].channel[channel_num].conf0.low_speed_update = 1;
+    *max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.duty_resolution));
 }
 
 /**
- * @brief Get LEDC max duty
+ * @brief Update channel configure when select low speed mode
  *
  * @param hw Beginning address of the peripheral registers
  * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
  * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
- * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-static inline void ledc_ll_get_max_duty(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t *max_duty)
+static inline void ledc_ll_ls_channel_update(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num)
 {
-    int timer_sel = hw->channel_group[speed_mode].channel[channel_num].conf0.timer_sel;
-    *max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.duty_resolution));
+    hw->channel_group[speed_mode].channel[channel_num].conf0.low_speed_update = 1;
 }
 
 /**
@@ -330,21 +329,6 @@ static inline void ledc_ll_set_duty_direction(ledc_dev_t *hw, ledc_mode_t speed_
     hw->channel_group[speed_mode].channel[channel_num].conf1.duty_inc = duty_direction;
 }
 
-/**
- * @brief Get LEDC duty change direction
- *
- * @param hw Beginning address of the peripheral registers
- * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
- * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
- * @param duty_direction Pointer to accept the LEDC duty change direction, increase or decrease
- *
- * @return None
- */
-static inline void ledc_ll_get_duty_direction(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, ledc_duty_direction_t *duty_direction)
-{
-    *duty_direction = hw->channel_group[speed_mode].channel[channel_num].conf1.duty_inc;
-}
-
 /**
  * @brief Set the number of increased or decreased times
  *
@@ -390,6 +374,30 @@ static inline void ledc_ll_set_duty_scale(ledc_dev_t *hw, ledc_mode_t speed_mode
     hw->channel_group[speed_mode].channel[channel_num].conf1.duty_scale = duty_scale;
 }
 
+/**
+ * @brief Function to set fade parameters all-in-one
+ *
+ * @param hw Beginning address of the peripheral registers
+ * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
+ * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
+ * @param dir LEDC duty change direction, increase or decrease
+ * @param cycle The duty cycles
+ * @param scale The step scale
+ * @param step The number of increased or decreased times
+ *
+ * @return None
+ */
+static inline void ledc_ll_set_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t dir, uint32_t cycle, uint32_t scale, uint32_t step)
+{
+    typeof(hw->channel_group[speed_mode].channel[channel_num].conf1) conf1_reg;
+    conf1_reg.val = hw->channel_group[speed_mode].channel[channel_num].conf1.val;
+    conf1_reg.duty_inc = dir;
+    conf1_reg.duty_num = step;
+    conf1_reg.duty_cycle = cycle;
+    conf1_reg.duty_scale = scale;
+    hw->channel_group[speed_mode].channel[channel_num].conf1.val = conf1_reg.val;
+}
+
 /**
  * @brief Set the output enable
  *

+ 32 - 24
components/hal/esp32c2/include/hal/ledc_ll.h

@@ -211,33 +211,32 @@ static inline void ledc_ll_get_duty_resolution(ledc_dev_t *hw, ledc_mode_t speed
 }
 
 /**
- * @brief Update channel configure when select low speed mode
+ * @brief Get LEDC max duty
  *
  * @param hw Beginning address of the peripheral registers
  * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
- * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
+ * @param timer_sel LEDC timer index (0-3), select from ledc_timer_t
+ * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-static inline void ledc_ll_ls_channel_update(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num)
+static inline void ledc_ll_get_max_duty(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t *max_duty)
 {
-    hw->channel_group[speed_mode].channel[channel_num].conf0.para_up = 1;
+    *max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.duty_res));
 }
 
 /**
- * @brief Get LEDC max duty
+ * @brief Update channel configure when select low speed mode
  *
  * @param hw Beginning address of the peripheral registers
  * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
  * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
- * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-static inline void ledc_ll_get_max_duty(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t *max_duty)
+static inline void ledc_ll_ls_channel_update(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num)
 {
-    uint32_t timer_sel = hw->channel_group[speed_mode].channel[channel_num].conf0.timer_sel;
-    *max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.duty_res));
+    hw->channel_group[speed_mode].channel[channel_num].conf0.para_up = 1;
 }
 
 /**
@@ -315,21 +314,6 @@ static inline void ledc_ll_set_duty_direction(ledc_dev_t *hw, ledc_mode_t speed_
     hw->channel_group[speed_mode].channel[channel_num].conf1.duty_inc = duty_direction;
 }
 
-/**
- * @brief Get LEDC duty change direction
- *
- * @param hw Beginning address of the peripheral registers
- * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
- * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
- * @param duty_direction Pointer to accept the LEDC duty change direction, increase or decrease
- *
- * @return None
- */
-static inline void ledc_ll_get_duty_direction(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, ledc_duty_direction_t *duty_direction)
-{
-    *duty_direction = hw->channel_group[speed_mode].channel[channel_num].conf1.duty_inc;
-}
-
 /**
  * @brief Set the number of increased or decreased times
  *
@@ -375,6 +359,30 @@ static inline void ledc_ll_set_duty_scale(ledc_dev_t *hw, ledc_mode_t speed_mode
     hw->channel_group[speed_mode].channel[channel_num].conf1.duty_scale = duty_scale;
 }
 
+/**
+ * @brief Function to set fade parameters all-in-one
+ *
+ * @param hw Beginning address of the peripheral registers
+ * @param speed_mode LEDC speed_mode, low-speed mode only
+ * @param channel_num LEDC channel index (0-5), select from ledc_channel_t
+ * @param dir LEDC duty change direction, increase or decrease
+ * @param cycle The duty cycles
+ * @param scale The step scale
+ * @param step The number of increased or decreased times
+ *
+ * @return None
+ */
+static inline void ledc_ll_set_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t dir, uint32_t cycle, uint32_t scale, uint32_t step)
+{
+    typeof(hw->channel_group[speed_mode].channel[channel_num].conf1) conf1_reg;
+    conf1_reg.val = hw->channel_group[speed_mode].channel[channel_num].conf1.val;
+    conf1_reg.duty_inc = dir;
+    conf1_reg.duty_num = step;
+    conf1_reg.duty_cycle = cycle;
+    conf1_reg.duty_scale = scale;
+    hw->channel_group[speed_mode].channel[channel_num].conf1.val = conf1_reg.val;
+}
+
 /**
  * @brief Set the output enable
  *

+ 32 - 24
components/hal/esp32c3/include/hal/ledc_ll.h

@@ -211,33 +211,32 @@ static inline void ledc_ll_get_duty_resolution(ledc_dev_t *hw, ledc_mode_t speed
 }
 
 /**
- * @brief Update channel configure when select low speed mode
+ * @brief Get LEDC max duty
  *
  * @param hw Beginning address of the peripheral registers
  * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
- * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
+ * @param timer_sel LEDC timer index (0-3), select from ledc_timer_t
+ * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-static inline void ledc_ll_ls_channel_update(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num)
+static inline void ledc_ll_get_max_duty(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t *max_duty)
 {
-    hw->channel_group[speed_mode].channel[channel_num].conf0.low_speed_update = 1;
+    *max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.duty_resolution));
 }
 
 /**
- * @brief Get LEDC max duty
+ * @brief Update channel configure when select low speed mode
  *
  * @param hw Beginning address of the peripheral registers
  * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
  * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
- * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-static inline void ledc_ll_get_max_duty(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t *max_duty)
+static inline void ledc_ll_ls_channel_update(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num)
 {
-    uint32_t timer_sel = hw->channel_group[speed_mode].channel[channel_num].conf0.timer_sel;
-    *max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.duty_resolution));
+    hw->channel_group[speed_mode].channel[channel_num].conf0.low_speed_update = 1;
 }
 
 /**
@@ -315,21 +314,6 @@ static inline void ledc_ll_set_duty_direction(ledc_dev_t *hw, ledc_mode_t speed_
     hw->channel_group[speed_mode].channel[channel_num].conf1.duty_inc = duty_direction;
 }
 
-/**
- * @brief Get LEDC duty change direction
- *
- * @param hw Beginning address of the peripheral registers
- * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
- * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
- * @param duty_direction Pointer to accept the LEDC duty change direction, increase or decrease
- *
- * @return None
- */
-static inline void ledc_ll_get_duty_direction(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, ledc_duty_direction_t *duty_direction)
-{
-    *duty_direction = hw->channel_group[speed_mode].channel[channel_num].conf1.duty_inc;
-}
-
 /**
  * @brief Set the number of increased or decreased times
  *
@@ -375,6 +359,30 @@ static inline void ledc_ll_set_duty_scale(ledc_dev_t *hw, ledc_mode_t speed_mode
     hw->channel_group[speed_mode].channel[channel_num].conf1.duty_scale = duty_scale;
 }
 
+/**
+ * @brief Function to set fade parameters all-in-one
+ *
+ * @param hw Beginning address of the peripheral registers
+ * @param speed_mode LEDC speed_mode, low-speed mode only
+ * @param channel_num LEDC channel index (0-5), select from ledc_channel_t
+ * @param dir LEDC duty change direction, increase or decrease
+ * @param cycle The duty cycles
+ * @param scale The step scale
+ * @param step The number of increased or decreased times
+ *
+ * @return None
+ */
+static inline void ledc_ll_set_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t dir, uint32_t cycle, uint32_t scale, uint32_t step)
+{
+    typeof(hw->channel_group[speed_mode].channel[channel_num].conf1) conf1_reg;
+    conf1_reg.val = hw->channel_group[speed_mode].channel[channel_num].conf1.val;
+    conf1_reg.duty_inc = dir;
+    conf1_reg.duty_num = step;
+    conf1_reg.duty_cycle = cycle;
+    conf1_reg.duty_scale = scale;
+    hw->channel_group[speed_mode].channel[channel_num].conf1.val = conf1_reg.val;
+}
+
 /**
  * @brief Set the output enable
  *

+ 28 - 25
components/hal/esp32c6/include/hal/ledc_ll.h

@@ -232,33 +232,32 @@ static inline void ledc_ll_get_duty_resolution(ledc_dev_t *hw, ledc_mode_t speed
 }
 
 /**
- * @brief Update channel configure when select low speed mode
+ * @brief Get LEDC max duty
  *
  * @param hw Beginning address of the peripheral registers
  * @param speed_mode LEDC speed_mode, low-speed mode only
- * @param channel_num LEDC channel index (0-5), select from ledc_channel_t
+ * @param timer_sel LEDC timer index (0-3), select from ledc_timer_t
+ * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-static inline void ledc_ll_ls_channel_update(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num)
+static inline void ledc_ll_get_max_duty(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t *max_duty)
 {
-    hw->channel_group[speed_mode].channel[channel_num].conf0.para_up = 1;
+    *max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.duty_res));
 }
 
 /**
- * @brief Get LEDC max duty
+ * @brief Update channel configure when select low speed mode
  *
  * @param hw Beginning address of the peripheral registers
  * @param speed_mode LEDC speed_mode, low-speed mode only
  * @param channel_num LEDC channel index (0-5), select from ledc_channel_t
- * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-static inline void ledc_ll_get_max_duty(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t *max_duty)
+static inline void ledc_ll_ls_channel_update(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num)
 {
-    uint32_t timer_sel = hw->channel_group[speed_mode].channel[channel_num].conf0.timer_sel;
-    *max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.duty_res));
+    hw->channel_group[speed_mode].channel[channel_num].conf0.para_up = 1;
 }
 
 /**
@@ -336,21 +335,6 @@ static inline void ledc_ll_set_duty_direction(ledc_dev_t *hw, ledc_mode_t speed_
     hw->channel_gamma_group[speed_mode].channel[channel_num].wr.gamma_duty_inc = duty_direction;
 }
 
-/**
- * @brief Get LEDC duty change direction
- *
- * @param hw Beginning address of the peripheral registers
- * @param speed_mode LEDC speed_mode, low-speed mode only
- * @param channel_num LEDC channel index (0-5), select from ledc_channel_t
- * @param duty_direction Pointer to accept the LEDC duty change direction, increase or decrease
- *
- * @return None
- */
-static inline void ledc_ll_get_duty_direction(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, ledc_duty_direction_t *duty_direction)
-{
-    *duty_direction = (ledc_duty_direction_t)(hw->channel_gamma_group[speed_mode].channel[channel_num].wr.gamma_duty_inc);
-}
-
 /**
  * @brief Set the number of increased or decreased times
  *
@@ -396,6 +380,25 @@ static inline void ledc_ll_set_duty_scale(ledc_dev_t *hw, ledc_mode_t speed_mode
     hw->channel_gamma_group[speed_mode].channel[channel_num].wr.gamma_scale = duty_scale;
 }
 
+/**
+ * @brief Function to set fade parameters all-in-one
+ *
+ * @param hw Beginning address of the peripheral registers
+ * @param speed_mode LEDC speed_mode, low-speed mode only
+ * @param channel_num LEDC channel index (0-5), select from ledc_channel_t
+ * @param dir LEDC duty change direction, increase or decrease
+ * @param cycle The duty cycles
+ * @param scale The step scale
+ * @param step The number of increased or decreased times
+ *
+ * @return None
+ */
+static inline void ledc_ll_set_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t dir, uint32_t cycle, uint32_t scale, uint32_t step)
+{
+    uint32_t val = (dir << LEDC_CH0_GAMMA_DUTY_INC_S) | (cycle << LEDC_CH0_GAMMA_DUTY_CYCLE_S) | (scale << LEDC_CH0_GAMMA_SCALE_S) | (step << LEDC_CH0_GAMMA_DUTY_NUM_S);
+    hw->channel_gamma_group[speed_mode].channel[channel_num].wr.val = val;
+}
+
 /**
  * @brief Set the range number of the specified duty configurations to be written from gamma_wr register to gamma ram
  *
@@ -469,7 +472,7 @@ static inline void ledc_ll_set_duty_range_rd_addr(ledc_dev_t *hw, ledc_mode_t sp
  *
  * @return None
  */
-static inline void ledc_ll_get_duty_param(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t *dir, uint32_t *cycle, uint32_t *scale, uint32_t *step)
+static inline void ledc_ll_get_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t *dir, uint32_t *cycle, uint32_t *scale, uint32_t *step)
 {
     uint32_t val = hw->channel_gamma_group[speed_mode].channel[channel_num].rd_data.gamma_rd_data;
     *dir = (val & LEDC_CH0_GAMMA_DUTY_INC_M) >> LEDC_CH0_GAMMA_DUTY_INC_S;

+ 28 - 25
components/hal/esp32h2/include/hal/ledc_ll.h

@@ -230,33 +230,32 @@ static inline void ledc_ll_get_duty_resolution(ledc_dev_t *hw, ledc_mode_t speed
 }
 
 /**
- * @brief Update channel configure when select low speed mode
+ * @brief Get LEDC max duty
  *
  * @param hw Beginning address of the peripheral registers
  * @param speed_mode LEDC speed_mode, low-speed mode only
- * @param channel_num LEDC channel index (0-5), select from ledc_channel_t
+ * @param timer_sel LEDC timer index (0-3), select from ledc_timer_t
+ * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-static inline void ledc_ll_ls_channel_update(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num)
+static inline void ledc_ll_get_max_duty(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t *max_duty)
 {
-    hw->channel_group[speed_mode].channel[channel_num].conf0.para_up = 1;
+    *max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.duty_res));
 }
 
 /**
- * @brief Get LEDC max duty
+ * @brief Update channel configure when select low speed mode
  *
  * @param hw Beginning address of the peripheral registers
  * @param speed_mode LEDC speed_mode, low-speed mode only
  * @param channel_num LEDC channel index (0-5), select from ledc_channel_t
- * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-static inline void ledc_ll_get_max_duty(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t *max_duty)
+static inline void ledc_ll_ls_channel_update(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num)
 {
-    uint32_t timer_sel = hw->channel_group[speed_mode].channel[channel_num].conf0.timer_sel;
-    *max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.duty_res));
+    hw->channel_group[speed_mode].channel[channel_num].conf0.para_up = 1;
 }
 
 /**
@@ -334,21 +333,6 @@ static inline void ledc_ll_set_duty_direction(ledc_dev_t *hw, ledc_mode_t speed_
     hw->channel_gamma_group[speed_mode].channel[channel_num].wr.gamma_duty_inc = duty_direction;
 }
 
-/**
- * @brief Get LEDC duty change direction
- *
- * @param hw Beginning address of the peripheral registers
- * @param speed_mode LEDC speed_mode, low-speed mode only
- * @param channel_num LEDC channel index (0-5), select from ledc_channel_t
- * @param duty_direction Pointer to accept the LEDC duty change direction, increase or decrease
- *
- * @return None
- */
-static inline void ledc_ll_get_duty_direction(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, ledc_duty_direction_t *duty_direction)
-{
-    *duty_direction = (ledc_duty_direction_t)(hw->channel_gamma_group[speed_mode].channel[channel_num].wr.gamma_duty_inc);
-}
-
 /**
  * @brief Set the number of increased or decreased times
  *
@@ -394,6 +378,25 @@ static inline void ledc_ll_set_duty_scale(ledc_dev_t *hw, ledc_mode_t speed_mode
     hw->channel_gamma_group[speed_mode].channel[channel_num].wr.gamma_scale = duty_scale;
 }
 
+/**
+ * @brief Function to set fade parameters all-in-one
+ *
+ * @param hw Beginning address of the peripheral registers
+ * @param speed_mode LEDC speed_mode, low-speed mode only
+ * @param channel_num LEDC channel index (0-5), select from ledc_channel_t
+ * @param dir LEDC duty change direction, increase or decrease
+ * @param cycle The duty cycles
+ * @param scale The step scale
+ * @param step The number of increased or decreased times
+ *
+ * @return None
+ */
+static inline void ledc_ll_set_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t dir, uint32_t cycle, uint32_t scale, uint32_t step)
+{
+    uint32_t val = (dir << LEDC_CH0_GAMMA_DUTY_INC_S) | (cycle << LEDC_CH0_GAMMA_DUTY_CYCLE_S) | (scale << LEDC_CH0_GAMMA_SCALE_S) | (step << LEDC_CH0_GAMMA_DUTY_NUM_S);
+    hw->channel_gamma_group[speed_mode].channel[channel_num].wr.val = val;
+}
+
 /**
  * @brief Set the range number of the specified duty configurations to be written from gamma_wr register to gamma ram
  *
@@ -467,7 +470,7 @@ static inline void ledc_ll_set_duty_range_rd_addr(ledc_dev_t *hw, ledc_mode_t sp
  *
  * @return None
  */
-static inline void ledc_ll_get_duty_param(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t *dir, uint32_t *cycle, uint32_t *scale, uint32_t *step)
+static inline void ledc_ll_get_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t *dir, uint32_t *cycle, uint32_t *scale, uint32_t *step)
 {
     uint32_t val = hw->channel_gamma_group[speed_mode].channel[channel_num].rd_data.gamma_rd_data;
     *dir = (val & LEDC_CH0_GAMMA_DUTY_INC_M) >> LEDC_CH0_GAMMA_DUTY_INC_S;

+ 32 - 24
components/hal/esp32s2/include/hal/ledc_ll.h

@@ -243,33 +243,32 @@ static inline void ledc_ll_get_duty_resolution(ledc_dev_t *hw, ledc_mode_t speed
 }
 
 /**
- * @brief Update channel configure when select low speed mode
+ * @brief Get LEDC max duty
  *
  * @param hw Beginning address of the peripheral registers
  * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
- * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
+ * @param timer_sel LEDC timer index (0-3), select from ledc_timer_t
+ * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-static inline void ledc_ll_ls_channel_update(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num)
+static inline void ledc_ll_get_max_duty(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t *max_duty)
 {
-    hw->channel_group[speed_mode].channel[channel_num].conf0.low_speed_update = 1;
+    *max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.duty_resolution));
 }
 
 /**
- * @brief Get LEDC max duty
+ * @brief Update channel configure when select low speed mode
  *
  * @param hw Beginning address of the peripheral registers
  * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
  * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
- * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-static inline void ledc_ll_get_max_duty(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t *max_duty)
+static inline void ledc_ll_ls_channel_update(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num)
 {
-    uint32_t timer_sel = hw->channel_group[speed_mode].channel[channel_num].conf0.timer_sel;
-    *max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.duty_resolution));
+    hw->channel_group[speed_mode].channel[channel_num].conf0.low_speed_update = 1;
 }
 
 /**
@@ -347,21 +346,6 @@ static inline void ledc_ll_set_duty_direction(ledc_dev_t *hw, ledc_mode_t speed_
     hw->channel_group[speed_mode].channel[channel_num].conf1.duty_inc = duty_direction;
 }
 
-/**
- * @brief Get LEDC duty change direction
- *
- * @param hw Beginning address of the peripheral registers
- * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
- * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
- * @param duty_direction Pointer to accept the LEDC duty change direction, increase or decrease
- *
- * @return None
- */
-static inline void ledc_ll_get_duty_direction(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, ledc_duty_direction_t *duty_direction)
-{
-    *duty_direction = hw->channel_group[speed_mode].channel[channel_num].conf1.duty_inc;
-}
-
 /**
  * @brief Set the number of increased or decreased times
  *
@@ -407,6 +391,30 @@ static inline void ledc_ll_set_duty_scale(ledc_dev_t *hw, ledc_mode_t speed_mode
     hw->channel_group[speed_mode].channel[channel_num].conf1.duty_scale = duty_scale;
 }
 
+/**
+ * @brief Function to set fade parameters all-in-one
+ *
+ * @param hw Beginning address of the peripheral registers
+ * @param speed_mode LEDC speed_mode, low-speed mode only
+ * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
+ * @param dir LEDC duty change direction, increase or decrease
+ * @param cycle The duty cycles
+ * @param scale The step scale
+ * @param step The number of increased or decreased times
+ *
+ * @return None
+ */
+static inline void ledc_ll_set_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t dir, uint32_t cycle, uint32_t scale, uint32_t step)
+{
+    typeof(hw->channel_group[speed_mode].channel[channel_num].conf1) conf1_reg;
+    conf1_reg.val = hw->channel_group[speed_mode].channel[channel_num].conf1.val;
+    conf1_reg.duty_inc = dir;
+    conf1_reg.duty_num = step;
+    conf1_reg.duty_cycle = cycle;
+    conf1_reg.duty_scale = scale;
+    hw->channel_group[speed_mode].channel[channel_num].conf1.val = conf1_reg.val;
+}
+
 /**
  * @brief Set the output enable
  *

+ 32 - 24
components/hal/esp32s3/include/hal/ledc_ll.h

@@ -212,33 +212,32 @@ static inline void ledc_ll_get_duty_resolution(ledc_dev_t *hw, ledc_mode_t speed
 }
 
 /**
- * @brief Update channel configure when select low speed mode
+ * @brief Get LEDC max duty
  *
  * @param hw Beginning address of the peripheral registers
  * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
- * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
+ * @param timer_sel LEDC timer index (0-3), select from ledc_timer_t
+ * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-static inline void ledc_ll_ls_channel_update(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num)
+static inline void ledc_ll_get_max_duty(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t *max_duty)
 {
-    hw->channel_group[speed_mode].channel[channel_num].conf0.low_speed_update = 1;
+    *max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.duty_resolution));
 }
 
 /**
- * @brief Get LEDC max duty
+ * @brief Update channel configure when select low speed mode
  *
  * @param hw Beginning address of the peripheral registers
  * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
  * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
- * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-static inline void ledc_ll_get_max_duty(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t *max_duty)
+static inline void ledc_ll_ls_channel_update(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num)
 {
-    uint32_t timer_sel = hw->channel_group[speed_mode].channel[channel_num].conf0.timer_sel;
-    *max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.duty_resolution));
+    hw->channel_group[speed_mode].channel[channel_num].conf0.low_speed_update = 1;
 }
 
 /**
@@ -316,21 +315,6 @@ static inline void ledc_ll_set_duty_direction(ledc_dev_t *hw, ledc_mode_t speed_
     hw->channel_group[speed_mode].channel[channel_num].conf1.duty_inc = duty_direction;
 }
 
-/**
- * @brief Get LEDC duty change direction
- *
- * @param hw Beginning address of the peripheral registers
- * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode
- * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
- * @param duty_direction Pointer to accept the LEDC duty change direction, increase or decrease
- *
- * @return None
- */
-static inline void ledc_ll_get_duty_direction(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, ledc_duty_direction_t *duty_direction)
-{
-    *duty_direction = hw->channel_group[speed_mode].channel[channel_num].conf1.duty_inc;
-}
-
 /**
  * @brief Set the number of increased or decreased times
  *
@@ -376,6 +360,30 @@ static inline void ledc_ll_set_duty_scale(ledc_dev_t *hw, ledc_mode_t speed_mode
     hw->channel_group[speed_mode].channel[channel_num].conf1.duty_scale = duty_scale;
 }
 
+/**
+ * @brief Function to set fade parameters all-in-one
+ *
+ * @param hw Beginning address of the peripheral registers
+ * @param speed_mode LEDC speed_mode, low-speed mode only
+ * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
+ * @param dir LEDC duty change direction, increase or decrease
+ * @param cycle The duty cycles
+ * @param scale The step scale
+ * @param step The number of increased or decreased times
+ *
+ * @return None
+ */
+static inline void ledc_ll_set_fade_param(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t dir, uint32_t cycle, uint32_t scale, uint32_t step)
+{
+    typeof(hw->channel_group[speed_mode].channel[channel_num].conf1) conf1_reg;
+    conf1_reg.val = hw->channel_group[speed_mode].channel[channel_num].conf1.val;
+    conf1_reg.duty_inc = dir;
+    conf1_reg.duty_num = step;
+    conf1_reg.duty_cycle = cycle;
+    conf1_reg.duty_scale = scale;
+    hw->channel_group[speed_mode].channel[channel_num].conf1.val = conf1_reg.val;
+}
+
 /**
  * @brief Set the output enable
  *

+ 17 - 2
components/hal/include/hal/ledc_hal.h

@@ -157,12 +157,12 @@ typedef struct {
  * @brief Get LEDC max duty
  *
  * @param hal Context of the HAL layer
- * @param channel_num LEDC channel index (0-7), select from ledc_channel_t
+ * @param channel_num LEDC timer index (0-3), select from ledc_timer_t
  * @param max_duty Pointer to accept the max duty
  *
  * @return None
  */
-#define ledc_hal_get_max_duty(hal, channel_num, max_duty)  ledc_ll_get_max_duty((hal)->dev, (hal)->speed_mode, channel_num, max_duty)
+#define ledc_hal_get_max_duty(hal, timer_sel, max_duty)  ledc_ll_get_max_duty((hal)->dev, (hal)->speed_mode, timer_sel, max_duty)
 
 /**
  * @brief Get LEDC hpoint value
@@ -338,6 +338,21 @@ void ledc_hal_set_duty_cycle(ledc_hal_context_t *hal, ledc_channel_t channel_num
  */
 void ledc_hal_set_duty_scale(ledc_hal_context_t *hal, ledc_channel_t channel_num, uint32_t duty_scale);
 
+/**
+ * @brief Function to set fade parameters all-in-one
+ *
+ * @param hal Context of the HAL layer
+ * @param channel_num LEDC channel index, select from ledc_channel_t
+ * @param range Range index
+ * @param dir LEDC duty change direction, increase or decrease
+ * @param cycle The duty cycles
+ * @param scale The step scale
+ * @param step The number of increased or decreased times
+ *
+ * @return None
+ */
+void ledc_hal_set_fade_param(const ledc_hal_context_t *hal, ledc_channel_t channel_num, uint32_t range, uint32_t dir, uint32_t cycle, uint32_t scale, uint32_t step);
+
 #if SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
 /**
  * @brief Set the range number of the specified duty configurations to be written from gamma_wr register to gamma ram

+ 3 - 5
components/hal/ledc_hal.c

@@ -6,11 +6,7 @@
 
 // The HAL layer for LEDC (common part)
 
-#include "esp_attr.h"
 #include "hal/ledc_hal.h"
-#include "soc/soc_caps.h"
-#include "sdkconfig.h"
-#include "hal/assert.h"
 #include "esp_rom_sys.h"
 
 void ledc_hal_init(ledc_hal_context_t *hal, ledc_mode_t speed_mode)
@@ -64,9 +60,11 @@ void ledc_hal_get_clk_cfg(ledc_hal_context_t *hal, ledc_timer_t timer_sel, ledc_
 void ledc_hal_get_fade_param(ledc_hal_context_t *hal, ledc_channel_t channel_num, uint32_t range, uint32_t *dir, uint32_t *cycle, uint32_t *scale, uint32_t *step)
 {
     ledc_ll_set_duty_range_rd_addr(hal->dev, hal->speed_mode, channel_num, range);
+#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2
     // On ESP32C6/H2, gamma ram read/write has the APB and LEDC clock domain sync issue
     // To make sure the parameter read is from the correct gamma ram addr, add a delay in between to ensure syncronization
     esp_rom_delay_us(5);
-    ledc_ll_get_duty_param(hal->dev, hal->speed_mode, channel_num, dir, cycle, scale, step);
+#endif
+    ledc_ll_get_fade_param(hal->dev, hal->speed_mode, channel_num, dir, cycle, scale, step);
 }
 #endif

+ 28 - 1
components/hal/ledc_hal_iram.c

@@ -7,8 +7,9 @@
 // The HAL layer for LEDC (common part, in iram)
 // make these functions in a seperate file to make sure all LL functions are in the IRAM.
 
-#include "esp_attr.h"
 #include "hal/ledc_hal.h"
+#include "hal/assert.h"
+#include "esp_rom_sys.h"
 
 void ledc_hal_ls_channel_update(ledc_hal_context_t *hal, ledc_channel_t channel_num)
 {
@@ -55,6 +56,32 @@ void ledc_hal_set_duty_scale(ledc_hal_context_t *hal, ledc_channel_t channel_num
     ledc_ll_set_duty_scale(hal->dev, hal->speed_mode, channel_num, duty_scale);
 }
 
+void ledc_hal_set_fade_param(const ledc_hal_context_t *hal, ledc_channel_t channel_num, uint32_t range, uint32_t dir, uint32_t cycle, uint32_t scale, uint32_t step)
+{
+#if SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
+    HAL_ASSERT(range < SOC_LEDC_GAMMA_CURVE_FADE_RANGE_MAX);
+
+#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2
+    // To workaround sync issue (C6, H2)
+    // This is to ensure the fade param write to the gamma_wr register would not mess up the last wr_addr
+    ledc_ll_set_duty_range_wr_addr(hal->dev, hal->speed_mode, channel_num, range);
+    esp_rom_delay_us(5);
+#endif
+
+    ledc_ll_set_fade_param(hal->dev, hal->speed_mode, channel_num, dir, cycle, scale, step);
+    ledc_ll_set_duty_range_wr_addr(hal->dev, hal->speed_mode, channel_num, range);
+#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2
+    // To workaround sync issue (C6, H2)
+    // This is to ensure the fade param in gamma_wr register can be written to the correct wr_addr
+    esp_rom_delay_us(5);
+#endif
+
+#else // !SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
+    HAL_ASSERT(range == 0);
+    ledc_ll_set_fade_param(hal->dev, hal->speed_mode, channel_num, dir, cycle, scale, step);
+#endif // SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
+}
+
 #if SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
 void ledc_hal_set_duty_range_wr_addr(ledc_hal_context_t *hal, ledc_channel_t channel_num, uint32_t duty_range)
 {

+ 9 - 1
docs/en/api-reference/peripherals/ledc.rst

@@ -60,7 +60,7 @@ Setting the timer is done by calling the function :cpp:func:`ledc_timer_config`
     :esp32:     - Speed mode :cpp:type:`ledc_mode_t`
     :not esp32: - Speed mode (value must be ``LEDC_LOW_SPEED_MODE``)
     - Timer number :cpp:type:`ledc_timer_t`
-    - PWM signal frequency
+    - PWM signal frequency in Hz
     - Resolution of PWM duty
     - Source clock :cpp:type:`ledc_clk_cfg_t`
 
@@ -222,6 +222,14 @@ The source clock can also limit the PWM frequency. The higher the source clock f
 
         2. For {IDF_TARGET_NAME}, all timers share one clock source. In other words, it is impossible to use different clock sources for different timers.
 
+When a timer is no longer needed by any channel, it can be deconfigured by calling the same function :cpp:func:`ledc_timer_config`. The configuration structure :cpp:type:`ledc_timer_config_t` passes in should be:
+
+-  :cpp:member:`ledc_timer_config_t::speed_mode` The speed mode of the timer which wants to be deconfigured belongs to (:cpp:type:`ledc_mode_t`)
+
+-  :cpp:member:`ledc_timer_config_t::timer_num` The ID of the timers which wants to be deconfigured (:cpp:type:`ledc_timer_t`)
+
+-  :cpp:member:`ledc_timer_config_t::deconfigure` Set this to true so that the timer specified can be deconfigured
+
 
 .. _ledc-api-configure-channel:
 

+ 9 - 1
docs/zh_CN/api-reference/peripherals/ledc.rst

@@ -60,7 +60,7 @@ LED PWM 控制器可在无需 CPU 干预的情况下自动改变占空比,实
     :esp32:     - 速度模式 :cpp:type:`ledc_mode_t`
     :not esp32: - 速度模式(值必须为 ``LEDC_LOW_SPEED_MODE``)
     - 定时器索引 :cpp:type:`ledc_timer_t`
-    - PWM 信号频率
+    - PWM 信号频率(Hz)
     - PWM 占空比分辨率
     - 时钟源 :cpp:type:`ledc_clk_cfg_t`
 
@@ -222,6 +222,14 @@ LED PWM 控制器可在无需 CPU 干预的情况下自动改变占空比,实
 
         2. {IDF_TARGET_NAME} 的所有定时器共用一个时钟源。因此 {IDF_TARGET_NAME} 不支持给不同的定时器配置不同的时钟源。
 
+当一个定时器不再被任何通道所需要时,可以通过调用相同的函数 :cpp:func:`ledc_timer_config` 来重置这个定时器。此时,函数入参的配置结构体需要指定:
+
+-  :cpp:member:`ledc_timer_config_t::speed_mode` 重置定时器的所属速度模式 (:cpp:type:`ledc_mode_t`)
+
+-  :cpp:member:`ledc_timer_config_t::timer_num` 重置定时器的索引 (:cpp:type:`ledc_timer_t`)
+
+-  :cpp:member:`ledc_timer_config_t::deconfigure` 将指定定时器重置必须配置此项为 true
+
 
 .. _ledc-api-configure-channel: