esp32s3/pwm: Fix pwm output
1. Fix pwm output always low problem. 2. Add multi channel support in defconfig
This commit is contained in:
parent
1e9434e2db
commit
829ec6d5e4
|
@ -30,6 +30,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <nuttx/config.h>
|
||||||
|
|
||||||
#include "esp32s3_clockconfig.h"
|
#include "esp32s3_clockconfig.h"
|
||||||
#include "esp32s3_gpio.h"
|
#include "esp32s3_gpio.h"
|
||||||
|
@ -104,15 +105,27 @@
|
||||||
|
|
||||||
/* LEDC clock resource */
|
/* 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 */
|
/* LEDC timer max reload */
|
||||||
|
|
||||||
#define LEDC_RELOAD_MAX (16384) /* 2^14 */
|
#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 */
|
/* LEDC timer max clock divider parameter */
|
||||||
|
|
||||||
#define LEDC_CLKDIV_MAX (262144) /* 2^18 */
|
#define LEDC_CLKDIV_MAX (1024) /* 2^10 */
|
||||||
|
|
||||||
/* LEDC timer registers mapping */
|
/* LEDC timer registers mapping */
|
||||||
|
|
||||||
|
@ -283,9 +296,9 @@ static struct esp32s3_ledc_s g_pwm3dev =
|
||||||
};
|
};
|
||||||
#endif /* CONFIG_ESP32S3_LEDC_TIM3 */
|
#endif /* CONFIG_ESP32S3_LEDC_TIM3 */
|
||||||
|
|
||||||
/* Clock reference count */
|
/* Clock source */
|
||||||
|
|
||||||
static uint32_t g_clk_ref;
|
static uint32_t clk_src = 0;
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Private functions
|
* Private functions
|
||||||
|
@ -311,19 +324,21 @@ static void ledc_enable_clk(void)
|
||||||
|
|
||||||
flags = enter_critical_section();
|
flags = enter_critical_section();
|
||||||
|
|
||||||
if (g_clk_ref == 0)
|
if (clk_src == 0)
|
||||||
{
|
{
|
||||||
setbits(SYSTEM_LEDC_CLK_EN, SYSTEM_PERIP_CLK_EN0_REG);
|
setbits(SYSTEM_LEDC_CLK_EN, SYSTEM_PERIP_CLK_EN0_REG);
|
||||||
resetbits(SYSTEM_LEDC_RST, SYSTEM_PERIP_RST_EN0_REG);
|
resetbits(SYSTEM_LEDC_RST, SYSTEM_PERIP_RST_EN0_REG);
|
||||||
|
|
||||||
putreg32(LEDC_CLK_RES, LEDC_CONF_REG);
|
putreg32(LEDC_CLK_RES_APB, LEDC_CONF_REG);
|
||||||
putreg32(LEDC_CLK_EN, 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");
|
pwminfo("Enable ledc clock\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
g_clk_ref++;
|
|
||||||
|
|
||||||
leave_critical_section(flags);
|
leave_critical_section(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,14 +362,13 @@ static void ledc_disable_clk(void)
|
||||||
|
|
||||||
flags = enter_critical_section();
|
flags = enter_critical_section();
|
||||||
|
|
||||||
g_clk_ref--;
|
if (clk_src != 0)
|
||||||
|
|
||||||
if (g_clk_ref == 0)
|
|
||||||
{
|
{
|
||||||
pwminfo("Disable ledc clock\n");
|
pwminfo("Disable ledc clock\n");
|
||||||
|
|
||||||
setbits(SYSTEM_LEDC_RST, SYSTEM_PERIP_RST_EN0_REG);
|
setbits(SYSTEM_LEDC_RST, SYSTEM_PERIP_RST_EN0_REG);
|
||||||
resetbits(SYSTEM_LEDC_CLK_EN, SYSTEM_PERIP_CLK_EN0_REG);
|
resetbits(SYSTEM_LEDC_CLK_EN, SYSTEM_PERIP_CLK_EN0_REG);
|
||||||
|
clk_src = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
leave_critical_section(flags);
|
leave_critical_section(flags);
|
||||||
|
@ -379,9 +393,41 @@ static void setup_timer(struct esp32s3_ledc_s *priv)
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
uint32_t regval;
|
uint32_t regval;
|
||||||
uint32_t reload;
|
uint32_t reload;
|
||||||
uint32_t prescaler;
|
float prescaler;
|
||||||
uint32_t shift = 1;
|
uint32_t integral_prescaler;
|
||||||
uint64_t pwmclk = esp_clk_apb_freq();
|
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 */
|
/* Reset timer */
|
||||||
|
|
||||||
|
@ -400,8 +446,8 @@ static void setup_timer(struct esp32s3_ledc_s *priv)
|
||||||
* In ESP32S3, there are 3 clock resources for PWM:
|
* In ESP32S3, there are 3 clock resources for PWM:
|
||||||
*
|
*
|
||||||
* 1. APB clock (80 MHz)
|
* 1. APB clock (80 MHz)
|
||||||
* 2. RTC clock (8 MHz)
|
* 2. RC_FAST_CLK (17.5 MHz)
|
||||||
* 3. XTAL_CLK
|
* 3. XTAL_CLK (40 MHZ)
|
||||||
*
|
*
|
||||||
* We mostly use APB clock generally.
|
* 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
|
* That is the solution that should give us the most accuracy in the timer
|
||||||
* control. Subject to:
|
* control. Subject to:
|
||||||
*
|
*
|
||||||
* 2 <= presc <= 2^18(262144)
|
* 2 <= presc <= 2^14(16384)
|
||||||
* 1 <= clkdiv <= 2^10
|
* 1 <= clkdiv <= 2^10
|
||||||
*
|
*
|
||||||
* clkdiv has 8-bit decimal precision, so
|
* clkdiv has 10-bit integral precision and 8-bit fractional precision, so
|
||||||
* clkdiv = pwmclk * 256 / 16384 / frequency would be optimal.
|
* clkdiv = pwmclk / 16384 / frequency would be optimal.
|
||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
*
|
*
|
||||||
|
@ -431,34 +477,42 @@ static void setup_timer(struct esp32s3_ledc_s *priv)
|
||||||
* shift = 14
|
* shift = 14
|
||||||
*/
|
*/
|
||||||
|
|
||||||
reload = (pwmclk * 256 / priv->frequency + LEDC_CLKDIV_MAX) /
|
/* Search the maximum value of timer reload value */
|
||||||
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)
|
for (reload = LEDC_RELOAD_MAX , shift = LEDC_RELOAD_MAX_BIT_LEN;
|
||||||
{
|
reload > 1;
|
||||||
if (c * 2 > reload)
|
reload = (reload >> 1), shift -= 1)
|
||||||
|
{
|
||||||
|
if (reload * priv->frequency <= pwmclk)
|
||||||
{
|
{
|
||||||
reload = c;
|
|
||||||
break;
|
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=%"
|
pwminfo("PWM timer%" PRIu8 " frequency=%0.4f reload=%" PRIu32 " shift=%"
|
||||||
PRIu32 " prescaler=%0.4f\n",
|
PRIu32 " prescaler=%0.4f\n",
|
||||||
priv->num, (float)pwmclk / reload / ((float)prescaler / 256),
|
priv->num, (float)pwmclk / reload / ((float)prescaler),
|
||||||
reload, shift, (float)prescaler / 256);
|
reload, shift, (float)prescaler);
|
||||||
|
|
||||||
/* Store reload for channel duty */
|
/* Store reload for channel duty */
|
||||||
|
|
||||||
|
@ -469,13 +523,10 @@ static void setup_timer(struct esp32s3_ledc_s *priv)
|
||||||
/* Set timer clock divide and reload */
|
/* Set timer clock divide and reload */
|
||||||
|
|
||||||
regval = (shift << LEDC_TIMER0_DUTY_RES_S) |
|
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);
|
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 */
|
/* Update clock divide and reload to hardware */
|
||||||
|
|
||||||
SET_TIMER_BITS(priv, LEDC_TIMER0_CONF_REG, LEDC_TIMER0_PARA_UP);
|
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_CONF0_REG, 0);
|
||||||
SET_CHAN_REG(chan, LEDC_CH0_CONF1_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 pulse phase 0 */
|
||||||
|
|
||||||
SET_CHAN_REG(chan, LEDC_CH0_HPOINT_REG, 0);
|
SET_CHAN_REG(chan, LEDC_CH0_HPOINT_REG, 0);
|
||||||
|
|
|
@ -20,8 +20,6 @@ CONFIG_ARCH_STACKDUMP=y
|
||||||
CONFIG_ARCH_XTENSA=y
|
CONFIG_ARCH_XTENSA=y
|
||||||
CONFIG_BOARD_LOOPSPERMSEC=16717
|
CONFIG_BOARD_LOOPSPERMSEC=16717
|
||||||
CONFIG_BUILTIN=y
|
CONFIG_BUILTIN=y
|
||||||
CONFIG_DEBUG_FULLOPT=y
|
|
||||||
CONFIG_DEBUG_SYMBOLS=y
|
|
||||||
CONFIG_ESP32S3_LEDC=y
|
CONFIG_ESP32S3_LEDC=y
|
||||||
CONFIG_ESP32S3_LEDC_TIM0=y
|
CONFIG_ESP32S3_LEDC_TIM0=y
|
||||||
CONFIG_ESP32S3_LEDC_TIM1=y
|
CONFIG_ESP32S3_LEDC_TIM1=y
|
||||||
|
@ -29,6 +27,8 @@ CONFIG_ESP32S3_LEDC_TIM2=y
|
||||||
CONFIG_ESP32S3_LEDC_TIM3=y
|
CONFIG_ESP32S3_LEDC_TIM3=y
|
||||||
CONFIG_ESP32S3_UART0=y
|
CONFIG_ESP32S3_UART0=y
|
||||||
CONFIG_EXAMPLES_PWM=y
|
CONFIG_EXAMPLES_PWM=y
|
||||||
|
CONFIG_EXAMPLES_PWM_CHANNEL1=0
|
||||||
|
CONFIG_EXAMPLES_PWM_CHANNEL2=1
|
||||||
CONFIG_FS_PROCFS=y
|
CONFIG_FS_PROCFS=y
|
||||||
CONFIG_HAVE_CXX=y
|
CONFIG_HAVE_CXX=y
|
||||||
CONFIG_HAVE_CXXINITIALIZE=y
|
CONFIG_HAVE_CXXINITIALIZE=y
|
||||||
|
@ -41,6 +41,8 @@ CONFIG_NSH_FILEIOSIZE=512
|
||||||
CONFIG_NSH_LINELEN=64
|
CONFIG_NSH_LINELEN=64
|
||||||
CONFIG_NSH_READLINE=y
|
CONFIG_NSH_READLINE=y
|
||||||
CONFIG_PREALLOC_TIMERS=4
|
CONFIG_PREALLOC_TIMERS=4
|
||||||
|
CONFIG_PWM_MULTICHAN=y
|
||||||
|
CONFIG_PWM_NCHANNELS=2
|
||||||
CONFIG_RAM_SIZE=114688
|
CONFIG_RAM_SIZE=114688
|
||||||
CONFIG_RAM_START=0x20000000
|
CONFIG_RAM_START=0x20000000
|
||||||
CONFIG_RR_INTERVAL=200
|
CONFIG_RR_INTERVAL=200
|
||||||
|
|
Loading…
Reference in New Issue