/** * \file * * * * \brief PWM driver (implementation) * * * \author Francesco Sacchi * \author Daniele Basile */ #include "cfg/cfg_pwm.h" #include #include // Define logging setting (for cfg/log.h module). #define LOG_LEVEL PWM_LOG_LEVEL #define LOG_VERBOSITY PWM_LOG_FORMAT #include #include #include #include CPU_HEADER(pwm) #include #include #include #if CFG_PWM_ENABLE_OLD_API /** * Set duty of PWM channel \p dev. * The current output frequency will be maintained. * \param dev PWM channel. * \param duty the new duty cycle. */ void pwm_setDuty(PwmDev dev, pwm_duty_t duty) { pwm_period_t period = 0; pwm_duty_t real_duty = 0; duty = MIN(duty, PWM_MAX_DUTY); period = pwm_hw_getPeriod(dev); real_duty = (uint64_t)(duty * period) >> (uint64_t)PWM_MAX_PERIOD_LOG2; LOG_INFO("real_duty[%d] duty[%d], period[%d]\n", real_duty, duty, period); pwm_hw_setDutyUnlock(dev, real_duty); } /** * Set frequency of PWM channel \p dev at \p freq. * \param dev PWM channel. * \param freq new frequency, in Hz. * \warning This function has to be called with PWM disabled, otherwise * the output value will be undefined. * The current duty cycle value will be lost, after calling this * function the duty cycle have to be set again. */ void pwm_setFrequency(PwmDev dev, pwm_freq_t freq) { pwm_hw_setFrequency(dev, freq); } /** * Enable/Disable PWM channel \p dev. * \param dev PWM channel. * \param state if true the PWM on \p dev is activated, if false is disabled. */ void pwm_enable(PwmDev dev, bool state) { if (state) pwm_hw_enable(dev); else pwm_hw_disable(dev); } MOD_DEFINE(pwm); /** * Initialize the PWM driver. * \warning all available PWM channels are initialized. */ void pwm_init(void) { cpu_flags_t flags; PwmDev dev; IRQ_SAVE_DISABLE(flags); pwm_hw_init(); /* set all pwm to 0 */ for (dev = 0; dev < PWM_CNT; dev++) pwm_setDuty(dev, 0); IRQ_RESTORE(flags); MOD_INIT(pwm); } #endif #if !CFG_PWM_ENABLE_OLD_API || defined(__doxygen__) INLINE void setRealDuty(Pwm *ctx, pwm_duty_t duty) { if (ctx->pol == PWM_POL_LOW_PULSE) duty = PWM_MAX_DUTY - duty; pwm_hwreg_t period = pwm_hw_getPeriod(ctx); pwm_hwreg_t hw_duty; switch (duty) { case 0: hw_duty = 0; break; case PWM_MAX_DUTY: hw_duty = period; break; default: hw_duty = (uint64_t)(duty * period) >> (uint64_t)PWM_MAX_PERIOD_LOG2; } pwm_hw_setDuty(ctx, hw_duty); } /** * Set the duty cycle of the PWM channel linked to \p ctx. * The modification will be applied to the channel immediatly. * The current frequency of the channel will be maintained. * \param ctx PWM channel context. * \param duty the new duty cycle value. * \see pwm_duty_t */ void pwm_setDuty(Pwm *ctx, pwm_duty_t duty) { ctx->duty = duty; if (ctx->enabled) setRealDuty(ctx, duty); } /** * Set PWM frequency of channel linked to \p ctx. * The modification will be applied to the channel immediatly. * The duty cycle of the channel will be maintained. * \param ctx PWM channel context. * \param freq the new frequency of the signal, in Hz. * \note Depending on the hardware implementation, this function may * generate a glitch in the output signal upon frequency changing. */ void pwm_setFrequency(Pwm *ctx, pwm_freq_t freq) { pwm_hw_setFrequency(ctx, freq); pwm_enable(ctx, ctx->enabled); } /** * Set PWM polarity of pwm channel linked to \p ctx. * The modification will be applied to the channel immediatly. * \param ctx PWM channel context. * \param pol the new polarity of the signal. * * \note if a channel is disabled, changing its polarity will change the * current steady output level. * \see pwm_enable * \see PwmPolarity */ void pwm_setPolarity(Pwm *ctx, PwmPolarity pol) { ctx->pol = pol; pwm_enable(ctx, ctx->enabled); } /** * Enable/Disable the pwm channel linked to \p ctx. * The modification will be applied to the channel immediatly. * \param ctx PWM channel context. * \param enable if true the channel will be enabled, if false will be disabled. * * \note When a PWM channel is disabled, the output level will be the same * as if the duty would be set to 0%. * So, if current polarity is positive, a disabled channel will be * low, if polarity is negative will be high. * \see pwm_setPolarity */ void pwm_enable(Pwm *ctx, bool enable) { ctx->enabled = enable; if (enable) setRealDuty(ctx, ctx->duty); else setRealDuty(ctx, 0); } /** * Initialize PWM driver. * \param ctx pointer to a PWM context structure, used for holding PWM * driver related information. * \param channel the channel you want to initialize. * \note The channel will be initialized disabled and with High polarity. */ void pwm_init(Pwm *ctx, unsigned channel) { memset(ctx, 0, sizeof(*ctx)); ctx->ch = channel; pwm_hw_init(ctx, channel); } #endif