From fd625eaa89505ae326a671ce04a74f36579c6aea Mon Sep 17 00:00:00 2001 From: Matias N Date: Sun, 27 Oct 2019 17:21:32 -0600 Subject: [PATCH] arch/arm/src/stm32l4 and boards/arm/stm32l4/nucleo-l476rg: Add support for LPTIM timers on the STM32L4 as PWM outputs. --- arch/arm/src/stm32l4/Kconfig | 68 ++ arch/arm/src/stm32l4/hardware/stm32l4_lptim.h | 8 + arch/arm/src/stm32l4/hardware/stm32l4_tim.h | 76 +- arch/arm/src/stm32l4/stm32l4_pwm.c | 715 ++++++++++++++---- arch/arm/src/stm32l4/stm32l4_pwm.h | 74 +- .../arm/stm32l4/nucleo-l476rg/include/board.h | 3 + .../nucleo-l476rg/include/nucleo-l476rg.h | 28 +- .../arm/stm32l4/nucleo-l476rg/src/stm32_pwm.c | 38 + 8 files changed, 791 insertions(+), 219 deletions(-) diff --git a/arch/arm/src/stm32l4/Kconfig b/arch/arm/src/stm32l4/Kconfig index 31c0b45124..a9b5f059a8 100644 --- a/arch/arm/src/stm32l4/Kconfig +++ b/arch/arm/src/stm32l4/Kconfig @@ -1810,6 +1810,74 @@ config STM32L4_ONESHOT_MAXTIMERS of the timers and places an upper limit on the number of oneshot timers that you can use. +config STM32L4_LPTIM1_PWM + bool "LPTIM1 PWM" + default n + depends on STM32L4_LPTIM1 + select PWM + ---help--- + Reserve low-power timer 1 for use by PWM + + Timer devices may be used for different purposes. One special purpose is + to generate modulated outputs for such things as motor control. If STM32L4_LPTIM1 + is defined then THIS following may also be defined to indicate that + the timer is intended to be used for pulsed output modulation. + +if STM32L4_LPTIM1_PWM + +choice + prompt "LPTIM1 clock source" + default STM32L4_LPTIM1_CLK_APB1 + +config STM32L4_LPTIM1_CLK_APB1 + bool "Clock LPTIM1 from APB1" + +config STM32L4_LPTIM1_CLK_LSE + bool "Clock LPTIM1 from LSE" + +config STM32L4_LPTIM1_CLK_LSI + bool "Clock LPTIM1 from LSI" + +config STM32L4_LPTIM1_CLK_HSI + bool "Clock LPTIM1 from HSI" +endchoice + +endif # STM32L4_LPTIM1_PWM + +config STM32L4_LPTIM2_PWM + bool "LPTIM2 PWM" + default n + depends on STM32L4_LPTIM2 + select PWM + ---help--- + Reserve low-power timer 2 for use by PWM + + Timer devices may be used for different purposes. One special purpose is + to generate modulated outputs for such things as motor control. If STM32L4_LPTIM2 + is defined then THIS following may also be defined to indicate that + the timer is intended to be used for pulsed output modulation. + +if STM32L4_LPTIM2_PWM + +choice + prompt "LPTIM2 clock source" + default STM32L4_LPTIM2_CLK_APB1 + +config STM32L4_LPTIM2_CLK_APB1 + bool "Clock LPTIM2 from APB1" + +config STM32L4_LPTIM2_CLK_LSE + bool "Clock LPTIM2 from LSE" + +config STM32L4_LPTIM2_CLK_LSI + bool "Clock LPTIM2 from LSI" + +config STM32L4_LPTIM2_CLK_HSI + bool "Clock LPTIM2 from HSI" +endchoice + +endif # STM32L4_LPTIM2_PWM + config STM32L4_TIM1_PWM bool "TIM1 PWM" default n diff --git a/arch/arm/src/stm32l4/hardware/stm32l4_lptim.h b/arch/arm/src/stm32l4/hardware/stm32l4_lptim.h index 325373bb3f..773a05201f 100644 --- a/arch/arm/src/stm32l4/hardware/stm32l4_lptim.h +++ b/arch/arm/src/stm32l4/hardware/stm32l4_lptim.h @@ -114,4 +114,12 @@ #define LPTIM_CR_SNGSTRT (1 << 1) /* Bit 1: Single Mode */ #define LPTIM_CR_CNTSTRT (1 << 2) /* Bit 2: Continuous Mode */ +#define LPTIM_ISR_CMPM (1 << 0) /* Bit 0: Compare match */ +#define LPTIM_ISR_ARRM (1 << 1) /* Bit 1: Autoreload match */ +#define LPTIM_ISR_EXTTRIG (1 << 2) /* Bit 2: External trigger edge event */ +#define LPTIM_ISR_CMPOK (1 << 3) /* Bit 3: Compare register update OK */ +#define LPTIM_ISR_ARROK (1 << 4) /* Bit 4: Autoreload register update OK */ +#define LPTIM_ISR_UP (1 << 5) /* Bit 5: Counter direction change down to up */ +#define LPTIM_ISR_DOWN (1 << 6) /* Bit 6: Counter direction change up to down */ + #endif /* __ARCH_ARM_SRC_STM32L4_HARDWARE_STM32L4_LPTIM_H */ diff --git a/arch/arm/src/stm32l4/hardware/stm32l4_tim.h b/arch/arm/src/stm32l4/hardware/stm32l4_tim.h index 329ea556fd..5a559ac8a5 100644 --- a/arch/arm/src/stm32l4/hardware/stm32l4_tim.h +++ b/arch/arm/src/stm32l4/hardware/stm32l4_tim.h @@ -116,18 +116,6 @@ #define STM32L4_ATIM_OR2_OFFSET 0x0050 /* Timer option register 2 */ #define STM32L4_ATIM_OR3_OFFSET 0x0050 /* Timer option register 3 */ -/* Low-Power Timers - LPTIM1 and LPTIM2 */ - -#define STM32L4_LPTIM_ISR_OFFSET 0x0000 /* Interrupt and Status register */ -#define STM32L4_LPTIM_ICR_OFFSET 0x0004 /* Interrupt clear register */ -#define STM32L4_LPTIM_IER_OFFSET 0x0008 /* Interrupt enable register */ -#define STM32L4_LPTIM_CFGR_OFFSET 0x000c /* Configuration register */ -#define STM32L4_LPTIM_CR_OFFSET 0x0010 /* Control register */ -#define STM32L4_LPTIM_CMP_OFFSET 0x0014 /* Compare register */ -#define STM32L4_LPTIM_ARR_OFFSET 0x0018 /* Auto-reloud register (16-bit) */ -#define STM32L4_LPTIM_CNT_OFFSET 0x001c /* Counter (16-bit) */ -#define STM32L4_LPTIM_OR_OFFSET 0x001c /* Options Register */ - /* Register Addresses ***************************************************************/ /* Advanced Timers - TIM1 and TIM8 */ @@ -715,40 +703,40 @@ #define ATIM_BDTR_MOE (1 << 15) /* Bit 15: Main Output enable */ #define ATIM_BDTR_BKF_SHIFT (16) /* Bits 16-19: Break filter */ #define ATIM_BDTR_BKF_MASK (15 << ATIM_BDTR_BKF_SHIFT) -# define ATIM_BDTR_BKF_NOFILT (0 << ATIM_BDTR_BKF_SHIFT) /* 0000: No filter, BRK acts asynchronously */ -# define ATIM_BDTR_BKF_FCKINT2 (1 << ATIM_BDTR_BKF_SHIFT) /* 0001: fSAMPLING=fCK_INT, N=2 */ -# define ATIM_BDTR_BKF_FCKINT4 (2 << ATIM_BDTR_BKF_SHIFT) /* 0010: fSAMPLING=fCK_INT, N=4 */ -# define ATIM_BDTR_BKF_FCKINT8 (3 << ATIM_BDTR_BKF_SHIFT) /* 0011: fSAMPLING=fCK_INT, N=8 */ -# define ATIM_BDTR_BKF_FDTSd26 (4 << ATIM_BDTR_BKF_SHIFT) /* 0100: fSAMPLING=fDTS/2, N=6 */ -# define ATIM_BDTR_BKF_FDTSd28 (5 << ATIM_BDTR_BKF_SHIFT) /* 0101: fSAMPLING=fDTS/2, N=8 */ -# define ATIM_BDTR_BKF_FDTSd36 (6 << ATIM_BDTR_BKF_SHIFT) /* 0110: fSAMPLING=fDTS/4, N=6 */ -# define ATIM_BDTR_BKF_FDTSd38 (7 << ATIM_BDTR_BKF_SHIFT) /* 0111: fSAMPLING=fDTS/4, N=8 */ -# define ATIM_BDTR_BKF_FDTSd86 (8 << ATIM_BDTR_BKF_SHIFT) /* 1000: fSAMPLING=fDTS/8, N=6 */ -# define ATIM_BDTR_BKF_FDTSd88 (9 << ATIM_BDTR_BKF_SHIFT) /* 1001: fSAMPLING=fDTS/8, N=8 */ -# define ATIM_BDTR_BKF_FDTSd165 (10 << ATIM_BDTR_BKF_SHIFT) /* 1010: fSAMPLING=fDTS/16, N=5 */ -# define ATIM_BDTR_BKF_FDTSd166 (11 << ATIM_BDTR_BKF_SHIFT) /* 1011: fSAMPLING=fDTS/16, N=6 */ -# define ATIM_BDTR_BKF_FDTSd168 (12 << ATIM_BDTR_BKF_SHIFT) /* 1100: fSAMPLING=fDTS/16, N=8 */ -# define ATIM_BDTR_BKF_FDTSd325 (13 << ATIM_BDTR_BKF_SHIFT) /* 1101: fSAMPLING=fDTS/32, N=5 */ -# define ATIM_BDTR_BKF_FDTSd326 (14 << ATIM_BDTR_BKF_SHIFT) /* 1110: fSAMPLING=fDTS/32, N=6 */ -# define ATIM_BDTR_BKF_FDTSd328 (15 << ATIM_BDTR_BKF_SHIFT) /* 1111: fSAMPLING=fDTS/32, N=8 */ +# define ATIM_BDTR_BKF_NOFILT (0 << ATIM_BDTR_BKF_SHIFT) /* 0000: No filter, BRK acts asynchronously */ +# define ATIM_BDTR_BKF_FCKINT2 (1 << ATIM_BDTR_BKF_SHIFT) /* 0001: fSAMPLING=fCK_INT, N=2 */ +# define ATIM_BDTR_BKF_FCKINT4 (2 << ATIM_BDTR_BKF_SHIFT) /* 0010: fSAMPLING=fCK_INT, N=4 */ +# define ATIM_BDTR_BKF_FCKINT8 (3 << ATIM_BDTR_BKF_SHIFT) /* 0011: fSAMPLING=fCK_INT, N=8 */ +# define ATIM_BDTR_BKF_FDTSd26 (4 << ATIM_BDTR_BKF_SHIFT) /* 0100: fSAMPLING=fDTS/2, N=6 */ +# define ATIM_BDTR_BKF_FDTSd28 (5 << ATIM_BDTR_BKF_SHIFT) /* 0101: fSAMPLING=fDTS/2, N=8 */ +# define ATIM_BDTR_BKF_FDTSd36 (6 << ATIM_BDTR_BKF_SHIFT) /* 0110: fSAMPLING=fDTS/4, N=6 */ +# define ATIM_BDTR_BKF_FDTSd38 (7 << ATIM_BDTR_BKF_SHIFT) /* 0111: fSAMPLING=fDTS/4, N=8 */ +# define ATIM_BDTR_BKF_FDTSd86 (8 << ATIM_BDTR_BKF_SHIFT) /* 1000: fSAMPLING=fDTS/8, N=6 */ +# define ATIM_BDTR_BKF_FDTSd88 (9 << ATIM_BDTR_BKF_SHIFT) /* 1001: fSAMPLING=fDTS/8, N=8 */ +# define ATIM_BDTR_BKF_FDTSd165 (10 << ATIM_BDTR_BKF_SHIFT) /* 1010: fSAMPLING=fDTS/16, N=5 */ +# define ATIM_BDTR_BKF_FDTSd166 (11 << ATIM_BDTR_BKF_SHIFT) /* 1011: fSAMPLING=fDTS/16, N=6 */ +# define ATIM_BDTR_BKF_FDTSd168 (12 << ATIM_BDTR_BKF_SHIFT) /* 1100: fSAMPLING=fDTS/16, N=8 */ +# define ATIM_BDTR_BKF_FDTSd325 (13 << ATIM_BDTR_BKF_SHIFT) /* 1101: fSAMPLING=fDTS/32, N=5 */ +# define ATIM_BDTR_BKF_FDTSd326 (14 << ATIM_BDTR_BKF_SHIFT) /* 1110: fSAMPLING=fDTS/32, N=6 */ +# define ATIM_BDTR_BKF_FDTSd328 (15 << ATIM_BDTR_BKF_SHIFT) /* 1111: fSAMPLING=fDTS/32, N=8 */ #define ATIM_BDTR_BK2F_SHIFT (20) /* Bits 20-23: Break 2 filter */ #define ATIM_BDTR_BK2F_MASK (15 << ATIM_BDTR_BK2F_SHIFT) -# define ATIM_BDTR_BK2F_NOFILT (0 << ATIM_BDTR_BK2F_SHIFT) /* 0000: No filter, BRK 2 acts asynchronously */ -# define ATIM_BDTR_BK2F_FCKINT2 (1 << ATIM_BDTR_BK2F_SHIFT) /* 0001: fSAMPLING=fCK_INT, N=2 */ -# define ATIM_BDTR_BK2F_FCKINT4 (2 << ATIM_BDTR_BK2F_SHIFT) /* 0010: fSAMPLING=fCK_INT, N=4 */ -# define ATIM_BDTR_BK2F_FCKINT8 (3 << ATIM_BDTR_BK2F_SHIFT) /* 0011: fSAMPLING=fCK_INT, N=8 */ -# define ATIM_BDTR_BK2F_FDTSd26 (4 << ATIM_BDTR_BK2F_SHIFT) /* 0100: fSAMPLING=fDTS/2, N=6 */ -# define ATIM_BDTR_BK2F_FDTSd28 (5 << ATIM_BDTR_BK2F_SHIFT) /* 0101: fSAMPLING=fDTS/2, N=8 */ -# define ATIM_BDTR_BK2F_FDTSd36 (6 << ATIM_BDTR_BK2F_SHIFT) /* 0110: fSAMPLING=fDTS/4, N=6 */ -# define ATIM_BDTR_BK2F_FDTSd38 (7 << ATIM_BDTR_BK2F_SHIFT) /* 0111: fSAMPLING=fDTS/4, N=8 */ -# define ATIM_BDTR_BK2F_FDTSd86 (8 << ATIM_BDTR_BK2F_SHIFT) /* 1000: fSAMPLING=fDTS/8, N=6 */ -# define ATIM_BDTR_BK2F_FDTSd88 (9 << ATIM_BDTR_BK2F_SHIFT) /* 1001: fSAMPLING=fDTS/8, N=8 */ -# define ATIM_BDTR_BK2F_FDTSd165 (10 << ATIM_BDTR_BK2F_SHIFT) /* 1010: fSAMPLING=fDTS/16, N=5 */ -# define ATIM_BDTR_BK2F_FDTSd166 (11 << ATIM_BDTR_BK2F_SHIFT) /* 1011: fSAMPLING=fDTS/16, N=6 */ -# define ATIM_BDTR_BK2F_FDTSd168 (12 << ATIM_BDTR_BK2F_SHIFT) /* 1100: fSAMPLING=fDTS/16, N=8 */ -# define ATIM_BDTR_BK2F_FDTSd325 (13 << ATIM_BDTR_BK2F_SHIFT) /* 1101: fSAMPLING=fDTS/32, N=5 */ -# define ATIM_BDTR_BK2F_FDTSd326 (14 << ATIM_BDTR_BK2F_SHIFT) /* 1110: fSAMPLING=fDTS/32, N=6 */ -# define ATIM_BDTR_BK2F_FDTSd328 (15 << ATIM_BDTR_BK2F_SHIFT) /* 1111: fSAMPLING=fDTS/32, N=8 */ +# define ATIM_BDTR_BK2F_NOFILT (0 << ATIM_BDTR_BK2F_SHIFT) /* 0000: No filter, BRK 2 acts asynchronously */ +# define ATIM_BDTR_BK2F_FCKINT2 (1 << ATIM_BDTR_BK2F_SHIFT) /* 0001: fSAMPLING=fCK_INT, N=2 */ +# define ATIM_BDTR_BK2F_FCKINT4 (2 << ATIM_BDTR_BK2F_SHIFT) /* 0010: fSAMPLING=fCK_INT, N=4 */ +# define ATIM_BDTR_BK2F_FCKINT8 (3 << ATIM_BDTR_BK2F_SHIFT) /* 0011: fSAMPLING=fCK_INT, N=8 */ +# define ATIM_BDTR_BK2F_FDTSd26 (4 << ATIM_BDTR_BK2F_SHIFT) /* 0100: fSAMPLING=fDTS/2, N=6 */ +# define ATIM_BDTR_BK2F_FDTSd28 (5 << ATIM_BDTR_BK2F_SHIFT) /* 0101: fSAMPLING=fDTS/2, N=8 */ +# define ATIM_BDTR_BK2F_FDTSd36 (6 << ATIM_BDTR_BK2F_SHIFT) /* 0110: fSAMPLING=fDTS/4, N=6 */ +# define ATIM_BDTR_BK2F_FDTSd38 (7 << ATIM_BDTR_BK2F_SHIFT) /* 0111: fSAMPLING=fDTS/4, N=8 */ +# define ATIM_BDTR_BK2F_FDTSd86 (8 << ATIM_BDTR_BK2F_SHIFT) /* 1000: fSAMPLING=fDTS/8, N=6 */ +# define ATIM_BDTR_BK2F_FDTSd88 (9 << ATIM_BDTR_BK2F_SHIFT) /* 1001: fSAMPLING=fDTS/8, N=8 */ +# define ATIM_BDTR_BK2F_FDTSd165 (10 << ATIM_BDTR_BK2F_SHIFT) /* 1010: fSAMPLING=fDTS/16, N=5 */ +# define ATIM_BDTR_BK2F_FDTSd166 (11 << ATIM_BDTR_BK2F_SHIFT) /* 1011: fSAMPLING=fDTS/16, N=6 */ +# define ATIM_BDTR_BK2F_FDTSd168 (12 << ATIM_BDTR_BK2F_SHIFT) /* 1100: fSAMPLING=fDTS/16, N=8 */ +# define ATIM_BDTR_BK2F_FDTSd325 (13 << ATIM_BDTR_BK2F_SHIFT) /* 1101: fSAMPLING=fDTS/32, N=5 */ +# define ATIM_BDTR_BK2F_FDTSd326 (14 << ATIM_BDTR_BK2F_SHIFT) /* 1110: fSAMPLING=fDTS/32, N=6 */ +# define ATIM_BDTR_BK2F_FDTSd328 (15 << ATIM_BDTR_BK2F_SHIFT) /* 1111: fSAMPLING=fDTS/32, N=8 */ #define ATIM_BDTR_BK2E (1 << 24) /* Bit 24: Break 2 enable */ #define ATIM_BDTR_BK2P (1 << 1525 /* Bit 25:Break 2 polarity */ diff --git a/arch/arm/src/stm32l4/stm32l4_pwm.c b/arch/arm/src/stm32l4/stm32l4_pwm.c index 2d2ed3bb93..5dde1e39b8 100644 --- a/arch/arm/src/stm32l4/stm32l4_pwm.c +++ b/arch/arm/src/stm32l4/stm32l4_pwm.c @@ -62,16 +62,19 @@ * intended for use with the PWM upper half driver. */ -#if defined(CONFIG_STM32L4_TIM1_PWM) || defined(CONFIG_STM32L4_TIM2_PWM) || \ - defined(CONFIG_STM32L4_TIM3_PWM) || defined(CONFIG_STM32L4_TIM4_PWM) || \ - defined(CONFIG_STM32L4_TIM5_PWM) || defined(CONFIG_STM32L4_TIM8_PWM) || \ - defined(CONFIG_STM32L4_TIM15_PWM) || defined(CONFIG_STM32L4_TIM16_PWM) || \ - defined(CONFIG_STM32L4_TIM17_PWM) +#if defined(CONFIG_STM32L4_TIM1_PWM) || defined(CONFIG_STM32L4_TIM2_PWM) || \ + defined(CONFIG_STM32L4_TIM3_PWM) || defined(CONFIG_STM32L4_TIM4_PWM) || \ + defined(CONFIG_STM32L4_TIM5_PWM) || defined(CONFIG_STM32L4_TIM8_PWM) || \ + defined(CONFIG_STM32L4_TIM15_PWM) || defined(CONFIG_STM32L4_TIM16_PWM) || \ + defined(CONFIG_STM32L4_TIM17_PWM) || defined(CONFIG_STM32L4_LPTIM1_PWM) || \ + defined(CONFIG_STM32L4_LPTIM2_PWM) /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ + /* PWM/Timer Definitions ****************************************************/ + /* The following definitions are used to identify the various time types */ #define TIMTYPE_BASIC 0 /* Basic timers: TIM6,7 */ @@ -79,6 +82,7 @@ #define TIMTYPE_COUNTUP16 2 /* General 16-bit count-up timers: TIM15-17 */ #define TIMTYPE_GENERAL32 3 /* General 32-bit timers: TIM2,5 */ #define TIMTYPE_ADVANCED 4 /* Advanced timers: TIM1,8 */ +#define TIMTYPE_LOWPOWER 5 /* Low Power timers: LPTIM1,2 */ #define TIMTYPE_TIM1 TIMTYPE_ADVANCED #define TIMTYPE_TIM2 TIMTYPE_GENERAL32 @@ -91,6 +95,8 @@ #define TIMTYPE_TIM15 TIMTYPE_COUNTUP16 #define TIMTYPE_TIM16 TIMTYPE_COUNTUP16 #define TIMTYPE_TIM17 TIMTYPE_COUNTUP16 +#define TIMTYPE_LPTIM1 TIMTYPE_LOWPOWER +#define TIMTYPE_LPTIM2 TIMTYPE_LOWPOWER /* Debug ********************************************************************/ @@ -161,13 +167,17 @@ struct stm32l4_pwmtimer_s /**************************************************************************** * Static Function Prototypes ****************************************************************************/ + /* Register access */ -static uint16_t stm32l4pwm_getreg(struct stm32l4_pwmtimer_s *priv, int offset); -static void stm32l4pwm_putreg(struct stm32l4_pwmtimer_s *priv, int offset, uint16_t value); +static uint16_t stm32l4pwm_getreg(struct stm32l4_pwmtimer_s *priv, + int offset); +static void stm32l4pwm_putreg(struct stm32l4_pwmtimer_s *priv, int offset, + uint16_t value); #ifdef CONFIG_DEBUG_PWM_INFO -static void stm32l4pwm_dumpregs(struct stm32l4_pwmtimer_s *priv, FAR const char *msg); +static void stm32l4pwm_dumpregs(struct stm32l4_pwmtimer_s *priv, + const char *msg); #else # define stm32l4pwm_dumpregs(priv,msg) #endif @@ -177,7 +187,11 @@ static void stm32l4pwm_dumpregs(struct stm32l4_pwmtimer_s *priv, FAR const char static int stm32l4pwm_timer(FAR struct stm32l4_pwmtimer_s *priv, FAR const struct pwm_info_s *info); -#if defined(CONFIG_PWM_PULSECOUNT) && (defined(CONFIG_STM32L4_TIM1_PWM) || defined(CONFIG_STM32L4_TIM8_PWM)) +static int stm32l4pwm_lptimer(FAR struct stm32l4_pwmtimer_s *priv, + FAR const struct pwm_info_s *info); + +#if defined(CONFIG_PWM_PULSECOUNT) && (defined(CONFIG_STM32L4_TIM1_PWM) || \ + defined(CONFIG_STM32L4_TIM8_PWM)) static int stm32l4pwm_interrupt(struct stm32l4_pwmtimer_s *priv); #if defined(CONFIG_STM32L4_TIM1_PWM) static int stm32l4pwm_tim1interrupt(int irq, void *context, FAR void *arg); @@ -209,7 +223,10 @@ static int stm32l4pwm_ioctl(FAR struct pwm_lowerhalf_s *dev, /**************************************************************************** * Private Data ****************************************************************************/ -/* This is the list of lower half PWM driver methods used by the upper half driver */ + +/* This is the list of lower half PWM driver methods used by the upper half + * driver. + */ static const struct pwm_ops_s g_pwmops = { @@ -606,6 +623,66 @@ static struct stm32l4_pwmtimer_s g_pwm17dev = }; #endif +#ifdef CONFIG_STM32L4_LPTIM1_PWM +static struct stm32l4_pwmtimer_s g_pwmlp1dev = +{ + .ops = &g_pwmops, + .timid = 1, + .channels = + { +#ifdef CONFIG_STM32L4_LPTIM1_CHANNEL1 + { + .channel = 1, + .pincfg = PWM_LPTIM1_CH1CFG, + .npincfg = PWM_LPTIM1_CH1NCFG, + }, +#endif + }, + .timtype = TIMTYPE_LPTIM1, + .mode = STM32L4_TIMMODE_COUNTUP, + .base = STM32L4_LPTIM1_BASE, +#if defined(CONFIG_STM32L4_LPTIM1_CLK_APB1) + .pclk = STM32L4_PCLK1_FREQUENCY, +#elif defined(CONFIG_STM32L4_LPTIM1_CLK_LSE) + .pclk = STM32L4_LSE_FREQUENCY, +#elif defined(CONFIG_STM32L4_LPTIM1_CLK_LSI) + .pclk = STM32L4_LSI_FREQUENCY, +#elif defined(CONFIG_STM32L4_LPTIM1_CLK_HSI) + .pclk = STM32L4_HSI_FREQUENCY, +#endif +}; +#endif + +#ifdef CONFIG_STM32L4_LPTIM2_PWM +static struct stm32l4_pwmtimer_s g_pwmlp2dev = +{ + .ops = &g_pwmops, + .timid = 2, + .channels = + { +#ifdef CONFIG_STM32L4_LPTIM2_CHANNEL1 + { + .channel = 1, + .pincfg = PWM_LPTIM2_CH1CFG, + .npincfg = PWM_LPTIM2_CH1NCFG, + }, +#endif + }, + .timtype = TIMTYPE_LPTIM2, + .mode = STM32L4_TIMMODE_COUNTUP, + .base = STM32L4_LPTIM2_BASE, +#if defined(CONFIG_STM32L4_LPTIM2_CLK_APB1) + .pclk = STM32L4_PCLK1_FREQUENCY, +#elif defined(CONFIG_STM32L4_LPTIM2_CLK_LSE) + .pclk = STM32L4_LSE_FREQUENCY, +#elif defined(CONFIG_STM32L4_LPTIM2_CLK_LSI) + .pclk = STM32L4_LSI_FREQUENCY, +#elif defined(CONFIG_STM32L4_LPTIM2_CLK_HSI) + .pclk = STM32L4_HSI_FREQUENCY, +#endif +}; +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -688,42 +765,57 @@ static void stm32l4pwm_putreg(struct stm32l4_pwmtimer_s *priv, int offset, static void stm32l4pwm_dumpregs(struct stm32l4_pwmtimer_s *priv, FAR const char *msg) { - pwminfo("%s:\n", msg); - pwminfo(" CR1: %04x CR2: %04x SMCR: %04x DIER: %04x\n", - stm32l4pwm_getreg(priv, STM32L4_GTIM_CR1_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_GTIM_CR2_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_GTIM_SMCR_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_GTIM_DIER_OFFSET)); - pwminfo(" SR: %04x EGR: %04x CCMR1: %04x CCMR2: %04x\n", - stm32l4pwm_getreg(priv, STM32L4_GTIM_SR_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_GTIM_EGR_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_GTIM_CCMR1_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_GTIM_CCMR2_OFFSET)); - pwminfo(" CCER: %04x CNT: %04x PSC: %04x ARR: %04x\n", - stm32l4pwm_getreg(priv, STM32L4_GTIM_CCER_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_GTIM_CNT_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_GTIM_PSC_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_GTIM_ARR_OFFSET)); - pwminfo(" CCR1: %04x CCR2: %04x CCR3: %04x CCR4: %04x\n", - stm32l4pwm_getreg(priv, STM32L4_GTIM_CCR1_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_GTIM_CCR2_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_GTIM_CCR3_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_GTIM_CCR4_OFFSET)); -#if defined(CONFIG_STM32L4_TIM1_PWM) || defined(CONFIG_STM32L4_TIM8_PWM) - if (priv->timtype == TIMTYPE_ADVANCED) + if (priv->timtype == TIMTYPE_LOWPOWER) { - pwminfo(" RCR: %04x BDTR: %04x DCR: %04x DMAR: %04x\n", - stm32l4pwm_getreg(priv, STM32L4_ATIM_RCR_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_ATIM_BDTR_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_ATIM_DCR_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_ATIM_DMAR_OFFSET)); + pwminfo("%s:\n", msg); + pwminfo(" CFGR: %04x CR: %04x CMP: %04x ARR: %04x\n", + stm32l4pwm_getreg(priv, STM32L4_LPTIM_CFGR_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_LPTIM_CR_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_LPTIM_CMP_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_LPTIM_ARR_OFFSET)); + pwminfo(" ISR: %04x CNT: %04x\n", + stm32l4pwm_getreg(priv, STM32L4_LPTIM_ISR_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_LPTIM_CNT_OFFSET)); } else -#endif { - pwminfo(" DCR: %04x DMAR: %04x\n", - stm32l4pwm_getreg(priv, STM32L4_GTIM_DCR_OFFSET), - stm32l4pwm_getreg(priv, STM32L4_GTIM_DMAR_OFFSET)); + pwminfo("%s:\n", msg); + pwminfo(" CR1: %04x CR2: %04x SMCR: %04x DIER: %04x\n", + stm32l4pwm_getreg(priv, STM32L4_GTIM_CR1_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_GTIM_CR2_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_GTIM_SMCR_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_GTIM_DIER_OFFSET)); + pwminfo(" SR: %04x EGR: %04x CCMR1: %04x CCMR2: %04x\n", + stm32l4pwm_getreg(priv, STM32L4_GTIM_SR_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_GTIM_EGR_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_GTIM_CCMR1_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_GTIM_CCMR2_OFFSET)); + pwminfo(" CCER: %04x CNT: %04x PSC: %04x ARR: %04x\n", + stm32l4pwm_getreg(priv, STM32L4_GTIM_CCER_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_GTIM_CNT_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_GTIM_PSC_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_GTIM_ARR_OFFSET)); + pwminfo(" CCR1: %04x CCR2: %04x CCR3: %04x CCR4: %04x\n", + stm32l4pwm_getreg(priv, STM32L4_GTIM_CCR1_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_GTIM_CCR2_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_GTIM_CCR3_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_GTIM_CCR4_OFFSET)); +#if defined(CONFIG_STM32L4_TIM1_PWM) || defined(CONFIG_STM32L4_TIM8_PWM) + if (priv->timtype == TIMTYPE_ADVANCED) + { + pwminfo(" RCR: %04x BDTR: %04x DCR: %04x DMAR: %04x\n", + stm32l4pwm_getreg(priv, STM32L4_ATIM_RCR_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_ATIM_BDTR_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_ATIM_DCR_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_ATIM_DMAR_OFFSET)); + } + else +#endif + { + pwminfo(" DCR: %04x DMAR: %04x\n", + stm32l4pwm_getreg(priv, STM32L4_GTIM_DCR_OFFSET), + stm32l4pwm_getreg(priv, STM32L4_GTIM_DMAR_OFFSET)); + } } } #endif @@ -1266,8 +1358,8 @@ static int stm32l4pwm_timer(FAR struct stm32l4_pwmtimer_s *priv, * output compare N idle state. */ - ccer &= ~(ATIM_CCER_CC1NE | ATIM_CCER_CC1NP | ATIM_CCER_CC2NE | ATIM_CCER_CC2NP | - ATIM_CCER_CC3NE | ATIM_CCER_CC3NP); + ccer &= ~(ATIM_CCER_CC1NE | ATIM_CCER_CC1NP | ATIM_CCER_CC2NE | + ATIM_CCER_CC2NP | ATIM_CCER_CC3NE | ATIM_CCER_CC3NP); ccer |= ccnenable; @@ -1290,7 +1382,6 @@ static int stm32l4pwm_timer(FAR struct stm32l4_pwmtimer_s *priv, defined(CONFIG_STM32L4_TIM17_PWM) if (priv->timtype == TIMTYPE_COUNTUP16) { - /* Reset output N polarity level, output N state, output compare state, * output compare N idle state. */ @@ -1353,6 +1444,145 @@ static int stm32l4pwm_timer(FAR struct stm32l4_pwmtimer_s *priv, return OK; } +/**************************************************************************** + * Name: stm32l4pwm_lptimer + * + * Description: + * (Re-)initialize the low-power timer resources and start the + * pulsed output + * + * Input Parameters: + * priv - 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 stm32l4pwm_lptimer(FAR struct stm32l4_pwmtimer_s *priv, + FAR const struct pwm_info_s *info) +{ + /* Calculated values */ + + uint8_t prescaler; + uint32_t timclk; + uint32_t reload; + uint32_t ccr; + + /* Register contents */ + + uint16_t cr; + uint32_t cfgr; + + DEBUGASSERT(priv != NULL && info != NULL); + + pwminfo("LPTIM%u frequency: %u duty: %08x\n", + priv->timid, info->frequency, info->duty); + + DEBUGASSERT(info->frequency > 0); + DEBUGASSERT(info->duty >= 0 && info->duty < uitoub16(100)); + + /* LPTIM only has 8 possible prescaler values, from /1 to /128 + * We will attempt to find the lowest prescaler that results + * in a maximum reload value which can be represented in 16 bit. + * For certain desired frequencies it is possible that the clock + * is too high and other one needs to be selected. + */ + + for (prescaler = 0; prescaler < 8; prescaler++) + { + timclk = priv->pclk / (1 << prescaler); + reload = timclk / info->frequency; + + if (reload <= 65535) + { + /* The reload counter is feasible, go with it */ + + break; + } + } + + if (reload < 2) + { + reload = 1; + } + else if (reload > 65535) + { + reload = 65535; + } + + /* TODO: if the desired frequency is not possible this should give an error and + * not simply return the feasible frequency without complaining. + */ + + pwminfo("LPTIM%u PCLK: %u frequency: %u TIMCLK: %u prescaler: %u reload: %u\n", + priv->timid, priv->pclk, info->frequency, timclk, prescaler, reload); + + /* Disable the timer to set the prescaler */ + + cr = stm32l4pwm_getreg(priv, STM32L4_LPTIM_CR_OFFSET); + + cr &= ~LPTIM_CR_ENABLE; + stm32l4pwm_putreg(priv, STM32L4_LPTIM_CR_OFFSET, cr); + + /* We have to way two counter clocks before the clock is actually enabled */ + + up_udelay(2 * USEC_PER_SEC / timclk); + + /* Set the prescaler value and choose high polarity */ + + cfgr = getreg32(priv->base + STM32L4_LPTIM_CFGR_OFFSET); + + cfgr &= ~LPTIM_CFGR_PRESC_MASK; + cfgr |= (prescaler << LPTIM_CFGR_PRESC_SHIFT) | LPTIM_CFGR_WAVPOL; + + putreg32(cfgr, priv->base + STM32L4_LPTIM_CFGR_OFFSET); + + /* Enable again, ARR and CMP need to be written while enabled */ + + cr |= LPTIM_CR_ENABLE; + stm32l4pwm_putreg(priv, STM32L4_LPTIM_CR_OFFSET, cr); + + /* Compute reload value */ + + ub16_t duty = info->duty; + + /* Duty cycle: + * + * duty cycle = ccr / reload (fractional value) + */ + + ccr = b16toi(duty * reload + b16HALF); + + pwminfo("ccr: %u\n", ccr); + + /* Set reload register */ + + stm32l4pwm_putreg(priv, STM32L4_LPTIM_ARR_OFFSET, (uint16_t)reload); + + /* Wait for write to complete */ + + while (!(getreg32(priv->base + STM32L4_LPTIM_ISR_OFFSET) & LPTIM_ISR_ARROK)); + + /* Set compare register */ + + stm32l4pwm_putreg(priv, STM32L4_LPTIM_CMP_OFFSET, (uint16_t)ccr); + + /* Wait for write to complete */ + + while (!(getreg32(priv->base + STM32L4_LPTIM_ISR_OFFSET) & LPTIM_ISR_CMPOK)); + + /* Start counter */ + + cr = stm32l4pwm_getreg(priv, STM32L4_LPTIM_CR_OFFSET); + cr |= LPTIM_CR_CNTSTRT; + stm32l4pwm_putreg(priv, STM32L4_LPTIM_CR_OFFSET, cr); + + stm32l4pwm_dumpregs(priv, "After starting"); + return OK; +} + #ifndef CONFIG_PWM_PULSECOUNT /**************************************************************************** * Name: stm32l4pwm_update_duty @@ -1384,7 +1614,8 @@ static int stm32l4pwm_update_duty(FAR struct stm32l4_pwmtimer_s *priv, DEBUGASSERT(priv != NULL); - pwminfo("TIM%u channel: %u duty: %08x\n", + pwminfo("%s%u channel: %u duty: %08x\n", + priv->timtype == TIMTYPE_LOWPOWER ? "LPTIM" : "TIM", priv->timid, channel, duty); #ifndef CONFIG_PWM_MULTICHAN @@ -1394,7 +1625,14 @@ static int stm32l4pwm_update_duty(FAR struct stm32l4_pwmtimer_s *priv, /* Get the reload values */ - reload = stm32l4pwm_getreg(priv, STM32L4_GTIM_ARR_OFFSET); + if (priv->timtype == TIMTYPE_LOWPOWER) + { + reload = stm32l4pwm_getreg(priv, STM32L4_LPTIM_ARR_OFFSET); + } + else + { + reload = stm32l4pwm_getreg(priv, STM32L4_GTIM_ARR_OFFSET); + } /* Duty cycle: * @@ -1405,27 +1643,36 @@ static int stm32l4pwm_update_duty(FAR struct stm32l4_pwmtimer_s *priv, pwminfo("ccr: %u\n", ccr); - switch (channel) + if (priv->timtype == TIMTYPE_LOWPOWER) { - case 1: /* Register offset for Channel 1 */ - ccr_offset = STM32L4_GTIM_CCR1_OFFSET; - break; + /* Low-power timers only have a compare register */ - case 2: /* Register offset for Channel 2 */ - ccr_offset = STM32L4_GTIM_CCR2_OFFSET; - break; + ccr_offset = STM32L4_LPTIM_CMP_OFFSET; + } + else + { + switch (channel) + { + case 1: /* Register offset for Channel 1 */ + ccr_offset = STM32L4_GTIM_CCR1_OFFSET; + break; - case 3: /* Register offset for Channel 3 */ - ccr_offset = STM32L4_GTIM_CCR3_OFFSET; - break; + case 2: /* Register offset for Channel 2 */ + ccr_offset = STM32L4_GTIM_CCR2_OFFSET; + break; - case 4: /* Register offset for Channel 4 */ - ccr_offset = STM32L4_GTIM_CCR4_OFFSET; - break; + case 3: /* Register offset for Channel 3 */ + ccr_offset = STM32L4_GTIM_CCR3_OFFSET; + break; - default: - pwmerr("ERROR: No such channel: %u\n", channel); - return -EINVAL; + case 4: /* Register offset for Channel 4 */ + ccr_offset = STM32L4_GTIM_CCR4_OFFSET; + break; + + default: + pwmerr("ERROR: No such channel: %u\n", channel); + return -EINVAL; + } } /* Set the duty cycle by writing to the CCR register for this channel */ @@ -1613,75 +1860,143 @@ static void stm32l4pwm_setapbclock(FAR struct stm32l4_pwmtimer_s *priv, bool on) /* Determine which timer to configure */ - switch (priv->timid) + if (priv->timtype != TIMTYPE_LOWPOWER) { + switch (priv->timid) + { #ifdef CONFIG_STM32L4_TIM1_PWM - case 1: - regaddr = STM32L4_RCC_APB2ENR; - en_bit = RCC_APB2ENR_TIM1EN; - break; + case 1: + regaddr = STM32L4_RCC_APB2ENR; + en_bit = RCC_APB2ENR_TIM1EN; + break; #endif #ifdef CONFIG_STM32L4_TIM2_PWM - case 2: - regaddr = STM32L4_RCC_APB1ENR1; - en_bit = RCC_APB1ENR1_TIM2EN; - break; + case 2: + regaddr = STM32L4_RCC_APB1ENR1; + en_bit = RCC_APB1ENR1_TIM2EN; + break; #endif #ifdef CONFIG_STM32L4_TIM3_PWM - case 3: - regaddr = STM32L4_RCC_APB1ENR1; - en_bit = RCC_APB1ENR_TIM3EN; - break; + case 3: + regaddr = STM32L4_RCC_APB1ENR1; + en_bit = RCC_APB1ENR_TIM3EN; + break; #endif #ifdef CONFIG_STM32L4_TIM4_PWM - case 4: - regaddr = STM32L4_RCC_APB1ENR1; - en_bit = RCC_APB1ENR1_TIM4EN; - break; + case 4: + regaddr = STM32L4_RCC_APB1ENR1; + en_bit = RCC_APB1ENR1_TIM4EN; + break; #endif #ifdef CONFIG_STM32L4_TIM5_PWM - case 5: - regaddr = STM32L4_RCC_APB1ENR1; - en_bit = RCC_APB1ENR_TIM5EN; - break; + case 5: + regaddr = STM32L4_RCC_APB1ENR1; + en_bit = RCC_APB1ENR_TIM5EN; + break; #endif #ifdef CONFIG_STM32L4_TIM8_PWM - case 8: - regaddr = STM32L4_RCC_APB2ENR; - en_bit = RCC_APB2ENR_TIM8EN; - break; + case 8: + regaddr = STM32L4_RCC_APB2ENR; + en_bit = RCC_APB2ENR_TIM8EN; + break; #endif #ifdef CONFIG_STM32L4_TIM15_PWM - case 15: - regaddr = STM32L4_RCC_APB2ENR; - en_bit = RCC_APB2ENR_TIM15EN; - break; + case 15: + regaddr = STM32L4_RCC_APB2ENR; + en_bit = RCC_APB2ENR_TIM15EN; + break; #endif #ifdef CONFIG_STM32L4_TIM16_PWM - case 16: - regaddr = STM32L4_RCC_APB2ENR; - en_bit = RCC_APB2ENR_TIM16EN; - break; + case 16: + regaddr = STM32L4_RCC_APB2ENR; + en_bit = RCC_APB2ENR_TIM16EN; + break; #endif #ifdef CONFIG_STM32L4_TIM17_PWM - case 17: - regaddr = STM32L4_RCC_APB2ENR; - en_bit = RCC_APB2ENR_TIM17EN; - break; + case 17: + regaddr = STM32L4_RCC_APB2ENR; + en_bit = RCC_APB2ENR_TIM17EN; + break; #endif - default: - return; - } + default: + return; + } - /* Enable/disable APB 1/2 clock for timer */ + /* Enable/disable APB 1/2 clock for timer */ - if (on) - { - modifyreg32(regaddr, 0, en_bit); + if (on) + { + modifyreg32(regaddr, 0, en_bit); + } + else + { + modifyreg32(regaddr, en_bit, 0); + } } else { - modifyreg32(regaddr, en_bit, 0); + uint32_t clock_bits; + + switch (priv->timid) + { +#ifdef CONFIG_STM32L4_LPTIM1_PWM + case 1: +#if defined(CONFIG_STM32L4_LPTIM1_CLK_APB1) + /* Enable APB clock for LPTIM1 */ + + if (on) + { + modifyreg32(STM32L4_RCC_APB1ENR1, 0, RCC_APB1ENR1_LPTIM1EN); + } + else + { + modifyreg32(STM32L4_RCC_APB1ENR1, RCC_APB1ENR1_LPTIM1EN, 0); + } + + clock_bits = RCC_CCIPR_LPTIM1SEL_PCLK; +#elif defined(CONFIG_STM32L4_LPTIM1_CLK_LSI) + clock_bits = RCC_CCIPR_LPTIM1SEL_LSI; +#elif defined(CONFIG_STM32L4_LPTIM1_CLK_LSE) + clock_bits = RCC_CCIPR_LPTIM1SEL_LSE; +#elif defined(CONFIG_STM32L4_LPTIM1_CLK_HSI) + clock_bits = RCC_CCIPR_LPTIM1SEL_HSI; +#endif + /* Choose which clock will be used for LPTIM1 */ + + modifyreg32(STM32L4_RCC_CCIPR, RCC_CCIPR_LPTIM1SEL_MASK, clock_bits); + break; +#endif + +#ifdef CONFIG_STM32L4_LPTIM2_PWM + case 2: +#if defined(CONFIG_STM32L4_LPTIM2_CLK_APB1) + /* Enable APB clock for LPTIM2 */ + + if (on) + { + modifyreg32(STM32L4_RCC_APB1ENR2, 0, RCC_APB1ENR2_LPTIM2EN); + } + else + { + modifyreg32(STM32L4_RCC_APB1ENR2, RCC_APB1ENR2_LPTIM2EN, 0); + } + + clock_bits = RCC_CCIPR_LPTIM2SEL_PCLK; +#elif defined(CONFIG_STM32L4_LPTIM2_CLK_LSI) + clock_bits = RCC_CCIPR_LPTIM2SEL_LSI; +#elif defined(CONFIG_STM32L4_LPTIM2_CLK_LSE) + clock_bits = RCC_CCIPR_LPTIM2SEL_LSE; +#elif defined(CONFIG_STM32L4_LPTIM2_CLK_HSI) + clock_bits = RCC_CCIPR_LPTIM2SEL_HSI; +#endif + /* Choose which clock will be used for LPTIM2 */ + + modifyreg32(STM32L4_RCC_CCIPR, RCC_CCIPR_LPTIM2SEL_MASK, clock_bits); + break; +#endif + default: + return; + } } } @@ -1711,7 +2026,15 @@ static int stm32l4pwm_setup(FAR struct pwm_lowerhalf_s *dev) uint32_t pincfg; int i; - pwminfo("TIM%u\n", priv->timid); + if (priv->timtype == TIMTYPE_LOWPOWER) + { + pwminfo("LPTIM%u\n", priv->timid); + } + else + { + pwminfo("TIM%u\n", priv->timid); + } + stm32l4pwm_dumpregs(priv, "Initially"); /* Enable APB1/2 clocking for timer. */ @@ -1730,7 +2053,6 @@ static int stm32l4pwm_setup(FAR struct pwm_lowerhalf_s *dev) stm32l4_configgpio(pincfg); } - /* Enable complementary channel if available */ pincfg = priv->channels[i].npincfg; @@ -1804,7 +2126,6 @@ static int stm32l4pwm_shutdown(FAR struct pwm_lowerhalf_s *dev) stm32l4_configgpio(pincfg); } - } return OK; @@ -1852,7 +2173,14 @@ static int stm32l4pwm_start(FAR struct pwm_lowerhalf_s *dev, /* Start the time */ - return stm32l4pwm_timer(priv, info); + if (priv->timtype == TIMTYPE_LOWPOWER) + { + return stm32l4pwm_lptimer(priv, info); + } + else + { + return stm32l4pwm_timer(priv, info); + } } #else static int stm32l4pwm_start(FAR struct pwm_lowerhalf_s *dev, @@ -1861,7 +2189,6 @@ static int stm32l4pwm_start(FAR struct pwm_lowerhalf_s *dev, int ret = OK; FAR struct stm32l4_pwmtimer_s *priv = (FAR struct stm32l4_pwmtimer_s *)dev; -#ifndef CONFIG_PWM_PULSECOUNT /* if frequency has not changed we just update duty */ if (info->frequency == priv->frequency) @@ -1875,27 +2202,32 @@ static int stm32l4pwm_start(FAR struct pwm_lowerhalf_s *dev, if (info->channels[i].channel != 0) { - ret = stm32l4pwm_update_duty(priv,info->channels[i].channel, + ret = stm32l4pwm_update_duty(priv, info->channels[i].channel, info->channels[i].duty); } } + #else - ret = stm32l4pwm_update_duty(priv,priv->channels[0].channel,info->duty); + ret = stm32l4pwm_update_duty(priv, priv->channels[0].channel, info->duty); #endif } else -#endif { - ret = stm32l4pwm_timer(priv, info); + if (priv->timtype == TIMTYPE_LOWPOWER) + { + ret = stm32l4pwm_lptimer(priv, info); + } + else + { + ret = stm32l4pwm_timer(priv, info); + } -#ifndef CONFIG_PWM_PULSECOUNT /* Save current frequency */ if (ret == OK) { priv->frequency = info->frequency; } -#endif } return ret; @@ -1929,7 +2261,14 @@ static int stm32l4pwm_stop(FAR struct pwm_lowerhalf_s *dev) uint32_t regval; irqstate_t flags; - pwminfo("TIM%u\n", priv->timid); + if (priv->timtype == TIMTYPE_LOWPOWER) + { + pwminfo("LPTIM%u\n", priv->timid); + } + else + { + pwminfo("TIM%u\n", priv->timid); + } /* Disable interrupts momentary to stop any ongoing timer processing and * to prevent any concurrent access to the reset register. @@ -1948,58 +2287,79 @@ static int stm32l4pwm_stop(FAR struct pwm_lowerhalf_s *dev) /* Determine which timer to reset */ - switch (priv->timid) + if (priv->timtype != TIMTYPE_LOWPOWER) { + switch (priv->timid) + { #ifdef CONFIG_STM32L4_TIM1_PWM - case 1: - regaddr = STM32L4_RCC_APB2RSTR; - resetbit = RCC_APB2RSTR_TIM1RST; - break; + case 1: + regaddr = STM32L4_RCC_APB2RSTR; + resetbit = RCC_APB2RSTR_TIM1RST; + break; #endif #ifdef CONFIG_STM32L4_TIM2_PWM - case 2: - regaddr = STM32L4_RCC_APB1RSTR1; - resetbit = RCC_APB1RSTR1_TIM2RST; - break; + case 2: + regaddr = STM32L4_RCC_APB1RSTR1; + resetbit = RCC_APB1RSTR1_TIM2RST; + break; #endif #ifdef CONFIG_STM32L4_TIM3_PWM - case 3: - regaddr = STM32L4_RCC_APB1RSTR1; - resetbit = RCC_APB1RSTR_TIM3RST; - break; + case 3: + regaddr = STM32L4_RCC_APB1RSTR1; + resetbit = RCC_APB1RSTR_TIM3RST; + break; #endif #ifdef CONFIG_STM32L4_TIM4_PWM - case 4: - regaddr = STM32L4_RCC_APB1RSTR1; - resetbit = RCC_APB1RSTR1_TIM4RST; - break; + case 4: + regaddr = STM32L4_RCC_APB1RSTR1; + resetbit = RCC_APB1RSTR1_TIM4RST; + break; #endif #ifdef CONFIG_STM32L4_TIM5_PWM - case 5: - regaddr = STM32L4_RCC_APB1RSTR1; - resetbit = RCC_APB1RSTR_TIM5RST; - break; + case 5: + regaddr = STM32L4_RCC_APB1RSTR1; + resetbit = RCC_APB1RSTR_TIM5RST; + break; #endif #ifdef CONFIG_STM32L4_TIM8_PWM - case 8: - regaddr = STM32L4_RCC_APB2RSTR; - resetbit = RCC_APB2RSTR_TIM8RST; - break; + case 8: + regaddr = STM32L4_RCC_APB2RSTR; + resetbit = RCC_APB2RSTR_TIM8RST; + break; #endif #ifdef CONFIG_STM32L4_TIM16_PWM - case 16: - regaddr = STM32L4_RCC_APB2RSTR; - resetbit = RCC_APB2RSTR_TIM16RST; - break; + case 16: + regaddr = STM32L4_RCC_APB2RSTR; + resetbit = RCC_APB2RSTR_TIM16RST; + break; #endif #ifdef CONFIG_STM32L4_TIM17_PWM - case 17: - regaddr = STM32L4_RCC_APB2RSTR; - resetbit = RCC_APB2RSTR_TIM17RST; - break; + case 17: + regaddr = STM32L4_RCC_APB2RSTR; + resetbit = RCC_APB2RSTR_TIM17RST; + break; #endif - default: - return -EINVAL; + default: + return -EINVAL; + } + } + else + { + switch (priv->timid) + { +#ifdef CONFIG_STM32L4_LPTIM1_PWM + case 1: + regaddr = STM32L4_RCC_APB2RSTR; + resetbit = RCC_APB1RSTR1_LPTIM1RST; + break; +#endif +#ifdef CONFIG_STM32L4_LPTIM2_PWM + case 2: + regaddr = STM32L4_RCC_APB1RSTR2; + resetbit = RCC_APB1RSTR2_LPTIM2RST; + break; +#endif + } } /* Reset the timer - stopping the output and putting the timer back @@ -2153,4 +2513,49 @@ FAR struct pwm_lowerhalf_s *stm32l4_pwminitialize(int timer) return (FAR struct pwm_lowerhalf_s *)lower; } +/**************************************************************************** + * Name: stm32l4_lp_pwminitialize + * + * Description: + * Initialize one low-power timer for use with the upper_level PWM driver. + * + * Input Parameters: + * timer - A number identifying the timer use. The number of valid timer + * IDs varies with the STM32 MCU and MCU family but is somewhere in + * the range of {1,..,2}. + * + * Returned Value: + * On success, a pointer to the STM32 lower half PWM driver is returned. + * NULL is returned on any failure. + * + ****************************************************************************/ + +FAR struct pwm_lowerhalf_s *stm32l4_lp_pwminitialize(int timer) +{ + FAR struct stm32l4_pwmtimer_s *lower; + + pwminfo("LPTIM%u\n", timer); + + switch (timer) + { +#ifdef CONFIG_STM32L4_LPTIM1_PWM + case 1: + lower = &g_pwmlp1dev; + break; +#endif + +#ifdef CONFIG_STM32L4_LPTIM2_PWM + case 2: + lower = &g_pwmlp2dev; + break; +#endif + + default: + pwmerr("ERROR: No such timer configured\n"); + return NULL; + } + + return (FAR struct pwm_lowerhalf_s *)lower; +} + #endif /* CONFIG_STM32L4_TIMn_PWM, n = 1,...,17 */ diff --git a/arch/arm/src/stm32l4/stm32l4_pwm.h b/arch/arm/src/stm32l4/stm32l4_pwm.h index ac065feffe..9508919b77 100644 --- a/arch/arm/src/stm32l4/stm32l4_pwm.h +++ b/arch/arm/src/stm32l4/stm32l4_pwm.h @@ -55,11 +55,14 @@ /************************************************************************************ * Pre-processor Definitions ************************************************************************************/ + /* Configuration ********************************************************************/ + /* Timer devices may be used for different purposes. One special purpose is - * to generate modulated outputs for such things as motor control. If CONFIG_STM32L4_TIMn - * is defined then the CONFIG_STM32L4_TIMn_PWM must also be defined to indicate that - * timer "n" is intended to be used for pulsed output signal generation. + * to generate modulated outputs for such things as motor control. If + * CONFIG_STM32L4_TIMn is defined then the CONFIG_STM32L4_TIMn_PWM must also be + * defined to indicate that timer "n" is intended to be used for pulsed output + * signal generation. */ #ifndef CONFIG_STM32L4_TIM1 @@ -97,14 +100,16 @@ /* Check if PWM support for any channel is enabled. */ -#if defined(CONFIG_STM32L4_TIM1_PWM) || defined(CONFIG_STM32L4_TIM2_PWM) || \ - defined(CONFIG_STM32L4_TIM3_PWM) || defined(CONFIG_STM32L4_TIM4_PWM) || \ - defined(CONFIG_STM32L4_TIM5_PWM) || defined(CONFIG_STM32L4_TIM8_PWM) || \ - defined(CONFIG_STM32L4_TIM15_PWM) || defined(CONFIG_STM32L4_TIM16_PWM) || \ - defined(CONFIG_STM32L4_TIM17_PWM) +#if defined(CONFIG_STM32L4_TIM1_PWM) || defined(CONFIG_STM32L4_TIM2_PWM) || \ + defined(CONFIG_STM32L4_TIM3_PWM) || defined(CONFIG_STM32L4_TIM4_PWM) || \ + defined(CONFIG_STM32L4_TIM5_PWM) || defined(CONFIG_STM32L4_TIM8_PWM) || \ + defined(CONFIG_STM32L4_TIM15_PWM) || defined(CONFIG_STM32L4_TIM16_PWM) || \ + defined(CONFIG_STM32L4_TIM17_PWM) || defined(CONFIG_STM32L4_LPTIM1_PWM) || \ + defined(CONFIG_STM32L4_LPTIM2_PWM) #include #include "hardware/stm32l4_tim.h" +#include "hardware/stm32l4_lptim.h" #ifdef CONFIG_PWM_MULTICHAN @@ -677,6 +682,40 @@ # endif #endif +/* REVISIT: any other LPTIM implementations have more than one channel? */ + +#define CONFIG_STM32L4_LPTIM1_CHANNEL 1 + +#ifdef CONFIG_STM32L4_LPTIM1_PWM +# if !defined(CONFIG_STM32L4_LPTIM1_CHANNEL) +# error "CONFIG_STM32L4_LPTIM1_CHANNEL must be provided" +# elif CONFIG_STM32L4_LPTIM1_CHANNEL == 1 +# define CONFIG_STM32L4_LPTIM1_CHANNEL1 1 +# define CONFIG_STM32L4_LPTIM1_CH1MODE CONFIG_STM32L4_LPTIM1_CHMODE +# define PWM_LPTIM1_CH1CFG GPIO_LPTIM1_CH1OUT +# define PWM_LPTIM1_CH1NCFG 0 +# else +# error "Unsupported value of CONFIG_STM32L4_LPTIM1_CHANNEL" +# endif +#endif + +/* REVISIT: any other LPTIM implementations have more than one channel? */ + +#define CONFIG_STM32L4_LPTIM2_CHANNEL 1 + +#ifdef CONFIG_STM32L4_LPTIM2_PWM +# if !defined(CONFIG_STM32L4_LPTIM2_CHANNEL) +# error "CONFIG_STM32L4_LPTIM2_CHANNEL must be provided" +# elif CONFIG_STM32L4_LPTIM2_CHANNEL == 1 +# define CONFIG_STM32L4_LPTIM2_CHANNEL1 1 +# define CONFIG_STM32L4_LPTIM2_CH1MODE CONFIG_STM32L4_LPTIM2_CHMODE +# define PWM_LPTIM2_CH1CFG GPIO_LPTIM2_CH1OUT +# define PWM_LPTIM2_CH1NCFG 0 +# else +# error "Unsupported value of CONFIG_STM32L4_LPTIM2_CHANNEL" +# endif +#endif + #define PWM_NCHANNELS 1 #endif @@ -723,6 +762,25 @@ extern "C" FAR struct pwm_lowerhalf_s *stm32l4_pwminitialize(int timer); +/**************************************************************************** + * Name: stm32l4_lp_pwminitialize + * + * Description: + * Initialize one low-power timer for use with the upper_level PWM driver. + * + * Input Parameters: + * timer - A number identifying the timer use. The number of valid timer + * IDs varies with the STM32 MCU and MCU family but is somewhere in + * the range of {1,..,2}. + * + * Returned Value: + * On success, a pointer to the STM32 lower half PWM driver is returned. + * NULL is returned on any failure. + * + ****************************************************************************/ + +FAR struct pwm_lowerhalf_s *stm32l4_lp_pwminitialize(int timer); + #undef EXTERN #if defined(__cplusplus) } diff --git a/boards/arm/stm32l4/nucleo-l476rg/include/board.h b/boards/arm/stm32l4/nucleo-l476rg/include/board.h index 124a583b94..6e6cd983c9 100644 --- a/boards/arm/stm32l4/nucleo-l476rg/include/board.h +++ b/boards/arm/stm32l4/nucleo-l476rg/include/board.h @@ -261,6 +261,9 @@ #define GPIO_TIM1_CH2OUT GPIO_TIM1_CH2OUT_1 #define GPIO_TIM1_CH2NOUT GPIO_TIM1_CH2N_1 +#define GPIO_LPTIM1_CH1OUT GPIO_LPTIM1_OUT_1 +#define GPIO_LPTIM2_CH1OUT GPIO_LPTIM2_OUT_2 + /**************************************************************************** * Public Data ****************************************************************************/ diff --git a/boards/arm/stm32l4/nucleo-l476rg/include/nucleo-l476rg.h b/boards/arm/stm32l4/nucleo-l476rg/include/nucleo-l476rg.h index 4a703c628d..235bc63df3 100644 --- a/boards/arm/stm32l4/nucleo-l476rg/include/nucleo-l476rg.h +++ b/boards/arm/stm32l4/nucleo-l476rg/include/nucleo-l476rg.h @@ -202,8 +202,9 @@ * DFSDM */ -/* prescaler common to all PLL inputs; will be 1 (XXX source is implicitly - as per comment above HSI) */ +/* Prescaler common to all PLL inputs; will be 1 (XXX source is implicitly + * as per comment above HSI) . + */ #define STM32L4_PLLCFG_PLLM RCC_PLLCFG_PLLM(1) @@ -271,24 +272,26 @@ /* APB1 clock (PCLK1) is HCLK/1 (80MHz) */ #define STM32L4_RCC_CFGR_PPRE1 RCC_CFGR_PPRE1_HCLK /* PCLK1 = HCLK / 1 */ -#define STM32L4_PCLK1_FREQUENCY (STM32L4_HCLK_FREQUENCY/1) +#define STM32L4_PCLK1_FREQUENCY (STM32L4_HCLK_FREQUENCY / 1) /* Timers driven from APB1 will be twice PCLK1 */ + /* REVISIT : this can be configured */ -#define STM32L4_APB1_TIM2_CLKIN (2*STM32L4_PCLK1_FREQUENCY) -#define STM32L4_APB1_TIM3_CLKIN (2*STM32L4_PCLK1_FREQUENCY) -#define STM32L4_APB1_TIM4_CLKIN (2*STM32L4_PCLK1_FREQUENCY) -#define STM32L4_APB1_TIM5_CLKIN (2*STM32L4_PCLK1_FREQUENCY) -#define STM32L4_APB1_TIM6_CLKIN (2*STM32L4_PCLK1_FREQUENCY) -#define STM32L4_APB1_TIM7_CLKIN (2*STM32L4_PCLK1_FREQUENCY) +#define STM32L4_APB1_TIM2_CLKIN (2 * STM32L4_PCLK1_FREQUENCY) +#define STM32L4_APB1_TIM3_CLKIN (2 * STM32L4_PCLK1_FREQUENCY) +#define STM32L4_APB1_TIM4_CLKIN (2 * STM32L4_PCLK1_FREQUENCY) +#define STM32L4_APB1_TIM5_CLKIN (2 * STM32L4_PCLK1_FREQUENCY) +#define STM32L4_APB1_TIM6_CLKIN (2 * STM32L4_PCLK1_FREQUENCY) +#define STM32L4_APB1_TIM7_CLKIN (2 * STM32L4_PCLK1_FREQUENCY) /* APB2 clock (PCLK2) is HCLK (80MHz) */ #define STM32L4_RCC_CFGR_PPRE2 RCC_CFGR_PPRE2_HCLK /* PCLK2 = HCLK / 1 */ -#define STM32L4_PCLK2_FREQUENCY (STM32L4_HCLK_FREQUENCY/1) +#define STM32L4_PCLK2_FREQUENCY (STM32L4_HCLK_FREQUENCY / 1) /* Timers driven from APB2 will be twice PCLK2 */ + /* REVISIT : this can be configured */ #define STM32L4_APB2_TIM1_CLKIN (2*STM32L4_PCLK2_FREQUENCY) @@ -301,6 +304,7 @@ * otherwise frequency is 2xAPBx. * Note: TIM1,8,15,16,17 are on APB2, others on APB1 */ + /* REVISIT : this can be configured */ /* TODO SDMMC */ @@ -488,8 +492,8 @@ #define BOARD_TIM15_FREQUENCY STM32L4_HCLK_FREQUENCY #define BOARD_TIM16_FREQUENCY STM32L4_HCLK_FREQUENCY #define BOARD_TIM17_FREQUENCY STM32L4_HCLK_FREQUENCY -#define BOARD_LPTIM1_FREQUENCY (STM32L4_HCLK_FREQUENCY / 2) -#define BOARD_LPTIM2_FREQUENCY (STM32L4_HCLK_FREQUENCY / 2) +#define STM32L4_LPTIM1_FREQUENCY (STM32L4_HCLK_FREQUENCY / 2) +#define STM32L4_LPTIM2_FREQUENCY (STM32L4_HCLK_FREQUENCY / 2) /**************************************************************************** * Public Data diff --git a/boards/arm/stm32l4/nucleo-l476rg/src/stm32_pwm.c b/boards/arm/stm32l4/nucleo-l476rg/src/stm32_pwm.c index e4144fd97b..f948bcbeb4 100644 --- a/boards/arm/stm32l4/nucleo-l476rg/src/stm32_pwm.c +++ b/boards/arm/stm32l4/nucleo-l476rg/src/stm32_pwm.c @@ -42,6 +42,7 @@ #include +#include #include #include @@ -262,6 +263,43 @@ int stm32l4_pwm_setup(void) return ret; } #endif + +#if defined(CONFIG_STM32L4_LPTIM1_PWM) + pwm = stm32l4_lp_pwminitialize(1); + if (!pwm) + { + aerr("ERROR: Failed to get the STM32L4 PWM lower half\n"); + return -ENODEV; + } + + /* Register the PWM driver at "/dev/lppwm1" */ + + ret = pwm_register("/dev/lppwm1", pwm); + if (ret < 0) + { + aerr("ERROR: pwm_register failed: %d\n", ret); + return ret; + } +#endif + +#if defined(CONFIG_STM32L4_LPTIM2_PWM) + pwm = stm32l4_lp_pwminitialize(2); + if (!pwm) + { + aerr("ERROR: Failed to get the STM32L4 PWM lower half\n"); + return -ENODEV; + } + + /* Register the PWM driver at "/dev/lppwm2" */ + + ret = pwm_register("/dev/lppwm2", pwm); + if (ret < 0) + { + aerr("ERROR: pwm_register failed: %d\n", ret); + return ret; + } +#endif + /* Now we are initialized */ initialized = true;