This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push: new 5991a8c4cc xtensa/espressif: Change LEDC implementation to common for Xtensa based Espressif chips 5991a8c4cc is described below commit 5991a8c4cc1152039e1167d6f6bfb25346f1c387 Author: Eren Terzioglu <eren.terzio...@espressif.com> AuthorDate: Tue Jun 10 13:59:35 2025 +0200 xtensa/espressif: Change LEDC implementation to common for Xtensa based Espressif chips Change LEDC implementation to common one for esp32[-s2|-s3] Signed-off-by: Eren Terzioglu <eren.terzio...@espressif.com> --- arch/xtensa/src/common/espressif/Kconfig | 168 ++ arch/xtensa/src/common/espressif/Make.defs | 4 + arch/xtensa/src/common/espressif/esp_ledc.c | 1787 ++++++++++++++++++++ .../esp32s2_ledc.h => common/espressif/esp_ledc.h} | 16 +- arch/xtensa/src/esp32s2/Kconfig | 1 + arch/xtensa/src/esp32s2/Make.defs | 4 - arch/xtensa/src/esp32s2/esp32s2_ledc.c | 810 --------- arch/xtensa/src/esp32s3/Kconfig | 1 + arch/xtensa/src/esp32s3/Make.defs | 4 - arch/xtensa/src/esp32s3/esp32s3_ledc.c | 865 ---------- arch/xtensa/src/esp32s3/esp32s3_ledc.h | 52 - .../esp32s2/esp32s2-saola-1/configs/pwm/defconfig | 10 +- .../esp32s2/esp32s2-saola-1/src/esp32s2-saola-1.h | 2 +- .../esp32s2/esp32s2-saola-1/src/esp32s2_bringup.c | 8 +- .../esp32s2/esp32s2-saola-1/src/esp32s2_ledc.c | 18 +- .../esp32s3/common/include/esp32s3_board_ledc.h | 2 +- boards/xtensa/esp32s3/common/src/Make.defs | 2 +- .../xtensa/esp32s3/common/src/esp32s3_board_ledc.c | 18 +- .../esp32s3/esp32s3-devkit/configs/pwm/defconfig | 10 +- .../esp32s3/esp32s3-devkit/src/esp32s3_bringup.c | 6 +- .../esp32s3/esp32s3-korvo-2/src/esp32s3_bringup.c | 6 +- .../lckfb-szpi-esp32s3/configs/lcd/defconfig | 7 +- .../lckfb-szpi-esp32s3/configs/lvgl/defconfig | 7 +- .../lckfb-szpi-esp32s3/configs/pwm/defconfig | 7 +- .../lckfb-szpi-esp32s3/src/esp32s3_bringup.c | 4 +- 25 files changed, 2022 insertions(+), 1797 deletions(-) diff --git a/arch/xtensa/src/common/espressif/Kconfig b/arch/xtensa/src/common/espressif/Kconfig index 6a39ad19e3..d0f9085433 100644 --- a/arch/xtensa/src/common/espressif/Kconfig +++ b/arch/xtensa/src/common/espressif/Kconfig @@ -54,6 +54,13 @@ config ESP_SDM select ANALOG select DAC +config ESPRESSIF_LEDC + bool "LEDC (PWM)" + default n + depends on ARCH_CHIP_ESP32S2 || ARCH_CHIP_ESP32S3 + select PWM + select ARCH_HAVE_PWM_MULTICHAN + config ESPRESSIF_I2S bool default n @@ -153,6 +160,167 @@ config ESPRESSIF_DEDICATED_GPIO_IRQ ---help--- Enable dedicated GPIO IRQ support +menu "LEDC configuration" + depends on ESPRESSIF_LEDC + +config ESPRESSIF_LEDC_HPOINT + hex "LEDC hpoint value" + default 0x0000 + range 0x0 1048575 + ---help--- + This value sets the start point of the pulse within each timer period, + measured in timer ticks. It allows adjusting the phase of the signal. + +menuconfig ESPRESSIF_LEDC_TIMER0 + bool "Timer 0" + default n + +if ESPRESSIF_LEDC_TIMER0 + +config ESPRESSIF_LEDC_TIMER0_CHANNELS + int "Number of Timer 0 channels" + default 2 if PWM_MULTICHAN && PWM_NCHANNELS > 1 + default 1 if !PWM_MULTICHAN || PWM_NCHANNELS = 1 + range 0 6 + +config ESPRESSIF_LEDC_TIMER0_RESOLUTION + int "Timer 0 resolution" + default 12 + range 1 14 + ---help--- + Timer resolution in bits. The resolution is the number of bits used to by the timer + counter to generate the PWM signal. The duty cycle provided by the upper layers + will be scaled to fit the resolution. + +endif # ESPRESSIF_LEDC_TIMER0 + +menuconfig ESPRESSIF_LEDC_TIMER1 + bool "Timer 1" + default n + +if ESPRESSIF_LEDC_TIMER1 + +config ESPRESSIF_LEDC_TIMER1_CHANNELS + int "Number of Timer 1 channels" + default 2 if PWM_MULTICHAN && PWM_NCHANNELS > 1 + default 1 if !PWM_MULTICHAN || PWM_NCHANNELS = 1 + range 0 6 + +config ESPRESSIF_LEDC_TIMER1_RESOLUTION + int "Timer 1 resolution" + default 12 + range 1 14 + ---help--- + Timer resolution in bits. The resolution is the number of bits used to by the timer + counter to generate the PWM signal. The duty cycle provided by the upper layers + will be scaled to fit the resolution. + +endif # ESPRESSIF_LEDC_TIMER1 + +menuconfig ESPRESSIF_LEDC_TIMER2 + bool "Timer 2" + default n + +if ESPRESSIF_LEDC_TIMER2 + +config ESPRESSIF_LEDC_TIMER2_CHANNELS + int "Number of Timer 2 channels" + default 2 if PWM_MULTICHAN && PWM_NCHANNELS > 1 + default 1 if !PWM_MULTICHAN || PWM_NCHANNELS = 1 + range 0 6 + +config ESPRESSIF_LEDC_TIMER2_RESOLUTION + int "Timer 2 resolution" + default 12 + range 1 14 + ---help--- + Timer resolution in bits. The resolution is the number of bits used to by the timer + counter to generate the PWM signal. The duty cycle provided by the upper layers + will be scaled to fit the resolution. + +endif # ESPRESSIF_LEDC_TIMER2 + +menuconfig ESPRESSIF_LEDC_TIMER3 + bool "Timer 3" + default n + +if ESPRESSIF_LEDC_TIMER3 + +config ESPRESSIF_LEDC_TIMER3_CHANNELS + int "Number of Timer 3 channels" + default 2 if PWM_MULTICHAN && PWM_NCHANNELS > 1 + default 1 if !PWM_MULTICHAN || PWM_NCHANNELS = 1 + range 0 6 + +config ESPRESSIF_LEDC_TIMER3_RESOLUTION + int "Timer 3 resolution" + default 12 + range 1 14 + ---help--- + Timer resolution in bits. The resolution is the number of bits used to by the timer + counter to generate the PWM signal. The duty cycle provided by the upper layers + will be scaled to fit the resolution. + +endif # ESPRESSIF_LEDC_TIMER3 + +if !ESP32S2_LEDC && !ESP32S3_LEDC + +config ESPRESSIF_LEDC_CHANNEL0_PIN + int "Channel 0 pin" + default 2 + range 0 48 if ARCH_CHIP_ESP32S3 + range 0 46 if ARCH_CHIP_ESP32S2 + +config ESPRESSIF_LEDC_CHANNEL1_PIN + int "Channel 1 pin" + default 3 + range 0 48 if ARCH_CHIP_ESP32S3 + range 0 46 if ARCH_CHIP_ESP32S2 + +config ESPRESSIF_LEDC_CHANNEL2_PIN + int "Channel 2 pin" + default 4 + range 0 48 if ARCH_CHIP_ESP32S3 + range 0 46 if ARCH_CHIP_ESP32S2 + +config ESPRESSIF_LEDC_CHANNEL3_PIN + int "Channel 3 pin" + default 5 + range 0 48 if ARCH_CHIP_ESP32S3 + range 0 46 if ARCH_CHIP_ESP32S2 + +if PWM_MULTICHAN && PWM_NCHANNELS > 1 + +config ESPRESSIF_LEDC_CHANNEL4_PIN + int "Channel 4 pin" + default 6 + range 0 48 if ARCH_CHIP_ESP32S3 + range 0 46 if ARCH_CHIP_ESP32S2 + +config ESPRESSIF_LEDC_CHANNEL5_PIN + int "Channel 5 pin" + default 7 + range 0 48 if ARCH_CHIP_ESP32S3 + range 0 46 if ARCH_CHIP_ESP32S2 + +config ESPRESSIF_LEDC_CHANNEL6_PIN + int "Channel 6 pin" + default 8 + range 0 48 if ARCH_CHIP_ESP32S3 + range 0 46 if ARCH_CHIP_ESP32S2 + +config ESPRESSIF_LEDC_CHANNEL7_PIN + int "Channel 7 pin" + default 9 + range 0 48 if ARCH_CHIP_ESP32S3 + range 0 46 if ARCH_CHIP_ESP32S2 + +endif # PWM_MULTICHAN && PWM_NCHANNELS > 1 + +endif # !ESP32_LEDC && !ESP32S2_LEDC && !ESP32S3_LEDC + +endmenu # LEDC configuration + menu "Bootloader and Image Configuration" config ESPRESSIF_BOOTLOADER_MCUBOOT diff --git a/arch/xtensa/src/common/espressif/Make.defs b/arch/xtensa/src/common/espressif/Make.defs index 225726f4e7..97165af073 100644 --- a/arch/xtensa/src/common/espressif/Make.defs +++ b/arch/xtensa/src/common/espressif/Make.defs @@ -65,6 +65,10 @@ CHIP_CSRCS += esp_qencoder.c endif endif +ifeq ($(CONFIG_ESPRESSIF_LEDC),y) +CHIP_CSRCS += esp_ledc.c +endif + ifeq ($(CONFIG_ESPRESSIF_SPIFLASH),y) CHIP_CSRCS += esp_spiflash.c ifeq ($(CONFIG_ESPRESSIF_MTD),y) diff --git a/arch/xtensa/src/common/espressif/esp_ledc.c b/arch/xtensa/src/common/espressif/esp_ledc.c new file mode 100644 index 0000000000..172d89936f --- /dev/null +++ b/arch/xtensa/src/common/espressif/esp_ledc.c @@ -0,0 +1,1787 @@ +/**************************************************************************** + * arch/xtensa/src/common/espressif/esp_ledc.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <inttypes.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <sys/param.h> +#include <debug.h> +#include <errno.h> + +#include <nuttx/kmalloc.h> + +#include "esp_ledc.h" +#include "xtensa.h" +#if defined(CONFIG_ARCH_CHIP_ESP32S3) +#include "esp32s3_gpio.h" +#include "hardware/esp32s3_gpio_sigmap.h" +#elif defined(CONFIG_ARCH_CHIP_ESP32S2) +#include "esp32s2_gpio.h" +#include "hardware/esp32s2_gpio_sigmap.h" +#else +#include "esp32_gpio.h" +#include "hardware/esp32_gpio_sigmap.h" +#endif + +#include "esp_private/periph_ctrl.h" +#include "hal/ledc_hal.h" +#include "hal/ledc_types.h" +#include "soc/soc_caps.h" +#include "clk_ctrl_os.h" +#include "esp_clk_tree.h" +#include "esp_attr.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if defined(CONFIG_ARCH_CHIP_ESP32S3) +# define esp_configgpio esp32s3_configgpio +# define esp_gpio_matrix_out esp32s3_gpio_matrix_out +# ifdef CONFIG_ESP32S3_LEDC +# define CONFIG_ESPRESSIF_LEDC_CHANNEL0_PIN CONFIG_ESP32S3_LEDC_CHANNEL0_PIN +# define CONFIG_ESPRESSIF_LEDC_CHANNEL1_PIN CONFIG_ESP32S3_LEDC_CHANNEL1_PIN +# define CONFIG_ESPRESSIF_LEDC_CHANNEL2_PIN CONFIG_ESP32S3_LEDC_CHANNEL2_PIN +# define CONFIG_ESPRESSIF_LEDC_CHANNEL3_PIN CONFIG_ESP32S3_LEDC_CHANNEL3_PIN +# define CONFIG_ESPRESSIF_LEDC_CHANNEL4_PIN CONFIG_ESP32S3_LEDC_CHANNEL4_PIN +# define CONFIG_ESPRESSIF_LEDC_CHANNEL5_PIN CONFIG_ESP32S3_LEDC_CHANNEL5_PIN +# define CONFIG_ESPRESSIF_LEDC_CHANNEL6_PIN CONFIG_ESP32S3_LEDC_CHANNEL6_PIN +# define CONFIG_ESPRESSIF_LEDC_CHANNEL7_PIN CONFIG_ESP32S3_LEDC_CHANNEL7_PIN +# ifdef CONFIG_ESP32S3_LEDC_TIM0 +# define CONFIG_ESPRESSIF_LEDC_TIMER0_CHANNELS CONFIG_ESP32S3_LEDC_TIM0_CHANNELS +# define CONFIG_ESPRESSIF_LEDC_TIMER0 1 +# define CONFIG_ESPRESSIF_LEDC_TIMER0_RESOLUTION 12 +# endif /* CONFIG_ESP32S3_LEDC_TIM0 */ +# ifdef CONFIG_ESP32S3_LEDC_TIM1 +# define CONFIG_ESPRESSIF_LEDC_TIMER1_CHANNELS CONFIG_ESP32S3_LEDC_TIM1_CHANNELS +# define CONFIG_ESPRESSIF_LEDC_TIMER1 1 +# define CONFIG_ESPRESSIF_LEDC_TIMER1_RESOLUTION 12 +# endif /* CONFIG_ESP32S3_LEDC_TIM1 */ +# ifdef CONFIG_ESP32S3_LEDC_TIM2 +# define CONFIG_ESPRESSIF_LEDC_TIMER2_CHANNELS CONFIG_ESP32S3_LEDC_TIM2_CHANNELS +# define CONFIG_ESPRESSIF_LEDC_TIMER2 1 +# define CONFIG_ESPRESSIF_LEDC_TIMER2_RESOLUTION 12 +# endif /* CONFIG_ESP32S3_LEDC_TIM2 */ +# ifdef CONFIG_ESP32S3_LEDC_TIM3 +# define CONFIG_ESPRESSIF_LEDC_TIMER3_CHANNELS CONFIG_ESP32S3_LEDC_TIM3_CHANNELS +# define CONFIG_ESPRESSIF_LEDC_TIMER3 1 +# define CONFIG_ESPRESSIF_LEDC_TIMER3_RESOLUTION 12 +# endif /* CONFIG_ESP32S3_LEDC_TIM3 */ +# endif /* CONFIG_ESP32S3_LEDC */ +#elif defined(CONFIG_ARCH_CHIP_ESP32S2) +# define esp_configgpio esp32s2_configgpio +# define esp_gpio_matrix_out esp32s2_gpio_matrix_out +# ifdef CONFIG_ESP32S2_LEDC +# define CONFIG_ESPRESSIF_LEDC_CHANNEL0_PIN CONFIG_ESP32S2_LEDC_CHANNEL0_PIN +# define CONFIG_ESPRESSIF_LEDC_CHANNEL1_PIN CONFIG_ESP32S2_LEDC_CHANNEL1_PIN +# define CONFIG_ESPRESSIF_LEDC_CHANNEL2_PIN CONFIG_ESP32S2_LEDC_CHANNEL2_PIN +# define CONFIG_ESPRESSIF_LEDC_CHANNEL3_PIN CONFIG_ESP32S2_LEDC_CHANNEL3_PIN +# define CONFIG_ESPRESSIF_LEDC_CHANNEL4_PIN CONFIG_ESP32S2_LEDC_CHANNEL4_PIN +# define CONFIG_ESPRESSIF_LEDC_CHANNEL5_PIN CONFIG_ESP32S2_LEDC_CHANNEL5_PIN +# define CONFIG_ESPRESSIF_LEDC_CHANNEL6_PIN CONFIG_ESP32S2_LEDC_CHANNEL6_PIN +# define CONFIG_ESPRESSIF_LEDC_CHANNEL7_PIN CONFIG_ESP32S2_LEDC_CHANNEL7_PIN +# ifdef CONFIG_ESP32S2_LEDC_TIM0 +# define CONFIG_ESPRESSIF_LEDC_TIMER0_CHANNELS CONFIG_ESP32S2_LEDC_TIM0_CHANNELS +# define CONFIG_ESPRESSIF_LEDC_TIMER0 1 +# define CONFIG_ESPRESSIF_LEDC_TIMER0_RESOLUTION 12 +# endif /* CONFIG_ESP32S2_LEDC_TIM0 */ +# ifdef CONFIG_ESP32S2_LEDC_TIM1 +# define CONFIG_ESPRESSIF_LEDC_TIMER1_CHANNELS CONFIG_ESP32S2_LEDC_TIM1_CHANNELS +# define CONFIG_ESPRESSIF_LEDC_TIMER1 1 +# define CONFIG_ESPRESSIF_LEDC_TIMER1_RESOLUTION 12 +# endif /* CONFIG_ESP32S2_LEDC_TIM1 */ +# ifdef CONFIG_ESP32S2_LEDC_TIM2 +# define CONFIG_ESPRESSIF_LEDC_TIMER2_CHANNELS CONFIG_ESP32S2_LEDC_TIM2_CHANNELS +# define CONFIG_ESPRESSIF_LEDC_TIMER2 1 +# define CONFIG_ESPRESSIF_LEDC_TIMER2_RESOLUTION 12 +# endif /* CONFIG_ESP32S2_LEDC_TIM2 */ +# ifdef CONFIG_ESP32S2_LEDC_TIM3 +# define CONFIG_ESPRESSIF_LEDC_TIMER3_CHANNELS CONFIG_ESP32S2_LEDC_TIM3_CHANNELS +# define CONFIG_ESPRESSIF_LEDC_TIMER3 1 +# define CONFIG_ESPRESSIF_LEDC_TIMER3_RESOLUTION 12 +# endif /* CONFIG_ESP32S2_LEDC_TIM3 */ +# endif /* CONFIG_ESP32S2_LEDC */ +#endif + +#if defined(CONFIG_ESP32S2_LEDC) || defined(CONFIG_ESP32S3_LEDC) +# define LEGACY_CONFIG 1 +#endif + +#define LEDC_TIMER_DIV_NUM_MAX (0x3FFFF) + +#define LEDC_IS_DIV_INVALID(div) ((div) <= LEDC_LL_FRACTIONAL_MAX || \ + (div) > LEDC_TIMER_DIV_NUM_MAX) + +/* Precision degree only affects RC_FAST, other clock sources' frequencies + * are fixed values. For targets that do not support RC_FAST calibration, + * can only use its approximate value. + */ + +#if SOC_CLK_RC_FAST_SUPPORT_CALIBRATION +# define LEDC_CLK_SRC_FREQ_PRECISION ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED +#else +# define LEDC_CLK_SRC_FREQ_PRECISION ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX +#endif + +#if !SOC_RCC_IS_INDEPENDENT +# define LEDC_BUS_CLOCK_ATOMIC() PERIPH_RCC_ATOMIC() +#else +# define LEDC_BUS_CLOCK_ATOMIC() +#endif + +#if SOC_PERIPH_CLK_CTRL_SHARED +# define LEDC_FUNC_CLOCK_ATOMIC() PERIPH_RCC_ATOMIC() +#else +# define LEDC_FUNC_CLOCK_ATOMIC() +#endif + +/* All chips have 4 internal timers */ + +#define LEDC_TIMERS (4) + +/* If PWM multi-channel is disabled, then only one channel is supported per + * timer. Thus, the number of channels is the same as the number of timers. + * Note that we only support the maximum of 6 PWM channels. + */ + +#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 +# define LEDC_CHANNELS SOC_LEDC_CHANNEL_NUM +#else +# define LEDC_CHANNELS LEDC_TIMERS +#endif + +/* LEDC timer0 channels and offset */ + +#ifdef CONFIG_ESPRESSIF_LEDC_TIMER0 +# if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 +# define LEDC_TIM0_CHANS CONFIG_ESPRESSIF_LEDC_TIMER0_CHANNELS +# else +# define LEDC_TIM0_CHANS (1) +# endif +# define LEDC_TIM0_CHANS_OFF (0) +#endif + +/* LEDC timer1 channels and offset */ + +#ifdef CONFIG_ESPRESSIF_LEDC_TIMER1 +# if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 +# define LEDC_TIM1_CHANS CONFIG_ESPRESSIF_LEDC_TIMER1_CHANNELS +# else +# define LEDC_TIM1_CHANS (1) +# endif +# define LEDC_TIM1_CHANS_OFF (LEDC_TIM0_CHANS_OFF + LEDC_TIM0_CHANS) +#endif + +/* LEDC timer2 channels and offset */ + +#ifdef CONFIG_ESPRESSIF_LEDC_TIMER2 +# if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 +# define LEDC_TIM2_CHANS CONFIG_ESPRESSIF_LEDC_TIMER2_CHANNELS +# else +# define LEDC_TIM2_CHANS (1) +# endif +# define LEDC_TIM2_CHANS_OFF (LEDC_TIM1_CHANS_OFF + LEDC_TIM1_CHANS) +#endif + +/* LEDC timer3 channels and offset */ + +#ifdef CONFIG_ESPRESSIF_LEDC_TIMER3 +# if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 +# define LEDC_TIM3_CHANS CONFIG_ESPRESSIF_LEDC_TIMER3_CHANNELS +# else +# define LEDC_TIM3_CHANS (1) +# endif +# define LEDC_TIM3_CHANS_OFF (LEDC_TIM2_CHANS_OFF + LEDC_TIM2_CHANS) +#endif + +/* Uninitialized LEDC timer clock */ + +#define LEDC_SLOW_CLK_UNINIT (-1) + +/* Clock not found */ + +#define LEDC_CLK_NOT_FOUND (0) + +/* LEDC keep config */ + +#define LEDC_VAL_NO_CHANGE (-1) + +/* LEDC Timer default frequency */ + +#define LEDC_DEFAULT_FREQ (1000) + +/* Check max LEDC channels number */ + +#ifndef LEGACY_CONFIG +# if CONFIG_ESPRESSIF_LEDC_TIMER0_CHANNELS + \ + CONFIG_ESPRESSIF_LEDC_TIMER1_CHANNELS + \ + CONFIG_ESPRESSIF_LEDC_TIMER2_CHANNELS + \ + CONFIG_ESPRESSIF_LEDC_TIMER3_CHANNELS > LEDC_CHANNELS +# error "Too many LEDC channels. The maximum number of channels used " \ + "by all timers is 6 when multi-channel PWM is enabled and " \ + "4 when multi-channel PWM is disabled." +#endif +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* LEDC timer channel configuration */ + +struct esp_ledc_chan_s +{ + const uint8_t num; /* PWM channel ID */ + const uint8_t pin; /* PWM channel GPIO pin number */ + + uint32_t duty; /* PWM channel current duty */ +}; + +/* This structure represents the state of one LEDC timer */ + +struct esp_ledc_s +{ + const struct pwm_ops_s *ops; /* PWM operations */ + + const uint8_t timer; /* Timer ID */ + + const uint8_t channels; /* Timer channels number */ + struct esp_ledc_chan_s *chans; /* Timer channels pointer */ + + ledc_timer_bit_t duty_resolution; /* Timer duty resolution */ + ledc_clk_cfg_t clk_cfg; /* Timer clock configuration */ + uint32_t frequency; /* Timer current frequency */ + uint32_t reload; /* Timer current reload */ +}; + +struct ledc_obj_s +{ + ledc_hal_context_t ledc_hal; /* LEDC hal context */ + ledc_slow_clk_sel_t glb_clk; /* LEDC global clock selection */ + bool timer_is_stopped[LEDC_TIMER_MAX]; /* Indicates whether each timer has been stopped */ + bool glb_clk_is_acquired[LEDC_TIMER_MAX]; /* Tracks whether the global clock is being acquired by each timer */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#if 0 /* Should be added when sleep is supported */ +extern void esp_sleep_periph_use_8m(bool use_or_not); +#endif + +static bool ledc_ctx_create(void); +static bool ledc_slow_clk_calibrate(void); +static uint32_t ledc_calculate_divisor(uint32_t src_clk_freq, + int freq_hz, + uint32_t precision); +static uint32_t ledc_auto_global_clk_div(int freq_hz, + uint32_t precision, + ledc_slow_clk_sel_t *clk_target); +static uint32_t ledc_auto_clk_divisor(int freq_hz, + uint32_t precision, + ledc_clk_src_t *clk_source, + ledc_slow_clk_sel_t *clk_target); +static int ledc_timer_set(ledc_timer_t timer_sel, + uint32_t clock_divider, + uint32_t duty_resolution, + ledc_clk_src_t clk_src); +static int ledc_set_timer_div(ledc_timer_t timer_num, + ledc_clk_cfg_t clk_cfg, + int freq_hz, + int duty_resolution); +static int ledc_timer_resume(ledc_timer_t timer_sel); +static int ledc_timer_rst(ledc_timer_t timer_sel); +static int ledc_timer_pause(ledc_timer_t timer_sel); +static int setup_timer(struct esp_ledc_s *priv); +static int ledc_duty_config(ledc_channel_t channel, + int hpoint_val, + int duty_val, + ledc_duty_direction_t duty_direction, + uint32_t duty_num, + uint32_t duty_cycle, + uint32_t duty_scale); +static int ledc_set_duty_with_hpoint(ledc_channel_t channel, + uint32_t duty, + uint32_t hpoint); +static int ledc_channel_output_enable(ledc_channel_t channel); +static int ledc_channel_output_disable(ledc_channel_t channel); +static int ledc_update_duty(ledc_channel_t channel); +static uint32_t ledc_duty_bin_conversion(uint16_t nuttx_duty, + uint32_t duty_resolution); +static int ledc_bind_channel_timer(ledc_channel_t channel, + ledc_timer_t timer_sel); +static void setup_channel(struct esp_ledc_s *priv, int cn); +static int pwm_setup(struct pwm_lowerhalf_s *dev); +static int pwm_shutdown(struct pwm_lowerhalf_s *dev); +static int pwm_start(struct pwm_lowerhalf_s *dev, + const struct pwm_info_s *info); +static int pwm_stop(struct pwm_lowerhalf_s *dev); +static int pwm_ioctl(struct pwm_lowerhalf_s *dev, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* LEDC context */ + +static struct ledc_obj_s *p_ledc_obj = NULL; + +/* LEDC available timer global clocks */ + +static const ledc_slow_clk_sel_t s_glb_clks[] = LEDC_LL_GLOBAL_CLOCKS; + +/* Current RC_FAST frequency */ + +static uint32_t s_ledc_slow_clk_rc_fast_freq = 0; + +/* LEDC PWM operations */ + +static const struct pwm_ops_s g_pwmops = +{ + .setup = pwm_setup, + .shutdown = pwm_shutdown, + .start = pwm_start, + .stop = pwm_stop, + .ioctl = pwm_ioctl +}; + +/* LEDC channels table */ + +static struct esp_ledc_chan_s g_ledc_chans[LEDC_CHANNELS] = +{ + { + .num = 0, + .pin = CONFIG_ESPRESSIF_LEDC_CHANNEL0_PIN + }, + { + .num = 1, + .pin = CONFIG_ESPRESSIF_LEDC_CHANNEL1_PIN + }, + { + .num = 2, + .pin = CONFIG_ESPRESSIF_LEDC_CHANNEL2_PIN + }, + { + .num = 3, + .pin = CONFIG_ESPRESSIF_LEDC_CHANNEL3_PIN + }, +#if LEDC_CHANNELS > 4 + { + .num = 4, + .pin = CONFIG_ESPRESSIF_LEDC_CHANNEL4_PIN + }, + { + .num = 5, + .pin = CONFIG_ESPRESSIF_LEDC_CHANNEL5_PIN + }, + { + .num = 6, + .pin = CONFIG_ESPRESSIF_LEDC_CHANNEL6_PIN + }, + { + .num = 7, + .pin = CONFIG_ESPRESSIF_LEDC_CHANNEL7_PIN + } +#endif +}; + +/* LEDC timer0 private data */ + +#ifdef CONFIG_ESPRESSIF_LEDC_TIMER0 + +# if CONFIG_ESPRESSIF_LEDC_TIMER0_RESOLUTION > SOC_LEDC_TIMER_BIT_WIDTH +# error "Invalid PWM Timer 0 resolution." +# endif + +static struct esp_ledc_s g_pwm0dev = +{ + .ops = &g_pwmops, + .timer = 0, + .channels = LEDC_TIM0_CHANS, + .chans = &g_ledc_chans[LEDC_TIM0_CHANS_OFF], + .duty_resolution = CONFIG_ESPRESSIF_LEDC_TIMER0_RESOLUTION, + .clk_cfg = LEDC_AUTO_CLK, + .frequency = LEDC_DEFAULT_FREQ +}; +#endif /* CONFIG_ESPRESSIF_LEDC_TIMER0 */ + +/* LEDC timer1 private data */ + +#ifdef CONFIG_ESPRESSIF_LEDC_TIMER1 + +# if CONFIG_ESPRESSIF_LEDC_TIMER1_RESOLUTION > SOC_LEDC_TIMER_BIT_WIDTH +# error "Invalid PWM Timer 1 resolution." +# endif + +static struct esp_ledc_s g_pwm1dev = +{ + .ops = &g_pwmops, + .timer = 1, + .channels = LEDC_TIM1_CHANS, + .chans = &g_ledc_chans[LEDC_TIM1_CHANS_OFF], + .duty_resolution = CONFIG_ESPRESSIF_LEDC_TIMER1_RESOLUTION, + .clk_cfg = LEDC_AUTO_CLK, + .frequency = LEDC_DEFAULT_FREQ +}; +#endif /* CONFIG_ESPRESSIF_LEDC_TIMER1 */ + +/* LEDC timer2 private data */ + +#ifdef CONFIG_ESPRESSIF_LEDC_TIMER2 + +# if CONFIG_ESPRESSIF_LEDC_TIMER2_RESOLUTION > SOC_LEDC_TIMER_BIT_WIDTH +# error "Invalid PWM Timer 2 resolution." +# endif + +static struct esp_ledc_s g_pwm2dev = +{ + .ops = &g_pwmops, + .timer = 2, + .channels = LEDC_TIM2_CHANS, + .chans = &g_ledc_chans[LEDC_TIM2_CHANS_OFF], + .duty_resolution = CONFIG_ESPRESSIF_LEDC_TIMER2_RESOLUTION, + .clk_cfg = LEDC_AUTO_CLK, + .frequency = LEDC_DEFAULT_FREQ +}; +#endif /* CONFIG_ESPRESSIF_LEDC_TIMER2 */ + +/* LEDC timer3 private data */ + +#ifdef CONFIG_ESPRESSIF_LEDC_TIMER3 + +# if CONFIG_ESPRESSIF_LEDC_TIMER3_RESOLUTION > SOC_LEDC_TIMER_BIT_WIDTH +# error "Invalid PWM Timer 3 resolution." +# endif + +static struct esp_ledc_s g_pwm3dev = +{ + .ops = &g_pwmops, + .timer = 3, + .channels = LEDC_TIM3_CHANS, + .chans = &g_ledc_chans[LEDC_TIM3_CHANS_OFF], + .duty_resolution = CONFIG_ESPRESSIF_LEDC_TIMER3_RESOLUTION, + .clk_cfg = LEDC_AUTO_CLK, + .frequency = LEDC_DEFAULT_FREQ +}; +#endif /* CONFIG_ESPRESSIF_LEDC_TIMER3 */ + +/**************************************************************************** + * Private functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ledc_ctx_create + * + * Description: + * Create LEDC context. + * + * Input Parameters: + * None. + * + * Returned Value: + * True if context was created, false otherwise. + * + ****************************************************************************/ + +static bool ledc_ctx_create(void) +{ + bool new_ctx = false; + irqstate_t flags; + + flags = enter_critical_section(); + + if (!p_ledc_obj) + { + struct ledc_obj_s *ledc_new_mode_obj; + ledc_new_mode_obj = (struct ledc_obj_s *) + kmm_calloc(1, sizeof(struct ledc_obj_s)); + if (ledc_new_mode_obj) + { + new_ctx = true; + + LEDC_BUS_CLOCK_ATOMIC() + { + ledc_ll_enable_bus_clock(true); + ledc_ll_enable_reset_reg(false); + } + + LEDC_FUNC_CLOCK_ATOMIC() + { + ledc_ll_enable_clock(LEDC_LL_GET_HW(), true); + } + + ledc_hal_init(&(ledc_new_mode_obj->ledc_hal), LEDC_LOW_SPEED_MODE); + ledc_new_mode_obj->glb_clk = LEDC_SLOW_CLK_UNINIT; + p_ledc_obj = ledc_new_mode_obj; + periph_module_enable(PERIPH_LEDC_MODULE); + } + } + + leave_critical_section(flags); + + return new_ctx; +} + +/**************************************************************************** + * Name: ledc_slow_clk_calibrate + * + * Description: + * Calibrate RC_FAST clock. + * + * Input Parameters: + * None. + * + * Returned Value: + * True if calibration was successful, false otherwise. + * + ****************************************************************************/ + +static bool ledc_slow_clk_calibrate(void) +{ + if (periph_rtc_dig_clk8m_enable()) + { + s_ledc_slow_clk_rc_fast_freq = periph_rtc_dig_clk8m_get_freq(); +#if !SOC_CLK_RC_FAST_SUPPORT_CALIBRATION + pwminfo("Calibration cannot be performed." + "Approximate RC_FAST_CLK : %"PRIu32" Hz\n", + s_ledc_slow_clk_rc_fast_freq); +#else + pwminfo("Calibrate RC_FAST_CLK : %"PRIu32" Hz", + s_ledc_slow_clk_rc_fast_freq); +#endif + return true; + } + + pwmerr("Calibrate RC_FAST_CLK failed\n"); + return false; +} + +/**************************************************************************** + * Name: ledc_calculate_divisor + * + * Description: + * Calculate the divisor to achieve the desired frequency. + * + * Input Parameters: + * src_clk_freq - The source clock frequency. + * freq_hz - The desired frequency. + * precision - The granularity of the clock. + * + * Returned Value: + * The divisor parameter to use to achieve the frequency requested. + * + ****************************************************************************/ + +static inline uint32_t ledc_calculate_divisor(uint32_t src_clk_freq, + int freq_hz, + uint32_t precision) +{ + /* In order to find the right divisor, we need to divide the source clock + * frequency by the desired frequency. However, two things to note here: + * - The lowest LEDC_LL_FRACTIONAL_BITS bits of the result are the + * FRACTIONAL part. The higher bits represent the integer part, this is + * why we need to right shift the source frequency. + * - The `precision` parameter represents the granularity of the clock. It + * **must** be a power of 2. It means that the resulted divisor is + * a multiplier of `precision`. + * + * Let's take a concrete example, we need to generate a 5KHz clock out of + * a 80MHz clock (APB). + * If the precision is 1024 (10 bits), the resulted multiplier is: + * (80000000 << 8) / (5000 * 1024) = 4000 (0xfa0) + * Let's ignore the fractional part to simplify the explanation, so we get + * a result of 15 (0xf). + * This can be interpreted as: every 15 "precision" ticks, the resulted + * clock will go high, where one precision tick is made out of 1024 source + * clock ticks. + * Thus, every `15 * 1024` source clock ticks, the resulted clock will go + * high. + * + * NOTE: We are also going to round up the value when necessary, thanks to: + * (freq_hz * precision) / 2 + */ + + return (((uint64_t)src_clk_freq << LEDC_LL_FRACTIONAL_BITS) + \ + ((freq_hz * precision) / 2)) / (freq_hz * precision); +} + +/**************************************************************************** + * Name: ledc_auto_global_clk_div + * + * Description: + * Try to find the clock with its divisor giving the frequency requested + * by the caller. + * + * Input Parameters: + * freq_hz - Frequency to achieve; + * precision - Precision of the frequency to achieve; + * clk_target - Clock target to use. + * + * Returned Value: + * The divisor parameter to use to achieve the frequency requested. + * + ****************************************************************************/ + +static uint32_t ledc_auto_global_clk_div(int freq_hz, + uint32_t precision, + ledc_slow_clk_sel_t *clk_target) +{ + uint32_t ret = LEDC_CLK_NOT_FOUND; + uint32_t clk_freq = 0; + + /* This function will go through all the following clock sources to look + * for a valid divisor which generates the requested frequency. + */ + + for (int i = 0; i < nitems(s_glb_clks); i++) + { + /* Before calculating the divisor, we need to have the RC_FAST + * frequency. If it hasn't been measured yet, try calibrating + * it now. + */ + + if (s_glb_clks[i] == LEDC_SLOW_CLK_RC_FAST && + s_ledc_slow_clk_rc_fast_freq == 0 && + !ledc_slow_clk_calibrate()) + { + pwminfo("Unable to retrieve RC_FAST clock frequency, skipping it"); + continue; + } + + esp_clk_tree_src_get_freq_hz((soc_module_clk_t)s_glb_clks[i], + LEDC_CLK_SRC_FREQ_PRECISION, + &clk_freq); + + uint32_t div_param = ledc_calculate_divisor(clk_freq, + freq_hz, + precision); + + /* If the divisor is valid, we can return this value. */ + + if (!LEDC_IS_DIV_INVALID(div_param)) + { + *clk_target = s_glb_clks[i]; + ret = div_param; + break; + } + } + + return ret; +} + +/**************************************************************************** + * Name: ledc_auto_clk_divisor + * + * Description: + * Try to find the clock with its divisor giving the frequency requested + * by the caller. + * + * Input Parameters: + * freq_hz - Frequency to achieve; + * precision - Precision of the frequency to achieve; + * clk_source - Clock source to use; + * clk_target - Clock target to use. + * + * Returned Value: + * The divisor parameter to use to achieve the frequency requested. + * + ****************************************************************************/ + +static uint32_t ledc_auto_clk_divisor(int freq_hz, + uint32_t precision, + ledc_clk_src_t *clk_source, + ledc_slow_clk_sel_t *clk_target) +{ + uint32_t ret = LEDC_CLK_NOT_FOUND; + + uint32_t div_param_global = ledc_auto_global_clk_div(freq_hz, + precision, + clk_target); + + if (div_param_global != LEDC_CLK_NOT_FOUND) + { + pwminfo("Found a global clock source for frequency %d Hz " + "and precision 0x%"PRIx32"\n", freq_hz, precision); + *clk_source = LEDC_SCLK; + ret = div_param_global; + } + else + { + pwminfo("Could not find a global clock source for frequency %d Hz " + "and precision 0x%"PRIx32"\n", freq_hz, precision); + } + + return ret; +} + +/**************************************************************************** + * Name: ledc_timer_set + * + * Description: + * Set LEDC timer. + * + * Input Parameters: + * timer_sel - Select the timer to be set; + * clock_divider - The divider of the clock source; + * duty_resolution- The duty resolution of the timer; + * clk_src - The clock source of the timer; + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int ledc_timer_set(ledc_timer_t timer_sel, + uint32_t clock_divider, + uint32_t duty_resolution, + ledc_clk_src_t clk_src) +{ + DEBUGASSERT(timer_sel < LEDC_TIMER_MAX); + + if (p_ledc_obj == NULL) + { + pwmerr("ERROR: LEDC not initialized\n"); + return -ENODEV; + } + + irqstate_t flags; + + flags = enter_critical_section(); + + ledc_hal_set_clock_divider(&(p_ledc_obj->ledc_hal), + timer_sel, + clock_divider); + + ledc_hal_set_duty_resolution(&(p_ledc_obj->ledc_hal), + timer_sel, + duty_resolution); + + ledc_hal_ls_timer_update(&(p_ledc_obj->ledc_hal), timer_sel); + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: ledc_set_timer_div + * + * Description: + * Set LEDC timer divider. + * + * Input Parameters: + * timer_num - LEDC timer number; + * clk_cfg - LEDC timer clock source; + * freq_hz - LEDC timer frequency; + * duty_resolution - LEDC timer duty resolution. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int ledc_set_timer_div(ledc_timer_t timer_num, + ledc_clk_cfg_t clk_cfg, + int freq_hz, + int duty_resolution) +{ + irqstate_t flags; + uint32_t div_param = 0; + int i; + const uint32_t precision = (0x1 << duty_resolution); + + /* The clock sources are not initialized on purpose. To produce compiler + * warning if used but the selector functions don't set them properly. + */ + + /* Timer-specific mux. Set to timer-specific clock or LEDC_SCLK if a global + * clock is used. + */ + + ledc_clk_src_t timer_clk_src; + + /* Global clock mux. Should be set when LEDC_SCLK is used in + * LOW_SPEED_MODE. Otherwise left uninitialized. + */ + + ledc_slow_clk_sel_t glb_clk = LEDC_SLOW_CLK_UNINIT; + + if (clk_cfg == LEDC_AUTO_CLK) + { + /* User hasn't specified the speed, we should try to guess it. */ + + pwminfo("Using auto clock source selection\n"); + + div_param = ledc_auto_clk_divisor(freq_hz, + precision, + &timer_clk_src, + &glb_clk); + } + else if (clk_cfg == LEDC_USE_RC_FAST_CLK) + { + pwminfo("Using RC_FAST clock source\n"); + + /* Before calculating the divisor, we need to have the RC_FAST + * frequency. If it hasn't been measured yet, try calibrating + * it now. + */ + + if (s_ledc_slow_clk_rc_fast_freq == 0 && + ledc_slow_clk_calibrate() == false) + { + goto error; + } + + /* Set the global clock source */ + + timer_clk_src = LEDC_SCLK; + glb_clk = LEDC_SLOW_CLK_RC_FAST; + + /* We have the RC_FAST clock frequency now. */ + + div_param = ledc_calculate_divisor(s_ledc_slow_clk_rc_fast_freq, + freq_hz, + precision); + + if (LEDC_IS_DIV_INVALID(div_param)) + { + div_param = LEDC_CLK_NOT_FOUND; + } + } + else + { + timer_clk_src = LEDC_SCLK; + glb_clk = (ledc_slow_clk_sel_t)clk_cfg; + + uint32_t src_clk_freq = 0; + esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_cfg, + LEDC_CLK_SRC_FREQ_PRECISION, + &src_clk_freq); + + div_param = ledc_calculate_divisor(src_clk_freq, freq_hz, precision); + + if (LEDC_IS_DIV_INVALID(div_param)) + { + div_param = LEDC_CLK_NOT_FOUND; + } + } + + if (div_param == LEDC_CLK_NOT_FOUND) + { + goto error; + } + + pwminfo("Using clock source %d (in slow mode). Divisor: 0x%"PRIx32, + timer_clk_src, + div_param); + + /* The following block configures the global clock. + * Arriving here, variable glb_clk must have been assigned to one of the + * ledc_slow_clk_sel_t enum values + */ + + ASSERT(timer_clk_src == LEDC_SCLK); + ASSERT(glb_clk != LEDC_SLOW_CLK_UNINIT); + + flags = enter_critical_section(); + + if (p_ledc_obj->glb_clk != LEDC_SLOW_CLK_UNINIT && + p_ledc_obj->glb_clk != glb_clk) + { + for (i = 0; i < LEDC_TIMER_MAX; i++) + { + if (i != timer_num && p_ledc_obj->glb_clk_is_acquired[i]) + { + leave_critical_section(flags); + pwmerr("Timer clock conflict. Already is %d but attempt to %d", + p_ledc_obj->glb_clk, + glb_clk); + } + } + } + + if (timer_num == LEDC_TIMER_MAX - 1 && + p_ledc_obj->glb_clk_is_acquired[timer_num - 1]) + { + return -EINVAL; + } + + p_ledc_obj->glb_clk_is_acquired[timer_num] = true; + if (p_ledc_obj->glb_clk != glb_clk) + { + p_ledc_obj->glb_clk = glb_clk; + ledc_hal_set_slow_clk_sel(&(p_ledc_obj->ledc_hal), glb_clk); + } + + leave_critical_section(flags); + + pwminfo("In slow speed mode. Global clock: %d", glb_clk); + + /* Keep ESP_PD_DOMAIN_RC_FAST on during light sleep */ + +#if 0 /* Should be added when sleep is supported */ + esp_sleep_periph_use_8m(glb_clk == LEDC_SLOW_CLK_RC_FAST); +#endif + + /* The divisor is correct, we can write in the hardware. */ + + ledc_timer_set(timer_num, div_param, duty_resolution, timer_clk_src); + return OK; + +error: + pwmerr("Requested frequency and duty resolution can not be achieved. " + "Try reducing the frequency or timer resolution.\n"); + + return -EINVAL; +} + +/**************************************************************************** + * Name: ledc_timer_resume + * + * Description: + * Resume a LEDC timer. + * + * Input Parameters: + * timer_sel - LEDC timer id. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int ledc_timer_resume(ledc_timer_t timer_sel) +{ + DEBUGASSERT(timer_sel < LEDC_TIMER_MAX); + + if (p_ledc_obj == NULL) + { + pwmerr("ERROR: LEDC not initialized\n"); + return -ENODEV; + } + + irqstate_t flags; + + flags = enter_critical_section(); + + p_ledc_obj->timer_is_stopped[timer_sel] = false; + ledc_hal_timer_resume(&(p_ledc_obj->ledc_hal), timer_sel); + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: ledc_timer_rst + * + * Description: + * Reset a LEDC timer. + * + * Input Parameters: + * timer_sel - LEDC timer id. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int ledc_timer_rst(ledc_timer_t timer_sel) +{ + DEBUGASSERT(timer_sel < LEDC_TIMER_MAX); + + if (p_ledc_obj == NULL) + { + pwmerr("ERROR: LEDC not initialized\n"); + return -ENODEV; + } + + irqstate_t flags; + + flags = enter_critical_section(); + + ledc_hal_timer_rst(&(p_ledc_obj->ledc_hal), timer_sel); + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: ledc_timer_pause + * + * Description: + * Pause a LEDC timer. + * + * Input Parameters: + * timer_sel - LEDC timer id. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int ledc_timer_pause(ledc_timer_t timer_sel) +{ + DEBUGASSERT(timer_sel < LEDC_TIMER_MAX); + + if (p_ledc_obj == NULL) + { + pwmerr("ERROR: LEDC not initialized\n"); + return -ENODEV; + } + + irqstate_t flags; + + flags = enter_critical_section(); + + p_ledc_obj->timer_is_stopped[timer_sel] = true; + ledc_hal_timer_pause(&(p_ledc_obj->ledc_hal), timer_sel); + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: setup_timer + * + * Description: + * Setup LEDC timer frequency and reload. + * + * Input Parameters: + * priv - A reference to the LEDC timer state structure + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int setup_timer(struct esp_ledc_s *priv) +{ + DEBUGASSERT(priv != NULL); + DEBUGASSERT(priv->timer < LEDC_TIMER_MAX); + DEBUGASSERT(priv->frequency > 0); + DEBUGASSERT(priv->duty_resolution < LEDC_TIMER_BIT_MAX && + priv->duty_resolution > 0); + + int ret; + + if (!ledc_ctx_create() && !p_ledc_obj) + { + pwmerr("ERROR: No memory for LEDC context\n"); + PANIC(); + } + + ret = ledc_set_timer_div(priv->timer, + priv->clk_cfg, + priv->frequency, + priv->duty_resolution); + + if (ret == OK) + { + ledc_timer_pause(priv->timer); + ledc_timer_rst(priv->timer); + } + + return ret; +} + +/**************************************************************************** + * Name: ledc_duty_config + * + * Description: + * Set the duty cycle of a PWM channel. + * + * Input Parameters: + * channel - LEDC channel index. + * hpoint_val - LEDC channel hpoint value. This value sets + * the pulse start point in timer ticks allowing phase control + * of the signal. + * duty_val - LEDC channel duty value. + * duty_direction - LEDC channel duty direction. + * duty_num - LEDC channel duty number. + * duty_cycle - LEDC channel duty cycle. + * + * Returned Value: + * OK. + * + ****************************************************************************/ + +static IRAM_ATTR int ledc_duty_config(ledc_channel_t channel, + int hpoint_val, + int duty_val, + ledc_duty_direction_t duty_direction, + uint32_t duty_num, + uint32_t duty_cycle, + uint32_t duty_scale) +{ + if (hpoint_val >= 0) + { + ledc_hal_set_hpoint(&(p_ledc_obj->ledc_hal), channel, hpoint_val); + } + + if (duty_val >= 0) + { + ledc_hal_set_duty_int_part(&(p_ledc_obj->ledc_hal), channel, duty_val); + } + + pwminfo("ledc_duty_config: channel: %d, hpoint_val: %d, duty_val: %d, " + "duty_direction: %d, duty_num: %" PRIu32 ", " + "duty_cycle: %" PRIu32 ", duty_scale: %" PRIu32 "\n", + channel, hpoint_val, duty_val, duty_direction, duty_num, + duty_cycle, duty_scale); + ledc_hal_set_fade_param(&(p_ledc_obj->ledc_hal), channel, 0, + duty_direction, duty_cycle, duty_scale, duty_num); + +#if SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED + ledc_hal_set_range_number(&(p_ledc_obj->ledc_hal), channel, 1); + ledc_hal_clear_left_off_fade_param(&(p_ledc_obj->ledc_hal), channel, 1); +#endif + return OK; +} + +/**************************************************************************** + * Name: ledc_set_duty_with_hpoint + * + * Description: + * Set the duty cycle of a PWM channel with a given hpoint value to set + * pulse start point in timer ticks for phase control of the signal. + * + * Input Parameters: + * channel - LEDC channel index. + * duty - Duty cycle value. + * hpoint - Hpoint value. + * + * Returned Value: + * Ok on success; A negated errno value is returned on failure. + * + ****************************************************************************/ + +static int ledc_set_duty_with_hpoint(ledc_channel_t channel, + uint32_t duty, + uint32_t hpoint) +{ + DEBUGASSERT(channel < LEDC_CHANNEL_MAX); + DEBUGASSERT(hpoint <= LEDC_LL_HPOINT_VAL_MAX); + + if (p_ledc_obj == NULL) + { + pwmerr("ERROR: LEDC not initialized\n"); + return -ENODEV; + } + + irqstate_t flags; + + flags = enter_critical_section(); + + ledc_duty_config(channel, hpoint, duty, LEDC_DUTY_DIR_INCREASE, 1, 1, 0); + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: ledc_channel_output_enable + * + * Description: + * Enable LEDC channel output. + * + * Input Parameters: + * channel - LEDC channel index. + * + * Returned Value: + * Ok on success; A negated errno value is returned on failure. + * + ****************************************************************************/ + +static int ledc_channel_output_enable(ledc_channel_t channel) +{ + DEBUGASSERT(channel < LEDC_CHANNEL_MAX); + + if (p_ledc_obj == NULL) + { + pwmerr("ERROR: LEDC not initialized\n"); + return -ENODEV; + } + + ledc_hal_set_sig_out_en(&(p_ledc_obj->ledc_hal), channel, true); + ledc_hal_set_duty_start(&(p_ledc_obj->ledc_hal), channel, true); + + return OK; +} + +/**************************************************************************** + * Name: ledc_channel_output_disable + * + * Description: + * Disable LEDC channel output. + * + * Input Parameters: + * channel - LEDC channel index. + * + * Returned Value: + * Ok on success; A negated errno value is returned on failure. + * + ****************************************************************************/ + +static int ledc_channel_output_disable(ledc_channel_t channel) +{ + DEBUGASSERT(channel < LEDC_CHANNEL_MAX); + + if (p_ledc_obj == NULL) + { + pwmerr("ERROR: LEDC not initialized\n"); + return -ENODEV; + } + + irqstate_t flags; + + flags = enter_critical_section(); + + ledc_hal_set_idle_level(&(p_ledc_obj->ledc_hal), channel, 0); + ledc_hal_set_sig_out_en(&(p_ledc_obj->ledc_hal), channel, false); + ledc_hal_set_duty_start(&(p_ledc_obj->ledc_hal), channel, false); + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: ledc_update_duty + * + * Description: + * Update the duty cycle of a PWM channel. + * + * Input Parameters: + * channel - LEDC channel index. + * + * Returned Value: + * Ok on success; A negated errno value is returned on failure. + * + ****************************************************************************/ + +static int ledc_update_duty(ledc_channel_t channel) +{ + DEBUGASSERT(channel < LEDC_CHANNEL_MAX); + + if (p_ledc_obj == NULL) + { + pwmerr("ERROR: LEDC not initialized\n"); + return -ENODEV; + } + + irqstate_t flags; + + ledc_channel_output_enable(channel); + + flags = enter_critical_section(); + ledc_hal_ls_channel_update(&(p_ledc_obj->ledc_hal), channel); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: ledc_duty_bin_conversion + * + * Description: + * Truncate or expand the duty cycle value to fit the duty resolution. + * + * Input Parameters: + * nuttx_duty - Duty cycle value from nuttx. + * duty_resolution - Duty resolution of the channel. + * + * Returned Value: + * Truncated or expanded duty cycle value. + * + ****************************************************************************/ + +static uint32_t ledc_duty_bin_conversion(uint16_t nuttx_duty, + uint32_t duty_resolution) +{ + /* Calculate the maximum value based on the duty resolution */ + + uint32_t max_value = (1 << duty_resolution) - 1; + + /* Map the value to the specified range */ + + uint32_t mapped_value = (nuttx_duty * max_value) / UINT16_MAX; + + return mapped_value; +} + +/**************************************************************************** + * Name: ledc_bind_channel_timer + * + * Description: + * Bind a LEDC channel to a LEDC timer. + * + * Input Parameters: + * channel - LEDC channel index. + * timer_sel - LEDC timer index. + * + * Returned Value: + * Ok on success; A negated errno value is returned on failure. + * + ****************************************************************************/ + +static int ledc_bind_channel_timer(ledc_channel_t channel, + ledc_timer_t timer_sel) +{ + DEBUGASSERT(timer_sel < LEDC_TIMER_MAX); + + if (p_ledc_obj == NULL) + { + pwmerr("ERROR: LEDC not initialized\n"); + return -ENODEV; + } + + irqstate_t flags; + + flags = enter_critical_section(); + + ledc_hal_bind_channel_timer(&(p_ledc_obj->ledc_hal), channel, timer_sel); + ledc_hal_ls_channel_update(&(p_ledc_obj->ledc_hal), channel); + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: setup_channel + * + * Description: + * Setup LEDC timer channel duty. + * + * Input Parameters: + * priv - A reference to the LEDC timer state structure + * cn - Timer channel number + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void setup_channel(struct esp_ledc_s *priv, int cn) +{ + irqstate_t flags; + bool new_ctx_created; + struct esp_ledc_chan_s *chan = &priv->chans[cn]; + + new_ctx_created = ledc_ctx_create(); + if (!new_ctx_created && !p_ledc_obj) + { + pwmerr("ERROR: No memory for LEDC context\n"); + PANIC(); + } + + /* On such targets (ESP32, ESP32-H2, ESP32-P4), + * the default ledc core(global) clock does not connect to + * any clock source. Setting channel configurations and updating bits + * before core clock is enabled could lead to an error. + * Therefore, we should connect the core clock to a real clock source to + * enable it before any ledc register operation happens. + * It can be switched to the other desired clock sources to meet the output + * PWM frequency requirements later at timer configuration. + * So we consider the glb_clk still as LEDC_SLOW_CLK_UNINIT. + */ + + else if (new_ctx_created) + { + flags = enter_critical_section(); + + if (p_ledc_obj->glb_clk == LEDC_SLOW_CLK_UNINIT) + { + ledc_hal_set_slow_clk_sel(&(p_ledc_obj->ledc_hal), + LEDC_LL_GLOBAL_CLK_DEFAULT); + } + + leave_critical_section(flags); + } + + ledc_set_duty_with_hpoint(chan->num, + chan->duty, + CONFIG_ESPRESSIF_LEDC_HPOINT); + + ledc_bind_channel_timer(chan->num, priv->timer); + + flags = enter_critical_section(); + ledc_hal_set_fade_end_intr(&(p_ledc_obj->ledc_hal), chan->num, false); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: pwm_setup + * + * Description: + * This method is called when the driver is opened. The lower half driver + * should configure and initialize the device so that it is ready for use. + * It should not, however, output pulses until the start method is called. + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_setup(struct pwm_lowerhalf_s *dev) +{ + struct esp_ledc_s *priv = (struct esp_ledc_s *)dev; + int ret; + + pwminfo("Initializing PWM Timer %d with default frequency of %d Hz\n", + priv->timer, + LEDC_DEFAULT_FREQ); + + ret = setup_timer(priv); + + if (ret != OK) + { + pwmerr("ERROR: Failed to setup timer\n"); + return ret; + } + + /* Setup channel GPIO pins */ + + for (int i = 0; i < priv->channels; i++) + { + setup_channel(priv, i); + + pwminfo("Channel %d mapped to pin %d\n", priv->chans[i].num, + priv->chans[i].pin); + + esp_configgpio(priv->chans[i].pin, OUTPUT | PULLUP); + esp_gpio_matrix_out(priv->chans[i].pin, + LEDC_LS_SIG_OUT0_IDX + priv->chans[i].num, + 0, 0); + } + + return OK; +} + +/**************************************************************************** + * Name: pwm_shutdown + * + * Description: + * This method is called when the driver is closed. The lower half driver + * stop pulsed output, free any resources, disable the timer hardware, and + * put the system into the lowest possible power usage state + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_shutdown(struct pwm_lowerhalf_s *dev) +{ + struct esp_ledc_s *priv = (struct esp_ledc_s *)dev; +#ifdef CONFIG_PWM_NCHANNELS + int channels = MIN(priv->channels, CONFIG_PWM_NCHANNELS); +#else + int channels = 1; +#endif + + pwminfo("Shutting down PWM Timer %d\n", priv->timer); + + /* Stop timer */ + + pwm_stop(dev); + + /* Clear timer and channel configuration */ + + priv->frequency = LEDC_DEFAULT_FREQ; + + for (int i = 0; i < channels; i++) + { + priv->chans[i].duty = 0; + } + + if (p_ledc_obj != NULL) + { + periph_module_disable(PERIPH_LEDC_MODULE); + kmm_free(p_ledc_obj); + p_ledc_obj = NULL; + s_ledc_slow_clk_rc_fast_freq = 0; + } + else + { + pwmerr("ERROR: LEDC not initialized\n"); + return -ENODEV; + } + + return 0; +} + +/**************************************************************************** + * Name: pwm_start + * + * Description: + * (Re-)initialize the timer resources and start the pulsed output + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * info - A reference to the characteristics of the pulsed output + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_start(struct pwm_lowerhalf_s *dev, + const struct pwm_info_s *info) +{ + uint32_t duty; + int ret = OK; + irqstate_t flags; + struct esp_ledc_s *priv = (struct esp_ledc_s *)dev; +#ifdef CONFIG_PWM_NCHANNELS + int channels = MIN(priv->channels, CONFIG_PWM_NCHANNELS); +#else + int channels = 1; +#endif + + /* Update timer with given PWM timer frequency */ + + if (priv->frequency != info->frequency) + { + pwminfo("Setting Timer %" PRIu8 + " to frequency %" PRIu32 + " Hz using %" PRIu8 " bits of resolution\n", + priv->timer, + info->frequency, + priv->duty_resolution); + + ret |= ledc_set_timer_div(priv->timer, + priv->clk_cfg, + info->frequency, + priv->duty_resolution); + + ret |= ledc_timer_rst(priv->timer); + + if (ret != OK) + { + pwmerr("ERROR: Failed to set timer configuration\n"); + return ret; + } + + pwminfo("Timer %d successfully configured\n", priv->timer); + priv->frequency = info->frequency; + } + + /* Update timer with given PWM channel duty */ + + for (int i = 0; i < channels; i++) + { +#ifdef CONFIG_PWM_NCHANNELS + duty = ledc_duty_bin_conversion(info->channels[i].duty, + priv->duty_resolution); +#else + duty = ledc_duty_bin_conversion(info[i].duty, + priv->duty_resolution); +#endif + if (priv->chans[i].duty != duty) + { + uint32_t max_value = (1 << priv->duty_resolution) - 1; + pwminfo("Setting PWM channel %" PRIu8 + " to duty cycle %" PRIu32 "(%0.4f)\n", + priv->chans[i].num, + duty, + (float)duty / max_value); + + flags = enter_critical_section(); + + ret |= ledc_duty_config(priv->chans[i].num, + LEDC_VAL_NO_CHANGE, + duty, + LEDC_DUTY_DIR_INCREASE, + 1, + 1, + 0); + + leave_critical_section(flags); + ret |= ledc_update_duty(priv->chans[i].num); + + if (ret != OK) + { + pwmerr("ERROR: Failed to set channel configuration\n"); + return ret; + } + + pwminfo("Channel %d successfully configured\n", + priv->chans[i].num); + + priv->chans[i].duty = duty; + } + + ledc_channel_output_enable(priv->chans[i].num); + } + + ledc_timer_resume(priv->timer); + + return 0; +} + +/**************************************************************************** + * Name: pwm_stop + * + * Description: + * Stop the pulsed output and reset the timer resources. + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_stop(struct pwm_lowerhalf_s *dev) +{ + int i; + int channel; + struct esp_ledc_s *priv = (struct esp_ledc_s *)dev; + + pwminfo("Stopping PWM Timer %d\n", priv->timer); + + for (i = 0; i < priv->channels; i++) + { + ledc_channel_output_disable(priv->chans[i].num); + } + + ledc_timer_pause(priv->timer); + ledc_timer_rst(priv->timer); + + return 0; +} + +/**************************************************************************** + * Name: pwm_ioctl + * + * Description: + * Lower-half logic may support platform-specific ioctl commands + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * cmd - The ioctl command + * arg - The argument accompanying the ioctl command + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_ioctl(struct pwm_lowerhalf_s *dev, int cmd, + unsigned long arg) +{ +#ifdef CONFIG_DEBUG_PWM_INFO + struct esp_ledc_s *priv = (struct esp_ledc_s *)dev; + + pwminfo("PWM Timer %d\n", priv->timer); +#endif + + return -ENOTTY; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp_ledc_init + * + * Description: + * Initialize one LEDC timer for use with the upper_level PWM driver. + * + * Input Parameters: + * timer - A number identifying the timer use. + * + * Returned Value: + * On success, a pointer to the ESP LEDC lower half PWM driver is + * returned. NULL is returned on any failure. + * + ****************************************************************************/ + +struct pwm_lowerhalf_s *esp_ledc_init(int timer) +{ + struct esp_ledc_s *lower = NULL; + + pwminfo("PWM Timer %u initialized\n", timer); + + switch (timer) + { +#ifdef CONFIG_ESPRESSIF_LEDC_TIMER0 + case 0: + { + lower = &g_pwm0dev; + break; + } +#endif + +#ifdef CONFIG_ESPRESSIF_LEDC_TIMER1 + case 1: + { + lower = &g_pwm1dev; + break; + } +#endif + +#ifdef CONFIG_ESPRESSIF_LEDC_TIMER2 + case 2: + { + lower = &g_pwm2dev; + break; + } +#endif + +#ifdef CONFIG_ESPRESSIF_LEDC_TIMER3 + case 3: + { + lower = &g_pwm3dev; + break; + } +#endif + + default: + { + pwmerr("ERROR: No such timer configured %d\n", timer); + lower = NULL; + break; + } + } + + return (struct pwm_lowerhalf_s *)lower; +} diff --git a/arch/xtensa/src/esp32s2/esp32s2_ledc.h b/arch/xtensa/src/common/espressif/esp_ledc.h similarity index 81% rename from arch/xtensa/src/esp32s2/esp32s2_ledc.h rename to arch/xtensa/src/common/espressif/esp_ledc.h index 542f160c2c..b6e1e29845 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_ledc.h +++ b/arch/xtensa/src/common/espressif/esp_ledc.h @@ -1,5 +1,7 @@ /**************************************************************************** - * arch/xtensa/src/esp32s2/esp32s2_ledc.h + * arch/xtensa/src/common/espressif/esp_ledc.h + * + * SPDX-License-Identifier: Apache-2.0 * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -18,8 +20,8 @@ * ****************************************************************************/ -#ifndef __ARCH_XTENSA_SRC_ESP32S2_ESP32S2_LEDC_H -#define __ARCH_XTENSA_SRC_ESP32S2_ESP32S2_LEDC_H +#ifndef __ARCH_XTENSA_SRC_COMMON_ESPRESSIF_ESP_LEDC_H +#define __ARCH_XTENSA_SRC_COMMON_ESPRESSIF_ESP_LEDC_H /**************************************************************************** * Included Files @@ -33,7 +35,7 @@ ****************************************************************************/ /**************************************************************************** - * Name: esp32s2_ledc_init + * Name: esp_ledc_init * * Description: * Initialize one LEDC timer for use with the upper_level PWM driver. @@ -42,11 +44,11 @@ * timer - A number identifying the timer use. * * Returned Value: - * On success, a pointer to the ESP32S2-C3 LEDC lower half PWM driver is + * On success, a pointer to the ESP LEDC lower half PWM driver is * returned. NULL is returned on any failure. * ****************************************************************************/ -struct pwm_lowerhalf_s *esp32s2_ledc_init(int timer); +struct pwm_lowerhalf_s *esp_ledc_init(int timer); -#endif /* __ARCH_RISCV_SRC_ESP32S2_ESP32S2_LEDC_H */ +#endif /* __ARCH_XTENSA_SRC_COMMON_ESPRESSIF_ESP_LEDC_H */ diff --git a/arch/xtensa/src/esp32s2/Kconfig b/arch/xtensa/src/esp32s2/Kconfig index 59249b43df..8f88fd6b33 100644 --- a/arch/xtensa/src/esp32s2/Kconfig +++ b/arch/xtensa/src/esp32s2/Kconfig @@ -540,6 +540,7 @@ config ESP32S2_LEDC default n select PWM select ARCH_HAVE_PWM_MULTICHAN + select ESPRESSIF_LEDC ---help--- Enable support to PWM on ESP32S2 using LEDC peripheral. diff --git a/arch/xtensa/src/esp32s2/Make.defs b/arch/xtensa/src/esp32s2/Make.defs index 72bfe6cd15..94350ff2d9 100644 --- a/arch/xtensa/src/esp32s2/Make.defs +++ b/arch/xtensa/src/esp32s2/Make.defs @@ -63,10 +63,6 @@ ifeq ($(CONFIG_ESP32S2_TWAI),y) CHIP_CSRCS += esp32s2_twai.c endif -ifeq ($(CONFIG_ESP32S2_LEDC),y) -CHIP_CSRCS += esp32s2_ledc.c -endif - ifeq ($(CONFIG_ESP32S2_SPI),y) CHIP_CSRCS += esp32s2_spi.c ifeq ($(CONFIG_SPI_SLAVE),y) diff --git a/arch/xtensa/src/esp32s2/esp32s2_ledc.c b/arch/xtensa/src/esp32s2/esp32s2_ledc.c deleted file mode 100644 index 8a66bc6caa..0000000000 --- a/arch/xtensa/src/esp32s2/esp32s2_ledc.c +++ /dev/null @@ -1,810 +0,0 @@ -/**************************************************************************** - * arch/xtensa/src/esp32s2/esp32s2_ledc.c - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you 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. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include <sys/param.h> -#include <inttypes.h> -#include <stdint.h> -#include <stdbool.h> -#include <stdio.h> -#include <string.h> -#include <debug.h> -#include <errno.h> - -#include "esp32s2_clockconfig.h" -#include "esp32s2_gpio.h" -#include "esp32s2_ledc.h" - -#include "xtensa.h" -#include "hardware/esp32s2_ledc.h" -#include "hardware/esp32s2_system.h" -#include "hardware/esp32s2_gpio_sigmap.h" - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/* LEDC total timers */ - -#define LEDC_TIMERS (4) - -/* LEDC total channels */ - -#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 -# define LEDC_CHANNELS (8) -#else -# define LEDC_CHANNELS (4) -#endif - -/* LEDC timer0 channels and offset */ - -#ifdef CONFIG_ESP32S2_LEDC_TIM0 -# if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 -# define LEDC_TIM0_CHANS CONFIG_ESP32S2_LEDC_TIM0_CHANNELS -# else -# define LEDC_TIM0_CHANS (1) -# endif -# define LEDC_TIM0_CHANS_OFF (0) -#endif - -/* LEDC timer1 channels and offset */ - -#ifdef CONFIG_ESP32S2_LEDC_TIM1 -# if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 -# define LEDC_TIM1_CHANS CONFIG_ESP32S2_LEDC_TIM1_CHANNELS -# else -# define LEDC_TIM1_CHANS (1) -# endif -# define LEDC_TIM1_CHANS_OFF (LEDC_TIM0_CHANS_OFF + LEDC_TIM0_CHANS) -#endif - -/* LEDC timer2 channels and offset */ - -#ifdef CONFIG_ESP32S2_LEDC_TIM2 -# if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 -# define LEDC_TIM2_CHANS CONFIG_ESP32S2_LEDC_TIM2_CHANNELS -# else -# define LEDC_TIM2_CHANS (1) -# endif - -# define LEDC_TIM2_CHANS_OFF (LEDC_TIM1_CHANS_OFF + LEDC_TIM1_CHANS) -#endif - -/* LEDC timer3 channels and offset */ - -#ifdef CONFIG_ESP32S2_LEDC_TIM3 -# if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 -# define LEDC_TIM3_CHANS CONFIG_ESP32S2_LEDC_TIM3_CHANNELS -# else -# define LEDC_TIM3_CHANS (1) -# endif - -# define LEDC_TIM3_CHANS_OFF (LEDC_TIM2_CHANS_OFF + LEDC_TIM2_CHANS) -#endif - -/* LEDC clock resource */ - -#define LEDC_CLK_RES (1) /* APB clock */ - -/* LEDC timer max reload */ - -#define LEDC_RELOAD_MAX (16384) /* 2^14 */ - -/* LEDC timer max clock divider parameter */ - -#define LEDC_CLKDIV_MAX (262144) /* 2^18 */ - -/* LEDC timer registers mapping */ - -#define LEDC_TIMER_REG(r, n) ((r) + (n) * (LEDC_TIMER1_CONF_REG - \ - LEDC_TIMER0_CONF_REG)) - -/* LEDC timer channel registers mapping */ - -#define setbits(bs, a) modifyreg32(a, 0, bs) -#define resetbits(bs, a) modifyreg32(a, bs, 0) - -#define LEDC_CHAN_REG(r, n) ((r) + (n) * (LEDC_CH1_CONF0_REG - \ - LEDC_CH0_CONF0_REG)) - -#define SET_TIMER_BITS(t, r, b) setbits(b, LEDC_TIMER_REG(r, (t)->num)); -#define SET_TIMER_REG(t, r, v) putreg32(v, LEDC_TIMER_REG(r, (t)->num)); - -#define SET_CHAN_BITS(c, r, b) setbits(b, LEDC_CHAN_REG(r, (c)->num)); -#define SET_CHAN_REG(c, r, v) putreg32(v, LEDC_CHAN_REG(r, (c)->num)); - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/* LEDC timer channel configuration */ - -struct esp32s2_ledc_chan_s -{ - const uint8_t num; /* Timer channel ID */ - const uint8_t pin; /* Timer channel GPIO pin number */ - uint16_t duty; /* Timer channel current duty */ -}; - -/* This structure represents the state of one LEDC timer */ - -struct esp32s2_ledc_s -{ - const struct pwm_ops_s *ops; /* PWM operations */ - - const uint8_t num; /* Timer ID */ - - const uint8_t channels; /* Timer channels number */ - struct esp32s2_ledc_chan_s *chans; /* Timer channels pointer */ - - uint32_t frequency; /* Timer current frequency */ - uint32_t reload; /* Timer current reload */ -}; - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -static int pwm_setup(struct pwm_lowerhalf_s *dev); -static int pwm_shutdown(struct pwm_lowerhalf_s *dev); -static int pwm_start(struct pwm_lowerhalf_s *dev, - const struct pwm_info_s *info); -static int pwm_stop(struct pwm_lowerhalf_s *dev); -static int pwm_ioctl(struct pwm_lowerhalf_s *dev, int cmd, - unsigned long arg); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/* LEDC PWM operations */ - -static const struct pwm_ops_s g_pwmops = -{ - .setup = pwm_setup, - .shutdown = pwm_shutdown, - .start = pwm_start, - .stop = pwm_stop, - .ioctl = pwm_ioctl -}; - -/* LEDC channels table */ - -static struct esp32s2_ledc_chan_s g_ledc_chans[LEDC_CHANNELS] = -{ - { - .num = 0, - .pin = CONFIG_ESP32S2_LEDC_CHANNEL0_PIN - }, - - { - .num = 1, - .pin = CONFIG_ESP32S2_LEDC_CHANNEL1_PIN - }, - - { - .num = 2, - .pin = CONFIG_ESP32S2_LEDC_CHANNEL2_PIN - }, - - { - .num = 3, - .pin = CONFIG_ESP32S2_LEDC_CHANNEL3_PIN - }, - -#if LEDC_CHANNELS > 4 - { - .num = 4, - .pin = CONFIG_ESP32S2_LEDC_CHANNEL4_PIN - }, - - { - .num = 5, - .pin = CONFIG_ESP32S2_LEDC_CHANNEL5_PIN - }, - - { - .num = 6, - .pin = CONFIG_ESP32S2_LEDC_CHANNEL6_PIN - }, - - { - .num = 7, - .pin = CONFIG_ESP32S2_LEDC_CHANNEL7_PIN - } -#endif -}; - -/* LEDC timer0 private data */ - -#ifdef CONFIG_ESP32S2_LEDC_TIM0 -static struct esp32s2_ledc_s g_pwm0dev = -{ - .ops = &g_pwmops, - .num = 0, - .channels = LEDC_TIM0_CHANS, - .chans = &g_ledc_chans[LEDC_TIM0_CHANS_OFF] -}; -#endif /* CONFIG_ESP32S2_LEDC_TIM0 */ - -/* LEDC timer1 private data */ - -#ifdef CONFIG_ESP32S2_LEDC_TIM1 -static struct esp32s2_ledc_s g_pwm1dev = -{ - .ops = &g_pwmops, - .num = 1, - .channels = LEDC_TIM1_CHANS, - .chans = &g_ledc_chans[LEDC_TIM1_CHANS_OFF] -}; -#endif /* CONFIG_ESP32S2_LEDC_TIM1 */ - -/* LEDC timer2 private data */ - -#ifdef CONFIG_ESP32S2_LEDC_TIM2 -static struct esp32s2_ledc_s g_pwm2dev = -{ - .ops = &g_pwmops, - .num = 2, - .channels = LEDC_TIM2_CHANS, - .chans = &g_ledc_chans[LEDC_TIM2_CHANS_OFF] -}; -#endif /* CONFIG_ESP32S2_LEDC_TIM2 */ - -/* LEDC timer3 private data */ - -#ifdef CONFIG_ESP32S2_LEDC_TIM3 -static struct esp32s2_ledc_s g_pwm3dev = -{ - .ops = &g_pwmops, - .num = 3, - .channels = LEDC_TIM3_CHANS, - .chans = &g_ledc_chans[LEDC_TIM3_CHANS_OFF] -}; -#endif /* CONFIG_ESP32S2_LEDC_TIM3 */ - -/* Clock reference count */ - -static uint32_t g_clk_ref; - -/**************************************************************************** - * Private functions - ****************************************************************************/ - -/**************************************************************************** - * Name: ledc_enable_clk - * - * Description: - * Enable LEDC clock. - * - * Input Parameters: - * None - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void ledc_enable_clk(void) -{ - irqstate_t flags; - - flags = enter_critical_section(); - - if (g_clk_ref == 0) - { - setbits(SYSTEM_LEDC_CLK_EN, SYSTEM_PERIP_CLK_EN0_REG); - resetbits(SYSTEM_LEDC_RST, SYSTEM_PERIP_RST_EN0_REG); - - putreg32(LEDC_CLK_RES, LEDC_CONF_REG); - putreg32(LEDC_CLK_EN, LEDC_CONF_REG); - - pwminfo("Enable ledc clock\n"); - } - - g_clk_ref++; - - leave_critical_section(flags); -} - -/**************************************************************************** - * Name: ledc_disable_clk - * - * Description: - * Disable LEDC clock. - * - * Input Parameters: - * None - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void ledc_disable_clk(void) -{ - irqstate_t flags; - - flags = enter_critical_section(); - - g_clk_ref--; - - if (g_clk_ref == 0) - { - pwminfo("Disable ledc clock\n"); - - setbits(SYSTEM_LEDC_RST, SYSTEM_PERIP_RST_EN0_REG); - resetbits(SYSTEM_LEDC_CLK_EN, SYSTEM_PERIP_CLK_EN0_REG); - } - - leave_critical_section(flags); -} - -/**************************************************************************** - * Name: setup_timer - * - * Description: - * Setup LEDC timer frequency and reload. - * - * Input Parameters: - * priv - A reference to the LEDC timer state structure - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void setup_timer(struct esp32s2_ledc_s *priv) -{ - irqstate_t flags; - uint32_t regval; - uint32_t reload; - uint32_t prescaler; - uint32_t shift = 1; - uint64_t pwmclk = esp_clk_apb_freq(); - - /* Reset timer */ - - SET_TIMER_BITS(priv, LEDC_TIMER0_CONF_REG, LEDC_TIMER0_RST); - - /* Calculate optimal values for the timer prescaler and for the timer - * modulo register. If 'frequency' is the desired frequency, then - * - * tpmclk = pwmclk / presc - * frequency = tpmclk / reload - * - * ==> - * - * reload = pwmclk / presc / frequency - * - * In ESP32S2, there are 3 clock resources for PWM: - * - * 1. APB clock (80 MHz) - * 2. RTC clock (8 MHz) - * 3. XTAL_CLK - * - * We mostly use APB clock generally. - * - * There are many solutions to this, but the best solution will be the one - * that has the largest reload value and the smallest prescaler value. - * That is the solution that should give us the most accuracy in the timer - * control. Subject to: - * - * 2 <= presc <= 2^18(262144) - * 1 <= clkdiv <= 2^10 - * - * clkdiv has 8-bit decimal precision, so - * clkdiv = pwmclk * 256 / 16384 / frequency would be optimal. - * - * Example: - * - * pwmclk = 80 MHz - * frequency = 100 Hz - * - * presc = 80,000,000 * 256 / 16,384 / 100 - * = 12,500 - * timclk = 80,000,000 / (12,500 / 256) - * = 1,638,400 - * counter = 1,638,400 / 100 - * = 16,384 - * = 2^14 - * shift = 14 - */ - - reload = (pwmclk * 256 / priv->frequency + LEDC_CLKDIV_MAX) / - LEDC_CLKDIV_MAX; - if (reload == 0) - { - reload = 1; - } - else if (reload > LEDC_RELOAD_MAX) - { - reload = LEDC_RELOAD_MAX; - } - - for (int c = 2; c <= LEDC_RELOAD_MAX; c *= 2) - { - if (c * 2 > reload) - { - reload = c; - break; - } - - shift++; - } - - prescaler = pwmclk * 256 / reload / priv->frequency; - - pwminfo("PWM timer%" PRIu8 " frequency=%0.4f reload=%" PRIu32 " shift=%" - PRIu32 " prescaler=%0.4f\n", - priv->num, (float)pwmclk / reload / ((float)prescaler / 256), - reload, shift, (float)prescaler / 256); - - /* Store reload for channel duty */ - - priv->reload = reload; - - flags = enter_critical_section(); - - /* Set timer clock divide and reload */ - - regval = (shift << LEDC_TIMER0_DUTY_RES_S) | - (prescaler << LEDC_CLK_DIV_TIMER0_S); - SET_TIMER_REG(priv, LEDC_TIMER0_CONF_REG, regval); - - /* Setup to timer to use APB clock (80MHz) */ - - SET_TIMER_BITS(priv, LEDC_TIMER0_CONF_REG, LEDC_TICK_SEL_TIMER0); - - /* Update clock divide and reload to hardware */ - - SET_TIMER_BITS(priv, LEDC_TIMER0_CONF_REG, LEDC_TIMER0_PARA_UP); - - leave_critical_section(flags); -} - -/**************************************************************************** - * Name: setup_channel - * - * Description: - * Setup LEDC timer channel duty. - * - * Input Parameters: - * priv - A reference to the LEDC timer state structure - * cn - Timer channel number - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void setup_channel(struct esp32s2_ledc_s *priv, int cn) -{ - irqstate_t flags; - uint32_t regval; - struct esp32s2_ledc_chan_s *chan = &priv->chans[cn]; - - /* Duty cycle: - * - * duty cycle = duty / 65536 * reload (fractional value) - */ - - regval = b16toi(chan->duty * priv->reload + b16HALF); - - pwminfo("channel=%" PRIu8 " duty=%" PRIu16 "(%0.4f) regval=%" PRIu32 - " reload=%" PRIu32 "\n", - chan->num, chan->duty, (float)chan->duty / UINT16_MAX, - regval, priv->reload); - - flags = enter_critical_section(); - - /* Reset config 0 & 1 registers */ - - SET_CHAN_REG(chan, LEDC_CH0_CONF0_REG, 0); - SET_CHAN_REG(chan, LEDC_CH0_CONF1_REG, 0); - - /* Set pulse phase 0 */ - - SET_CHAN_REG(chan, LEDC_CH0_HPOINT_REG, 0); - - /* Duty register uses bits [18:4] */ - - SET_CHAN_REG(chan, LEDC_CH0_DUTY_REG, regval << 4); - - /* Start GPIO output */ - - SET_CHAN_BITS(chan, LEDC_CH0_CONF0_REG, LEDC_SIG_OUT_EN_CH0); - - /* Start Duty counter */ - - SET_CHAN_BITS(chan, LEDC_CH0_CONF1_REG, LEDC_DUTY_START_CH0); - - /* Update duty and phase to hardware */ - - SET_CHAN_BITS(chan, LEDC_CH0_CONF0_REG, LEDC_PARA_UP_CH0); - - leave_critical_section(flags); -} - -/**************************************************************************** - * Name: pwm_setup - * - * Description: - * This method is called when the driver is opened. The lower half driver - * should configure and initialize the device so that it is ready for use. - * It should not, however, output pulses until the start method is called. - * - * Input Parameters: - * dev - A reference to the lower half PWM driver state structure - * - * Returned Value: - * Zero on success; a negated errno value on failure - * - ****************************************************************************/ - -static int pwm_setup(struct pwm_lowerhalf_s *dev) -{ - struct esp32s2_ledc_s *priv = (struct esp32s2_ledc_s *)dev; - - pwminfo("PWM timer%u\n", priv->num); - - ledc_enable_clk(); - - /* Setup channel GPIO pins */ - - for (int i = 0; i < priv->channels; i++) - { - pwminfo("channel%d --> pin%d\n", priv->chans[i].num, - priv->chans[i].pin); - - esp32s2_configgpio(priv->chans[i].pin, OUTPUT | PULLUP); - esp32s2_gpio_matrix_out(priv->chans[i].pin, - LEDC_LS_SIG_OUT0_IDX + priv->chans[i].num, - 0, 0); - } - - return 0; -} - -/**************************************************************************** - * Name: pwm_shutdown - * - * Description: - * This method is called when the driver is closed. The lower half driver - * stop pulsed output, free any resources, disable the timer hardware, and - * put the system into the lowest possible power usage state - * - * Input Parameters: - * dev - A reference to the lower half PWM driver state structure - * - * Returned Value: - * Zero on success; a negated errno value on failure - * - ****************************************************************************/ - -static int pwm_shutdown(struct pwm_lowerhalf_s *dev) -{ - struct esp32s2_ledc_s *priv = (struct esp32s2_ledc_s *)dev; -#ifdef CONFIG_PWM_NCHANNELS - int channels = MIN(priv->channels, CONFIG_PWM_NCHANNELS); -#else - int channels = 1; -#endif - - /* Stop timer */ - - pwm_stop(dev); - - /* Clear timer and channel configuration */ - - priv->frequency = 0; - priv->reload = 0; - for (int i = 0; i < channels; i++) - { - priv->chans[i].duty = 0; - } - - ledc_disable_clk(); - - return 0; -} - -/**************************************************************************** - * Name: pwm_start - * - * Description: - * (Re-)initialize the timer resources and start the pulsed output - * - * Input Parameters: - * dev - A reference to the lower half PWM driver state structure - * info - A reference to the characteristics of the pulsed output - * - * Returned Value: - * Zero on success; a negated errno value on failure - * - ****************************************************************************/ - -static int pwm_start(struct pwm_lowerhalf_s *dev, - const struct pwm_info_s *info) -{ - struct esp32s2_ledc_s *priv = (struct esp32s2_ledc_s *)dev; -#ifdef CONFIG_PWM_NCHANNELS - int channels = MIN(priv->channels, CONFIG_PWM_NCHANNELS); -#else - int channels = 1; -#endif - - pwminfo("PWM timer%d\n", priv->num); - - /* Update timer with given PWM timer frequency */ - - if (priv->frequency != info->frequency) - { - priv->frequency = info->frequency; - setup_timer(priv); - } - - /* Update timer with given PWM channel duty */ - - for (int i = 0; i < channels; i++) - { -#ifdef CONFIG_PWM_NCHANNELS - if (priv->chans[i].duty != info->channels[i].duty) -#else - if (priv->chans[i].duty != info[i].duty) -#endif - { -#ifdef CONFIG_PWM_NCHANNELS - priv->chans[i].duty = info->channels[i].duty; -#else - priv->chans[i].duty = info[i].duty; -#endif - setup_channel(priv, i); - } - } - - return 0; -} - -/**************************************************************************** - * Name: pwm_stop - * - * Description: - * Stop the pulsed output and reset the timer resources. - * - * Input Parameters: - * dev - A reference to the lower half PWM driver state structure - * - * Returned Value: - * Zero on success; a negated errno value on failure - * - ****************************************************************************/ - -static int pwm_stop(struct pwm_lowerhalf_s *dev) -{ - irqstate_t flags; - struct esp32s2_ledc_s *priv = (struct esp32s2_ledc_s *)dev; - - pwminfo("PWM timer%d\n", priv->num); - - flags = enter_critical_section(); - - /* Stop timer */ - - SET_TIMER_BITS(priv, LEDC_TIMER0_CONF_REG, LEDC_TIMER0_PAUSE); - - /* Reset timer */ - - SET_TIMER_BITS(priv, LEDC_TIMER0_CONF_REG, LEDC_TIMER0_RST); - - leave_critical_section(flags); - return 0; -} - -/**************************************************************************** - * Name: pwm_ioctl - * - * Description: - * Lower-half logic may support platform-specific ioctl commands - * - * Input Parameters: - * dev - A reference to the lower half PWM driver state structure - * cmd - The ioctl command - * arg - The argument accompanying the ioctl command - * - * Returned Value: - * Zero on success; a negated errno value on failure - * - ****************************************************************************/ - -static int pwm_ioctl(struct pwm_lowerhalf_s *dev, int cmd, - unsigned long arg) -{ -#ifdef CONFIG_DEBUG_PWM_INFO - struct esp32s2_ledc_s *priv = (struct esp32s2_ledc_s *)dev; - - pwminfo("PWM timer%d\n", priv->num); -#endif - - return -ENOTTY; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: esp32s2_ledc_init - * - * Description: - * Initialize one LEDC timer for use with the upper_level PWM driver. - * - * Input Parameters: - * timer - A number identifying the timer use. - * - * Returned Value: - * On success, a pointer to the ESP32S2 LEDC lower half PWM driver is - * returned. NULL is returned on any failure. - * - ****************************************************************************/ - -struct pwm_lowerhalf_s *esp32s2_ledc_init(int timer) -{ - struct esp32s2_ledc_s *lower = NULL; - - pwminfo("TIM%u\n", timer); - - switch (timer) - { -#ifdef CONFIG_ESP32S2_LEDC_TIM0 - case 0: - lower = &g_pwm0dev; - break; -#endif - -#ifdef CONFIG_ESP32S2_LEDC_TIM1 - case 1: - lower = &g_pwm1dev; - break; -#endif - -#ifdef CONFIG_ESP32S2_LEDC_TIM2 - case 2: - lower = &g_pwm2dev; - break; -#endif - -#ifdef CONFIG_ESP32S2_LEDC_TIM3 - case 3: - lower = &g_pwm3dev; - break; -#endif - - default: - pwmerr("ERROR: No such timer configured %d\n", timer); - lower = NULL; - break; - } - - return (struct pwm_lowerhalf_s *)lower; -} diff --git a/arch/xtensa/src/esp32s3/Kconfig b/arch/xtensa/src/esp32s3/Kconfig index ae8436c07b..f1e577e33c 100644 --- a/arch/xtensa/src/esp32s3/Kconfig +++ b/arch/xtensa/src/esp32s3/Kconfig @@ -813,6 +813,7 @@ config ESP32S3_LEDC default n select PWM select ARCH_HAVE_PWM_MULTICHAN + select ESPRESSIF_LEDC ---help--- Enable support to PWM on ESP32S3 using LEDC peripheral. diff --git a/arch/xtensa/src/esp32s3/Make.defs b/arch/xtensa/src/esp32s3/Make.defs index 1abe16fc01..a145f48bb0 100644 --- a/arch/xtensa/src/esp32s3/Make.defs +++ b/arch/xtensa/src/esp32s3/Make.defs @@ -61,10 +61,6 @@ ifeq ($(CONFIG_ESP32S3_TWAI),y) CHIP_CSRCS += esp32s3_twai.c endif -ifeq ($(CONFIG_ESP32S3_LEDC),y) -CHIP_CSRCS += esp32s3_ledc.c -endif - ifeq ($(CONFIG_ESP32S3_USBSERIAL),y) CHIP_CSRCS += esp32s3_usbserial.c endif diff --git a/arch/xtensa/src/esp32s3/esp32s3_ledc.c b/arch/xtensa/src/esp32s3/esp32s3_ledc.c deleted file mode 100644 index f01926eb65..0000000000 --- a/arch/xtensa/src/esp32s3/esp32s3_ledc.c +++ /dev/null @@ -1,865 +0,0 @@ -/**************************************************************************** - * arch/xtensa/src/esp32s3/esp32s3_ledc.c - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you 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. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include <sys/param.h> -#include <inttypes.h> -#include <stdint.h> -#include <stdbool.h> -#include <stdio.h> -#include <string.h> -#include <debug.h> -#include <errno.h> -#include <nuttx/config.h> - -#include "esp32s3_clockconfig.h" -#include "esp32s3_gpio.h" -#include "esp32s3_ledc.h" - -#include "xtensa.h" -#include "hardware/esp32s3_ledc.h" -#include "hardware/esp32s3_system.h" -#include "hardware/esp32s3_gpio_sigmap.h" - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/* LEDC total timers */ - -#define LEDC_TIMERS (4) - -/* LEDC total channels */ - -#if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 -# define LEDC_CHANNELS (8) -#else -# define LEDC_CHANNELS (4) -#endif - -/* LEDC timer0 channels and offset */ - -#ifdef CONFIG_ESP32S3_LEDC_TIM0 -# if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 -# define LEDC_TIM0_CHANS CONFIG_ESP32S3_LEDC_TIM0_CHANNELS -# else -# define LEDC_TIM0_CHANS (1) -# endif -# define LEDC_TIM0_CHANS_OFF (0) -#endif - -/* LEDC timer1 channels and offset */ - -#ifdef CONFIG_ESP32S3_LEDC_TIM1 -# if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 -# define LEDC_TIM1_CHANS CONFIG_ESP32S3_LEDC_TIM1_CHANNELS -# else -# define LEDC_TIM1_CHANS (1) -# endif -# define LEDC_TIM1_CHANS_OFF (LEDC_TIM0_CHANS_OFF + LEDC_TIM0_CHANS) -#endif - -/* LEDC timer2 channels and offset */ - -#ifdef CONFIG_ESP32S3_LEDC_TIM2 -# if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 -# define LEDC_TIM2_CHANS CONFIG_ESP32S3_LEDC_TIM2_CHANNELS -# else -# define LEDC_TIM2_CHANS (1) -# endif - -# define LEDC_TIM2_CHANS_OFF (LEDC_TIM1_CHANS_OFF + LEDC_TIM1_CHANS) -#endif - -/* LEDC timer3 channels and offset */ - -#ifdef CONFIG_ESP32S3_LEDC_TIM3 -# if defined(CONFIG_PWM_NCHANNELS) && CONFIG_PWM_NCHANNELS > 1 -# define LEDC_TIM3_CHANS CONFIG_ESP32S3_LEDC_TIM3_CHANNELS -# else -# define LEDC_TIM3_CHANS (1) -# endif - -# define LEDC_TIM3_CHANS_OFF (LEDC_TIM2_CHANS_OFF + LEDC_TIM2_CHANS) -#endif - -/* LEDC clock resource */ - -#define LEDC_CLK_RES_APB (1) /* APB clock */ -#define LEDC_CLK_RES_RC_FAST (2) /* RC_FAST clock */ -#define LEDC_CLK_RES_XTAL (3) /* XTAL clock */ - -/* LEDC clock source frequency */ - -#define LEDC_CLK_APB_FREQ (80 * MHZ) /* APB clock frequency */ -#define LEDC_CLK_RC_FAST_FREQ (17.5 * MHZ) /* RC_FAST clock frequency */ -#define LEDC_CLK_XTAL_FREQ (40 * MHZ) /* XTAL clock frequency */ - -/* LEDC timer max reload */ - -#define LEDC_RELOAD_MAX (16384) /* 2^14 */ - -/* LEDC timer max reload bit length */ - -#define LEDC_RELOAD_MAX_BIT_LEN (14) - -/* LEDC timer max clock divider parameter */ - -#define LEDC_CLKDIV_MAX (1024) /* 2^10 */ - -/* LEDC timer registers mapping */ - -#define LEDC_TIMER_REG(r, n) ((r) + (n) * (LEDC_TIMER1_CONF_REG - \ - LEDC_TIMER0_CONF_REG)) - -/* LEDC timer channel registers mapping */ - -#define setbits(bs, a) modifyreg32(a, 0, bs) -#define resetbits(bs, a) modifyreg32(a, bs, 0) - -#define LEDC_CHAN_REG(r, n) ((r) + (n) * (LEDC_CH1_CONF0_REG - \ - LEDC_CH0_CONF0_REG)) - -#define SET_TIMER_BITS(t, r, b) setbits(b, LEDC_TIMER_REG(r, (t)->num)); -#define SET_TIMER_REG(t, r, v) putreg32(v, LEDC_TIMER_REG(r, (t)->num)); - -#define SET_CHAN_BITS(c, r, b) setbits(b, LEDC_CHAN_REG(r, (c)->num)); -#define SET_CHAN_REG(c, r, v) putreg32(v, LEDC_CHAN_REG(r, (c)->num)); - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/* LEDC timer channel configuration */ - -struct esp32s3_ledc_chan_s -{ - const uint8_t num; /* Timer channel ID */ - const uint8_t pin; /* Timer channel GPIO pin number */ - uint16_t duty; /* Timer channel current duty */ -}; - -/* This structure represents the state of one LEDC timer */ - -struct esp32s3_ledc_s -{ - const struct pwm_ops_s *ops; /* PWM operations */ - - const uint8_t num; /* Timer ID */ - - const uint8_t channels; /* Timer channels number */ - struct esp32s3_ledc_chan_s *chans; /* Timer channels pointer */ - - uint32_t frequency; /* Timer current frequency */ - uint32_t reload; /* Timer current reload */ -}; - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -static int pwm_setup(struct pwm_lowerhalf_s *dev); -static int pwm_shutdown(struct pwm_lowerhalf_s *dev); -static int pwm_start(struct pwm_lowerhalf_s *dev, - const struct pwm_info_s *info); -static int pwm_stop(struct pwm_lowerhalf_s *dev); -static int pwm_ioctl(struct pwm_lowerhalf_s *dev, int cmd, - unsigned long arg); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/* LEDC PWM operations */ - -static const struct pwm_ops_s g_pwmops = -{ - .setup = pwm_setup, - .shutdown = pwm_shutdown, - .start = pwm_start, - .stop = pwm_stop, - .ioctl = pwm_ioctl -}; - -/* LEDC channels table */ - -static struct esp32s3_ledc_chan_s g_ledc_chans[LEDC_CHANNELS] = -{ - { - .num = 0, - .pin = CONFIG_ESP32S3_LEDC_CHANNEL0_PIN - }, - - { - .num = 1, - .pin = CONFIG_ESP32S3_LEDC_CHANNEL1_PIN - }, - - { - .num = 2, - .pin = CONFIG_ESP32S3_LEDC_CHANNEL2_PIN - }, - - { - .num = 3, - .pin = CONFIG_ESP32S3_LEDC_CHANNEL3_PIN - }, - -#if LEDC_CHANNELS > 4 - { - .num = 4, - .pin = CONFIG_ESP32S3_LEDC_CHANNEL4_PIN - }, - - { - .num = 5, - .pin = CONFIG_ESP32S3_LEDC_CHANNEL5_PIN - }, - - { - .num = 6, - .pin = CONFIG_ESP32S3_LEDC_CHANNEL6_PIN - }, - - { - .num = 7, - .pin = CONFIG_ESP32S3_LEDC_CHANNEL7_PIN - } -#endif -}; - -/* LEDC timer0 private data */ - -#ifdef CONFIG_ESP32S3_LEDC_TIM0 -static struct esp32s3_ledc_s g_pwm0dev = -{ - .ops = &g_pwmops, - .num = 0, - .channels = LEDC_TIM0_CHANS, - .chans = &g_ledc_chans[LEDC_TIM0_CHANS_OFF] -}; -#endif /* CONFIG_ESP32S3_LEDC_TIM0 */ - -/* LEDC timer1 private data */ - -#ifdef CONFIG_ESP32S3_LEDC_TIM1 -static struct esp32s3_ledc_s g_pwm1dev = -{ - .ops = &g_pwmops, - .num = 1, - .channels = LEDC_TIM1_CHANS, - .chans = &g_ledc_chans[LEDC_TIM1_CHANS_OFF] -}; -#endif /* CONFIG_ESP32S3_LEDC_TIM1 */ - -/* LEDC timer2 private data */ - -#ifdef CONFIG_ESP32S3_LEDC_TIM2 -static struct esp32s3_ledc_s g_pwm2dev = -{ - .ops = &g_pwmops, - .num = 2, - .channels = LEDC_TIM2_CHANS, - .chans = &g_ledc_chans[LEDC_TIM2_CHANS_OFF] -}; -#endif /* CONFIG_ESP32S3_LEDC_TIM2 */ - -/* LEDC timer3 private data */ - -#ifdef CONFIG_ESP32S3_LEDC_TIM3 -static struct esp32s3_ledc_s g_pwm3dev = -{ - .ops = &g_pwmops, - .num = 3, - .channels = LEDC_TIM3_CHANS, - .chans = &g_ledc_chans[LEDC_TIM3_CHANS_OFF] -}; -#endif /* CONFIG_ESP32S3_LEDC_TIM3 */ - -/* Clock source */ - -static uint32_t clk_src = 0; - -/**************************************************************************** - * Private functions - ****************************************************************************/ - -/**************************************************************************** - * Name: ledc_enable_clk - * - * Description: - * Enable LEDC clock. - * - * Input Parameters: - * None - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void ledc_enable_clk(void) -{ - irqstate_t flags; - - flags = enter_critical_section(); - - if (clk_src == 0) - { - setbits(SYSTEM_LEDC_CLK_EN, SYSTEM_PERIP_CLK_EN0_REG); - resetbits(SYSTEM_LEDC_RST, SYSTEM_PERIP_RST_EN0_REG); - - putreg32(LEDC_CLK_RES_APB, LEDC_CONF_REG); - setbits(LEDC_CLK_EN, LEDC_CONF_REG); - - /* We set default clock is APB. */ - - clk_src = LEDC_CLK_RES_APB; - - pwminfo("Enable ledc clock\n"); - } - - leave_critical_section(flags); -} - -/**************************************************************************** - * Name: ledc_disable_clk - * - * Description: - * Disable LEDC clock. - * - * Input Parameters: - * None - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void ledc_disable_clk(void) -{ - irqstate_t flags; - - flags = enter_critical_section(); - - if (clk_src != 0) - { - pwminfo("Disable ledc clock\n"); - - setbits(SYSTEM_LEDC_RST, SYSTEM_PERIP_RST_EN0_REG); - resetbits(SYSTEM_LEDC_CLK_EN, SYSTEM_PERIP_CLK_EN0_REG); - clk_src = 0; - } - - leave_critical_section(flags); -} - -/**************************************************************************** - * Name: setup_timer - * - * Description: - * Setup LEDC timer frequency and reload. - * - * Input Parameters: - * priv - A reference to the LEDC timer state structure - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void setup_timer(struct esp32s3_ledc_s *priv) -{ - irqstate_t flags; - uint32_t regval; - uint32_t reload; - float prescaler; - uint32_t integral_prescaler; - uint32_t fractional_prescaler; - uint8_t shift; - uint64_t pwmclk = LEDC_CLK_APB_FREQ; - - /* Determine the using clock source and set pwmclk */ - - switch (clk_src) - { - case LEDC_CLK_RES_APB: - - /* use APB clock */ - - pwmclk = LEDC_CLK_APB_FREQ; - break; - - case LEDC_CLK_RES_RC_FAST: - - /* use RC_FAST clock */ - - pwmclk = LEDC_CLK_RC_FAST_FREQ; - break; - - case LEDC_CLK_RES_XTAL: - - /* use XTAL clock */ - - pwmclk = LEDC_CLK_XTAL_FREQ; - break; - - default: - pwmerr("Invalid clock source or no clock has been inited !"); - break; - } - - /* Reset timer */ - - SET_TIMER_BITS(priv, LEDC_TIMER0_CONF_REG, LEDC_TIMER0_RST); - - /* Calculate optimal values for the timer prescaler and for the timer - * modulo register. If 'frequency' is the desired frequency, then - * - * tpmclk = pwmclk / presc - * frequency = tpmclk / reload - * - * ==> - * - * reload = pwmclk / presc / frequency - * - * In ESP32S3, there are 3 clock resources for PWM: - * - * 1. APB clock (80 MHz) - * 2. RC_FAST_CLK (17.5 MHz) - * 3. XTAL_CLK (40 MHZ) - * - * We mostly use APB clock generally. - * - * There are many solutions to this, but the best solution will be the one - * that has the largest reload value and the smallest prescaler value. - * That is the solution that should give us the most accuracy in the timer - * control. Subject to: - * - * 2 <= presc <= 2^14(16384) - * 1 <= clkdiv <= 2^10 - * - * clkdiv has 10-bit integral precision and 8-bit fractional precision, so - * clkdiv = pwmclk / 16384 / frequency would be optimal. - * - * Example: - * - * pwmclk = 80 MHz - * frequency = 100 Hz - * - * presc = 80,000,000 * 256 / 16,384 / 100 - * = 12,500 - * timclk = 80,000,000 / (12,500 / 256) - * = 1,638,400 - * counter = 1,638,400 / 100 - * = 16,384 - * = 2^14 - * shift = 14 - */ - - /* Search the maximum value of timer reload value */ - - for (reload = LEDC_RELOAD_MAX , shift = LEDC_RELOAD_MAX_BIT_LEN; - reload > 1; - reload = (reload >> 1), shift -= 1) - { - if (reload * priv->frequency <= pwmclk) - { - break; - } - } - - /* Calculate the prescaler */ - - prescaler = (float)pwmclk / priv->frequency / reload; - - /* Get the integral part */ - - integral_prescaler = (uint32_t) prescaler; - - /* Get the fractional part. To write to the registers, value need to be - * multiply by 256 - */ - - fractional_prescaler = (uint32_t)((prescaler - integral_prescaler) * 256); - - /* Prevent prescaler goto 0. In esp32 series, prescaler == 1 means - * clock signal goes pass-through - */ - - if (integral_prescaler == 0) integral_prescaler = 1; - - pwminfo("PWM timer%" PRIu8 " frequency=%0.4f reload=%" PRIu32 " shift=%" - PRIu8 " prescaler=%0.4f\n", - priv->num, (float)pwmclk / reload / ((float)prescaler), - reload, shift, (float)prescaler); - - /* Store reload for channel duty */ - - priv->reload = reload; - - flags = enter_critical_section(); - - /* Set timer clock divide and reload */ - - regval = (shift << LEDC_TIMER0_DUTY_RES_S) | - (fractional_prescaler << LEDC_CLK_DIV_TIMER0_S) | - (integral_prescaler << LEDC_CLK_DIV_TIMER0_S << 8); - SET_TIMER_REG(priv, LEDC_TIMER0_CONF_REG, regval); - - /* Update clock divide and reload to hardware */ - - SET_TIMER_BITS(priv, LEDC_TIMER0_CONF_REG, LEDC_TIMER0_PARA_UP); - - leave_critical_section(flags); -} - -/**************************************************************************** - * Name: setup_channel - * - * Description: - * Setup LEDC timer channel duty. - * - * Input Parameters: - * priv - A reference to the LEDC timer state structure - * cn - Timer channel number - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void setup_channel(struct esp32s3_ledc_s *priv, int cn) -{ - irqstate_t flags; - uint32_t regval; - struct esp32s3_ledc_chan_s *chan = &priv->chans[cn]; - - /* Duty cycle: - * - * duty cycle = duty / 65536 * reload (fractional value) - */ - - regval = b16toi(chan->duty * priv->reload + b16HALF); - - pwminfo("channel=%" PRIu8 " duty=%" PRIu16 "(%0.4f) regval=%" PRIu32 - " reload=%" PRIu32 "\n", - chan->num, chan->duty, (float)chan->duty / UINT16_MAX, - regval, priv->reload); - - flags = enter_critical_section(); - - /* Reset config 0 & 1 registers */ - - SET_CHAN_REG(chan, LEDC_CH0_CONF0_REG, 0); - SET_CHAN_REG(chan, LEDC_CH0_CONF1_REG, 0); - - /* Select the clock source */ - - SET_CHAN_REG(chan, LEDC_CH0_CONF0_REG, priv->num); - - /* Set pulse phase 0 */ - - SET_CHAN_REG(chan, LEDC_CH0_HPOINT_REG, 0); - - /* Duty register uses bits [18:4] */ - - SET_CHAN_REG(chan, LEDC_CH0_DUTY_REG, regval << 4); - - /* Start GPIO output */ - - SET_CHAN_BITS(chan, LEDC_CH0_CONF0_REG, LEDC_SIG_OUT_EN_CH0); - - /* Start Duty counter */ - - SET_CHAN_BITS(chan, LEDC_CH0_CONF1_REG, LEDC_DUTY_START_CH0); - - /* Update duty and phase to hardware */ - - SET_CHAN_BITS(chan, LEDC_CH0_CONF0_REG, LEDC_PARA_UP_CH0); - - leave_critical_section(flags); -} - -/**************************************************************************** - * Name: pwm_setup - * - * Description: - * This method is called when the driver is opened. The lower half driver - * should configure and initialize the device so that it is ready for use. - * It should not, however, output pulses until the start method is called. - * - * Input Parameters: - * dev - A reference to the lower half PWM driver state structure - * - * Returned Value: - * Zero on success; a negated errno value on failure - * - ****************************************************************************/ - -static int pwm_setup(struct pwm_lowerhalf_s *dev) -{ - struct esp32s3_ledc_s *priv = (struct esp32s3_ledc_s *)dev; - - pwminfo("PWM timer%u\n", priv->num); - - ledc_enable_clk(); - - /* Setup channel GPIO pins */ - - for (int i = 0; i < priv->channels; i++) - { - pwminfo("channel%d --> pin%d\n", priv->chans[i].num, - priv->chans[i].pin); - - esp32s3_configgpio(priv->chans[i].pin, OUTPUT | PULLUP); - esp32s3_gpio_matrix_out(priv->chans[i].pin, - LEDC_LS_SIG_OUT0_IDX + priv->chans[i].num, - 0, 0); - } - - return 0; -} - -/**************************************************************************** - * Name: pwm_shutdown - * - * Description: - * This method is called when the driver is closed. The lower half driver - * stop pulsed output, free any resources, disable the timer hardware, and - * put the system into the lowest possible power usage state - * - * Input Parameters: - * dev - A reference to the lower half PWM driver state structure - * - * Returned Value: - * Zero on success; a negated errno value on failure - * - ****************************************************************************/ - -static int pwm_shutdown(struct pwm_lowerhalf_s *dev) -{ - struct esp32s3_ledc_s *priv = (struct esp32s3_ledc_s *)dev; -#ifdef CONFIG_PWM_NCHANNELS - int channels = MIN(priv->channels, CONFIG_PWM_NCHANNELS); -#else - int channels = 1; -#endif - - /* Stop timer */ - - pwm_stop(dev); - - /* Clear timer and channel configuration */ - - priv->frequency = 0; - priv->reload = 0; - for (int i = 0; i < channels; i++) - { - priv->chans[i].duty = 0; - } - - ledc_disable_clk(); - - return 0; -} - -/**************************************************************************** - * Name: pwm_start - * - * Description: - * (Re-)initialize the timer resources and start the pulsed output - * - * Input Parameters: - * dev - A reference to the lower half PWM driver state structure - * info - A reference to the characteristics of the pulsed output - * - * Returned Value: - * Zero on success; a negated errno value on failure - * - ****************************************************************************/ - -static int pwm_start(struct pwm_lowerhalf_s *dev, - const struct pwm_info_s *info) -{ - struct esp32s3_ledc_s *priv = (struct esp32s3_ledc_s *)dev; -#ifdef CONFIG_PWM_NCHANNELS - int channels = MIN(priv->channels, CONFIG_PWM_NCHANNELS); -#else - int channels = 1; -#endif - - pwminfo("PWM timer%d\n", priv->num); - - /* Update timer with given PWM timer frequency */ - - if (priv->frequency != info->frequency) - { - priv->frequency = info->frequency; - setup_timer(priv); - } - - /* Update timer with given PWM channel duty */ - - for (int i = 0; i < channels; i++) - { -#ifdef CONFIG_PWM_NCHANNELS - if (priv->chans[i].duty != info->channels[i].duty) -#else - if (priv->chans[i].duty != info[i].duty) -#endif - { -#ifdef CONFIG_PWM_NCHANNELS - priv->chans[i].duty = info->channels[i].duty; -#else - priv->chans[i].duty = info[i].duty; -#endif - setup_channel(priv, i); - } - } - - return 0; -} - -/**************************************************************************** - * Name: pwm_stop - * - * Description: - * Stop the pulsed output and reset the timer resources. - * - * Input Parameters: - * dev - A reference to the lower half PWM driver state structure - * - * Returned Value: - * Zero on success; a negated errno value on failure - * - ****************************************************************************/ - -static int pwm_stop(struct pwm_lowerhalf_s *dev) -{ - irqstate_t flags; - struct esp32s3_ledc_s *priv = (struct esp32s3_ledc_s *)dev; - - pwminfo("PWM timer%d\n", priv->num); - - flags = enter_critical_section(); - - /* Stop timer */ - - SET_TIMER_BITS(priv, LEDC_TIMER0_CONF_REG, LEDC_TIMER0_PAUSE); - - /* Reset timer */ - - SET_TIMER_BITS(priv, LEDC_TIMER0_CONF_REG, LEDC_TIMER0_RST); - - leave_critical_section(flags); - return 0; -} - -/**************************************************************************** - * Name: pwm_ioctl - * - * Description: - * Lower-half logic may support platform-specific ioctl commands - * - * Input Parameters: - * dev - A reference to the lower half PWM driver state structure - * cmd - The ioctl command - * arg - The argument accompanying the ioctl command - * - * Returned Value: - * Zero on success; a negated errno value on failure - * - ****************************************************************************/ - -static int pwm_ioctl(struct pwm_lowerhalf_s *dev, int cmd, - unsigned long arg) -{ -#ifdef CONFIG_DEBUG_PWM_INFO - struct esp32s3_ledc_s *priv = (struct esp32s3_ledc_s *)dev; - - pwminfo("PWM timer%d\n", priv->num); -#endif - - return -ENOTTY; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: esp32s3_ledc_init - * - * Description: - * Initialize one LEDC timer for use with the upper_level PWM driver. - * - * Input Parameters: - * timer - A number identifying the timer use. - * - * Returned Value: - * On success, a pointer to the ESP32S3 LEDC lower half PWM driver is - * returned. NULL is returned on any failure. - * - ****************************************************************************/ - -struct pwm_lowerhalf_s *esp32s3_ledc_init(int timer) -{ - struct esp32s3_ledc_s *lower = NULL; - - pwminfo("TIM%u\n", timer); - - switch (timer) - { -#ifdef CONFIG_ESP32S3_LEDC_TIM0 - case 0: - lower = &g_pwm0dev; - break; -#endif - -#ifdef CONFIG_ESP32S3_LEDC_TIM1 - case 1: - lower = &g_pwm1dev; - break; -#endif - -#ifdef CONFIG_ESP32S3_LEDC_TIM2 - case 2: - lower = &g_pwm2dev; - break; -#endif - -#ifdef CONFIG_ESP32S3_LEDC_TIM3 - case 3: - lower = &g_pwm3dev; - break; -#endif - - default: - pwmerr("ERROR: No such timer configured %d\n", timer); - lower = NULL; - break; - } - - return (struct pwm_lowerhalf_s *)lower; -} diff --git a/arch/xtensa/src/esp32s3/esp32s3_ledc.h b/arch/xtensa/src/esp32s3/esp32s3_ledc.h deleted file mode 100644 index f39d102603..0000000000 --- a/arch/xtensa/src/esp32s3/esp32s3_ledc.h +++ /dev/null @@ -1,52 +0,0 @@ -/**************************************************************************** - * arch/xtensa/src/esp32s3/esp32s3_ledc.h - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you 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. - * - ****************************************************************************/ - -#ifndef __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_LEDC_H -#define __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_LEDC_H - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include <nuttx/config.h> -#include <nuttx/timers/pwm.h> - -/**************************************************************************** - * Public functions - ****************************************************************************/ - -/**************************************************************************** - * Name: esp32s3_ledc_init - * - * Description: - * Initialize one LEDC timer for use with the upper_level PWM driver. - * - * Input Parameters: - * timer - A number identifying the timer use. - * - * Returned Value: - * On success, a pointer to the ESP32S3-C3 LEDC lower half PWM driver is - * returned. NULL is returned on any failure. - * - ****************************************************************************/ - -struct pwm_lowerhalf_s *esp32s3_ledc_init(int timer); - -#endif /* __ARCH_RISCV_SRC_ESP32S3_ESP32S3_LEDC_H */ diff --git a/boards/xtensa/esp32s2/esp32s2-saola-1/configs/pwm/defconfig b/boards/xtensa/esp32s2/esp32s2-saola-1/configs/pwm/defconfig index bdb2e1e405..18028f92e7 100644 --- a/boards/xtensa/esp32s2/esp32s2-saola-1/configs/pwm/defconfig +++ b/boards/xtensa/esp32s2/esp32s2-saola-1/configs/pwm/defconfig @@ -19,12 +19,12 @@ CONFIG_ARCH_STACKDUMP=y CONFIG_ARCH_XTENSA=y CONFIG_BOARD_LOOPSPERMSEC=16717 CONFIG_BUILTIN=y -CONFIG_ESP32S2_LEDC=y -CONFIG_ESP32S2_LEDC_TIM0=y -CONFIG_ESP32S2_LEDC_TIM1=y -CONFIG_ESP32S2_LEDC_TIM2=y -CONFIG_ESP32S2_LEDC_TIM3=y CONFIG_ESP32S2_UART0=y +CONFIG_ESPRESSIF_LEDC=y +CONFIG_ESPRESSIF_LEDC_TIMER0=y +CONFIG_ESPRESSIF_LEDC_TIMER1=y +CONFIG_ESPRESSIF_LEDC_TIMER2=y +CONFIG_ESPRESSIF_LEDC_TIMER3=y CONFIG_EXAMPLES_PWM=y CONFIG_FS_PROCFS=y CONFIG_HAVE_CXX=y diff --git a/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2-saola-1.h b/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2-saola-1.h index fac2a2ca18..e242ba00af 100644 --- a/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2-saola-1.h +++ b/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2-saola-1.h @@ -232,7 +232,7 @@ int esp32s2_cs4344_initialize(void); * ****************************************************************************/ -#ifdef CONFIG_ESP32S2_LEDC +#ifdef CONFIG_ESPRESSIF_LEDC int esp32s2_pwm_setup(void); #endif diff --git a/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_bringup.c b/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_bringup.c index 03ec565d7c..f6cd43178a 100644 --- a/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_bringup.c +++ b/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_bringup.c @@ -66,8 +66,8 @@ # include "esp32s2_efuse.h" #endif -#ifdef CONFIG_ESP32S2_LEDC -# include "esp32s2_ledc.h" +#ifdef CONFIG_ESPRESSIF_LEDC +# include "espressif/esp_ledc.h" #endif #ifdef CONFIG_WATCHDOG @@ -184,13 +184,13 @@ int esp32s2_bringup(void) } #endif -#ifdef CONFIG_ESP32S2_LEDC +#ifdef CONFIG_ESPRESSIF_LEDC ret = esp32s2_pwm_setup(); if (ret < 0) { syslog(LOG_ERR, "ERROR: esp32s2_pwm_setup() failed: %d\n", ret); } -#endif /* CONFIG_ESP32S2_LEDC */ +#endif /* CONFIG_ESPRESSIF_LEDC */ #ifdef CONFIG_ESPRESSIF_SPIFLASH ret = board_spiflash_init(); diff --git a/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_ledc.c b/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_ledc.c index 442eccc627..e74c06f82f 100644 --- a/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_ledc.c +++ b/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_ledc.c @@ -35,7 +35,7 @@ #include <arch/board/board.h> -#include "esp32s2_ledc.h" +#include "espressif/esp_ledc.h" /**************************************************************************** * Pre-processor Definitions @@ -58,8 +58,8 @@ int esp32s2_pwm_setup(void) int ret; struct pwm_lowerhalf_s *pwm; -#ifdef CONFIG_ESP32S2_LEDC_TIM0 - pwm = esp32s2_ledc_init(0); +#if defined(CONFIG_ESP32S2_LEDC_TIM0) || defined(CONFIG_ESPRESSIF_LEDC_TIMER0) + pwm = esp_ledc_init(0); if (!pwm) { syslog(LOG_ERR, "ERROR: Failed to get the LEDC PWM 0 lower half\n"); @@ -76,8 +76,8 @@ int esp32s2_pwm_setup(void) } #endif -#ifdef CONFIG_ESP32S2_LEDC_TIM1 - pwm = esp32s2_ledc_init(1); +#if defined(CONFIG_ESP32S2_LEDC_TIM1) || defined(CONFIG_ESPRESSIF_LEDC_TIMER1) + pwm = esp_ledc_init(1); if (!pwm) { syslog(LOG_ERR, "ERROR: Failed to get the LEDC PWM 1 lower half\n"); @@ -94,8 +94,8 @@ int esp32s2_pwm_setup(void) } #endif -#ifdef CONFIG_ESP32S2_LEDC_TIM2 - pwm = esp32s2_ledc_init(2); +#if defined(CONFIG_ESP32S2_LEDC_TIM2) || defined(CONFIG_ESPRESSIF_LEDC_TIMER2) + pwm = esp_ledc_init(2); if (!pwm) { syslog(LOG_ERR, "ERROR: Failed to get the LEDC PWM 2 lower half\n"); @@ -112,8 +112,8 @@ int esp32s2_pwm_setup(void) } #endif -#ifdef CONFIG_ESP32S2_LEDC_TIM3 - pwm = esp32s2_ledc_init(3); +#if defined(CONFIG_ESP32S2_LEDC_TIM3) || defined(CONFIG_ESPRESSIF_LEDC_TIMER3) + pwm = esp_ledc_init(3); if (!pwm) { syslog(LOG_ERR, "ERROR: Failed to get the LEDC PWM 3 lower half\n"); diff --git a/boards/xtensa/esp32s3/common/include/esp32s3_board_ledc.h b/boards/xtensa/esp32s3/common/include/esp32s3_board_ledc.h index f5b514073e..8b3a3c36de 100644 --- a/boards/xtensa/esp32s3/common/include/esp32s3_board_ledc.h +++ b/boards/xtensa/esp32s3/common/include/esp32s3_board_ledc.h @@ -56,7 +56,7 @@ extern "C" * ****************************************************************************/ -#ifdef CONFIG_ESP32S3_LEDC +#ifdef CONFIG_ESPRESSIF_LEDC int esp32s3_pwm_setup(void); #endif diff --git a/boards/xtensa/esp32s3/common/src/Make.defs b/boards/xtensa/esp32s3/common/src/Make.defs index 100c9a84a7..14eab861b3 100644 --- a/boards/xtensa/esp32s3/common/src/Make.defs +++ b/boards/xtensa/esp32s3/common/src/Make.defs @@ -98,7 +98,7 @@ ifeq ($(CONFIG_USBMSC),y) CSRCS += esp32s3_usbmsc.c endif -ifeq ($(CONFIG_ESP32S3_LEDC),y) +ifeq ($(CONFIG_ESPRESSIF_LEDC),y) CSRCS += esp32s3_board_ledc.c endif diff --git a/boards/xtensa/esp32s3/common/src/esp32s3_board_ledc.c b/boards/xtensa/esp32s3/common/src/esp32s3_board_ledc.c index bf3f469cfa..6c9dd6eebe 100644 --- a/boards/xtensa/esp32s3/common/src/esp32s3_board_ledc.c +++ b/boards/xtensa/esp32s3/common/src/esp32s3_board_ledc.c @@ -35,7 +35,7 @@ #include <arch/board/board.h> -#include "esp32s3_ledc.h" +#include "espressif/esp_ledc.h" /**************************************************************************** * Pre-processor Definitions @@ -58,8 +58,8 @@ int esp32s3_pwm_setup(void) int ret; struct pwm_lowerhalf_s *pwm; -#ifdef CONFIG_ESP32S3_LEDC_TIM0 - pwm = esp32s3_ledc_init(0); +#if defined(CONFIG_ESP32S3_LEDC_TIM0) || defined(CONFIG_ESPRESSIF_LEDC_TIMER0) + pwm = esp_ledc_init(0); if (!pwm) { syslog(LOG_ERR, "ERROR: Failed to get the LEDC PWM 0 lower half\n"); @@ -76,8 +76,8 @@ int esp32s3_pwm_setup(void) } #endif -#ifdef CONFIG_ESP32S3_LEDC_TIM1 - pwm = esp32s3_ledc_init(1); +#if defined(CONFIG_ESP32S3_LEDC_TIM1) || defined(CONFIG_ESPRESSIF_LEDC_TIMER1) + pwm = esp_ledc_init(1); if (!pwm) { syslog(LOG_ERR, "ERROR: Failed to get the LEDC PWM 1 lower half\n"); @@ -94,8 +94,8 @@ int esp32s3_pwm_setup(void) } #endif -#ifdef CONFIG_ESP32S3_LEDC_TIM2 - pwm = esp32s3_ledc_init(2); +#if defined(CONFIG_ESP32S3_LEDC_TIM2) || defined(CONFIG_ESPRESSIF_LEDC_TIMER2) + pwm = esp_ledc_init(2); if (!pwm) { syslog(LOG_ERR, "ERROR: Failed to get the LEDC PWM 2 lower half\n"); @@ -112,8 +112,8 @@ int esp32s3_pwm_setup(void) } #endif -#ifdef CONFIG_ESP32S3_LEDC_TIM3 - pwm = esp32s3_ledc_init(3); +#if defined(CONFIG_ESP32S3_LEDC_TIM3) || defined(CONFIG_ESPRESSIF_LEDC_TIMER3) + pwm = esp_ledc_init(3); if (!pwm) { syslog(LOG_ERR, "ERROR: Failed to get the LEDC PWM 3 lower half\n"); diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/configs/pwm/defconfig b/boards/xtensa/esp32s3/esp32s3-devkit/configs/pwm/defconfig index 579224e214..d87be5396a 100644 --- a/boards/xtensa/esp32s3/esp32s3-devkit/configs/pwm/defconfig +++ b/boards/xtensa/esp32s3/esp32s3-devkit/configs/pwm/defconfig @@ -20,12 +20,12 @@ CONFIG_ARCH_STACKDUMP=y CONFIG_ARCH_XTENSA=y CONFIG_BOARD_LOOPSPERMSEC=16717 CONFIG_BUILTIN=y -CONFIG_ESP32S3_LEDC=y -CONFIG_ESP32S3_LEDC_TIM0=y -CONFIG_ESP32S3_LEDC_TIM1=y -CONFIG_ESP32S3_LEDC_TIM2=y -CONFIG_ESP32S3_LEDC_TIM3=y CONFIG_ESP32S3_UART0=y +CONFIG_ESPRESSIF_LEDC=y +CONFIG_ESPRESSIF_LEDC_TIMER0=y +CONFIG_ESPRESSIF_LEDC_TIMER1=y +CONFIG_ESPRESSIF_LEDC_TIMER2=y +CONFIG_ESPRESSIF_LEDC_TIMER3=y CONFIG_EXAMPLES_PWM=y CONFIG_EXAMPLES_PWM_CHANNEL1=0 CONFIG_EXAMPLES_PWM_CHANNEL2=1 diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c b/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c index d254bde29f..2e5021f39c 100644 --- a/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c +++ b/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c @@ -92,7 +92,7 @@ # include "esp32s3_efuse.h" #endif -#ifdef CONFIG_ESP32S3_LEDC +#ifdef CONFIG_ESPRESSIF_LEDC # include "esp32s3_board_ledc.h" #endif @@ -238,13 +238,13 @@ int esp32s3_bringup(void) } #endif -#ifdef CONFIG_ESP32S3_LEDC +#ifdef CONFIG_ESPRESSIF_LEDC ret = esp32s3_pwm_setup(); if (ret < 0) { syslog(LOG_ERR, "ERROR: esp32s3_pwm_setup() failed: %d\n", ret); } -#endif /* CONFIG_ESP32S3_LEDC */ +#endif /* CONFIG_ESPRESSIF_LEDC */ #ifdef CONFIG_ESP32S3_TIMER /* Configure general purpose timers */ diff --git a/boards/xtensa/esp32s3/esp32s3-korvo-2/src/esp32s3_bringup.c b/boards/xtensa/esp32s3/esp32s3-korvo-2/src/esp32s3_bringup.c index d56ae1f468..d1fb021736 100644 --- a/boards/xtensa/esp32s3/esp32s3-korvo-2/src/esp32s3_bringup.c +++ b/boards/xtensa/esp32s3/esp32s3-korvo-2/src/esp32s3_bringup.c @@ -85,7 +85,7 @@ # include "esp32s3_efuse.h" #endif -#ifdef CONFIG_ESP32S3_LEDC +#ifdef CONFIG_ESPRESSIF_LEDC # include "esp32s3_board_ledc.h" #endif @@ -203,13 +203,13 @@ int esp32s3_bringup(void) } #endif -#ifdef CONFIG_ESP32S3_LEDC +#ifdef CONFIG_ESPRESSIF_LEDC ret = esp32s3_pwm_setup(); if (ret < 0) { syslog(LOG_ERR, "ERROR: esp32s3_pwm_setup() failed: %d\n", ret); } -#endif /* CONFIG_ESP32S3_LEDC */ +#endif /* CONFIG_ESPRESSIF_LEDC */ #ifdef CONFIG_ESP32S3_TIMER /* Configure general purpose timers */ diff --git a/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/lcd/defconfig b/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/lcd/defconfig index 3bb5b45851..dbf50c4259 100644 --- a/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/lcd/defconfig +++ b/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/lcd/defconfig @@ -31,10 +31,6 @@ CONFIG_DEV_GPIO=y CONFIG_DRIVERS_VIDEO=y CONFIG_ESP32S3_GPIO_IRQ=y CONFIG_ESP32S3_I2C0=y -CONFIG_ESP32S3_LEDC=y -CONFIG_ESP32S3_LEDC_CHANNEL0_PIN=42 -CONFIG_ESP32S3_LEDC_TIM0=y -CONFIG_ESP32S3_LEDC_TIM0_CHANNELS=1 CONFIG_ESP32S3_SPI2=y CONFIG_ESP32S3_SPI2_CLKPIN=41 CONFIG_ESP32S3_SPI2_CSPIN=-1 @@ -43,6 +39,9 @@ CONFIG_ESP32S3_SPI2_MOSIPIN=40 CONFIG_ESP32S3_SPI_SWCS=y CONFIG_ESP32S3_SPI_UDCS=y CONFIG_ESP32S3_UART0=y +CONFIG_ESPRESSIF_LEDC=y +CONFIG_ESPRESSIF_LEDC_CHANNEL0_PIN=42 +CONFIG_ESPRESSIF_LEDC_TIMER0=y CONFIG_EXAMPLES_FB=y CONFIG_EXAMPLES_PWM=y CONFIG_FS_PROCFS=y diff --git a/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/lvgl/defconfig b/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/lvgl/defconfig index a823218382..277806a3b6 100644 --- a/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/lvgl/defconfig +++ b/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/lvgl/defconfig @@ -35,10 +35,6 @@ CONFIG_DEV_GPIO=y CONFIG_DRIVERS_VIDEO=y CONFIG_ESP32S3_GPIO_IRQ=y CONFIG_ESP32S3_I2C0=y -CONFIG_ESP32S3_LEDC=y -CONFIG_ESP32S3_LEDC_CHANNEL0_PIN=42 -CONFIG_ESP32S3_LEDC_TIM0=y -CONFIG_ESP32S3_LEDC_TIM0_CHANNELS=1 CONFIG_ESP32S3_SPI2=y CONFIG_ESP32S3_SPI2_CLKPIN=41 CONFIG_ESP32S3_SPI2_CSPIN=-1 @@ -50,6 +46,9 @@ CONFIG_ESP32S3_SPIRAM_MODE_OCT=y CONFIG_ESP32S3_SPI_SWCS=y CONFIG_ESP32S3_SPI_UDCS=y CONFIG_ESP32S3_UART0=y +CONFIG_ESPRESSIF_LEDC=y +CONFIG_ESPRESSIF_LEDC_CHANNEL0_PIN=42 +CONFIG_ESPRESSIF_LEDC_TIMER0=y CONFIG_EXAMPLES_FB=y CONFIG_EXAMPLES_LVGLDEMO=y CONFIG_EXAMPLES_PWM=y diff --git a/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/pwm/defconfig b/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/pwm/defconfig index ed5d8b60c7..7f35a8fa26 100644 --- a/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/pwm/defconfig +++ b/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/pwm/defconfig @@ -22,11 +22,10 @@ CONFIG_BOARD_LOOPSPERMSEC=16717 CONFIG_BUILTIN=y CONFIG_DEBUG_FULLOPT=y CONFIG_DEBUG_SYMBOLS=y -CONFIG_ESP32S3_LEDC=y -CONFIG_ESP32S3_LEDC_CHANNEL0_PIN=42 -CONFIG_ESP32S3_LEDC_TIM0=y -CONFIG_ESP32S3_LEDC_TIM0_CHANNELS=1 CONFIG_ESP32S3_UART0=y +CONFIG_ESPRESSIF_LEDC=y +CONFIG_ESPRESSIF_LEDC_CHANNEL0_PIN=42 +CONFIG_ESPRESSIF_LEDC_TIMER0=y CONFIG_EXAMPLES_PWM=y CONFIG_FS_PROCFS=y CONFIG_HAVE_CXX=y diff --git a/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/src/esp32s3_bringup.c b/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/src/esp32s3_bringup.c index 9be06f7f71..52eb07d853 100644 --- a/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/src/esp32s3_bringup.c +++ b/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/src/esp32s3_bringup.c @@ -85,7 +85,7 @@ # include "esp32s3_efuse.h" #endif -#ifdef CONFIG_ESP32S3_LEDC +#ifdef CONFIG_ESPRESSIF_LEDC # include "esp32s3_board_ledc.h" #endif @@ -223,7 +223,7 @@ int esp32s3_bringup(void) } #endif -#ifdef CONFIG_ESP32S3_LEDC +#ifdef CONFIG_ESPRESSIF_LEDC ret = esp32s3_pwm_setup(); if (ret < 0) {