diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index 691379a3d..af7c6b807 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -16,9 +16,6 @@ #define _DRIVER_LEDC_H_ #include "esp_err.h" #include "soc/soc.h" -#include "soc/ledc_reg.h" -#include "soc/ledc_reg.h" -#include "soc/ledc_struct.h" #include "driver/gpio.h" #include "driver/periph_ctrl.h" #include "esp_intr_alloc.h" @@ -68,6 +65,7 @@ typedef enum { LEDC_CHANNEL_5, /*!< LEDC channel 5 */ LEDC_CHANNEL_6, /*!< LEDC channel 6 */ LEDC_CHANNEL_7, /*!< LEDC channel 7 */ + LEDC_CHANNEL_MAX, } ledc_channel_t; typedef enum { @@ -79,6 +77,11 @@ typedef enum { LEDC_TIMER_15_BIT = 15, /*!< LEDC PWM depth 15Bit */ } ledc_timer_bit_t; +typedef enum { + LEDC_FADE_NO_WAIT = 0, /*!< LEDC fade function will return immediately */ + LEDC_FADE_WAIT_DONE, /*!< LEDC fade function will block until fading to the target duty*/ + LEDC_FADE_MAX, +} ledc_fade_mode_t; /** * @brief Configuration parameters of LEDC channel for ledc_channel_config function */ @@ -104,43 +107,39 @@ typedef struct { typedef intr_handle_t ledc_isr_handle_t; /** - * @brief LEDC channel configuration + * @brief LEDC channel configuration + * Configure LEDC channel with the given channel/output gpio_num/interrupt/source timer/frequency(Hz)/LEDC depth * - * User this Function, configure LEDC channel with the given channel/output gpio_num/interrupt/source timer/frequency(Hz)/LEDC depth + * @param ledc_conf Pointer of LEDC channel configure struct * - * @param ledc_conf Pointer of LEDC channel configure struct * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error - * */ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf); /** - * @brief LEDC timer configuration - * - * User this Function, configure LEDC timer with the given source timer/frequency(Hz)/bit_num + * @brief LEDC timer configuration + * Configure LEDC timer with the given source timer/frequency(Hz)/bit_num * * @param timer_conf Pointer of LEDC timer configure struct * - * * @return * - 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 bit_num. - * */ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf); /** - * @brief LEDC update channel parameters + * @brief LEDC update channel parameters + * Call this function to activate the LEDC updated parameters. + * After ledc_set_duty, ledc_set_fade, we need to call this function to update the settings. * - * Call this function to activate the LEDC updated parameters. - * After ledc_set_duty, ledc_set_fade, we need to call this function to update the settings. - * - * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * - * @param channel LEDC channel(0-7), select from ledc_channel_t + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, + * now we only support high-speed mode. + * We will access low-speed mode in next version + * @param channel LEDC channel(0-7), select from ledc_channel_t * * @return * - ESP_OK Success @@ -150,14 +149,11 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf); esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel); /** - * @brief LEDC stop - * - * Disable LEDC output, and set idle level + * @brief LEDC stop. + * Disable LEDC output, and set idle level * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param channel LEDC channel(0-7), select from ledc_channel_t - * * @param idle_level Set output idle level after LEDC stops. * * @return @@ -167,14 +163,10 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel); esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level); /** - * @brief LEDC set channel frequency(Hz) - * - * Set LEDC frequency(Hz) + * @brief LEDC set channel frequency(Hz) * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param timer_num LEDC timer index(0-3), select from ledc_timer_t - * * @param freq_hz Set the LEDC frequency * * @return @@ -188,25 +180,20 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t * @brief LEDC get channel frequency(Hz) * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param timer_num LEDC timer index(0-3), select from ledc_timer_t * * @return * - 0 error * - Others Current LEDC frequency - * */ uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num); /** - * @brief LEDC set duty - * - * Set LEDC duty, After the function calls the ledc_update_duty function, the function can take effect. + * @brief LEDC set duty + * Only after calling ledc_update_duty will the duty update. * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param channel LEDC channel(0-7), select from ledc_channel_t - * * @param duty Set the LEDC duty, the duty range is [0, (2**bit_num) - 1] * * @return @@ -216,37 +203,27 @@ uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num); esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty); /** - * @brief LEDC get duty + * @brief LEDC get duty * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param channel LEDC channel(0-7), select from ledc_channel_t * - * * @return * - (-1) parameter error * - Others Current LEDC duty - * */ int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel); /** - * @brief LEDC set gradient - * - * Set LEDC gradient, After the function calls the ledc_update_duty function, the function can take effect. + * @brief LEDC set gradient + * Set LEDC gradient, After the function calls the ledc_update_duty function, the function can take effect. * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param channel LEDC channel(0-7), select from ledc_channel_t - * * @param duty Set the start of the gradient duty, the duty range is [0, (2**bit_num) - 1] - * * @param gradule_direction Set the direction of the gradient - * * @param step_num Set the number of the gradient - * * @param duty_cyle_num Set how many LEDC tick each time the gradient lasts - * * @param duty_scale Set gradient change amplitude * * @return @@ -257,16 +234,16 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale); /** - * @brief register LEDC interrupt handler, the handler is an ISR. - * The handler will be attached to the same CPU core that this function is running on. + * @brief Register LEDC interrupt handler, the handler is an ISR. + * The handler will be attached to the same CPU core that this function is running on. * - * @param fn Interrupt handler function. - * @param arg User-supplied argument passed to the handler function. - * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) - * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. - * @param arg Parameter for handler function - * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will - * be returned here. + * @param fn Interrupt handler function. + * @param arg User-supplied argument passed to the handler function. + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * @param arg Parameter for handler function + * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will + * be returned here. * * @return * - ESP_OK Success @@ -275,48 +252,38 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, ledc_isr_handle_t *handle); /** - * @brief configure LEDC settings + * @brief Configure LEDC settings * - * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * - * @param timer_sel Timer index(0-3), there are 4 timers in LEDC module - * - * @param div_num Timer clock divide number, the timer clock is divided from the selected clock source - * - * @param bit_num The count number of one period, counter range is 0 ~ ((2 ** bit_num) - 1) - * - * @param clk_src Select LEDC source clock. + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param timer_sel Timer index(0-3), there are 4 timers in LEDC module + * @param div_num Timer clock divide number, the timer clock is divided from the selected clock source + * @param bit_num The count number of one period, counter range is 0 ~ ((2 ** bit_num) - 1) + * @param clk_src Select LEDC source clock. * * @return * - (-1) Parameter error * - Other Current LEDC duty - * */ esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src); /** - * @brief reset LEDC timer + * @brief Reset LEDC timer * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t * - * * @return * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success - * */ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel); /** - * @brief pause LEDC timer counter + * @brief Pause LEDC timer counter * * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t * - * * @return * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success @@ -325,104 +292,96 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel); esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel); /** - * @brief pause LEDC timer resume - * - * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * - * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t + * @brief Resume LEDC timer * + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t * * @return * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success - * */ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel); /** - * @brief bind LEDC channel with the selected timer - * - * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version - * - * @param channel LEDC channel index(0-7), select from ledc_channel_t - * - * @param timer_idx LEDC timer index(0-3), select from ledc_timer_t + * @brief Bind LEDC channel with the selected timer * + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param channel LEDC channel index(0-7), select from ledc_channel_t + * @param timer_idx LEDC timer index(0-3), select from ledc_timer_t * * @return * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success + */ +esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx); + +/** + * @brief Set LEDC fade function. Should call ledc_fade_func_install() before calling this function. + * Call ledc_fade_start() after this to start fading. + * + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, + * For now we only support high-speed mode. We will access low-speed mode soon. + * @param channel LEDC channel index(0-7), select from ledc_channel_t + * @param target_duty Target duty of fading.( 0 - (2 ** bit_num - 1))) + * @param scale Controls the increase or decrease step scale. + * @param cycle_num increase or decrease the duty every cycle_num cycles + * + * @return + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Fade function not installed. + */ +esp_err_t ledc_set_fade_with_step(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int scale, int cycle_num); + +/** + * @brief Set LEDC fade function, with a limited time. Should call ledc_fade_func_install() before calling this function. + * Call ledc_fade_start() after this to start fading. + * + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, + * For now we only support high-speed mode. We will access low-speed mode soon. + * @param channel LEDC channel index(0-7), select from ledc_channel_t + * @param target_duty Target duty of fading.( 0 - (2 ** bit_num - 1))) + * @param max_fade_time_ms The maximum time of the fading ( ms ). + * + * @return + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Fade function not installed. + */ +esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int max_fade_time_ms); + +/** + * @brief Install ledc fade function. This function will occupy interrupt of LEDC module. + * + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * + * @return + * - ESP_ERR_NO_MEM No enough memory + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Fade function already installed. + */ +esp_err_t ledc_fade_func_install(int intr_alloc_flags); + +/** + * @brief Uninstall LEDC fade function. * */ -esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t timer_idx); +void ledc_fade_func_uninstall(); -/***************************EXAMPLE********************************** +/** + * @brief Start LEDC fading. * + * @param channel LEDC channel number + * @param wait_done Whether to block until fading done. * - * ----------------EXAMPLE OF LEDC SETTING --------------------- - * @code{c} - * //1. enable LEDC - * //enable LEDC module, or you can not set any register of it. - * periph_module_enable(PERIPH_LEDC_MODULE); - * @endcode - * - * @code{c} - * //2. set LEDC timer - * ledc_timer_config_t timer_conf = { - * .bit_num = LEDC_TIMER_12_BIT, //set timer counter bit number - * .freq_hz = 1000, //set frequency of pwm, here, 1000Hz - * .speed_mode = LEDC_HIGH_SPEED_MODE, //timer mode, - * .timer_num = LEDC_TIMER_0, //timer number - * }; - * ledc_timer_config(&timer_conf); //setup timer. - * @endcode - * - * @code{c} - * //3. set LEDC channel - * ledc_channel_config_t ledc_conf = { - * .channel = LEDC_CHANNEL_0; //set LEDC channel 0 - * .duty = 1000; //set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1) - * .gpio_num = 16; //GPIO number - * .intr_type = LEDC_INTR_FADE_END; //GPIO INTR TYPE, as an example, we enable fade_end interrupt here. - * .speed_mode = LEDC_HIGH_SPEED_MODE; //set LEDC mode, from ledc_mode_t - * .timer_sel = LEDC_TIMER_0; //set LEDC timer source, if different channel use one timer, the frequency and bit_num of these channels should be the same - * } - * ledc_channel_config(&ledc_conf); //setup the configuration - * - * ----------------EXAMPLE OF SETTING DUTY --- ----------------- - * @code{c} - * ledc_channel_t ledc_channel = LEDC_CHANNEL_0; //LEDC channel(0-73) - * uint32_t duty = 2000; //duty range is 0 ~ ((2**bit_num)-1) - * LEDC_set_duty(LEDC_HIGH_SPEED_MODE, ledc_channel, duty); //set speed mode, channel, and duty. - * ledc_update_duty(LEDC_HIGH_SPEED_MODE, ledc_channel); //after set duty, we need to call ledc_update_duty to update the settings. - * @endcode - * - * ----------------EXAMPLE OF LEDC INTERRUPT ------------------ - * @code{c} - * //we have fade_end interrupt and counter overflow interrupt. we just give an example of fade_end interrupt here. - * ledc_isr_register(ledc_isr_handler, NULL, 0); //hook the isr handler for LEDC interrupt - * @endcode - * - * ----------------EXAMPLE OF INTERRUPT HANDLER --------------- - * @code{c} - * #include "esp_attr.h" - * void IRAM_ATTR ledc_isr_handler(void* arg) //we should add 'IRAM_ATTR' attribution when we declare the isr function - * { - * uint32_t intr_st = LEDC.int_st.val; //read LEDC interrupt status. - * - * //you will find which channels have triggered fade_end interrupt here, - * //then, you can post some event to RTOS queue to process the event. - * //later we will add a queue in the driver code. - * - * LEDC.int_clr.val = intr_st; //clear LEDC interrupt status. - * } - * @endcode - * - *--------------------------END OF EXAMPLE -------------------------- + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Fade function not installed. + * - ESP_ERR_INVALID_ARG Parameter error. */ - - - +esp_err_t ledc_fade_start(ledc_channel_t channel, ledc_fade_mode_t wait_done); #ifdef __cplusplus } diff --git a/components/driver/ledc.c b/components/driver/ledc.c index c00cf26bb..6944f1b11 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -19,6 +19,8 @@ #include "freertos/xtensa_api.h" #include "soc/gpio_sig_map.h" #include "driver/ledc.h" +#include "soc/ledc_reg.h" +#include "soc/ledc_struct.h" #include "esp_log.h" static const char* LEDC_TAG = "ledc"; @@ -29,26 +31,56 @@ static portMUX_TYPE ledc_spinlock = portMUX_INITIALIZER_UNLOCKED; return (ret_val); \ } +typedef struct { + uint16_t speed_mode; + uint16_t direction; + int target_duty; + int cycle_num; + int scale; + ledc_fade_mode_t mode; + xSemaphoreHandle ledc_fade_sem; + xSemaphoreHandle ledc_fade_mux; +} ledc_fade_t; +static ledc_fade_t* s_ledc_fade_rec = NULL; +static ledc_isr_handle_t s_ledc_fade_isr_handle = NULL; + +#define LEDC_VAL_NO_CHANGE (-1) +#define LEDC_STEP_NUM_MAX (1023) +#define LEDC_DUTY_DECIMAL_BIT_NUM (4) + +#define LEDC_MODE_ERR_STR "LEDC mode error" +#define LEDC_TIMER_ERR_STR "LEDC timer error" +#define LEDC_CHANNEL_ERR_STR "LEDC channel error" +#define LEDC_GPIO_OUT_ERR_STR "LEDC GPIO output number error" +#define LEDC_FADE_DIR_ERR_STR "LEDC fade direction error" +#define LEDC_FADE_SERVICE_ERR_STR "LEDC fade service not installed" +#define LEDC_FADE_TARGET_ERR_STR "LEDC fade target duty error" +#define LEDC_FADE_INSTALLED_ERR_STR "LEDC fade service already installed" +#define LEDC_FADE_MODE_ERR_STR "LEDC fade mode error" + + esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.div_num = div_num; LEDC.timer_group[speed_mode].timer[timer_sel].conf.tick_sel = clk_src; LEDC.timer_group[speed_mode].timer[timer_sel].conf.bit_num = bit_num; - if(speed_mode != LEDC_HIGH_SPEED_MODE) { + if (speed_mode != LEDC_HIGH_SPEED_MODE) { LEDC.timer_group[speed_mode].timer[timer_sel].conf.low_speed_update = 1; } portEXIT_CRITICAL(&ledc_spinlock); return ESP_OK; } -static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t hpoint_val, uint32_t duty_val, +static IRAM_ATTR esp_err_t ledc_duty_config(ledc_mode_t speed_mode, ledc_channel_t channel_num, int hpoint_val, uint32_t duty_val, uint32_t duty_direction, uint32_t duty_num, uint32_t duty_cycle, uint32_t duty_scale) { portENTER_CRITICAL(&ledc_spinlock); - LEDC.channel_group[speed_mode].channel[channel_num].hpoint.hpoint = hpoint_val; + if (hpoint_val >= 0) { + LEDC.channel_group[speed_mode].channel[channel_num].hpoint.hpoint = hpoint_val; + } LEDC.channel_group[speed_mode].channel[channel_num].duty.duty = duty_val; LEDC.channel_group[speed_mode].channel[channel_num].conf1.val = ((duty_direction & LEDC_DUTY_INC_HSCH0_V) << LEDC_DUTY_INC_HSCH0_S) | ((duty_num & LEDC_DUTY_NUM_HSCH0_V) << LEDC_DUTY_NUM_HSCH0_S) | @@ -60,8 +92,8 @@ static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, ledc_channel_t channel esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t timer_idx) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_idx <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_idx <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel = timer_idx; portEXIT_CRITICAL(&ledc_spinlock); @@ -70,8 +102,8 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, ledc_channel_t channel esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 1; LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 0; @@ -81,8 +113,8 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel) esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 1; portEXIT_CRITICAL(&ledc_spinlock); @@ -91,8 +123,8 @@ esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel) esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 0; portEXIT_CRITICAL(&ledc_spinlock); @@ -101,15 +133,15 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel) static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, ledc_intr_type_t type) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); uint32_t value; uint32_t intr_type = type; portENTER_CRITICAL(&ledc_spinlock); value = LEDC.int_ena.val; - if(intr_type == LEDC_INTR_FADE_END) { + if (intr_type == LEDC_INTR_FADE_END) { LEDC.int_ena.val = value | BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel); } else { - LEDC.int_ena.val = (value & (~(BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel)))); + LEDC.int_ena.val = ( value & ( ~( BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel) ) ) ); } portEXIT_CRITICAL(&ledc_spinlock); return ESP_OK; @@ -120,7 +152,7 @@ esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, esp_err_t ret; LEDC_CHECK(fn, "ledc isr null", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); - ret=esp_intr_alloc(ETS_LEDC_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); + ret = esp_intr_alloc(ETS_LEDC_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); portEXIT_CRITICAL(&ledc_spinlock); return ret; } @@ -131,32 +163,35 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) int bit_num = timer_conf->bit_num; int timer_num = timer_conf->timer_num; int speed_mode = timer_conf->speed_mode; - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - if(freq_hz == 0 || bit_num == 0 || bit_num > LEDC_TIMER_15_BIT) { + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + periph_module_enable(PERIPH_LEDC_MODULE); + if (freq_hz == 0 || bit_num == 0 || bit_num > LEDC_TIMER_15_BIT) { ESP_LOGE(LEDC_TAG, "freq_hz=%u bit_num=%u", freq_hz, bit_num); return ESP_ERR_INVALID_ARG; } - if(timer_num > LEDC_TIMER_3) { + if (timer_num > LEDC_TIMER_3) { ESP_LOGE(LEDC_TAG, "invalid timer #%u", timer_num); return ESP_ERR_INVALID_ARG; } esp_err_t ret = ESP_OK; - uint32_t precision = (0x1 << bit_num); // 2**depth + uint32_t precision = ( 0x1 << bit_num ); // 2**depth // Try calculating divisor based on LEDC_APB_CLK ledc_clk_src_t timer_clk_src = LEDC_APB_CLK; // div_param is a Q10.8 fixed point value - uint64_t div_param = ((uint64_t) LEDC_APB_CLK_HZ << 8) / freq_hz / precision; + uint64_t div_param = ( (uint64_t) LEDC_APB_CLK_HZ << 8 ) / freq_hz / precision; if (div_param < 256) { // divisor is too low - ESP_LOGE(LEDC_TAG, "requested frequency and bit depth can not be achieved, try reducing freq_hz or bit_num. div_param=%d", (uint32_t) div_param); + ESP_LOGE(LEDC_TAG, "requested frequency and bit depth can not be achieved, try reducing freq_hz or bit_num. div_param=%d", + (uint32_t ) div_param); ret = ESP_FAIL; } if (div_param > LEDC_DIV_NUM_HSTIMER0_V) { // APB_CLK results in divisor which too high. Try using REF_TICK as clock source. timer_clk_src = LEDC_REF_TICK; - div_param = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; - if(div_param < 256 || div_param > LEDC_DIV_NUM_HSTIMER0_V) { - ESP_LOGE(LEDC_TAG, "requested frequency and bit depth can not be achieved, try increasing freq_hz or bit_num. div_param=%d", (uint32_t) div_param); + div_param = ( (uint64_t) LEDC_REF_CLK_HZ << 8 ) / freq_hz / precision; + if (div_param < 256 || div_param > LEDC_DIV_NUM_HSTIMER0_V) { + ESP_LOGE(LEDC_TAG, "requested frequency and bit depth can not be achieved, try increasing freq_hz or bit_num. div_param=%d", + (uint32_t ) div_param); ret = ESP_FAIL; } } @@ -169,12 +204,12 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel) { - LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), LEDC_GPIO_OUT_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO); gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT); - if(speed_mode == LEDC_HIGH_SPEED_MODE) { + if (speed_mode == LEDC_HIGH_SPEED_MODE) { gpio_matrix_out(gpio_num, LEDC_HS_SIG_OUT0_IDX + ledc_channel, 0, 0); } else { ESP_LOGE(LEDC_TAG, "low speed mode is not implemented"); @@ -191,10 +226,10 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) uint32_t timer_select = ledc_conf->timer_sel; uint32_t intr_type = ledc_conf->intr_type; uint32_t duty = ledc_conf->duty; - LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_select <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), LEDC_GPIO_OUT_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_select <= LEDC_TIMER_3, LEDC_TIMER_ERR_STR, ESP_ERR_INVALID_ARG); periph_module_enable(PERIPH_LEDC_MODULE); esp_err_t ret = ESP_OK; /*set channel parameters*/ @@ -207,7 +242,7 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) ledc_bind_channel_timer(speed_mode, ledc_channel, timer_select); /*set interrupt type*/ ledc_enable_intr_type(speed_mode, ledc_channel, intr_type); - ESP_LOGI(LEDC_TAG, "LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u", + ESP_LOGD(LEDC_TAG, "LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u", ledc_channel, gpio_num, duty, timer_select ); /*set LEDC signal in gpio matrix*/ @@ -219,8 +254,8 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 1; LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 1; @@ -230,8 +265,8 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.idle_lv = idle_level & 0x1; LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 0; @@ -239,16 +274,20 @@ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idl portEXIT_CRITICAL(&ledc_spinlock); return ESP_OK; } + esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty, ledc_duty_direction_t fade_direction, uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(fade_direction <= LEDC_DUTY_DIR_INCREASE, "ledc fade direction error", ESP_ERR_INVALID_ARG); - if(step_num > LEDC_DUTY_NUM_HSCH0_V || duty_cyle_num > LEDC_DUTY_CYCLE_HSCH0_V || duty_scale > LEDC_DUTY_SCALE_HSCH0_V) { + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(fade_direction <= LEDC_DUTY_DIR_INCREASE, LEDC_FADE_DIR_ERR_STR, ESP_ERR_INVALID_ARG); + if (step_num > LEDC_DUTY_NUM_HSCH0_V || duty_cyle_num > LEDC_DUTY_CYCLE_HSCH0_V || duty_scale > LEDC_DUTY_SCALE_HSCH0_V) { ESP_LOGE(LEDC_TAG, "step_num=%u duty_cyle_num=%u duty_scale=%u", step_num, duty_cyle_num, duty_scale); return ESP_ERR_INVALID_ARG; } + if (s_ledc_fade_rec) { + ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_DISABLE); + } ledc_duty_config(speed_mode, channel, //uint32_t chan_num, 0, //uint32_t hpoint_val, @@ -263,8 +302,11 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + if (s_ledc_fade_rec) { + ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_DISABLE); + } ledc_duty_config(speed_mode, channel, //uint32_t chan_num, 0, //uint32_t hpoint_val, @@ -279,26 +321,26 @@ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", (-1)); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, (-1)); uint32_t duty = (LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> 4); return duty; } esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t freq_hz) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); esp_err_t ret = ESP_OK; uint32_t div_num = 0; uint32_t bit_num = LEDC.timer_group[speed_mode].timer[timer_num].conf.bit_num; uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel; - uint32_t precision = (0x1 << bit_num); - if(timer_source_clk == LEDC_APB_CLK) { - div_num = ((uint64_t) LEDC_APB_CLK_HZ << 8) / freq_hz / precision; + uint32_t precision = ( 0x1 << bit_num ); + if (timer_source_clk == LEDC_APB_CLK) { + div_num = ( (uint64_t) LEDC_APB_CLK_HZ << 8 ) / freq_hz / precision; } else { - div_num = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; + div_num = ( (uint64_t) LEDC_REF_CLK_HZ << 8 ) / freq_hz / precision; } - if(div_num <= 256 || div_num > LEDC_DIV_NUM_HSTIMER0) { + if (div_num <= 256 || div_num > LEDC_DIV_NUM_HSTIMER0) { ESP_LOGE(LEDC_TAG, "div param err,div_param=%u", div_num); ret = ESP_FAIL; } @@ -309,18 +351,217 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", (0)); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ( 0 )); portENTER_CRITICAL(&ledc_spinlock); uint32_t freq = 0; uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel; uint32_t bit_num = LEDC.timer_group[speed_mode].timer[timer_num].conf.bit_num; uint32_t div_num = LEDC.timer_group[speed_mode].timer[timer_num].conf.div_num; - uint32_t precision = (0x1 << bit_num); - if(timer_source_clk == LEDC_APB_CLK) { - freq = ((uint64_t) LEDC_APB_CLK_HZ << 8) / precision / div_num; + uint32_t precision = ( 0x1 << bit_num ); + if (timer_source_clk == LEDC_APB_CLK) { + freq = ( (uint64_t) LEDC_APB_CLK_HZ << 8 ) / precision / div_num; } else { - freq = ((uint64_t) LEDC_REF_CLK_HZ << 8) / precision / div_num; + freq = ( (uint64_t) LEDC_REF_CLK_HZ << 8 ) / precision / div_num; } portEXIT_CRITICAL(&ledc_spinlock); return freq; } + +void IRAM_ATTR ledc_fade_isr() +{ + int i; + portBASE_TYPE HPTaskAwoken = pdFALSE; + uint32_t intr_status = LEDC.int_st.val; //read LEDC interrupt status. + LEDC.int_clr.val = intr_status; //clear LEDC interrupt status. + for (i = 0; i < 8; i++) { + if (intr_status & BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ST_S + i)) { + int speed_mode = s_ledc_fade_rec[i].speed_mode; + int duty_cur = LEDC.channel_group[speed_mode].channel[i].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM; + if (duty_cur == s_ledc_fade_rec[i].target_duty) { + if(s_ledc_fade_rec[i].mode == LEDC_FADE_WAIT_DONE) { + xSemaphoreGiveFromISR(s_ledc_fade_rec[i].ledc_fade_sem, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } + } + continue; + } + int duty_tar = s_ledc_fade_rec[i].target_duty; + int scale = s_ledc_fade_rec[i].scale; + if (scale == 0) { + continue; + } + int cycle = s_ledc_fade_rec[i].cycle_num; + int delta = s_ledc_fade_rec[i].direction == LEDC_DUTY_DIR_DECREASE ? duty_cur - duty_tar : duty_tar - duty_cur; + int step = delta / scale > LEDC_STEP_NUM_MAX ? LEDC_STEP_NUM_MAX : delta / scale; + + if (delta > scale) { + ledc_duty_config( + speed_mode, + i, + LEDC_VAL_NO_CHANGE, + duty_cur << LEDC_DUTY_DECIMAL_BIT_NUM, + s_ledc_fade_rec[i].direction, + step, + cycle, + scale); + } else { + ledc_duty_config( + speed_mode, + i, + LEDC_VAL_NO_CHANGE, + duty_tar << LEDC_DUTY_DECIMAL_BIT_NUM, + s_ledc_fade_rec[i].direction, + 1, + 1, + 0); + } + LEDC.channel_group[speed_mode].channel[i].conf1.duty_start = 1; + } + } + LEDC.int_clr.val = intr_status; //clear LEDC interrupt status. +} + +esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int max_fade_time_ms) +{ + int timer_sel = LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel; + int max_duty = ( 1 << ( LEDC.timer_group[speed_mode].timer[timer_sel].conf.bit_num ) ) - 1; + LEDC_CHECK(target_duty <= max_duty, LEDC_FADE_TARGET_ERR_STR, ESP_ERR_INVALID_ARG); + uint32_t freq = ledc_get_freq(speed_mode, timer_sel); + int duty_cur = LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM; + int duty_delta = target_duty > duty_cur ? target_duty - duty_cur : duty_cur - target_duty; + + if (duty_delta == 0) { + return ESP_OK; + } + int total_cycles = max_fade_time_ms * freq / 1000; + int scale, cycle_num; + if (total_cycles > duty_delta) { + scale = 1; + cycle_num = total_cycles / duty_delta; + } else { + cycle_num = 1; + scale = ( duty_delta + total_cycles - 1 ) / total_cycles; + } + return ledc_set_fade_with_step(speed_mode, channel, target_duty, scale, cycle_num); +} + +esp_err_t ledc_set_fade_with_step(ledc_mode_t speed_mode, ledc_channel_t channel, int target_duty, int scale, int cycle_num) +{ + LEDC_CHECK(s_ledc_fade_rec != NULL, LEDC_FADE_SERVICE_ERR_STR, ESP_ERR_INVALID_STATE); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, LEDC_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel < LEDC_CHANNEL_MAX, LEDC_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG); + int timer_sel = LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel; + int max_duty = (1 << (LEDC.timer_group[speed_mode].timer[timer_sel].conf.bit_num)) - 1; + LEDC_CHECK(target_duty <= max_duty, LEDC_FADE_TARGET_ERR_STR, ESP_ERR_INVALID_ARG); + //disable the interrupt, so the operation will not mess up + ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_DISABLE); + + portENTER_CRITICAL(&ledc_spinlock); + int duty_cur = LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM; + int duty_delta = target_duty > duty_cur ? target_duty - duty_cur : duty_cur - target_duty; + if (duty_delta == 0) { + return ESP_OK; + } + s_ledc_fade_rec[channel].speed_mode = speed_mode; + s_ledc_fade_rec[channel].target_duty = target_duty; + s_ledc_fade_rec[channel].cycle_num = cycle_num; + s_ledc_fade_rec[channel].scale = scale; + int step_num; + if (duty_cur > target_duty) { + s_ledc_fade_rec[channel].direction = LEDC_DUTY_DIR_DECREASE; + step_num = ( duty_cur - target_duty ) / scale; + step_num = step_num > LEDC_STEP_NUM_MAX ? LEDC_STEP_NUM_MAX : step_num; + } else { + s_ledc_fade_rec[channel].direction = LEDC_DUTY_DIR_INCREASE; + step_num = ( target_duty - duty_cur ) / scale; + step_num = step_num > LEDC_STEP_NUM_MAX ? LEDC_STEP_NUM_MAX : step_num; + } + portEXIT_CRITICAL(&ledc_spinlock); + + ledc_set_fade( + speed_mode, + channel, + duty_cur, + s_ledc_fade_rec[channel].direction, + step_num, + s_ledc_fade_rec[channel].cycle_num, + s_ledc_fade_rec[channel].scale + ); + ESP_LOGD(LEDC_TAG, "cur duty: %d; target: %d, step: %d, cycle: %d; scale: %d\n", + LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> LEDC_DUTY_DECIMAL_BIT_NUM, + target_duty, + step_num, + s_ledc_fade_rec[channel].cycle_num, + s_ledc_fade_rec[channel].scale + ); + LEDC.int_clr.val |= BIT(LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S + channel); + ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_FADE_END); + return ESP_OK; +} + +esp_err_t ledc_fade_func_install(int intr_alloc_flags) +{ + LEDC_CHECK(s_ledc_fade_rec == NULL, LEDC_FADE_INSTALLED_ERR_STR, ESP_ERR_INVALID_STATE); + s_ledc_fade_rec = (ledc_fade_t*) calloc(LEDC_CHANNEL_MAX, sizeof(ledc_fade_t)); + if (s_ledc_fade_rec == NULL) { + return ESP_ERR_NO_MEM; + } + int i = 0; + for (i = 0; i < LEDC_CHANNEL_MAX; i++) { + s_ledc_fade_rec[i].ledc_fade_sem = xSemaphoreCreateBinary(); + s_ledc_fade_rec[i].ledc_fade_mux = xSemaphoreCreateMutex(); + if (s_ledc_fade_rec[i].ledc_fade_sem == NULL || s_ledc_fade_rec[i].ledc_fade_mux == NULL) { + ledc_fade_func_uninstall(); + return ESP_ERR_NO_MEM; + } + } + //OR intr_alloc_flags with ESP_INTR_FLAG_IRAM because the fade isr is in IRAM + ledc_isr_register(ledc_fade_isr, NULL, intr_alloc_flags | ESP_INTR_FLAG_IRAM, &s_ledc_fade_isr_handle); + return ESP_OK; +} + +void ledc_fade_func_uninstall() +{ + if (s_ledc_fade_rec == NULL) { + return; + } + if(s_ledc_fade_isr_handle) { + esp_intr_free(s_ledc_fade_isr_handle); + s_ledc_fade_isr_handle = NULL; + } + int i; + for (i = 0; i < LEDC_CHANNEL_MAX; i++) { + if (s_ledc_fade_rec[i].ledc_fade_sem) { + xSemaphoreHandle sem_tmp = s_ledc_fade_rec[i].ledc_fade_sem; + s_ledc_fade_rec[i].ledc_fade_sem = NULL; + vSemaphoreDelete(sem_tmp); + } + if (s_ledc_fade_rec[i].ledc_fade_mux) { + xSemaphoreHandle mux_tmp = s_ledc_fade_rec[i].ledc_fade_mux; + s_ledc_fade_rec[i].ledc_fade_mux = NULL; + vSemaphoreDelete(mux_tmp); + } + } + free(s_ledc_fade_rec); + s_ledc_fade_rec = NULL; + return; +} + +esp_err_t ledc_fade_start(ledc_channel_t channel, ledc_fade_mode_t wait_done) +{ + LEDC_CHECK(s_ledc_fade_rec != NULL, LEDC_FADE_SERVICE_ERR_STR, ESP_ERR_INVALID_STATE); + LEDC_CHECK(wait_done < LEDC_FADE_MAX, LEDC_FADE_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + int speed_mode = s_ledc_fade_rec[channel].speed_mode; + xSemaphoreTake(s_ledc_fade_rec[channel].ledc_fade_mux, portMAX_DELAY); + if (wait_done == LEDC_FADE_WAIT_DONE) { + s_ledc_fade_rec[channel].mode = LEDC_FADE_WAIT_DONE; + ledc_update_duty(speed_mode, channel); + xSemaphoreTake(s_ledc_fade_rec[channel].ledc_fade_sem, portMAX_DELAY); + } else { + s_ledc_fade_rec[channel].mode = LEDC_FADE_NO_WAIT; + ledc_update_duty(speed_mode, channel); + } + xSemaphoreGive(s_ledc_fade_rec[channel].ledc_fade_mux); + return ESP_OK; +} diff --git a/docs/api/ledc.rst b/docs/api/ledc.rst index 855f82216..40855ddb4 100644 --- a/docs/api/ledc.rst +++ b/docs/api/ledc.rst @@ -4,20 +4,19 @@ LED Control Overview -------- -`Instructions`_ +The LED control module is primarily designed to control the intensity of LEDs, although it can be used to generate PWM signals for other purposes as well. +It has 16 channels which can generate independent waveforms that can be used to drive e.g. RGB LED devices. For maximum flexibility, the high-speed as well +as the low-speed channels can be driven from one of four high-speed/low-speed timers. The PWM controller also has the ability to automatically increase or +decrease the duty cycle gradually, allowing for fades without any processor interference. Application Example ------------------- -`Instructions`_ +LEDC change duty cycle and fading control example: `examples/29_ledc `_. API Reference ------------- -`Instructions`_ - -.. _Instructions: template.html - Header Files ^^^^^^^^^^^^ @@ -38,6 +37,11 @@ Macros .. doxygendefine:: LEDC_APB_CLK_HZ .. doxygendefine:: LEDC_REF_CLK_HZ +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: ledc_isr_handle_t + Enumerations ^^^^^^^^^^^^ @@ -49,6 +53,16 @@ Enumerations .. doxygenenum:: ledc_channel_t .. doxygenenum:: ledc_timer_bit_t +Structures +^^^^^^^^^^ + +.. doxygenstruct:: ledc_channel_config_t + :members: + +.. doxygenstruct:: ledc_timer_config_t + :members: + + Functions ^^^^^^^^^ @@ -67,3 +81,9 @@ Functions .. doxygenfunction:: ledc_timer_pause .. doxygenfunction:: ledc_timer_resume .. doxygenfunction:: ledc_bind_channel_timer +.. doxygenfunction:: ledc_set_fade_with_step +.. doxygenfunction:: ledc_set_fade_with_time +.. doxygenfunction:: ledc_fade_func_install +.. doxygenfunction:: ledc_fade_func_uninstall +.. doxygenfunction:: ledc_fade_start + diff --git a/examples/29_ledc/Makefile b/examples/29_ledc/Makefile new file mode 100644 index 000000000..675fc8a38 --- /dev/null +++ b/examples/29_ledc/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := ledc + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/29_ledc/README.md b/examples/29_ledc/README.md new file mode 100644 index 000000000..9feea6b25 --- /dev/null +++ b/examples/29_ledc/README.md @@ -0,0 +1,16 @@ +# LEDC(LED control) Example + +###This example shows: + + * init LEDC module: + + a. You need to set the timer of LEDC first, this decide the frequency and resolution of PWM. + + b. You need to set the LEDC channel you want to use, and bind the channel with one of the timers. + + * You can install a default fade function, then you can use fade APIs. + + * You can also set a target duty directly without fading. + + * This example use GPIO18/19/4/5 as LEDC ouput, and it will change the duty repeatedly. + diff --git a/examples/29_ledc/main/component.mk b/examples/29_ledc/main/component.mk new file mode 100644 index 000000000..44bd2b527 --- /dev/null +++ b/examples/29_ledc/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/29_ledc/main/ledc_fade.c b/examples/29_ledc/main/ledc_fade.c new file mode 100644 index 000000000..dcdb0462f --- /dev/null +++ b/examples/29_ledc/main/ledc_fade.c @@ -0,0 +1,131 @@ +/* Ledc fade example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/xtensa_api.h" +#include "freertos/queue.h" +#include "driver/ledc.h" +#include "esp_attr.h" +#include "esp_err.h" + +/* + * About this example + * 1. init LEDC module: + * a. You need to set the timer of LEDC first, this decide the frequency and resolution of PWM. + * b. You need to set the LEDC channel you want to use, and bind with one of the timers. + * + * 2. You can install a default fade function, then you can use fade APIs. + * + * 3. You can also set a target duty directly without fading. + * + * 4. This example use GPIO18/19/4/5 as LEDC ouput, and it will change the duty repeatedly. + * + * + */ + +#define LEDC_IO_0 (18) +#define LEDC_IO_1 (19) +#define LEDC_IO_2 (4) +#define LEDC_IO_3 (5) + +esp_err_t app_main() +{ + ledc_timer_config_t ledc_timer = { + //set timer counter bit number + .bit_num = LEDC_TIMER_13_BIT, + //set frequency of pwm + .freq_hz = 5000, + //timer mode, + .speed_mode = LEDC_HIGH_SPEED_MODE, + //timer index + .timer_num = LEDC_TIMER_0 + }; + ledc_timer_config(&ledc_timer); + + ledc_channel_config_t ledc_channel = { + //set LEDC channel 0 + .channel = LEDC_CHANNEL_0, + //set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1) + .duty = 100, + //GPIO number + .gpio_num = LEDC_IO_0, + //GPIO INTR TYPE, as an example, we enable fade_end interrupt here. + .intr_type = LEDC_INTR_FADE_END, + //set LEDC mode, from ledc_mode_t + .speed_mode = LEDC_HIGH_SPEED_MODE, + //set LEDC timer source, if different channel use one timer, + //the frequency and bit_num of these channels should be the same + .timer_sel = LEDC_TIMER_0 + }; + //set the configuration + ledc_channel_config(&ledc_channel); + + //config ledc channel1 + ledc_channel.channel = LEDC_CHANNEL_1; + ledc_channel.gpio_num = LEDC_IO_1; + ledc_channel_config(&ledc_channel); + //config ledc channel2 + ledc_channel.channel = LEDC_CHANNEL_2; + ledc_channel.gpio_num = LEDC_IO_2; + ledc_channel_config(&ledc_channel); + //config ledc channel3 + ledc_channel.channel = LEDC_CHANNEL_3; + ledc_channel.gpio_num = LEDC_IO_3; + ledc_channel_config(&ledc_channel); + + //initialize fade service. + ledc_fade_func_install(0); + + while(1) { + printf("LEDC fade up\n"); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 1000, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 7000, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 5000, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 3000, 2000); + ledc_fade_start(LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_1, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_2, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_3, LEDC_FADE_NO_WAIT); + vTaskDelay(3000 / portTICK_PERIOD_MS); + + printf("LEDC fade down\n"); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 100, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 300, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 500, 2000); + ledc_set_fade_with_time(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 700, 2000); + ledc_fade_start(LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_1, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_2, LEDC_FADE_NO_WAIT); + ledc_fade_start(LEDC_CHANNEL_3, LEDC_FADE_NO_WAIT); + vTaskDelay(3000 / portTICK_PERIOD_MS); + + printf("LEDC set duty without fade\n"); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 1000); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 7000); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 5000); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 3000); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3); + vTaskDelay(2000 / portTICK_PERIOD_MS); + + printf("LEDC set duty without fade\n"); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 0); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1, 0); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2, 0); + ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3, 0); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_1); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_2); + ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_3); + vTaskDelay(2000 / portTICK_PERIOD_MS); + } +}