esp32s3/pwm: Fix pwm output

1. Fix pwm output always low problem.
2. Add multi channel support in defconfig
This commit is contained in:
w2016561536 2024-01-17 21:56:13 +08:00 committed by Alan Carvalho de Assis
parent 1e9434e2db
commit 829ec6d5e4
2 changed files with 103 additions and 46 deletions

View File

@ -30,6 +30,7 @@
#include <string.h>
#include <debug.h>
#include <errno.h>
#include <nuttx/config.h>
#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);

View File

@ -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