/* * Copyright (C) 2016-2017 Bas Stottelaar * * 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_efm32 * @ingroup drivers_periph_pwm * @{ * * @file * @brief Low-level PWM peripheral driver implementation * * @author Bas Stottelaar */ #include #include "cpu.h" #include "periph_conf.h" #include "periph/gpio.h" #include "periph/pwm.h" #include "em_cmu.h" #include "em_timer.h" #include "em_timer_utils.h" uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) { /* check if device is valid */ if (dev >= PWM_NUMOF) { return -1; } /* enable clocks */ CMU_ClockEnable(cmuClock_HFPER, true); CMU_ClockEnable(pwm_config[dev].cmu, true); /* calculate the prescaler by determining the best prescaler */ uint32_t freq_timer = CMU_ClockFreqGet(pwm_config[dev].cmu); TIMER_Prescale_TypeDef prescaler = TIMER_PrescalerCalc(freq * res, freq_timer); if (prescaler > timerPrescale1024) { return -2; } /* reset and initialize peripheral */ TIMER_Init_TypeDef init = TIMER_INIT_DEFAULT; init.enable = false; init.prescale = prescaler; init.mode = (TIMER_Mode_TypeDef) mode; TIMER_Reset(pwm_config[dev].dev); TIMER_Init(pwm_config[dev].dev, &init); TIMER_TopSet(pwm_config[dev].dev, res); /* initialize channels */ TIMER_InitCC_TypeDef init_channel = TIMER_INITCC_DEFAULT; init_channel.mode = timerCCModePWM; for (int i = 0; i < pwm_config[dev].channels; i++) { pwm_chan_conf_t channel = pwm_config[dev].channel[i]; /* configure the pin */ gpio_init(channel.pin, GPIO_OUT); /* configure pin function */ #if defined(_SILICON_LABS_32B_SERIES_0) pwm_config[dev].dev->ROUTE |= (channel.loc | TIMER_Channel2Route(channel.index)); #elif defined(_SILICON_LABS_32B_SERIES_1) pwm_config[dev].dev->ROUTELOC0 |= channel.loc; pwm_config[dev].dev->ROUTEPEN |= TIMER_Channel2Route(channel.index); #endif /* setup channel */ TIMER_InitCC(pwm_config[dev].dev, channel.index, &init_channel); } /* enable peripheral */ TIMER_Enable(pwm_config[dev].dev, true); return freq_timer / TIMER_Prescaler2Div(prescaler) / res; } uint8_t pwm_channels(pwm_t dev) { assert(dev < PWM_NUMOF); return pwm_config[dev].channels; } void pwm_set(pwm_t dev, uint8_t channel, uint16_t value) { assert(dev < PWM_NUMOF); TIMER_CompareBufSet(pwm_config[dev].dev, pwm_config[dev].channel[channel].index, value); } void pwm_start(pwm_t dev) { assert(dev < PWM_NUMOF); TIMER_Enable(pwm_config[dev].dev, true); } void pwm_stop(pwm_t dev) { assert(dev < PWM_NUMOF); TIMER_Enable(pwm_config[dev].dev, false); } void pwm_poweron(pwm_t dev) { assert(dev < PWM_NUMOF); CMU_ClockEnable(pwm_config[dev].cmu, true); } void pwm_poweroff(pwm_t dev) { assert(dev < PWM_NUMOF); CMU_ClockEnable(pwm_config[dev].cmu, false); }