/* * Copyright (C) 2014-2016 Freie UniversitÀt Berlin * Copyright (C) 2014 PHYTEC Messtechnik GmbH * Copyright (C) 2015-2016 Eistec AB * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @ingroup cpu_kinetis_common * @ingroup drivers_periph_pwm * * @{ * * @file * @brief Low-level PWM driver implementation * * @author Hauke Petersen * @author Johann Fischer * @author Jonas Remmert * @author Joakim NohlgÄrd * * @} */ #include "cpu.h" #include "assert.h" #include "periph/pwm.h" #define PRESCALER_MAX (7U) 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; if (pwm >= PWM_NUMOF || ((res * freq) > CLOCK_BUSCLOCK)) { return 0; } /* figure out the clock settings * the resulting frequency is calculated by * ticks := BUS_CLK / 2 ^ pre * where `ticks` is `freq * res` * and `pre` must be between [0, 7]. * * The resulting prescaler yields a timer frequency, which is the closest * possible frequency requested. */ while ((CLOCK_BUSCLOCK >> pre) > (res * freq)) { ++pre; } /* make sure the calculated prescaler is valid, else return */ if (pre > PRESCALER_MAX) { return 0; } /* configure the used timer */ poweron(pwm); /* disable write protect for changing settings */ ftm(pwm)->MODE = FTM_MODE_WPDIS_MASK; /* clear any existing configuration */ ftm(pwm)->COMBINE = 0; ftm(pwm)->CNTIN = 0; ftm(pwm)->SWOCTRL = 0; /* apply prescaler and set resolution */ ftm(pwm)->SC = FTM_SC_PS(pre); ftm(pwm)->MOD = (res - 1); /* set CPWMS bit in the SC register in case of center aligned mode */ if (mode == PWM_CENTER) { BITBAND_REG32(ftm(pwm)->SC, FTM_SC_CPWMS_SHIFT) = 1; } /* setup the configured channels */ for (int i = 0; i < (int)pwm_config[pwm].chan_numof; i++) { /* configure the used pin */ gpio_init_port(pwm_config[pwm].chan[i].pin, PORT_PCR_MUX(pwm_config[pwm].chan[i].af)); /* set the given mode */ ftm(pwm)->CONTROLS[pwm_config[pwm].chan[i].ftm_chan].CnSC = mode; /* and reset the PWM to 0% duty cycle */ ftm(pwm)->CONTROLS[pwm_config[pwm].chan[i].ftm_chan].CnV = 0; } /* and now we start the actual waveform generation */ ftm(pwm)->SC |= FTM_SC_CLKS(1); /* finally we need to return the actual applied PWM frequency */ return (CLOCK_BUSCLOCK >> pre) / res; } uint8_t pwm_channels(pwm_t pwm) { assert(pwm < PWM_NUMOF); return pwm_config[pwm].chan_numof; } void pwm_set(pwm_t pwm, uint8_t channel, uint16_t value) { assert((pwm < PWM_NUMOF) && (channel < pwm_config[pwm].chan_numof)); ftm(pwm)->CONTROLS[pwm_config[pwm].chan[channel].ftm_chan].CnV = value; } void pwm_poweron(pwm_t pwm) { assert(pwm < PWM_NUMOF); poweron(pwm); ftm(pwm)->SC |= FTM_SC_CLKS(1); } void pwm_poweroff(pwm_t pwm) { assert(pwm < PWM_NUMOF); 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_num) = 0; #else if (ftm_num < 2) { BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_FTM0_SHIFT + ftm_num) = 0; } else if (ftm_num == 2) { BITBAND_REG32(SIM->SCGC3, SIM_SCGC3_FTM2_SHIFT) = 0; } #endif }