X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;ds=sidebyside;f=bertos%2Fcpu%2Farm%2Fdrv%2Fpwm_at91.c;h=c5780c3baa36421780529fea6287ca1f8e685f9f;hb=HEAD;hp=a95e60613fa6d0e5e8140735f24d468eec0ecc79;hpb=fb3dbd280b48cbcc92b208e0068e464abbef2773;p=bertos.git diff --git a/bertos/cpu/arm/drv/pwm_at91.c b/bertos/cpu/arm/drv/pwm_at91.c index a95e6061..c5780c3b 100644 --- a/bertos/cpu/arm/drv/pwm_at91.c +++ b/bertos/cpu/arm/drv/pwm_at91.c @@ -26,19 +26,19 @@ * invalidate any other reasons why the executable file might be covered by * the GNU General Public License. * - * Copyright 2008 Develer S.r.l. (http://www.develer.com/) + * Copyright 2011 Develer S.r.l. (http://www.develer.com/) * * --> * * * \brief PWM hardware-specific implementation * - * \version $Id$ * \author Daniele Basile + * \author Francesco Sacchi */ +#include #include "pwm_at91.h" -#include "hw/pwm_map.h" #include #include "cfg/cfg_pwm.h" @@ -51,196 +51,337 @@ #include #include +#include +#define PWM_HW_MAX_PRESCALER_STEP 10 +#define PWM_HW_MAX_PERIOD 0xFFFF -/** - * Register structure for pwm driver. - * This array content all data and register pointer - * to manage pwm peripheral device. - */ -static PwmChannel pwm_map[PWM_CNT] = -{ - {//PWM Channel 0 - .duty_zero = false, - .pol = false, - .pwm_pin = BV(PWM0), - .mode_reg = &PWM_CMR0, - .duty_reg = &PWM_CDTY0, - .period_reg = &PWM_CPRD0, - .update_reg = &PWM_CUPD0, - }, - {//PWM Channel 1 - .duty_zero = false, - .pol = false, - .pwm_pin = BV(PWM1), - .mode_reg = &PWM_CMR1, - .duty_reg = &PWM_CDTY1, - .period_reg = &PWM_CPRD1, - .update_reg = &PWM_CUPD1, - }, - {//PWM Channel 2 - .duty_zero = false, - .pol = false, - .pwm_pin = BV(PWM2), - .mode_reg = &PWM_CMR2, - .duty_reg = &PWM_CDTY2, - .period_reg = &PWM_CPRD2, - .update_reg = &PWM_CUPD2, - }, - {//PWM Channel 3 - .duty_zero = false, - .pol = false, - .pwm_pin = BV(PWM3), - .mode_reg = &PWM_CMR3, - .duty_reg = &PWM_CDTY3, - .period_reg = &PWM_CPRD3, - .update_reg = &PWM_CUPD3, - } -}; +#if CFG_PWM_ENABLE_OLD_API + #include "hw/pwm_map.h" + /** + * Register structure for pwm driver. + * This array content all data and register pointer + * to manage pwm peripheral device. + */ + static PwmChannel pwm_map[PWM_CNT] = + { + {//PWM Channel 0 + .duty_zero = false, + .pol = false, + .pwm_pin = BV(PWM0), + .mode_reg = &PWM_CMR0, + .duty_reg = &PWM_CDTY0, + .period_reg = &PWM_CPRD0, + .update_reg = &PWM_CUPD0, + }, + {//PWM Channel 1 + .duty_zero = false, + .pol = false, + .pwm_pin = BV(PWM1), + .mode_reg = &PWM_CMR1, + .duty_reg = &PWM_CDTY1, + .period_reg = &PWM_CPRD1, + .update_reg = &PWM_CUPD1, + }, + {//PWM Channel 2 + .duty_zero = false, + .pol = false, + .pwm_pin = BV(PWM2), + .mode_reg = &PWM_CMR2, + .duty_reg = &PWM_CDTY2, + .period_reg = &PWM_CPRD2, + .update_reg = &PWM_CUPD2, + }, + {//PWM Channel 3 + .duty_zero = false, + .pol = false, + .pwm_pin = BV(PWM3), + .mode_reg = &PWM_CMR3, + .duty_reg = &PWM_CDTY3, + .period_reg = &PWM_CPRD3, + .update_reg = &PWM_CUPD3, + } + }; -/** - * Get preiod from select channel - * - * \a dev channel - */ -pwm_period_t pwm_hw_getPeriod(PwmDev dev) -{ - return *pwm_map[dev].period_reg; -} -/** - * Set pwm waveform frequecy. - * - * \a freq in Hz - */ -void pwm_hw_setFrequency(PwmDev dev, uint32_t freq) -{ - uint32_t period = 0; + /** + * Get preiod from select channel + * + * \a dev channel + */ + pwm_period_t pwm_hw_getPeriod(PwmDev dev) + { + return *pwm_map[dev].period_reg; + } - for(int i = 0; i <= PWM_HW_MAX_PRESCALER_STEP; i++) + /** + * Set pwm waveform frequecy. + * + * \a freq in Hz + */ + void pwm_hw_setFrequency(PwmDev dev, uint32_t freq) { - period = CPU_FREQ / (BV(i) * freq); -// LOG_INFO("period[%ld], prescale[%d]\n", period, i); - if ((period < PWM_HW_MAX_PERIOD) && (period != 0)) + uint32_t period = 0; + + for(int i = 0; i <= PWM_HW_MAX_PRESCALER_STEP; i++) { - //Clean previous channel prescaler, and set new - *pwm_map[dev].mode_reg &= ~PWM_CPRE_MCK_MASK; - *pwm_map[dev].mode_reg |= i; - //Set pwm period - *pwm_map[dev].period_reg = period; - break; + period = CPU_FREQ / (BV(i) * freq); + LOG_INFO("period[%ld], prescale[%d]\n", period, i); + if ((period < PWM_HW_MAX_PERIOD) && (period != 0)) + { + //Clean previous channel prescaler, and set new + *pwm_map[dev].mode_reg &= ~PWM_CPRE_MCK_MASK; + *pwm_map[dev].mode_reg |= i; + //Set pwm period + *pwm_map[dev].period_reg = period; + break; + } } + + LOG_INFO("PWM ch[%d] period[%ld]\n", dev, period); } - LOG_INFO("PWM ch[%d] period[%ld]\n", dev, period); -} + /** + * Set pwm duty cycle. + * + * \a duty value 0 - 2^16 + */ + void pwm_hw_setDutyUnlock(PwmDev dev, uint16_t duty) + { + ASSERT(duty <= (uint16_t)*pwm_map[dev].period_reg); -/** - * Set pwm duty cycle. - * - * \a duty value 0 - 2^16 - */ -void pwm_hw_setDutyUnlock(PwmDev dev, uint16_t duty) -{ - ASSERT(duty <= (uint16_t)*pwm_map[dev].period_reg); + /* + * If polarity flag is true we must invert + * PWM polarity. + */ + if (pwm_map[dev].pol) + { + duty = (uint16_t)*pwm_map[dev].period_reg - duty; + LOG_INFO("Inverted duty[%d], pol[%d]\n", duty, pwm_map[dev].pol); + } - /* - * WARNING: is forbidden to write 0 to duty cycle value, - * and so for duty = 0 we must enable PIO and clear output! + /* + * WARNING: is forbidden to write 0 to duty cycle value, + * and so for duty = 0 we must enable PIO and clear output! + */ + if (!duty) + { + PWM_PIO_CODR = pwm_map[dev].pwm_pin; + PWM_PIO_PER = pwm_map[dev].pwm_pin; + pwm_map[dev].duty_zero = true; + } + else + { + PWM_PIO_PDR = pwm_map[dev].pwm_pin; + PWM_PIO_ABSR = pwm_map[dev].pwm_pin; + + *pwm_map[dev].update_reg = duty; + pwm_map[dev].duty_zero = false; + } + + PWM_ENA = BV(dev); + LOG_INFO("PWM ch[%d] duty[%d], period[%ld]\n", dev, duty, *pwm_map[dev].period_reg); + } + + + /** + * Enable select pwm channel + */ + void pwm_hw_enable(PwmDev dev) + { + if (!pwm_map[dev].duty_zero) + { + PWM_PIO_PDR = pwm_map[dev].pwm_pin; + PWM_PIO_ABSR = pwm_map[dev].pwm_pin; + } + } + + /** + * Disable select pwm channel */ - if (!duty) + void pwm_hw_disable(PwmDev dev) { PWM_PIO_PER = pwm_map[dev].pwm_pin; - pwm_map[dev].duty_zero = true; } - else + + /** + * Set PWM polarity to select pwm channel + */ + void pwm_hw_setPolarity(PwmDev dev, bool pol) { - /* - * If polarity flag is true we must invert - * PWM polarity. - */ - if (pwm_map[dev].pol) - { - duty = (uint16_t)*pwm_map[dev].period_reg - duty; - LOG_INFO("Inverted duty[%d], pol[%d]\n", duty, pwm_map[dev].pol); - } - - PWM_PIO_PDR = pwm_map[dev].pwm_pin; - *pwm_map[dev].update_reg = duty; - pwm_map[dev].duty_zero = false; + pwm_map[dev].pol = pol; + LOG_INFO("Set pol[%d]\n", pwm_map[dev].pol); } - PWM_ENA = BV(dev); + /** + * Init pwm. + */ + void pwm_hw_init(void) + { - LOG_INFO("PWM ch[%d] duty[%d], period[%ld]\n", dev, duty, *pwm_map[dev].period_reg); -} + /* + * Init pwm: + * WARNING: is forbidden to write 0 to duty cycle value, + * and so for duty = 0 we must enable PIO and clear output! + * - clear PIO outputs + * - enable PIO outputs + * - Disable PIO and enable PWM functions + * - Power on PWM + */ + PWM_PIO_CODR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3); + PWM_PIO_OER = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3); + PWM_PIO_PDR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3); + PWM_PIO_ABSR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3); + PMC_PCER |= BV(PWMC_ID); + /* Disable all channels. */ + PWM_DIS = 0xFFFFFFFF; + /* Disable prescalers A and B */ + PWM_MR = 0; -/** - * Enable select pwm channel - */ -void pwm_hw_enable(PwmDev dev) -{ - if (!pwm_map[dev].duty_zero) - PWM_PIO_PDR = pwm_map[dev].pwm_pin; -} + /* + * Set pwm mode: + * - set period alidned to left + * - set output waveform to start at high level + * - allow duty cycle modify at next period event + */ + for (int ch = 0; ch < PWM_CNT; ch++) + { + *pwm_map[ch].mode_reg = 0; + *pwm_map[ch].mode_reg = BV(PWM_CPOL); + } -/** - * Disable select pwm channel - */ -void pwm_hw_disable(PwmDev dev) -{ - PWM_PIO_PER = pwm_map[dev].pwm_pin; -} + } -/** - * Set PWM polarity to select pwm channel - */ -void pwm_hw_setPolarity(PwmDev dev, bool pol) -{ - pwm_map[dev].pol = pol; - LOG_INFO("Set pol[%d]\n", pwm_map[dev].pol); -} +#else + + typedef struct PwmChannelRegs + { + reg32_t CMR; + reg32_t CDTY; + reg32_t CPRD; + reg32_t CCNT; + reg32_t CUPD; + } PwmChannelRegs; -/** - * Init pwm. - */ -void pwm_hw_init(void) -{ /* - * Init pwm: - * WARNING: is forbidden to write 0 to duty cycle value, - * and so for duty = 0 we must enable PIO and clear output! - * - clear PIO outputs - * - enable PIO outputs - * - Disable PIO and enable PWM functions - * - Power on PWM + * Set pwm waveform frequecy. */ - PWM_PIO_CODR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3); - PWM_PIO_OER = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3); - PWM_PIO_PDR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3); - PWM_PIO_ABSR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3); - PMC_PCER |= BV(PWMC_ID); + void pwm_hw_setFrequency(Pwm *ctx, pwm_freq_t freq) + { + uint32_t period = 0; - /* Disable all channels. */ - PWM_DIS = 0xFFFFFFFF; - /* Disable prescalers A and B */ - PWM_MR = 0; + for(int i = 0; i <= PWM_HW_MAX_PRESCALER_STEP; i++) + { + period = CPU_FREQ / (BV(i) * freq); + LOG_INFO("period[%ld], prescale[%d]\n", period, i); + if ((period < PWM_HW_MAX_PERIOD) && (period != 0)) + { + //Clear previous channel prescaler, and set new + ctx->hw->base->CMR &= ~PWM_CPRE_MCK_MASK; + ctx->hw->base->CMR |= i; + //Set pwm period + ATOMIC( + ctx->hw->base->CPRD = period; + ctx->hw->base->CDTY = period; + ); + break; + } + } + + LOG_INFO("PWM ch[%d] period[%ld]\n", ctx->ch, period); + } + + pwm_hwreg_t pwm_hw_getPeriod(Pwm *ctx) + { + return ctx->hw->base->CPRD; + } /* - * Set pwm mode: - * - set period alidned to left - * - set output waveform to start at high level - * - allow duty cycle modify at next period event + * Set pwm duty cycle. + * + * duty value 0 - (2^16 - 1) */ - for (int ch = 0; ch < PWM_CNT; ch++) + void pwm_hw_setDuty(Pwm *ctx, pwm_hwreg_t hw_duty) { - *pwm_map[ch].mode_reg = 0; - *pwm_map[ch].mode_reg = BV(PWM_CPOL); + ASSERT(hw_duty <= ctx->hw->base->CPRD); + + /* + * WARNING: is forbidden to write 0 or 1 to duty cycle value, + * and so for duty < 2 we must enable PIO and clear output! + */ + if (hw_duty < 2) + { + hw_duty = 2; + PWM_PIO_PER = ctx->hw->pwm_pin; + } + else + PWM_PIO_PDR = ctx->hw->pwm_pin; + + ctx->hw->base->CUPD = hw_duty; + LOG_INFO("PWM ch[%d] duty[%d], period[%ld]\n", ctx->ch, hw_duty, ctx->hw->base->CPRD); } -} + static PwmHardware pwm_channels[] = + { + {//PWM Channel 0 + .pwm_pin = BV(PWM0), + .base = (volatile PwmChannelRegs *)&PWM_CMR0, + }, + {//PWM Channel 1 + .pwm_pin = BV(PWM1), + .base = (volatile PwmChannelRegs *)&PWM_CMR1, + }, + {//PWM Channel 2 + .pwm_pin = BV(PWM2), + .base = (volatile PwmChannelRegs *)&PWM_CMR2, + }, + {//PWM Channel 3 + .pwm_pin = BV(PWM3), + .base = (volatile PwmChannelRegs *)&PWM_CMR3, + }, + }; + + /* + * Init pwm. + */ + void pwm_hw_init(Pwm *ctx, unsigned ch) + { + + ctx->hw = &pwm_channels[ch]; + + /* + * Init pwm: + * - clear PIO outputs + * - enable PIO outputs + * - Enable PWM functions + * - Power on PWM + */ + PWM_PIO_CODR = ctx->hw->pwm_pin; + PWM_PIO_OER = ctx->hw->pwm_pin; + PWM_PIO_PER = ctx->hw->pwm_pin; + PWM_PIO_ABSR = ctx->hw->pwm_pin; + + PMC_PCER |= BV(PWMC_ID); + + /* Disable prescalers A and B */ + PWM_MR = 0; + + /* + * Set pwm mode: + * WARNING: is forbidden to write 0 or 1 to duty cycle value, + * and so for start we set duty to 2. + * Also: + * - set period aligned to left + * - set output waveform to start at high level + * - allow duty cycle modify at next period event + */ + ctx->hw->base->CDTY = 2; + ctx->hw->base->CMR = BV(PWM_CPOL); + PWM_ENA = BV(ch); + } +#endif