From 2fa47825998f795b6304ebdc7bbb65622a4c45c1 Mon Sep 17 00:00:00 2001 From: ChenZhengwei Date: Fri, 9 Aug 2019 16:27:39 +0800 Subject: [PATCH 1/2] feature(ledc):add ledc about esp8266 --- components/esp8266/CMakeLists.txt | 3 +- components/esp8266/driver/ledc.c | 317 ++++++++++++++++++ components/esp8266/include/driver/ledc.h | 208 ++++++++++++ examples/peripherals/ledc/CMakeLists.txt | 6 + examples/peripherals/ledc/Makefile | 9 + examples/peripherals/ledc/README.md | 85 +++++ examples/peripherals/ledc/main/CMakeLists.txt | 3 + examples/peripherals/ledc/main/component.mk | 3 + .../peripherals/ledc/main/ledc_example_main.c | 131 ++++++++ examples/peripherals/ledc/wave.png | Bin 0 -> 1088 bytes 10 files changed, 764 insertions(+), 1 deletion(-) create mode 100644 components/esp8266/driver/ledc.c create mode 100644 components/esp8266/include/driver/ledc.h create mode 100644 examples/peripherals/ledc/CMakeLists.txt create mode 100644 examples/peripherals/ledc/Makefile create mode 100644 examples/peripherals/ledc/README.md create mode 100644 examples/peripherals/ledc/main/CMakeLists.txt create mode 100644 examples/peripherals/ledc/main/component.mk create mode 100644 examples/peripherals/ledc/main/ledc_example_main.c create mode 100644 examples/peripherals/ledc/wave.png diff --git a/components/esp8266/CMakeLists.txt b/components/esp8266/CMakeLists.txt index 88de46d8..31783bda 100644 --- a/components/esp8266/CMakeLists.txt +++ b/components/esp8266/CMakeLists.txt @@ -48,7 +48,8 @@ else() "driver/spi.c" "driver/uart.c" "driver/ir_tx.c" - "driver/ir_rx.c") + "driver/ir_rx.c" + "driver/ledc.c") set(include_dirs "include" "include/driver") diff --git a/components/esp8266/driver/ledc.c b/components/esp8266/driver/ledc.c new file mode 100644 index 00000000..74b330cb --- /dev/null +++ b/components/esp8266/driver/ledc.c @@ -0,0 +1,317 @@ +// Copyright 2018-2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "esp_err.h" +#include "esp_log.h" + +#include "driver/gpio.h" +#include "driver/ledc.h" +#include "driver/pwm.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" + +#define LEDC_PRIORITY (2) +#define LEDC_CHANNEL_MAX (8) +#define LEDC_STEP_TIME (10) +#define LEDC_FLAG_ON (1) +#define LEDC_FLAG_OFF (0) +#define LEDC_TASK_STACK_DEPTH (1024) + +static const char* LEDC_TAG = "ledc"; + +#define LEDC_CHECK(a, str, ret) \ + if (!(a)) { \ + ESP_LOGE(LEDC_TAG, "%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ + return (ret); \ + } + +typedef struct { + ledc_channel_t channel_num; // Channel + uint32_t duty_p; // Duty at present + uint32_t duty; // Duty what we want to + uint32_t step_duty; // Duty/10ms means every 10ms change step_duty + uint32_t step_01duty; // 0.1 of the duty value + uint32_t step_001duty; // 0.01 of the duty value + uint32_t gpio_num;//gpio pins + int16_t phase; //init phase + int fade_time; // Time to duty by fade +} ledc_obj_t; + +QueueHandle_t channel_queue; +static ledc_obj_t *p_ledc_obj[LEDC_CHANNEL_MAX] = { 0 }; +static uint8_t ledc_usr_channel_max = 0; //This is to allocate some channels according to the channel used by the user +static uint32_t ledc_period; + +/** + * @brief set down ledc duty by step + * + * @param channel set channel to change duty + * @param flag tells the caller whether the set duty cycle has been reached + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +static esp_err_t ledc_fade_down(ledc_channel_t channel, uint8_t* flag); + +/** + * @brief set up ledc duty by step + * + * @param channel set channel to change duty + * @param flag tells the caller whether the set duty cycle has been reached + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +static esp_err_t ledc_fade_up(ledc_channel_t channel, uint8_t* flag); + +esp_err_t ledc_timer_config(const ledc_timer_config_t* timer_conf) +{ + // Just freq_hz is useful + // Hz to period + LEDC_CHECK(timer_conf != NULL, "time_conf error", ESP_ERR_INVALID_ARG); + + ledc_period = 1000000 / timer_conf->freq_hz; + + return ESP_OK; +} + +// The difference between the current duty cycle and the target duty cycle +esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t ledc_channel, uint32_t ledc_duty) +{ + LEDC_CHECK(ledc_channel < LEDC_CHANNEL_MAX, "ledc_channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(ledc_duty <= ledc_period, "ledc_duty error", ESP_ERR_INVALID_ARG); + + p_ledc_obj[ledc_channel]->channel_num = ledc_channel; + p_ledc_obj[ledc_channel]->duty = ledc_duty; + pwm_get_duty(ledc_channel, &p_ledc_obj[ledc_channel]->duty_p); + + p_ledc_obj[ledc_channel]->step_duty = (p_ledc_obj[ledc_channel]->duty_p > p_ledc_obj[ledc_channel]->duty ? p_ledc_obj[ledc_channel]->duty_p - p_ledc_obj[ledc_channel]->duty : p_ledc_obj[ledc_channel]->duty - p_ledc_obj[ledc_channel]->duty_p); + ESP_LOGI(LEDC_TAG, "channel_num = %d | duty = %d; duty_p = %d | step_duty = %d;", + p_ledc_obj[ledc_channel]->channel_num, p_ledc_obj[ledc_channel]->duty, + p_ledc_obj[ledc_channel]->duty_p, p_ledc_obj[ledc_channel]->step_duty); + + return ESP_OK; +} + +esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t ledc_channel) +{ + // send the queue + LEDC_CHECK(ledc_channel < LEDC_CHANNEL_MAX, "ledc_channel error", ESP_ERR_INVALID_ARG); + + uint8_t ret = xQueueSend(channel_queue, &ledc_channel, 0); + if (ret != pdPASS) { + ESP_LOGE(LEDC_TAG, "xQueueSend err\r\n"); + return ESP_FAIL; + } + + return ESP_OK; +} + +esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t ledc_channel, uint32_t ledc_duty, int ledc_fade_time) +{ + // For porting, speed_mode is not used + LEDC_CHECK(ledc_channel < LEDC_CHANNEL_MAX, "ledc_channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(ledc_duty <= ledc_period, "ledc_duty error", ESP_ERR_INVALID_ARG); + + p_ledc_obj[ledc_channel]->channel_num = ledc_channel; + p_ledc_obj[ledc_channel]->duty = ledc_duty; + p_ledc_obj[ledc_channel]->fade_time = ledc_fade_time; + pwm_get_duty(ledc_channel, &p_ledc_obj[ledc_channel]->duty_p); + uint32_t duty_value = (p_ledc_obj[ledc_channel]->duty_p > p_ledc_obj[ledc_channel]->duty ? (p_ledc_obj[ledc_channel]->duty_p - p_ledc_obj[ledc_channel]->duty) : (p_ledc_obj[ledc_channel]->duty - p_ledc_obj[ledc_channel]->duty_p)); + + p_ledc_obj[ledc_channel]->step_duty = duty_value / (ledc_fade_time / LEDC_STEP_TIME); + p_ledc_obj[ledc_channel]->step_01duty = duty_value * 10 / (ledc_fade_time / LEDC_STEP_TIME) % 10; + p_ledc_obj[ledc_channel]->step_001duty = duty_value * 100 / (ledc_fade_time / LEDC_STEP_TIME) % 10; + + ESP_LOGI(LEDC_TAG, "channel_num = %d | duty = %d; duty_p = %d | step_duty = %d | step_01duty = %d | step_001duty = %d", + p_ledc_obj[ledc_channel]->channel_num, p_ledc_obj[ledc_channel]->duty, + p_ledc_obj[ledc_channel]->duty_p, p_ledc_obj[ledc_channel]->step_duty, p_ledc_obj[ledc_channel]->step_01duty, p_ledc_obj[ledc_channel]->step_001duty); + + return ESP_OK; +} + +esp_err_t ledc_fade_start(ledc_mode_t speed_mode, ledc_channel_t ledc_channel, ledc_fade_mode_t fade_mode) +{ + LEDC_CHECK(ledc_channel < LEDC_CHANNEL_MAX, "ledc_channel error", ESP_ERR_INVALID_ARG); + + esp_err_t ret; + ret = ledc_update_duty(speed_mode, ledc_channel); + if (ret == ESP_FAIL) { + return ESP_FAIL; + } + + if (fade_mode == LEDC_FADE_WAIT_DONE) { + vTaskDelay(p_ledc_obj[ledc_channel]->fade_time / portTICK_PERIOD_MS); + } + return ESP_OK; +} + +esp_err_t ledc_channel_config(const ledc_channel_config_t* ledc_conf) +{ + LEDC_CHECK(ledc_conf != NULL, "ledc_conf error", ESP_ERR_INVALID_ARG); + LEDC_CHECK( ledc_conf->duty <= ledc_period, "ledc_duty error", ESP_ERR_INVALID_ARG); + + if (p_ledc_obj[ledc_usr_channel_max] == NULL){ + p_ledc_obj[ledc_usr_channel_max] = (ledc_obj_t *)calloc(1, sizeof(ledc_obj_t)); + if (p_ledc_obj[ledc_usr_channel_max] == NULL){ + ESP_LOGE(LEDC_TAG, "LEDC driver malloc error"); + return ESP_FAIL; + } + } + p_ledc_obj[ledc_usr_channel_max]->gpio_num = ledc_conf->gpio_num; + p_ledc_obj[ledc_usr_channel_max]->duty = ledc_conf->duty; + ledc_usr_channel_max++; + + return ESP_OK; +} + +esp_err_t ledc_fade_up(ledc_channel_t channel, uint8_t* flag) +{ + LEDC_CHECK(flag != NULL, "flag error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel < LEDC_CHANNEL_MAX, "ledc_channel error", ESP_ERR_INVALID_ARG); + + static uint8_t i[LEDC_CHANNEL_MAX] = { 0 }; + uint32_t duty_value = 0; + + duty_value = (i[channel] % 10 == 5 ? p_ledc_obj[channel]->step_duty + p_ledc_obj[channel]->step_01duty : p_ledc_obj[channel]->step_duty); + duty_value += (i[channel] == 50 ? p_ledc_obj[channel]->step_001duty : 0); + + if (p_ledc_obj[channel]->duty_p < p_ledc_obj[channel]->duty) { + if (p_ledc_obj[channel]->duty_p + duty_value > p_ledc_obj[channel]->duty) { + p_ledc_obj[channel]->duty_p = p_ledc_obj[channel]->duty; + } else { + p_ledc_obj[channel]->duty_p += duty_value; + } + pwm_set_duty(channel, p_ledc_obj[channel]->duty_p); + pwm_start(); + i[channel]++; + if (i[channel] == 100) { + i[channel] = 0; + } + } + if (p_ledc_obj[channel]->duty_p == p_ledc_obj[channel]->duty) { + *flag = LEDC_FLAG_OFF; + ESP_LOGI(LEDC_TAG, "channel%d is end", channel); + } + + return ESP_OK; +} + +esp_err_t ledc_fade_down(ledc_channel_t channel, uint8_t* flag) +{ + LEDC_CHECK(flag != NULL, "flag error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel < LEDC_CHANNEL_MAX, "ledc_channel error", ESP_ERR_INVALID_ARG); + + static uint8_t i[LEDC_CHANNEL_MAX] = { 0 }; + uint32_t duty_value = 0; + + duty_value = (i[channel] % 10 == 5 ? p_ledc_obj[channel]->step_duty + p_ledc_obj[channel]->step_01duty : p_ledc_obj[channel]->step_duty); + duty_value += (i[channel] == 50 ? p_ledc_obj[channel]->step_001duty : 0); + + if (p_ledc_obj[channel]->duty_p > p_ledc_obj[channel]->duty) { + // it is more smart than 'p_ledc_obj[channel].duty_p - p_ledc_obj[channel].step_duty < p_ledc_obj[channel].duty' + if (p_ledc_obj[channel]->duty_p < p_ledc_obj[channel]->duty + duty_value) { + p_ledc_obj[channel]->duty_p = p_ledc_obj[channel]->duty; + } else { + p_ledc_obj[channel]->duty_p -= duty_value; + } + pwm_set_duty(channel, p_ledc_obj[channel]->duty_p); + pwm_start(); + i[channel]++; + if (i[channel] == 100) { + i[channel] = 0; + } + } + if (p_ledc_obj[channel]->duty_p == p_ledc_obj[channel]->duty) { + *flag = LEDC_FLAG_OFF; + ESP_LOGI(LEDC_TAG, "channel%d is end", channel); + } + + return ESP_OK; +} + +// Complete message queue reception while changing duty cycle +static void ledc_task(void* pvParameters) +{ + ledc_channel_t channel; + uint8_t i; + uint8_t flag[LEDC_CHANNEL_MAX] = { 0 }; + + while (1) { + + while (pdTRUE == xQueueReceive(channel_queue, &channel, 0)) { + flag[channel] = LEDC_FLAG_ON; + ESP_LOGI(LEDC_TAG, "channel%d is start", channel); + } + vTaskSuspendAll(); + for (i = 0; i < ledc_usr_channel_max; i++) { + if (flag[i] == LEDC_FLAG_ON) { + if (p_ledc_obj[i]->duty_p < p_ledc_obj[i]->duty) { + ledc_fade_up(i, &flag[i]); + } else { + ledc_fade_down(i, &flag[i]); + } + } + } + xTaskResumeAll(); + vTaskDelay(LEDC_STEP_TIME / portTICK_PERIOD_MS); + } + vTaskDelete(NULL); +} + +esp_err_t ledc_fade_func_install(int intr_alloc_flags) +{ + int16_t ledc_phase[LEDC_CHANNEL_MAX] = {0}; + uint32_t ledc_duty[LEDC_CHANNEL_MAX] = {0}; + uint32_t ledc_gpio_num[LEDC_CHANNEL_MAX] = {0}; + + for (int i = 0; i < ledc_usr_channel_max; i++){ + ledc_gpio_num[i] = p_ledc_obj[i]->gpio_num; + ledc_duty[i] = p_ledc_obj[i]->duty; + } + + LEDC_CHECK(ledc_usr_channel_max < LEDC_CHANNEL_MAX, "flag error", ESP_ERR_INVALID_ARG); + pwm_init(ledc_period, ledc_duty, ledc_usr_channel_max, ledc_gpio_num); + ESP_LOGI(LEDC_TAG, "ledc_usr_channel_max:%d", ledc_usr_channel_max); + for (int i = 0; i < ledc_usr_channel_max; i++) { + ESP_LOGI(LEDC_TAG, "gpio:%d", ledc_gpio_num[i]); + } + + + pwm_set_phases(ledc_phase); + channel_queue = xQueueCreate(ledc_usr_channel_max, sizeof(uint8_t)); + if (channel_queue == 0) { + ESP_LOGE(LEDC_TAG, "xQueueCreate err\r\n"); + return ESP_ERR_INVALID_STATE; + } + + xTaskCreate(ledc_task, "ledc_task", LEDC_TASK_STACK_DEPTH, NULL, LEDC_PRIORITY, NULL); + + return pwm_start(); +} + +esp_err_t ledc_fade_func_uninstall(void) +{ + for (int i = 0; i < ledc_usr_channel_max; i++){ + free(p_ledc_obj[i]); + p_ledc_obj[i] = NULL; + } + return pwm_stop(0x00); +} \ No newline at end of file diff --git a/components/esp8266/include/driver/ledc.h b/components/esp8266/include/driver/ledc.h new file mode 100644 index 00000000..b447b279 --- /dev/null +++ b/components/esp8266/include/driver/ledc.h @@ -0,0 +1,208 @@ +// Copyright 2018-2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define LEDC_APB_CLK_HZ (APB_CLK_FREQ) +#define LEDC_REF_CLK_HZ (1*1000000) +#define LEDC_ERR_DUTY (0xFFFFFFFF) +#define LEDC_ERR_VAL (-1) + +typedef enum { + LEDC_HIGH_SPEED_MODE = 0, /*!< LEDC high speed speed_mode */ + LEDC_LOW_SPEED_MODE, /*!< LEDC low speed speed_mode */ + LEDC_SPEED_MODE_MAX, /*!< LEDC speed limit */ +} ledc_mode_t; + +typedef enum { + LEDC_INTR_DISABLE = 0, /*!< Disable LEDC interrupt */ + LEDC_INTR_FADE_END, /*!< Enable LEDC interrupt */ +} ledc_intr_type_t; + +typedef enum { + LEDC_TIMER_0 = 0, /*!< LEDC timer 0 */ + LEDC_TIMER_1, /*!< LEDC timer 1 */ + LEDC_TIMER_2, /*!< LEDC timer 2 */ + LEDC_TIMER_3, /*!< LEDC timer 3 */ + LEDC_TIMER_MAX, +} ledc_timer_t; + +typedef enum { + LEDC_CHANNEL_0 = 0, /*!< LEDC channel 0 */ + LEDC_CHANNEL_1, /*!< LEDC channel 1 */ + LEDC_CHANNEL_2, /*!< LEDC channel 2 */ + LEDC_CHANNEL_3, /*!< LEDC channel 3 */ + LEDC_CHANNEL_4, /*!< LEDC channel 4 */ + 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 { + LEDC_TIMER_1_BIT = 1, /*!< LEDC PWM duty resolution of 1 bits */ + LEDC_TIMER_2_BIT, /*!< LEDC PWM duty resolution of 2 bits */ + LEDC_TIMER_3_BIT, /*!< LEDC PWM duty resolution of 3 bits */ + LEDC_TIMER_4_BIT, /*!< LEDC PWM duty resolution of 4 bits */ + LEDC_TIMER_5_BIT, /*!< LEDC PWM duty resolution of 5 bits */ + LEDC_TIMER_6_BIT, /*!< LEDC PWM duty resolution of 6 bits */ + LEDC_TIMER_7_BIT, /*!< LEDC PWM duty resolution of 7 bits */ + LEDC_TIMER_8_BIT, /*!< LEDC PWM duty resolution of 8 bits */ + LEDC_TIMER_9_BIT, /*!< LEDC PWM duty resolution of 9 bits */ + LEDC_TIMER_10_BIT, /*!< LEDC PWM duty resolution of 10 bits */ + LEDC_TIMER_11_BIT, /*!< LEDC PWM duty resolution of 11 bits */ + LEDC_TIMER_12_BIT, /*!< LEDC PWM duty resolution of 12 bits */ + LEDC_TIMER_13_BIT, /*!< LEDC PWM duty resolution of 13 bits */ + LEDC_TIMER_14_BIT, /*!< LEDC PWM duty resolution of 14 bits */ + LEDC_TIMER_15_BIT, /*!< LEDC PWM duty resolution of 15 bits */ + LEDC_TIMER_16_BIT, /*!< LEDC PWM duty resolution of 16 bits */ + LEDC_TIMER_17_BIT, /*!< LEDC PWM duty resolution of 17 bits */ + LEDC_TIMER_18_BIT, /*!< LEDC PWM duty resolution of 18 bits */ + LEDC_TIMER_19_BIT, /*!< LEDC PWM duty resolution of 19 bits */ + LEDC_TIMER_20_BIT, /*!< LEDC PWM duty resolution of 20 bits */ + LEDC_TIMER_BIT_MAX, +} 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 Timer timer for ledc_timer_config function + */ +typedef struct { + ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */ + union { + ledc_timer_bit_t duty_resolution; /*!< LEDC channel duty resolution */ + }; + ledc_timer_t timer_num; /*!< The timer source of channel (0 - 3) */ + uint32_t freq_hz; /*!< LEDC timer frequency (100Hz ~ 1KHz) */ +} ledc_timer_config_t; + +/** + * @brief Configuration parameters of LEDC channel for ledc_channel_config function + */ +typedef struct { + int gpio_num; /*!< the LEDC output gpio_num, if you want to use gpio16, gpio_num = 16 */ + ledc_mode_t speed_mode; /*!< Invalid parameter, compatible with esp32 API. Configure interrupt, LEDC speed speed_mode, high-speed mode or low-speed mode */ + ledc_channel_t channel; /*!< LEDC channel (0 - 7) */ + ledc_intr_type_t intr_type; /*!< Invalid parameter, compatible with esp32 API. Configure interrupt,Fade interrupt enable or Fade interrupt disable */ + ledc_timer_t timer_sel; /*!< Invalid parameter, compatible with esp32 API. Select the timer source of channel (0 - 3) */ + uint32_t duty; /*!< LEDC channel duty, the range of duty setting is [0, (2**duty_resolution)] */ + int hpoint; /*!< Invalid parameter, compatible with esp32 API.LEDC channel hpoint value, the max value is 0xfffff */ +} ledc_channel_config_t; + +/** + * @brief set ledc duty + * + * @param speed_mode unnecessary parameters, just for code unity + * @param ledc_channel ledc channel num + * @param ledc_duty set the ledc duty you want + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t ledc_channel, uint32_t ledc_duty); + +/** + * @brief update ledc duty + * + * @param speed_mode unnecessary parameters, just for code unity + * @param ledc_channel ledc channel num + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_FAIL IO error + */ +esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t ledc_channel); + +/** + * @brief set ledc duty by fade + * + * @param speed_mode unnecessary parameters, just for code unity + * @param ledc_channel ledc channel num + * @param ledc_duty set ledc duty + * @param ledc_fade_time set ledc fade time + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t ledc_channel, uint32_t ledc_duty, int ledc_fade_time); + +/** + * @brief start change ledc duty by fade + * @param speed_mode unnecessary parameters, just for code unity + * @param ledc_channel ledc channel num + * @param fade_mode set fade mode, for example set LEDC_FADE_NO_WAIT means LEDC fade function will return immediately + * set LEDC_FADE_WAIT_DONE means LEDC fade function will block until fading to the target duty + * + *@return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_FAIL IO error + */ +esp_err_t ledc_fade_start(ledc_mode_t speed_mode, ledc_channel_t ledc_channel, ledc_fade_mode_t fade_mode); + +/** + * @brief LEDC channel configuration + * Configure LEDC channel with the given channel/output gpio_num/interrupt/source timer/frequency(Hz)/LEDC duty resolution + * + * @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(const ledc_channel_config_t* ledc_conf); + +/** + * @brief LEDC timer configuration + * Configure LEDC timer with the given source timer/frequency(Hz)/duty_resolution + * + * @param timer_conf Pointer of LEDC timer configure struct + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t ledc_timer_config(const ledc_timer_config_t* timer_conf); + +/** + * @brief Install LEDC fade function. This function will occupy interrupt of LEDC module. + * @param intr_alloc_flags unnecessary parameters, just for code unity, maybe will be used later + * + * @return + * - 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_fade_func_uninstall(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/examples/peripherals/ledc/CMakeLists.txt b/examples/peripherals/ledc/CMakeLists.txt new file mode 100644 index 00000000..79e371e1 --- /dev/null +++ b/examples/peripherals/ledc/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ledc) diff --git a/examples/peripherals/ledc/Makefile b/examples/peripherals/ledc/Makefile new file mode 100644 index 00000000..675fc8a3 --- /dev/null +++ b/examples/peripherals/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/peripherals/ledc/README.md b/examples/peripherals/ledc/README.md new file mode 100644 index 00000000..31ab2ab3 --- /dev/null +++ b/examples/peripherals/ledc/README.md @@ -0,0 +1,85 @@ +# _ledc Example_ + +- Ledc is implemented by pwm, and this example will show you how to use ledc module by running four channels, but this is only designed to be compatible with the ESP32 ledc interface, many parameters are actually invalid. +- Observe ledc signal with logic analyzer or oscilloscope. +- Since ledc is based on PWM, using ledc will occupy PWM channel. +- Since the step value of the gradient is 10ms, there may be an error of about 10ms between different channels. + +## Pin assignment + + * GPIO13 is assigned as the LEDC_HS_CH0_GPIO . + * GPIO14 is assigned as the LEDC_HS_CH1_GPIO . + * GPIO15 is assigned as the LEDC_HS_CH2_GPIO . + * GPIO12 is assigned as the LEDC_HS_CH3_GPIO . + +## How to use example + +### Hardware Required + +- Connection: + - Connect the ledc channel to a logic analyzer or oscilloscope. + +### Configure the project + +``` +make menuconfig +``` + +- Set serial port under Serial Flasher Options. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +make -j4 flash monitor +``` + +(To exit the serial monitor, type `Ctrl-]`.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +``` + +I (497) gpio: GPIO[12]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (507) gpio: GPIO[13]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (517) gpio: GPIO[14]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (527) gpio: GPIO[15]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (547) pwm: --- PWM v3.2 + +I (547) ledc: ledc_channel_max:4 + +I (557) ledc: gpio:13 + +I (567) ledc: gpio:14 + +I (567) ledc: gpio:15 + +I (567) ledc: gpio:12 + +I (577) main: 1. LEDC fade up to duty = 980 + +I (587) ledc: channel_num = 0 | duty = 980; duty_p = 0 | step_duty = 3 | step_01duty = 2 | step_001duty = 6 + +I (597) ledc: channel_num = 1 | duty = 980; duty_p = 0 | step_duty = 3 | step_01duty = 2 | step_001duty = 6 + +I (617) ledc: channel_num = 2 | duty = 980; duty_p = 0 | step_duty = 3 | step_01duty = 2 | step_001duty = 6 + +I (637) ledc: channel_num = 3 | duty = 980; duty_p = 0 | step_duty = 3 | step_01duty = 2 | step_001duty = 6 + +I (647) ledc: channel0 is start +I (657) ledc: channel1 is start +I (657) ledc: channel2 is start +I (667) ledc: channel3 is start +I (3677) ledc: channel0 is end +I (3677) ledc: channel1 is end +I (3677) ledc: channel2 is end +I (3677) ledc: channel3 is end + +``` + +- WAVE FORM: + +![wave](wave.png) diff --git a/examples/peripherals/ledc/main/CMakeLists.txt b/examples/peripherals/ledc/main/CMakeLists.txt new file mode 100644 index 00000000..178e1209 --- /dev/null +++ b/examples/peripherals/ledc/main/CMakeLists.txt @@ -0,0 +1,3 @@ +set(COMPONENT_SRCS "ledc_example_main.c") + +register_component() diff --git a/examples/peripherals/ledc/main/component.mk b/examples/peripherals/ledc/main/component.mk new file mode 100644 index 00000000..44bd2b52 --- /dev/null +++ b/examples/peripherals/ledc/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/peripherals/ledc/main/ledc_example_main.c b/examples/peripherals/ledc/main/ledc_example_main.c new file mode 100644 index 00000000..f5eb9743 --- /dev/null +++ b/examples/peripherals/ledc/main/ledc_example_main.c @@ -0,0 +1,131 @@ +// Copyright 2018-2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" + +#include "esp_err.h" +#include "esp_log.h" + +#include "driver/ledc.h" + +#define LEDC_HS_CH0_GPIO (13) +#define LEDC_HS_CH1_GPIO (14) +#define LEDC_LS_CH2_GPIO (15) +#define LEDC_LS_CH3_GPIO (12) + +#define LEDC_HS_CH0_CHANNEL LEDC_CHANNEL_0 +#define LEDC_HS_CH1_CHANNEL LEDC_CHANNEL_1 +#define LEDC_LS_CH2_CHANNEL LEDC_CHANNEL_2 +#define LEDC_LS_CH3_CHANNEL LEDC_CHANNEL_3 + +#define LEDC_TEST_CH_NUM (4) +#define LEDC_TEST_FREQ (1000) //1KHz +#define LEDC_TEST_DUTY (900) //duty = LEDC_TEST_DUTY / LEDC_TEST_PERIOD & LEDC_TEST_PERIOD = 1,000,000 / LEDC_TEST_FREQ +#define LEDC_TEST_FADE_TIME (3000) + +static const char* TAG = "main"; + +void app_main() +{ + int ch; + /* + * Prepare and set configuration of timers + * that will be used by LED Controller + */ + + ledc_timer_config_t ledc_timer = { + .duty_resolution = LEDC_TIMER_1_BIT, // resolution of PWM duty(Invalid parameter, compatible with esp32 API) + .freq_hz = LEDC_TEST_FREQ, // frequency of PWM signal + .speed_mode = 0, // timer mode (Invalid parameter, compatible with esp32 API) + .timer_num = 0 // timer index (Invalid parameter, compatible with esp32 API) + }; + // Set configuration of timer0 for high speed channels + ledc_timer_config(&ledc_timer); + + /* + * Prepare individual configuration + * for each channel of LED Controller + * by selecting: + * - controller's channel number + * - output duty cycle, set initially to 0 + * - GPIO number where LED is connected to + * - speed mode, either high or low + * - timer servicing selected channel + * Note: Speed_mode and timer_sel are only compatible with esp32, + * but it is not required in esp8266, so it can be set to 0 when + * using esp8266 + */ + ledc_channel_config_t ledc_channel[LEDC_TEST_CH_NUM] = { + { .channel = LEDC_HS_CH0_CHANNEL, + .duty = 0, + .gpio_num = LEDC_HS_CH0_GPIO, + .speed_mode = 0, + .timer_sel = 0 }, + { .channel = LEDC_HS_CH1_CHANNEL, + .duty = 0, + .gpio_num = LEDC_HS_CH1_GPIO, + .speed_mode = 0, + .timer_sel = 0 }, + { .channel = LEDC_LS_CH2_CHANNEL, + .duty = 0, + .gpio_num = LEDC_LS_CH2_GPIO, + .speed_mode = 0, + .timer_sel = 0 }, + { .channel = LEDC_LS_CH3_CHANNEL, + .duty = 0, + .gpio_num = LEDC_LS_CH3_GPIO, + .speed_mode = 0, + .timer_sel = 0 }, + }; + + // Set LED Controller with previously prepared configuration + for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { + ledc_channel_config(&ledc_channel[ch]); + } + + // Initialize fade service. + ledc_fade_func_install(0); + + while (1) { + ESP_LOGI(TAG, "1. LEDC fade up to duty = %d\n", LEDC_TEST_DUTY); + for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { + ledc_set_fade_with_time(ledc_channel[ch].speed_mode, + ledc_channel[ch].channel, LEDC_TEST_DUTY, LEDC_TEST_FADE_TIME); + } + for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { + ledc_fade_start(ledc_channel[ch].speed_mode, + ledc_channel[ch].channel, LEDC_FADE_NO_WAIT); + } + vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS); + vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS); + + ESP_LOGI(TAG, "2. LEDC fade down to duty = 0\n"); + for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { + ledc_set_fade_with_time(ledc_channel[ch].speed_mode, + ledc_channel[ch].channel, 0, LEDC_TEST_FADE_TIME); + } + for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { + ledc_fade_start(ledc_channel[ch].speed_mode, + ledc_channel[ch].channel, LEDC_FADE_NO_WAIT); + } + vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS); + vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS); + } +} diff --git a/examples/peripherals/ledc/wave.png b/examples/peripherals/ledc/wave.png new file mode 100644 index 0000000000000000000000000000000000000000..7c52a98b8d4c67a07b3600f6b677851d621ee397 GIT binary patch literal 1088 zcmeAS@N?(olHy`uVBq!ia0y~yV6p+SS8^}`$pw)GB0!3{*vT`5gM;JtL;nXru4IX8 zL`iUdT1k0gQ7S`vUVd(>ZgFx^YHD6_Mt;eA=KXd+EtfrA978JRyuIt#^~gb@A#kdU z<9R{^c)33A`U=UB*B4JeU|*SY`=a-Q_Bs1@H7@tx@~_E%uipHsQos1?H!eS~ z5@_JL$fV%Bh2eyNE`yN^Hwss8`|YomlKbT2?xonWH6NTH5!Yt(p=SPbVQZb&uj)VT z&WWG()?0da?K*ZwY}%owC4Vx0Qp2&{N+$Ke%g?8PiH_t9T{* zbFHg5|4jJ4=iJNJQq{lI)*te-`}%6nt60DIy9X%pp0?;NBE4r-#i&~MS-S7hmyb`R z4H-@p*m4jVNw}lXkhkXArghBokG-#k#h%sj{N)obTK(KT{pXd{#lLpV+4nbwp9xP= zIQFz`x1IXoc7FZ*O>)f#6Mk6DdH=aaDkU=f|K$6rzb}Ejcl-QT1$?Q>r|#KIaeZc) zqc3k~J~2;XI8gr-f0Dr+V@T;MIqF*JgU52W%|2xQS++5sY5)0`uZwFh? zR*XL`F5fu*^K8CF*X85t5|$QxR;t#)dR}_22WQ%mvv4FO#m!l Bo4^17 literal 0 HcmV?d00001 From 046ba744fa0ed60db3f1ee5d4e73bc171e9d6a46 Mon Sep 17 00:00:00 2001 From: chenzhengwei Date: Thu, 10 Oct 2019 15:33:22 +0800 Subject: [PATCH 2/2] feature(ledc): Redefine duty --- components/esp8266/driver/ledc.c | 41 +++- components/esp8266/include/driver/ledc.h | 22 +++ .../peripherals/ledc/main/ledc_example_main.c | 186 ++++++++++-------- 3 files changed, 162 insertions(+), 87 deletions(-) diff --git a/components/esp8266/driver/ledc.c b/components/esp8266/driver/ledc.c index 74b330cb..14169d08 100644 --- a/components/esp8266/driver/ledc.c +++ b/components/esp8266/driver/ledc.c @@ -33,6 +33,7 @@ #define LEDC_FLAG_ON (1) #define LEDC_FLAG_OFF (0) #define LEDC_TASK_STACK_DEPTH (1024) +#define LEDC_MAX_DUTY (8196) static const char* LEDC_TAG = "ledc"; @@ -96,13 +97,14 @@ esp_err_t ledc_timer_config(const ledc_timer_config_t* timer_conf) esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t ledc_channel, uint32_t ledc_duty) { LEDC_CHECK(ledc_channel < LEDC_CHANNEL_MAX, "ledc_channel error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(ledc_duty <= ledc_period, "ledc_duty error", ESP_ERR_INVALID_ARG); - + LEDC_CHECK(ledc_period * ledc_duty / LEDC_MAX_DUTY <= ledc_period, "ledc_duty error", ESP_ERR_INVALID_ARG); + p_ledc_obj[ledc_channel]->channel_num = ledc_channel; - p_ledc_obj[ledc_channel]->duty = ledc_duty; + p_ledc_obj[ledc_channel]->duty = ledc_period * ledc_duty / LEDC_MAX_DUTY; pwm_get_duty(ledc_channel, &p_ledc_obj[ledc_channel]->duty_p); p_ledc_obj[ledc_channel]->step_duty = (p_ledc_obj[ledc_channel]->duty_p > p_ledc_obj[ledc_channel]->duty ? p_ledc_obj[ledc_channel]->duty_p - p_ledc_obj[ledc_channel]->duty : p_ledc_obj[ledc_channel]->duty - p_ledc_obj[ledc_channel]->duty_p); + //The duty print value for this channel is duty/period(Program internal value), corresponding to duty/ledc_max_duty ESP_LOGI(LEDC_TAG, "channel_num = %d | duty = %d; duty_p = %d | step_duty = %d;", p_ledc_obj[ledc_channel]->channel_num, p_ledc_obj[ledc_channel]->duty, p_ledc_obj[ledc_channel]->duty_p, p_ledc_obj[ledc_channel]->step_duty); @@ -128,10 +130,10 @@ esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t ledc_ch { // For porting, speed_mode is not used LEDC_CHECK(ledc_channel < LEDC_CHANNEL_MAX, "ledc_channel error", ESP_ERR_INVALID_ARG); - LEDC_CHECK(ledc_duty <= ledc_period, "ledc_duty error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(ledc_period * ledc_duty / LEDC_MAX_DUTY <= ledc_period, "ledc_duty error", ESP_ERR_INVALID_ARG); p_ledc_obj[ledc_channel]->channel_num = ledc_channel; - p_ledc_obj[ledc_channel]->duty = ledc_duty; + p_ledc_obj[ledc_channel]->duty = ledc_period * ledc_duty / LEDC_MAX_DUTY; p_ledc_obj[ledc_channel]->fade_time = ledc_fade_time; pwm_get_duty(ledc_channel, &p_ledc_obj[ledc_channel]->duty_p); uint32_t duty_value = (p_ledc_obj[ledc_channel]->duty_p > p_ledc_obj[ledc_channel]->duty ? (p_ledc_obj[ledc_channel]->duty_p - p_ledc_obj[ledc_channel]->duty) : (p_ledc_obj[ledc_channel]->duty - p_ledc_obj[ledc_channel]->duty_p)); @@ -140,6 +142,7 @@ esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t ledc_ch p_ledc_obj[ledc_channel]->step_01duty = duty_value * 10 / (ledc_fade_time / LEDC_STEP_TIME) % 10; p_ledc_obj[ledc_channel]->step_001duty = duty_value * 100 / (ledc_fade_time / LEDC_STEP_TIME) % 10; + //The duty print value for this channel is duty/period(Program internal value), corresponding to duty/ledc_max_duty ESP_LOGI(LEDC_TAG, "channel_num = %d | duty = %d; duty_p = %d | step_duty = %d | step_01duty = %d | step_001duty = %d", p_ledc_obj[ledc_channel]->channel_num, p_ledc_obj[ledc_channel]->duty, p_ledc_obj[ledc_channel]->duty_p, p_ledc_obj[ledc_channel]->step_duty, p_ledc_obj[ledc_channel]->step_01duty, p_ledc_obj[ledc_channel]->step_001duty); @@ -176,7 +179,7 @@ esp_err_t ledc_channel_config(const ledc_channel_config_t* ledc_conf) } } p_ledc_obj[ledc_usr_channel_max]->gpio_num = ledc_conf->gpio_num; - p_ledc_obj[ledc_usr_channel_max]->duty = ledc_conf->duty; + p_ledc_obj[ledc_usr_channel_max]->duty = ledc_period * ledc_conf->duty / LEDC_MAX_DUTY; ledc_usr_channel_max++; return ESP_OK; @@ -208,7 +211,6 @@ esp_err_t ledc_fade_up(ledc_channel_t channel, uint8_t* flag) } if (p_ledc_obj[channel]->duty_p == p_ledc_obj[channel]->duty) { *flag = LEDC_FLAG_OFF; - ESP_LOGI(LEDC_TAG, "channel%d is end", channel); } return ESP_OK; @@ -241,7 +243,6 @@ esp_err_t ledc_fade_down(ledc_channel_t channel, uint8_t* flag) } if (p_ledc_obj[channel]->duty_p == p_ledc_obj[channel]->duty) { *flag = LEDC_FLAG_OFF; - ESP_LOGI(LEDC_TAG, "channel%d is end", channel); } return ESP_OK; @@ -258,7 +259,6 @@ static void ledc_task(void* pvParameters) while (pdTRUE == xQueueReceive(channel_queue, &channel, 0)) { flag[channel] = LEDC_FLAG_ON; - ESP_LOGI(LEDC_TAG, "channel%d is start", channel); } vTaskSuspendAll(); for (i = 0; i < ledc_usr_channel_max; i++) { @@ -294,7 +294,6 @@ esp_err_t ledc_fade_func_install(int intr_alloc_flags) ESP_LOGI(LEDC_TAG, "gpio:%d", ledc_gpio_num[i]); } - pwm_set_phases(ledc_phase); channel_queue = xQueueCreate(ledc_usr_channel_max, sizeof(uint8_t)); if (channel_queue == 0) { @@ -314,4 +313,26 @@ esp_err_t ledc_fade_func_uninstall(void) p_ledc_obj[i] = NULL; } return pwm_stop(0x00); +} + +esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level) +{ + LEDC_CHECK(idle_level == 0 || idle_level == 1, "idle_level error", ESP_ERR_INVALID_ARG); + + static uint32_t stop_level_mask = 0x0; + if (idle_level == 0){ + stop_level_mask = stop_level_mask | 0x1 << channel; + } + else if (idle_level == 1){ + stop_level_mask = stop_level_mask & ~(0x1 << channel); + } + + pwm_stop(stop_level_mask); + + return ESP_OK; +} + +int periph_module_enable(int none) +{ + return ESP_OK; } \ No newline at end of file diff --git a/components/esp8266/include/driver/ledc.h b/components/esp8266/include/driver/ledc.h index b447b279..6b44bdca 100644 --- a/components/esp8266/include/driver/ledc.h +++ b/components/esp8266/include/driver/ledc.h @@ -18,6 +18,8 @@ extern "C" { #endif +#include "esp_err.h" +#define PERIPH_LEDC_MODULE (0) #define LEDC_APB_CLK_HZ (APB_CLK_FREQ) #define LEDC_REF_CLK_HZ (1*1000000) #define LEDC_ERR_DUTY (0xFFFFFFFF) @@ -91,6 +93,7 @@ typedef struct { ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */ union { ledc_timer_bit_t duty_resolution; /*!< LEDC channel duty resolution */ + ledc_timer_bit_t bit_num __attribute__((deprecated)); /*!< Deprecated in ESP-IDF 3.0. This is an alias to 'duty_resolution' for backward compatibility with ESP-IDF 2.1 */ }; ledc_timer_t timer_num; /*!< The timer source of channel (0 - 3) */ uint32_t freq_hz; /*!< LEDC timer frequency (100Hz ~ 1KHz) */ @@ -203,6 +206,25 @@ esp_err_t ledc_fade_func_install(int intr_alloc_flags); */ esp_err_t ledc_fade_func_uninstall(void); +/** + * @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 + * @param channel LEDC channel (0-7), select from ledc_channel_t + * @param idle_level Set output idle level after LEDC stops. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level); + +/** + * it is an empty function + */ +int periph_module_enable(int none); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/examples/peripherals/ledc/main/ledc_example_main.c b/examples/peripherals/ledc/main/ledc_example_main.c index f5eb9743..28f0b42e 100644 --- a/examples/peripherals/ledc/main/ledc_example_main.c +++ b/examples/peripherals/ledc/main/ledc_example_main.c @@ -1,64 +1,76 @@ -// Copyright 2018-2025 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +/* LEDC (LED Controller) 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 -#include - #include "freertos/FreeRTOS.h" -#include "freertos/queue.h" #include "freertos/task.h" - -#include "esp_err.h" -#include "esp_log.h" - #include "driver/ledc.h" +#include "esp_err.h" -#define LEDC_HS_CH0_GPIO (13) -#define LEDC_HS_CH1_GPIO (14) -#define LEDC_LS_CH2_GPIO (15) -#define LEDC_LS_CH3_GPIO (12) +/* + * About this example + * + * 1. Start with initializing LEDC module: + * a. Set the timer of LEDC first, this determines the frequency + * and resolution of PWM. + * b. Then set the LEDC channel you want to use, + * and bind with one of the timers. + * + * 2. You need first to 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 uses GPIO18/19/4/5 as LEDC output, + * and it will change the duty repeatedly. + * + * 5. GPIO18/19 are from high speed channel group. + * GPIO4/5 are from low speed channel group. + * + */ +#define LEDC_HS_TIMER LEDC_TIMER_0 +#define LEDC_HS_MODE LEDC_HIGH_SPEED_MODE +#define LEDC_HS_CH0_GPIO (12) +#define LEDC_HS_CH0_CHANNEL LEDC_CHANNEL_0 +#define LEDC_HS_CH1_GPIO (14) +#define LEDC_HS_CH1_CHANNEL LEDC_CHANNEL_1 -#define LEDC_HS_CH0_CHANNEL LEDC_CHANNEL_0 -#define LEDC_HS_CH1_CHANNEL LEDC_CHANNEL_1 -#define LEDC_LS_CH2_CHANNEL LEDC_CHANNEL_2 -#define LEDC_LS_CH3_CHANNEL LEDC_CHANNEL_3 +#define LEDC_LS_TIMER LEDC_TIMER_1 +#define LEDC_LS_MODE LEDC_LOW_SPEED_MODE +#define LEDC_LS_CH2_GPIO (4) +#define LEDC_LS_CH2_CHANNEL LEDC_CHANNEL_2 +#define LEDC_LS_CH3_GPIO (15) +#define LEDC_LS_CH3_CHANNEL LEDC_CHANNEL_3 -#define LEDC_TEST_CH_NUM (4) -#define LEDC_TEST_FREQ (1000) //1KHz -#define LEDC_TEST_DUTY (900) //duty = LEDC_TEST_DUTY / LEDC_TEST_PERIOD & LEDC_TEST_PERIOD = 1,000,000 / LEDC_TEST_FREQ -#define LEDC_TEST_FADE_TIME (3000) - -static const char* TAG = "main"; +#define LEDC_TEST_CH_NUM (4) +#define LEDC_TEST_DUTY (4096) +#define LEDC_TEST_FADE_TIME (3000) void app_main() { int ch; + /* * Prepare and set configuration of timers * that will be used by LED Controller */ - ledc_timer_config_t ledc_timer = { - .duty_resolution = LEDC_TIMER_1_BIT, // resolution of PWM duty(Invalid parameter, compatible with esp32 API) - .freq_hz = LEDC_TEST_FREQ, // frequency of PWM signal - .speed_mode = 0, // timer mode (Invalid parameter, compatible with esp32 API) - .timer_num = 0 // timer index (Invalid parameter, compatible with esp32 API) + .duty_resolution = LEDC_TIMER_13_BIT, // resolution of PWM duty + .freq_hz = 5000, // frequency of PWM signal + .speed_mode = LEDC_HS_MODE, // timer mode + .timer_num = LEDC_HS_TIMER // timer index }; // Set configuration of timer0 for high speed channels ledc_timer_config(&ledc_timer); + // Prepare and set configuration of timer1 for low speed channels + ledc_timer.speed_mode = LEDC_LS_MODE; + ledc_timer.timer_num = LEDC_LS_TIMER; + ledc_timer_config(&ledc_timer); + /* * Prepare individual configuration * for each channel of LED Controller @@ -68,31 +80,43 @@ void app_main() * - GPIO number where LED is connected to * - speed mode, either high or low * - timer servicing selected channel - * Note: Speed_mode and timer_sel are only compatible with esp32, - * but it is not required in esp8266, so it can be set to 0 when - * using esp8266 + * Note: if different channels use one timer, + * then frequency and bit_num of these channels + * will be the same */ ledc_channel_config_t ledc_channel[LEDC_TEST_CH_NUM] = { - { .channel = LEDC_HS_CH0_CHANNEL, - .duty = 0, - .gpio_num = LEDC_HS_CH0_GPIO, - .speed_mode = 0, - .timer_sel = 0 }, - { .channel = LEDC_HS_CH1_CHANNEL, - .duty = 0, - .gpio_num = LEDC_HS_CH1_GPIO, - .speed_mode = 0, - .timer_sel = 0 }, - { .channel = LEDC_LS_CH2_CHANNEL, - .duty = 0, - .gpio_num = LEDC_LS_CH2_GPIO, - .speed_mode = 0, - .timer_sel = 0 }, - { .channel = LEDC_LS_CH3_CHANNEL, - .duty = 0, - .gpio_num = LEDC_LS_CH3_GPIO, - .speed_mode = 0, - .timer_sel = 0 }, + { + .channel = LEDC_HS_CH0_CHANNEL, + .duty = 0, + .gpio_num = LEDC_HS_CH0_GPIO, + .speed_mode = LEDC_HS_MODE, + .hpoint = 0, + .timer_sel = LEDC_HS_TIMER + }, + { + .channel = LEDC_HS_CH1_CHANNEL, + .duty = 0, + .gpio_num = LEDC_HS_CH1_GPIO, + .speed_mode = LEDC_HS_MODE, + .hpoint = 0, + .timer_sel = LEDC_HS_TIMER + }, + { + .channel = LEDC_LS_CH2_CHANNEL, + .duty = 0, + .gpio_num = LEDC_LS_CH2_GPIO, + .speed_mode = LEDC_LS_MODE, + .hpoint = 0, + .timer_sel = LEDC_LS_TIMER + }, + { + .channel = LEDC_LS_CH3_CHANNEL, + .duty = 0, + .gpio_num = LEDC_LS_CH3_GPIO, + .speed_mode = LEDC_LS_MODE, + .hpoint = 0, + .timer_sel = LEDC_LS_TIMER + }, }; // Set LED Controller with previously prepared configuration @@ -104,28 +128,36 @@ void app_main() ledc_fade_func_install(0); while (1) { - ESP_LOGI(TAG, "1. LEDC fade up to duty = %d\n", LEDC_TEST_DUTY); + printf("1. LEDC fade up to duty = %d\n", LEDC_TEST_DUTY); for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { ledc_set_fade_with_time(ledc_channel[ch].speed_mode, - ledc_channel[ch].channel, LEDC_TEST_DUTY, LEDC_TEST_FADE_TIME); - } - for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { + ledc_channel[ch].channel, LEDC_TEST_DUTY, LEDC_TEST_FADE_TIME); ledc_fade_start(ledc_channel[ch].speed_mode, - ledc_channel[ch].channel, LEDC_FADE_NO_WAIT); + ledc_channel[ch].channel, LEDC_FADE_NO_WAIT); } vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS); - vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS); - ESP_LOGI(TAG, "2. LEDC fade down to duty = 0\n"); + printf("2. LEDC fade down to duty = 0\n"); for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { ledc_set_fade_with_time(ledc_channel[ch].speed_mode, - ledc_channel[ch].channel, 0, LEDC_TEST_FADE_TIME); - } - for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { + ledc_channel[ch].channel, 0, LEDC_TEST_FADE_TIME); ledc_fade_start(ledc_channel[ch].speed_mode, - ledc_channel[ch].channel, LEDC_FADE_NO_WAIT); + ledc_channel[ch].channel, LEDC_FADE_NO_WAIT); } vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS); - vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS); + + printf("3. LEDC set duty = %d without fade\n", LEDC_TEST_DUTY); + for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { + ledc_set_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel, LEDC_TEST_DUTY); + ledc_update_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel); + } + vTaskDelay(1000 / portTICK_PERIOD_MS); + + printf("4. LEDC set duty = 0 without fade\n"); + for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) { + ledc_set_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel, 0); + ledc_update_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel); + } + vTaskDelay(1000 / portTICK_PERIOD_MS); } -} +} \ No newline at end of file