ledc: Add a callback for LEDC fade end

This commit adds a feature where users can register a callback that is
called when an LEDC fade completes.
pull/7365/head
songruojing 2021-07-08 17:47:05 +08:00
rodzic 16057d6c8b
commit 9d098cc9bd
3 zmienionych plików z 133 dodań i 32 usunięć

Wyświetl plik

@ -24,6 +24,38 @@ extern "C" {
typedef intr_handle_t ledc_isr_handle_t;
/**
* @brief LEDC callback event type
*/
typedef enum {
LEDC_FADE_END_EVT /**< LEDC fade end event */
} ledc_cb_event_t;
/**
* @brief LEDC callback parameter
*/
typedef struct {
ledc_cb_event_t event; /**< Event name */
uint32_t speed_mode; /**< Speed mode of the LEDC channel group */
uint32_t channel; /**< LEDC channel (0 - LEDC_CHANNEL_MAX-1) */
uint32_t duty; /**< LEDC current duty of the channel, the range of duty is [0, (2**duty_resolution) - 1] */
} ledc_cb_param_t;
/**
* @brief Type of LEDC event callback
* @param param LEDC callback parameter
* @param user_arg User registered data
*/
typedef bool (* ledc_cb_t)(const ledc_cb_param_t *param, void *user_arg);
/**
* @brief Group of supported LEDC callbacks
* @note The callbacks are all running under ISR environment
*/
typedef struct {
ledc_cb_t fade_cb; /**< LEDC fade_end callback function */
} ledc_cbs_t;
/**
* @brief LEDC channel configuration
* Configure LEDC channel with the given channel/output gpio_num/interrupt/source timer/frequency(Hz)/LEDC duty resolution
@ -57,7 +89,7 @@ esp_err_t ledc_timer_config(const ledc_timer_config_t* timer_conf);
* control one LEDC channel in different tasks at the same time.
* A thread-safe version of API is ledc_set_duty_and_update
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
* @param channel LEDC channel (0-7), select from ledc_channel_t
* @param channel LEDC channel (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
*
* @return
* - ESP_OK Success
@ -72,7 +104,7 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel);
*
* @param gpio_num The LEDC output gpio
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
* @param ledc_channel LEDC channel (0-7), select from ledc_channel_t
* @param ledc_channel LEDC channel (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
*
* @return
* - ESP_OK Success
@ -85,7 +117,7 @@ esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc
* Disable LEDC output, and set idle level
*
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
* @param channel LEDC channel (0-7), select from ledc_channel_t
* @param channel LEDC channel (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
* @param idle_level Set output idle level after LEDC stops.
*
* @return
@ -129,8 +161,8 @@ uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num);
* @note If a fade operation is running in progress on that channel, the driver would not allow it to be stopped.
* Other duty operations will have to wait until the fade operation has finished.
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
* @param channel LEDC channel (0-7), select from ledc_channel_t
* @param duty Set the LEDC duty, the range of duty setting is [0, (2**duty_resolution)]
* @param channel LEDC channel (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
* @param duty Set the LEDC duty, the range of duty setting is [0, (2**duty_resolution) - 1]
* @param hpoint Set the LEDC hpoint value(max: 0xfffff)
*
* @return
@ -143,7 +175,7 @@ esp_err_t ledc_set_duty_with_hpoint(ledc_mode_t speed_mode, ledc_channel_t chann
* @brief LEDC get hpoint value, the counter value when the output is set high level.
*
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
* @param channel LEDC channel (0-7), select from ledc_channel_t
* @param channel LEDC channel (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
* @return
* - LEDC_ERR_VAL if parameter error
* - Others Current hpoint value of LEDC channel
@ -160,8 +192,8 @@ int ledc_get_hpoint(ledc_mode_t speed_mode, ledc_channel_t channel);
* @note If a fade operation is running in progress on that channel, the driver would not allow it to be stopped.
* Other duty operations will have to wait until the fade operation has finished.
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
* @param channel LEDC channel (0-7), select from ledc_channel_t
* @param duty Set the LEDC duty, the range of duty setting is [0, (2**duty_resolution)]
* @param channel LEDC channel (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
* @param duty Set the LEDC duty, the range of duty setting is [0, (2**duty_resolution) - 1]
*
* @return
* - ESP_OK Success
@ -173,7 +205,7 @@ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t
* @brief LEDC get duty
*
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
* @param channel LEDC channel (0-7), select from ledc_channel_t
* @param channel LEDC channel (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
*
* @return
* - LEDC_ERR_DUTY if parameter error
@ -187,8 +219,8 @@ uint32_t ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel);
* @note If a fade operation is running in progress on that channel, the driver would not allow it to be stopped.
* Other duty operations will have to wait until the fade operation has finished.
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
* @param channel LEDC channel (0-7), select from ledc_channel_t
* @param duty Set the start of the gradient duty, the range of duty setting is [0, (2**duty_resolution)]
* @param channel LEDC channel (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
* @param duty Set the start of the gradient duty, the range of duty setting is [0, (2**duty_resolution) - 1]
* @param fade_direction Set the direction of the gradient
* @param step_num Set the number of the gradient
* @param duty_cycle_num Set how many LEDC tick each time the gradient lasts
@ -274,7 +306,7 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, ledc_timer_t timer_sel);
* @brief Bind LEDC channel with the selected timer
*
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
* @param channel LEDC channel index (0-7), select from ledc_channel_t
* @param channel LEDC channel index (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
* @param timer_sel LEDC timer index (0-3), select from ledc_timer_t
*
* @return
@ -293,7 +325,7 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, ledc_channel_t channel
* @note If a fade operation is running in progress on that channel, the driver would not allow it to be stopped.
* Other duty operations will have to wait until the fade operation has finished.
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode. ,
* @param channel LEDC channel index (0-7), select from ledc_channel_t
* @param channel LEDC channel index (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
* @param target_duty Target duty of fading [0, (2**duty_resolution) - 1]
* @param scale Controls the increase or decrease step scale.
* @param cycle_num increase or decrease the duty every cycle_num cycles
@ -316,8 +348,8 @@ esp_err_t ledc_set_fade_with_step(ledc_mode_t speed_mode, ledc_channel_t channel
* @note If a fade operation is running in progress on that channel, the driver would not allow it to be stopped.
* Other duty operations will have to wait until the fade operation has finished.
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode. ,
* @param channel LEDC channel index (0-7), select from ledc_channel_t
* @param target_duty Target duty of fading.( 0 - (2 ** duty_resolution - 1)))
* @param channel LEDC channel index (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
* @param target_duty Target duty of fading [0, (2**duty_resolution) - 1]
* @param max_fade_time_ms The maximum time of the fading ( ms ).
*
* @return
@ -368,8 +400,8 @@ esp_err_t ledc_fade_start(ledc_mode_t speed_mode, ledc_channel_t channel, ledc_f
* Other duty operations will have to wait until the fade operation has finished.
*
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
* @param channel LEDC channel (0-7), select from ledc_channel_t
* @param duty Set the LEDC duty, the range of duty setting is [0, (2**duty_resolution)]
* @param channel LEDC channel (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
* @param duty Set the LEDC duty, the range of duty setting is [0, (2**duty_resolution) - 1]
* @param hpoint Set the LEDC hpoint value(max: 0xfffff)
*
*/
@ -381,8 +413,8 @@ esp_err_t ledc_set_duty_and_update(ledc_mode_t speed_mode, ledc_channel_t channe
* @note If a fade operation is running in progress on that channel, the driver would not allow it to be stopped.
* Other duty operations will have to wait until the fade operation has finished.
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
* @param channel LEDC channel index (0-7), select from ledc_channel_t
* @param target_duty Target duty of fading.( 0 - (2 ** duty_resolution - 1)))
* @param channel LEDC channel index (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
* @param target_duty Target duty of fading [0, (2**duty_resolution) - 1]
* @param max_fade_time_ms The maximum time of the fading ( ms ).
* @param fade_mode choose blocking or non-blocking mode
* @return
@ -399,7 +431,7 @@ esp_err_t ledc_set_fade_time_and_start(ledc_mode_t speed_mode, ledc_channel_t ch
* @note If a fade operation is running in progress on that channel, the driver would not allow it to be stopped.
* Other duty operations will have to wait until the fade operation has finished.
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
* @param channel LEDC channel index (0-7), select from ledc_channel_t
* @param channel LEDC channel index (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
* @param target_duty Target duty of fading [0, (2**duty_resolution) - 1]
* @param scale Controls the increase or decrease step scale.
* @param cycle_num increase or decrease the duty every cycle_num cycles
@ -414,3 +446,18 @@ esp_err_t ledc_set_fade_step_and_start(ledc_mode_t speed_mode, ledc_channel_t ch
#ifdef __cplusplus
}
#endif
/**
* @brief LEDC callback registration function
* @note The callback is called from an ISR, it must never attempt to block, and any FreeRTOS API called must be ISR capable.
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
* @param channel LEDC channel index (0 - LEDC_CHANNEL_MAX-1), select from ledc_channel_t
* @param cbs Group of LEDC callback functions
* @param user_arg user registered data for the callback function
* @return
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE Fade function not installed.
* - ESP_FAIL Fade function init error
*/
esp_err_t ledc_cb_register(ledc_mode_t speed_mode, ledc_channel_t channel, ledc_cbs_t *cbs, void *user_arg);

Wyświetl plik

@ -37,6 +37,8 @@ typedef struct {
#if CONFIG_SPIRAM_USE_MALLOC
StaticQueue_t ledc_fade_sem_storage;
#endif
ledc_cb_t ledc_fade_callback;
void *cb_user_arg;
} ledc_fade_t;
typedef struct {
@ -551,6 +553,7 @@ static inline void ledc_calc_fade_end_channel(uint32_t *fade_end_status, uint32_
void IRAM_ATTR ledc_fade_isr(void* arg)
{
bool cb_yield = false;
portBASE_TYPE HPTaskAwoken = pdFALSE;
uint32_t speed_mode = 0;
uint32_t channel = 0;
@ -576,17 +579,21 @@ void IRAM_ATTR ledc_fade_isr(void* arg)
uint32_t duty_cur = 0;
ledc_hal_get_duty(&(p_ledc_obj[speed_mode]->ledc_hal), channel, &duty_cur);
if (duty_cur == s_ledc_fade_rec[speed_mode][channel]->target_duty) {
xSemaphoreGiveFromISR(s_ledc_fade_rec[speed_mode][channel]->ledc_fade_sem, &HPTaskAwoken);
if (HPTaskAwoken == pdTRUE) {
portYIELD_FROM_ISR();
}
continue;
}
uint32_t duty_tar = s_ledc_fade_rec[speed_mode][channel]->target_duty;
int scale = s_ledc_fade_rec[speed_mode][channel]->scale;
if (scale == 0) {
if (duty_cur == duty_tar || scale == 0) {
xSemaphoreGiveFromISR(s_ledc_fade_rec[speed_mode][channel]->ledc_fade_sem, &HPTaskAwoken);
ledc_cb_param_t param = {
.event = LEDC_FADE_END_EVT,
.speed_mode = speed_mode,
.channel = channel,
.duty = duty_cur
};
ledc_cb_t fade_cb = s_ledc_fade_rec[speed_mode][channel]->ledc_fade_callback;
if (fade_cb) {
cb_yield |= fade_cb(&param, s_ledc_fade_rec[speed_mode][channel]->cb_user_arg);
}
continue;
}
int cycle = s_ledc_fade_rec[speed_mode][channel]->cycle_num;
@ -618,6 +625,9 @@ void IRAM_ATTR ledc_fade_isr(void* arg)
portEXIT_CRITICAL(&ledc_spinlock);
}
}
if (HPTaskAwoken == pdTRUE || cb_yield) {
portYIELD_FROM_ISR();
}
}
static esp_err_t ledc_fade_channel_deinit(ledc_mode_t speed_mode, ledc_channel_t channel)
@ -825,6 +835,17 @@ void ledc_fade_func_uninstall(void)
return;
}
esp_err_t ledc_cb_register(ledc_mode_t speed_mode, ledc_channel_t channel, ledc_cbs_t *cbs, void *user_arg)
{
LEDC_ARG_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "speed_mode");
LEDC_ARG_CHECK(channel < LEDC_CHANNEL_MAX, "channel");
LEDC_CHECK(p_ledc_obj[speed_mode] != NULL, LEDC_NOT_INIT, ESP_ERR_INVALID_STATE);
LEDC_CHECK(ledc_fade_channel_init_check(speed_mode, channel) == ESP_OK , LEDC_FADE_INIT_ERROR_STR, ESP_FAIL);
s_ledc_fade_rec[speed_mode][channel]->ledc_fade_callback = cbs->fade_cb;
s_ledc_fade_rec[speed_mode][channel]->cb_user_arg = user_arg;
return ESP_OK;
}
/*
* The functions below are thread-safe version of APIs for duty and fade control.
* These APIs can be called from different tasks.

Wyświetl plik

@ -11,6 +11,8 @@
#include "freertos/task.h"
#include "driver/ledc.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
/*
* About this example
@ -43,7 +45,7 @@
#endif
#define LEDC_LS_TIMER LEDC_TIMER_1
#define LEDC_LS_MODE LEDC_LOW_SPEED_MODE
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3
#if !CONFIG_IDF_TARGET_ESP32
#define LEDC_LS_CH0_GPIO (18)
#define LEDC_LS_CH0_CHANNEL LEDC_CHANNEL_0
#define LEDC_LS_CH1_GPIO (19)
@ -58,6 +60,23 @@
#define LEDC_TEST_DUTY (4000)
#define LEDC_TEST_FADE_TIME (3000)
/*
* This callback function will be called when fade operation has ended
* Use callback only if you are aware it is being called inside an ISR
* Otherwise, you can use a semaphore to unblock tasks
*/
static bool cb_ledc_fade_end_event(const ledc_cb_param_t *param, void *user_arg)
{
portBASE_TYPE taskAwoken = pdFALSE;
if (param->event == LEDC_FADE_END_EVT) {
SemaphoreHandle_t counting_sem = (SemaphoreHandle_t) user_arg;
xSemaphoreGiveFromISR(counting_sem, &taskAwoken);
}
return (taskAwoken == pdTRUE);
}
void app_main(void)
{
int ch;
@ -114,7 +133,7 @@ void app_main(void)
.timer_sel = LEDC_HS_TIMER,
.flags.output_invert = 0
},
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3
#else
{
.channel = LEDC_LS_CH0_CHANNEL,
.duty = 0,
@ -161,6 +180,14 @@ void app_main(void)
// Initialize fade service.
ledc_fade_func_install(0);
ledc_cbs_t callbacks = {
.fade_cb = cb_ledc_fade_end_event
};
SemaphoreHandle_t counting_sem = xSemaphoreCreateCounting(LEDC_TEST_CH_NUM, 0);
for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
ledc_cb_register(ledc_channel[ch].speed_mode, ledc_channel[ch].channel, &callbacks, (void *) counting_sem);
}
while (1) {
printf("1. LEDC fade up to duty = %d\n", LEDC_TEST_DUTY);
@ -170,7 +197,10 @@ void app_main(void)
ledc_fade_start(ledc_channel[ch].speed_mode,
ledc_channel[ch].channel, LEDC_FADE_NO_WAIT);
}
vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS);
for (int i = 0; i < LEDC_TEST_CH_NUM; i++) {
xSemaphoreTake(counting_sem, portMAX_DELAY);
}
printf("2. LEDC fade down to duty = 0\n");
for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
@ -179,7 +209,10 @@ void app_main(void)
ledc_fade_start(ledc_channel[ch].speed_mode,
ledc_channel[ch].channel, LEDC_FADE_NO_WAIT);
}
vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS);
for (int i = 0; i < LEDC_TEST_CH_NUM; i++) {
xSemaphoreTake(counting_sem, portMAX_DELAY);
}
printf("3. LEDC set duty = %d without fade\n", LEDC_TEST_DUTY);
for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {