/* * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include "driver/ledc.h" #include "esp_log.h" #include "led_ledc.h" #include "led_common.h" #define TAG "led_ledc" #define LED_LEDC_CHECK(a, str, action, ...) \ if (unlikely(!(a))) \ { \ ESP_LOGE(TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \ action; \ } #define LEDC_MAX_CHANNEL 8 typedef struct { bool is_init; /*!< Is the channel being used */ bool is_active_level_high; /*!< Set true if GPIO level is high when light is ON, otherwise false. */ ledc_channel_t channel; /*!< LEDC channel (0 - 7) */ } led_indicator_ledc_channel_t; typedef struct { uint8_t channel_num; /*!< Number of LEDC channels already in use */ uint32_t max_duty; /*!< Max duty cycle from duty_resolution : 2^duty_resolution -1 */ led_indicator_ledc_channel_t ledc_channel[LEDC_MAX_CHANNEL]; /*!< LEDC channel state */ } led_indicator_ledc_t; static led_indicator_ledc_t *s_ledc = NULL; esp_err_t led_indicator_ledc_init(void *param) { esp_err_t ret = ESP_OK; const led_indicator_ledc_config_t *cfg = (led_indicator_ledc_config_t *)param; if (!s_ledc) { s_ledc = calloc(1, sizeof(led_indicator_ledc_t)); } LED_LEDC_CHECK(NULL != s_ledc, "alloc fail", return ESP_ERR_NO_MEM); if (!cfg->timer_inited) { ledc_timer_config_t ledc_timer_cfg = LEDC_TIMER_CONFIG(cfg->timer_num); ret = ledc_timer_config(&ledc_timer_cfg); LED_LEDC_CHECK(ESP_OK == ret, "LEDC timer config fail!", goto EXIT); s_ledc->max_duty = pow(2, ledc_timer_cfg.duty_resolution) - 1; } ledc_channel_t ch = cfg->channel; LED_LEDC_CHECK(!s_ledc->ledc_channel[ch].is_init, "LEDC channel is already initialized!", goto EXIT); ledc_channel_config_t ledc_ch_cfg = LEDC_CHANNEL_CONFIG(cfg->timer_num, cfg->channel, cfg->gpio_num); ret = ledc_channel_config(&ledc_ch_cfg); LED_LEDC_CHECK(ESP_OK == ret, "ledc_channel_config fail!", goto EXIT); s_ledc->ledc_channel[ch].channel = ch; s_ledc->ledc_channel[ch].is_init = true; s_ledc->ledc_channel[ch].is_active_level_high = cfg->is_active_level_high; s_ledc->channel_num += 1; return ESP_OK; EXIT: if (s_ledc) { free(s_ledc); s_ledc = NULL; } return ret; } esp_err_t led_indicator_ledc_deinit(void *channel) { uint32_t ch = (uint32_t)channel; LED_LEDC_CHECK(NULL != s_ledc, "LEDC is not initialized", return ESP_FAIL); LED_LEDC_CHECK(s_ledc->ledc_channel[ch].is_init, "LEDC channel doesn't init", return ESP_FAIL); s_ledc->ledc_channel[ch].is_init = false; s_ledc->channel_num -= 1; if (s_ledc->channel_num <= 0 && s_ledc) { free(s_ledc); s_ledc = NULL; } return ESP_OK; } esp_err_t led_indicator_ledc_set_on_off(void *channel, bool on_off) { esp_err_t ret; uint32_t ch = (uint32_t)channel; LED_LEDC_CHECK(s_ledc->ledc_channel[ch].is_init, "LEDC channel doesn't init", return ESP_FAIL); uint32_t duty = on_off ? s_ledc->max_duty : 0; if (!s_ledc->ledc_channel[ch].is_active_level_high) { duty = s_ledc->max_duty - duty; } ret = ledc_set_duty(LEDC_MODE, s_ledc->ledc_channel[ch].channel, duty); LED_LEDC_CHECK(ESP_OK == ret, "LEDC set duty error", return ret); ret = ledc_update_duty(LEDC_MODE, s_ledc->ledc_channel[ch].channel); LED_LEDC_CHECK(ESP_OK == ret, "LEDC update duty error", return ret); return ESP_OK; } esp_err_t led_indicator_ledc_set_brightness(void *channel, uint32_t brightness) { esp_err_t ret; uint32_t ch = (uint32_t)channel; LED_LEDC_CHECK(s_ledc->ledc_channel[ch].is_init, "LEDC channel doesn't init", return ESP_FAIL); LED_LEDC_CHECK(brightness <= UINT8_MAX, "brightness can't be larger than UINT8_MAX", return ESP_FAIL); brightness = s_ledc->ledc_channel[ch].is_active_level_high ? brightness : (UINT8_MAX - brightness); ret = ledc_set_duty(LEDC_MODE, s_ledc->ledc_channel[ch].channel, brightness * s_ledc->max_duty / UINT8_MAX); LED_LEDC_CHECK(ESP_OK == ret, "LEDC set duty error", return ret); ret = ledc_update_duty(LEDC_MODE, s_ledc->ledc_channel[ch].channel); LED_LEDC_CHECK(ESP_OK == ret, "LEDC update duty error", return ret); return ESP_OK; }