diff --git a/arch/xtensa/src/esp32s3/esp32s3_ledc.c b/arch/xtensa/src/esp32s3/esp32s3_ledc.c index d5ffba0a5b..afbd8a9b78 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_ledc.c +++ b/arch/xtensa/src/esp32s3/esp32s3_ledc.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "esp32s3_clockconfig.h" #include "esp32s3_gpio.h" @@ -104,15 +105,27 @@ /* LEDC clock resource */ -#define LEDC_CLK_RES (1) /* APB clock */ +#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 (262144) /* 2^18 */ +#define LEDC_CLKDIV_MAX (1024) /* 2^10 */ /* LEDC timer registers mapping */ @@ -283,9 +296,9 @@ static struct esp32s3_ledc_s g_pwm3dev = }; #endif /* CONFIG_ESP32S3_LEDC_TIM3 */ -/* Clock reference count */ +/* Clock source */ -static uint32_t g_clk_ref; +static uint32_t clk_src = 0; /**************************************************************************** * Private functions @@ -311,19 +324,21 @@ static void ledc_enable_clk(void) flags = enter_critical_section(); - if (g_clk_ref == 0) + 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, LEDC_CONF_REG); - putreg32(LEDC_CLK_EN, LEDC_CONF_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"); } - g_clk_ref++; - leave_critical_section(flags); } @@ -347,14 +362,13 @@ static void ledc_disable_clk(void) flags = enter_critical_section(); - g_clk_ref--; - - if (g_clk_ref == 0) + 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); @@ -379,9 +393,41 @@ static void setup_timer(struct esp32s3_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(); + 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 */ @@ -400,8 +446,8 @@ static void setup_timer(struct esp32s3_ledc_s *priv) * In ESP32S3, there are 3 clock resources for PWM: * * 1. APB clock (80 MHz) - * 2. RTC clock (8 MHz) - * 3. XTAL_CLK + * 2. RC_FAST_CLK (17.5 MHz) + * 3. XTAL_CLK (40 MHZ) * * We mostly use APB clock generally. * @@ -410,11 +456,11 @@ static void setup_timer(struct esp32s3_ledc_s *priv) * That is the solution that should give us the most accuracy in the timer * control. Subject to: * - * 2 <= presc <= 2^18(262144) + * 2 <= presc <= 2^14(16384) * 1 <= clkdiv <= 2^10 * - * clkdiv has 8-bit decimal precision, so - * clkdiv = pwmclk * 256 / 16384 / frequency would be optimal. + * clkdiv has 10-bit integral precision and 8-bit fractional precision, so + * clkdiv = pwmclk / 16384 / frequency would be optimal. * * Example: * @@ -431,34 +477,42 @@ static void setup_timer(struct esp32s3_ledc_s *priv) * 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; - } + /* Search the maximum value of timer reload value */ - for (int c = 2; c <= LEDC_RELOAD_MAX; c *= 2) - { - if (c * 2 > reload) + for (reload = LEDC_RELOAD_MAX , shift = LEDC_RELOAD_MAX_BIT_LEN; + reload > 1; + reload = (reload >> 1), shift -= 1) + { + if (reload * priv->frequency <= pwmclk) { - reload = c; break; } + } - shift++; - } + /* Caculate the prescaler */ - prescaler = pwmclk * 256 / reload / priv->frequency; + 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=%" PRIu32 " prescaler=%0.4f\n", - priv->num, (float)pwmclk / reload / ((float)prescaler / 256), - reload, shift, (float)prescaler / 256); + priv->num, (float)pwmclk / reload / ((float)prescaler), + reload, shift, (float)prescaler); /* Store reload for channel duty */ @@ -469,13 +523,10 @@ static void setup_timer(struct esp32s3_ledc_s *priv) /* Set timer clock divide and reload */ regval = (shift << LEDC_TIMER0_DUTY_RES_S) | - (prescaler << LEDC_CLK_DIV_TIMER0_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); - /* 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); @@ -523,6 +574,10 @@ static void setup_channel(struct esp32s3_ledc_s *priv, int cn) 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); diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/configs/pwm/defconfig b/boards/xtensa/esp32s3/esp32s3-devkit/configs/pwm/defconfig index 1421e4de0d..e2719f2ff6 100644 --- a/boards/xtensa/esp32s3/esp32s3-devkit/configs/pwm/defconfig +++ b/boards/xtensa/esp32s3/esp32s3-devkit/configs/pwm/defconfig @@ -20,8 +20,6 @@ CONFIG_ARCH_STACKDUMP=y CONFIG_ARCH_XTENSA=y CONFIG_BOARD_LOOPSPERMSEC=16717 CONFIG_BUILTIN=y -CONFIG_DEBUG_FULLOPT=y -CONFIG_DEBUG_SYMBOLS=y CONFIG_ESP32S3_LEDC=y CONFIG_ESP32S3_LEDC_TIM0=y CONFIG_ESP32S3_LEDC_TIM1=y @@ -29,6 +27,8 @@ CONFIG_ESP32S3_LEDC_TIM2=y CONFIG_ESP32S3_LEDC_TIM3=y CONFIG_ESP32S3_UART0=y CONFIG_EXAMPLES_PWM=y +CONFIG_EXAMPLES_PWM_CHANNEL1=0 +CONFIG_EXAMPLES_PWM_CHANNEL2=1 CONFIG_FS_PROCFS=y CONFIG_HAVE_CXX=y CONFIG_HAVE_CXXINITIALIZE=y @@ -41,6 +41,8 @@ CONFIG_NSH_FILEIOSIZE=512 CONFIG_NSH_LINELEN=64 CONFIG_NSH_READLINE=y CONFIG_PREALLOC_TIMERS=4 +CONFIG_PWM_MULTICHAN=y +CONFIG_PWM_NCHANNELS=2 CONFIG_RAM_SIZE=114688 CONFIG_RAM_START=0x20000000 CONFIG_RR_INTERVAL=200