driver: ledc - update fading functions, add example and doc

1. add fading functions.
2. clear up ledc.c and ledc.h
3. update api doc.
4. add ledc example
5. edit ledc.rst and add readme for example code.
6. add s_ prefix for static global values.
7. add mutex for fade functions
8. minor changes according to the comments.
This commit is contained in:
Wangjialin 2016-12-25 23:11:24 +08:00
parent 3cfe738fcf
commit 6b5e734901
7 changed files with 596 additions and 217 deletions

View file

@ -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
}

View file

@ -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;
}

View file

@ -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 <https://github.com/espressif/esp-idf/tree/master/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

View file

@ -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

View file

@ -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.

View file

@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View file

@ -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 <stdio.h>
#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);
}
}