This is an automated email from the ASF dual-hosted git repository. jerpelea pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit ae4f9f5b444ee2cd34d44272efe9eda3cc4a740a Author: raiden00pl <[email protected]> AuthorDate: Wed May 20 10:16:02 2026 +0200 !arch/stm32f0l0g0: separate pulse count from PWM driver BREAKING CHANGE: separate pulse count from PWM driver Pulse count handling was removed from PWM driver and moved to a separate driver. For details about this change, look at previous commit. Signed-off-by: raiden00pl <[email protected]> --- arch/arm/src/stm32f0l0g0/CMakeLists.txt | 4 + arch/arm/src/stm32f0l0g0/Kconfig | 41 +- arch/arm/src/stm32f0l0g0/Make.defs | 6 + arch/arm/src/stm32f0l0g0/stm32_pulsecount.c | 1277 +++++++++++++++++++++++++++ arch/arm/src/stm32f0l0g0/stm32_pulsecount.h | 39 + arch/arm/src/stm32f0l0g0/stm32_pwm.c | 341 +------ 6 files changed, 1361 insertions(+), 347 deletions(-) diff --git a/arch/arm/src/stm32f0l0g0/CMakeLists.txt b/arch/arm/src/stm32f0l0g0/CMakeLists.txt index b2cf874c32e..a60ec1966a7 100644 --- a/arch/arm/src/stm32f0l0g0/CMakeLists.txt +++ b/arch/arm/src/stm32f0l0g0/CMakeLists.txt @@ -91,6 +91,10 @@ if(CONFIG_STM32F0L0G0_PWM) list(APPEND SRCS stm32_pwm.c) endif() +if(CONFIG_PULSECOUNT AND CONFIG_STM32F0L0G0_TIM1_PULSECOUNT) + list(APPEND SRCS stm32_pulsecount.c) +endif() + if(CONFIG_STM32F0L0G0_ADC) list(APPEND SRCS stm32_adc.c) endif() diff --git a/arch/arm/src/stm32f0l0g0/Kconfig b/arch/arm/src/stm32f0l0g0/Kconfig index ad53ee4d492..31cddad994e 100644 --- a/arch/arm/src/stm32f0l0g0/Kconfig +++ b/arch/arm/src/stm32f0l0g0/Kconfig @@ -2167,7 +2167,6 @@ config STM32F0L0G0_CAN config STM32F0L0G0_PWM bool default n - select ARCH_HAVE_PWM_PULSECOUNT config STM32F0L0G0_USART bool @@ -2191,17 +2190,30 @@ config STM32F0L0G0_1WIREDRIVER menu "Timer Configuration" +config STM32F0L0G0_TIM1_PULSECOUNT + bool "TIM1 pulse count" + default n + depends on STM32F0L0G0_TIM1 + select ARCH_HAVE_PULSECOUNT + select PULSECOUNT + ---help--- + Reserve timer 1 for use by the pulse count driver. + + Timer devices may be used for different purposes. If STM32F0L0G0_TIM1 + is defined then this option may also be defined to indicate that TIM1 is + intended to generate a fixed number of output pulses. + config STM32F0L0G0_TIM1_PWM bool "TIM1 PWM" default n - depends on STM32F0L0G0_TIM1 + depends on STM32F0L0G0_TIM1 && !STM32F0L0G0_TIM1_PULSECOUNT select STM32F0L0G0_PWM ---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 - STM32F0L0G0_TIM1 is defined then THIS option may also be defined to + STM32F0L0G0_TIM1 is defined then this option may also be defined to indicate that the timer is intended to be used for pulsed output modulation. Valid channel modes: @@ -2243,7 +2255,7 @@ config STM32F0L0G0_TIM1_CH1MODE default 0 range 0 5 ---help--- - Specifies the channel mode. See STM32F0L0G0_TIM1_PWM description for available modes. + Specifies the channel mode. config STM32F0L0G0_TIM1_CH1OUT bool "TIM1 Channel 1 Output" @@ -2273,7 +2285,7 @@ config STM32F0L0G0_TIM1_CH2MODE default 0 range 0 5 ---help--- - Specifies the channel mode. See STM32F0L0G0_TIM1_PWM description for available modes. + Specifies the channel mode. config STM32F0L0G0_TIM1_CH2OUT bool "TIM1 Channel 2 Output" @@ -2303,7 +2315,7 @@ config STM32F0L0G0_TIM1_CH3MODE default 0 range 0 5 ---help--- - Specifies the channel mode. See STM32F0L0G0_TIM1_PWM description for available modes. + Specifies the channel mode. config STM32F0L0G0_TIM1_CH3OUT bool "TIM1 Channel 3 Output" @@ -2333,7 +2345,7 @@ config STM32F0L0G0_TIM1_CH4MODE default 0 range 0 5 ---help--- - Specifies the channel mode. See STM32F0L0G0_TIM1_PWM description for available modes. + Specifies the channel mode. config STM32F0L0G0_TIM1_CH4OUT bool "TIM1 Channel 4 Output" @@ -2352,7 +2364,7 @@ config STM32F0L0G0_TIM1_CHANNEL default 1 range 1 4 ---help--- - If TIM1 is enabled for PWM usage, you also need specifies the timer output + If TIM1 is enabled for output usage, you also need specifies the timer output channel {1,..,4} config STM32F0L0G0_TIM1_CHMODE @@ -2360,12 +2372,23 @@ config STM32F0L0G0_TIM1_CHMODE default 0 range 0 5 ---help--- - Specifies the channel mode. See STM32F0L0G0_TIM1_PWM description for available modes. + Specifies the channel mode. endif # !STM32F0L0G0_PWM_MULTICHAN endif # STM32F0L0G0_TIM1_PWM +if STM32F0L0G0_TIM1_PULSECOUNT + +config STM32F0L0G0_TIM1_PULSECOUNT_CHANNEL + int "TIM1 pulse count channel" + default 1 + range 1 4 + ---help--- + Specifies the timer channel {1,..,4}. + +endif # STM32F0L0G0_TIM1_PULSECOUNT + config STM32F0L0G0_TIM1_QE bool "TIM1 Quadrature Encoder" default n diff --git a/arch/arm/src/stm32f0l0g0/Make.defs b/arch/arm/src/stm32f0l0g0/Make.defs index df47027944c..e4ab1438876 100644 --- a/arch/arm/src/stm32f0l0g0/Make.defs +++ b/arch/arm/src/stm32f0l0g0/Make.defs @@ -77,6 +77,12 @@ ifeq ($(CONFIG_STM32F0L0G0_PWM),y) CHIP_CSRCS += stm32_pwm.c endif +ifeq ($(CONFIG_PULSECOUNT),y) +ifeq ($(CONFIG_STM32F0L0G0_TIM1_PULSECOUNT),y) +CHIP_CSRCS += stm32_pulsecount.c +endif +endif + ifeq ($(CONFIG_STM32F0L0G0_ADC),y) CHIP_CSRCS += stm32_adc.c endif diff --git a/arch/arm/src/stm32f0l0g0/stm32_pulsecount.c b/arch/arm/src/stm32f0l0g0/stm32_pulsecount.c new file mode 100644 index 00000000000..a60c576c4a5 --- /dev/null +++ b/arch/arm/src/stm32f0l0g0/stm32_pulsecount.c @@ -0,0 +1,1277 @@ +/**************************************************************************** + * arch/arm/src/stm32f0l0g0/stm32_pulsecount.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <inttypes.h> +#include <stdio.h> +#include <assert.h> +#include <errno.h> +#include <nuttx/debug.h> + +#include <nuttx/arch.h> +#include <nuttx/timers/pulsecount.h> +#include <arch/board/board.h> + +#include "arm_internal.h" +#include "chip.h" +#include "stm32_gpio.h" +#include "stm32_pulsecount.h" +#include "stm32_rcc.h" +#include "stm32_tim.h" + +/* This module only supports pulse count on advanced timers. */ + +#ifdef CONFIG_STM32F0L0G0_TIM1_PULSECOUNT + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Pulse count is supported by advanced timers only. */ + +#define TIMTYPE_ADVANCED 4 /* Advanced timers: TIM1 */ +#define TIMTYPE_TIM1 TIMTYPE_ADVANCED + +#define HAVE_IP_TIMERS_V2 1 + +/* CCMR2 */ + +#define HAVE_CCMR2 1 + +#ifdef STM32_APB2_TIM1_CLKIN +# define PULSECOUNT_TIM1_CLKIN STM32_APB2_TIM1_CLKIN +#else +# define PULSECOUNT_TIM1_CLKIN STM32_APB1_TIM1_CLKIN +#endif + +#if CONFIG_STM32F0L0G0_TIM1_PULSECOUNT_CHANNEL == 1 +# define PULSECOUNT_TIM1_CHCFG GPIO_TIM1_CH1OUT +#elif CONFIG_STM32F0L0G0_TIM1_PULSECOUNT_CHANNEL == 2 +# define PULSECOUNT_TIM1_CHCFG GPIO_TIM1_CH2OUT +#elif CONFIG_STM32F0L0G0_TIM1_PULSECOUNT_CHANNEL == 3 +# define PULSECOUNT_TIM1_CHCFG GPIO_TIM1_CH3OUT +#elif CONFIG_STM32F0L0G0_TIM1_PULSECOUNT_CHANNEL == 4 +# define PULSECOUNT_TIM1_CHCFG GPIO_TIM1_CH4OUT +#else +# error Unsupported TIM1 pulse count channel +#endif + +/* Debug ********************************************************************/ + +#ifdef CONFIG_DEBUG_TIMER_INFO +# define pulsecount_dumpgpio(p,m) +# warning "pulsecount_dumpgpio not implemented" +#else +# define pulsecount_dumpgpio(p,m) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct stm32_pulsecountchan_s +{ + uint8_t channel; + uint32_t pincfg; +}; + +/* This structure represents the state of one pulsecount timer */ + +struct stm32_pulsecounttimer_s +{ + const struct pulsecount_ops_s *ops; + struct stm32_pulsecountchan_s channel; + uint8_t timid; + uint8_t timtype; + uint8_t irq; + uint32_t prev; + uint32_t curr; + uint32_t count; + uint32_t base; + uint32_t pclk; + void *handle; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register access */ + +static uint32_t stm32pulsecount_getreg(struct stm32_pulsecounttimer_s *priv, + int offset); +static void stm32pulsecount_putreg(struct stm32_pulsecounttimer_s *priv, + int offset, uint32_t value); +static void stm32pulsecount_modifyreg(struct stm32_pulsecounttimer_s *priv, + uint32_t offset, uint32_t clearbits, + uint32_t setbits); + +#ifdef CONFIG_DEBUG_TIMER_INFO +static void stm32pulsecount_dumpregs(struct stm32_pulsecounttimer_s *priv, + const char *msg); +#else +# define stm32pulsecount_dumpregs(priv,msg) +#endif + +/* Timer management */ + +static int +stm32pulsecount_output_configure(struct stm32_pulsecounttimer_s *priv, + uint8_t channel); +static int stm32pulsecount_timer(struct stm32_pulsecounttimer_s *priv, + const struct pulsecount_info_s *info); +static void stm32pulsecount_setapbclock( + struct stm32_pulsecounttimer_s *priv, bool on); +static int stm32pulsecount_interrupt(struct stm32_pulsecounttimer_s *priv); +static int stm32pulsecount_tim1interrupt(int irq, void *context, void *arg); +static uint32_t stm32pulsecount_pulsecount(uint32_t count); + +/* Pulsecount driver methods */ + +static int stm32pulsecount_setup(struct pulsecount_lowerhalf_s *dev); +static int stm32pulsecount_shutdown(struct pulsecount_lowerhalf_s *dev); + +static int stm32pulsecount_start(struct pulsecount_lowerhalf_s *dev, + const struct pulsecount_info_s *info, + void *handle); + +static int stm32pulsecount_stop(struct pulsecount_lowerhalf_s *dev); +static int stm32pulsecount_ioctl(struct pulsecount_lowerhalf_s *dev, + int cmd, unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This is the list of lower half pulsecount driver methods used by the upper + * half driver. + */ + +static const struct pulsecount_ops_s g_pulsecountops = +{ + .setup = stm32pulsecount_setup, + .shutdown = stm32pulsecount_shutdown, + .start = stm32pulsecount_start, + .stop = stm32pulsecount_stop, + .ioctl = stm32pulsecount_ioctl, +}; + +#ifdef CONFIG_STM32F0L0G0_TIM1_PULSECOUNT +static struct stm32_pulsecounttimer_s g_pulsecount1dev = +{ + .ops = &g_pulsecountops, + .timid = 1, + .channel = + { + .channel = CONFIG_STM32F0L0G0_TIM1_PULSECOUNT_CHANNEL, + .pincfg = PULSECOUNT_TIM1_CHCFG, + }, + .timtype = TIMTYPE_TIM1, + .irq = STM32_IRQ_TIM1_BRK, + .base = STM32_TIM1_BASE, + .pclk = PULSECOUNT_TIM1_CLKIN, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32pulsecount_reg_is_32bit + * + * Description: + * Verify whether the timer register is 32bit or not. + * + * Input Parameters: + * timtype - The type of the timer. See the TIMTYPE_* definitions + * offset - The offset to the register to read + * + * Returned Value: + * Return true for 32 bits register; false otherwise. + * + ****************************************************************************/ + +static bool stm32pulsecount_reg_is_32bit(uint8_t timtype, uint32_t offset) +{ + if (timtype == TIMTYPE_ADVANCED) + { + if (offset == STM32_ATIM_CR2_OFFSET || + offset == STM32_ATIM_CCMR1_OFFSET || + offset == STM32_ATIM_CCMR2_OFFSET || + offset == STM32_ATIM_CCER_OFFSET || + offset == STM32_ATIM_BDTR_OFFSET || + offset == STM32_ATIM_DMAR_OFFSET || + offset == STM32_ATIM_AF1_OFFSET || + offset == STM32_ATIM_TISEL_OFFSET) + { + return true; + } + } + + return false; +} + +/**************************************************************************** + * Name: stm32pulsecount_getreg + * + * Description: + * Read the value of an pulsecount timer register + * + * Input Parameters: + * priv - A reference to the pulsecount timer status + * offset - The offset to the register to read + * + * Returned Value: + * The current contents of the specified register + * + ****************************************************************************/ + +static uint32_t stm32pulsecount_getreg( + struct stm32_pulsecounttimer_s *priv, int offset) +{ + uint32_t retval; + + if (stm32pulsecount_reg_is_32bit(priv->timtype, offset) == true) + { + /* 32-bit register */ + + retval = getreg32(priv->base + offset); + } + else + { + /* 16-bit register */ + + retval = getreg16(priv->base + offset); + } + + /* Return 32-bit value */ + + return retval; +} + +/**************************************************************************** + * Name: stm32pulsecount_putreg + * + * Description: + * Read the value of an pulsecount timer register + * + * Input Parameters: + * priv - A reference to the pulsecount timer status + * offset - The offset to the register to read + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32pulsecount_putreg(struct stm32_pulsecounttimer_s *priv, + int offset, uint32_t value) +{ + if (stm32pulsecount_reg_is_32bit(priv->timtype, offset) == true) + { + /* 32-bit register */ + + putreg32(value, priv->base + offset); + } + else + { + /* 16-bit register */ + + putreg16((uint16_t)value, priv->base + offset); + } +} + +/**************************************************************************** + * Name: stm32pulsecount_modifyreg + * + * Description: + * Modify timer register (32-bit or 16-bit) + * + * Input Parameters: + * priv - A reference to the pulsecount timer status + * offset - The offset to the register to read + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32pulsecount_modifyreg(struct stm32_pulsecounttimer_s *priv, + uint32_t offset, uint32_t clearbits, + uint32_t setbits) +{ + if (stm32pulsecount_reg_is_32bit(priv->timtype, offset) == true) + { + /* 32-bit register */ + + modifyreg32(priv->base + offset, clearbits, setbits); + } + else + { + /* 16-bit register */ + + modifyreg16(priv->base + offset, clearbits, setbits); + } +} + +/**************************************************************************** + * Name: stm32pulsecount_dumpregs + * + * Description: + * Dump all timer registers. + * + * Input Parameters: + * priv - A reference to the pulsecount timer status + * msg - A message to be printed on the screen + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_TIMER_INFO +static void stm32pulsecount_dumpregs(struct stm32_pulsecounttimer_s *priv, + const char *msg) +{ + _info("%s:\n", msg); + _info(" CR1: %04x CR2: %04x SMCR: %04x DIER: %04x\n", + stm32pulsecount_getreg(priv, STM32_GTIM_CR1_OFFSET), + stm32pulsecount_getreg(priv, STM32_GTIM_CR2_OFFSET), + stm32pulsecount_getreg(priv, STM32_GTIM_SMCR_OFFSET), + stm32pulsecount_getreg(priv, STM32_GTIM_DIER_OFFSET)); + _info(" SR: %04x EGR: %04x CCMR1: %04x CCMR2: %04x\n", + stm32pulsecount_getreg(priv, STM32_GTIM_SR_OFFSET), + stm32pulsecount_getreg(priv, STM32_GTIM_EGR_OFFSET), + stm32pulsecount_getreg(priv, STM32_GTIM_CCMR1_OFFSET), + stm32pulsecount_getreg(priv, STM32_GTIM_CCMR2_OFFSET)); + _info(" CCER: %04x CNT: %04x PSC: %04x ARR: %04x\n", + stm32pulsecount_getreg(priv, STM32_GTIM_CCER_OFFSET), + stm32pulsecount_getreg(priv, STM32_GTIM_CNT_OFFSET), + stm32pulsecount_getreg(priv, STM32_GTIM_PSC_OFFSET), + stm32pulsecount_getreg(priv, STM32_GTIM_ARR_OFFSET)); + _info(" CCR1: %04x CCR2: %04x CCR3: %04x CCR4: %04x\n", + stm32pulsecount_getreg(priv, STM32_GTIM_CCR1_OFFSET), + stm32pulsecount_getreg(priv, STM32_GTIM_CCR2_OFFSET), + stm32pulsecount_getreg(priv, STM32_GTIM_CCR3_OFFSET), + stm32pulsecount_getreg(priv, STM32_GTIM_CCR4_OFFSET)); + if (priv->timtype == TIMTYPE_ADVANCED) + { + _info(" RCR: %04x BDTR: %04x DCR: %04x DMAR: %04x\n", + stm32pulsecount_getreg(priv, STM32_ATIM_RCR_OFFSET), + stm32pulsecount_getreg(priv, STM32_ATIM_BDTR_OFFSET), + stm32pulsecount_getreg(priv, STM32_ATIM_DCR_OFFSET), + stm32pulsecount_getreg(priv, STM32_ATIM_DMAR_OFFSET)); + + _info(" AF1: %04x TISEL: %04x\n", + stm32pulsecount_getreg(priv, STM32_ATIM_AF1_OFFSET), + stm32pulsecount_getreg(priv, STM32_ATIM_TISEL_OFFSET)); + } +} +#endif + +/**************************************************************************** + * Name: stm32pulsecount_output_configure + * + * Description: + * Configure pulsecount output for given channel + * + * Input Parameters: + * priv - A reference to the pulsecount timer status + * channel - Timer output channel + * + * Returned Value: + * Zero on success; + ****************************************************************************/ + +static int +stm32pulsecount_output_configure(struct stm32_pulsecounttimer_s *priv, + uint8_t channel) +{ + uint32_t cr2; + uint32_t ccer; + + /* Get current registers state */ + + cr2 = stm32pulsecount_getreg(priv, STM32_GTIM_CR2_OFFSET); + ccer = stm32pulsecount_getreg(priv, STM32_GTIM_CCER_OFFSET); + + /* Reset the output polarity level of all channels (selects high + * polarity) + */ + + ccer &= ~(GTIM_CCER_CC1P << ((channel - 1) * 4)); + + /* Enable the output state of the selected channels */ + + ccer |= (GTIM_CCER_CC1E << ((channel - 1) * 4)); + + if (priv->timtype == TIMTYPE_ADVANCED) + { + cr2 &= ~(ATIM_CR2_OIS1 << ((channel - 1) * 2)); + } + + stm32pulsecount_modifyreg(priv, STM32_GTIM_CR2_OFFSET, 0, cr2); + stm32pulsecount_modifyreg(priv, STM32_GTIM_CCER_OFFSET, 0, ccer); + + return OK; +} + +/**************************************************************************** + * Name: stm32pulsecount_timer + * + * Description: + * (Re-)initialize the timer resources and start the pulsed output + * + * Input Parameters: + * priv - A reference to the lower half pulsecount 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 stm32pulsecount_timer(struct stm32_pulsecounttimer_s *priv, + const struct pulsecount_info_s *info) +{ + /* Calculated values */ + + uint32_t prescaler; + uint32_t timclk; + uint32_t reload; + uint32_t ccr; + ub16_t duty; + uint32_t chanmode = GTIM_CCMR_MODE_PWM1; + uint8_t channel; + + /* Register contents */ + + uint32_t cr1; + uint32_t ccmr1; +#if defined(HAVE_CCMR2) + uint32_t ccmr2; + uint32_t ocmode2; +#endif + + /* New timer register bit settings */ + + uint32_t ocmode1; + + DEBUGASSERT(priv != NULL && info != NULL); + + ccmr1 = stm32pulsecount_getreg(priv, STM32_GTIM_CCMR1_OFFSET); + +#if defined(HAVE_CCMR2) + ccmr2 = stm32pulsecount_getreg(priv, STM32_GTIM_CCMR2_OFFSET); +#endif + + _info("TIM%u channel: %u high: %" PRIu32 " ns low: %" PRIu32 + " ns count: %" PRIu32 "\n", + priv->timid, priv->channel.channel, info->high_ns, + info->low_ns, info->count); + + DEBUGASSERT(pulsecount_frequency(info) > 0); + + /* Disable all interrupts and DMA requests, clear all pending status */ + + stm32pulsecount_putreg(priv, STM32_GTIM_DIER_OFFSET, 0); + stm32pulsecount_putreg(priv, STM32_GTIM_SR_OFFSET, 0); + + /* 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, 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 / pulsecount_frequency(info) + 65534) / 65535; + if (prescaler < 1) + { + prescaler = 1; + } + else if (prescaler > 65536) + { + prescaler = 65536; + } + + timclk = priv->pclk / prescaler; + + reload = timclk / pulsecount_frequency(info); + + if (reload < 2) + { + reload = 1; + } + else if (reload > 65535) + { + reload = 65535; + } + else + { + reload--; + } + + _info("TIM%u PCLK: %" PRIu32 " frequency: %" PRIu32 " " + "TIMCLK: %" PRIu32 " prescaler: %" PRIu32 + " reload: %" PRIu32 "\n", + priv->timid, priv->pclk, pulsecount_frequency(info), 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 = stm32pulsecount_getreg(priv, STM32_GTIM_CR1_OFFSET); + + /* Disable the timer until we get it configured */ + + cr1 &= ~GTIM_CR1_CEN; + + cr1 &= ~(GTIM_CR1_DIR | GTIM_CR1_CMS_MASK); + + cr1 |= GTIM_CR1_EDGE; + + /* 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; + stm32pulsecount_putreg(priv, STM32_GTIM_CR1_OFFSET, cr1); + + /* Set the reload and prescaler values */ + + stm32pulsecount_putreg(priv, STM32_GTIM_ARR_OFFSET, reload); + stm32pulsecount_putreg(priv, STM32_GTIM_PSC_OFFSET, (prescaler - 1)); + + /* Set the advanced timer's repetition counter */ + + if (priv->timtype == TIMTYPE_ADVANCED) + { + /* If a non-zero repetition count has been selected, then set the + * repetition counter to the count-1. stm32pulsecount_start() has + * already assured us that the count value is within range. + */ + + 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 = stm32pulsecount_pulsecount(info->count); + stm32pulsecount_putreg(priv, STM32_ATIM_RCR_OFFSET, + priv->prev - 1); + + /* Generate an update event to reload the prescaler. This should + * preload the RCR into active repetition counter. + */ + + stm32pulsecount_putreg(priv, STM32_ATIM_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 = stm32pulsecount_pulsecount(info->count + - priv->prev); + stm32pulsecount_putreg(priv, STM32_ATIM_RCR_OFFSET, + priv->curr - 1); + } + + /* Otherwise, just clear the repetition counter */ + + else + { + /* Set the repetition counter to zero */ + + stm32pulsecount_putreg(priv, STM32_ATIM_RCR_OFFSET, 0); + + /* Generate an update event to reload the prescaler */ + + stm32pulsecount_putreg(priv, STM32_ATIM_EGR_OFFSET, ATIM_EGR_UG); + } + } + + /* Handle channel specific setup */ + + ocmode1 = 0; +#if defined(HAVE_CCMR2) + ocmode2 = 0; +#endif + + duty = pulsecount_duty(info); + channel = priv->channel.channel; + + /* Duty cycle: + * + * duty cycle = ccr / reload (fractional value) + */ + + ccr = b16toi(duty * reload + b16HALF); + + _info("ccr: %" PRIu32 "\n", ccr); + + switch (channel) + { + case 1: + ocmode1 |= (GTIM_CCMR_CCS_CCOUT << GTIM_CCMR1_CC1S_SHIFT) | + (chanmode << GTIM_CCMR1_OC1M_SHIFT) | + GTIM_CCMR1_OC1PE; + stm32pulsecount_putreg(priv, STM32_GTIM_CCR1_OFFSET, ccr); + ccmr1 &= ~(GTIM_CCMR1_CC1S_MASK | GTIM_CCMR1_OC1M_MASK | + GTIM_CCMR1_OC1PE | GTIM_CCMR1_OC1M); + break; + + case 2: + ocmode1 |= (GTIM_CCMR_CCS_CCOUT << GTIM_CCMR1_CC2S_SHIFT) | + (chanmode << GTIM_CCMR1_OC2M_SHIFT) | + GTIM_CCMR1_OC2PE; + stm32pulsecount_putreg(priv, STM32_GTIM_CCR2_OFFSET, ccr); + ccmr1 &= ~(GTIM_CCMR1_CC2S_MASK | GTIM_CCMR1_OC2M_MASK | + GTIM_CCMR1_OC2PE | GTIM_CCMR1_OC2M); + break; + + case 3: + ocmode2 |= (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR2_CC3S_SHIFT) | + (chanmode << ATIM_CCMR2_OC3M_SHIFT) | + ATIM_CCMR2_OC3PE; + stm32pulsecount_putreg(priv, STM32_ATIM_CCR3_OFFSET, ccr); + ccmr2 &= ~(ATIM_CCMR2_CC3S_MASK | ATIM_CCMR2_OC3M_MASK | + ATIM_CCMR2_OC3PE | ATIM_CCMR2_OC3M); + break; + + case 4: + ocmode2 |= (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR2_CC4S_SHIFT) | + (chanmode << ATIM_CCMR2_OC4M_SHIFT) | + ATIM_CCMR2_OC4PE; + stm32pulsecount_putreg(priv, STM32_ATIM_CCR4_OFFSET, ccr); + ccmr2 &= ~(ATIM_CCMR2_CC4S_MASK | ATIM_CCMR2_OC4M_MASK | + ATIM_CCMR2_OC4PE | ATIM_CCMR2_OC4M); + break; + + default: + _err("ERROR: No such channel: %u\n", channel); + return -EINVAL; + } + + stm32pulsecount_output_configure(priv, channel); + + ccmr1 |= ocmode1; +#if defined(HAVE_CCMR2) + ccmr2 |= ocmode2; +#endif + + if (priv->timtype == TIMTYPE_ADVANCED) + { + uint32_t bdtr; + + /* Get current register state */ + + bdtr = stm32pulsecount_getreg(priv, STM32_ATIM_BDTR_OFFSET); + + bdtr &= ~(ATIM_BDTR_OSSI | ATIM_BDTR_OSSR); + bdtr |= ATIM_BDTR_MOE; + + stm32pulsecount_putreg(priv, STM32_ATIM_BDTR_OFFSET, bdtr); + } + + /* Save the modified register values */ + + putreg32(ccmr1, priv->base + STM32_GTIM_CCMR1_OFFSET); +#if defined(HAVE_CCMR2) + putreg32(ccmr2, priv->base + STM32_ATIM_CCMR2_OFFSET); +#endif + + /* Set the ARR Preload Bit */ + + cr1 = stm32pulsecount_getreg(priv, STM32_GTIM_CR1_OFFSET); + cr1 |= GTIM_CR1_ARPE; + stm32pulsecount_putreg(priv, STM32_GTIM_CR1_OFFSET, cr1); + + /* Setup update interrupt. If info->count is > 0, then we can + * be assured that stm32pulsecount_start() has already verified: (1) that + * this is an advanced timer, and that (2) the repetition count is within + * range. + */ + + if (info->count > 0) + { + /* Clear all pending interrupts and enable the update interrupt. */ + + stm32pulsecount_putreg(priv, STM32_GTIM_SR_OFFSET, 0); + stm32pulsecount_putreg(priv, STM32_GTIM_DIER_OFFSET, GTIM_DIER_UIE); + + /* Enable the timer */ + + cr1 |= GTIM_CR1_CEN; + stm32pulsecount_putreg(priv, STM32_GTIM_CR1_OFFSET, cr1); + + /* And enable timer interrupts at the NVIC */ + + up_enable_irq(priv->irq); + } + else + { + /* Just enable the timer, leaving all interrupts disabled */ + + cr1 |= GTIM_CR1_CEN; + stm32pulsecount_putreg(priv, STM32_GTIM_CR1_OFFSET, cr1); + } + + stm32pulsecount_dumpregs(priv, "After starting"); + return OK; +} + +/**************************************************************************** + * Name: stm32pulsecount_interrupt + * + * Description: + * Handle timer interrupts. + * + * Input Parameters: + * priv - A reference to the lower half pulsecount driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + ****************************************************************************/ + +static int stm32pulsecount_interrupt(struct stm32_pulsecounttimer_s *priv) +{ + uint16_t regval; + + /* Verify that this is an update interrupt. Nothing else is expected. */ + + regval = stm32pulsecount_getreg(priv, STM32_ATIM_SR_OFFSET); + DEBUGASSERT((regval & ATIM_SR_UIF) != 0); + + /* Clear the UIF interrupt bit */ + + stm32pulsecount_putreg(priv, STM32_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 = stm32pulsecount_getreg(priv, STM32_ATIM_BDTR_OFFSET); + regval &= ~ATIM_BDTR_MOE; + stm32pulsecount_putreg(priv, STM32_ATIM_BDTR_OFFSET, regval); + + /* Disable first interrupts, stop and reset the timer */ + + stm32pulsecount_stop((struct pulsecount_lowerhalf_s *)priv); + + /* Then perform the callback into the upper half driver */ + + pulsecount_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 = stm32pulsecount_pulsecount(priv->count - priv->prev); + stm32pulsecount_putreg(priv, STM32_ATIM_RCR_OFFSET, priv->curr - 1); + } + + /* Now all of the time critical stuff is done so we can do some debug + * output + */ + + _info("Update interrupt SR: %04x prev: %" PRIu32 " curr: %" PRIu32 + " count: %" PRIu32 "\n", + regval, priv->prev, priv->curr, priv->count); + + return OK; +} + +/**************************************************************************** + * Name: stm32pulsecount_tim1interrupt + * + * Description: + * Handle timer 1 interrupts. + * + * Input Parameters: + * Standard NuttX interrupt inputs + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int stm32pulsecount_tim1interrupt(int irq, void *context, void *arg) +{ + return stm32pulsecount_interrupt(&g_pulsecount1dev); +} + +/**************************************************************************** + * Name: stm32pulsecount_pulsecount + * + * Description: + * Pick an optimal pulse count to program the RCR. + * + * Input Parameters: + * count - The total count remaining + * + * Returned Value: + * The recommended pulse count + * + ****************************************************************************/ + +static uint32_t stm32pulsecount_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; + } +} + +/**************************************************************************** + * Name: stm32pulsecount_setapbclock + * + * Description: + * Enable or disable APB clock for the timer peripheral + * + * Input Parameters: + * dev - A reference to the lower half pulsecount driver state structure + * on - Enable clock if 'on' is 'true' and disable if 'false' + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32pulsecount_setapbclock( + struct stm32_pulsecounttimer_s *priv, bool on) +{ + uint32_t en_bit; + uint32_t regaddr; + + /* Determine which timer to configure */ + + switch (priv->timid) + { +#ifdef CONFIG_STM32F0L0G0_TIM1_PULSECOUNT + case 1: + regaddr = STM32_RCC_APB2ENR; + en_bit = RCC_APB2ENR_TIM1EN; + 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: stm32pulsecount_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 pulsecount 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 stm32pulsecount_setup(struct pulsecount_lowerhalf_s *dev) +{ + struct stm32_pulsecounttimer_s *priv = + (struct stm32_pulsecounttimer_s *)dev; + uint32_t pincfg; + + _info("TIM%u\n", priv->timid); + stm32pulsecount_dumpregs(priv, "Initially"); + + /* Enable APB1/2 clocking for timer. */ + + stm32pulsecount_setapbclock(priv, true); + + /* Configure the pulsecount output pins, but do not start the timer yet */ + + pincfg = priv->channel.pincfg; + if (pincfg != 0) + { + _info("pincfg: %08" PRIx32 "\n", pincfg); + + stm32_configgpio(pincfg); + } + + pulsecount_dumpgpio(pincfg, "pulsecount setup"); + + return OK; +} + +/**************************************************************************** + * Name: stm32pulsecount_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 pulsecount driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int stm32pulsecount_shutdown(struct pulsecount_lowerhalf_s *dev) +{ + struct stm32_pulsecounttimer_s *priv = + (struct stm32_pulsecounttimer_s *)dev; + uint32_t pincfg; + + _info("TIM%u\n", priv->timid); + + /* Make sure that the output has been stopped */ + + stm32pulsecount_stop(dev); + + /* Disable APB1/2 clocking for timer. */ + + stm32pulsecount_setapbclock(priv, false); + + /* Then put the GPIO pins back to the default state */ + + pincfg = priv->channel.pincfg; + if (pincfg != 0) + { + _info("pincfg: %08" PRIx32 "\n", pincfg); + + pincfg &= (GPIO_PORT_MASK | GPIO_PIN_MASK); + pincfg |= GPIO_INPUT | GPIO_FLOAT; + + stm32_configgpio(pincfg); + } + + return OK; +} + +/**************************************************************************** + * Name: stm32pulsecount_start + * + * Description: + * (Re-)initialize the timer resources and start the pulsed output + * + * Input Parameters: + * dev - A reference to the lower half pulsecount 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 stm32pulsecount_start(struct pulsecount_lowerhalf_s *dev, + const struct pulsecount_info_s *info, + void *handle) +{ + struct stm32_pulsecounttimer_s *priv = + (struct stm32_pulsecounttimer_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) + { + _err("ERROR: TIM%u cannot support pulse count: %" PRIu32 "\n", + priv->timid, info->count); + return -EPERM; + } + } + + /* Save the handle */ + + priv->handle = handle; + + /* Start the time */ + + return stm32pulsecount_timer(priv, info); +} + +/**************************************************************************** + * Name: stm32pulsecount_stop + * + * Description: + * Stop the pulsed output and reset the timer resources + * + * Input Parameters: + * dev - A reference to the lower half pulsecount 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 stm32pulsecount_stop(struct pulsecount_lowerhalf_s *dev) +{ + struct stm32_pulsecounttimer_s *priv = + (struct stm32_pulsecounttimer_s *)dev; + uint32_t resetbit; + uint32_t regaddr; + uint32_t regval; + irqstate_t flags; + + _info("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(); + + /* Disable further interrupts and stop the timer */ + + stm32pulsecount_putreg(priv, STM32_GTIM_DIER_OFFSET, 0); + stm32pulsecount_putreg(priv, STM32_GTIM_SR_OFFSET, 0); + + /* Determine which timer to reset */ + + switch (priv->timid) + { +#ifdef CONFIG_STM32F0L0G0_TIM1_PULSECOUNT + case 1: + regaddr = STM32_RCC_APB2RSTR; + resetbit = RCC_APB2RSTR_TIM1RST; + break; +#endif + + default: + leave_critical_section(flags); + return -EINVAL; + } + + /* Reset the timer - stopping the output and putting the timer back + * into a state where stm32pulsecount_start() can be called. + */ + + regval = getreg32(regaddr); + regval |= resetbit; + putreg32(regval, regaddr); + + regval &= ~resetbit; + putreg32(regval, regaddr); + leave_critical_section(flags); + + _info("regaddr: %08" PRIx32 " resetbit: %08" PRIx32 "\n", + regaddr, resetbit); + stm32pulsecount_dumpregs(priv, "After stop"); + return OK; +} + +/**************************************************************************** + * Name: stm32pulsecount_ioctl + * + * Description: + * Lower-half logic may support platform-specific ioctl commands + * + * Input Parameters: + * dev - A reference to the lower half pulsecount 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 stm32pulsecount_ioctl(struct pulsecount_lowerhalf_s *dev, int cmd, + unsigned long arg) +{ +#ifdef CONFIG_DEBUG_TIMER_INFO + struct stm32_pulsecounttimer_s *priv = + (struct stm32_pulsecounttimer_s *)dev; + + /* There are no platform-specific ioctl commands */ + + _info("TIM%u\n", priv->timid); +#endif + return -ENOTTY; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_pulsecountinitialize + * + * Description: + * Initialize one timer for use with the upper-level pulsecount driver. + * + * Input Parameters: + * timer - A number identifying the timer use. The number of valid timer + * IDs varies with the STM32 MCU and MCU family. This pulsecount driver + * supports TIM1 only on STM32F0/L0/G0. + * + * Returned Value: + * On success, a pointer to the STM32 lower half pulsecount driver is + * returned. NULL is returned on any failure. + * + ****************************************************************************/ + +struct pulsecount_lowerhalf_s *stm32_pulsecountinitialize(int timer) +{ + struct stm32_pulsecounttimer_s *lower; + + _info("TIM%u\n", timer); + + switch (timer) + { +#ifdef CONFIG_STM32F0L0G0_TIM1_PULSECOUNT + case 1: + lower = &g_pulsecount1dev; + + /* Attach but disable the TIM1 update interrupt */ + + irq_attach(lower->irq, stm32pulsecount_tim1interrupt, NULL); + up_disable_irq(lower->irq); + break; +#endif + + default: + _err("ERROR: No such timer configured\n"); + return NULL; + } + + return (struct pulsecount_lowerhalf_s *)lower; +} + +#endif /* CONFIG_STM32F0L0G0_TIMx_PULSECOUNT */ diff --git a/arch/arm/src/stm32f0l0g0/stm32_pulsecount.h b/arch/arm/src/stm32f0l0g0/stm32_pulsecount.h new file mode 100644 index 00000000000..8c8aca535f1 --- /dev/null +++ b/arch/arm/src/stm32f0l0g0/stm32_pulsecount.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * arch/arm/src/stm32f0l0g0/stm32_pulsecount.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32F0L0G0_STM32_PULSECOUNT_H +#define __ARCH_ARM_SRC_STM32F0L0G0_STM32_PULSECOUNT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/timers/pulsecount.h> + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +struct pulsecount_lowerhalf_s *stm32_pulsecountinitialize(int timer); + +#endif /* __ARCH_ARM_SRC_STM32F0L0G0_STM32_PULSECOUNT_H */ diff --git a/arch/arm/src/stm32f0l0g0/stm32_pwm.c b/arch/arm/src/stm32f0l0g0/stm32_pwm.c index 2aff3c8e1f9..df066d05163 100644 --- a/arch/arm/src/stm32f0l0g0/stm32_pwm.c +++ b/arch/arm/src/stm32f0l0g0/stm32_pwm.c @@ -172,20 +172,10 @@ struct stm32_pwmtimer_s uint8_t timid; /* Timer ID {1,...,17} */ 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 pulse 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 - void *handle; /* Handle used for upper-half callback */ -#endif }; /**************************************************************************** @@ -218,31 +208,13 @@ static int stm32pwm_update_duty(struct stm32_pwmtimer_s *priv, uint8_t channel, ub16_t duty); static void stm32pwm_setapbclock(struct stm32_pwmtimer_s *priv, bool on); -#if defined(CONFIG_PWM_PULSECOUNT) && \ - (defined(CONFIG_STM32F0L0G0_TIM1_PWM) || defined(CONFIG_STM32F0L0G0_TIM8_PWM)) -static int stm32pwm_interrupt(struct stm32_pwmtimer_s *priv); -# if defined(CONFIG_STM32F0L0G0_TIM1_PWM) -static int stm32pwm_tim1interrupt(int irq, void *context, void *arg); -# endif -# if defined(CONFIG_STM32F0L0G0_TIM8_PWM) -static int stm32pwm_tim8interrupt(int irq, void *context, void *arg); -# endif -static uint8_t stm32pwm_pulsecount(uint32_t count); -#endif /* CONFIG_PWM_PULSECOUNT && CONFIG_STM32F0L0G0_TIM{1,8}_PWM */ - /* PWM driver methods */ static int stm32pwm_setup(struct pwm_lowerhalf_s *dev); static int stm32pwm_shutdown(struct pwm_lowerhalf_s *dev); -#ifdef CONFIG_PWM_PULSECOUNT -static int stm32pwm_start(struct pwm_lowerhalf_s *dev, - const struct pwm_info_s *info, - void *handle); -#else static int stm32pwm_start(struct pwm_lowerhalf_s *dev, const struct pwm_info_s *info); -#endif static int stm32pwm_stop(struct pwm_lowerhalf_s *dev); static int stm32pwm_ioctl(struct pwm_lowerhalf_s *dev, @@ -307,9 +279,6 @@ static struct stm32_pwmtimer_s g_pwm1dev = }, .timtype = TIMTYPE_TIM1, .mode = CONFIG_STM32F0L0G0_TIM1_MODE, -#ifdef CONFIG_PWM_PULSECOUNT - .irq = STM32_IRQ_TIM1UP, -#endif .base = STM32_TIM1_BASE, .pclk = STM32_APB2_TIM1_CLKIN, }; @@ -357,9 +326,6 @@ static struct stm32_pwmtimer_s g_pwm2dev = }, .timtype = TIMTYPE_TIM2, .mode = CONFIG_STM32F0L0G0_TIM2_MODE, -#ifdef CONFIG_PWM_PULSECOUNT - .irq = STM32_IRQ_TIM2, -#endif .base = STM32_TIM2_BASE, .pclk = STM32_APB1_TIM2_CLKIN, }; @@ -407,9 +373,6 @@ static struct stm32_pwmtimer_s g_pwm3dev = }, .timtype = TIMTYPE_TIM3, .mode = CONFIG_STM32F0L0G0_TIM3_MODE, -#ifdef CONFIG_PWM_PULSECOUNT - .irq = STM32_IRQ_TIM3, -#endif .base = STM32_TIM3_BASE, .pclk = STM32_APB1_TIM3_CLKIN, }; @@ -433,9 +396,6 @@ static struct stm32_pwmtimer_s g_pwm14dev = }, .timtype = TIMTYPE_TIM14, .mode = STM32_TIMMODE_COUNTUP, -#ifdef CONFIG_PWM_PULSECOUNT - .irq = STM32_IRQ_TIM14, -#endif .base = STM32_TIM14_BASE, .pclk = STM32_APB2_TIM14_CLKIN, }; @@ -467,9 +427,6 @@ static struct stm32_pwmtimer_s g_pwm15dev = }, .timtype = TIMTYPE_TIM15, .mode = STM32_TIMMODE_COUNTUP, -#ifdef CONFIG_PWM_PULSECOUNT - .irq = STM32_IRQ_TIM15, -#endif .base = STM32_TIM15_BASE, .pclk = STM32_APB2_TIM15_CLKIN, }; @@ -493,9 +450,6 @@ static struct stm32_pwmtimer_s g_pwm16dev = }, .timtype = TIMTYPE_TIM16, .mode = STM32_TIMMODE_COUNTUP, -#ifdef CONFIG_PWM_PULSECOUNT - .irq = STM32_IRQ_TIM16, -#endif .base = STM32_TIM16_BASE, .pclk = STM32_APB2_TIM16_CLKIN, }; @@ -519,9 +473,6 @@ static struct stm32_pwmtimer_s g_pwm17dev = }, .timtype = TIMTYPE_TIM17, .mode = STM32_TIMMODE_COUNTUP, -#ifdef CONFIG_PWM_PULSECOUNT - .irq = STM32_IRQ_TIM17, -#endif .base = STM32_TIM17_BASE, .pclk = STM32_APB2_TIM17_CLKIN, }; @@ -882,25 +833,13 @@ static int stm32pwm_timer(struct stm32_pwmtimer_s *priv, ccmr2 = stm32pwm_getreg(priv, STM32_GTIM_CCMR2_OFFSET); #endif -#ifdef CONFIG_PWM_PULSECOUNT - pwminfo("TIM%u channel: %u frequency: %" PRIu32 " duty: %08" PRIx32 - " count: %u\n", - priv->timid, priv->channels[0].channel, info->frequency, - info->channels[0].duty, info->channels[0].count); -#else pwminfo("TIM%u frequency: %" PRIu32 "\n", priv->timid, info->frequency); -#endif DEBUGASSERT(info->frequency > 0); /* Disable all interrupts and DMA requests, clear all pending status */ -#ifdef CONFIG_PWM_PULSECOUNT - stm32pwm_putreg(priv, STM32_GTIM_DIER_OFFSET, 0); - stm32pwm_putreg(priv, STM32_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 * @@ -1070,41 +1009,6 @@ static int stm32pwm_timer(struct stm32_pwmtimer_s *priv, * assured us that the count value is within range). */ -#ifdef CONFIG_PWM_PULSECOUNT - if (info->channels[0].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 = stm32pwm_pulsecount(info->channels[0].count); - stm32pwm_putreg(priv, STM32_ATIM_RCR_OFFSET, priv->prev - 1); - - /* Generate an update event to reload the prescaler. This should - * preload the RCR into active repetition counter. - */ - - stm32pwm_putreg(priv, STM32_ATIM_EGR_OFFSET, ATIM_EGR_UG); - - /* Now set the value of the RCR that will be loaded on the next - * update event. - */ - - priv->count = info->channels[0].count; - priv->curr = stm32pwm_pulsecount(info->channels[0].count - - priv->prev); - stm32pwm_putreg(priv, STM32_ATIM_RCR_OFFSET, priv->curr - 1); - } - - /* Otherwise, just clear the repetition counter */ - - else -#endif { /* Set the repetition counter to zero */ @@ -1381,43 +1285,15 @@ static int stm32pwm_timer(struct stm32_pwmtimer_s *priv, cr1 |= GTIM_CR1_ARPE; stm32pwm_putreg(priv, STM32_GTIM_CR1_OFFSET, cr1); - /* Setup update interrupt. If info->channels[0].count is > 0, then we can - * be assured that stm32pwm_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->channels[0].count > 0) - { - /* Clear all pending interrupts and enable the update interrupt. */ - - stm32pwm_putreg(priv, STM32_GTIM_SR_OFFSET, 0); - stm32pwm_putreg(priv, STM32_GTIM_DIER_OFFSET, GTIM_DIER_UIE); - - /* Enable the timer */ - - cr1 |= GTIM_CR1_CEN; - stm32pwm_putreg(priv, STM32_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 */ + /* Just enable the timer, leaving all interrupts disabled */ - cr1 |= GTIM_CR1_CEN; - stm32pwm_putreg(priv, STM32_GTIM_CR1_OFFSET, cr1); - } + cr1 |= GTIM_CR1_CEN; + stm32pwm_putreg(priv, STM32_GTIM_CR1_OFFSET, cr1); stm32pwm_dumpregs(priv, "After starting"); return OK; } -#ifndef CONFIG_PWM_PULSECOUNT /**************************************************************************** * Name: stm32pwm_update_duty * @@ -1493,174 +1369,6 @@ static int stm32pwm_update_duty(struct stm32_pwmtimer_s *priv, return OK; } -#endif - -/**************************************************************************** - * Name: stm32pwm_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 - * - * Input Parameters: - * dev - A reference to the lower half PWM driver state structure - * channel - Timer output channel - * mode - PWM mode. See stm32_chanmode_e - * - * Returned Values: - * Zero on success; a negated errno value on failure - ****************************************************************************/ - -#if defined(CONFIG_PWM_PULSECOUNT) && (defined(CONFIG_STM32F0L0G0_TIM1_PWM) || defined(CONFIG_STM32F0L0G0_TIM8_PWM)) -static int stm32pwm_interrupt(struct stm32_pwmtimer_s *priv) -{ - uint16_t regval; - - /* Verify that this is an update interrupt. Nothing else is expected. */ - - regval = stm32pwm_getreg(priv, STM32_ATIM_SR_OFFSET); - DEBUGASSERT((regval & ATIM_SR_UIF) != 0); - - /* Clear the UIF interrupt bit */ - - stm32pwm_putreg(priv, STM32_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 = stm32pwm_getreg(priv, STM32_ATIM_BDTR_OFFSET); - regval &= ~ATIM_BDTR_MOE; - stm32pwm_putreg(priv, STM32_ATIM_BDTR_OFFSET, regval); - - /* Disable first interrupts, stop and reset the timer */ - - stm32pwm_stop((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 = stm32pwm_pulsecount(priv->count - priv->prev); - stm32pwm_putreg(priv, STM32_ATIM_RCR_OFFSET, 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_STM32F0L0G0_TIM1_PWM) -static int stm32pwm_tim1interrupt(int irq, void *context, void *arg) -{ - return stm32pwm_interrupt(&g_pwm1dev); -} -#endif - -#if defined(CONFIG_PWM_PULSECOUNT) && defined(CONFIG_STM32F0L0G0_TIM8_PWM) -static int stm32pwm_tim8interrupt(int irq, void *context, void *arg) -{ - return stm32pwm_interrupt(&g_pwm8dev); -} -#endif - -/**************************************************************************** - * Name: stm32pwm_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_STM32F0L0G0_TIM1_PWM) || defined(CONFIG_STM32F0L0G0_TIM8_PWM)) -static uint8_t stm32pwm_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: stm32pwm_setapbclock @@ -1882,43 +1590,12 @@ static int stm32pwm_shutdown(struct pwm_lowerhalf_s *dev) * ****************************************************************************/ -#ifdef CONFIG_PWM_PULSECOUNT -static int stm32pwm_start(struct pwm_lowerhalf_s *dev, - const struct pwm_info_s *info, - void *handle) -{ - struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; - - /* Check if a pulsecount has been selected */ - - if (info->channels[0].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->channels[0].count); - return -EPERM; - } - } - - /* Save the handle */ - - priv->handle = handle; - - /* Start the time */ - - return stm32pwm_timer(priv, info); -} -#else static int stm32pwm_start(struct pwm_lowerhalf_s *dev, const struct pwm_info_s *info) { int ret = OK; struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; -#ifndef CONFIG_PWM_PULSECOUNT /* if frequency has not changed we just update duty */ if (info->frequency == priv->frequency) @@ -1944,23 +1621,19 @@ static int stm32pwm_start(struct pwm_lowerhalf_s *dev, } } else -#endif { ret = stm32pwm_timer(priv, info); -#ifndef CONFIG_PWM_PULSECOUNT /* Save current frequency */ if (ret == OK) { priv->frequency = info->frequency; } -#endif } return ret; } -#endif /**************************************************************************** * Name: stm32pwm_stop @@ -2167,10 +1840,6 @@ struct pwm_lowerhalf_s *stm32_pwminitialize(int timer) /* Attach but disable the TIM1 update interrupt */ -#ifdef CONFIG_PWM_PULSECOUNT - irq_attach(lower->irq, stm32pwm_tim1interrupt, NULL); - up_disable_irq(lower->irq); -#endif break; #endif @@ -2204,10 +1873,6 @@ struct pwm_lowerhalf_s *stm32_pwminitialize(int timer) /* Attach but disable the TIM8 update interrupt */ -#ifdef CONFIG_PWM_PULSECOUNT - irq_attach(lower->irq, stm32pwm_tim8interrupt, NULL); - up_disable_irq(lower->irq); -#endif break; #endif
