BeRTOS
pwm_at91.c
Go to the documentation of this file.
00001 
00040 #include <drv/pwm.h>
00041 #include "pwm_at91.h"
00042 #include <hw/hw_cpufreq.h>
00043 #include "cfg/cfg_pwm.h"
00044 
00045 // Define logging setting (for cfg/log.h module).
00046 #define LOG_LEVEL         PWM_LOG_LEVEL
00047 #define LOG_FORMAT        PWM_LOG_FORMAT
00048 #include <cfg/log.h>
00049 
00050 #include <cfg/macros.h>
00051 #include <cfg/debug.h>
00052 
00053 #include <io/arm.h>
00054 #include <cpu/irq.h>
00055 
00056 #define PWM_HW_MAX_PRESCALER_STEP         10
00057 #define PWM_HW_MAX_PERIOD             0xFFFF
00058 
00059 #if CFG_PWM_ENABLE_OLD_API
00060     #include "hw/pwm_map.h"
00061 
00067     static PwmChannel pwm_map[PWM_CNT] =
00068     {
00069         {//PWM Channel 0
00070             .duty_zero = false,
00071             .pol = false,
00072             .pwm_pin = BV(PWM0),
00073             .mode_reg = &PWM_CMR0,
00074             .duty_reg = &PWM_CDTY0,
00075             .period_reg = &PWM_CPRD0,
00076             .update_reg = &PWM_CUPD0,
00077         },
00078         {//PWM Channel 1
00079             .duty_zero = false,
00080             .pol = false,
00081             .pwm_pin = BV(PWM1),
00082             .mode_reg = &PWM_CMR1,
00083             .duty_reg = &PWM_CDTY1,
00084             .period_reg = &PWM_CPRD1,
00085             .update_reg = &PWM_CUPD1,
00086         },
00087         {//PWM Channel 2
00088             .duty_zero = false,
00089             .pol = false,
00090             .pwm_pin = BV(PWM2),
00091             .mode_reg = &PWM_CMR2,
00092             .duty_reg = &PWM_CDTY2,
00093             .period_reg = &PWM_CPRD2,
00094             .update_reg = &PWM_CUPD2,
00095         },
00096         {//PWM Channel 3
00097             .duty_zero = false,
00098             .pol = false,
00099             .pwm_pin = BV(PWM3),
00100             .mode_reg = &PWM_CMR3,
00101             .duty_reg = &PWM_CDTY3,
00102             .period_reg = &PWM_CPRD3,
00103             .update_reg = &PWM_CUPD3,
00104         }
00105     };
00106 
00107 
00113     pwm_period_t pwm_hw_getPeriod(PwmDev dev)
00114     {
00115         return *pwm_map[dev].period_reg;
00116     }
00117 
00123     void pwm_hw_setFrequency(PwmDev dev, uint32_t freq)
00124     {
00125         uint32_t period = 0;
00126 
00127         for(int i = 0; i <= PWM_HW_MAX_PRESCALER_STEP; i++)
00128         {
00129             period = CPU_FREQ / (BV(i) * freq);
00130             LOG_INFO("period[%ld], prescale[%d]\n", period, i);
00131             if ((period < PWM_HW_MAX_PERIOD) && (period != 0))
00132             {
00133                 //Clean previous channel prescaler, and set new
00134                 *pwm_map[dev].mode_reg &= ~PWM_CPRE_MCK_MASK;
00135                 *pwm_map[dev].mode_reg |= i;
00136                 //Set pwm period
00137                 *pwm_map[dev].period_reg = period;
00138                 break;
00139             }
00140         }
00141 
00142         LOG_INFO("PWM ch[%d] period[%ld]\n", dev, period);
00143     }
00144 
00150     void pwm_hw_setDutyUnlock(PwmDev dev, uint16_t duty)
00151     {
00152         ASSERT(duty <= (uint16_t)*pwm_map[dev].period_reg);
00153 
00154 
00155         /*
00156          * If polarity flag is true we must invert
00157          * PWM polarity.
00158          */
00159         if (pwm_map[dev].pol)
00160         {
00161             duty = (uint16_t)*pwm_map[dev].period_reg - duty;
00162             LOG_INFO("Inverted duty[%d], pol[%d]\n", duty, pwm_map[dev].pol);
00163         }
00164 
00165         /*
00166          * WARNING: is forbidden to write 0 to duty cycle value,
00167          * and so for duty = 0 we must enable PIO and clear output!
00168          */
00169         if (!duty)
00170         {
00171             PWM_PIO_CODR = pwm_map[dev].pwm_pin;
00172             PWM_PIO_PER  = pwm_map[dev].pwm_pin;
00173             pwm_map[dev].duty_zero = true;
00174         }
00175         else
00176         {
00177             PWM_PIO_PDR = pwm_map[dev].pwm_pin;
00178             PWM_PIO_ABSR = pwm_map[dev].pwm_pin;
00179 
00180             *pwm_map[dev].update_reg = duty;
00181             pwm_map[dev].duty_zero = false;
00182         }
00183 
00184         PWM_ENA = BV(dev);
00185         LOG_INFO("PWM ch[%d] duty[%d], period[%ld]\n", dev, duty, *pwm_map[dev].period_reg);
00186     }
00187 
00188 
00192     void pwm_hw_enable(PwmDev dev)
00193     {
00194         if (!pwm_map[dev].duty_zero)
00195         {
00196             PWM_PIO_PDR  = pwm_map[dev].pwm_pin;
00197             PWM_PIO_ABSR = pwm_map[dev].pwm_pin;
00198         }
00199     }
00200 
00204     void pwm_hw_disable(PwmDev dev)
00205     {
00206         PWM_PIO_PER = pwm_map[dev].pwm_pin;
00207     }
00208 
00212     void pwm_hw_setPolarity(PwmDev dev, bool pol)
00213     {
00214             pwm_map[dev].pol = pol;
00215             LOG_INFO("Set pol[%d]\n", pwm_map[dev].pol);
00216     }
00217 
00221     void pwm_hw_init(void)
00222     {
00223 
00224         /*
00225          * Init pwm:
00226          * WARNING: is forbidden to write 0 to duty cycle value,
00227          * and so for duty = 0 we must enable PIO and clear output!
00228          * - clear PIO outputs
00229          * - enable PIO outputs
00230          * - Disable PIO and enable PWM functions
00231          * - Power on PWM
00232          */
00233         PWM_PIO_CODR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
00234         PWM_PIO_OER  = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
00235         PWM_PIO_PDR  = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
00236         PWM_PIO_ABSR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
00237         PMC_PCER |= BV(PWMC_ID);
00238 
00239         /* Disable all channels. */
00240         PWM_DIS = 0xFFFFFFFF;
00241         /* Disable prescalers A and B */
00242         PWM_MR = 0;
00243 
00244         /*
00245          * Set pwm mode:
00246          * - set period alidned to left
00247          * - set output waveform to start at high level
00248          * - allow duty cycle modify at next period event
00249          */
00250         for (int ch = 0; ch < PWM_CNT; ch++)
00251         {
00252             *pwm_map[ch].mode_reg = 0;
00253             *pwm_map[ch].mode_reg = BV(PWM_CPOL);
00254         }
00255 
00256     }
00257 
00258 #else
00259 
00260     typedef struct PwmChannelRegs
00261     {
00262         reg32_t CMR;
00263         reg32_t CDTY;
00264         reg32_t CPRD;
00265         reg32_t CCNT;
00266         reg32_t CUPD;
00267     } PwmChannelRegs;
00268 
00269 
00270     /*
00271      * Set pwm waveform frequecy.
00272      */
00273     void pwm_hw_setFrequency(Pwm *ctx, pwm_freq_t freq)
00274     {
00275         uint32_t period = 0;
00276 
00277         for(int i = 0; i <= PWM_HW_MAX_PRESCALER_STEP; i++)
00278         {
00279             period = CPU_FREQ / (BV(i) * freq);
00280             LOG_INFO("period[%ld], prescale[%d]\n", period, i);
00281             if ((period < PWM_HW_MAX_PERIOD) && (period != 0))
00282             {
00283                 //Clear previous channel prescaler, and set new
00284                 ctx->hw->base->CMR &= ~PWM_CPRE_MCK_MASK;
00285                 ctx->hw->base->CMR |= i;
00286                 //Set pwm period
00287                 ATOMIC(
00288                     ctx->hw->base->CPRD = period;
00289                     ctx->hw->base->CDTY = period;
00290                 );
00291                 break;
00292             }
00293         }
00294 
00295         LOG_INFO("PWM ch[%d] period[%ld]\n", ctx->ch, period);
00296     }
00297 
00298     pwm_hwreg_t pwm_hw_getPeriod(Pwm *ctx)
00299     {
00300         return ctx->hw->base->CPRD;
00301     }
00302 
00303     /*
00304      * Set pwm duty cycle.
00305      *
00306      * duty value 0 - (2^16 - 1)
00307      */
00308     void pwm_hw_setDuty(Pwm *ctx, pwm_hwreg_t hw_duty)
00309     {
00310         ASSERT(hw_duty <= ctx->hw->base->CPRD);
00311 
00312         /*
00313          * WARNING: is forbidden to write 0 or 1 to duty cycle value,
00314          * and so for duty < 2 we must enable PIO and clear output!
00315          */
00316         if (hw_duty < 2)
00317         {
00318             hw_duty = 2;
00319             PWM_PIO_PER = ctx->hw->pwm_pin;
00320         }
00321         else
00322             PWM_PIO_PDR = ctx->hw->pwm_pin;
00323 
00324         ctx->hw->base->CUPD = hw_duty;
00325         LOG_INFO("PWM ch[%d] duty[%d], period[%ld]\n", ctx->ch, hw_duty, ctx->hw->base->CPRD);
00326     }
00327 
00328     static PwmHardware pwm_channels[] =
00329     {
00330         {//PWM Channel 0
00331             .pwm_pin = BV(PWM0),
00332             .base = (volatile PwmChannelRegs *)&PWM_CMR0,
00333         },
00334         {//PWM Channel 1
00335             .pwm_pin = BV(PWM1),
00336             .base = (volatile PwmChannelRegs *)&PWM_CMR1,
00337         },
00338         {//PWM Channel 2
00339             .pwm_pin = BV(PWM2),
00340             .base = (volatile PwmChannelRegs *)&PWM_CMR2,
00341         },
00342         {//PWM Channel 3
00343             .pwm_pin = BV(PWM3),
00344             .base = (volatile PwmChannelRegs *)&PWM_CMR3,
00345         },
00346     };
00347 
00348     /*
00349      * Init pwm.
00350      */
00351     void pwm_hw_init(Pwm *ctx, unsigned ch)
00352     {
00353 
00354         ctx->hw = &pwm_channels[ch];
00355 
00356         /*
00357          * Init pwm:
00358          * - clear PIO outputs
00359          * - enable PIO outputs
00360          * - Enable PWM functions
00361          * - Power on PWM
00362          */
00363         PWM_PIO_CODR = ctx->hw->pwm_pin;
00364         PWM_PIO_OER  = ctx->hw->pwm_pin;
00365         PWM_PIO_PER  = ctx->hw->pwm_pin;
00366         PWM_PIO_ABSR = ctx->hw->pwm_pin;
00367 
00368         PMC_PCER |= BV(PWMC_ID);
00369 
00370         /* Disable prescalers A and B */
00371         PWM_MR = 0;
00372 
00373         /*
00374          * Set pwm mode:
00375          * WARNING: is forbidden to write 0 or 1 to duty cycle value,
00376          * and so for start we set duty to 2.
00377          * Also:
00378          * - set period aligned to left
00379          * - set output waveform to start at high level
00380          * - allow duty cycle modify at next period event
00381          */
00382         ctx->hw->base->CDTY = 2;
00383         ctx->hw->base->CMR = BV(PWM_CPOL);
00384         PWM_ENA = BV(ch);
00385     }
00386 
00387 #endif