From 01547c4bd819bd28bc91211577e1bdc6d60c6eed Mon Sep 17 00:00:00 2001 From: songruojing Date: Wed, 28 Jul 2021 19:46:33 +0800 Subject: [PATCH 1/2] ledc: Simplify the procedure to perform a one-time duty update Avoid adding one extra fade cycle when performing a one-time duty update. Add some notes to ledc_get_duty and ledc_update_duty APIs, so that users are aware of when the new duty will be effective. Closes https://github.com/espressif/esp-idf/issues/7288 (cherry picked from commit e175086226405ca5dfd0b0cdde917b0ad8330827) --- components/driver/include/driver/ledc.h | 4 ++++ components/driver/ledc.c | 13 +++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index 2ffcbd74c8..b1e75c642c 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -61,6 +61,7 @@ esp_err_t ledc_timer_config(const ledc_timer_config_t* timer_conf); * @brief LEDC update channel parameters * @note Call this function to activate the LEDC updated parameters. * After ledc_set_duty, we need to call this function to update the settings. + * And the new LEDC parameters don't take effect until the next PWM cycle. * @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 @@ -178,6 +179,9 @@ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t /** * @brief LEDC get duty + * This function returns the duty at the present PWM cycle. + * You shouldn't expect the function to return the new duty in the same cycle of calling ledc_update_duty, + * because duty update doesn't take effect until the next cycle. * * @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-7), select from ledc_channel_t diff --git a/components/driver/ledc.c b/components/driver/ledc.c index 62d7ae7667..b46754dbad 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -472,8 +472,8 @@ esp_err_t ledc_set_duty_with_hpoint(ledc_mode_t speed_mode, ledc_channel_t chann hpoint, //uint32_t hpoint_val, duty, //uint32_t duty_val, 1, //uint32_t increase, - 1, //uint32_t duty_num, - 1, //uint32_t duty_cycle, + 0, //uint32_t duty_num, + 0, //uint32_t duty_cycle, 0 //uint32_t duty_scale ); _ledc_fade_hw_release(speed_mode, channel); @@ -492,8 +492,8 @@ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t LEDC_VAL_NO_CHANGE, duty, //uint32_t duty_val, 1, //uint32_t increase, - 1, //uint32_t duty_num, - 1, //uint32_t duty_cycle, + 0, //uint32_t duty_num, + 0, //uint32_t duty_cycle, 0 //uint32_t duty_scale ); _ledc_fade_hw_release(speed_mode, channel); @@ -842,12 +842,13 @@ esp_err_t ledc_set_duty_and_update(ledc_mode_t speed_mode, ledc_channel_t channe LEDC_ARG_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "speed_mode"); LEDC_ARG_CHECK(channel < LEDC_CHANNEL_MAX, "channel"); LEDC_ARG_CHECK(duty <= ledc_get_max_duty(speed_mode, channel), "target_duty"); + LEDC_ARG_CHECK(hpoint <= LEDC_HPOINT_VAL_MAX, "hpoint"); LEDC_CHECK(p_ledc_obj[speed_mode] != NULL, LEDC_NOT_INIT, ESP_ERR_INVALID_STATE); LEDC_CHECK(ledc_fade_channel_init_check(speed_mode, channel) == ESP_OK , LEDC_FADE_INIT_ERROR_STR, ESP_FAIL); _ledc_op_lock_acquire(speed_mode, channel); _ledc_fade_hw_acquire(speed_mode, channel); - _ledc_set_fade_with_step(speed_mode, channel, duty, 0, 1); - _ledc_fade_start(speed_mode, channel, LEDC_FADE_WAIT_DONE); + ledc_duty_config(speed_mode, channel, hpoint, duty, 1, 0, 0, 0); + ledc_update_duty(speed_mode, channel); _ledc_fade_hw_release(speed_mode, channel); _ledc_op_lock_release(speed_mode, channel); return ESP_OK; From 9e79d0e06f7dd9be431e5f5b6768260859460cde Mon Sep 17 00:00:00 2001 From: songruojing Date: Thu, 27 Jan 2022 16:12:15 +0800 Subject: [PATCH 2/2] ledc: Fix FADE_NO_WAIT mode concurrency problem. Add test cases for fade concurrency issue and fade timing check. Closes https://github.com/espressif/esp-idf/issues/6710 (cherry picked from commit be2ab0983216d4d298b219ea21d75433efdae98c) --- components/driver/include/driver/ledc.h | 3 +- components/driver/ledc.c | 28 ++-- components/driver/test/test_ledc.c | 210 ++++++++++++++---------- 3 files changed, 139 insertions(+), 102 deletions(-) diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index b1e75c642c..58f858b7e8 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -364,7 +364,8 @@ void ledc_fade_func_uninstall(void); * Other duty operations will have to wait until the fade operation has finished. * @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 - * @param fade_mode Whether to block until fading done. + * @param fade_mode Whether to block until fading done. See ledc_types.h ledc_fade_mode_t for more info. + * Note that this function will not return until fading to the target duty if LEDC_FADE_WAIT_DONE mode is selected. * * @return * - ESP_OK Success diff --git a/components/driver/ledc.c b/components/driver/ledc.c index b46754dbad..1e4bd56536 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -199,7 +199,7 @@ esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_ static IRAM_ATTR esp_err_t ledc_duty_config(ledc_mode_t speed_mode, ledc_channel_t channel, int hpoint_val, int duty_val, ledc_duty_direction_t duty_direction, uint32_t duty_num, uint32_t duty_cycle, uint32_t duty_scale) { - portENTER_CRITICAL(&ledc_spinlock); + portENTER_CRITICAL_SAFE(&ledc_spinlock); if (hpoint_val >= 0) { ledc_hal_set_hpoint(&(p_ledc_obj[speed_mode]->ledc_hal), channel, hpoint_val); } @@ -211,7 +211,7 @@ static IRAM_ATTR esp_err_t ledc_duty_config(ledc_mode_t speed_mode, ledc_channel 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_ls_channel_update(speed_mode, channel); - portEXIT_CRITICAL(&ledc_spinlock); + portEXIT_CRITICAL_SAFE(&ledc_spinlock); return ESP_OK; } @@ -573,9 +573,9 @@ void IRAM_ATTR ledc_fade_isr(void* arg) ledc_calc_fade_end_channel(&intr_status, &channel); // clear interrupt - portENTER_CRITICAL(&ledc_spinlock); + portENTER_CRITICAL_ISR(&ledc_spinlock); ledc_hal_clear_fade_end_intr_status(&(p_ledc_obj[speed_mode]->ledc_hal), channel); - portEXIT_CRITICAL(&ledc_spinlock); + portEXIT_CRITICAL_ISR(&ledc_spinlock); if (s_ledc_fade_rec[speed_mode][channel] == NULL) { //fade object not initialized yet. @@ -621,9 +621,9 @@ void IRAM_ATTR ledc_fade_isr(void* arg) 1, 0); } - portENTER_CRITICAL(&ledc_spinlock); + portENTER_CRITICAL_ISR(&ledc_spinlock); ledc_hal_set_duty_start(&(p_ledc_obj[speed_mode]->ledc_hal), channel, true); - portEXIT_CRITICAL(&ledc_spinlock); + portEXIT_CRITICAL_ISR(&ledc_spinlock); } } } @@ -763,7 +763,10 @@ static void _ledc_fade_start(ledc_mode_t speed_mode, ledc_channel_t channel, led ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_FADE_END); ledc_update_duty(speed_mode, channel); if (fade_mode == LEDC_FADE_WAIT_DONE) { - xSemaphoreTake(s_ledc_fade_rec[speed_mode][channel]->ledc_fade_sem, portMAX_DELAY); + // Waiting for fade done + _ledc_fade_hw_acquire(speed_mode, channel); + // Release hardware to support next time fade configure + _ledc_fade_hw_release(speed_mode, channel); } } @@ -805,7 +808,6 @@ esp_err_t ledc_fade_start(ledc_mode_t speed_mode, ledc_channel_t channel, ledc_f LEDC_CHECK(p_ledc_obj[speed_mode] != NULL, LEDC_NOT_INIT, ESP_ERR_INVALID_STATE); _ledc_fade_hw_acquire(speed_mode, channel); _ledc_fade_start(speed_mode, channel, fade_mode); - _ledc_fade_hw_release(speed_mode, channel); return ESP_OK; } @@ -844,13 +846,11 @@ esp_err_t ledc_set_duty_and_update(ledc_mode_t speed_mode, ledc_channel_t channe LEDC_ARG_CHECK(duty <= ledc_get_max_duty(speed_mode, channel), "target_duty"); LEDC_ARG_CHECK(hpoint <= LEDC_HPOINT_VAL_MAX, "hpoint"); LEDC_CHECK(p_ledc_obj[speed_mode] != NULL, LEDC_NOT_INIT, ESP_ERR_INVALID_STATE); - LEDC_CHECK(ledc_fade_channel_init_check(speed_mode, channel) == ESP_OK , LEDC_FADE_INIT_ERROR_STR, ESP_FAIL); - _ledc_op_lock_acquire(speed_mode, channel); + LEDC_CHECK(ledc_fade_channel_init_check(speed_mode, channel) == ESP_OK, LEDC_FADE_INIT_ERROR_STR, ESP_FAIL); _ledc_fade_hw_acquire(speed_mode, channel); ledc_duty_config(speed_mode, channel, hpoint, duty, 1, 0, 0, 0); ledc_update_duty(speed_mode, channel); _ledc_fade_hw_release(speed_mode, channel); - _ledc_op_lock_release(speed_mode, channel); return ESP_OK; } @@ -866,9 +866,6 @@ esp_err_t ledc_set_fade_time_and_start(ledc_mode_t speed_mode, ledc_channel_t ch _ledc_fade_hw_acquire(speed_mode, channel); _ledc_set_fade_with_time(speed_mode, channel, target_duty, max_fade_time_ms); _ledc_fade_start(speed_mode, channel, fade_mode); - if (fade_mode == LEDC_FADE_WAIT_DONE) { - _ledc_fade_hw_release(speed_mode, channel); - } _ledc_op_lock_release(speed_mode, channel); return ESP_OK; } @@ -887,9 +884,6 @@ esp_err_t ledc_set_fade_step_and_start(ledc_mode_t speed_mode, ledc_channel_t ch _ledc_fade_hw_acquire(speed_mode, channel); _ledc_set_fade_with_step(speed_mode, channel, target_duty, scale, cycle_num); _ledc_fade_start(speed_mode, channel, fade_mode); - if (fade_mode == LEDC_FADE_WAIT_DONE) { - _ledc_fade_hw_release(speed_mode, channel); - } _ledc_op_lock_release(speed_mode, channel); return ESP_OK; } diff --git a/components/driver/test/test_ledc.c b/components/driver/test/test_ledc.c index 64a37ca5e0..983ecba429 100644 --- a/components/driver/test/test_ledc.c +++ b/components/driver/test/test_ledc.c @@ -31,6 +31,12 @@ #define HIGHEST_LIMIT 10000 #define LOWEST_LIMIT -10000 +#ifdef CONFIG_IDF_TARGET_ESP32 +#define TEST_SPEED_MODE LEDC_HIGH_SPEED_MODE +#elif defined CONFIG_IDF_TARGET_ESP32S2 +#define TEST_SPEED_MODE LEDC_LOW_SPEED_MODE +#endif + #if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2) //no runners @@ -99,12 +105,39 @@ static void timer_frequency_test(ledc_channel_t channel, ledc_timer_bit_t timer_ frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 9000, 9025, 5); } +static void fade_setup(void) +{ + ledc_channel_config_t ledc_ch_config = { + .gpio_num = PULSE_IO, + .speed_mode = TEST_SPEED_MODE, + .channel = LEDC_CHANNEL_0, + .intr_type = LEDC_INTR_DISABLE, + .timer_sel = LEDC_TIMER_0, + .duty = 0, + .hpoint = 0, + }; + ledc_timer_config_t ledc_time_config = { + .speed_mode = TEST_SPEED_MODE, + .duty_resolution = LEDC_TIMER_13_BIT, + .timer_num = LEDC_TIMER_0, + .freq_hz = 2000, + .clk_cfg = LEDC_USE_APB_CLK, + }; + + TEST_ESP_OK(ledc_channel_config(&ledc_ch_config)); + TEST_ESP_OK(ledc_timer_config(&ledc_time_config)); + vTaskDelay(5 / portTICK_PERIOD_MS); + + //initialize fade service + TEST_ESP_OK(ledc_fade_func_install(0)); +} + static void timer_duty_set_get(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty) { TEST_ESP_OK(ledc_set_duty(speed_mode, channel, duty)); TEST_ESP_OK(ledc_update_duty(speed_mode, channel)); - vTaskDelay(1000 / portTICK_RATE_MS); - TEST_ASSERT_EQUAL_INT32(ledc_get_duty(speed_mode, channel), duty); + vTaskDelay(100 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL_INT32(duty, ledc_get_duty(speed_mode, channel)); } // use logic analyzer to view @@ -277,9 +310,9 @@ TEST_CASE("LEDC set and get frequency", "[ledc][test_env=UT_T1_LEDC][timeout=60] timer_frequency_test(LEDC_CHANNEL_0, LEDC_TIMER_13_BIT, LEDC_TIMER_3, LEDC_LOW_SPEED_MODE); } -// the duty need to be detected by waveform given by the logic analyzer -// can't get it directly, so set it "ignore" -TEST_CASE("LEDC set and get dut(with logic analyzer)", "[ledc][ignore]") +// duty should be manually checked from the waveform using a logic analyzer +// this test is enabled only for testting the settings +TEST_CASE("LEDC set and get duty", "[ledc]") { ledc_timer_t timer_list[4] = {LEDC_TIMER_0, LEDC_TIMER_1, LEDC_TIMER_2, LEDC_TIMER_3}; #ifdef CONFIG_IDF_TARGET_ESP32 @@ -331,7 +364,7 @@ TEST_CASE("LEDC timer set", "[ledc][test_env=UT_T1_LEDC]") count = wave_count(1000); TEST_ASSERT_UINT32_WITHIN(10, count, freq_get); - //set timer 3 as 500Hz, use APB_CLK + //set timer 0 as 500Hz, use APB_CLK TEST_ESP_OK(ledc_timer_set(test_speed_mode, LEDC_TIMER_0, 5000, 13, LEDC_APB_CLK)); TEST_ESP_OK(ledc_timer_rst(test_speed_mode, LEDC_TIMER_0)); TEST_ASSERT_EQUAL_INT32(ledc_get_freq(test_speed_mode, LEDC_TIMER_0), 500); @@ -403,102 +436,111 @@ TEST_CASE("LEDC timer pause and resume", "[ledc][test_env=UT_T1_LEDC]") TEST_ASSERT_UINT32_WITHIN(5, count, 5000); } -TEST_CASE("LEDC fade with time(logic analyzer)", "[ledc][test_env=UT_T1_LEDC]") +TEST_CASE("LEDC fade with time", "[ledc]") { -#ifdef CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE - return; -#endif + const ledc_mode_t test_speed_mode = TEST_SPEED_MODE; + fade_setup(); -#ifdef CONFIG_IDF_TARGET_ESP32 - const ledc_mode_t test_speed_mode = LEDC_HIGH_SPEED_MODE; -#elif defined CONFIG_IDF_TARGET_ESP32S2 - const ledc_mode_t test_speed_mode = LEDC_LOW_SPEED_MODE; -#endif - ledc_channel_config_t ledc_ch_config = { - .gpio_num = PULSE_IO, - .speed_mode = test_speed_mode, - .channel = LEDC_CHANNEL_0, - .intr_type = LEDC_INTR_DISABLE, - .timer_sel = LEDC_TIMER_0, - .duty = 0, - .hpoint = 0, - }; - TEST_ESP_OK(ledc_channel_config(&ledc_ch_config)); - - ledc_timer_config_t ledc_time_config = { - .speed_mode = test_speed_mode, - .duty_resolution = LEDC_TIMER_13_BIT, - .timer_num = LEDC_TIMER_0, - .freq_hz = 5000, - .clk_cfg = LEDC_USE_APB_CLK, - }; - TEST_ESP_OK(ledc_timer_config(&ledc_time_config)); - - //initialize fade service - TEST_ESP_OK(ledc_fade_func_install(0)); - - TEST_ESP_OK(ledc_set_fade_with_time(test_speed_mode, LEDC_CHANNEL_0, 4000, 1000)); + TEST_ESP_OK(ledc_set_fade_with_time(test_speed_mode, LEDC_CHANNEL_0, 4000, 200)); TEST_ESP_OK(ledc_fade_start(test_speed_mode, LEDC_CHANNEL_0, LEDC_FADE_WAIT_DONE)); - vTaskDelay(1000 / portTICK_RATE_MS); - TEST_ASSERT_EQUAL_INT32(ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0), 4000); + TEST_ASSERT_EQUAL_INT32(4000, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); - TEST_ESP_OK(ledc_set_fade_with_time(test_speed_mode, LEDC_CHANNEL_0, 0, 1000)); + TEST_ESP_OK(ledc_set_fade_with_time(test_speed_mode, LEDC_CHANNEL_0, 0, 200)); TEST_ESP_OK(ledc_fade_start(test_speed_mode, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT)); - vTaskDelay(1000 / portTICK_RATE_MS); - TEST_ASSERT_EQUAL_INT32(ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0), 0); + // duty should not be too far from initial value + TEST_ASSERT_INT32_WITHIN(20, 4000, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); + vTaskDelay(210 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT32(0, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); - //deinitial fade service + //deinitialize fade service ledc_fade_func_uninstall(); } -TEST_CASE("LEDC fade with step(logic analyzer)", "[ledc][test_env=UT_T1_LEDC]") +TEST_CASE("LEDC fade with step", "[ledc]") { -#ifdef CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE - return; -#endif + const ledc_mode_t test_speed_mode = TEST_SPEED_MODE; + fade_setup(); -#ifdef CONFIG_IDF_TARGET_ESP32 - const ledc_mode_t test_speed_mode = LEDC_HIGH_SPEED_MODE; -#elif defined CONFIG_IDF_TARGET_ESP32S2 - const ledc_mode_t test_speed_mode = LEDC_LOW_SPEED_MODE; -#endif - ledc_channel_config_t ledc_ch_config = { - .gpio_num = PULSE_IO, - .speed_mode = test_speed_mode, - .channel = LEDC_CHANNEL_0, - .intr_type = LEDC_INTR_DISABLE, - .timer_sel = LEDC_TIMER_0, - .duty = 0, - .hpoint = 0, - }; - TEST_ESP_OK(ledc_channel_config(&ledc_ch_config)); - - ledc_timer_config_t ledc_time_config = { - .speed_mode = test_speed_mode, - .duty_resolution = LEDC_TIMER_13_BIT, - .timer_num = LEDC_TIMER_0, - .freq_hz = 5000, - .clk_cfg = LEDC_USE_APB_CLK, - }; - TEST_ESP_OK(ledc_timer_config(&ledc_time_config)); - - //initialize fade service. - TEST_ESP_OK(ledc_fade_func_install(0)); - - TEST_ESP_OK(ledc_set_fade_with_step(test_speed_mode, LEDC_CHANNEL_0, 4000, 2, 1)); + TEST_ESP_OK(ledc_set_fade_with_step(test_speed_mode, LEDC_CHANNEL_0, 4000, 4, 1)); TEST_ESP_OK(ledc_fade_start(test_speed_mode, LEDC_CHANNEL_0, LEDC_FADE_WAIT_DONE)); - vTaskDelay(1000 / portTICK_RATE_MS); - TEST_ASSERT_EQUAL_INT32(ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0), 4000); + TEST_ASSERT_EQUAL_INT32(4000, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); - TEST_ESP_OK(ledc_set_fade_with_step(test_speed_mode, LEDC_CHANNEL_0, 0, 4, 2)); + TEST_ESP_OK(ledc_set_fade_with_step(test_speed_mode, LEDC_CHANNEL_0, 0, 4, 1)); TEST_ESP_OK(ledc_fade_start(test_speed_mode, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT)); - vTaskDelay(1000 / portTICK_RATE_MS); - TEST_ASSERT_EQUAL_INT32(ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0), 0); + // duty should not be too far from initial value + TEST_ASSERT_INT32_WITHIN(20, 4000, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); + vTaskDelay(525 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT32(0, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); //scaler=0 check TEST_ASSERT(ledc_set_fade_with_step(test_speed_mode, LEDC_CHANNEL_0, 4000, 0, 1) == ESP_ERR_INVALID_ARG); - //deinitial fade service + //deinitialize fade service + ledc_fade_func_uninstall(); +} + +TEST_CASE("LEDC fast switching duty with fade_wait_done", "[ledc]") +{ + const ledc_mode_t test_speed_mode = TEST_SPEED_MODE; + fade_setup(); + + // fade function will block until fading to the target duty + int64_t fade_start, fade_stop; + fade_start = esp_timer_get_time(); + TEST_ESP_OK(ledc_set_fade_with_time(test_speed_mode, LEDC_CHANNEL_0, 4000, 200)); + TEST_ESP_OK(ledc_fade_start(test_speed_mode, LEDC_CHANNEL_0, LEDC_FADE_WAIT_DONE)); + TEST_ASSERT_EQUAL_INT32(4000, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); + TEST_ESP_OK(ledc_set_fade_with_time(test_speed_mode, LEDC_CHANNEL_0, 1000, 150)); + TEST_ESP_OK(ledc_fade_start(test_speed_mode, LEDC_CHANNEL_0, LEDC_FADE_WAIT_DONE)); + TEST_ASSERT_EQUAL_INT32(1000, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); + fade_stop = esp_timer_get_time(); + int time_ms = (fade_stop - fade_start) / 1000; + TEST_ASSERT_TRUE(fabs(time_ms - 350) < 20); + + // next duty update will not take place until last fade reaches its target duty + TEST_ESP_OK(ledc_set_fade_with_time(test_speed_mode, LEDC_CHANNEL_0, 4000, 200)); + TEST_ESP_OK(ledc_fade_start(test_speed_mode, LEDC_CHANNEL_0, LEDC_FADE_WAIT_DONE)); + TEST_ASSERT_EQUAL_INT32(4000, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); + TEST_ESP_OK(ledc_set_duty(test_speed_mode, LEDC_CHANNEL_0, 500)); + TEST_ESP_OK(ledc_update_duty(test_speed_mode, LEDC_CHANNEL_0)); + vTaskDelay(5 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT32(500, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); + + //deinitialize fade service + ledc_fade_func_uninstall(); +} + +TEST_CASE("LEDC fast switching duty with fade_no_wait", "[ledc]") +{ + const ledc_mode_t test_speed_mode = TEST_SPEED_MODE; + fade_setup(); + + // fade function returns immediately, but next fade still needs to wait for last fade ends + int64_t fade_start, first_fade_complete; + fade_start = esp_timer_get_time(); + TEST_ESP_OK(ledc_set_fade_with_time(test_speed_mode, LEDC_CHANNEL_0, 4000, 200)); + TEST_ESP_OK(ledc_fade_start(test_speed_mode, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT)); + TEST_ASSERT_LESS_THAN(4000, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); + TEST_ESP_OK(ledc_set_fade_with_time(test_speed_mode, LEDC_CHANNEL_0, 1000, 150)); + TEST_ESP_OK(ledc_fade_start(test_speed_mode, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT)); + first_fade_complete = esp_timer_get_time(); + // duty should not be too far from first fade target duty + TEST_ASSERT_INT32_WITHIN(20, 4000, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); + int time_ms = (first_fade_complete - fade_start) / 1000; + TEST_ASSERT_TRUE(fabs(time_ms - 200) < 20); + vTaskDelay(158 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT32(1000, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); + + // next duty update will not take place until last fade reaches its target duty + TEST_ESP_OK(ledc_set_fade_with_time(test_speed_mode, LEDC_CHANNEL_0, 4000, 200)); + TEST_ESP_OK(ledc_fade_start(test_speed_mode, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT)); + TEST_ASSERT_LESS_THAN(4000, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); + TEST_ESP_OK(ledc_set_duty(test_speed_mode, LEDC_CHANNEL_0, 500)); + TEST_ESP_OK(ledc_update_duty(test_speed_mode, LEDC_CHANNEL_0)); + vTaskDelay(5 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT32(500, ledc_get_duty(test_speed_mode, LEDC_CHANNEL_0)); + + //deinitialize fade service ledc_fade_func_uninstall(); }