diff --git a/cpu/kinetis_common/periph/pwm.c b/cpu/kinetis_common/periph/pwm.c index f6166a1963bebce83488c2842ea4fef2f10eb6a5..6d1d98b3d53f38ec5e212ac09c8c70037eaffb26 100644 --- a/cpu/kinetis_common/periph/pwm.c +++ b/cpu/kinetis_common/periph/pwm.c @@ -35,6 +35,22 @@ static inline FTM_Type *ftm(pwm_t pwm) return pwm_config[pwm].ftm; } +static void poweron(pwm_t pwm) +{ + int ftm = pwm_config[pwm].ftm_num; + +#ifdef SIM_SCGC6_FTM2_SHIFT + BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_FTM0_SHIFT + ftm) = 1; +#else + if (ftm < 2) { + BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_FTM0_SHIFT + ftm) = 1; + } + else if (ftm == 2) { + BITBAND_REG32(SIM->SCGC3, SIM_SCGC3_FTM2_SHIFT) = 1; + } +#endif +} + uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res) { uint8_t pre = 0; @@ -60,7 +76,7 @@ uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res) } /* configure the used timer */ - pwm_poweron(pwm); + poweron(pwm); /* disable write protect for changing settings */ ftm(pwm)->MODE = FTM_MODE_WPDIS_MASK; /* clear any existing configuration */ @@ -88,7 +104,7 @@ uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res) } /* and now we start the actual waveform generation */ - pwm_start(pwm); + ftm(pwm)->SC |= FTM_SC_CLKS(1); /* finally we need to return the actual applied PWM frequency */ return (CLOCK_BUSCLOCK >> pre) / res; @@ -106,47 +122,29 @@ void pwm_set(pwm_t pwm, uint8_t channel, uint16_t value) ftm(pwm)->CONTROLS[pwm_config[pwm].chan[channel].ftm_chan].CnV = value; } -void pwm_start(pwm_t pwm) -{ - assert(pwm < PWM_NUMOF); - ftm(pwm)->SC |= FTM_SC_CLKS(1); -} - -void pwm_stop(pwm_t pwm) -{ - assert(pwm < PWM_NUMOF); - ftm(pwm)->SC &= ~(FTM_SC_CLKS_MASK); -} - void pwm_poweron(pwm_t pwm) { assert(pwm < PWM_NUMOF); - int ftm = pwm_config[pwm].ftm_num; - -#ifdef SIM_SCGC6_FTM2_SHIFT - BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_FTM0_SHIFT + ftm) = 1; -#else - if (ftm < 2) { - BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_FTM0_SHIFT + ftm) = 1; - } - else if (ftm == 2) { - BITBAND_REG32(SIM->SCGC3, SIM_SCGC3_FTM2_SHIFT) = 1; - } -#endif + poweron(pwm); + ftm(pwm)->SC |= FTM_SC_CLKS(1); } void pwm_poweroff(pwm_t pwm) { assert(pwm < PWM_NUMOF); - int ftm = pwm_config[pwm].ftm_num; + int ftm_num = pwm_config[pwm].ftm_num; + + /* disable PWM generation */ + ftm(pwm)->SC &= ~(FTM_SC_CLKS_MASK); + /* and power of the peripheral */ #ifdef SIM_SCGC6_FTM2_SHIFT - BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_FTM0_SHIFT + ftm) = 0; + BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_FTM0_SHIFT + ftm_num) = 0; #else - if (ftm < 2) { - BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_FTM0_SHIFT + ftm) = 0; + if (ftm_num < 2) { + BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_FTM0_SHIFT + ftm_num) = 0; } - else if (ftm == 2) { + else if (ftm_num == 2) { BITBAND_REG32(SIM->SCGC3, SIM_SCGC3_FTM2_SHIFT) = 0; } #endif diff --git a/cpu/lpc11u34/periph/pwm.c b/cpu/lpc11u34/periph/pwm.c index b405ffe64b068c749c9c8fcbb97954b2f104182e..decb039544ac5443eb1b706a161bc794e93d441a 100644 --- a/cpu/lpc11u34/periph/pwm.c +++ b/cpu/lpc11u34/periph/pwm.c @@ -50,7 +50,7 @@ uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res) } /* power on and configure the timer */ - pwm_poweron(pwm); + LPC_SYSCON->SYSAHBCLKCTRL |= pwm_config[pwm].clk_bit; /* enable the timer and keep it in reset state */ dev(pwm)->TCR = BIT0 | BIT1; /* set prescaler */ @@ -85,27 +85,17 @@ void pwm_set(pwm_t pwm, uint8_t channel, uint16_t value) dev(pwm)->MR[channel] = dev(pwm)->MR3 - value; } -void pwm_start(pwm_t pwm) -{ - assert(pwm < PWM_NUMOF); - dev(pwm)->TCR &= ~(BIT1); -} - -void pwm_stop(pwm_t pwm) -{ - assert(pwm < PWM_NUMOF); - dev(pwm)->TCR |= (BIT1); -} - void pwm_poweron(pwm_t pwm) { assert(pwm < PWM_NUMOF); LPC_SYSCON->SYSAHBCLKCTRL |= pwm_config[pwm].clk_bit; + dev(pwm)->TCR &= ~(BIT1); } void pwm_poweroff(pwm_t pwm) { assert(pwm < PWM_NUMOF); + dev(pwm)->TCR |= (BIT1); LPC_SYSCON->SYSAHBCLKCTRL &= ~(pwm_config[pwm].clk_bit); } diff --git a/cpu/lpc2387/periph/pwm.c b/cpu/lpc2387/periph/pwm.c index c3146aee3a6de117f8794f35e9e0cdbfa1c17f75..4cd027bde306cf0df61013675f31b763a035f3c8 100644 --- a/cpu/lpc2387/periph/pwm.c +++ b/cpu/lpc2387/periph/pwm.c @@ -49,7 +49,7 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) (PWM_FUNC << PWM_CH2_PIN * 2); /* power on PWM1 */ - pwm_poweron(dev); + PCONP |= PCPWM1; /* select PWM1 clock */ PCLKSEL0 &= ~(BIT13); @@ -108,27 +108,17 @@ void pwm_set(pwm_t dev, uint8_t channel, uint16_t value) } } -void pwm_start(pwm_t dev) -{ - assert(dev == PWM_DEV(0)); - PWM1TCR |= BIT0; -} - -void pwm_stop(pwm_t dev) -{ - assert(dev == PWM_DEV(0)); - PWM1TCR &= ~(BIT0); -} - void pwm_poweron(pwm_t dev) { assert(dev == PWM_DEV(0)); PCONP |= PCPWM1; + PWM1TCR |= BIT0; } void pwm_poweroff(pwm_t dev) { assert(dev == PWM_DEV(0)); + PWM1TCR &= ~(BIT0); PCONP &= ~(PCPWM1); } diff --git a/cpu/sam3/periph/pwm.c b/cpu/sam3/periph/pwm.c index af694c4174a62e410f928891a9b8723b65bb6e7b..fe84c92af9b2f754a9caaa2a3599d7d86ac77ae3 100644 --- a/cpu/sam3/periph/pwm.c +++ b/cpu/sam3/periph/pwm.c @@ -114,34 +114,17 @@ void pwm_set(pwm_t pwm, uint8_t channel, uint16_t value) PWM->PWM_CH_NUM[pwm_chan[channel].hwchan].PWM_CDTYUPD = value; } -void pwm_start(pwm_t pwm) -{ - assert(pwm == PWM_DEV(0)); - PWM->PWM_ENA = pwm_chan_mask; -} - -void pwm_stop(pwm_t pwm) -{ - assert(pwm == PWM_DEV(0)); - PWM->PWM_ENA = 0; -} - -/* - * The device is reactivated by by clocking the device block. - * Operation continues where it has been stopped by poweroff. - */ void pwm_poweron(pwm_t pwm) { assert(pwm == PWM_DEV(0)); PMC->PMC_PCER1 = PMC_PCDR1_PID36; + PWM->PWM_ENA = pwm_chan_mask; } -/* - * The device is set to power saving mode by disabling the clock. - */ void pwm_poweroff(pwm_t pwm) { assert(pwm == PWM_DEV(0)); + PWM->PWM_ENA = 0; PMC->PMC_PCDR1 = PMC_PCDR1_PID36; } diff --git a/cpu/samd21/periph/pwm.c b/cpu/samd21/periph/pwm.c index 2c96f23dfc8b4d20f4a3eb8351e72d55168e47ac..6b073412bb83fa7e3bf1157529fe73ac2735965f 100644 --- a/cpu/samd21/periph/pwm.c +++ b/cpu/samd21/periph/pwm.c @@ -87,6 +87,15 @@ static uint8_t get_prescaler(unsigned int target, int *scale) return target - 1; } +static void poweron(pwm_t dev) +{ + PM->APBCMASK.reg |= (PM_APBCMASK_TCC0 << _num(dev)); + GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_CLKEN | + GCLK_CLKCTRL_GEN_GCLK0 | + GCLK_CLKCTRL_ID(_clk_id(dev))); + while (GCLK->STATUS.bit.SYNCBUSY) {} +} + uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) { uint8_t prescaler; @@ -113,7 +122,7 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) } /* power on the device */ - pwm_poweron(dev); + poweron(dev); /* reset TCC module */ _tcc(dev)->CTRLA.reg = TCC_CTRLA_SWRST; @@ -142,7 +151,7 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) _tcc(dev)->PER.reg = (res - 1); while (_tcc(dev)->SYNCBUSY.reg & TCC_SYNCBUSY_PER) {} /* start PWM operation */ - pwm_start(dev); + _tcc(dev)->CTRLA.reg |= (TCC_CTRLA_ENABLE); /* return the actual frequency the PWM is running at */ return f_real; } @@ -162,36 +171,17 @@ void pwm_set(pwm_t dev, uint8_t channel, uint16_t value) while (_tcc(dev)->SYNCBUSY.reg & (TCC_SYNCBUSY_CC0 << _chan(dev, channel))) {} } -void pwm_start(pwm_t dev) +void pwm_poweron(pwm_t dev) { + poweron(dev); _tcc(dev)->CTRLA.reg |= (TCC_CTRLA_ENABLE); } -void pwm_stop(pwm_t dev) +void pwm_poweroff(pwm_t dev) { _tcc(dev)->CTRLA.reg &= ~(TCC_CTRLA_ENABLE); -} -void pwm_poweron(pwm_t dev) -{ - int num = _num(dev); - if (num < 0) { - return; - } - PM->APBCMASK.reg |= (PM_APBCMASK_TCC0 << num); - GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_CLKEN | - GCLK_CLKCTRL_GEN_GCLK0 | - GCLK_CLKCTRL_ID(_clk_id(dev))); - while (GCLK->STATUS.bit.SYNCBUSY) {} -} - -void pwm_poweroff(pwm_t dev) -{ - int num = _num(dev); - if (num < 0) { - return; - } - PM->APBCMASK.reg &= ~(PM_APBCMASK_TCC0 << num); + PM->APBCMASK.reg &= ~(PM_APBCMASK_TCC0 << _num(dev)); GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_GEN_GCLK7 | GCLK_CLKCTRL_ID(_clk_id(dev))); while (GCLK->STATUS.bit.SYNCBUSY) {} diff --git a/cpu/stm32_common/periph/pwm.c b/cpu/stm32_common/periph/pwm.c index 511ecf71ff96de34833193058a7fdad226804b4d..15892d9819b8812f3ca6ad993ca09b76235080be 100644 --- a/cpu/stm32_common/periph/pwm.c +++ b/cpu/stm32_common/periph/pwm.c @@ -49,7 +49,7 @@ uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res) assert((pwm < PWM_NUMOF) && ((freq * res) < bus_clk)); /* power on the used timer */ - pwm_poweron(pwm); + periph_clk_en(pwm_config[pwm].bus, pwm_config[pwm].rcc_mask); /* reset configuration and CC channels */ dev(pwm)->CR1 = 0; dev(pwm)->CR2 = 0; @@ -124,27 +124,17 @@ void pwm_set(pwm_t pwm, uint8_t channel, uint16_t value) dev(pwm)->CCR[pwm_config[pwm].chan[channel].cc_chan] = value; } -void pwm_start(pwm_t pwm) -{ - assert(pwm < PWM_NUMOF); - dev(pwm)->CR1 |= TIM_CR1_CEN; -} - -void pwm_stop(pwm_t pwm) -{ - assert(pwm < PWM_NUMOF); - dev(pwm)->CR1 &= ~TIM_CR1_CEN; -} - void pwm_poweron(pwm_t pwm) { assert(pwm < PWM_NUMOF); periph_clk_en(pwm_config[pwm].bus, pwm_config[pwm].rcc_mask); + dev(pwm)->CR1 |= TIM_CR1_CEN; } void pwm_poweroff(pwm_t pwm) { assert(pwm < PWM_NUMOF); + dev(pwm)->CR1 &= ~TIM_CR1_CEN; periph_clk_dis(pwm_config[pwm].bus, pwm_config[pwm].rcc_mask); } diff --git a/drivers/include/periph/pwm.h b/drivers/include/periph/pwm.h index 985d544b6835b65a5a8a5e6fae85bb214a4ffcd8..d9ad13ac742560eb507fdcdb58a9786c60c819e9 100644 --- a/drivers/include/periph/pwm.h +++ b/drivers/include/periph/pwm.h @@ -11,6 +11,46 @@ * @ingroup drivers_periph * @brief Low-level PWM peripheral driver * + * This interface enables access to CPU peripherals generating PWM signals. On + * most platforms, this interface will be implemented based on hardware timers, + * though some CPUs provide dedicated PWM peripherals. + * + * The characteristics of a PWM signal can be defined by three basic parameters, + * namely the frequency, the duty cycle, and the operational mode. This + * interface supports basic PWM generation in left-aligned, right-aligned, and + * center mode. Additionally the interface supports the definition of the used + * resolution, defining the granularity with which one can specify the duty + * cycle. This brings more flexibility to the configuration of the frequency, + * especially on systems with low system clocks. + * + * Typically, a single PWM device (e.g. hardware timer) supports PWM signal + * generation on multiple pins in parallel. While the duty cycle is selectable + * for each channel individually, the frequency and resolution are shared for + * all channels. + * + * The mapping/configuration of PWM devices (timers) and the used pins has to be + * done in the board configuration (the board's `periph_conf.h). + * + * When using the PWM interface, first thing you have to do is initialize the + * PWM device with the targeted mode, frequency, and resolution settings. Once + * the device is initialized, it will start the generation of PWM signals on all + * configured pins immediately, with an initial duty cycle of `0`. Use the + * pwm_set() function to change the duty cycle for a given channel. If you + * want to disable the PWM generation again, simply call pwm_poweroff(). + * + * @section sec_pm (Low-) power implications + * + * After initialization, the a PWM peripheral **should** be powered on and + * active. When manually stopped using the pwm_poweroff() function, the PWM + * generation **should** be stopped for all channels and the PWM peripheral + * **should** be fully power off (e.g. through peripheral clock gating). Once + * being re-enabled by calling the pwm_poweron() function, the PWM peripheral + * **should** transparently continue its previously configured operation, + * including the last active duty cycle values. + * + * While a PWM device is active, some implementations might need to block + * certain power modes. + * * @{ * @file * @brief Low-level PWM peripheral driver interface definitions @@ -116,40 +156,25 @@ uint8_t pwm_channels(pwm_t dev); void pwm_set(pwm_t dev, uint8_t channel, uint16_t value); /** - * @brief Start PWM generation on the given device - * - * @param[in] dev device to start - */ -void pwm_start(pwm_t dev); - -/** - * @brief Stop PWM generation on the given device + * @brief Resume PWM generation on the given device * - * @param[in] dev device to stop - */ -void pwm_stop(pwm_t dev); - -/** - * @brief Power on the PWM device + * When this function is called, the given PWM device is powered on and + * continues its previously configured operation. The duty cycle of each channel + * will be the value that was last set. * - * When the device is powered on the first time, no configuration is set. If - * the device is powered back on, after having been initialized and powered off - * before, the PWM device will continue its operation with the previously set - * configuration. So there is no need in re-initializing then. + * This function must not be called before the PWM device was initialized. * - * @param[in] dev device to power on + * @param[in] dev device to start */ void pwm_poweron(pwm_t dev); /** - * @brief Power off the given PWM device + * @brief Stop PWM generation on the given device * - * This function will power off the given PWM device, which on most platforms - * means that it will be disabled using clock gating. The implementation must - * make sure, that any previously configuration will hold when the device is - * being powered back on. + * This function stops the PWM generation on all configured channels for the + * given device and powers down the given PWM peripheral. * - * @param[in] dev device to power off + * @param[in] dev device to stop */ void pwm_poweroff(pwm_t dev);