From af236d478445de457534a84b7de345359199eb53 Mon Sep 17 00:00:00 2001 From: ziggurat29 Date: Fri, 8 Jul 2016 17:30:55 -0500 Subject: [PATCH] STM32L4: add support for tickless OS, and incidentally timers, pwm, oneshot, free-running.... --- arch/arm/Kconfig | 1 + arch/arm/src/stm32l4/Kconfig | 1845 +++++++++++++++- arch/arm/src/stm32l4/Make.defs | 19 + arch/arm/src/stm32l4/stm32l4_freerun.c | 297 +++ arch/arm/src/stm32l4/stm32l4_freerun.h | 159 ++ arch/arm/src/stm32l4/stm32l4_oneshot.c | 416 ++++ arch/arm/src/stm32l4/stm32l4_oneshot.h | 190 ++ arch/arm/src/stm32l4/stm32l4_pwm.c | 2052 ++++++++++++++++++ arch/arm/src/stm32l4/stm32l4_pwm.h | 679 ++++++ arch/arm/src/stm32l4/stm32l4_tickless.c | 351 +++ arch/arm/src/stm32l4/stm32l4_tim.c | 1463 +++++++++++++ arch/arm/src/stm32l4/stm32l4_tim.h | 223 ++ arch/arm/src/stm32l4/stm32l4_tim_lowerhalf.c | 668 ++++++ 13 files changed, 8360 insertions(+), 3 deletions(-) create mode 100644 arch/arm/src/stm32l4/stm32l4_freerun.c create mode 100644 arch/arm/src/stm32l4/stm32l4_freerun.h create mode 100644 arch/arm/src/stm32l4/stm32l4_oneshot.c create mode 100644 arch/arm/src/stm32l4/stm32l4_oneshot.h create mode 100644 arch/arm/src/stm32l4/stm32l4_pwm.c create mode 100644 arch/arm/src/stm32l4/stm32l4_pwm.h create mode 100644 arch/arm/src/stm32l4/stm32l4_tickless.c create mode 100644 arch/arm/src/stm32l4/stm32l4_tim.c create mode 100644 arch/arm/src/stm32l4/stm32l4_tim_lowerhalf.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 12af0d6056..8728230276 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -251,6 +251,7 @@ config ARCH_CHIP_STM32L4 select ARM_HAVE_MPU_UNIFIED select ARCH_HAVE_I2CRESET select ARCH_HAVE_HEAPCHECK + select ARCH_HAVE_TICKLESS select ARMV7M_HAVE_STACKCHECK ---help--- STMicro STM32 architectures (ARM Cortex-M4). diff --git a/arch/arm/src/stm32l4/Kconfig b/arch/arm/src/stm32l4/Kconfig index 143230fa5b..66be03660c 100644 --- a/arch/arm/src/stm32l4/Kconfig +++ b/arch/arm/src/stm32l4/Kconfig @@ -69,7 +69,7 @@ config STM32L4_FLASH_1024KB endchoice # Embedded FLASH size -comment "SRAM2 Options" +comment "STM32L4 SRAM2 Options" config STM32L4_SRAM2_HEAP bool "SRAM2 is used for heap" @@ -97,6 +97,8 @@ config STM32L4_SRAM2_INIT case, the board should handle the initialization itself at the appropriate time. +comment "STM32L4 Peripherals" + menu "STM32L4 Peripheral Support" # These "hidden" settings determine is a peripheral option is available for the @@ -630,6 +632,1843 @@ config STM32L4_SAI2PLL config STM32L4_USART bool +menu "Timer Configuration" + +if SCHED_TICKLESS + +config STM32L4_ONESHOT + bool + default y + +config STM32L4_FREERUN + bool + default y + +endif # SCHED_TICKLESS + +if !SCHED_TICKLESS + +config STM32L4_ONESHOT + bool "TIM one-shot wrapper" + default n + ---help--- + Enable a wrapper around the low level timer/counter functions to + support one-shot timer. + +config STM32L4_FREERUN + bool "TIM free-running wrapper" + default n + ---help--- + Enable a wrapper around the low level timer/counter functions to + support a free-running timer. + +endif # !SCHED_TICKLESS + +config STM32L4_TICKLESS_ONESHOT + int "Tickless one-shot timer channel" + default 2 + range 1 8 + depends on STM32L4_ONESHOT + ---help--- + If the Tickless OS feature is enabled, then one clock must be + assigned to provide the one-shot timer needed by the OS. + +config STM32L4_TICKLESS_FREERUN + int "Tickless free-running timer channel" + default 5 + range 1 8 + depends on STM32L4_FREERUN + ---help--- + If the Tickless OS feature is enabled, then one clock must be + assigned to provide the free-running timer needed by the OS. + +config STM32L4_TIM1_PWM + bool "TIM1 PWM" + default n + depends on STM32L4_TIM1 + select ARCH_HAVE_PWM_PULSECOUNT + ---help--- + Reserve 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_TIM1 + 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_TIM1_PWM + +config STM32L4_TIM1_MODE + int "TIM1 Mode" + default 0 + range 0 4 + ---help--- + Specifies the timer mode. + +if STM32L4_PWM_MULTICHAN + +config STM32L4_TIM1_CHANNEL1 + bool "TIM1 Channel 1" + default n + ---help--- + Enables channel 1. + +if STM32L4_TIM1_CHANNEL1 + +config STM32L4_TIM1_CH1MODE + int "TIM1 Channel 1 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM1_CH1OUT + bool "TIM1 Channel 1 Output" + default n + ---help--- + Enables channel 1 output. + +endif # STM32L4_TIM1_CHANNEL1 + +config STM32L4_TIM1_CHANNEL2 + bool "TIM1 Channel 2" + default n + ---help--- + Enables channel 2. + +if STM32L4_TIM1_CHANNEL2 + +config STM32L4_TIM1_CH2MODE + int "TIM1 Channel 2 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM1_CH2OUT + bool "TIM1 Channel 2 Output" + default n + ---help--- + Enables channel 2 output. + +endif # STM32L4_TIM1_CHANNEL2 + +config STM32L4_TIM1_CHANNEL3 + bool "TIM1 Channel 3" + default n + ---help--- + Enables channel 3. + +if STM32L4_TIM1_CHANNEL3 + +config STM32L4_TIM1_CH3MODE + int "TIM1 Channel 3 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM1_CH3OUT + bool "TIM1 Channel 3 Output" + default n + ---help--- + Enables channel 3 output. + +endif # STM32L4_TIM1_CHANNEL3 + +config STM32L4_TIM1_CHANNEL4 + bool "TIM1 Channel 4" + default n + ---help--- + Enables channel 4. + +if STM32L4_TIM1_CHANNEL4 + +config STM32L4_TIM1_CH4MODE + int "TIM1 Channel 4 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM1_CH4OUT + bool "TIM1 Channel 4 Output" + default n + ---help--- + Enables channel 4 output. + +endif # STM32L4_TIM1_CHANNEL4 + +endif # STM32L4_PWM_MULTICHAN + +if !STM32L4_PWM_MULTICHAN + +config STM32L4_TIM1_CHANNEL + int "TIM1 PWM Output Channel" + default 1 + range 1 4 + ---help--- + If TIM1 is enabled for PWM usage, you also need specifies the timer output + channel {1,..,4} + +config STM32L4_TIM1_CHMODE + int "TIM1 Channel Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +endif # !STM32L4_PWM_MULTICHAN + +endif # STM32L4_TIM1_PWM + +config STM32L4_TIM2_PWM + bool "TIM2 PWM" + default n + depends on STM32L4_TIM2 + select ARCH_HAVE_PWM_PULSECOUNT + ---help--- + Reserve 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_TIM2 + 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_TIM2_PWM + +config STM32L4_TIM2_MODE + int "TIM2 Mode" + default 0 + range 0 4 + ---help--- + Specifies the timer mode. + +if STM32L4_PWM_MULTICHAN + +config STM32L4_TIM2_CHANNEL1 + bool "TIM2 Channel 1" + default n + ---help--- + Enables channel 1. + +if STM32L4_TIM2_CHANNEL1 + +config STM32L4_TIM2_CH1MODE + int "TIM2 Channel 1 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM2_CH1OUT + bool "TIM2 Channel 1 Output" + default n + ---help--- + Enables channel 1 output. + +endif # STM32L4_TIM2_CHANNEL1 + +config STM32L4_TIM2_CHANNEL2 + bool "TIM2 Channel 2" + default n + ---help--- + Enables channel 2. + +if STM32L4_TIM2_CHANNEL2 + +config STM32L4_TIM2_CH2MODE + int "TIM2 Channel 2 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM2_CH2OUT + bool "TIM2 Channel 2 Output" + default n + ---help--- + Enables channel 2 output. + +endif # STM32L4_TIM2_CHANNEL2 + +config STM32L4_TIM2_CHANNEL3 + bool "TIM2 Channel 3" + default n + ---help--- + Enables channel 3. + +if STM32L4_TIM2_CHANNEL3 + +config STM32L4_TIM2_CH3MODE + int "TIM2 Channel 3 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM2_CH3OUT + bool "TIM2 Channel 3 Output" + default n + ---help--- + Enables channel 3 output. + +endif # STM32L4_TIM2_CHANNEL3 + +config STM32L4_TIM2_CHANNEL4 + bool "TIM2 Channel 4" + default n + ---help--- + Enables channel 4. + +if STM32L4_TIM2_CHANNEL4 + +config STM32L4_TIM2_CH4MODE + int "TIM2 Channel 4 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM2_CH4OUT + bool "TIM2 Channel 4 Output" + default n + ---help--- + Enables channel 4 output. + +endif # STM32L4_TIM2_CHANNEL4 + +endif # STM32L4_PWM_MULTICHAN + +if !STM32L4_PWM_MULTICHAN + +config STM32L4_TIM2_CHANNEL + int "TIM2 PWM Output Channel" + default 1 + range 1 4 + ---help--- + If TIM2 is enabled for PWM usage, you also need specifies the timer output + channel {1,..,4} + +config STM32L4_TIM2_CHMODE + int "TIM2 Channel Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +endif # !STM32L4_PWM_MULTICHAN + +endif # STM32L4_TIM2_PWM + +config STM32L4_TIM3_PWM + bool "TIM3 PWM" + default n + depends on STM32L4_TIM3 + select ARCH_HAVE_PWM_PULSECOUNT + ---help--- + Reserve timer 3 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_TIM3 + 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_TIM3_PWM + +config STM32L4_TIM3_MODE + int "TIM3 Mode" + default 0 + range 0 4 + ---help--- + Specifies the timer mode. + +if STM32L4_PWM_MULTICHAN + +config STM32L4_TIM3_CHANNEL1 + bool "TIM3 Channel 1" + default n + ---help--- + Enables channel 1. + +if STM32L4_TIM3_CHANNEL1 + +config STM32L4_TIM3_CH1MODE + int "TIM3 Channel 1 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM3_CH1OUT + bool "TIM3 Channel 1 Output" + default n + ---help--- + Enables channel 1 output. + +endif # STM32L4_TIM3_CHANNEL1 + +config STM32L4_TIM3_CHANNEL2 + bool "TIM3 Channel 2" + default n + ---help--- + Enables channel 2. + +if STM32L4_TIM3_CHANNEL2 + +config STM32L4_TIM3_CH2MODE + int "TIM3 Channel 2 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM3_CH2OUT + bool "TIM3 Channel 2 Output" + default n + ---help--- + Enables channel 2 output. + +endif # STM32L4_TIM3_CHANNEL2 + +config STM32L4_TIM3_CHANNEL3 + bool "TIM3 Channel 3" + default n + ---help--- + Enables channel 3. + +if STM32L4_TIM3_CHANNEL3 + +config STM32L4_TIM3_CH3MODE + int "TIM3 Channel 3 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM3_CH3OUT + bool "TIM3 Channel 3 Output" + default n + ---help--- + Enables channel 3 output. + +endif # STM32L4_TIM3_CHANNEL3 + +config STM32L4_TIM3_CHANNEL4 + bool "TIM3 Channel 4" + default n + ---help--- + Enables channel 4. + +if STM32L4_TIM3_CHANNEL4 + +config STM32L4_TIM3_CH4MODE + int "TIM3 Channel 4 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM3_CH4OUT + bool "TIM3 Channel 4 Output" + default n + ---help--- + Enables channel 4 output. + +endif # STM32L4_TIM3_CHANNEL4 + +endif # STM32L4_PWM_MULTICHAN + +if !STM32L4_PWM_MULTICHAN + +config STM32L4_TIM3_CHANNEL + int "TIM3 PWM Output Channel" + default 1 + range 1 4 + ---help--- + If TIM3 is enabled for PWM usage, you also need specifies the timer output + channel {1,..,4} + +config STM32L4_TIM3_CHMODE + int "TIM3 Channel Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +endif # !STM32L4_PWM_MULTICHAN + +endif # STM32L4_TIM3_PWM + +config STM32L4_TIM4_PWM + bool "TIM4 PWM" + default n + depends on STM32L4_TIM4 + select ARCH_HAVE_PWM_PULSECOUNT + ---help--- + Reserve timer 4 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_TIM4 + 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_TIM4_PWM + +config STM32L4_TIM4_MODE + int "TIM4 Mode" + default 0 + range 0 4 + ---help--- + Specifies the timer mode. + +if STM32L4_PWM_MULTICHAN + +config STM32L4_TIM4_CHANNEL1 + bool "TIM4 Channel 1" + default n + ---help--- + Enables channel 1. + +if STM32L4_TIM4_CHANNEL1 + +config STM32L4_TIM4_CH1MODE + int "TIM4 Channel 1 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM4_CH1OUT + bool "TIM4 Channel 1 Output" + default n + ---help--- + Enables channel 1 output. + +endif # STM32L4_TIM4_CHANNEL1 + +config STM32L4_TIM4_CHANNEL2 + bool "TIM4 Channel 2" + default n + ---help--- + Enables channel 2. + +if STM32L4_TIM4_CHANNEL2 + +config STM32L4_TIM4_CH2MODE + int "TIM4 Channel 2 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM4_CH2OUT + bool "TIM4 Channel 2 Output" + default n + ---help--- + Enables channel 2 output. + +endif # STM32L4_TIM4_CHANNEL2 + +config STM32L4_TIM4_CHANNEL3 + bool "TIM4 Channel 3" + default n + ---help--- + Enables channel 3. + +if STM32L4_TIM4_CHANNEL3 + +config STM32L4_TIM4_CH3MODE + int "TIM4 Channel 3 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM4_CH3OUT + bool "TIM4 Channel 3 Output" + default n + ---help--- + Enables channel 3 output. + +endif # STM32L4_TIM4_CHANNEL3 + +config STM32L4_TIM4_CHANNEL4 + bool "TIM4 Channel 4" + default n + ---help--- + Enables channel 4. + +if STM32L4_TIM4_CHANNEL4 + +config STM32L4_TIM4_CH4MODE + int "TIM4 Channel 4 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM4_CH4OUT + bool "TIM4 Channel 4 Output" + default n + ---help--- + Enables channel 4 output. + +endif # STM32L4_TIM4_CHANNEL4 + +endif # STM32L4_PWM_MULTICHAN + +if !STM32L4_PWM_MULTICHAN + +config STM32L4_TIM4_CHANNEL + int "TIM4 PWM Output Channel" + default 1 + range 1 4 + ---help--- + If TIM4 is enabled for PWM usage, you also need specifies the timer output + channel {1,..,4} + +config STM32L4_TIM4_CHMODE + int "TIM4 Channel Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +endif # !STM32L4_PWM_MULTICHAN + +endif # STM32L4_TIM4_PWM + +config STM32L4_TIM5_PWM + bool "TIM5 PWM" + default n + depends on STM32L4_TIM5 + select ARCH_HAVE_PWM_PULSECOUNT + ---help--- + Reserve timer 5 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_TIM5 + 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_TIM5_PWM + +config STM32L4_TIM5_MODE + int "TIM5 Mode" + default 0 + range 0 4 + ---help--- + Specifies the timer mode. + +if STM32L4_PWM_MULTICHAN + +config STM32L4_TIM5_CHANNEL1 + bool "TIM5 Channel 1" + default n + ---help--- + Enables channel 1. + +if STM32L4_TIM5_CHANNEL1 + +config STM32L4_TIM5_CH1MODE + int "TIM5 Channel 1 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM5_CH1OUT + bool "TIM5 Channel 1 Output" + default n + ---help--- + Enables channel 1 output. + +endif # STM32L4_TIM5_CHANNEL1 + +config STM32L4_TIM5_CHANNEL2 + bool "TIM5 Channel 2" + default n + ---help--- + Enables channel 2. + +if STM32L4_TIM5_CHANNEL2 + +config STM32L4_TIM5_CH2MODE + int "TIM5 Channel 2 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM5_CH2OUT + bool "TIM5 Channel 2 Output" + default n + ---help--- + Enables channel 2 output. + +endif # STM32L4_TIM5_CHANNEL2 + +config STM32L4_TIM5_CHANNEL3 + bool "TIM5 Channel 3" + default n + ---help--- + Enables channel 3. + +if STM32L4_TIM5_CHANNEL3 + +config STM32L4_TIM5_CH3MODE + int "TIM5 Channel 3 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM5_CH3OUT + bool "TIM5 Channel 3 Output" + default n + ---help--- + Enables channel 3 output. + +endif # STM32L4_TIM5_CHANNEL3 + +config STM32L4_TIM5_CHANNEL4 + bool "TIM5 Channel 4" + default n + ---help--- + Enables channel 4. + +if STM32L4_TIM5_CHANNEL4 + +config STM32L4_TIM5_CH4MODE + int "TIM5 Channel 4 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM5_CH4OUT + bool "TIM5 Channel 4 Output" + default n + ---help--- + Enables channel 4 output. + +endif # STM32L4_TIM5_CHANNEL4 + +endif # STM32L4_PWM_MULTICHAN + +if !STM32L4_PWM_MULTICHAN + +config STM32L4_TIM5_CHANNEL + int "TIM5 PWM Output Channel" + default 1 + range 1 4 + ---help--- + If TIM5 is enabled for PWM usage, you also need specifies the timer output + channel {1,..,4} + +config STM32L4_TIM5_CHMODE + int "TIM5 Channel Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +endif # !STM32L4_PWM_MULTICHAN + +endif # STM32L4_TIM5_PWM + +config STM32L4_TIM8_PWM + bool "TIM8 PWM" + default n + depends on STM32L4_TIM8 + select ARCH_HAVE_PWM_PULSECOUNT + ---help--- + Reserve timer 8 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_TIM8 + 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_TIM8_PWM + +config STM32L4_TIM8_MODE + int "TIM8 Mode" + default 0 + range 0 4 + ---help--- + Specifies the timer mode. + +if STM32L4_PWM_MULTICHAN + +config STM32L4_TIM8_CHANNEL1 + bool "TIM8 Channel 1" + default n + ---help--- + Enables channel 1. + +if STM32L4_TIM8_CHANNEL1 + +config STM32L4_TIM8_CH1MODE + int "TIM8 Channel 1 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM8_CH1OUT + bool "TIM8 Channel 1 Output" + default n + ---help--- + Enables channel 1 output. + +endif # STM32L4_TIM8_CHANNEL1 + +config STM32L4_TIM8_CHANNEL2 + bool "TIM8 Channel 2" + default n + ---help--- + Enables channel 2. + +if STM32L4_TIM8_CHANNEL2 + +config STM32L4_TIM8_CH2MODE + int "TIM8 Channel 2 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM8_CH2OUT + bool "TIM8 Channel 2 Output" + default n + ---help--- + Enables channel 2 output. + +endif # STM32L4_TIM8_CHANNEL2 + +config STM32L4_TIM8_CHANNEL3 + bool "TIM8 Channel 3" + default n + ---help--- + Enables channel 3. + +if STM32L4_TIM8_CHANNEL3 + +config STM32L4_TIM8_CH3MODE + int "TIM8 Channel 3 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM8_CH3OUT + bool "TIM8 Channel 3 Output" + default n + ---help--- + Enables channel 3 output. + +endif # STM32L4_TIM8_CHANNEL3 + +config STM32L4_TIM8_CHANNEL4 + bool "TIM8 Channel 4" + default n + ---help--- + Enables channel 4. + +if STM32L4_TIM8_CHANNEL4 + +config STM32L4_TIM8_CH4MODE + int "TIM8 Channel 4 Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM8_CH4OUT + bool "TIM8 Channel 4 Output" + default n + ---help--- + Enables channel 4 output. + +endif # STM32L4_TIM8_CHANNEL4 + +endif # STM32L4_PWM_MULTICHAN + +if !STM32L4_PWM_MULTICHAN + +config STM32L4_TIM8_CHANNEL + int "TIM8 PWM Output Channel" + default 1 + range 1 4 + ---help--- + If TIM8 is enabled for PWM usage, you also need specifies the timer output + channel {1,..,4} + +config STM32L4_TIM8_CHMODE + int "TIM8 Channel Mode" + default 0 + range 0 5 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +endif # !STM32L4_PWM_MULTICHAN + +endif # STM32L4_TIM8_PWM + +config STM32L4_TIM15_PWM + bool "TIM15 PWM" + default n + depends on STM32L4_TIM15 + ---help--- + Reserve timer 15 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_TIM15 + 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_TIM15_PWM + +if STM32L4_PWM_MULTICHAN + +config STM32L4_TIM15_CHANNEL1 + bool "TIM15 Channel 1" + default n + ---help--- + Enables channel 1. + +if STM32L4_TIM15_CHANNEL1 + +config STM32L4_TIM15_CH1MODE + int "TIM15 Channel 1 Mode" + default 0 + range 0 3 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM15_CH1OUT + bool "TIM15 Channel 1 Output" + default n + ---help--- + Enables channel 1 output. + +endif # STM32L4_TIM15_CHANNEL1 + +config STM32L4_TIM15_CHANNEL2 + bool "TIM15 Channel 2" + default n + ---help--- + Enables channel 2. + +if STM32L4_TIM15_CHANNEL2 + +config STM32L4_TIM15_CH2MODE + int "TIM15 Channel 2 Mode" + default 0 + range 0 3 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +config STM32L4_TIM15_CH2OUT + bool "TIM15 Channel 2 Output" + default n + ---help--- + Enables channel 2 output. + +endif # STM32L4_TIM15_CHANNEL2 + +endif # STM32L4_PWM_MULTICHAN + +if !STM32L4_PWM_MULTICHAN + +config STM32L4_TIM15_CHANNEL + int "TIM15 PWM Output Channel" + default 1 + range 1 2 + ---help--- + If TIM15 is enabled for PWM usage, you also need specifies the timer output + channel {1,2} + +config STM32L4_TIM15_CHMODE + int "TIM15 Channel Mode" + default 0 + range 0 3 if STM32_STM32F30XX + range 0 1 if !STM32_STM32F30XX + ---help--- + Specifies the channel mode. + +endif # !STM32L4_PWM_MULTICHAN + +endif # STM32L4_TIM15_PWM + +config STM32L4_TIM16_PWM + bool "TIM16 PWM" + default n + depends on STM32L4_TIM16 + ---help--- + Reserve timer 16 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_TIM16 + 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_TIM16_PWM + +if STM32L4_PWM_MULTICHAN + +config STM32L4_TIM16_CHANNEL1 + bool "TIM16 Channel 1" + default n + ---help--- + Enables channel 1. + +if STM32L4_TIM16_CHANNEL1 + +config STM32L4_TIM16_CH1MODE + int "TIM16 Channel 1 Mode" + default 0 + range 0 1 + ---help--- + Specifies the channel mode. + +config STM32L4_TIM16_CH1OUT + bool "TIM16 Channel 1 Output" + default n + ---help--- + Enables channel 1 output. + +endif # STM32L4_TIM16_CHANNEL1 + +endif # STM32L4_PWM_MULTICHAN + +if !STM32L4_PWM_MULTICHAN + +config STM32L4_TIM16_CHANNEL + int "TIM16 PWM Output Channel" + default 1 + range 1 1 + ---help--- + If TIM16 is enabled for PWM usage, you also need specifies the timer output + channel {1} + +config STM32L4_TIM16_CHMODE + int "TIM16 Channel Mode" + default 0 + range 0 1 + ---help--- + Specifies the channel mode. + +endif # !STM32L4_PWM_MULTICHAN + +endif # STM32L4_TIM16_PWM + +config STM32L4_TIM17_PWM + bool "TIM17 PWM" + default n + depends on STM32L4_TIM17 + ---help--- + Reserve timer 17 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_TIM17 + 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_TIM17_PWM + +if STM32L4_PWM_MULTICHAN + +config STM32L4_TIM17_CHANNEL1 + bool "TIM17 Channel 1" + default n + ---help--- + Enables channel 1. + +if STM32L4_TIM17_CHANNEL1 + +config STM32L4_TIM17_CH1MODE + int "TIM17 Channel 1 Mode" + default 0 + range 0 1 + ---help--- + Specifies the channel mode. + +config STM32L4_TIM17_CH1OUT + bool "TIM17 Channel 1 Output" + default n + ---help--- + Enables channel 1 output. + +endif # STM32L4_TIM17_CHANNEL1 + +endif # STM32L4_PWM_MULTICHAN + +if !STM32L4_PWM_MULTICHAN + +config STM32L4_TIM17_CHANNEL + int "TIM17 PWM Output Channel" + default 1 + range 1 1 + ---help--- + If TIM17 is enabled for PWM usage, you also need specifies the timer output + channel {1} + +config STM32L4_TIM17_CHMODE + int "TIM17 Channel Mode" + default 0 + range 0 1 + ---help--- + Specifies the channel mode. + +endif # !STM32L4_PWM_MULTICHAN + +endif # STM32L4_TIM17_PWM + +config STM32L4_PWM_MULTICHAN + bool "PWM Multiple Output Channels" + default n + depends on STM32L4_TIM1_PWM || STM32L4_TIM2_PWM || STM32L4_TIM3_PWM || STM32L4_TIM4_PWM || STM32L4_TIM5_PWM || STM32L4_TIM8_PWM || STM32L4_TIM15_PWM || STM32L4_TIM16_PWM || STM32L4_TIM17_PWM + select ARCH_HAVE_PWM_MULTICHAN + ---help--- + Specifies that the PWM driver supports multiple output + channels per timer. + +config STM32L4_TIM1_ADC + bool "TIM1 ADC" + default n + depends on STM32L4_TIM1 && STM32L4_ADC + ---help--- + Reserve timer 1 for use by ADC + + Timer devices may be used for different purposes. If STM32L4_TIM1 is + defined then the following may also be defined to indicate that the + timer is intended to be used for ADC conversion. Note that ADC usage + requires two definition: Not only do you have to assign the timer + for used by the ADC, but then you also have to configure which ADC + channel it is assigned to. + +choice + prompt "Select TIM1 ADC channel" + default STM32L4_TIM1_ADC1 + depends on STM32L4_TIM1_ADC + +config STM32L4_TIM1_ADC1 + bool "TIM1 ADC channel 1" + depends on STM32L4_ADC1 + select HAVE_ADC1_TIMER + ---help--- + Reserve TIM1 to trigger ADC1 + +config STM32L4_TIM1_ADC2 + bool "TIM1 ADC channel 2" + depends on STM32L4_ADC2 + select HAVE_ADC2_TIMER + ---help--- + Reserve TIM1 to trigger ADC2 + +config STM32L4_TIM1_ADC3 + bool "TIM1 ADC channel 3" + depends on STM32L4_ADC3 + select HAVE_ADC3_TIMER + ---help--- + Reserve TIM1 to trigger ADC3 + +endchoice + +config STM32L4_TIM2_ADC + bool "TIM2 ADC" + default n + depends on STM32L4_TIM2 && STM32L4_ADC + ---help--- + Reserve timer 1 for use by ADC + + Timer devices may be used for different purposes. If STM32L4_TIM2 is + defined then the following may also be defined to indicate that the + timer is intended to be used for ADC conversion. Note that ADC usage + requires two definition: Not only do you have to assign the timer + for used by the ADC, but then you also have to configure which ADC + channel it is assigned to. + +choice + prompt "Select TIM2 ADC channel" + default STM32L4_TIM2_ADC1 + depends on STM32L4_TIM2_ADC + +config STM32L4_TIM2_ADC1 + bool "TIM2 ADC channel 1" + depends on STM32L4_ADC1 + select HAVE_ADC1_TIMER + ---help--- + Reserve TIM2 to trigger ADC1 + +config STM32L4_TIM2_ADC2 + bool "TIM2 ADC channel 2" + depends on STM32L4_ADC2 + select HAVE_ADC2_TIMER + ---help--- + Reserve TIM2 to trigger ADC2 + +config STM32L4_TIM2_ADC3 + bool "TIM2 ADC channel 3" + depends on STM32L4_ADC3 + select HAVE_ADC3_TIMER + ---help--- + Reserve TIM2 to trigger ADC3 + +endchoice + +config STM32L4_TIM3_ADC + bool "TIM3 ADC" + default n + depends on STM32L4_TIM3 && STM32L4_ADC + ---help--- + Reserve timer 1 for use by ADC + + Timer devices may be used for different purposes. If STM32L4_TIM3 is + defined then the following may also be defined to indicate that the + timer is intended to be used for ADC conversion. Note that ADC usage + requires two definition: Not only do you have to assign the timer + for used by the ADC, but then you also have to configure which ADC + channel it is assigned to. + +choice + prompt "Select TIM3 ADC channel" + default STM32L4_TIM3_ADC1 + depends on STM32L4_TIM3_ADC + +config STM32L4_TIM3_ADC1 + bool "TIM3 ADC channel 1" + depends on STM32L4_ADC1 + select HAVE_ADC1_TIMER + ---help--- + Reserve TIM3 to trigger ADC1 + +config STM32L4_TIM3_ADC2 + bool "TIM3 ADC channel 2" + depends on STM32L4_ADC2 + select HAVE_ADC2_TIMER + ---help--- + Reserve TIM3 to trigger ADC2 + +config STM32L4_TIM3_ADC3 + bool "TIM3 ADC channel 3" + depends on STM32L4_ADC3 + select HAVE_ADC3_TIMER + ---help--- + Reserve TIM3 to trigger ADC3 + +endchoice + +config STM32L4_TIM4_ADC + bool "TIM4 ADC" + default n + depends on STM32L4_TIM4 && STM32L4_ADC + ---help--- + Reserve timer 1 for use by ADC + + Timer devices may be used for different purposes. If STM32L4_TIM4 is + defined then the following may also be defined to indicate that the + timer is intended to be used for ADC conversion. Note that ADC usage + requires two definition: Not only do you have to assign the timer + for used by the ADC, but then you also have to configure which ADC + channel it is assigned to. + +choice + prompt "Select TIM4 ADC channel" + default STM32L4_TIM4_ADC1 + depends on STM32L4_TIM4_ADC + +config STM32L4_TIM4_ADC1 + bool "TIM4 ADC channel 1" + depends on STM32L4_ADC1 + select HAVE_ADC1_TIMER + ---help--- + Reserve TIM4 to trigger ADC1 + +config STM32L4_TIM4_ADC2 + bool "TIM4 ADC channel 2" + depends on STM32L4_ADC2 + select HAVE_ADC2_TIMER + ---help--- + Reserve TIM4 to trigger ADC2 + +config STM32L4_TIM4_ADC3 + bool "TIM4 ADC channel 3" + depends on STM32L4_ADC3 + select HAVE_ADC3_TIMER + ---help--- + Reserve TIM4 to trigger ADC3 + +endchoice + +config STM32L4_TIM5_ADC + bool "TIM5 ADC" + default n + depends on STM32L4_TIM5 && STM32L4_ADC + ---help--- + Reserve timer 1 for use by ADC + + Timer devices may be used for different purposes. If STM32L4_TIM5 is + defined then the following may also be defined to indicate that the + timer is intended to be used for ADC conversion. Note that ADC usage + requires two definition: Not only do you have to assign the timer + for used by the ADC, but then you also have to configure which ADC + channel it is assigned to. + +choice + prompt "Select TIM5 ADC channel" + default STM32L4_TIM5_ADC1 + depends on STM32L4_TIM5_ADC + +config STM32L4_TIM5_ADC1 + bool "TIM5 ADC channel 1" + depends on STM32L4_ADC1 + select HAVE_ADC1_TIMER + ---help--- + Reserve TIM5 to trigger ADC1 + +config STM32L4_TIM5_ADC2 + bool "TIM5 ADC channel 2" + depends on STM32L4_ADC2 + select HAVE_ADC2_TIMER + ---help--- + Reserve TIM5 to trigger ADC2 + +config STM32L4_TIM5_ADC3 + bool "TIM5 ADC channel 3" + depends on STM32L4_ADC3 + select HAVE_ADC3_TIMER + ---help--- + Reserve TIM5 to trigger ADC3 + +endchoice + +config STM32L4_TIM8_ADC + bool "TIM8 ADC" + default n + depends on STM32L4_TIM8 && STM32L4_ADC + ---help--- + Reserve timer 1 for use by ADC + + Timer devices may be used for different purposes. If STM32L4_TIM8 is + defined then the following may also be defined to indicate that the + timer is intended to be used for ADC conversion. Note that ADC usage + requires two definition: Not only do you have to assign the timer + for used by the ADC, but then you also have to configure which ADC + channel it is assigned to. + +choice + prompt "Select TIM8 ADC channel" + default STM32L4_TIM8_ADC1 + depends on STM32L4_TIM8_ADC + +config STM32L4_TIM8_ADC1 + bool "TIM8 ADC channel 1" + depends on STM32L4_ADC1 + select HAVE_ADC1_TIMER + ---help--- + Reserve TIM8 to trigger ADC1 + +config STM32L4_TIM8_ADC2 + bool "TIM8 ADC channel 2" + depends on STM32L4_ADC2 + select HAVE_ADC2_TIMER + ---help--- + Reserve TIM8 to trigger ADC2 + +config STM32L4_TIM8_ADC3 + bool "TIM8 ADC channel 3" + depends on STM32L4_ADC3 + select HAVE_ADC3_TIMER + ---help--- + Reserve TIM8 to trigger ADC3 + +endchoice + +config HAVE_ADC1_TIMER + bool + +config HAVE_ADC2_TIMER + bool + +config HAVE_ADC3_TIMER + bool + +config STM32L4_ADC1_SAMPLE_FREQUENCY + int "ADC1 Sampling Frequency" + default 100 + depends on HAVE_ADC1_TIMER + ---help--- + ADC1 sampling frequency. Default: 100Hz + +config STM32L4_ADC1_TIMTRIG + int "ADC1 Timer Trigger" + default 0 + range 0 4 + depends on HAVE_ADC1_TIMER + ---help--- + Values 0:CC1 1:CC2 2:CC3 3:CC4 4:TRGO + +config STM32L4_ADC2_SAMPLE_FREQUENCY + int "ADC2 Sampling Frequency" + default 100 + depends on HAVE_ADC2_TIMER + ---help--- + ADC2 sampling frequency. Default: 100Hz + +config STM32L4_ADC2_TIMTRIG + int "ADC2 Timer Trigger" + default 0 + range 0 4 + depends on HAVE_ADC2_TIMER + ---help--- + Values 0:CC1 1:CC2 2:CC3 3:CC4 4:TRGO + +config STM32L4_ADC3_SAMPLE_FREQUENCY + int "ADC3 Sampling Frequency" + default 100 + depends on HAVE_ADC3_TIMER + ---help--- + ADC3 sampling frequency. Default: 100Hz + +config STM32L4_ADC3_TIMTRIG + int "ADC3 Timer Trigger" + default 0 + range 0 4 + depends on HAVE_ADC3_TIMER + ---help--- + Values 0:CC1 1:CC2 2:CC3 3:CC4 4:TRGO + +config STM32L4_TIM1_DAC + bool "TIM1 DAC" + default n + depends on STM32L4_TIM1 && STM32L4_DAC + ---help--- + Reserve timer 1 for use by DAC + + Timer devices may be used for different purposes. If STM32L4_TIM1 is + defined then the following may also be defined to indicate that the + timer is intended to be used for DAC conversion. Note that DAC usage + requires two definition: Not only do you have to assign the timer + for used by the DAC, but then you also have to configure which DAC + channel it is assigned to. + +choice + prompt "Select TIM1 DAC channel" + default STM32L4_TIM1_DAC1 + depends on STM32L4_TIM1_DAC + +config STM32L4_TIM1_DAC1 + bool "TIM1 DAC channel 1" + ---help--- + Reserve TIM1 to trigger DAC1 + +config STM32L4_TIM1_DAC2 + bool "TIM1 DAC channel 2" + ---help--- + Reserve TIM1 to trigger DAC2 + +endchoice + +config STM32L4_TIM2_DAC + bool "TIM2 DAC" + default n + depends on STM32L4_TIM2 && STM32L4_DAC + ---help--- + Reserve timer 2 for use by DAC + + Timer devices may be used for different purposes. If STM32L4_TIM2 is + defined then the following may also be defined to indicate that the + timer is intended to be used for DAC conversion. Note that DAC usage + requires two definition: Not only do you have to assign the timer + for used by the DAC, but then you also have to configure which DAC + channel it is assigned to. + +choice + prompt "Select TIM2 DAC channel" + default STM32L4_TIM2_DAC1 + depends on STM32L4_TIM2_DAC + +config STM32L4_TIM2_DAC1 + bool "TIM2 DAC channel 1" + ---help--- + Reserve TIM2 to trigger DAC1 + +config STM32L4_TIM2_DAC2 + bool "TIM2 DAC channel 2" + ---help--- + Reserve TIM2 to trigger DAC2 + +endchoice + +config STM32L4_TIM3_DAC + bool "TIM3 DAC" + default n + depends on STM32L4_TIM3 && STM32L4_DAC + ---help--- + Reserve timer 3 for use by DAC + + Timer devices may be used for different purposes. If STM32L4_TIM3 is + defined then the following may also be defined to indicate that the + timer is intended to be used for DAC conversion. Note that DAC usage + requires two definition: Not only do you have to assign the timer + for used by the DAC, but then you also have to configure which DAC + channel it is assigned to. + +choice + prompt "Select TIM3 DAC channel" + default STM32L4_TIM3_DAC1 + depends on STM32L4_TIM3_DAC + +config STM32L4_TIM3_DAC1 + bool "TIM3 DAC channel 1" + ---help--- + Reserve TIM3 to trigger DAC1 + +config STM32L4_TIM3_DAC2 + bool "TIM3 DAC channel 2" + ---help--- + Reserve TIM3 to trigger DAC2 + +endchoice + +config STM32L4_TIM4_DAC + bool "TIM4 DAC" + default n + depends on STM32L4_TIM4 && STM32L4_DAC + ---help--- + Reserve timer 4 for use by DAC + + Timer devices may be used for different purposes. If STM32L4_TIM4 is + defined then the following may also be defined to indicate that the + timer is intended to be used for DAC conversion. Note that DAC usage + requires two definition: Not only do you have to assign the timer + for used by the DAC, but then you also have to configure which DAC + channel it is assigned to. + +choice + prompt "Select TIM4 DAC channel" + default STM32L4_TIM4_DAC1 + depends on STM32L4_TIM4_DAC + +config STM32L4_TIM4_DAC1 + bool "TIM4 DAC channel 1" + ---help--- + Reserve TIM4 to trigger DAC1 + +config STM32L4_TIM4_DAC2 + bool "TIM4 DAC channel 2" + ---help--- + Reserve TIM4 to trigger DAC2 + +endchoice + +config STM32L4_TIM5_DAC + bool "TIM5 DAC" + default n + depends on STM32L4_TIM5 && STM32L4_DAC + ---help--- + Reserve timer 5 for use by DAC + + Timer devices may be used for different purposes. If STM32L4_TIM5 is + defined then the following may also be defined to indicate that the + timer is intended to be used for DAC conversion. Note that DAC usage + requires two definition: Not only do you have to assign the timer + for used by the DAC, but then you also have to configure which DAC + channel it is assigned to. + +choice + prompt "Select TIM5 DAC channel" + default STM32L4_TIM5_DAC1 + depends on STM32L4_TIM5_DAC + +config STM32L4_TIM5_DAC1 + bool "TIM5 DAC channel 1" + ---help--- + Reserve TIM5 to trigger DAC1 + +config STM32L4_TIM5_DAC2 + bool "TIM5 DAC channel 2" + ---help--- + Reserve TIM5 to trigger DAC2 + +endchoice + +config STM32L4_TIM6_DAC + bool "TIM6 DAC" + default n + depends on STM32L4_TIM6 && STM32L4_DAC + ---help--- + Reserve timer 6 for use by DAC + + Timer devices may be used for different purposes. If STM32L4_TIM6 is + defined then the following may also be defined to indicate that the + timer is intended to be used for DAC conversion. Note that DAC usage + requires two definition: Not only do you have to assign the timer + for used by the DAC, but then you also have to configure which DAC + channel it is assigned to. + +choice + prompt "Select TIM6 DAC channel" + default STM32L4_TIM6_DAC1 + depends on STM32L4_TIM6_DAC + +config STM32L4_TIM6_DAC1 + bool "TIM6 DAC channel 1" + ---help--- + Reserve TIM6 to trigger DAC1 + +config STM32L4_TIM6_DAC2 + bool "TIM6 DAC channel 2" + ---help--- + Reserve TIM6 to trigger DAC2 + +endchoice + +config STM32L4_TIM7_DAC + bool "TIM7 DAC" + default n + depends on STM32L4_TIM7 && STM32L4_DAC + ---help--- + Reserve timer 7 for use by DAC + + Timer devices may be used for different purposes. If STM32L4_TIM7 is + defined then the following may also be defined to indicate that the + timer is intended to be used for DAC conversion. Note that DAC usage + requires two definition: Not only do you have to assign the timer + for used by the DAC, but then you also have to configure which DAC + channel it is assigned to. + +choice + prompt "Select TIM7 DAC channel" + default STM32L4_TIM7_DAC1 + depends on STM32L4_TIM7_DAC + +config STM32L4_TIM7_DAC1 + bool "TIM7 DAC channel 1" + ---help--- + Reserve TIM7 to trigger DAC1 + +config STM32L4_TIM7_DAC2 + bool "TIM7 DAC channel 2" + ---help--- + Reserve TIM7 to trigger DAC2 + +endchoice + +config STM32L4_TIM8_DAC + bool "TIM8 DAC" + default n + depends on STM32L4_TIM8 && STM32L4_DAC + ---help--- + Reserve timer 8 for use by DAC + + Timer devices may be used for different purposes. If STM32L4_TIM8 is + defined then the following may also be defined to indicate that the + timer is intended to be used for DAC conversion. Note that DAC usage + requires two definition: Not only do you have to assign the timer + for used by the DAC, but then you also have to configure which DAC + channel it is assigned to. + +choice + prompt "Select TIM8 DAC channel" + default STM32L4_TIM8_DAC1 + depends on STM32L4_TIM8_DAC + +config STM32L4_TIM8_DAC1 + bool "TIM8 DAC channel 1" + ---help--- + Reserve TIM8 to trigger DAC1 + +config STM32L4_TIM8_DAC2 + bool "TIM8 DAC channel 2" + ---help--- + Reserve TIM8 to trigger DAC2 + +endchoice + +config STM32L4_TIM1_CAP + bool "TIM1 Capture" + default n + depends on STM32L4_HAVE_TIM1 + ---help--- + Reserve timer 1 for use by Capture + + Timer devices may be used for different purposes. One special purpose is + to capture input. + +config STM32L4_TIM2_CAP + bool "TIM2 Capture" + default n + depends on STM32L4_HAVE_TIM2 + ---help--- + Reserve timer 2 for use by Capture + + Timer devices may be used for different purposes. One special purpose is + to capture input. + +config STM32L4_TIM3_CAP + bool "TIM3 Capture" + default n + depends on STM32L4_HAVE_TIM3 + ---help--- + Reserve timer 3 for use by Capture + + Timer devices may be used for different purposes. One special purpose is + to capture input. + +config STM32L4_TIM4_CAP + bool "TIM4 Capture" + default n + depends on STM32L4_HAVE_TIM4 + ---help--- + Reserve timer 4 for use by Capture + + Timer devices may be used for different purposes. One special purpose is + to capture input. + +config STM32L4_TIM5_CAP + bool "TIM5 Capture" + default n + depends on STM32L4_HAVE_TIM5 + ---help--- + Reserve timer 5 for use by Capture + + Timer devices may be used for different purposes. One special purpose is + to capture input. + +config STM32L4_TIM8_CAP + bool "TIM8 Capture" + default n + depends on STM32L4_HAVE_TIM8 + ---help--- + Reserve timer 8 for use by Capture + + Timer devices may be used for different purposes. One special purpose is + to capture input. + +endmenu # Timer Configuration + +menu "ADC Configuration" + depends on STM32L4_ADC + +config STM32L4_ADC1_DMA + bool "ADC1 DMA" + depends on STM32L4_ADC1 && STM32L4_HAVE_ADC1_DMA + default n + ---help--- + If DMA is selected, then the ADC may be configured to support + DMA transfer, which is necessary if multiple channels are read + or if very high trigger frequencies are used. + +config STM32L4_ADC2_DMA + bool "ADC2 DMA" + depends on STM32L4_ADC2 && STM32L4_HAVE_ADC2_DMA + default n + ---help--- + If DMA is selected, then the ADC may be configured to support + DMA transfer, which is necessary if multiple channels are read + or if very high trigger frequencies are used. + +config STM32L4_ADC3_DMA + bool "ADC3 DMA" + depends on STM32L4_ADC3 && STM32L4_HAVE_ADC3_DMA + default n + ---help--- + If DMA is selected, then the ADC may be configured to support + DMA transfer, which is necessary if multiple channels are read + or if very high trigger frequencies are used. + +endmenu + +menu "DAC Configuration" + depends on STM32L4_DAC1 || STM32L4_DAC2 + +config STM32L4_DAC1_DMA + bool "DAC1 DMA" + depends on STM32L4_DAC1 + default n + ---help--- + If DMA is selected, then a timer and output frequency must also be + provided to support the DMA transfer. The DMA transfer could be + supported by and EXTI trigger, but this feature is not currently + supported by the driver. + +if STM32L4_DAC1_DMA + +config STM32L4_DAC1_TIMER + int "DAC1 timer" + range 2 8 + +config STM32L4_DAC1_TIMER_FREQUENCY + int "DAC1 timer frequency" + default 0 + +endif + +config STM32L4_DAC2_DMA + bool "DAC2 DMA" + depends on STM32L4_DAC2 + default n + ---help--- + If DMA is selected, then a timer and output frequency must also be + provided to support the DMA transfer. The DMA transfer could be + supported by and EXTI trigger, but this feature is not currently + supported by the driver. + +if STM32L4_DAC2_DMA + +config STM32L4_DAC2_TIMER + int "DAC2 timer" + default 0 + range 2 8 + +config STM32L4_DAC2_TIMER_FREQUENCY + int "DAC2 timer frequency" + default 0 + +endif + +config STM32L4_DAC_DMA_BUFFER_SIZE + int "DAC DMA buffer size" + default 256 + +endmenu + menu "U[S]ART Configuration" depends on STM32L4_USART @@ -784,7 +2623,7 @@ config STM32L4_FLOWCONTROL_BROKEN config STM32L4_USART_BREAKS bool "Add TIOxSBRK to support sending Breaks" - depends on STM32_USART + depends on STM32L4_USART default n ---help--- Add TIOCxBRK routines to send a line break per the STM32 manual, the @@ -793,7 +2632,7 @@ config STM32L4_USART_BREAKS config STM32L4_SERIALBRK_BSDCOMPAT bool "Use GPIO To send Break" - depends on STM32_USART && STM32_USART_BREAKS + depends on STM32L4_USART && STM32L4_USART_BREAKS default n ---help--- Enable using GPIO on the TX pin to send a BSD compatible break: diff --git a/arch/arm/src/stm32l4/Make.defs b/arch/arm/src/stm32l4/Make.defs index a47e7004b0..33a5e0a5ba 100644 --- a/arch/arm/src/stm32l4/Make.defs +++ b/arch/arm/src/stm32l4/Make.defs @@ -112,9 +112,24 @@ CHIP_CSRCS = stm32l4_allocateheap.c stm32l4_exti_gpio.c stm32l4_gpio.c CHIP_CSRCS += stm32l4_idle.c stm32l4_irq.c stm32l4_lowputc.c stm32l4_rcc.c CHIP_CSRCS += stm32l4_serial.c stm32l4_start.c stm32l4_waste.c stm32l4_uid.c CHIP_CSRCS += stm32l4_spi.c stm32l4_i2c.c stm32l4_lse.c stm32l4_pwr.c +CHIP_CSRCS += stm32l4_tim.c + +ifeq ($(CONFIG_TIMER),y) +CHIP_CSRCS += stm32l4_tim_lowerhalf.c +endif ifneq ($(CONFIG_SCHED_TICKLESS),y) CHIP_CSRCS += stm32l4_timerisr.c +else +CHIP_CSRCS += stm32l4_tickless.c +endif + +ifeq ($(CONFIG_STM32L4_ONESHOT),y) +CHIP_CSRCS += stm32l4_oneshot.c +endif + +ifeq ($(CONFIG_STM32L4_FREERUN),y) +CHIP_CSRCS += stm32l4_freerun.c endif ifeq ($(CONFIG_BUILD_PROTECTED),y) @@ -176,6 +191,10 @@ ifeq ($(CONFIG_STM32L4_RNG),y) CHIP_CSRCS += stm32l4_rng.c endif +ifeq ($(CONFIG_PWM),y) +CHIP_CSRCS += stm32l4_pwm.c +endif + ifeq ($(CONFIG_STM32L4_QSPI),y) CHIP_CSRCS += stm32l4_qspi.c endif diff --git a/arch/arm/src/stm32l4/stm32l4_freerun.c b/arch/arm/src/stm32l4/stm32l4_freerun.c new file mode 100644 index 0000000000..74fc952ef0 --- /dev/null +++ b/arch/arm/src/stm32l4/stm32l4_freerun.c @@ -0,0 +1,297 @@ +/**************************************************************************** + * arch/arm/src/stm32l4/stm32l4_freerun.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * dev@ziggurat29.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the names NuttX nor Atmel nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "stm32l4_freerun.h" + +#ifdef CONFIG_STM32L4_FREERUN + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static struct stm32l4_freerun_s *g_freerun; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_freerun_handler + * + * Description: + * Timer interrupt callback. When the freerun timer counter overflows, + * this interrupt will occur. We will just increment an overflow count. + * + * Input Parameters: + * tch - The handle that represents the timer state + * arg - An opaque argument provided when the interrupt was registered + * sr - The value of the timer interrupt status register at the time + * that the interrupt occurred. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int stm32_freerun_handler(int irq, void *context) +{ + struct stm32l4_freerun_s *freerun = g_freerun; + + DEBUGASSERT(freerun != NULL && freerun->overflow < UINT32_MAX); + freerun->overflow++; + + STM32L4_TIM_ACKINT(freerun->tch, 0); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32l4_freerun_initialize + * + * Description: + * Initialize the freerun timer wrapper + * + * Input Parameters: + * freerun Caller allocated instance of the freerun state structure + * chan Timer counter channel to be used. + * resolution The required resolution of the timer in units of + * microseconds. NOTE that the range is restricted to the + * range of uint16_t (excluding zero). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int stm32l4_freerun_initialize(struct stm32l4_freerun_s *freerun, int chan, + uint16_t resolution) +{ + uint32_t frequency; + + tmrinfo("chan=%d resolution=%d usec\n", chan, resolution); + DEBUGASSERT(freerun != NULL && resolution > 0); + + /* Get the TC frequency the corresponds to the requested resolution */ + + frequency = USEC_PER_SEC / (uint32_t)resolution; + freerun->frequency = frequency; + + freerun->tch = stm32l4_tim_init(chan); + if (!freerun->tch) + { + tmrerr("ERROR: Failed to allocate TIM%d\n", chan); + return -EBUSY; + } + + STM32L4_TIM_SETCLOCK(freerun->tch, frequency); + + /* Initialize the remaining fields in the state structure and return + * success. + */ + + freerun->chan = chan; + freerun->running = false; + freerun->overflow = 0; + + g_freerun = freerun; + + /* Set up to receive the callback when the counter overflow occurs */ + + STM32L4_TIM_SETISR(freerun->tch, stm32_freerun_handler, 0); + + /* Set timer period */ + + STM32L4_TIM_SETPERIOD(freerun->tch, UINT32_MAX); + + /* Start the counter */ + + STM32L4_TIM_SETMODE(freerun->tch, STM32L4_TIM_MODE_UP); + STM32L4_TIM_ACKINT(freerun->tch, 0); + STM32L4_TIM_ENABLEINT(freerun->tch, 0); + + return OK; +} + +/**************************************************************************** + * Name: stm32l4_freerun_counter + * + * Description: + * Read the counter register of the free-running timer. + * + * Input Parameters: + * freerun Caller allocated instance of the freerun state structure. This + * structure must have been previously initialized via a call to + * stm32_freerun_initialize(); + * ts The location in which to return the time from the free-running + * timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int stm32l4_freerun_counter(struct stm32l4_freerun_s *freerun, + struct timespec *ts) +{ + uint64_t usec; + uint32_t counter; + uint32_t verify; + uint32_t overflow; + uint32_t sec; + int pending; + irqstate_t flags; + + DEBUGASSERT(freerun && freerun->tch && ts); + + /* Temporarily disable the overflow counter. NOTE that we have to be + * careful here because stm32_tc_getpending() will reset the pending + * interrupt status. If we do not handle the overflow here then, it will + * be lost. + */ + + flags = enter_critical_section(); + + overflow = freerun->overflow; + counter = STM32L4_TIM_GETCOUNTER(freerun->tch); + pending = STM32L4_TIM_CHECKINT(freerun->tch, 0); + verify = STM32L4_TIM_GETCOUNTER(freerun->tch); + + /* If an interrupt was pending before we re-enabled interrupts, + * then the overflow needs to be incremented. + */ + + if (pending) + { + STM32L4_TIM_ACKINT(freerun->tch, 0); + + /* Increment the overflow count and use the value of the + * guaranteed to be AFTER the overflow occurred. + */ + + overflow++; + counter = verify; + + /* Update freerun overflow counter. */ + + freerun->overflow = overflow; + } + + leave_critical_section(flags); + + tmrinfo("counter=%lu (%lu) overflow=%lu, pending=%i\n", + (unsigned long)counter, (unsigned long)verify, + (unsigned long)overflow, pending); + tmrinfo("frequency=%u\n", freerun->frequency); + + /* Convert the whole thing to units of microseconds. + * + * frequency = ticks / second + * seconds = ticks * frequency + * usecs = (ticks * USEC_PER_SEC) / frequency; + */ + + usec = ((((uint64_t)overflow << 32) + (uint64_t)counter) * USEC_PER_SEC) / + freerun->frequency; + + /* And return the value of the timer */ + + sec = (uint32_t)(usec / USEC_PER_SEC); + ts->tv_sec = sec; + ts->tv_nsec = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; + + tmrinfo("usec=%llu ts=(%u, %lu)\n", + usec, (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); + + return OK; +} + +/**************************************************************************** + * Name: stm32l4_freerun_uninitialize + * + * Description: + * Stop the free-running timer and release all resources that it uses. + * + * Input Parameters: + * freerun Caller allocated instance of the freerun state structure. This + * structure must have been previously initialized via a call to + * stm32_freerun_initialize(); + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int stm32l4_freerun_uninitialize(struct stm32l4_freerun_s *freerun) +{ + DEBUGASSERT(freerun && freerun->tch); + + /* Now we can disable the timer interrupt and disable the timer. */ + + STM32L4_TIM_DISABLEINT(freerun->tch, 0); + STM32L4_TIM_SETMODE(freerun->tch, STM32L4_TIM_MODE_DISABLED); + STM32L4_TIM_SETISR(freerun->tch, NULL, 0); + + /* Free the timer */ + + stm32l4_tim_deinit(freerun->tch); + freerun->tch = NULL; + + g_freerun = NULL; + return OK; +} + +#endif /* CONFIG_STM32L4_FREERUN */ diff --git a/arch/arm/src/stm32l4/stm32l4_freerun.h b/arch/arm/src/stm32l4/stm32l4_freerun.h new file mode 100644 index 0000000000..8b5e004d50 --- /dev/null +++ b/arch/arm/src/stm32l4/stm32l4_freerun.h @@ -0,0 +1,159 @@ +/**************************************************************************** + * arch/arm/src/stm32l4/stm32l4_freerun.h + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * dev@ziggurat29.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32L4_FREERUN_H +#define __ARCH_ARM_SRC_STM32L4_FREERUN_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include "stm32l4_tim.h" + +#ifdef CONFIG_STM32L4_FREERUN + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* The freerun client must allocate an instance of this structure and called + * stm32_freerun_initialize() before using the freerun facilities. The client + * should not access the contents of this structure directly since the + * contents are subject to change. + */ + +struct stm32l4_freerun_s +{ + uint8_t chan; /* The timer/counter in use */ + bool running; /* True: the timer is running */ + uint32_t overflow; /* Timer counter overflow */ + FAR struct stm32l4_tim_dev_s *tch; /* Handle returned by stm32l4_tim_init() */ + uint32_t frequency; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32l4_freerun_initialize + * + * Description: + * Initialize the freerun timer wrapper + * + * Input Parameters: + * freerun Caller allocated instance of the freerun state structure + * chan Timer counter channel to be used. + * resolution The required resolution of the timer in units of + * microseconds. NOTE that the range is restricted to the + * range of uint16_t (excluding zero). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int stm32l4_freerun_initialize(struct stm32l4_freerun_s *freerun, int chan, + uint16_t resolution); + +/**************************************************************************** + * Name: stm32l4_freerun_counter + * + * Description: + * Read the counter register of the free-running timer. + * + * Input Parameters: + * freerun Caller allocated instance of the freerun state structure. This + * structure must have been previously initialized via a call to + * stm32_freerun_initialize(); + * ts The location in which to return the time remaining on the + * oneshot timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int stm32l4_freerun_counter(struct stm32l4_freerun_s *freerun, + struct timespec *ts); + +/**************************************************************************** + * Name: stm32l4_freerun_uninitialize + * + * Description: + * Stop the free-running timer and release all resources that it uses. + * + * Input Parameters: + * freerun Caller allocated instance of the freerun state structure. This + * structure must have been previously initialized via a call to + * stm32_freerun_initialize(); + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int stm32l4_freerun_uninitialize(struct stm32l4_freerun_s *freerun); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_STM32L4_FREERUN */ +#endif /* __ARCH_ARM_SRC_STM32L4_FREERUN_H */ diff --git a/arch/arm/src/stm32l4/stm32l4_oneshot.c b/arch/arm/src/stm32l4/stm32l4_oneshot.c new file mode 100644 index 0000000000..3abb44e862 --- /dev/null +++ b/arch/arm/src/stm32l4/stm32l4_oneshot.c @@ -0,0 +1,416 @@ +/**************************************************************************** + * arch/arm/src/stm32l4/stm32l4_oneshot.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * dev@ziggurat29.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the names NuttX nor Atmel nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "stm32l4_oneshot.h" + +#ifdef CONFIG_STM32L4_ONESHOT + +/**************************************************************************** + * Private Date + ****************************************************************************/ + +static struct stm32l4_oneshot_s *g_oneshot; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_oneshot_handler + * + * Description: + * Timer interrupt callback. When the oneshot timer interrupt expires, + * this function will be called. It will forward the call to the next + * level up. + * + * Input Parameters: + * tch - The handle that represents the timer state + * arg - An opaque argument provided when the interrupt was registered + * sr - The value of the timer interrupt status register at the time + * that the interrupt occurred. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int stm32_oneshot_handler(int irq, void *context) +{ + struct stm32l4_oneshot_s *oneshot = g_oneshot; + oneshot_handler_t oneshot_handler; + void *oneshot_arg; + + tmrinfo("Expired...\n"); + DEBUGASSERT(oneshot != NULL && oneshot->handler); + + /* The clock was stopped, but not disabled when the RC match occurred. + * Disable the TC now and disable any further interrupts. + */ + + STM32L4_TIM_SETMODE(oneshot->tch, STM32L4_TIM_MODE_DISABLED); + STM32L4_TIM_ACKINT(oneshot->tch, 0); + STM32L4_TIM_DISABLEINT(oneshot->tch, 0); + + /* The timer is no longer running */ + + oneshot->running = false; + + /* Forward the event, clearing out any vestiges */ + + oneshot_handler = (oneshot_handler_t)oneshot->handler; + oneshot->handler = NULL; + oneshot_arg = (void *)oneshot->arg; + oneshot->arg = NULL; + + oneshot_handler(oneshot_arg); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32l4_oneshot_initialize + * + * Description: + * Initialize the oneshot timer wrapper + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure + * chan Timer counter channel to be used. + * resolution The required resolution of the timer in units of + * microseconds. NOTE that the range is restricted to the + * range of uint16_t (excluding zero). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int stm32l4_oneshot_initialize(struct stm32l4_oneshot_s *oneshot, int chan, + uint16_t resolution) +{ + uint32_t frequency; + + tmrinfo("chan=%d resolution=%d usec\n", chan, resolution); + DEBUGASSERT(oneshot && resolution > 0); + + /* Get the TC frequency the corresponds to the requested resolution */ + + frequency = USEC_PER_SEC / (uint32_t)resolution; + oneshot->frequency = frequency; + + oneshot->tch = stm32l4_tim_init(chan); + if (!oneshot->tch) + { + tmrerr("ERROR: Failed to allocate TIM%d\n", chan); + return -EBUSY; + } + + STM32L4_TIM_SETCLOCK(oneshot->tch, frequency); + + /* Initialize the remaining fields in the state structure and return + * success. + */ + + oneshot->chan = chan; + oneshot->running = false; + oneshot->handler = NULL; + oneshot->arg = NULL; + + g_oneshot = oneshot; + return OK; +} + +/**************************************************************************** + * Name: stm32_oneshot_max_delay + * + * Description: + * Determine the maximum delay of the one-shot timer (in microseconds) + * + ****************************************************************************/ + +int stm32l4_oneshot_max_delay(struct stm32l4_oneshot_s *oneshot, uint64_t *usec) +{ + DEBUGASSERT(oneshot != NULL && usec != NULL); + + *usec = (uint64_t)(UINT32_MAX / oneshot->frequency) * + (uint64_t)USEC_PER_SEC; + return OK; +} + +/**************************************************************************** + * Name: stm32l4_oneshot_start + * + * Description: + * Start the oneshot timer + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * stm32_oneshot_initialize(); + * handler The function to call when when the oneshot timer expires. + * arg An opaque argument that will accompany the callback. + * ts Provides the duration of the one shot timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int stm32l4_oneshot_start(struct stm32l4_oneshot_s *oneshot, + oneshot_handler_t handler, void *arg, + const struct timespec *ts) +{ + uint64_t usec; + uint64_t period; + irqstate_t flags; + + tmrinfo("handler=%p arg=%p, ts=(%lu, %lu)\n", + handler, arg, (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); + DEBUGASSERT(oneshot && handler && ts); + DEBUGASSERT(oneshot->tch); + + /* Was the oneshot already running? */ + + flags = enter_critical_section(); + if (oneshot->running) + { + /* Yes.. then cancel it */ + + tmrinfo("Already running... cancelling\n"); + (void)stm32l4_oneshot_cancel(oneshot, NULL); + } + + /* Save the new handler and its argument */ + + oneshot->handler = handler; + oneshot->arg = arg; + + /* Express the delay in microseconds */ + + usec = (uint64_t)ts->tv_sec * USEC_PER_SEC + + (uint64_t)(ts->tv_nsec / NSEC_PER_USEC); + + /* Get the timer counter frequency and determine the number of counts need + * to achieve the requested delay. + * + * frequency = ticks / second + * ticks = seconds * frequency + * = (usecs * frequency) / USEC_PER_SEC; + */ + + period = (usec * (uint64_t)oneshot->frequency) / USEC_PER_SEC; + + tmrinfo("usec=%llu period=%08llx\n", usec, period); + DEBUGASSERT(period <= UINT32_MAX); + + /* Set up to receive the callback when the interrupt occurs */ + + STM32L4_TIM_SETISR(oneshot->tch, stm32_oneshot_handler, 0); + + /* Set timer period */ + + oneshot->period = (uint32_t)period; + STM32L4_TIM_SETPERIOD(oneshot->tch, (uint32_t)period); + + /* Start the counter */ + + STM32L4_TIM_SETMODE(oneshot->tch, STM32L4_TIM_MODE_PULSE); + + STM32L4_TIM_ACKINT(oneshot->tch, 0); + STM32L4_TIM_ENABLEINT(oneshot->tch, 0); + + /* Enable interrupts. We should get the callback when the interrupt + * occurs. + */ + + oneshot->running = true; + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: stm32l4_oneshot_cancel + * + * Description: + * Cancel the oneshot timer and return the time remaining on the timer. + * + * NOTE: This function may execute at a high rate with no timer running (as + * when pre-emption is enabled and disabled). + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * stm32_oneshot_initialize(); + * ts The location in which to return the time remaining on the + * oneshot timer. A time of zero is returned if the timer is + * not running. ts may be zero in which case the time remaining + * is not returned. + * + * Returned Value: + * Zero (OK) is returned on success. A call to up_timer_cancel() when + * the timer is not active should also return success; a negated errno + * value is returned on any failure. + * + ****************************************************************************/ + +int stm32l4_oneshot_cancel(struct stm32l4_oneshot_s *oneshot, + struct timespec *ts) +{ + irqstate_t flags; + uint64_t usec; + uint64_t sec; + uint64_t nsec; + uint32_t count; + uint32_t period; + + /* Was the timer running? */ + + flags = enter_critical_section(); + if (!oneshot->running) + { + /* No.. Just return zero timer remaining and successful cancellation. + * This function may execute at a high rate with no timer running + * (as when pre-emption is enabled and disabled). + */ + + ts->tv_sec = 0; + ts->tv_nsec = 0; + leave_critical_section(flags); + return OK; + } + + /* Yes.. Get the timer counter and period registers and stop the counter. + * If the counter expires while we are doing this, the counter clock will + * be stopped, but the clock will not be disabled. + * + * The expected behavior is that the the counter register will freezes at + * a value equal to the RC register when the timer expires. The counter + * should have values between 0 and RC in all other cased. + * + * REVISIT: This does not appear to be the case. + */ + + tmrinfo("Cancelling...\n"); + + count = STM32L4_TIM_GETCOUNTER(oneshot->tch); + period = oneshot->period; + + /* Now we can disable the interrupt and stop the timer. */ + + STM32L4_TIM_DISABLEINT(oneshot->tch, 0); + STM32L4_TIM_SETMODE(oneshot->tch, STM32L4_TIM_MODE_DISABLED); + + oneshot->running = false; + oneshot->handler = NULL; + oneshot->arg = NULL; + leave_critical_section(flags); + + /* Did the caller provide us with a location to return the time + * remaining? + */ + + if (ts) + { + /* Yes.. then calculate and return the time remaining on the + * oneshot timer. + */ + + tmrinfo("period=%lu count=%lu\n", + (unsigned long)period, (unsigned long)count); + + /* REVISIT: I am not certain why the timer counter value sometimes + * exceeds RC. Might be a bug, or perhaps the counter does not stop + * in all cases. + */ + + if (count >= period) + { + /* No time remaining (?) */ + + ts->tv_sec = 0; + ts->tv_nsec = 0; + } + else + { + /* The total time remaining is the difference. Convert the that + * to units of microseconds. + * + * frequency = ticks / second + * seconds = ticks * frequency + * usecs = (ticks * USEC_PER_SEC) / frequency; + */ + + usec = (((uint64_t)(period - count)) * USEC_PER_SEC) / + oneshot->frequency; + + /* Return the time remaining in the correct form */ + + sec = usec / USEC_PER_SEC; + nsec = ((usec) - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; + + ts->tv_sec = (time_t)sec; + ts->tv_nsec = (unsigned long)nsec; + } + + tmrinfo("remaining (%lu, %lu)\n", + (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); + } + + return OK; +} + +#endif /* CONFIG_STM32L4_ONESHOT */ diff --git a/arch/arm/src/stm32l4/stm32l4_oneshot.h b/arch/arm/src/stm32l4/stm32l4_oneshot.h new file mode 100644 index 0000000000..6111706321 --- /dev/null +++ b/arch/arm/src/stm32l4/stm32l4_oneshot.h @@ -0,0 +1,190 @@ +/**************************************************************************** + * arch/arm/src/stm32l4/stm32l4_oneshot.h + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * dev@ziggurat29.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32L4_ONESHOT_H +#define __ARCH_ARM_SRC_STM32L4_ONESHOT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "stm32l4_tim.h" + +#ifdef CONFIG_STM32L4_ONESHOT + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This describes the callback function that will be invoked when the oneshot + * timer expires. The oneshot fires, the client will receive: + * + * arg - The opaque argument provided when the interrupt was registered + */ + +typedef void (*oneshot_handler_t)(void *arg); + +/* The oneshot client must allocate an instance of this structure and called + * stm32_oneshot_initialize() before using the oneshot facilities. The client + * should not access the contents of this structure directly since the + * contents are subject to change. + */ + +struct stm32l4_oneshot_s +{ + uint8_t chan; /* The timer/counter in use */ + volatile bool running; /* True: the timer is running */ + FAR struct stm32l4_tim_dev_s *tch; /* Pointer returned by + * stm32l4_tim_init() */ + volatile oneshot_handler_t handler; /* Oneshot expiration callback */ + volatile void *arg; /* The argument that will accompany + * the callback */ + uint32_t frequency; + uint32_t period; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32l4_oneshot_initialize + * + * Description: + * Initialize the oneshot timer wrapper + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure + * chan Timer counter channel to be used. + * resolution The required resolution of the timer in units of + * microseconds. NOTE that the range is restricted to the + * range of uint16_t (excluding zero). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int stm32l4_oneshot_initialize(struct stm32l4_oneshot_s *oneshot, int chan, + uint16_t resolution); + +/**************************************************************************** + * Name: stm32l4_oneshot_max_delay + * + * Description: + * Determine the maximum delay of the one-shot timer (in microseconds) + * + ****************************************************************************/ + +int stm32l4_oneshot_max_delay(struct stm32l4_oneshot_s *oneshot, uint64_t *usec); + +/**************************************************************************** + * Name: stm32l4_oneshot_start + * + * Description: + * Start the oneshot timer + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * stm32_oneshot_initialize(); + * handler The function to call when when the oneshot timer expires. + * arg An opaque argument that will accompany the callback. + * ts Provides the duration of the one shot timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int stm32l4_oneshot_start(struct stm32l4_oneshot_s *oneshot, + oneshot_handler_t handler, void *arg, + const struct timespec *ts); + +/**************************************************************************** + * Name: stm32l4_oneshot_cancel + * + * Description: + * Cancel the oneshot timer and return the time remaining on the timer. + * + * NOTE: This function may execute at a high rate with no timer running (as + * when pre-emption is enabled and disabled). + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * stm32_oneshot_initialize(); + * ts The location in which to return the time remaining on the + * oneshot timer. A time of zero is returned if the timer is + * not running. + * + * Returned Value: + * Zero (OK) is returned on success. A call to up_timer_cancel() when + * the timer is not active should also return success; a negated errno + * value is returned on any failure. + * + ****************************************************************************/ + +int stm32l4_oneshot_cancel(struct stm32l4_oneshot_s *oneshot, + struct timespec *ts); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_STM32L4_ONESHOT */ +#endif /* __ARCH_ARM_SRC_STM32L4_ONESHOT_H */ diff --git a/arch/arm/src/stm32l4/stm32l4_pwm.c b/arch/arm/src/stm32l4/stm32l4_pwm.c new file mode 100644 index 0000000000..b679da395c --- /dev/null +++ b/arch/arm/src/stm32l4/stm32l4_pwm.c @@ -0,0 +1,2052 @@ +/**************************************************************************** + * arch/arm/src/stm32l4/stm32l4_pwm.c + * + * Copyright (C) 2011-2012, 2016 Gregory Nutt. All rights reserved. + * Copyright (C) 2015 Omni Hoverboards Inc. All rights reserved. + * Authors: Gregory Nutt + * Paul Alexander Patience + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "up_internal.h" +#include "up_arch.h" + +#include "chip.h" +#include "stm32l4_pwm.h" +#include "stm32l4.h" + +/* This module then only compiles if there is at least one enabled timer + * 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) + +/**************************************************************************** + * 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 */ +#define TIMTYPE_GENERAL16 1 /* General 16-bit timers: TIM3,4 */ +#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_TIM1 TIMTYPE_ADVANCED +#define TIMTYPE_TIM2 TIMTYPE_GENERAL32 +#define TIMTYPE_TIM3 TIMTYPE_GENERAL16 +#define TIMTYPE_TIM4 TIMTYPE_GENERAL16 +#define TIMTYPE_TIM5 TIMTYPE_GENERAL32 +#define TIMTYPE_TIM6 TIMTYPE_BASIC +#define TIMTYPE_TIM7 TIMTYPE_BASIC +#define TIMTYPE_TIM8 TIMTYPE_ADVANCED +#define TIMTYPE_TIM15 TIMTYPE_COUNTUP16 +#define TIMTYPE_TIM16 TIMTYPE_COUNTUP16 +#define TIMTYPE_TIM17 TIMTYPE_COUNTUP16 + +/* Debug ********************************************************************/ + +#ifdef CONFIG_DEBUG_PWM_INFO +# define pwm_dumpgpio(p,m) stm32_dumpgpio(p,m) +#else +# define pwm_dumpgpio(p,m) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum stm32_timmode_e +{ + STM32_TIMMODE_COUNTUP = 0, + STM32_TIMMODE_COUNTDOWN = 1, + STM32_TIMMODE_CENTER1 = 2, + STM32_TIMMODE_CENTER2 = 3, + STM32_TIMMODE_CENTER3 = 4, +}; + +enum stm32_chanmode_e +{ + STM32_CHANMODE_PWM1 = 0, + STM32_CHANMODE_PWM2 = 1, + STM32_CHANMODE_COMBINED1 = 2, + STM32_CHANMODE_COMBINED2 = 3, + STM32_CHANMODE_ASYMMETRIC1 = 4, + STM32_CHANMODE_ASYMMETRIC2 = 5, +}; + +struct stm32_pwmchan_s +{ + uint8_t channel; /* Timer output channel: {1,..4} */ + uint32_t pincfg; /* Output pin configuration */ + enum stm32_chanmode_e mode; +}; + +/* This structure represents the state of one PWM timer */ + +struct stm32_pwmtimer_s +{ + FAR const struct pwm_ops_s *ops; /* PWM operations */ + uint8_t timid; /* Timer ID {1,...,17} */ + struct stm32_pwmchan_s channels[PWM_NCHANNELS]; + uint8_t timtype; /* See the TIMTYPE_* definitions */ + enum stm32_timmode_e mode; +#ifdef CONFIG_PWM_PULSECOUNT + uint8_t irq; /* Timer update IRQ */ + uint8_t prev; /* The previous value of the RCR (pre-loaded) */ + uint8_t curr; /* The current value of the RCR (pre-loaded) */ + uint32_t count; /* Remaining pluse count */ +#else + uint32_t frequency; /* Current frequency setting */ +#endif + uint32_t base; /* The base address of the timer */ + uint32_t pclk; /* The frequency of the peripheral clock + * that drives the timer module. */ +#ifdef CONFIG_PWM_PULSECOUNT + FAR void *handle; /* Handle used for upper-half callback */ +#endif +}; + +/**************************************************************************** + * Static Function Prototypes + ****************************************************************************/ +/* Register access */ + +static uint16_t pwm_getreg(struct stm32_pwmtimer_s *priv, int offset); +static void pwm_putreg(struct stm32_pwmtimer_s *priv, int offset, uint16_t value); + +#ifdef CONFIG_DEBUG_PWM_INFO +static void pwm_dumpregs(struct stm32_pwmtimer_s *priv, FAR const char *msg); +#else +# define pwm_dumpregs(priv,msg) +#endif + +/* Timer management */ + +static int pwm_timer(FAR struct stm32_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 pwm_interrupt(struct stm32_pwmtimer_s *priv); +#if defined(CONFIG_STM32L4_TIM1_PWM) +static int pwm_tim1interrupt(int irq, void *context); +#endif +#if defined(CONFIG_STM32L4_TIM8_PWM) +static int pwm_tim8interrupt(int irq, void *context); +#endif +static uint8_t pwm_pulsecount(uint32_t count); +#endif + +/* PWM driver methods */ + +static int pwm_setup(FAR struct pwm_lowerhalf_s *dev); +static int pwm_shutdown(FAR struct pwm_lowerhalf_s *dev); + +#ifdef CONFIG_PWM_PULSECOUNT +static int pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info, + FAR void *handle); +#else +static int pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info); +#endif + +static int pwm_stop(FAR struct pwm_lowerhalf_s *dev); +static int pwm_ioctl(FAR struct pwm_lowerhalf_s *dev, + int cmd, unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ +/* This is the list of lower half PWM driver methods used by the upper half driver */ + +static const struct pwm_ops_s g_pwmops = +{ + .setup = pwm_setup, + .shutdown = pwm_shutdown, + .start = pwm_start, + .stop = pwm_stop, + .ioctl = pwm_ioctl, +}; + +#ifdef CONFIG_STM32L4_TIM1_PWM +static struct stm32_pwmtimer_s g_pwm1dev = +{ + .ops = &g_pwmops, + .timid = 1, + .channels = + { +#ifdef CONFIG_STM32L4_TIM1_CHANNEL1 + { + .channel = 1, + .pincfg = PWM_TIM1_CH1CFG, + .mode = CONFIG_STM32L4_TIM1_CH1MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM1_CHANNEL2 + { + .channel = 2, + .pincfg = PWM_TIM1_CH2CFG, + .mode = CONFIG_STM32L4_TIM1_CH2MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM1_CHANNEL3 + { + .channel = 3, + .pincfg = PWM_TIM1_CH3CFG, + .mode = CONFIG_STM32L4_TIM1_CH3MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM1_CHANNEL4 + { + .channel = 4, + .pincfg = PWM_TIM1_CH4CFG, + .mode = CONFIG_STM32L4_TIM1_CH4MODE, + }, +#endif + }, + .timtype = TIMTYPE_TIM1, + .mode = CONFIG_STM32L4_TIM1_MODE, +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32L4_IRQ_TIM1UP, +#endif + .base = STM32L4_TIM1_BASE, + .pclk = STM32L4_APB2_TIM1_CLKIN, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM2_PWM +static struct stm32_pwmtimer_s g_pwm2dev = +{ + .ops = &g_pwmops, + .timid = 2, + .channels = + { +#ifdef CONFIG_STM32L4_TIM2_CHANNEL1 + { + .channel = 1, + .pincfg = PWM_TIM2_CH1CFG, + .mode = CONFIG_STM32L4_TIM2_CH1MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM2_CHANNEL2 + { + .channel = 2, + .pincfg = PWM_TIM2_CH2CFG, + .mode = CONFIG_STM32L4_TIM2_CH2MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM2_CHANNEL3 + { + .channel = 3, + .pincfg = PWM_TIM2_CH3CFG, + .mode = CONFIG_STM32L4_TIM2_CH3MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM2_CHANNEL4 + { + .channel = 4, + .pincfg = PWM_TIM2_CH4CFG, + .mode = CONFIG_STM32L4_TIM2_CH4MODE, + }, +#endif + }, + .timtype = TIMTYPE_TIM2, + .mode = CONFIG_STM32L4_TIM2_MODE, +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32L4_IRQ_TIM2, +#endif + .base = STM32L4_TIM2_BASE, + .pclk = STM32L4_APB1_TIM2_CLKIN, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM3_PWM +static struct stm32_pwmtimer_s g_pwm3dev = +{ + .ops = &g_pwmops, + .timid = 3, + .channels = + { +#ifdef CONFIG_STM32L4_TIM3_CHANNEL1 + { + .channel = 1, + .pincfg = PWM_TIM3_CH1CFG, + .mode = CONFIG_STM32L4_TIM3_CH1MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM3_CHANNEL2 + { + .channel = 2, + .pincfg = PWM_TIM3_CH2CFG, + .mode = CONFIG_STM32L4_TIM3_CH2MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM3_CHANNEL3 + { + .channel = 3, + .pincfg = PWM_TIM3_CH3CFG, + .mode = CONFIG_STM32L4_TIM3_CH3MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM3_CHANNEL4 + { + .channel = 4, + .pincfg = PWM_TIM3_CH4CFG, + .mode = CONFIG_STM32L4_TIM3_CH4MODE, + }, +#endif + }, + .timtype = TIMTYPE_TIM3, + .mode = CONFIG_STM32L4_TIM3_MODE, +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32L4_IRQ_TIM3, +#endif + .base = STM32L4_TIM3_BASE, + .pclk = STM32L4_APB1_TIM3_CLKIN, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM4_PWM +static struct stm32_pwmtimer_s g_pwm4dev = +{ + .ops = &g_pwmops, + .timid = 4, + .channels = + { +#ifdef CONFIG_STM32L4_TIM4_CHANNEL1 + { + .channel = 1, + .pincfg = PWM_TIM4_CH1CFG, + .mode = CONFIG_STM32L4_TIM4_CH1MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM4_CHANNEL2 + { + .channel = 2, + .pincfg = PWM_TIM4_CH2CFG, + .mode = CONFIG_STM32L4_TIM4_CH2MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM4_CHANNEL3 + { + .channel = 3, + .pincfg = PWM_TIM4_CH3CFG, + .mode = CONFIG_STM32L4_TIM4_CH3MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM4_CHANNEL4 + { + .channel = 4, + .pincfg = PWM_TIM4_CH4CFG, + .mode = CONFIG_STM32L4_TIM4_CH4MODE, + }, +#endif + }, + .timtype = TIMTYPE_TIM4, + .mode = CONFIG_STM32L4_TIM4_MODE, +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32L4_IRQ_TIM4, +#endif + .base = STM32L4_TIM4_BASE, + .pclk = STM32L4_APB1_TIM4_CLKIN, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM5_PWM +static struct stm32_pwmtimer_s g_pwm5dev = +{ + .ops = &g_pwmops, + .timid = 5, + .channels = + { +#ifdef CONFIG_STM32L4_TIM5_CHANNEL1 + { + .channel = 1, + .pincfg = PWM_TIM5_CH1CFG, + .mode = CONFIG_STM32L4_TIM5_CH1MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM5_CHANNEL2 + { + .channel = 2, + .pincfg = PWM_TIM5_CH2CFG, + .mode = CONFIG_STM32L4_TIM5_CH2MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM5_CHANNEL3 + { + .channel = 3, + .pincfg = PWM_TIM5_CH3CFG, + .mode = CONFIG_STM32L4_TIM5_CH3MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM5_CHANNEL4 + { + .channel = 4, + .pincfg = PWM_TIM5_CH4CFG, + .mode = CONFIG_STM32L4_TIM5_CH4MODE, + }, +#endif + }, + .timtype = TIMTYPE_TIM5, + .mode = CONFIG_STM32L4_TIM5_MODE, +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32L4_IRQ_TIM5, +#endif + .base = STM32L4_TIM5_BASE, + .pclk = STM32L4_APB1_TIM5_CLKIN, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM8_PWM +static struct stm32_pwmtimer_s g_pwm8dev = +{ + .ops = &g_pwmops, + .timid = 8, + .channels = + { +#ifdef CONFIG_STM32L4_TIM8_CHANNEL1 + { + .channel = 1, + .pincfg = PWM_TIM8_CH1CFG, + .mode = CONFIG_STM32L4_TIM8_CH1MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM8_CHANNEL2 + { + .channel = 2, + .pincfg = PWM_TIM8_CH2CFG, + .mode = CONFIG_STM32L4_TIM8_CH2MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM8_CHANNEL3 + { + .channel = 3, + .pincfg = PWM_TIM8_CH3CFG, + .mode = CONFIG_STM32L4_TIM8_CH3MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM8_CHANNEL4 + { + .channel = 4, + .pincfg = PWM_TIM8_CH4CFG, + .mode = CONFIG_STM32L4_TIM8_CH4MODE, + }, +#endif + }, + .timtype = TIMTYPE_TIM8, + .mode = CONFIG_STM32L4_TIM8_MODE, +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32L4_IRQ_TIM8UP, +#endif + .base = STM32L4_TIM8_BASE, + .pclk = STM32L4_APB2_TIM8_CLKIN, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM15_PWM +static struct stm32_pwmtimer_s g_pwm15dev = +{ + .ops = &g_pwmops, + .timid = 15, + .channels = + { +#ifdef CONFIG_STM32L4_TIM15_CHANNEL1 + { + .channel = 1, + .pincfg = PWM_TIM15_CH1CFG, + .mode = CONFIG_STM32L4_TIM15_CH1MODE, + }, +#endif +#ifdef CONFIG_STM32L4_TIM15_CHANNEL2 + { + .channel = 2, + .pincfg = PWM_TIM15_CH2CFG, + .mode = CONFIG_STM32L4_TIM15_CH2MODE, + }, +#endif + }, + .timtype = TIMTYPE_TIM15, + .mode = STM32L4_TIMMODE_COUNTUP, +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32L4_IRQ_TIM15, +#endif + .base = STM32L4_TIM15_BASE, + .pclk = STM32L4_APB1_TIM15_CLKIN, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM16_PWM +static struct stm32_pwmtimer_s g_pwm16dev = +{ + .ops = &g_pwmops, + .timid = 16, + .channels = + { +#ifdef CONFIG_STM32L4_TIM16_CHANNEL1 + { + .channel = 1, + .pincfg = PWM_TIM16_CH1CFG, + .mode = CONFIG_STM32L4_TIM16_CH1MODE, + }, +#endif + }, + .timtype = TIMTYPE_TIM16, + .mode = STM32L4_TIMMODE_COUNTUP, +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32L4_IRQ_TIM16, +#endif + .base = STM32L4_TIM16_BASE, + .pclk = STM32L4_APB1_TIM16_CLKIN, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM17_PWM +static struct stm32_pwmtimer_s g_pwm17dev = +{ + .ops = &g_pwmops, + .timid = 17, + .channels = + { +#ifdef CONFIG_STM32L4_TIM17_CHANNEL1 + { + .channel = 1, + .pincfg = PWM_TIM17_CH1CFG, + .mode = CONFIG_STM32L4_TIM17_CH1MODE, + }, +#endif + }, + .timtype = TIMTYPE_TIM17, + .mode = STM32L4_TIMMODE_COUNTUP, +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32L4_IRQ_TIM17, +#endif + .base = STM32L4_TIM17_BASE, + .pclk = STM32L4_APB1_TIM17_CLKIN, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pwm_getreg + * + * Description: + * Read the value of an PWM timer register. + * + * Input Parameters: + * priv - A reference to the PWM block status + * offset - The offset to the register to read + * + * Returned Value: + * The current contents of the specified register + * + ****************************************************************************/ + +static uint16_t pwm_getreg(struct stm32_pwmtimer_s *priv, int offset) +{ + return getreg16(priv->base + offset); +} + +/**************************************************************************** + * Name: pwm_putreg + * + * Description: + * Read the value of an PWM timer register. + * + * Input Parameters: + * priv - A reference to the PWM block status + * offset - The offset to the register to read + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void pwm_putreg(struct stm32_pwmtimer_s *priv, int offset, uint16_t value) +{ + if (priv->timtype == TIMTYPE_GENERAL32 && + (offset == STM32L4_GTIM_CNT_OFFSET || + offset == STM32L4_GTIM_ARR_OFFSET || + offset == STM32L4_GTIM_CCR1_OFFSET || + offset == STM32L4_GTIM_CCR2_OFFSET || + offset == STM32L4_GTIM_CCR3_OFFSET || + offset == STM32L4_GTIM_CCR4_OFFSET)) + { + /* a 32 bit access is required for a 32 bit register: + * if only a 16 bit write would be performed, then the + * upper 16 bits of the 32 bit register will be a copy of + * the lower 16 bits. + */ + + putreg32(value, priv->base + offset); + } + else + { + putreg16(value, priv->base + offset); + } +} + +/**************************************************************************** + * Name: pwm_dumpregs + * + * Description: + * Dump all timer registers. + * + * Input parameters: + * priv - A reference to the PWM block status + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_PWM_INFO +static void pwm_dumpregs(struct stm32_pwmtimer_s *priv, FAR const char *msg) +{ + pwminfo("%s:\n", msg); + pwminfo(" CR1: %04x CR2: %04x SMCR: %04x DIER: %04x\n", + pwm_getreg(priv, STM32L4_GTIM_CR1_OFFSET), + pwm_getreg(priv, STM32L4_GTIM_CR2_OFFSET), + pwm_getreg(priv, STM32L4_GTIM_SMCR_OFFSET), + pwm_getreg(priv, STM32L4_GTIM_DIER_OFFSET)); + pwminfo(" SR: %04x EGR: %04x CCMR1: %04x CCMR2: %04x\n", + pwm_getreg(priv, STM32L4_GTIM_SR_OFFSET), + pwm_getreg(priv, STM32L4_GTIM_EGR_OFFSET), + pwm_getreg(priv, STM32L4_GTIM_CCMR1_OFFSET), + pwm_getreg(priv, STM32L4_GTIM_CCMR2_OFFSET)); + pwminfo(" CCER: %04x CNT: %04x PSC: %04x ARR: %04x\n", + pwm_getreg(priv, STM32L4_GTIM_CCER_OFFSET), + pwm_getreg(priv, STM32L4_GTIM_CNT_OFFSET), + pwm_getreg(priv, STM32L4_GTIM_PSC_OFFSET), + pwm_getreg(priv, STM32L4_GTIM_ARR_OFFSET)); + pwminfo(" CCR1: %04x CCR2: %04x CCR3: %04x CCR4: %04x\n", + pwm_getreg(priv, STM32L4_GTIM_CCR1_OFFSET), + pwm_getreg(priv, STM32L4_GTIM_CCR2_OFFSET), + pwm_getreg(priv, STM32L4_GTIM_CCR3_OFFSET), + pwm_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", + pwm_getreg(priv, STM32L4_ATIM_RCR_OFFSET), + pwm_getreg(priv, STM32L4_ATIM_BDTR_OFFSET), + pwm_getreg(priv, STM32L4_ATIM_DCR_OFFSET), + pwm_getreg(priv, STM32L4_ATIM_DMAR_OFFSET)); + } + else +#endif + { + pwminfo(" DCR: %04x DMAR: %04x\n", + pwm_getreg(priv, STM32L4_GTIM_DCR_OFFSET), + pwm_getreg(priv, STM32L4_GTIM_DMAR_OFFSET)); + } +} +#endif + +/**************************************************************************** + * Name: pwm_timer + * + * Description: + * (Re-)initialize the 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 pwm_timer(FAR struct stm32_pwmtimer_s *priv, + FAR const struct pwm_info_s *info) +{ +#ifdef CONFIG_PWM_MULTICHAN + int i; +#endif + + /* Calculated values */ + + uint32_t prescaler; + uint32_t timclk; + uint32_t reload; + uint32_t ccr; + + /* Register contents */ + + uint16_t cr1; + uint16_t ccer; + uint16_t cr2; + uint32_t ccmr1; + uint32_t ccmr2; + + /* New timer register bit settings */ + + uint16_t ccenable; + uint32_t ocmode1; + uint32_t ocmode2; + + DEBUGASSERT(priv != NULL && info != NULL); + +#if defined(CONFIG_PWM_MULTICHAN) + pwminfo("TIM%u frequency: %u\n", + priv->timid, info->frequency); +#elif defined(CONFIG_PWM_PULSECOUNT) + pwminfo("TIM%u channel: %u frequency: %u duty: %08x count: %u\n", + priv->timid, priv->channels[0].channel, info->frequency, + info->duty, info->count); +#else + pwminfo("TIM%u channel: %u frequency: %u duty: %08x\n", + priv->timid, priv->channels[0].channel, info->frequency, info->duty); +#endif + + DEBUGASSERT(info->frequency > 0); +#ifndef CONFIG_PWM_MULTICHAN + DEBUGASSERT(info->duty >= 0 && info->duty < uitoub16(100)); +#endif + + /* Disable all interrupts and DMA requests, clear all pending status */ + +#ifdef CONFIG_PWM_PULSECOUNT + pwm_putreg(priv, STM32L4_GTIM_DIER_OFFSET, 0); + pwm_putreg(priv, STM32L4_GTIM_SR_OFFSET, 0); +#endif + + /* Calculate optimal values for the timer prescaler and for the timer reload + * register. If 'frequency' is the desired frequency, then + * + * reload = timclk / frequency + * timclk = pclk / presc + * + * Or, + * + * reload = pclk / presc / frequency + * + * There are many solutions to this 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: + * + * 0 <= presc <= 65536 + * 1 <= reload <= 65535 + * + * So presc = pclk / 65535 / frequency would be optimal. + * + * Example: + * + * pclk = 42 MHz + * frequency = 100 Hz + * + * prescaler = 42,000,000 / 65,535 / 100 + * = 6.4 (or 7 -- taking the ceiling always) + * timclk = 42,000,000 / 7 + * = 6,000,000 + * reload = 6,000,000 / 100 + * = 60,000 + */ + + prescaler = (priv->pclk / info->frequency + 65534) / 65535; + if (prescaler < 1) + { + prescaler = 1; + } + else if (prescaler > 65536) + { + prescaler = 65536; + } + + timclk = priv->pclk / prescaler; + + reload = timclk / info->frequency; + if (reload < 1) + { + reload = 1; + } + else if (reload > 65535) + { + reload = 65535; + } + + pwminfo("TIM%u PCLK: %u frequency: %u TIMCLK: %u prescaler: %u reload: %u\n", + priv->timid, priv->pclk, info->frequency, timclk, prescaler, reload); + + /* Set up the timer CR1 register: + * + * 1,8 CKD[1:0] ARPE CMS[1:0] DIR OPM URS UDIS CEN + * 2-5 CKD[1:0] ARPE CMS DIR OPM URS UDIS CEN + * 6-7 ARPE OPM URS UDIS CEN + * 9-14 CKD[1:0] ARPE URS UDIS CEN + * 15-17 CKD[1:0] ARPE OPM URS UDIS CEN + */ + + cr1 = pwm_getreg(priv, STM32L4_GTIM_CR1_OFFSET); + + /* Disable the timer until we get it configured */ + + cr1 &= ~GTIM_CR1_CEN; + + /* Set the counter mode for the advanced timers (1,8) and most general + * purpose timers (all 2-5, but not 9-17), i.e., all but TIMTYPE_COUNTUP16 + * and TIMTYPE_BASIC + */ + +#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) + + if (priv->timtype != TIMTYPE_BASIC && priv->timtype != TIMTYPE_COUNTUP16) + { + /* Select the Counter Mode: + * + * GTIM_CR1_EDGE: The counter counts up or down depending on the + * direction bit (DIR). + * GTIM_CR1_CENTER1, GTIM_CR1_CENTER2, GTIM_CR1_CENTER3: The counter + * counts up then down. + * GTIM_CR1_DIR: 0: count up, 1: count down + */ + + cr1 &= ~(GTIM_CR1_DIR | GTIM_CR1_CMS_MASK); + + switch (priv->mode) + { + case STM32_TIMMODE_COUNTUP: + cr1 |= GTIM_CR1_EDGE; + break; + + case STM32_TIMMODE_COUNTDOWN: + cr1 |= GTIM_CR1_EDGE | GTIM_CR1_DIR; + break; + + case STM32_TIMMODE_CENTER1: + cr1 |= GTIM_CR1_CENTER1; + break; + + case STM32_TIMMODE_CENTER2: + cr1 |= GTIM_CR1_CENTER2; + break; + + case STM32_TIMMODE_CENTER3: + cr1 |= GTIM_CR1_CENTER3; + break; + + default: + pwmerr("ERROR: No such timer mode: %u\n", (unsigned int)priv->mode); + return -EINVAL; + } + } +#endif + + /* Set the clock division to zero for all (but the basic timers, but there + * should be no basic timers in this context + */ + + cr1 &= ~GTIM_CR1_CKD_MASK; + pwm_putreg(priv, STM32L4_GTIM_CR1_OFFSET, cr1); + + /* Set the reload and prescaler values */ + + pwm_putreg(priv, STM32L4_GTIM_ARR_OFFSET, (uint16_t)reload); + pwm_putreg(priv, STM32L4_GTIM_PSC_OFFSET, (uint16_t)(prescaler - 1)); + + /* Set the advanced timer's repetition counter */ + +#if defined(CONFIG_STM32L4_TIM1_PWM) || defined(CONFIG_STM32L4_TIM8_PWM) + if (priv->timtype == TIMTYPE_ADVANCED) + { + /* If a non-zero repetition count has been selected, then set the + * repitition counter to the count-1 (pwm_start() has already + * assured us that the count value is within range). + */ + +#ifdef CONFIG_PWM_PULSECOUNT + if (info->count > 0) + { + /* Save the remaining count and the number of counts that will have + * elapsed on the first interrupt. + */ + + /* If the first interrupt occurs at the end end of the first + * repetition count, then the count will be the same as the RCR + * value. + */ + + priv->prev = pwm_pulsecount(info->count); + pwm_putreg(priv, STM32L4_ATIM_RCR_OFFSET, (uint16_t)priv->prev - 1); + + /* Generate an update event to reload the prescaler. This should + * preload the RCR into active repetition counter. + */ + + pwm_putreg(priv, STM32L4_GTIM_EGR_OFFSET, ATIM_EGR_UG); + + /* Now set the value of the RCR that will be loaded on the next + * update event. + */ + + priv->count = info->count; + priv->curr = pwm_pulsecount(info->count - priv->prev); + pwm_putreg(priv, STM32L4_ATIM_RCR_OFFSET, (uint16_t)priv->curr - 1); + } + + /* Otherwise, just clear the repetition counter */ + + else +#endif + { + /* Set the repetition counter to zero */ + + pwm_putreg(priv, STM32L4_ATIM_RCR_OFFSET, 0); + + /* Generate an update event to reload the prescaler */ + + pwm_putreg(priv, STM32L4_GTIM_EGR_OFFSET, ATIM_EGR_UG); + } + } + else +#endif + { + /* Generate an update event to reload the prescaler (all timers) */ + + pwm_putreg(priv, STM32L4_GTIM_EGR_OFFSET, ATIM_EGR_UG); + } + + /* Handle channel specific setup */ + + ccenable = 0; + ocmode1 = 0; + ocmode2 = 0; + +#ifdef CONFIG_PWM_MULTICHAN + for (i = 0; i < CONFIG_PWM_NCHANNELS; i++) +#endif + { + ub16_t duty; + uint32_t chanmode; + bool ocmbit = false; + uint8_t channel; +#ifdef CONFIG_PWM_MULTICHAN + int j; +#endif + enum stm32_chanmode_e mode; + +#ifdef CONFIG_PWM_MULTICHAN + duty = info->channels[i].duty; + channel = info->channels[i].channel; + + /* A value of zero means to skip this channel */ + + if (channel == 0) + { + continue; + } + + /* Find the channel */ + + for (j = 0; j < PWM_NCHANNELS; j++) + { + if (priv->channels[j].channel == channel) + { + mode = priv->channels[j].mode; + break; + } + } + + if (j >= PWM_NCHANNELS) + { + pwmerr("ERROR: No such channel: %u\n", channel); + return -EINVAL; + } +#else + duty = info->duty; + channel = priv->channels[0].channel; + mode = priv->channels[0].mode; +#endif + + /* Duty cycle: + * + * duty cycle = ccr / reload (fractional value) + */ + + ccr = b16toi(duty * reload + b16HALF); + + pwminfo("ccr: %u\n", ccr); + + switch (mode) + { + case STM32_CHANMODE_PWM1: + chanmode = ATIM_CCMR_MODE_PWM1; + break; + + case STM32_CHANMODE_PWM2: + chanmode = ATIM_CCMR_MODE_PWM2; + break; + + case STM32_CHANMODE_COMBINED1: + chanmode = ATIM_CCMR_MODE_COMBINED1; + ocmbit = true; + break; + + case STM32_CHANMODE_COMBINED2: + chanmode = ATIM_CCMR_MODE_COMBINED2; + ocmbit = true; + break; + + case STM32_CHANMODE_ASYMMETRIC1: + chanmode = ATIM_CCMR_MODE_ASYMMETRIC1; + ocmbit = true; + break; + + case STM32_CHANMODE_ASYMMETRIC2: + chanmode = ATIM_CCMR_MODE_ASYMMETRIC2; + ocmbit = true; + break; + + default: + pwmerr("ERROR: No such mode: %u\n", (unsigned int)mode); + return -EINVAL; + } + + switch (channel) + { + case 1: /* PWM Mode configuration: Channel 1 */ + { + /* Select the CCER enable bit for this channel */ + + ccenable |= ATIM_CCER_CC1E; + + /* Set the CCMR1 mode values (leave CCMR2 zero) */ + + ocmode1 |= (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR1_CC1S_SHIFT) | + (chanmode << ATIM_CCMR1_OC1M_SHIFT) | + ATIM_CCMR1_OC1PE; + + if (ocmbit) + { + ocmode1 |= ATIM_CCMR1_OC1M; + } + + /* Set the duty cycle by writing to the CCR register for this channel */ + + pwm_putreg(priv, STM32L4_GTIM_CCR1_OFFSET, (uint16_t)ccr); + } + break; + + case 2: /* PWM Mode configuration: Channel 2 */ + { + /* Select the CCER enable bit for this channel */ + + ccenable |= ATIM_CCER_CC2E; + + /* Set the CCMR1 mode values (leave CCMR2 zero) */ + + ocmode1 |= (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR1_CC2S_SHIFT) | + (chanmode << ATIM_CCMR1_OC2M_SHIFT) | + ATIM_CCMR1_OC2PE; + + if (ocmbit) + { + ocmode1 |= ATIM_CCMR1_OC2M; + } + + /* Set the duty cycle by writing to the CCR register for this channel */ + + pwm_putreg(priv, STM32L4_GTIM_CCR2_OFFSET, (uint16_t)ccr); + } + break; + + case 3: /* PWM Mode configuration: Channel 3 */ + { + /* Select the CCER enable bit for this channel */ + + ccenable |= ATIM_CCER_CC3E; + + /* Set the CCMR2 mode values (leave CCMR1 zero) */ + + ocmode2 |= (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR2_CC3S_SHIFT) | + (chanmode << ATIM_CCMR2_OC3M_SHIFT) | + ATIM_CCMR2_OC3PE; + + if (ocmbit) + { + ocmode2 |= ATIM_CCMR2_OC3M; + } + + /* Set the duty cycle by writing to the CCR register for this channel */ + + pwm_putreg(priv, STM32L4_GTIM_CCR3_OFFSET, (uint16_t)ccr); + } + break; + + case 4: /* PWM Mode configuration: Channel 4 */ + { + /* Select the CCER enable bit for this channel */ + + ccenable |= ATIM_CCER_CC4E; + + /* Set the CCMR2 mode values (leave CCMR1 zero) */ + + ocmode2 |= (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR2_CC4S_SHIFT) | + (chanmode << ATIM_CCMR2_OC4M_SHIFT) | + ATIM_CCMR2_OC4PE; + + if (ocmbit) + { + ocmode2 |= ATIM_CCMR2_OC4M; + } + + /* Set the duty cycle by writing to the CCR register for this channel */ + + pwm_putreg(priv, STM32L4_GTIM_CCR4_OFFSET, (uint16_t)ccr); + } + break; + + default: + pwmerr("ERROR: No such channel: %u\n", channel); + return -EINVAL; + } + } + + /* Disable the Channel by resetting the CCxE Bit in the CCER register */ + + ccer = pwm_getreg(priv, STM32L4_GTIM_CCER_OFFSET); + ccer &= ~ccenable; + pwm_putreg(priv, STM32L4_GTIM_CCER_OFFSET, ccer); + + /* Fetch the CR2, CCMR1, and CCMR2 register (already have cr1 and ccer) */ + + cr2 = pwm_getreg(priv, STM32L4_GTIM_CR2_OFFSET); + ccmr1 = pwm_getreg(priv, STM32L4_GTIM_CCMR1_OFFSET); + ccmr2 = pwm_getreg(priv, STM32L4_GTIM_CCMR2_OFFSET); + + /* Reset the Output Compare Mode Bits and set the select output compare mode */ + + ccmr1 &= ~(ATIM_CCMR1_CC1S_MASK | ATIM_CCMR1_OC1M_MASK | ATIM_CCMR1_OC1PE | + ATIM_CCMR1_CC2S_MASK | ATIM_CCMR1_OC2M_MASK | ATIM_CCMR1_OC2PE + | ATIM_CCMR1_OC1M | ATIM_CCMR1_OC2M + ); + ccmr2 &= ~(ATIM_CCMR2_CC3S_MASK | ATIM_CCMR2_OC3M_MASK | ATIM_CCMR2_OC3PE | + ATIM_CCMR2_CC4S_MASK | ATIM_CCMR2_OC4M_MASK | ATIM_CCMR2_OC4PE + | ATIM_CCMR2_OC3M | ATIM_CCMR2_OC4M + ); + ccmr1 |= ocmode1; + ccmr2 |= ocmode2; + + /* Reset the output polarity level of all channels (selects high polarity)*/ + + ccer &= ~(ATIM_CCER_CC1P | ATIM_CCER_CC2P | ATIM_CCER_CC3P | ATIM_CCER_CC4P); + + /* Enable the output state of the selected channels */ + + ccer &= ~(ATIM_CCER_CC1E | ATIM_CCER_CC2E | ATIM_CCER_CC3E | ATIM_CCER_CC4E); + ccer |= ccenable; + + /* Some special setup for advanced timers */ + +#if defined(CONFIG_STM32L4_TIM1_PWM) || defined(CONFIG_STM32L4_TIM8_PWM) + if (priv->timtype == TIMTYPE_ADVANCED) + { + uint16_t bdtr; + + /* Reset output N polarity level, output N state, output compare state, + * output compare N idle state. + */ + + ccer &= ~(ATIM_CCER_CC1NE | ATIM_CCER_CC1NP | ATIM_CCER_CC2NE | ATIM_CCER_CC2NP | + ATIM_CCER_CC3NE | ATIM_CCER_CC3NP | ATIM_CCER_CC4NP); + + /* Reset the output compare and output compare N IDLE State */ + + cr2 &= ~(ATIM_CR2_OIS1 | ATIM_CR2_OIS1N | ATIM_CR2_OIS2 | ATIM_CR2_OIS2N | + ATIM_CR2_OIS3 | ATIM_CR2_OIS3N | ATIM_CR2_OIS4); + + /* Set the main output enable (MOE) bit and clear the OSSI and OSSR + * bits in the BDTR register. + */ + + bdtr = pwm_getreg(priv, STM32L4_ATIM_BDTR_OFFSET); + bdtr &= ~(ATIM_BDTR_OSSI | ATIM_BDTR_OSSR); + bdtr |= ATIM_BDTR_MOE; + pwm_putreg(priv, STM32L4_ATIM_BDTR_OFFSET, bdtr); + } + else +#endif + { + ccer &= ~(GTIM_CCER_CC1NP | GTIM_CCER_CC2NP | GTIM_CCER_CC3NP | ATIM_CCER_CC4NP); + } + + /* Save the modified register values */ + + pwm_putreg(priv, STM32L4_GTIM_CR2_OFFSET, cr2); + putreg32(ccmr1, priv->base + STM32L4_GTIM_CCMR1_OFFSET); + putreg32(ccmr2, priv->base + STM32L4_GTIM_CCMR2_OFFSET); + pwm_putreg(priv, STM32L4_GTIM_CCER_OFFSET, ccer); + + /* Set the ARR Preload Bit */ + + cr1 = pwm_getreg(priv, STM32L4_GTIM_CR1_OFFSET); + cr1 |= GTIM_CR1_ARPE; + pwm_putreg(priv, STM32L4_GTIM_CR1_OFFSET, cr1); + + /* Setup update interrupt. If info->count is > 0, then we can be + * assured that pwm_start() has already verified: (1) that this is an + * advanced timer, and that (2) the repetition count is within range. + */ + +#ifdef CONFIG_PWM_PULSECOUNT + if (info->count > 0) + { + /* Clear all pending interrupts and enable the update interrupt. */ + + pwm_putreg(priv, STM32L4_GTIM_SR_OFFSET, 0); + pwm_putreg(priv, STM32L4_GTIM_DIER_OFFSET, ATIM_DIER_UIE); + + /* Enable the timer */ + + cr1 |= GTIM_CR1_CEN; + pwm_putreg(priv, STM32L4_GTIM_CR1_OFFSET, cr1); + + /* And enable timer interrupts at the NVIC */ + + up_enable_irq(priv->irq); + } + else +#endif + { + /* Just enable the timer, leaving all interrupts disabled */ + + cr1 |= GTIM_CR1_CEN; + pwm_putreg(priv, STM32L4_GTIM_CR1_OFFSET, cr1); + } + + pwm_dumpregs(priv, "After starting"); + return OK; +} + +#ifndef CONFIG_PWM_PULSECOUNT +/**************************************************************************** + * Name: pwm_update_duty + * + * Description: + * Try to change only channel duty. + * + * Input parameters: + * priv - A reference to the lower half PWM driver state structure + * channel - Channel to by updated + * duty - New duty. + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_update_duty(FAR struct stm32_pwmtimer_s *priv, uint8_t channel, + ub16_t duty) +{ + /* Register offset */ + + int ccr_offset; + + /* Calculated values */ + + uint32_t reload; + uint32_t ccr; + + DEBUGASSERT(priv != NULL); + + pwminfo("TIM%u channel: %u duty: %08x\n", + priv->timid, channel, duty); + +#ifndef CONFIG_PWM_MULTICHAN + DEBUGASSERT(channel == priv->channels[0].channel); + DEBUGASSERT(duty >= 0 && duty < uitoub16(100)); +#endif + + /* Get the reload values */ + + reload = pwm_getreg(priv, STM32L4_GTIM_ARR_OFFSET); + + /* Duty cycle: + * + * duty cycle = ccr / reload (fractional value) + */ + + ccr = b16toi(duty * reload + b16HALF); + + pwminfo("ccr: %u\n", ccr); + + switch (channel) + { + case 1: /* Register offset for Channel 1 */ + ccr_offset = STM32L4_GTIM_CCR1_OFFSET; + break; + + case 2: /* Register offset for Channel 2 */ + ccr_offset = STM32L4_GTIM_CCR2_OFFSET; + break; + + case 3: /* Register offset for Channel 3 */ + ccr_offset = STM32L4_GTIM_CCR3_OFFSET; + break; + + 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 */ + + pwm_putreg(priv, ccr_offset, (uint16_t)ccr); + + return OK; +} +#endif + +/**************************************************************************** + * Name: pwm_interrupt + * + * Description: + * Handle timer interrupts. + * + * Input parameters: + * priv - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +#if defined(CONFIG_PWM_PULSECOUNT) && (defined(CONFIG_STM32_TIM1_PWM) || defined(CONFIG_STM32_TIM8_PWM)) +static int pwm_interrupt(struct stm32_pwmtimer_s *priv) +{ + uint16_t regval; + + /* Verify that this is an update interrupt. Nothing else is expected. */ + + regval = pwm_getreg(priv, STM32L4_ATIM_SR_OFFSET); + DEBUGASSERT((regval & ATIM_SR_UIF) != 0); + + /* Clear the UIF interrupt bit */ + + pwm_putreg(priv, STM32L4_ATIM_SR_OFFSET, regval & ~ATIM_SR_UIF); + + /* Calculate the new count by subtracting the number of pulses + * since the last interrupt. + */ + + if (priv->count <= priv->prev) + { + /* We are finished. Turn off the mast output to stop the output as + * quickly as possible. + */ + + regval = pwm_getreg(priv, STM32L4_ATIM_BDTR_OFFSET); + regval &= ~ATIM_BDTR_MOE; + pwm_putreg(priv, STM32L4_ATIM_BDTR_OFFSET, regval); + + /* Disable first interrtups, stop and reset the timer */ + + (void)pwm_stop((FAR struct pwm_lowerhalf_s *)priv); + + /* Then perform the callback into the upper half driver */ + + pwm_expired(priv->handle); + + priv->handle = NULL; + priv->count = 0; + priv->prev = 0; + priv->curr = 0; + } + else + { + /* Decrement the count of pulses remaining using the number of + * pulses generated since the last interrupt. + */ + + priv->count -= priv->prev; + + /* Set up the next RCR. Set 'prev' to the value of the RCR that + * was loaded when the update occurred (just before this interrupt) + * and set 'curr' to the current value of the RCR register (which + * will bet loaded on the next update event). + */ + + priv->prev = priv->curr; + priv->curr = pwm_pulsecount(priv->count - priv->prev); + pwm_putreg(priv, STM32L4_ATIM_RCR_OFFSET, (uint16_t)priv->curr - 1); + } + + /* Now all of the time critical stuff is done so we can do some debug output */ + + pwminfo("Update interrupt SR: %04x prev: %u curr: %u count: %u\n", + regval, priv->prev, priv->curr, priv->count); + + return OK; +} +#endif + +/**************************************************************************** + * Name: pwm_tim1/8interrupt + * + * Description: + * Handle timer 1 and 8 interrupts. + * + * Input parameters: + * Standard NuttX interrupt inputs + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +#if defined(CONFIG_PWM_PULSECOUNT) && defined(CONFIG_STM32_TIM1_PWM) +static int pwm_tim1interrupt(int irq, void *context) +{ + return pwm_interrupt(&g_pwm1dev); +} +#endif + +#if defined(CONFIG_PWM_PULSECOUNT) && defined(CONFIG_STM32_TIM8_PWM) +static int pwm_tim8interrupt(int irq, void *context) +{ + return pwm_interrupt(&g_pwm8dev); +} +#endif + +/**************************************************************************** + * Name: pwm_pulsecount + * + * Description: + * Pick an optimal pulse count to program the RCR. + * + * Input parameters: + * count - The total count remaining + * + * Returned Value: + * The recommended pulse count + * + ****************************************************************************/ + +#if defined(CONFIG_PWM_PULSECOUNT) && (defined(CONFIG_STM32_TIM1_PWM) || defined(CONFIG_STM32_TIM8_PWM)) +static uint8_t pwm_pulsecount(uint32_t count) +{ + /* The the remaining pulse count is less than or equal to the maximum, the + * just return the count. + */ + + if (count <= ATIM_RCR_REP_MAX) + { + return count; + } + + /* Otherwise, we have to be careful. We do not want a small number of + * counts at the end because we might have trouble responding fast enough. + * If the remaining count is less than 150% of the maximum, then return + * half of the maximum. In this case the final sequence will be between 64 + * and 128. + */ + + else if (count < (3 * ATIM_RCR_REP_MAX / 2)) + { + return (ATIM_RCR_REP_MAX + 1) >> 1; + } + + /* Otherwise, return the maximum. The final count will be 64 or more */ + + else + { + return ATIM_RCR_REP_MAX; + } +} +#endif + +/**************************************************************************** + * Name: pwm_set_apb_clock + * + * Description: + * Enable or disable APB clock for the timer peripheral + * + * Input parameters: + * dev - A reference to the lower half PWM driver state structure + * on - Enable clock if 'on' is 'true' and disable if 'false' + * + ****************************************************************************/ + +static void pwm_set_apb_clock(FAR struct stm32_pwmtimer_s *priv, bool on) +{ + uint32_t en_bit; + uint32_t regaddr; + + /* Determine which timer to configure */ + + switch (priv->timid) + { +#ifdef CONFIG_STM32L4_TIM1_PWM + case 1: + regaddr = STM32L4_RCC_APB2ENR; + en_bit = RCC_APB2ENR_TIM1EN; + break; +#endif +#ifdef CONFIG_STM32L4_TIM2_PWM + case 2: + regaddr = STM32L4_RCC_APB1ENR; + en_bit = RCC_APB1ENR_TIM2EN; + break; +#endif +#ifdef CONFIG_STM32L4_TIM3_PWM + case 3: + regaddr = STM32L4_RCC_APB1ENR; + en_bit = RCC_APB1ENR_TIM3EN; + break; +#endif +#ifdef CONFIG_STM32L4_TIM4_PWM + case 4: + regaddr = STM32L4_RCC_APB1ENR; + en_bit = RCC_APB1ENR_TIM4EN; + break; +#endif +#ifdef CONFIG_STM32L4_TIM5_PWM + case 5: + regaddr = STM32L4_RCC_APB1ENR; + en_bit = RCC_APB1ENR_TIM5EN; + break; +#endif +#ifdef CONFIG_STM32L4_TIM8_PWM + 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; +#endif +#ifdef CONFIG_STM32L4_TIM16_PWM + 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; +#endif + default: + return; + } + + /* Enable/disable APB 1/2 clock for timer */ + + if (on) + { + modifyreg32(regaddr, 0, en_bit); + } + else + { + modifyreg32(regaddr, en_bit, 0); + } +} + +/**************************************************************************** + * 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 + * + * Assumptions: + * APB1 or 2 clocking for the GPIOs has already been configured by the RCC + * logic at power up. + * + ****************************************************************************/ + +static int pwm_setup(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct stm32_pwmtimer_s *priv = (FAR struct stm32_pwmtimer_s *)dev; + uint32_t pincfg; + int i; + + pwminfo("TIM%u\n", priv->timid); + pwm_dumpregs(priv, "Initially"); + + /* Enable APB1/2 clocking for timer. */ + + pwm_set_apb_clock(priv, true); + + /* Configure the PWM output pins, but do not start the timer yet */ + + for (i = 0; i < PWM_NCHANNELS; i++) + { + pincfg = priv->channels[i].pincfg; + if (pincfg == 0) + { + continue; + } + + pwminfo("pincfg: %08x\n", pincfg); + + stm32_configgpio(pincfg); + pwm_dumpgpio(pincfg, "PWM setup"); + } + + 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(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct stm32_pwmtimer_s *priv = (FAR struct stm32_pwmtimer_s *)dev; + uint32_t pincfg; + int i; + + pwminfo("TIM%u\n", priv->timid); + + /* Make sure that the output has been stopped */ + + pwm_stop(dev); + + /* Disable APB1/2 clocking for timer. */ + + pwm_set_apb_clock(priv, false); + + /* Then put the GPIO pins back to the default state */ + + for (i = 0; i < PWM_NCHANNELS; i++) + { + pincfg = priv->channels[i].pincfg; + if (pincfg == 0) + { + continue; + } + + pwminfo("pincfg: %08x\n", pincfg); + + pincfg &= (GPIO_PORT_MASK | GPIO_PIN_MASK); + + pincfg |= GPIO_INPUT | GPIO_FLOAT; + + stm32_configgpio(pincfg); + } + + return OK; +} + +/**************************************************************************** + * 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 + * + ****************************************************************************/ + +#ifdef CONFIG_PWM_PULSECOUNT +static int pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info, + FAR void *handle) +{ + FAR struct stm32_pwmtimer_s *priv = (FAR struct stm32_pwmtimer_s *)dev; + + /* Check if a pulsecount has been selected */ + + if (info->count > 0) + { + /* Only the advanced timers (TIM1,8 can support the pulse counting) */ + + if (priv->timtype != TIMTYPE_ADVANCED) + { + pwmerr("ERROR: TIM%u cannot support pulse count: %u\n", + priv->timid, info->count); + return -EPERM; + } + } + + /* Save the handle */ + + priv->handle = handle; + + /* Start the time */ + + return pwm_timer(priv, info); +} +#else +static int pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info) +{ + int ret = OK; + FAR struct stm32_pwmtimer_s *priv = (FAR struct stm32_pwmtimer_s *)dev; + +#ifndef CONFIG_PWM_PULSECOUNT + /* if frequency has not changed we just update duty */ + + if (info->frequency == priv->frequency) + { +#ifdef CONFIG_PWM_MULTICHAN + int i; + + for (i = 0; ret == OK && i < CONFIG_PWM_NCHANNELS; i++) + { + ret = pwm_update_duty(priv,info->channels[i].channel, + info->channels[i].duty); + } +#else + ret = pwm_update_duty(priv,priv->channels[0].channel,info->duty); +#endif + } + else +#endif + { + ret = pwm_timer(priv, info); + +#ifndef CONFIG_PWM_PULSECOUNT + /* Save current frequency */ + + if (ret == OK) + { + priv->frequency = info->frequency; + } +#endif + } + + return ret; +} +#endif + +/**************************************************************************** + * 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 + * + * Assumptions: + * This function is called to stop the pulsed output at anytime. This + * method is also called from the timer interrupt handler when a repetition + * count expires... automatically stopping the timer. + * + ****************************************************************************/ + +static int pwm_stop(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct stm32_pwmtimer_s *priv = (FAR struct stm32_pwmtimer_s *)dev; + uint32_t resetbit; + uint32_t regaddr; + uint32_t regval; + irqstate_t flags; + + 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. + */ + + flags = enter_critical_section(); + + /* Stopped so frequency is zero */ + + priv->frequency = 0; + + /* Disable further interrupts and stop the timer */ + + pwm_putreg(priv, STM32L4_GTIM_DIER_OFFSET, 0); + pwm_putreg(priv, STM32L4_GTIM_SR_OFFSET, 0); + + /* Determine which timer to reset */ + + switch (priv->timid) + { +#ifdef CONFIG_STM32L4_TIM1_PWM + case 1: + regaddr = STM32L4_RCC_APB2RSTR; + resetbit = RCC_APB2RSTR_TIM1RST; + break; +#endif +#ifdef CONFIG_STM32L4_TIM2_PWM + case 2: + regaddr = STM32L4_RCC_APB1RSTR; + resetbit = RCC_APB1RSTR_TIM2RST; + break; +#endif +#ifdef CONFIG_STM32L4_TIM3_PWM + case 3: + regaddr = STM32L4_RCC_APB1RSTR; + resetbit = RCC_APB1RSTR_TIM3RST; + break; +#endif +#ifdef CONFIG_STM32L4_TIM4_PWM + case 4: + regaddr = STM32L4_RCC_APB1RSTR; + resetbit = RCC_APB1RSTR_TIM4RST; + break; +#endif +#ifdef CONFIG_STM32L4_TIM5_PWM + case 5: + regaddr = STM32L4_RCC_APB1RSTR; + resetbit = RCC_APB1RSTR_TIM5RST; + break; +#endif +#ifdef CONFIG_STM32L4_TIM8_PWM + 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; +#endif +#ifdef CONFIG_STM32L4_TIM17_PWM + case 17: + regaddr = STM32L4_RCC_APB2RSTR; + resetbit = RCC_APB2RSTR_TIM17RST; + break; +#endif + default: + return -EINVAL; + } + + /* Reset the timer - stopping the output and putting the timer back + * into a state where pwm_start() can be called. + */ + + regval = getreg32(regaddr); + regval |= resetbit; + putreg32(regval, regaddr); + + regval &= ~resetbit; + putreg32(regval, regaddr); + leave_critical_section(flags); + + pwminfo("regaddr: %08x resetbit: %08x\n", regaddr, resetbit); + pwm_dumpregs(priv, "After stop"); + return OK; +} + +/**************************************************************************** + * 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(FAR struct pwm_lowerhalf_s *dev, int cmd, unsigned long arg) +{ +#ifdef CONFIG_DEBUG_PWM_INFO + FAR struct stm32_pwmtimer_s *priv = (FAR struct stm32_pwmtimer_s *)dev; + + /* There are no platform-specific ioctl commands */ + + pwminfo("TIM%u\n", priv->timid); +#endif + return -ENOTTY; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32l4_pwminitialize + * + * Description: + * Initialize one 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,..,17}. + * + * 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_pwminitialize(int timer) +{ + FAR struct stm32_pwmtimer_s *lower; + + pwminfo("TIM%u\n", timer); + + switch (timer) + { +#ifdef CONFIG_STM32L4_TIM1_PWM + case 1: + lower = &g_pwm1dev; + + /* Attach but disable the TIM1 update interrupt */ + +#ifdef CONFIG_PWM_PULSECOUNT + irq_attach(lower->irq, pwm_tim1interrupt); + up_disable_irq(lower->irq); +#endif + break; +#endif + +#ifdef CONFIG_STM32L4_TIM2_PWM + case 2: + lower = &g_pwm2dev; + break; +#endif + +#ifdef CONFIG_STM32L4_TIM3_PWM + case 3: + lower = &g_pwm3dev; + break; +#endif + +#ifdef CONFIG_STM32L4_TIM4_PWM + case 4: + lower = &g_pwm4dev; + break; +#endif + +#ifdef CONFIG_STM32L4_TIM5_PWM + case 5: + lower = &g_pwm5dev; + break; +#endif + +#ifdef CONFIG_STM32L4_TIM8_PWM + case 8: + lower = &g_pwm8dev; + + /* Attach but disable the TIM8 update interrupt */ + +#ifdef CONFIG_PWM_PULSECOUNT + irq_attach(lower->irq, pwm_tim8interrupt); + up_disable_irq(lower->irq); +#endif + break; +#endif + +#ifdef CONFIG_STM32L4_TIM15_PWM + case 15: + lower = &g_pwm15dev; + break; +#endif + +#ifdef CONFIG_STM32L4_TIM16_PWM + case 16: + lower = &g_pwm16dev; + break; +#endif + +#ifdef CONFIG_STM32L4_TIM17_PWM + case 17: + lower = &g_pwm17dev; + 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 new file mode 100644 index 0000000000..fff04e9e47 --- /dev/null +++ b/arch/arm/src/stm32l4/stm32l4_pwm.h @@ -0,0 +1,679 @@ +/************************************************************************************ + * arch/arm/src/stm32l4/stm32l4_pwm.h + * + * Copyright (C) 2011, 2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2015 Omni Hoverboards Inc. All rights reserved. + * Authors: Gregory Nutt + * Paul Alexander Patience + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32L4_STM32L4_PWM_H +#define __ARCH_ARM_SRC_STM32L4_STM32L4_PWM_H + +/* The STM32L4 does not have dedicated PWM hardware. Rather, pulsed output control + * is a capability of the STM32L4 timers. The logic in this file implements the + * lower half of the standard, NuttX PWM interface using the STM32L4 timers. That + * interface is described in include/nuttx/pwm.h. + */ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include "chip.h" + +/************************************************************************************ + * 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. + */ + +#ifndef CONFIG_STM32L4_TIM1 +# undef CONFIG_STM32L4_TIM1_PWM +#endif +#ifndef CONFIG_STM32L4_TIM2 +# undef CONFIG_STM32L4_TIM2_PWM +#endif +#ifndef CONFIG_STM32L4_TIM3 +# undef CONFIG_STM32L4_TIM3_PWM +#endif +#ifndef CONFIG_STM32L4_TIM4 +# undef CONFIG_STM32L4_TIM4_PWM +#endif +#ifndef CONFIG_STM32L4_TIM5 +# undef CONFIG_STM32L4_TIM5_PWM +#endif +#ifndef CONFIG_STM32L4_TIM8 +# undef CONFIG_STM32L4_TIM8_PWM +#endif +#ifndef CONFIG_STM32L4_TIM15 +# undef CONFIG_STM32L4_TIM15_PWM +#endif +#ifndef CONFIG_STM32L4_TIM16 +# undef CONFIG_STM32L4_TIM16_PWM +#endif +#ifndef CONFIG_STM32L4_TIM17 +# undef CONFIG_STM32L4_TIM17_PWM +#endif + +/* The basic timers (timer 6 and 7) are not capable of generating output pulses */ + +#undef CONFIG_STM32L4_TIM6_PWM +#undef CONFIG_STM32L4_TIM7_PWM + +/* 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) + +#include +#include "chip/stm32l4_tim.h" + +#ifdef CONFIG_PWM_MULTICHAN + +#ifdef CONFIG_STM32L4_TIM1_CHANNEL1 +# ifdef CONFIG_STM32L4_TIM1_CH1OUT +# define PWM_TIM1_CH1CFG GPIO_TIM1_CH1OUT +# else +# define PWM_TIM1_CH1CFG 0 +# endif +# define PWM_TIM1_CHANNEL1 1 +#else +# define PWM_TIM1_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32L4_TIM1_CHANNEL2 +# ifdef CONFIG_STM32L4_TIM1_CH2OUT +# define PWM_TIM1_CH2CFG GPIO_TIM1_CH2OUT +# else +# define PWM_TIM1_CH2CFG 0 +# endif +# define PWM_TIM1_CHANNEL2 1 +#else +# define PWM_TIM1_CHANNEL2 0 +#endif +#ifdef CONFIG_STM32L4_TIM1_CHANNEL3 +# ifdef CONFIG_STM32L4_TIM1_CH3OUT +# define PWM_TIM1_CH3CFG GPIO_TIM1_CH3OUT +# else +# define PWM_TIM1_CH3CFG 0 +# endif +# define PWM_TIM1_CHANNEL3 1 +#else +# define PWM_TIM1_CHANNEL3 0 +#endif +#ifdef CONFIG_STM32L4_TIM1_CHANNEL4 +# ifdef CONFIG_STM32L4_TIM1_CH4OUT +# define PWM_TIM1_CH4CFG GPIO_TIM1_CH4OUT +# else +# define PWM_TIM1_CH4CFG 0 +# endif +# define PWM_TIM1_CHANNEL4 1 +#else +# define PWM_TIM1_CHANNEL4 0 +#endif +#define PWM_TIM1_NCHANNELS (PWM_TIM1_CHANNEL1 + PWM_TIM1_CHANNEL2 + \ + PWM_TIM1_CHANNEL3 + PWM_TIM1_CHANNEL4) + +#ifdef CONFIG_STM32L4_TIM2_CHANNEL1 +# ifdef CONFIG_STM32L4_TIM2_CH1OUT +# define PWM_TIM2_CH1CFG GPIO_TIM2_CH1OUT +# else +# define PWM_TIM2_CH1CFG 0 +# endif +# define PWM_TIM2_CHANNEL1 1 +#else +# define PWM_TIM2_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32L4_TIM2_CHANNEL2 +# ifdef CONFIG_STM32L4_TIM2_CH2OUT +# define PWM_TIM2_CH2CFG GPIO_TIM2_CH2OUT +# else +# define PWM_TIM2_CH2CFG 0 +# endif +# define PWM_TIM2_CHANNEL2 1 +#else +# define PWM_TIM2_CHANNEL2 0 +#endif +#ifdef CONFIG_STM32L4_TIM2_CHANNEL3 +# ifdef CONFIG_STM32L4_TIM2_CH3OUT +# define PWM_TIM2_CH3CFG GPIO_TIM2_CH3OUT +# else +# define PWM_TIM2_CH3CFG 0 +# endif +# define PWM_TIM2_CHANNEL3 1 +#else +# define PWM_TIM2_CHANNEL3 0 +#endif +#ifdef CONFIG_STM32L4_TIM2_CHANNEL4 +# ifdef CONFIG_STM32L4_TIM2_CH4OUT +# define PWM_TIM2_CH4CFG GPIO_TIM2_CH4OUT +# else +# define PWM_TIM2_CH4CFG 0 +# endif +# define PWM_TIM2_CHANNEL4 1 +#else +# define PWM_TIM2_CHANNEL4 0 +#endif +#define PWM_TIM2_NCHANNELS (PWM_TIM2_CHANNEL1 + PWM_TIM2_CHANNEL2 + \ + PWM_TIM2_CHANNEL3 + PWM_TIM2_CHANNEL4) + +#ifdef CONFIG_STM32L4_TIM3_CHANNEL1 +# ifdef CONFIG_STM32L4_TIM3_CH1OUT +# define PWM_TIM3_CH1CFG GPIO_TIM3_CH1OUT +# else +# define PWM_TIM3_CH1CFG 0 +# endif +# define PWM_TIM3_CHANNEL1 1 +#else +# define PWM_TIM3_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32L4_TIM3_CHANNEL2 +# ifdef CONFIG_STM32L4_TIM3_CH2OUT +# define PWM_TIM3_CH2CFG GPIO_TIM3_CH2OUT +# else +# define PWM_TIM3_CH2CFG 0 +# endif +# define PWM_TIM3_CHANNEL2 1 +#else +# define PWM_TIM3_CHANNEL2 0 +#endif +#ifdef CONFIG_STM32L4_TIM3_CHANNEL3 +# ifdef CONFIG_STM32L4_TIM3_CH3OUT +# define PWM_TIM3_CH3CFG GPIO_TIM3_CH3OUT +# else +# define PWM_TIM3_CH3CFG 0 +# endif +# define PWM_TIM3_CHANNEL3 1 +#else +# define PWM_TIM3_CHANNEL3 0 +#endif +#ifdef CONFIG_STM32L4_TIM3_CHANNEL4 +# ifdef CONFIG_STM32L4_TIM3_CH4OUT +# define PWM_TIM3_CH4CFG GPIO_TIM3_CH4OUT +# else +# define PWM_TIM3_CH4CFG 0 +# endif +# define PWM_TIM3_CHANNEL4 1 +#else +# define PWM_TIM3_CHANNEL4 0 +#endif +#define PWM_TIM3_NCHANNELS (PWM_TIM3_CHANNEL1 + PWM_TIM3_CHANNEL2 + \ + PWM_TIM3_CHANNEL3 + PWM_TIM3_CHANNEL4) + +#ifdef CONFIG_STM32L4_TIM4_CHANNEL1 +# ifdef CONFIG_STM32L4_TIM4_CH1OUT +# define PWM_TIM4_CH1CFG GPIO_TIM4_CH1OUT +# else +# define PWM_TIM4_CH1CFG 0 +# endif +# define PWM_TIM4_CHANNEL1 1 +#else +# define PWM_TIM4_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32L4_TIM4_CHANNEL2 +# ifdef CONFIG_STM32L4_TIM4_CH2OUT +# define PWM_TIM4_CH2CFG GPIO_TIM4_CH2OUT +# else +# define PWM_TIM4_CH2CFG 0 +# endif +# define PWM_TIM4_CHANNEL2 1 +#else +# define PWM_TIM4_CHANNEL2 0 +#endif +#ifdef CONFIG_STM32L4_TIM4_CHANNEL3 +# ifdef CONFIG_STM32L4_TIM4_CH3OUT +# define PWM_TIM4_CH3CFG GPIO_TIM4_CH3OUT +# else +# define PWM_TIM4_CH3CFG 0 +# endif +# define PWM_TIM4_CHANNEL3 1 +#else +# define PWM_TIM4_CHANNEL3 0 +#endif +#ifdef CONFIG_STM32L4_TIM4_CHANNEL4 +# ifdef CONFIG_STM32L4_TIM4_CH4OUT +# define PWM_TIM4_CH4CFG GPIO_TIM4_CH4OUT +# else +# define PWM_TIM4_CH4CFG 0 +# endif +# define PWM_TIM4_CHANNEL4 1 +#else +# define PWM_TIM4_CHANNEL4 0 +#endif +#define PWM_TIM4_NCHANNELS (PWM_TIM4_CHANNEL1 + PWM_TIM4_CHANNEL2 + \ + PWM_TIM4_CHANNEL3 + PWM_TIM4_CHANNEL4) + +#ifdef CONFIG_STM32L4_TIM5_CHANNEL1 +# ifdef CONFIG_STM32L4_TIM5_CH1OUT +# define PWM_TIM5_CH1CFG GPIO_TIM5_CH1OUT +# else +# define PWM_TIM5_CH1CFG 0 +# endif +# define PWM_TIM5_CHANNEL1 1 +#else +# define PWM_TIM5_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32L4_TIM5_CHANNEL2 +# ifdef CONFIG_STM32L4_TIM5_CH2OUT +# define PWM_TIM5_CH2CFG GPIO_TIM5_CH2OUT +# else +# define PWM_TIM5_CH2CFG 0 +# endif +# define PWM_TIM5_CHANNEL2 1 +#else +# define PWM_TIM5_CHANNEL2 0 +#endif +#ifdef CONFIG_STM32L4_TIM5_CHANNEL3 +# ifdef CONFIG_STM32L4_TIM5_CH3OUT +# define PWM_TIM5_CH3CFG GPIO_TIM5_CH3OUT +# else +# define PWM_TIM5_CH3CFG 0 +# endif +# define PWM_TIM5_CHANNEL3 1 +#else +# define PWM_TIM5_CHANNEL3 0 +#endif +#ifdef CONFIG_STM32L4_TIM5_CHANNEL4 +# ifdef CONFIG_STM32L4_TIM5_CH4OUT +# define PWM_TIM5_CH4CFG GPIO_TIM5_CH4OUT +# else +# define PWM_TIM5_CH4CFG 0 +# endif +# define PWM_TIM5_CHANNEL4 1 +#else +# define PWM_TIM5_CHANNEL4 0 +#endif +#define PWM_TIM5_NCHANNELS (PWM_TIM5_CHANNEL1 + PWM_TIM5_CHANNEL2 + \ + PWM_TIM5_CHANNEL3 + PWM_TIM5_CHANNEL4) + +#ifdef CONFIG_STM32L4_TIM8_CHANNEL1 +# ifdef CONFIG_STM32L4_TIM8_CH1OUT +# define PWM_TIM8_CH1CFG GPIO_TIM8_CH1OUT +# else +# define PWM_TIM8_CH1CFG 0 +# endif +# define PWM_TIM8_CHANNEL1 1 +#else +# define PWM_TIM8_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32L4_TIM8_CHANNEL2 +# ifdef CONFIG_STM32L4_TIM8_CH2OUT +# define PWM_TIM8_CH2CFG GPIO_TIM8_CH2OUT +# else +# define PWM_TIM8_CH2CFG 0 +# endif +# define PWM_TIM8_CHANNEL2 1 +#else +# define PWM_TIM8_CHANNEL2 0 +#endif +#ifdef CONFIG_STM32L4_TIM8_CHANNEL3 +# ifdef CONFIG_STM32L4_TIM8_CH3OUT +# define PWM_TIM8_CH3CFG GPIO_TIM8_CH3OUT +# else +# define PWM_TIM8_CH3CFG 0 +# endif +# define PWM_TIM8_CHANNEL3 1 +#else +# define PWM_TIM8_CHANNEL3 0 +#endif +#ifdef CONFIG_STM32L4_TIM8_CHANNEL4 +# ifdef CONFIG_STM32L4_TIM8_CH4OUT +# define PWM_TIM8_CH4CFG GPIO_TIM8_CH4OUT +# else +# define PWM_TIM8_CH4CFG 0 +# endif +# define PWM_TIM8_CHANNEL4 1 +#else +# define PWM_TIM8_CHANNEL4 0 +#endif +#define PWM_TIM8_NCHANNELS (PWM_TIM8_CHANNEL1 + PWM_TIM8_CHANNEL2 + \ + PWM_TIM8_CHANNEL3 + PWM_TIM8_CHANNEL4) + +#ifdef CONFIG_STM32L4_TIM15_CHANNEL1 +# ifdef CONFIG_STM32L4_TIM15_CH1OUT +# define PWM_TIM15_CH1CFG GPIO_TIM15_CH1OUT +# else +# define PWM_TIM15_CH1CFG 0 +# endif +# define PWM_TIM15_CHANNEL1 1 +#else +# define PWM_TIM15_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32L4_TIM15_CHANNEL2 +# ifdef CONFIG_STM32L4_TIM15_CH2OUT +# define PWM_TIM15_CH2CFG GPIO_TIM15_CH2OUT +# else +# define PWM_TIM15_CH2CFG 0 +# endif +# define PWM_TIM15_CHANNEL2 1 +#else +# define PWM_TIM15_CHANNEL2 0 +#endif +#define PWM_TIM15_NCHANNELS (PWM_TIM15_CHANNEL1 + PWM_TIM15_CHANNEL2) + +#ifdef CONFIG_STM32L4_TIM16_CHANNEL1 +# ifdef CONFIG_STM32L4_TIM16_CH1OUT +# define PWM_TIM16_CH1CFG GPIO_TIM16_CH1OUT +# else +# define PWM_TIM16_CH1CFG 0 +# endif +# define PWM_TIM16_CHANNEL1 1 +#else +# define PWM_TIM16_CHANNEL1 0 +#endif +#define PWM_TIM16_NCHANNELS PWM_TIM16_CHANNEL1 + +#ifdef CONFIG_STM32L4_TIM17_CHANNEL1 +# ifdef CONFIG_STM32L4_TIM17_CH1OUT +# define PWM_TIM17_CH1CFG GPIO_TIM17_CH1OUT +# else +# define PWM_TIM17_CH1CFG 0 +# endif +# define PWM_TIM17_CHANNEL1 1 +#else +# define PWM_TIM17_CHANNEL1 0 +#endif +#define PWM_TIM17_NCHANNELS PWM_TIM17_CHANNEL1 + +#define PWM_MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define PWM_NCHANNELS PWM_MAX(PWM_TIM1_NCHANNELS, \ + PWM_MAX(PWM_TIM2_NCHANNELS, \ + PWM_MAX(PWM_TIM3_NCHANNELS, \ + PWM_MAX(PWM_TIM4_NCHANNELS, \ + PWM_MAX(PWM_TIM5_NCHANNELS, \ + PWM_MAX(PWM_TIM8_NCHANNELS, \ + PWM_MAX(PWM_TIM15_NCHANNELS, \ + PWM_MAX(PWM_TIM16_NCHANNELS, \ + PWM_TIM17_NCHANNELS)))))))))))))) + +#else + +/* For each timer that is enabled for PWM usage, we need the following additional + * configuration settings: + * + * CONFIG_STM32L4_TIMx_CHANNEL - Specifies the timer output channel {1,..,4} + * PWM_TIMx_CHn - One of the values defined in chip/stm32*_pinmap.h. In the case + * where there are multiple pin selections, the correct setting must be provided + * in the arch/board/board.h file. + * + * NOTE: The STM32L4 timers are each capable of generating different signals on + * each of the four channels with different duty cycles. That capability is + * not supported by this driver: Only one output channel per timer. + */ + +#ifdef CONFIG_STM32L4_TIM1_PWM +# if !defined(CONFIG_STM32L4_TIM1_CHANNEL) +# error "CONFIG_STM32L4_TIM1_CHANNEL must be provided" +# elif CONFIG_STM32L4_TIM1_CHANNEL == 1 +# define CONFIG_STM32L4_TIM1_CHANNEL1 1 +# define CONFIG_STM32L4_TIM1_CH1MODE CONFIG_STM32L4_TIM1_CHMODE +# define PWM_TIM1_CH1CFG GPIO_TIM1_CH1OUT +# elif CONFIG_STM32L4_TIM1_CHANNEL == 2 +# define CONFIG_STM32L4_TIM1_CHANNEL2 1 +# define CONFIG_STM32L4_TIM1_CH2MODE CONFIG_STM32L4_TIM1_CHMODE +# define PWM_TIM1_CH2CFG GPIO_TIM1_CH2OUT +# elif CONFIG_STM32L4_TIM1_CHANNEL == 3 +# define CONFIG_STM32L4_TIM1_CHANNEL3 1 +# define CONFIG_STM32L4_TIM1_CH3MODE CONFIG_STM32L4_TIM1_CHMODE +# define PWM_TIM1_CH3CFG GPIO_TIM1_CH3OUT +# elif CONFIG_STM32L4_TIM1_CHANNEL == 4 +# define CONFIG_STM32L4_TIM1_CHANNEL4 1 +# define CONFIG_STM32L4_TIM1_CH4MODE CONFIG_STM32L4_TIM1_CHMODE +# define PWM_TIM1_CH4CFG GPIO_TIM1_CH4OUT +# else +# error "Unsupported value of CONFIG_STM32L4_TIM1_CHANNEL" +# endif +#endif + +#ifdef CONFIG_STM32L4_TIM2_PWM +# if !defined(CONFIG_STM32L4_TIM2_CHANNEL) +# error "CONFIG_STM32L4_TIM2_CHANNEL must be provided" +# elif CONFIG_STM32L4_TIM2_CHANNEL == 1 +# define CONFIG_STM32L4_TIM2_CHANNEL1 1 +# define CONFIG_STM32L4_TIM2_CH1MODE CONFIG_STM32L4_TIM2_CHMODE +# define PWM_TIM2_CH1CFG GPIO_TIM2_CH1OUT +# elif CONFIG_STM32L4_TIM2_CHANNEL == 2 +# define CONFIG_STM32L4_TIM2_CHANNEL2 1 +# define CONFIG_STM32L4_TIM2_CH2MODE CONFIG_STM32L4_TIM2_CHMODE +# define PWM_TIM2_CH2CFG GPIO_TIM2_CH2OUT +# elif CONFIG_STM32L4_TIM2_CHANNEL == 3 +# define CONFIG_STM32L4_TIM2_CHANNEL3 1 +# define CONFIG_STM32L4_TIM2_CH3MODE CONFIG_STM32L4_TIM2_CHMODE +# define PWM_TIM2_CH3CFG GPIO_TIM2_CH3OUT +# elif CONFIG_STM32L4_TIM2_CHANNEL == 4 +# define CONFIG_STM32L4_TIM2_CHANNEL4 1 +# define CONFIG_STM32L4_TIM2_CH4MODE CONFIG_STM32L4_TIM2_CHMODE +# define PWM_TIM2_CH4CFG GPIO_TIM2_CH4OUT +# else +# error "Unsupported value of CONFIG_STM32L4_TIM2_CHANNEL" +# endif +#endif + +#ifdef CONFIG_STM32L4_TIM3_PWM +# if !defined(CONFIG_STM32L4_TIM3_CHANNEL) +# error "CONFIG_STM32L4_TIM3_CHANNEL must be provided" +# elif CONFIG_STM32L4_TIM3_CHANNEL == 1 +# define CONFIG_STM32L4_TIM3_CHANNEL1 1 +# define CONFIG_STM32L4_TIM3_CH1MODE CONFIG_STM32L4_TIM3_CHMODE +# define PWM_TIM3_CH1CFG GPIO_TIM3_CH1OUT +# elif CONFIG_STM32L4_TIM3_CHANNEL == 2 +# define CONFIG_STM32L4_TIM3_CHANNEL2 1 +# define CONFIG_STM32L4_TIM3_CH2MODE CONFIG_STM32L4_TIM3_CHMODE +# define PWM_TIM3_CH2CFG GPIO_TIM3_CH2OUT +# elif CONFIG_STM32L4_TIM3_CHANNEL == 3 +# define CONFIG_STM32L4_TIM3_CHANNEL3 1 +# define CONFIG_STM32L4_TIM3_CH3MODE CONFIG_STM32L4_TIM3_CHMODE +# define PWM_TIM3_CH3CFG GPIO_TIM3_CH3OUT +# elif CONFIG_STM32L4_TIM3_CHANNEL == 4 +# define CONFIG_STM32L4_TIM3_CHANNEL4 1 +# define CONFIG_STM32L4_TIM3_CH4MODE CONFIG_STM32L4_TIM3_CHMODE +# define PWM_TIM3_CH4CFG GPIO_TIM3_CH4OUT +# else +# error "Unsupported value of CONFIG_STM32L4_TIM3_CHANNEL" +# endif +#endif + +#ifdef CONFIG_STM32L4_TIM4_PWM +# if !defined(CONFIG_STM32L4_TIM4_CHANNEL) +# error "CONFIG_STM32L4_TIM4_CHANNEL must be provided" +# elif CONFIG_STM32L4_TIM4_CHANNEL == 1 +# define CONFIG_STM32L4_TIM4_CHANNEL1 1 +# define CONFIG_STM32L4_TIM4_CH1MODE CONFIG_STM32L4_TIM4_CHMODE +# define PWM_TIM4_CH1CFG GPIO_TIM4_CH1OUT +# elif CONFIG_STM32L4_TIM4_CHANNEL == 2 +# define CONFIG_STM32L4_TIM4_CHANNEL2 1 +# define CONFIG_STM32L4_TIM4_CH2MODE CONFIG_STM32L4_TIM4_CHMODE +# define PWM_TIM4_CH2CFG GPIO_TIM4_CH2OUT +# elif CONFIG_STM32L4_TIM4_CHANNEL == 3 +# define CONFIG_STM32L4_TIM4_CHANNEL3 1 +# define CONFIG_STM32L4_TIM4_CH3MODE CONFIG_STM32L4_TIM4_CHMODE +# define PWM_TIM4_CH3CFG GPIO_TIM4_CH3OUT +# elif CONFIG_STM32L4_TIM4_CHANNEL == 4 +# define CONFIG_STM32L4_TIM4_CHANNEL4 1 +# define CONFIG_STM32L4_TIM4_CH4MODE CONFIG_STM32L4_TIM4_CHMODE +# define PWM_TIM4_CH4CFG GPIO_TIM4_CH4OUT +# else +# error "Unsupported value of CONFIG_STM32L4_TIM4_CHANNEL" +# endif +#endif + +#ifdef CONFIG_STM32L4_TIM5_PWM +# if !defined(CONFIG_STM32L4_TIM5_CHANNEL) +# error "CONFIG_STM32L4_TIM5_CHANNEL must be provided" +# elif CONFIG_STM32L4_TIM5_CHANNEL == 1 +# define CONFIG_STM32L4_TIM5_CHANNEL1 1 +# define CONFIG_STM32L4_TIM5_CH1MODE CONFIG_STM32L4_TIM5_CHMODE +# define PWM_TIM5_CH1CFG GPIO_TIM5_CH1OUT +# elif CONFIG_STM32L4_TIM5_CHANNEL == 2 +# define CONFIG_STM32L4_TIM5_CHANNEL2 1 +# define CONFIG_STM32L4_TIM5_CH2MODE CONFIG_STM32L4_TIM5_CHMODE +# define PWM_TIM5_CH2CFG GPIO_TIM5_CH2OUT +# elif CONFIG_STM32L4_TIM5_CHANNEL == 3 +# define CONFIG_STM32L4_TIM5_CHANNEL3 1 +# define CONFIG_STM32L4_TIM5_CH3MODE CONFIG_STM32L4_TIM5_CHMODE +# define PWM_TIM5_CH3CFG GPIO_TIM5_CH3OUT +# elif CONFIG_STM32L4_TIM5_CHANNEL == 4 +# define CONFIG_STM32L4_TIM5_CHANNEL4 1 +# define CONFIG_STM32L4_TIM5_CH4MODE CONFIG_STM32L4_TIM5_CHMODE +# define PWM_TIM5_CH4CFG GPIO_TIM5_CH4OUT +# else +# error "Unsupported value of CONFIG_STM32L4_TIM5_CHANNEL" +# endif +#endif + +#ifdef CONFIG_STM32L4_TIM8_PWM +# if !defined(CONFIG_STM32L4_TIM8_CHANNEL) +# error "CONFIG_STM32L4_TIM8_CHANNEL must be provided" +# elif CONFIG_STM32L4_TIM8_CHANNEL == 1 +# define CONFIG_STM32L4_TIM8_CHANNEL1 1 +# define CONFIG_STM32L4_TIM8_CH1MODE CONFIG_STM32L4_TIM8_CHMODE +# define PWM_TIM8_CH1CFG GPIO_TIM8_CH1OUT +# elif CONFIG_STM32L4_TIM8_CHANNEL == 2 +# define CONFIG_STM32L4_TIM8_CHANNEL2 1 +# define CONFIG_STM32L4_TIM8_CH2MODE CONFIG_STM32L4_TIM8_CHMODE +# define PWM_TIM8_CH2CFG GPIO_TIM8_CH2OUT +# elif CONFIG_STM32L4_TIM8_CHANNEL == 3 +# define CONFIG_STM32L4_TIM8_CHANNEL3 1 +# define CONFIG_STM32L4_TIM8_CH3MODE CONFIG_STM32L4_TIM8_CHMODE +# define PWM_TIM8_CH3CFG GPIO_TIM8_CH3OUT +# elif CONFIG_STM32L4_TIM8_CHANNEL == 4 +# define CONFIG_STM32L4_TIM8_CHANNEL4 1 +# define CONFIG_STM32L4_TIM8_CH4MODE CONFIG_STM32L4_TIM8_CHMODE +# define PWM_TIM8_CH4CFG GPIO_TIM8_CH4OUT +# else +# error "Unsupported value of CONFIG_STM32L4_TIM8_CHANNEL" +# endif +#endif + +#ifdef CONFIG_STM32L4_TIM15_PWM +# if !defined(CONFIG_STM32L4_TIM15_CHANNEL) +# error "CONFIG_STM32L4_TIM15_CHANNEL must be provided" +# elif CONFIG_STM32L4_TIM15_CHANNEL == 1 +# define CONFIG_STM32L4_TIM15_CHANNEL1 1 +# define CONFIG_STM32L4_TIM15_CH1MODE CONFIG_STM32L4_TIM15_CHMODE +# define PWM_TIM15_CH1CFG GPIO_TIM15_CH1OUT +# elif CONFIG_STM32L4_TIM15_CHANNEL == 2 +# define CONFIG_STM32L4_TIM15_CHANNEL2 1 +# define CONFIG_STM32L4_TIM15_CH2MODE CONFIG_STM32L4_TIM15_CHMODE +# define PWM_TIM15_CH2CFG GPIO_TIM15_CH2OUT +# else +# error "Unsupported value of CONFIG_STM32L4_TIM15_CHANNEL" +# endif +#endif + +#ifdef CONFIG_STM32L4_TIM16_PWM +# if !defined(CONFIG_STM32L4_TIM16_CHANNEL) +# error "CONFIG_STM32L4_TIM16_CHANNEL must be provided" +# elif CONFIG_STM32L4_TIM16_CHANNEL == 1 +# define CONFIG_STM32L4_TIM16_CHANNEL1 1 +# define CONFIG_STM32L4_TIM16_CH1MODE CONFIG_STM32L4_TIM16_CHMODE +# define PWM_TIM16_CH1CFG GPIO_TIM16_CH1OUT +# else +# error "Unsupported value of CONFIG_STM32L4_TIM16_CHANNEL" +# endif +#endif + +#ifdef CONFIG_STM32L4_TIM17_PWM +# if !defined(CONFIG_STM32L4_TIM17_CHANNEL) +# error "CONFIG_STM32L4_TIM17_CHANNEL must be provided" +# elif CONFIG_STM32L4_TIM17_CHANNEL == 1 +# define CONFIG_STM32L4_TIM17_CHANNEL1 1 +# define CONFIG_STM32L4_TIM17_CH1MODE CONFIG_STM32L4_TIM17_CHMODE +# define PWM_TIM17_CH1CFG GPIO_TIM17_CH1OUT +# else +# error "Unsupported value of CONFIG_STM32L4_TIM17_CHANNEL" +# endif +#endif + +#define PWM_NCHANNELS 1 + +#endif + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: stm32l4_pwminitialize + * + * Description: + * Initialize one 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,..,17}. + * + * 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_pwminitialize(int timer); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_STM32L4_TIMx_PWM */ +#endif /* __ARCH_ARM_SRC_STM32L4_STM32L4_PWM_H */ diff --git a/arch/arm/src/stm32l4/stm32l4_tickless.c b/arch/arm/src/stm32l4/stm32l4_tickless.c new file mode 100644 index 0000000000..123e93459d --- /dev/null +++ b/arch/arm/src/stm32l4/stm32l4_tickless.c @@ -0,0 +1,351 @@ +/**************************************************************************** + * arch/arm/src/stm32l4/stm32l4_tickless.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * dev@ziggurat29.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/**************************************************************************** + * Tickless OS Support. + * + * When CONFIG_SCHED_TICKLESS is enabled, all support for timer interrupts + * is suppressed and the platform specific code is expected to provide the + * following custom functions. + * + * void up_timer_initialize(void): Initializes the timer facilities. + * Called early in the initialization sequence (by up_intialize()). + * int up_timer_gettime(FAR struct timespec *ts): Returns the current + * time from the platform specific time source. + * int up_timer_cancel(void): Cancels the interval timer. + * int up_timer_start(FAR const struct timespec *ts): Start (or re-starts) + * the interval timer. + * + * The RTOS will provide the following interfaces for use by the platform- + * specific interval timer implementation: + * + * void sched_timer_expiration(void): Called by the platform-specific + * logic when the interval timer expires. + * + ****************************************************************************/ +/**************************************************************************** + * STM32L4 Timer Usage + * + * This current implementation uses two timers: A one-shot timer to provide + * the timed events and a free running timer to provide the current time. + * Since timers are a limited resource, that could be an issue on some + * systems. + * + * We could do the job with a single timer if we were to keep the single + * timer in a free-running at all times. The STM32 timer/counters have + * 16-bit/32-bit counters with the capability to generate a compare interrupt + * when the timer matches a compare value but also to continue counting + * without stopping (giving another, different interrupt when the timer + * rolls over from 0xffffffff to zero). So we could potentially just set + * the compare at the number of ticks you want PLUS the current value of + * timer. Then you could have both with a single timer: An interval timer + * and a free-running counter with the same timer! + * + * Patches are welcome! + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include + +#include "stm32l4_oneshot.h" +#include "stm32l4_freerun.h" + +#ifdef CONFIG_SCHED_TICKLESS + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_STM32L4_ONESHOT +# error CONFIG_STM32L4_ONESHOT must be selected for the Tickless OS option +#endif + +#ifndef CONFIG_STM32L4_FREERUN +# error CONFIG_STM32L4_FREERUN must be selected for the Tickless OS option +#endif + +#ifndef CONFIG_STM32L4_TICKLESS_FREERUN +# error CONFIG_STM32L4_TICKLESS_FREERUN must be selected for the Tickless OS option +#endif + +#ifndef CONFIG_STM32L4_TICKLESS_ONESHOT +# error CONFIG_STM32L4_TICKLESS_ONESHOT must be selected for the Tickless OS option +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct stm32_tickless_s +{ + struct stm32l4_oneshot_s oneshot; + struct stm32l4_freerun_s freerun; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct stm32_tickless_s g_tickless; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_oneshot_handler + * + * Description: + * Called when the one shot timer expires + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions: + * Called early in the initialization sequence before any special + * concurrency protections are required. + * + ****************************************************************************/ + +static void stm32_oneshot_handler(void *arg) +{ + tmrinfo("Expired...\n"); + sched_timer_expiration(); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_timer_initialize + * + * Description: + * Initializes all platform-specific timer facilities. This function is + * called early in the initialization sequence by up_intialize(). + * On return, the current up-time should be available from + * up_timer_gettime() and the interval timer is ready for use (but not + * actively timing. + * + * Provided by platform-specific code and called from the architecture- + * specific logic. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions: + * Called early in the initialization sequence before any special + * concurrency protections are required. + * + ****************************************************************************/ + +void up_timer_initialize(void) +{ +#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP + uint64_t max_delay; +#endif + int ret; + + /* Initialize the one-shot timer */ + + ret = stm32l4_oneshot_initialize(&g_tickless.oneshot, + CONFIG_STM32L4_TICKLESS_ONESHOT, + CONFIG_USEC_PER_TICK); + if (ret < 0) + { + tmrerr("ERROR: stm32_oneshot_initialize failed\n"); + PANIC(); + } + +#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP + /* Get the maximum delay of the one-shot timer in microseconds */ + + ret = stm32l4_oneshot_max_delay(&g_tickless.oneshot, &max_delay); + if (ret < 0) + { + tmrerr("ERROR: stm32_oneshot_max_delay failed\n"); + PANIC(); + } + + /* Convert this to configured clock ticks for use by the OS timer logic */ + + max_delay /= CONFIG_USEC_PER_TICK; + if (max_delay > UINT32_MAX) + { + g_oneshot_maxticks = UINT32_MAX; + } + else + { + g_oneshot_maxticks = max_delay; + } +#endif + + /* Initialize the free-running timer */ + + ret = stm32l4_freerun_initialize(&g_tickless.freerun, + CONFIG_STM32L4_TICKLESS_FREERUN, + CONFIG_USEC_PER_TICK); + if (ret < 0) + { + tmrerr("ERROR: stm32_freerun_initialize failed\n"); + PANIC(); + } +} + +/**************************************************************************** + * Name: up_timer_gettime + * + * Description: + * Return the elapsed time since power-up (or, more correctly, since + * up_timer_initialize() was called). This function is functionally + * equivalent to: + * + * int clock_gettime(clockid_t clockid, FAR struct timespec *ts); + * + * when clockid is CLOCK_MONOTONIC. + * + * This function provides the basis for reporting the current time and + * also is used to eliminate error build-up from small errors in interval + * time calculations. + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * ts - Provides the location in which to return the up-time. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + * Assumptions: + * Called from the the normal tasking context. The implementation must + * provide whatever mutual exclusion is necessary for correct operation. + * This can include disabling interrupts in order to assure atomic register + * operations. + * + ****************************************************************************/ + +int up_timer_gettime(FAR struct timespec *ts) +{ + return stm32l4_freerun_counter(&g_tickless.freerun, ts); +} + +/**************************************************************************** + * Name: up_timer_cancel + * + * Description: + * Cancel the interval timer and return the time remaining on the timer. + * These two steps need to be as nearly atomic as possible. + * sched_timer_expiration() will not be called unless the timer is + * restarted with up_timer_start(). + * + * If, as a race condition, the timer has already expired when this + * function is called, then that pending interrupt must be cleared so + * that up_timer_start() and the remaining time of zero should be + * returned. + * + * NOTE: This function may execute at a high rate with no timer running (as + * when pre-emption is enabled and disabled). + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * ts - Location to return the remaining time. Zero should be returned + * if the timer is not active. ts may be zero in which case the + * time remaining is not returned. + * + * Returned Value: + * Zero (OK) is returned on success. A call to up_timer_cancel() when + * the timer is not active should also return success; a negated errno + * value is returned on any failure. + * + * Assumptions: + * May be called from interrupt level handling or from the normal tasking + * level. Interrupts may need to be disabled internally to assure + * non-reentrancy. + * + ****************************************************************************/ + +int up_timer_cancel(FAR struct timespec *ts) +{ + return stm32l4_oneshot_cancel(&g_tickless.oneshot, ts); +} + +/**************************************************************************** + * Name: up_timer_start + * + * Description: + * Start the interval timer. sched_timer_expiration() will be + * called at the completion of the timeout (unless up_timer_cancel + * is called to stop the timing. + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * ts - Provides the time interval until sched_timer_expiration() is + * called. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + * Assumptions: + * May be called from interrupt level handling or from the normal tasking + * level. Interrupts may need to be disabled internally to assure + * non-reentrancy. + * + ****************************************************************************/ + +int up_timer_start(FAR const struct timespec *ts) +{ + return stm32l4_oneshot_start(&g_tickless.oneshot, stm32_oneshot_handler, NULL, ts); +} +#endif /* CONFIG_SCHED_TICKLESS */ diff --git a/arch/arm/src/stm32l4/stm32l4_tim.c b/arch/arm/src/stm32l4/stm32l4_tim.c new file mode 100644 index 0000000000..8d273fce1b --- /dev/null +++ b/arch/arm/src/stm32l4/stm32l4_tim.c @@ -0,0 +1,1463 @@ +/************************************************************************************ + * arm/arm/src/stm32l4/stm32l4_tim.c + * + * Copyright (C) 2011 Uros Platise. All rights reserved. + * Author: Uros Platise + * + * With modifications and updates by: + * + * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "chip.h" +#include "up_internal.h" +#include "up_arch.h" + +#include "stm32l4.h" +#include "stm32l4_gpio.h" +#include "stm32l4_tim.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Configuration ********************************************************************/ +/* Timer devices may be used for different purposes. Such special purposes include: + * + * - To generate modulated outputs for such things as motor control. If CONFIG_STM32L4_TIMn + * is defined then the CONFIG_STM32L4_TIMn_PWM may also be defined to indicate that + * the timer is intended to be used for pulsed output modulation. + * + * - To control periodic ADC input sampling. If CONFIG_STM32L4_TIMn is defined then + * CONFIG_STM32L4_TIMn_ADC may also be defined to indicate that timer "n" is intended + * to be used for that purpose. + * + * - To control periodic DAC outputs. If CONFIG_STM32L4_TIMn is defined then + * CONFIG_STM32L4_TIMn_DAC may also be defined to indicate that timer "n" is intended + * to be used for that purpose. + * + * - To use a Quadrature Encoder. If CONFIG_STM32L4_TIMn is defined then + * CONFIG_STM32L4_TIMn_QE may also be defined to indicate that timer "n" is intended + * to be used for that purpose. + * + * In any of these cases, the timer will not be used by this timer module. + */ + +#if defined(CONFIG_STM32L4_TIM1_PWM) || defined (CONFIG_STM32L4_TIM1_ADC) || \ + defined(CONFIG_STM32L4_TIM1_DAC) || defined(CONFIG_STM32L4_TIM1_QE) +# undef CONFIG_STM32L4_TIM1 +#endif +#if defined(CONFIG_STM32L4_TIM2_PWM) || defined (CONFIG_STM32L4_TIM2_ADC) || \ + defined(CONFIG_STM32L4_TIM2_DAC) || defined(CONFIG_STM32L4_TIM2_QE) +# undef CONFIG_STM32L4_TIM2 +#endif +#if defined(CONFIG_STM32L4_TIM3_PWM) || defined (CONFIG_STM32L4_TIM3_ADC) || \ + defined(CONFIG_STM32L4_TIM3_DAC) || defined(CONFIG_STM32L4_TIM3_QE) +# undef CONFIG_STM32L4_TIM3 +#endif +#if defined(CONFIG_STM32L4_TIM4_PWM) || defined (CONFIG_STM32L4_TIM4_ADC) || \ + defined(CONFIG_STM32L4_TIM4_DAC) || defined(CONFIG_STM32L4_TIM4_QE) +# undef CONFIG_STM32L4_TIM4 +#endif +#if defined(CONFIG_STM32L4_TIM5_PWM) || defined (CONFIG_STM32L4_TIM5_ADC) || \ + defined(CONFIG_STM32L4_TIM5_DAC) || defined(CONFIG_STM32L4_TIM5_QE) +# undef CONFIG_STM32L4_TIM5 +#endif +#if defined(CONFIG_STM32L4_TIM6_PWM) || defined (CONFIG_STM32L4_TIM6_ADC) || \ + defined(CONFIG_STM32L4_TIM6_DAC) || defined(CONFIG_STM32L4_TIM6_QE) +# undef CONFIG_STM32L4_TIM6 +#endif +#if defined(CONFIG_STM32L4_TIM7_PWM) || defined (CONFIG_STM32L4_TIM7_ADC) || \ + defined(CONFIG_STM32L4_TIM7_DAC) || defined(CONFIG_STM32L4_TIM7_QE) +# undef CONFIG_STM32L4_TIM7 +#endif +#if defined(CONFIG_STM32L4_TIM8_PWM) || defined (CONFIG_STM32L4_TIM8_ADC) || \ + defined(CONFIG_STM32L4_TIM8_DAC) || defined(CONFIG_STM32L4_TIM8_QE) +# undef CONFIG_STM32L4_TIM8 +#endif +#if defined(CONFIG_STM32L4_TIM15_PWM) || defined (CONFIG_STM32L4_TIM15_ADC) || \ + defined(CONFIG_STM32L4_TIM15_DAC) || defined(CONFIG_STM32L4_TIM15_QE) +# undef CONFIG_STM32L4_TIM15 +#endif +#if defined(CONFIG_STM32L4_TIM16_PWM) || defined (CONFIG_STM32L4_TIM16_ADC) || \ + defined(CONFIG_STM32L4_TIM16_DAC) || defined(CONFIG_STM32L4_TIM16_QE) +# undef CONFIG_STM32L4_TIM16 +#endif +#if defined(CONFIG_STM32L4_TIM17_PWM) || defined (CONFIG_STM32L4_TIM17_ADC) || \ + defined(CONFIG_STM32L4_TIM17_DAC) || defined(CONFIG_STM32L4_TIM17_QE) +# undef CONFIG_STM32L4_TIM17 +#endif + +#if defined(CONFIG_STM32L4_TIM1) +# if defined(GPIO_TIM1_CH1OUT) ||defined(GPIO_TIM1_CH2OUT)||\ + defined(GPIO_TIM1_CH3OUT) ||defined(GPIO_TIM1_CH4OUT) +# define HAVE_TIM1_GPIOCONFIG 1 +#endif +#endif + +#if defined(CONFIG_STM32L4_TIM2) +# if defined(GPIO_TIM2_CH1OUT) ||defined(GPIO_TIM2_CH2OUT)||\ + defined(GPIO_TIM2_CH3OUT) ||defined(GPIO_TIM2_CH4OUT) +# define HAVE_TIM2_GPIOCONFIG 1 +#endif +#endif + +#if defined(CONFIG_STM32L4_TIM3) +# if defined(GPIO_TIM3_CH1OUT) ||defined(GPIO_TIM3_CH2OUT)||\ + defined(GPIO_TIM3_CH3OUT) ||defined(GPIO_TIM3_CH4OUT) +# define HAVE_TIM3_GPIOCONFIG 1 +#endif +#endif + +#if defined(CONFIG_STM32L4_TIM4) +# if defined(GPIO_TIM4_CH1OUT) ||defined(GPIO_TIM4_CH2OUT)||\ + defined(GPIO_TIM4_CH3OUT) ||defined(GPIO_TIM4_CH4OUT) +# define HAVE_TIM4_GPIOCONFIG 1 +#endif +#endif + +#if defined(CONFIG_STM32L4_TIM5) +# if defined(GPIO_TIM5_CH1OUT) ||defined(GPIO_TIM5_CH2OUT)||\ + defined(GPIO_TIM5_CH3OUT) ||defined(GPIO_TIM5_CH4OUT) +# define HAVE_TIM5_GPIOCONFIG 1 +#endif +#endif + +#if defined(CONFIG_STM32L4_TIM8) +# if defined(GPIO_TIM8_CH1OUT) ||defined(GPIO_TIM8_CH2OUT)||\ + defined(GPIO_TIM8_CH3OUT) ||defined(GPIO_TIM8_CH4OUT) +# define HAVE_TIM8_GPIOCONFIG 1 +#endif +#endif + +#if defined(CONFIG_STM32L4_TIM15) +# if defined(GPIO_TIM15_CH1OUT) ||defined(GPIO_TIM15_CH2OUT)||\ + defined(GPIO_TIM15_CH3OUT) ||defined(GPIO_TIM15_CH4OUT) +# define HAVE_TIM15_GPIOCONFIG 1 +#endif +#endif + +#if defined(CONFIG_STM32L4_TIM16) +# if defined(GPIO_TIM16_CH1OUT) ||defined(GPIO_TIM16_CH2OUT)||\ + defined(GPIO_TIM16_CH3OUT) ||defined(GPIO_TIM16_CH4OUT) +# define HAVE_TIM16_GPIOCONFIG 1 +#endif +#endif + +#if defined(CONFIG_STM32L4_TIM17) +# if defined(GPIO_TIM17_CH1OUT) ||defined(GPIO_TIM17_CH2OUT)||\ + defined(GPIO_TIM17_CH3OUT) ||defined(GPIO_TIM17_CH4OUT) +# define HAVE_TIM17_GPIOCONFIG 1 +#endif +#endif + +/* This module then only compiles if there are enabled timers that are not intended for + * some other purpose. + */ + +#if defined(CONFIG_STM32L4_TIM1) || defined(CONFIG_STM32L4_TIM2) || \ + defined(CONFIG_STM32L4_TIM3) || defined(CONFIG_STM32L4_TIM4) || \ + defined(CONFIG_STM32L4_TIM5) || defined(CONFIG_STM32L4_TIM6) || \ + defined(CONFIG_STM32L4_TIM7) || defined(CONFIG_STM32L4_TIM8) || \ + defined(CONFIG_STM32L4_TIM15) || defined(CONFIG_STM32L4_TIM16) || \ + defined(CONFIG_STM32L4_TIM17) + +/************************************************************************************ + * Private Types + ************************************************************************************/ + +/* TIM Device Structure */ + +struct stm32_tim_priv_s +{ + const struct stm32l4_tim_ops_s *ops; + stm32l4_tim_mode_t mode; + uint32_t base; /* TIMn base address */ +}; + +/************************************************************************************ + * Private Function prototypes + ************************************************************************************/ + +/* Register helpers */ + +static inline uint16_t stm32_getreg16(FAR struct stm32l4_tim_dev_s *dev, + uint8_t offset); +static inline void stm32_putreg16(FAR struct stm32l4_tim_dev_s *dev, uint8_t offset, + uint16_t value); +static inline void stm32_modifyreg16(FAR struct stm32l4_tim_dev_s *dev, + uint8_t offset, uint16_t clearbits, + uint16_t setbits); +static inline uint32_t stm32_getreg32(FAR struct stm32l4_tim_dev_s *dev, + uint8_t offset); +static inline void stm32_putreg32(FAR struct stm32l4_tim_dev_s *dev, uint8_t offset, + uint32_t value); + +/* Timer helpers */ + +static void stm32_tim_reload_counter(FAR struct stm32l4_tim_dev_s *dev); +static void stm32_tim_enable(FAR struct stm32l4_tim_dev_s *dev); +static void stm32_tim_disable(FAR struct stm32l4_tim_dev_s *dev); +static void stm32_tim_reset(FAR struct stm32l4_tim_dev_s *dev); +#if defined(HAVE_TIM1_GPIOCONFIG)||defined(HAVE_TIM2_GPIOCONFIG)||\ + defined(HAVE_TIM3_GPIOCONFIG)||defined(HAVE_TIM4_GPIOCONFIG)||\ + defined(HAVE_TIM5_GPIOCONFIG)||defined(HAVE_TIM8_GPIOCONFIG) +static void stm32_tim_gpioconfig(uint32_t cfg, stm32l4_tim_channel_t mode); +#endif + +/* Timer methods */ + +static int stm32_tim_setmode(FAR struct stm32l4_tim_dev_s *dev, stm32l4_tim_mode_t mode); +static int stm32_tim_setclock(FAR struct stm32l4_tim_dev_s *dev, uint32_t freq); +static void stm32_tim_setperiod(FAR struct stm32l4_tim_dev_s *dev, + uint32_t period); +static uint32_t stm32_tim_getcounter(FAR struct stm32l4_tim_dev_s *dev); +static int stm32_tim_setchannel(FAR struct stm32l4_tim_dev_s *dev, uint8_t channel, + stm32l4_tim_channel_t mode); +static int stm32_tim_setcompare(FAR struct stm32l4_tim_dev_s *dev, uint8_t channel, + uint32_t compare); +static int stm32_tim_getcapture(FAR struct stm32l4_tim_dev_s *dev, uint8_t channel); +static int stm32_tim_setisr(FAR struct stm32l4_tim_dev_s *dev, + int (*handler)(int irq, void *context), + int source); +static void stm32_tim_enableint(FAR struct stm32l4_tim_dev_s *dev, int source); +static void stm32_tim_disableint(FAR struct stm32l4_tim_dev_s *dev, int source); +static void stm32_tim_ackint(FAR struct stm32l4_tim_dev_s *dev, int source); +static int stm32_tim_checkint(FAR struct stm32l4_tim_dev_s *dev, int source); + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +static const struct stm32l4_tim_ops_s stm32_tim_ops = +{ + .setmode = stm32_tim_setmode, + .setclock = stm32_tim_setclock, + .setperiod = stm32_tim_setperiod, + .getcounter = stm32_tim_getcounter, + .setchannel = stm32_tim_setchannel, + .setcompare = stm32_tim_setcompare, + .getcapture = stm32_tim_getcapture, + .setisr = stm32_tim_setisr, + .enableint = stm32_tim_enableint, + .disableint = stm32_tim_disableint, + .ackint = stm32_tim_ackint, + .checkint = stm32_tim_checkint, +}; + +#ifdef CONFIG_STM32L4_TIM1 +struct stm32_tim_priv_s stm32_tim1_priv = +{ + .ops = &stm32_tim_ops, + .mode = STM32L4_TIM_MODE_UNUSED, + .base = STM32L4_TIM1_BASE, +}; +#endif +#ifdef CONFIG_STM32L4_TIM2 +struct stm32_tim_priv_s stm32_tim2_priv = +{ + .ops = &stm32_tim_ops, + .mode = STM32L4_TIM_MODE_UNUSED, + .base = STM32L4_TIM2_BASE, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM3 +struct stm32_tim_priv_s stm32_tim3_priv = +{ + .ops = &stm32_tim_ops, + .mode = STM32L4_TIM_MODE_UNUSED, + .base = STM32L4_TIM3_BASE, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM4 +struct stm32_tim_priv_s stm32_tim4_priv = +{ + .ops = &stm32_tim_ops, + .mode = STM32L4_TIM_MODE_UNUSED, + .base = STM32L4_TIM4_BASE, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM5 +struct stm32_tim_priv_s stm32_tim5_priv = +{ + .ops = &stm32_tim_ops, + .mode = STM32L4_TIM_MODE_UNUSED, + .base = STM32L4_TIM5_BASE, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM6 +struct stm32_tim_priv_s stm32_tim6_priv = +{ + .ops = &stm32_tim_ops, + .mode = STM32L4_TIM_MODE_UNUSED, + .base = STM32L4_TIM6_BASE, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM7 +struct stm32_tim_priv_s stm32_tim7_priv = +{ + .ops = &stm32_tim_ops, + .mode = STM32L4_TIM_MODE_UNUSED, + .base = STM32L4_TIM7_BASE, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM8 +struct stm32_tim_priv_s stm32_tim8_priv = +{ + .ops = &stm32_tim_ops, + .mode = STM32L4_TIM_MODE_UNUSED, + .base = STM32L4_TIM8_BASE, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM15 +struct stm32_tim_priv_s stm32_tim15_priv = +{ + .ops = &stm32_tim_ops, + .mode = STM32L4_TIM_MODE_UNUSED, + .base = STM32L4_TIM15_BASE, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM16 +struct stm32_tim_priv_s stm32_tim16_priv = +{ + .ops = &stm32_tim_ops, + .mode = STM32L4_TIM_MODE_UNUSED, + .base = STM32L4_TIM16_BASE, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM17 +struct stm32_tim_priv_s stm32_tim17_priv = +{ + .ops = &stm32_tim_ops, + .mode = STM32L4_TIM_MODE_UNUSED, + .base = STM32L4_TIM17_BASE, +}; +#endif + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: stm32_getreg16 + * + * Description: + * Get a 16-bit register value by offset + * + ************************************************************************************/ + +static inline uint16_t stm32_getreg16(FAR struct stm32l4_tim_dev_s *dev, + uint8_t offset) +{ + return getreg16(((struct stm32_tim_priv_s *)dev)->base + offset); +} + +/************************************************************************************ + * Name: stm32_putreg16 + * + * Description: + * Put a 16-bit register value by offset + * + ************************************************************************************/ + +static inline void stm32_putreg16(FAR struct stm32l4_tim_dev_s *dev, uint8_t offset, + uint16_t value) +{ + putreg16(value, ((struct stm32_tim_priv_s *)dev)->base + offset); +} + +/************************************************************************************ + * Name: stm32_modifyreg16 + * + * Description: + * Modify a 16-bit register value by offset + * + ************************************************************************************/ + +static inline void stm32_modifyreg16(FAR struct stm32l4_tim_dev_s *dev, + uint8_t offset, uint16_t clearbits, + uint16_t setbits) +{ + modifyreg16(((struct stm32_tim_priv_s *)dev)->base + offset, clearbits, setbits); +} + +/************************************************************************************ + * Name: stm32_getreg32 + * + * Description: + * Get a 32-bit register value by offset. This applies only for the STM32 F4 + * 32-bit registers (CNT, ARR, CRR1-4) in the 32-bit timers TIM2-5. + * + ************************************************************************************/ + +static inline uint32_t stm32_getreg32(FAR struct stm32l4_tim_dev_s *dev, + uint8_t offset) +{ + return getreg32(((struct stm32_tim_priv_s *)dev)->base + offset); +} + +/************************************************************************************ + * Name: stm32_putreg32 + * + * Description: + * Put a 32-bit register value by offset. This applies only for the STM32 F4 + * 32-bit registers (CNT, ARR, CRR1-4) in the 32-bit timers TIM2-5. + * + ************************************************************************************/ + +static inline void stm32_putreg32(FAR struct stm32l4_tim_dev_s *dev, uint8_t offset, + uint32_t value) +{ + putreg32(value, ((struct stm32_tim_priv_s *)dev)->base + offset); +} + +/************************************************************************************ + * Name: stm32_tim_reload_counter + ************************************************************************************/ + +static void stm32_tim_reload_counter(FAR struct stm32l4_tim_dev_s *dev) +{ + uint16_t val = stm32_getreg16(dev, STM32L4_BTIM_EGR_OFFSET); + val |= ATIM_EGR_UG; + stm32_putreg16(dev, STM32L4_BTIM_EGR_OFFSET, val); +} + +/************************************************************************************ + * Name: stm32_tim_enable + ************************************************************************************/ + +static void stm32_tim_enable(FAR struct stm32l4_tim_dev_s *dev) +{ + uint16_t val = stm32_getreg16(dev, STM32L4_BTIM_CR1_OFFSET); + val |= ATIM_CR1_CEN; + stm32_tim_reload_counter(dev); + stm32_putreg16(dev, STM32L4_BTIM_CR1_OFFSET, val); +} + +/************************************************************************************ + * Name: stm32_tim_disable + ************************************************************************************/ + +static void stm32_tim_disable(FAR struct stm32l4_tim_dev_s *dev) +{ + uint16_t val = stm32_getreg16(dev, STM32L4_BTIM_CR1_OFFSET); + val &= ~ATIM_CR1_CEN; + stm32_putreg16(dev, STM32L4_BTIM_CR1_OFFSET, val); +} + +/************************************************************************************ + * Name: stm32_tim_reset + * + * Description: + * Reset timer into system default state, but do not affect output/input pins + * + ************************************************************************************/ + +static void stm32_tim_reset(FAR struct stm32l4_tim_dev_s *dev) +{ + ((struct stm32_tim_priv_s *)dev)->mode = STM32L4_TIM_MODE_DISABLED; + stm32_tim_disable(dev); +} + +/************************************************************************************ + * Name: stm32_tim_gpioconfig + ************************************************************************************/ + +#if defined(HAVE_TIM1_GPIOCONFIG)||defined(HAVE_TIM2_GPIOCONFIG)||\ + defined(HAVE_TIM3_GPIOCONFIG)||defined(HAVE_TIM4_GPIOCONFIG)||\ + defined(HAVE_TIM5_GPIOCONFIG)||defined(HAVE_TIM8_GPIOCONFIG) +static void stm32_tim_gpioconfig(uint32_t cfg, stm32l4_tim_channel_t mode) +{ + /* TODO: Add support for input capture and bipolar dual outputs for TIM8 */ + + if (mode & STM32L4_TIM_CH_MODE_MASK) + { + stm32_configgpio(cfg); + } + else + { + stm32_unconfiggpio(cfg); + } +} +#endif + +/************************************************************************************ + * Name: stm32_tim_setmode + ************************************************************************************/ + +static int stm32_tim_setmode(FAR struct stm32l4_tim_dev_s *dev, stm32l4_tim_mode_t mode) +{ + uint16_t val = ATIM_CR1_CEN | ATIM_CR1_ARPE; + + DEBUGASSERT(dev != NULL); + + /* This function is not supported on basic timers. To enable or + * disable it, simply set its clock to valid frequency or zero. + */ + +#if STM32L4_NBTIM > 0 + if (((struct stm32_tim_priv_s *)dev)->base == STM32L4_TIM6_BASE +#endif +#if STM32L4_NBTIM > 1 + || ((struct stm32_tim_priv_s *)dev)->base == STM32L4_TIM7_BASE +#endif +#if STM32L4_NBTIM > 0 + ) + { + return -EINVAL; + } +#endif + + /* Decode operational modes */ + + switch (mode & STM32L4_TIM_MODE_MASK) + { + case STM32L4_TIM_MODE_DISABLED: + val = 0; + break; + + case STM32L4_TIM_MODE_DOWN: + val |= ATIM_CR1_DIR; + + case STM32L4_TIM_MODE_UP: + break; + + case STM32L4_TIM_MODE_UPDOWN: + val |= ATIM_CR1_CENTER1; + // Our default: Interrupts are generated on compare, when counting down + break; + + case STM32L4_TIM_MODE_PULSE: + val |= ATIM_CR1_OPM; + break; + + default: + return -EINVAL; + } + + stm32_tim_reload_counter(dev); + stm32_putreg16(dev, STM32L4_BTIM_CR1_OFFSET, val); + +#if STM32L4_NATIM > 0 + /* Advanced registers require Main Output Enable */ + + if (((struct stm32_tim_priv_s *)dev)->base == STM32L4_TIM1_BASE || + ((struct stm32_tim_priv_s *)dev)->base == STM32L4_TIM8_BASE) + { + stm32_modifyreg16(dev, STM32L4_ATIM_BDTR_OFFSET, 0, ATIM_BDTR_MOE); + } +#endif + + return OK; +} + +/************************************************************************************ + * Name: stm32_tim_setclock + ************************************************************************************/ + +static int stm32_tim_setclock(FAR struct stm32l4_tim_dev_s *dev, uint32_t freq) +{ + uint32_t freqin; + int prescaler; + + DEBUGASSERT(dev != NULL); + + /* Disable Timer? */ + + if (freq == 0) + { + stm32_tim_disable(dev); + return 0; + } + + /* Get the input clock frequency for this timer. These vary with + * different timer clock sources, MCU-specific timer configuration, and + * board-specific clock configuration. The correct input clock frequency + * must be defined in the board.h header file. + */ + + switch (((struct stm32_tim_priv_s *)dev)->base) + { +#ifdef CONFIG_STM32L4_TIM1 + case STM32L4_TIM1_BASE: + freqin = BOARD_TIM1_FREQUENCY; + break; +#endif +#ifdef CONFIG_STM32L4_TIM2 + case STM32L4_TIM2_BASE: + freqin = BOARD_TIM2_FREQUENCY; + break; +#endif +#ifdef CONFIG_STM32L4_TIM3 + case STM32L4_TIM3_BASE: + freqin = BOARD_TIM3_FREQUENCY; + break; +#endif +#ifdef CONFIG_STM32L4_TIM4 + case STM32L4_TIM4_BASE: + freqin = BOARD_TIM4_FREQUENCY; + break; +#endif +#ifdef CONFIG_STM32L4_TIM5 + case STM32L4_TIM5_BASE: + freqin = BOARD_TIM5_FREQUENCY; + break; +#endif +#ifdef CONFIG_STM32L4_TIM6 + case STM32L4_TIM6_BASE: + freqin = BOARD_TIM6_FREQUENCY; + break; +#endif +#ifdef CONFIG_STM32L4_TIM7 + case STM32L4_TIM7_BASE: + freqin = BOARD_TIM7_FREQUENCY; + break; +#endif +#ifdef CONFIG_STM32L4_TIM8 + case STM32L4_TIM8_BASE: + freqin = BOARD_TIM8_FREQUENCY; + break; +#endif +#ifdef CONFIG_STM32L4_TIM15 + case STM32L4_TIM15_BASE: + freqin = BOARD_TIM15_FREQUENCY; + break; +#endif +#ifdef CONFIG_STM32L4_TIM16 + case STM32L4_TIM16_BASE: + freqin = BOARD_TIM16_FREQUENCY; + break; +#endif +#ifdef CONFIG_STM32L4_TIM17 + case STM32L4_TIM17_BASE: + freqin = BOARD_TIM17_FREQUENCY; + break; +#endif + + default: + return -EINVAL; + } + + /* Select a pre-scaler value for this timer using the input clock + * frequency. + */ + + prescaler = freqin / freq; + + /* We need to decrement value for '1', but only, if that will not to + * cause underflow. + */ + + if (prescaler > 0) + { + prescaler--; + } + + /* Check for overflow as well. */ + + if (prescaler > 0xffff) + { + prescaler = 0xffff; + } + + stm32_putreg16(dev, STM32L4_BTIM_PSC_OFFSET, prescaler); + stm32_tim_enable(dev); + + return prescaler; +} + +/************************************************************************************ + * Name: stm32_tim_setperiod + ************************************************************************************/ + +static void stm32_tim_setperiod(FAR struct stm32l4_tim_dev_s *dev, + uint32_t period) +{ + DEBUGASSERT(dev != NULL); + stm32_putreg32(dev, STM32L4_BTIM_ARR_OFFSET, period); +} + +/************************************************************************************ + * Name: stm32_tim_getcounter + ************************************************************************************/ + +static uint32_t stm32_tim_getcounter(FAR struct stm32l4_tim_dev_s *dev) +{ + DEBUGASSERT(dev != NULL); + return stm32_getreg32(dev, STM32L4_BTIM_CNT_OFFSET); +} + +/************************************************************************************ + * Name: stm32_tim_setchannel + ************************************************************************************/ + +static int stm32_tim_setchannel(FAR struct stm32l4_tim_dev_s *dev, uint8_t channel, + stm32l4_tim_channel_t mode) +{ + uint16_t ccmr_orig = 0; + uint16_t ccmr_val = 0; + uint16_t ccmr_mask = 0xff; + uint16_t ccer_val = stm32_getreg16(dev, STM32L4_GTIM_CCER_OFFSET); + uint8_t ccmr_offset = STM32L4_GTIM_CCMR1_OFFSET; + + DEBUGASSERT(dev != NULL); + + /* Further we use range as 0..3; if channel=0 it will also overflow here */ + + if (--channel > 4) + { + return -EINVAL; + } + + /* Assume that channel is disabled and polarity is active high */ + + ccer_val &= ~(3 << (channel << 2)); + + /* This function is not supported on basic timers. To enable or + * disable it, simply set its clock to valid frequency or zero. + */ + +#if STM32L4_NBTIM > 0 + if (((struct stm32_tim_priv_s *)dev)->base == STM32L4_TIM6_BASE +#endif +#if STM32L4_NBTIM > 1 + || ((struct stm32_tim_priv_s *)dev)->base == STM32L4_TIM7_BASE +#endif +#if STM32L4_NBTIM > 0 + ) + { + return -EINVAL; + } +#endif + + /* Decode configuration */ + + switch (mode & STM32L4_TIM_CH_MODE_MASK) + { + case STM32L4_TIM_CH_DISABLED: + break; + + case STM32L4_TIM_CH_OUTPWM: + ccmr_val = (ATIM_CCMR_MODE_PWM1 << ATIM_CCMR1_OC1M_SHIFT) + ATIM_CCMR1_OC1PE; + ccer_val |= ATIM_CCER_CC1E << (channel << 2); + break; + + default: + return -EINVAL; + } + + /* Set polarity */ + + if (mode & STM32L4_TIM_CH_POLARITY_NEG) + { + ccer_val |= ATIM_CCER_CC1P << (channel << 2); + } + + /* Define its position (shift) and get register offset */ + + if (channel & 1) + { + ccmr_val <<= 8; + ccmr_mask <<= 8; + } + + if (channel > 1) + { + ccmr_offset = STM32L4_GTIM_CCMR2_OFFSET; + } + + ccmr_orig = stm32_getreg16(dev, ccmr_offset); + ccmr_orig &= ~ccmr_mask; + ccmr_orig |= ccmr_val; + stm32_putreg16(dev, ccmr_offset, ccmr_orig); + stm32_putreg16(dev, STM32L4_GTIM_CCER_OFFSET, ccer_val); + + /* set GPIO */ + + switch (((struct stm32_tim_priv_s *)dev)->base) + { +#ifdef CONFIG_STM32L4_TIM1 + case STM32L4_TIM1_BASE: + switch (channel) + { +#if defined(GPIO_TIM1_CH1OUT) + case 0: + stm32_tim_gpioconfig(GPIO_TIM1_CH1OUT, mode); break; +#endif +#if defined(GPIO_TIM1_CH2OUT) + case 1: + stm32_tim_gpioconfig(GPIO_TIM1_CH2OUT, mode); break; +#endif +#if defined(GPIO_TIM1_CH3OUT) + case 2: + stm32_tim_gpioconfig(GPIO_TIM1_CH3OUT, mode); break; +#endif +#if defined(GPIO_TIM1_CH4OUT) + case 3: + stm32_tim_gpioconfig(GPIO_TIM1_CH4OUT, mode); break; +#endif + default: + return -EINVAL; + } + break; +#endif +#ifdef CONFIG_STM32L4_TIM2 + case STM32L4_TIM2_BASE: + switch (channel) + { +#if defined(GPIO_TIM2_CH1OUT) + case 0: + stm32_tim_gpioconfig(GPIO_TIM2_CH1OUT, mode); + break; +#endif +#if defined(GPIO_TIM2_CH2OUT) + case 1: + stm32_tim_gpioconfig(GPIO_TIM2_CH2OUT, mode); + break; +#endif +#if defined(GPIO_TIM2_CH3OUT) + case 2: + stm32_tim_gpioconfig(GPIO_TIM2_CH3OUT, mode); + break; +#endif +#if defined(GPIO_TIM2_CH4OUT) + case 3: + stm32_tim_gpioconfig(GPIO_TIM2_CH4OUT, mode); + break; +#endif + default: + return -EINVAL; + } + break; +#endif +#ifdef CONFIG_STM32L4_TIM3 + case STM32L4_TIM3_BASE: + switch (channel) + { +#if defined(GPIO_TIM3_CH1OUT) + case 0: + stm32_tim_gpioconfig(GPIO_TIM3_CH1OUT, mode); + break; +#endif +#if defined(GPIO_TIM3_CH2OUT) + case 1: + stm32_tim_gpioconfig(GPIO_TIM3_CH2OUT, mode); + break; +#endif +#if defined(GPIO_TIM3_CH3OUT) + case 2: + stm32_tim_gpioconfig(GPIO_TIM3_CH3OUT, mode); + break; +#endif +#if defined(GPIO_TIM3_CH4OUT) + case 3: + stm32_tim_gpioconfig(GPIO_TIM3_CH4OUT, mode); + break; +#endif + default: + return -EINVAL; + } + break; +#endif +#ifdef CONFIG_STM32L4_TIM4 + case STM32L4_TIM4_BASE: + switch (channel) + { +#if defined(GPIO_TIM4_CH1OUT) + case 0: + stm32_tim_gpioconfig(GPIO_TIM4_CH1OUT, mode); + break; +#endif +#if defined(GPIO_TIM4_CH2OUT) + case 1: + stm32_tim_gpioconfig(GPIO_TIM4_CH2OUT, mode); + break; +#endif +#if defined(GPIO_TIM4_CH3OUT) + case 2: + stm32_tim_gpioconfig(GPIO_TIM4_CH3OUT, mode); + break; +#endif +#if defined(GPIO_TIM4_CH4OUT) + case 3: + stm32_tim_gpioconfig(GPIO_TIM4_CH4OUT, mode); + break; +#endif + default: + return -EINVAL; + } + break; +#endif +#ifdef CONFIG_STM32L4_TIM5 + case STM32L4_TIM5_BASE: + switch (channel) + { +#if defined(GPIO_TIM5_CH1OUT) + case 0: + stm32_tim_gpioconfig(GPIO_TIM5_CH1OUT, mode); + break; +#endif +#if defined(GPIO_TIM5_CH2OUT) + case 1: + stm32_tim_gpioconfig(GPIO_TIM5_CH2OUT, mode); + break; +#endif +#if defined(GPIO_TIM5_CH3OUT) + case 2: + stm32_tim_gpioconfig(GPIO_TIM5_CH3OUT, mode); + break; +#endif +#if defined(GPIO_TIM5_CH4OUT) + case 3: + stm32_tim_gpioconfig(GPIO_TIM5_CH4OUT, mode); + break; +#endif + default: + return -EINVAL; + } + break; +#endif +#ifdef CONFIG_STM32L4_TIM8 + case STM32L4_TIM8_BASE: + switch (channel) + { +#if defined(GPIO_TIM8_CH1OUT) + case 0: + stm32_tim_gpioconfig(GPIO_TIM8_CH1OUT, mode); break; +#endif +#if defined(GPIO_TIM8_CH2OUT) + case 1: + stm32_tim_gpioconfig(GPIO_TIM8_CH2OUT, mode); break; +#endif +#if defined(GPIO_TIM8_CH3OUT) + case 2: + stm32_tim_gpioconfig(GPIO_TIM8_CH3OUT, mode); break; +#endif +#if defined(GPIO_TIM8_CH4OUT) + case 3: + stm32_tim_gpioconfig(GPIO_TIM8_CH4OUT, mode); break; +#endif + default: + return -EINVAL; + } + break; +#endif +#ifdef CONFIG_STM32L4_TIM15 + case STM32L4_TIM15_BASE: + switch (channel) + { +#if defined(GPIO_TIM15_CH1OUT) + case 0: + stm32_tim_gpioconfig(GPIO_TIM15_CH1OUT, mode); + break; +#endif +#if defined(GPIO_TIM15_CH2OUT) + case 1: + stm32_tim_gpioconfig(GPIO_TIM15_CH2OUT, mode); + break; +#endif +#if defined(GPIO_TIM15_CH3OUT) + case 2: + stm32_tim_gpioconfig(GPIO_TIM15_CH3OUT, mode); + break; +#endif +#if defined(GPIO_TIM15_CH4OUT) + case 3: + stm32_tim_gpioconfig(GPIO_TIM15_CH4OUT, mode); + break; +#endif + default: + return -EINVAL; + } + break; +#endif +#ifdef CONFIG_STM32L4_TIM16 + case STM32L4_TIM16_BASE: + switch (channel) + { +#if defined(GPIO_TIM16_CH1OUT) + case 0: + stm32_tim_gpioconfig(GPIO_TIM16_CH1OUT, mode); + break; +#endif +#if defined(GPIO_TIM16_CH2OUT) + case 1: + stm32_tim_gpioconfig(GPIO_TIM16_CH2OUT, mode); + break; +#endif +#if defined(GPIO_TIM16_CH3OUT) + case 2: + stm32_tim_gpioconfig(GPIO_TIM16_CH3OUT, mode); + break; +#endif +#if defined(GPIO_TIM16_CH4OUT) + case 3: + stm32_tim_gpioconfig(GPIO_TIM16_CH4OUT, mode); + break; +#endif + default: + return -EINVAL; + } + break; +#endif +#ifdef CONFIG_STM32L4_TIM17 + case STM32L4_TIM17_BASE: + switch (channel) + { +#if defined(GPIO_TIM17_CH1OUT) + case 0: + stm32_tim_gpioconfig(GPIO_TIM17_CH1OUT, mode); + break; +#endif +#if defined(GPIO_TIM17_CH2OUT) + case 1: + stm32_tim_gpioconfig(GPIO_TIM17_CH2OUT, mode); + break; +#endif +#if defined(GPIO_TIM17_CH3OUT) + case 2: + stm32_tim_gpioconfig(GPIO_TIM17_CH3OUT, mode); + break; +#endif +#if defined(GPIO_TIM17_CH4OUT) + case 3: + stm32_tim_gpioconfig(GPIO_TIM17_CH4OUT, mode); + break; +#endif + default: + return -EINVAL; + } + break; +#endif + default: + return -EINVAL; + } + + return OK; +} + +/************************************************************************************ + * Name: stm32_tim_setcompare + ************************************************************************************/ + +static int stm32_tim_setcompare(FAR struct stm32l4_tim_dev_s *dev, uint8_t channel, + uint32_t compare) +{ + DEBUGASSERT(dev != NULL); + + switch (channel) + { + case 1: + stm32_putreg32(dev, STM32L4_GTIM_CCR1_OFFSET, compare); + break; + case 2: + stm32_putreg32(dev, STM32L4_GTIM_CCR2_OFFSET, compare); + break; + case 3: + stm32_putreg32(dev, STM32L4_GTIM_CCR3_OFFSET, compare); + break; + case 4: + stm32_putreg32(dev, STM32L4_GTIM_CCR4_OFFSET, compare); + break; + default: + return -EINVAL; + } + return OK; +} + +/************************************************************************************ + * Name: stm32_tim_getcapture + ************************************************************************************/ + +static int stm32_tim_getcapture(FAR struct stm32l4_tim_dev_s *dev, uint8_t channel) +{ + DEBUGASSERT(dev != NULL); + + switch (channel) + { + case 1: + return stm32_getreg32(dev, STM32L4_GTIM_CCR1_OFFSET); + case 2: + return stm32_getreg32(dev, STM32L4_GTIM_CCR2_OFFSET); + case 3: + return stm32_getreg32(dev, STM32L4_GTIM_CCR3_OFFSET); + case 4: + return stm32_getreg32(dev, STM32L4_GTIM_CCR4_OFFSET); + } + + return -EINVAL; +} + +/************************************************************************************ + * Name: stm32_tim_setisr + ************************************************************************************/ + +static int stm32_tim_setisr(FAR struct stm32l4_tim_dev_s *dev, + int (*handler)(int irq, void *context), + int source) +{ + int vectorno; + + DEBUGASSERT(dev != NULL); + DEBUGASSERT(source == 0); + + switch (((struct stm32_tim_priv_s *)dev)->base) + { +#ifdef CONFIG_STM32L4_TIM1 + case STM32L4_TIM1_BASE: + vectorno = STM32L4_IRQ_TIM1UP; + break; +#endif +#ifdef CONFIG_STM32L4_TIM2 + case STM32L4_TIM2_BASE: + vectorno = STM32L4_IRQ_TIM2; + break; +#endif +#ifdef CONFIG_STM32L4_TIM3 + case STM32L4_TIM3_BASE: + vectorno = STM32L4_IRQ_TIM3; + break; +#endif +#ifdef CONFIG_STM32L4_TIM4 + case STM32L4_TIM4_BASE: + vectorno = STM32L4_IRQ_TIM4; + break; +#endif +#ifdef CONFIG_STM32L4_TIM5 + case STM32L4_TIM5_BASE: + vectorno = STM32L4_IRQ_TIM5; + break; +#endif +#ifdef CONFIG_STM32L4_TIM6 + case STM32L4_TIM6_BASE: + vectorno = STM32L4_IRQ_TIM6; + break; +#endif +#ifdef CONFIG_STM32L4_TIM7 + case STM32L4_TIM7_BASE: + vectorno = STM32L4_IRQ_TIM7; + break; +#endif +#ifdef CONFIG_STM32L4_TIM8 + case STM32L4_TIM8_BASE: + vectorno = STM32L4_IRQ_TIM8UP; + break; +#endif +#ifdef CONFIG_STM32L4_TIM15 + case STM32L4_TIM15_BASE: + vectorno = STM32L4_IRQ_TIM15; + break; +#endif +#ifdef CONFIG_STM32L4_TIM16 + case STM32L4_TIM16_BASE: + vectorno = STM32L4_IRQ_TIM16; + break; +#endif +#ifdef CONFIG_STM32L4_TIM17 + case STM32L4_TIM17_BASE: + vectorno = STM32L4_IRQ_TIM17; + break; +#endif + + default: + return -EINVAL; + } + + /* Disable interrupt when callback is removed */ + + if (!handler) + { + up_disable_irq(vectorno); + irq_detach(vectorno); + return OK; + } + + /* Otherwise set callback and enable interrupt */ + + irq_attach(vectorno, handler); + up_enable_irq(vectorno); + +#ifdef CONFIG_ARCH_IRQPRIO + /* Set the interrupt priority */ + + up_prioritize_irq(vectorno, NVIC_SYSH_PRIORITY_DEFAULT); +#endif + + return OK; +} + +/************************************************************************************ + * Name: stm32_tim_enableint + ************************************************************************************/ + +static void stm32_tim_enableint(FAR struct stm32l4_tim_dev_s *dev, int source) +{ + DEBUGASSERT(dev != NULL); + stm32_modifyreg16(dev, STM32L4_BTIM_DIER_OFFSET, 0, ATIM_DIER_UIE); +} + +/************************************************************************************ + * Name: stm32_tim_disableint + ************************************************************************************/ + +static void stm32_tim_disableint(FAR struct stm32l4_tim_dev_s *dev, int source) +{ + DEBUGASSERT(dev != NULL); + stm32_modifyreg16(dev, STM32L4_BTIM_DIER_OFFSET, ATIM_DIER_UIE, 0); +} + +/************************************************************************************ + * Name: stm32_tim_ackint + ************************************************************************************/ + +static void stm32_tim_ackint(FAR struct stm32l4_tim_dev_s *dev, int source) +{ + stm32_putreg16(dev, STM32L4_BTIM_SR_OFFSET, ~ATIM_SR_UIF); +} + +/************************************************************************************ + * Name: stm32_tim_checkint + ************************************************************************************/ + +static int stm32_tim_checkint(FAR struct stm32l4_tim_dev_s *dev, int source) +{ + uint16_t regval = stm32_getreg16(dev, STM32L4_BTIM_SR_OFFSET); + return (regval & ATIM_SR_UIF) ? 1 : 0; +} + +/************************************************************************************ + * Pubic Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: stm32_tim_init + ************************************************************************************/ + +FAR struct stm32l4_tim_dev_s *stm32l4_tim_init(int timer) +{ + struct stm32l4_tim_dev_s *dev = NULL; + + /* Get structure and enable power */ + + switch (timer) + { +#ifdef CONFIG_STM32L4_TIM1 + case 1: + dev = (struct stm32l4_tim_dev_s *)&stm32_tim1_priv; + modifyreg32(STM32L4_RCC_APB2ENR, 0, RCC_APB2ENR_TIM1EN); + break; +#endif +#ifdef CONFIG_STM32L4_TIM2 + case 2: + dev = (struct stm32l4_tim_dev_s *)&stm32_tim2_priv; + modifyreg32(STM32L4_RCC_APB1ENR1, 0, RCC_APB1ENR1_TIM2EN); + break; +#endif +#ifdef CONFIG_STM32L4_TIM3 + case 3: + dev = (struct stm32l4_tim_dev_s *)&stm32_tim3_priv; + modifyreg32(STM32L4_RCC_APB1ENR1, 0, RCC_APB1ENR1_TIM3EN); + break; +#endif +#ifdef CONFIG_STM32L4_TIM4 + case 4: + dev = (struct stm32l4_tim_dev_s *)&stm32_tim4_priv; + modifyreg32(STM32L4_RCC_APB1ENR1, 0, RCC_APB1ENR1_TIM4EN); + break; +#endif +#ifdef CONFIG_STM32L4_TIM5 + case 5: + dev = (struct stm32l4_tim_dev_s *)&stm32_tim5_priv; + modifyreg32(STM32L4_RCC_APB1ENR1, 0, RCC_APB1ENR1_TIM5EN); + break; +#endif +#ifdef CONFIG_STM32L4_TIM6 + case 6: + dev = (struct stm32l4_tim_dev_s *)&stm32_tim6_priv; + modifyreg32(STM32L4_RCC_APB1ENR1, 0, RCC_APB1ENR1_TIM6EN); + break; +#endif +#ifdef CONFIG_STM32L4_TIM7 + case 7: + dev = (struct stm32l4_tim_dev_s *)&stm32_tim7_priv; + modifyreg32(STM32L4_RCC_APB1ENR1, 0, RCC_APB1ENR1_TIM7EN); + break; +#endif +#ifdef CONFIG_STM32L4_TIM8 + case 8: + dev = (struct stm32l4_tim_dev_s *)&stm32_tim8_priv; + modifyreg32(STM32L4_RCC_APB2ENR, 0, RCC_APB2ENR_TIM8EN); + break; +#endif +#ifdef CONFIG_STM32L4_TIM15 + case 15: + dev = (struct stm32l4_tim_dev_s *)&stm32_tim15_priv; + modifyreg32(STM32L4_RCC_APB2ENR, 0, RCC_APB2ENR_TIM15EN); + break; +#endif +#ifdef CONFIG_STM32L4_TIM16 + case 16: + dev = (struct stm32l4_tim_dev_s *)&stm32_tim16_priv; + modifyreg32(STM32L4_RCC_APB2ENR, 0, RCC_APB2ENR_TIM16EN); + break; +#endif +#ifdef CONFIG_STM32L4_TIM17 + case 17: + dev = (struct stm32l4_tim_dev_s *)&stm32_tim17_priv; + modifyreg32(STM32L4_RCC_APB2ENR, 0, RCC_APB2ENR_TIM17EN); + break; +#endif + default: + return NULL; + } + + /* Is device already allocated */ + + if (((struct stm32_tim_priv_s *)dev)->mode != STM32L4_TIM_MODE_UNUSED) + { + return NULL; + } + + stm32_tim_reset(dev); + + return dev; +} + +/************************************************************************************ + * Name: stm32l4_tim_deinit + * + * TODO: Detach interrupts, and close down all TIM Channels + * + ************************************************************************************/ + +int stm32l4_tim_deinit(FAR struct stm32l4_tim_dev_s * dev) +{ + DEBUGASSERT(dev != NULL); + + /* Disable power */ + + switch (((struct stm32_tim_priv_s *)dev)->base) + { +#ifdef CONFIG_STM32L4_TIM1 + case STM32L4_TIM1_BASE: + modifyreg32(STM32L4_RCC_APB2ENR, RCC_APB2ENR_TIM1EN, 0); + break; +#endif +#ifdef CONFIG_STM32L4_TIM2 + case STM32L4_TIM2_BASE: + modifyreg32(STM32L4_RCC_APB1ENR1, RCC_APB1ENR1_TIM2EN, 0); + break; +#endif +#ifdef CONFIG_STM32L4_TIM3 + case STM32L4_TIM3_BASE: + modifyreg32(STM32L4_RCC_APB1ENR1, RCC_APB1ENR1_TIM3EN, 0); + break; +#endif +#ifdef CONFIG_STM32L4_TIM4 + case STM32L4_TIM4_BASE: + modifyreg32(STM32L4_RCC_APB1ENR1, RCC_APB1ENR1_TIM4EN, 0); + break; +#endif +#ifdef CONFIG_STM32L4_TIM5 + case STM32L4_TIM5_BASE: + modifyreg32(STM32L4_RCC_APB1ENR1, RCC_APB1ENR1_TIM5EN, 0); + break; +#endif +#ifdef CONFIG_STM32L4_TIM6 + case STM32L4_TIM6_BASE: + modifyreg32(STM32L4_RCC_APB1ENR1, RCC_APB1ENR1_TIM6EN, 0); + break; +#endif +#ifdef CONFIG_STM32L4_TIM7 + case STM32L4_TIM7_BASE: + modifyreg32(STM32L4_RCC_APB1ENR1, RCC_APB1ENR1_TIM7EN, 0); + break; +#endif +#ifdef CONFIG_STM32L4_TIM8 + case STM32L4_TIM8_BASE: + modifyreg32(STM32L4_RCC_APB2ENR, RCC_APB2ENR_TIM8EN, 0); + break; +#endif +#ifdef CONFIG_STM32L4_TIM15 + case STM32L4_TIM15_BASE: + modifyreg32(STM32L4_RCC_APB2ENR, RCC_APB2ENR_TIM15EN, 0); + break; +#endif +#ifdef CONFIG_STM32L4_TIM16 + case STM32L4_TIM16_BASE: + modifyreg32(STM32L4_RCC_APB2ENR, RCC_APB2ENR_TIM16EN, 0); + break; +#endif +#ifdef CONFIG_STM32L4_TIM17 + case STM32L4_TIM17_BASE: + modifyreg32(STM32L4_RCC_APB2ENR, RCC_APB2ENR_TIM17EN, 0); + break; +#endif + default: + return -EINVAL; + } + + /* Mark it as free */ + + ((struct stm32_tim_priv_s *)dev)->mode = STM32L4_TIM_MODE_UNUSED; + + return OK; +} + +#endif /* defined(CONFIG_STM32L4_TIM1 || ... || TIM17) */ diff --git a/arch/arm/src/stm32l4/stm32l4_tim.h b/arch/arm/src/stm32l4/stm32l4_tim.h index e69de29bb2..1601f1277e 100644 --- a/arch/arm/src/stm32l4/stm32l4_tim.h +++ b/arch/arm/src/stm32l4/stm32l4_tim.h @@ -0,0 +1,223 @@ +/************************************************************************************ + * arch/arm/src/stm32l4/stm32l4_tim.h + * + * Copyright (C) 2011 Uros Platise. All rights reserved. + * Author: Uros Platise + * + * With modifications and updates by: + * + * Copyright (C) 2011-2012, 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * dev@ziggurat29.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32L4_STM32L4_TIM_H +#define __ARCH_ARM_SRC_STM32L4_STM32L4_TIM_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include "chip.h" +#include "chip/stm32l4_tim.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ +/* Helpers **************************************************************************/ + +#define STM32L4_TIM_SETMODE(d,mode) ((d)->ops->setmode(d,mode)) +#define STM32L4_TIM_SETCLOCK(d,freq) ((d)->ops->setclock(d,freq)) +#define STM32L4_TIM_SETPERIOD(d,period) ((d)->ops->setperiod(d,period)) +#define STM32L4_TIM_GETCOUNTER(d) ((d)->ops->getcounter(d)) +#define STM32L4_TIM_SETCHANNEL(d,ch,mode) ((d)->ops->setchannel(d,ch,mode)) +#define STM32L4_TIM_SETCOMPARE(d,ch,comp) ((d)->ops->setcompare(d,ch,comp)) +#define STM32L4_TIM_GETCAPTURE(d,ch) ((d)->ops->getcapture(d,ch)) +#define STM32L4_TIM_SETISR(d,hnd,s) ((d)->ops->setisr(d,hnd,s)) +#define STM32L4_TIM_ENABLEINT(d,s) ((d)->ops->enableint(d,s)) +#define STM32L4_TIM_DISABLEINT(d,s) ((d)->ops->disableint(d,s)) +#define STM32L4_TIM_ACKINT(d,s) ((d)->ops->ackint(d,s)) +#define STM32L4_TIM_CHECKINT(d,s) ((d)->ops->checkint(d,s)) + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/* TIM Device Structure */ + +struct stm32l4_tim_dev_s +{ + struct stm32l4_tim_ops_s *ops; +}; + +/* TIM Modes of Operation */ + +typedef enum +{ + STM32L4_TIM_MODE_UNUSED = -1, + + /* One of the following */ + + STM32L4_TIM_MODE_MASK = 0x0310, + STM32L4_TIM_MODE_DISABLED = 0x0000, + STM32L4_TIM_MODE_UP = 0x0100, + STM32L4_TIM_MODE_DOWN = 0x0110, + STM32L4_TIM_MODE_UPDOWN = 0x0200, + STM32L4_TIM_MODE_PULSE = 0x0300, + + /* One of the following */ + + STM32L4_TIM_MODE_CK_INT = 0x0000, +//STM32L4_TIM_MODE_CK_INT_TRIG = 0x0400, +//STM32L4_TIM_MODE_CK_EXT = 0x0800, +//STM32L4_TIM_MODE_CK_EXT_TRIG = 0x0C00, + + /* Clock sources, OR'ed with CK_EXT */ + +//STM32L4_TIM_MODE_CK_CHINVALID = 0x0000, +//STM32L4_TIM_MODE_CK_CH1 = 0x0001, +//STM32L4_TIM_MODE_CK_CH2 = 0x0002, +//STM32L4_TIM_MODE_CK_CH3 = 0x0003, +//STM32L4_TIM_MODE_CK_CH4 = 0x0004 + + /* Todo: external trigger block */ + +} stm32l4_tim_mode_t; + +/* TIM Channel Modes */ + +typedef enum +{ + STM32L4_TIM_CH_DISABLED = 0x00, + + /* Common configuration */ + + STM32L4_TIM_CH_POLARITY_POS = 0x00, + STM32L4_TIM_CH_POLARITY_NEG = 0x01, + + /* MODES: */ + + STM32L4_TIM_CH_MODE_MASK = 0x06, + + /* Output Compare Modes */ + + STM32L4_TIM_CH_OUTPWM = 0x04, /** Enable standard PWM mode, active high when counter < compare */ +//STM32L4_TIM_CH_OUTCOMPARE = 0x06, + + // TODO other modes ... as PWM capture, ENCODER and Hall Sensor +//STM32L4_TIM_CH_INCAPTURE = 0x10, +//STM32L4_TIM_CH_INPWM = 0x20 +//STM32L4_TIM_CH_DRIVE_OC -- open collector mode + +} stm32l4_tim_channel_t; + +/* TIM Operations */ + +struct stm32l4_tim_ops_s +{ + /* Basic Timers */ + + int (*setmode)(FAR struct stm32l4_tim_dev_s *dev, stm32l4_tim_mode_t mode); + int (*setclock)(FAR struct stm32l4_tim_dev_s *dev, uint32_t freq); + void (*setperiod)(FAR struct stm32l4_tim_dev_s *dev, uint32_t period); + uint32_t (*getcounter)(FAR struct stm32l4_tim_dev_s *dev); + + /* General and Advanced Timers Adds */ + + int (*setchannel)(FAR struct stm32l4_tim_dev_s *dev, uint8_t channel, + stm32l4_tim_channel_t mode); + int (*setcompare)(FAR struct stm32l4_tim_dev_s *dev, uint8_t channel, + uint32_t compare); + int (*getcapture)(FAR struct stm32l4_tim_dev_s *dev, uint8_t channel); + + /* Timer interrupts */ + + int (*setisr)(FAR struct stm32l4_tim_dev_s *dev, + int (*handler)(int irq, void *context), int source); + void (*enableint)(FAR struct stm32l4_tim_dev_s *dev, int source); + void (*disableint)(FAR struct stm32l4_tim_dev_s *dev, int source); + void (*ackint)(FAR struct stm32l4_tim_dev_s *dev, int source); + int (*checkint)(FAR struct stm32l4_tim_dev_s *dev, int source); +}; + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/* Power-up timer and get its structure */ + +FAR struct stm32l4_tim_dev_s *stm32l4_tim_init(int timer); + +/* Power-down timer, mark it as unused */ + +int stm32l4_tim_deinit(FAR struct stm32l4_tim_dev_s * dev); + +/**************************************************************************** + * Name: stm32l4_timer_initialize + * + * Description: + * Bind the configuration timer to a timer lower half instance and + * register the timer drivers at 'devpath' + * + * Input Parameters: + * devpath - The full path to the timer device. This should be of the form /dev/timer0 + * timer - the timer number. + * + * Returned Values: + * Zero (OK) is returned on success; A negated errno value is returned + * to indicate the nature of any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_TIMER +int stm32l4_timer_initialize(FAR const char *devpath, int timer); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_STM32L4_STM32L4_TIM_H */ diff --git a/arch/arm/src/stm32l4/stm32l4_tim_lowerhalf.c b/arch/arm/src/stm32l4/stm32l4_tim_lowerhalf.c new file mode 100644 index 0000000000..5a7639cb3a --- /dev/null +++ b/arch/arm/src/stm32l4/stm32l4_tim_lowerhalf.c @@ -0,0 +1,668 @@ +/**************************************************************************** + * arch/arm/src/stm32l4/stm32l4_tim_lowerhalf.c + * + * Copyright (C) 2015 Wail Khemir. All rights reserved. + * Copyright (C) 2015 Omni Hoverboards Inc. All rights reserved. + * Authors: Wail Khemir + * Paul Alexander Patience + * dev@ziggurat29.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include + +#include +#include + +#include + +#include "stm32l4_tim.h" + +#if defined(CONFIG_TIMER) && \ + (defined(CONFIG_STM32L4_TIM1) || defined(CONFIG_STM32L4_TIM2) || \ + defined(CONFIG_STM32L4_TIM3) || defined(CONFIG_STM32L4_TIM4) || \ + defined(CONFIG_STM32L4_TIM5) || defined(CONFIG_STM32L4_TIM6) || \ + defined(CONFIG_STM32L4_TIM7) || defined(CONFIG_STM32L4_TIM8) || \ + defined(CONFIG_STM32L4_TIM15) || defined(CONFIG_STM32L4_TIM16) || \ + defined(CONFIG_STM32L4_TIM17)) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define STM32L4_TIM1_RES 16 +#define STM32L4_TIM2_RES 32 +#define STM32L4_TIM3_RES 16 +#define STM32L4_TIM4_RES 16 +#define STM32L4_TIM5_RES 32 +#define STM32L4_TIM6_RES 16 +#define STM32L4_TIM7_RES 16 +#define STM32L4_TIM8_RES 16 +#define STM32L4_TIM15_RES 16 +#define STM32L4_TIM16_RES 16 +#define STM32L4_TIM17_RES 16 + +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* This structure provides the private representation of the "lower-half" + * driver state structure. This structure must be cast-compatible with the + * timer_lowerhalf_s structure. + */ + +struct stm32_lowerhalf_s +{ + FAR const struct timer_ops_s *ops; /* Lower half operations */ + FAR struct stm32l4_tim_dev_s *tim; /* stm32 timer driver */ + tccb_t usrhandler; /* Current user interrupt handler */ + const xcpt_t timhandler; /* Current timer interrupt handler */ + bool started; /* True: Timer has been started */ + const uint8_t resolution; /* Number of bits in the timer (16 or 32 bits) */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Interrupt handling *******************************************************/ + +#ifdef CONFIG_STM32L4_TIM1 +static int stm32_tim1_interrupt(int irq, FAR void *context); +#endif +#ifdef CONFIG_STM32L4_TIM2 +static int stm32_tim2_interrupt(int irq, FAR void *context); +#endif +#ifdef CONFIG_STM32L4_TIM3 +static int stm32_tim3_interrupt(int irq, FAR void *context); +#endif +#ifdef CONFIG_STM32L4_TIM4 +static int stm32_tim4_interrupt(int irq, FAR void *context); +#endif +#ifdef CONFIG_STM32L4_TIM5 +static int stm32_tim5_interrupt(int irq, FAR void *context); +#endif +#ifdef CONFIG_STM32L4_TIM6 +static int stm32_tim6_interrupt(int irq, FAR void *context); +#endif +#ifdef CONFIG_STM32L4_TIM7 +static int stm32_tim7_interrupt(int irq, FAR void *context); +#endif +#ifdef CONFIG_STM32L4_TIM8 +static int stm32_tim8_interrupt(int irq, FAR void *context); +#endif +#ifdef CONFIG_STM32L4_TIM15 +static int stm32_tim15_interrupt(int irq, FAR void *context); +#endif +#ifdef CONFIG_STM32L4_TIM16 +static int stm32_tim16_interrupt(int irq, FAR void *context); +#endif +#ifdef CONFIG_STM32L4_TIM17 +static int stm32_tim17_interrupt(int irq, FAR void *context); +#endif + +static int stm32_timer_handler(FAR struct stm32_lowerhalf_s *lower); + +/* "Lower half" driver methods **********************************************/ + +static int stm32_start(FAR struct timer_lowerhalf_s *lower); +static int stm32_stop(FAR struct timer_lowerhalf_s *lower); +static int stm32_settimeout(FAR struct timer_lowerhalf_s *lower, + uint32_t timeout); +static tccb_t stm32_sethandler(FAR struct timer_lowerhalf_s *lower, + tccb_t handler); + +/**************************************************************************** + * Private Data + ****************************************************************************/ +/* "Lower half" driver methods */ + +static const struct timer_ops_s g_timer_ops = +{ + .start = stm32_start, + .stop = stm32_stop, + .getstatus = NULL, + .settimeout = stm32_settimeout, + .sethandler = stm32_sethandler, + .ioctl = NULL, +}; + +#ifdef CONFIG_STM32L4_TIM1 +static struct stm32_lowerhalf_s g_tim1_lowerhalf = +{ + .ops = &g_timer_ops, + .timhandler = stm32_tim1_interrupt, + .resolution = STM32L4_TIM1_RES, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM2 +static struct stm32_lowerhalf_s g_tim2_lowerhalf = +{ + .ops = &g_timer_ops, + .timhandler = stm32_tim2_interrupt, + .resolution = STM32L4_TIM2_RES, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM3 +static struct stm32_lowerhalf_s g_tim3_lowerhalf = +{ + .ops = &g_timer_ops, + .timhandler = stm32_tim3_interrupt, + .resolution = STM32L4_TIM3_RES, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM4 +static struct stm32_lowerhalf_s g_tim4_lowerhalf = +{ + .ops = &g_timer_ops, + .timhandler = stm32_tim4_interrupt, + .resolution = STM32L4_TIM4_RES, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM5 +static struct stm32_lowerhalf_s g_tim5_lowerhalf = +{ + .ops = &g_timer_ops, + .timhandler = stm32_tim5_interrupt, + .resolution = STM32L4_TIM5_RES, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM6 +static struct stm32_lowerhalf_s g_tim6_lowerhalf = +{ + .ops = &g_timer_ops, + .timhandler = stm32_tim6_interrupt, + .resolution = STM32L4_TIM6_RES, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM7 +static struct stm32_lowerhalf_s g_tim7_lowerhalf = +{ + .ops = &g_timer_ops, + .timhandler = stm32_tim7_interrupt, + .resolution = STM32L4_TIM7_RES, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM8 +static struct stm32_lowerhalf_s g_tim8_lowerhalf = +{ + .ops = &g_timer_ops, + .timhandler = stm32_tim8_interrupt, + .resolution = STM32L4_TIM8_RES, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM15 +static struct stm32_lowerhalf_s g_tim15_lowerhalf = +{ + .ops = &g_timer_ops, + .timhandler = stm32_tim15_interrupt, + .resolution = STM32L4_TIM15_RES, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM16 +static struct stm32_lowerhalf_s g_tim16_lowerhalf = +{ + .ops = &g_timer_ops, + .timhandler = stm32_tim16_interrupt, + .resolution = STM32L4_TIM16_RES, +}; +#endif + +#ifdef CONFIG_STM32L4_TIM17 +static struct stm32_lowerhalf_s g_tim17_lowerhalf = +{ + .ops = &g_timer_ops, + .timhandler = stm32_tim17_interrupt, + .resolution = STM32L4_TIM17_RES, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_timN_interrupt, N=1..14 + * + * Description: + * Individual interrupt handlers for each timer + * + ****************************************************************************/ + +#ifdef CONFIG_STM32L4_TIM1 +static int stm32_tim1_interrupt(int irq, FAR void *context) +{ + return stm32_timer_handler(&g_tim1_lowerhalf); +} +#endif + +#ifdef CONFIG_STM32L4_TIM2 +static int stm32_tim2_interrupt(int irq, FAR void *context) +{ + return stm32_timer_handler(&g_tim2_lowerhalf); +} +#endif + +#ifdef CONFIG_STM32L4_TIM3 +static int stm32_tim3_interrupt(int irq, FAR void *context) +{ + return stm32_timer_handler(&g_tim3_lowerhalf); +} +#endif + +#ifdef CONFIG_STM32L4_TIM4 +static int stm32_tim4_interrupt(int irq, FAR void *context) +{ + return stm32_timer_handler(&g_tim4_lowerhalf); +} +#endif + +#ifdef CONFIG_STM32L4_TIM5 +static int stm32_tim5_interrupt(int irq, FAR void *context) +{ + return stm32_timer_handler(&g_tim5_lowerhalf); +} +#endif + +#ifdef CONFIG_STM32L4_TIM6 +static int stm32_tim6_interrupt(int irq, FAR void *context) +{ + return stm32_timer_handler(&g_tim6_lowerhalf); +} +#endif + +#ifdef CONFIG_STM32L4_TIM7 +static int stm32_tim7_interrupt(int irq, FAR void *context) +{ + return stm32_timer_handler(&g_tim7_lowerhalf); +} +#endif + +#ifdef CONFIG_STM32L4_TIM8 +static int stm32_tim8_interrupt(int irq, FAR void *context) +{ + return stm32_timer_handler(&g_tim8_lowerhalf); +} +#endif + +#ifdef CONFIG_STM32L4_TIM15 +static int stm32_tim15_interrupt(int irq, FAR void *context) +{ + return stm32_timer_handler(&g_tim15_lowerhalf); +} +#endif + +#ifdef CONFIG_STM32L4_TIM16 +static int stm32_tim16_interrupt(int irq, FAR void *context) +{ + return stm32_timer_handler(&g_tim16_lowerhalf); +} +#endif + +#ifdef CONFIG_STM32L4_TIM17 +static int stm32_tim17_interrupt(int irq, FAR void *context) +{ + return stm32_timer_handler(&g_tim17_lowerhalf); +} +#endif + +/**************************************************************************** + * Name: stm32_timer_handler + * + * Description: + * timer interrupt handler + * + * Input Parameters: + * + * Returned Values: + * + ****************************************************************************/ + +static int stm32_timer_handler(FAR struct stm32_lowerhalf_s *lower) +{ + uint32_t next_interval_us = 0; + + STM32L4_TIM_ACKINT(lower->tim, 0); + + if (lower->usrhandler(&next_interval_us)) + { + if (next_interval_us > 0) + { + STM32L4_TIM_SETPERIOD(lower->tim, next_interval_us); + } + } + else + { + stm32_stop((struct timer_lowerhalf_s *)lower); + } + + return OK; +} + +/**************************************************************************** + * Name: stm32_start + * + * Description: + * Start the timer, resetting the time to the current timeout, + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * + * Returned Values: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32_start(FAR struct timer_lowerhalf_s *lower) +{ + FAR struct stm32_lowerhalf_s *priv = (FAR struct stm32_lowerhalf_s *)lower; + + if (!priv->started) + { + STM32L4_TIM_SETMODE(priv->tim, STM32L4_TIM_MODE_UP); + + if (priv->usrhandler != NULL) + { + STM32L4_TIM_SETISR(priv->tim, priv->timhandler, 0); + STM32L4_TIM_ENABLEINT(priv->tim, 0); + } + + priv->started = true; + return OK; + } + + /* Return EBUSY to indicate that the timer was already running */ + + return -EBUSY; +} + +/**************************************************************************** + * Name: stm32_stop + * + * Description: + * Stop the timer + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * + * Returned Values: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32_stop(struct timer_lowerhalf_s *lower) +{ + struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower; + + if (priv->started) + { + STM32L4_TIM_SETMODE(priv->tim, STM32L4_TIM_MODE_DISABLED); + STM32L4_TIM_DISABLEINT(priv->tim, 0); + STM32L4_TIM_SETISR(priv->tim, 0, 0); + priv->started = false; + return OK; + } + + /* Return ENODEV to indicate that the timer was not running */ + + return -ENODEV; +} + +/**************************************************************************** + * Name: stm32_settimeout + * + * Description: + * Set a new timeout value (and reset the timer) + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * timeout - The new timeout value in microseconds. + * + * Returned Values: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32_settimeout(FAR struct timer_lowerhalf_s *lower, uint32_t timeout) +{ + FAR struct stm32_lowerhalf_s *priv = (FAR struct stm32_lowerhalf_s *)lower; + uint64_t maxtimeout; + + if (priv->started) + { + return -EPERM; + } + + maxtimeout = (1 << priv->resolution) - 1; + if (timeout > maxtimeout) + { + uint64_t freq = (maxtimeout * 1000000) / timeout; + STM32L4_TIM_SETCLOCK(priv->tim, freq); + STM32L4_TIM_SETPERIOD(priv->tim, maxtimeout); + } + else + { + STM32L4_TIM_SETCLOCK(priv->tim, 1000000); + STM32L4_TIM_SETPERIOD(priv->tim, timeout); + } + + return OK; +} + +/**************************************************************************** + * Name: stm32_sethandler + * + * Description: + * Call this user provided timeout handler. + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * newhandler - The new timer expiration function pointer. If this + * function pointer is NULL, then the reset-on-expiration + * behavior is restored, + * + * Returned Values: + * The previous timer expiration function pointer or NULL is there was + * no previous function pointer. + * + ****************************************************************************/ + +static tccb_t stm32_sethandler(FAR struct timer_lowerhalf_s *lower, + tccb_t newhandler) +{ + FAR struct stm32_lowerhalf_s *priv = (FAR struct stm32_lowerhalf_s *)lower; + + irqstate_t flags = enter_critical_section(); + + /* Get the old handler return value */ + + tccb_t oldhandler = priv->usrhandler; + + /* Save the new handler */ + + priv->usrhandler = newhandler; + + if (newhandler != NULL && priv->started) + { + STM32L4_TIM_SETISR(priv->tim, priv->timhandler, 0); + STM32L4_TIM_ENABLEINT(priv->tim, 0); + } + else + { + STM32L4_TIM_DISABLEINT(priv->tim, 0); + STM32L4_TIM_SETISR(priv->tim, 0, 0); + } + + leave_critical_section(flags); + return oldhandler; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32l4_timer_initialize + * + * Description: + * Bind the configuration timer to a timer lower half instance and + * register the timer drivers at 'devpath' + * + * Input Parameters: + * devpath - The full path to the timer device. This should be of the + * form /dev/timer0 + * timer - the timer's number. + * + * Returned Values: + * Zero (OK) is returned on success; A negated errno value is returned + * to indicate the nature of any failure. + * + ****************************************************************************/ + +int stm32l4_timer_initialize(FAR const char *devpath, int timer) +{ + FAR struct stm32_lowerhalf_s *lower; + + switch (timer) + { +#ifdef CONFIG_STM32L4_TIM1 + case 1: + lower = &g_tim1_lowerhalf; + break; +#endif +#ifdef CONFIG_STM32L4_TIM2 + case 2: + lower = &g_tim2_lowerhalf; + break; +#endif +#ifdef CONFIG_STM32L4_TIM3 + case 3: + lower = &g_tim3_lowerhalf; + break; +#endif +#ifdef CONFIG_STM32L4_TIM4 + case 4: + lower = &g_tim4_lowerhalf; + break; +#endif +#ifdef CONFIG_STM32L4_TIM5 + case 5: + lower = &g_tim5_lowerhalf; + break; +#endif +#ifdef CONFIG_STM32L4_TIM6 + case 6: + lower = &g_tim6_lowerhalf; + break; +#endif +#ifdef CONFIG_STM32L4_TIM7 + case 7: + lower = &g_tim7_lowerhalf; + break; +#endif +#ifdef CONFIG_STM32L4_TIM8 + case 8: + lower = &g_tim8_lowerhalf; + break; +#endif +#ifdef CONFIG_STM32L4_TIM15 + case 15: + lower = &g_tim15_lowerhalf; + break; +#endif +#ifdef CONFIG_STM32L4_TIM16 + case 16: + lower = &g_tim16_lowerhalf; + break; +#endif +#ifdef CONFIG_STM32L4_TIM17 + case 17: + lower = &g_tim17_lowerhalf; + break; +#endif + default: + return -ENODEV; + } + + /* Initialize the elements of lower half state structure */ + + lower->started = false; + lower->usrhandler = NULL; + lower->tim = stm32l4_tim_init(timer); + + if (lower->tim == NULL) + { + return -EINVAL; + } + + /* Register the timer driver as /dev/timerX. The returned value from + * timer_register is a handle that could be used with timer_unregister(). + * REVISIT: The returned handle is discard here. + */ + + FAR void *drvr = timer_register(devpath, + (FAR struct timer_lowerhalf_s *)lower); + if (drvr == NULL) + { + /* The actual cause of the failure may have been a failure to allocate + * perhaps a failure to register the timer driver (such as if the + * 'depath' were not unique). We know here but we return EEXIST to + * indicate the failure (implying the non-unique devpath). + */ + + return -EEXIST; + } + + return OK; +} + +#endif /* CONFIG_TIMER */