Refactor pwm driver; add documentation.
authorbatt <batt@38d2e660-2303-0410-9eaa-f027e97ec537>
Thu, 10 Mar 2011 15:52:16 +0000 (15:52 +0000)
committerbatt <batt@38d2e660-2303-0410-9eaa-f027e97ec537>
Thu, 10 Mar 2011 15:52:16 +0000 (15:52 +0000)
git-svn-id: https://src.develer.com/svnoss/bertos/trunk@4761 38d2e660-2303-0410-9eaa-f027e97ec537

bertos/cfg/cfg_pwm.h
bertos/cpu/arm/drv/pwm_at91.c
bertos/cpu/arm/drv/pwm_at91.h
bertos/drv/dc_motor.c
bertos/drv/dc_motor.h
bertos/drv/dc_motor_hwtest.c
bertos/drv/pwm.c
bertos/drv/pwm.h

index 595189e64433402dfc0c33c2879385dee3f45351..68d62837c8ee73db35a3bf91e60acc068f465558 100644 (file)
  */
 #define PWM_LOG_FORMAT     LOG_FMT_VERBOSE
 
+/**
+ * Enable the OLD pwm API.
+ * Not recommended for new projects.
+ *
+ * $WIZ$ type = "boolean"
+ */
+#define CFG_PWM_ENABLE_OLD_API 1
+
 #endif /* CFG_PWM_H */
index 397240ec2318f51e6971c52c3639aee3f5796351..c5780c3baa36421780529fea6287ca1f8e685f9f 100644 (file)
@@ -26,7 +26,7 @@
  * 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
  *
  * \author Daniele Basile <asterix@develer.com>
+ * \author Francesco Sacchi <batt@develer.com>
  */
 
+#include <drv/pwm.h>
 #include "pwm_at91.h"
-#include "hw/pwm_map.h"
 #include <hw/hw_cpufreq.h>
 #include "cfg/cfg_pwm.h"
 
 #include <cfg/debug.h>
 
 #include <io/arm.h>
+#include <cpu/irq.h>
 
+#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!
+                */
+               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;
 
-       /*
-        * If polarity flag is true we must invert
-        * PWM polarity.
+                       *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
         */
-       if (pwm_map[dev].pol)
+       void pwm_hw_enable(PwmDev dev)
        {
-               duty = (uint16_t)*pwm_map[dev].period_reg - duty;
-               LOG_INFO("Inverted duty[%d], pol[%d]\n", duty, pwm_map[dev].pol);
+               if (!pwm_map[dev].duty_zero)
+               {
+                       PWM_PIO_PDR  = pwm_map[dev].pwm_pin;
+                       PWM_PIO_ABSR = pwm_map[dev].pwm_pin;
+               }
        }
 
-       /*
-        * WARNING: is forbidden to write 0 to duty cycle value,
-        * and so for duty = 0 we must enable PIO and clear output!
+       /**
+        * Disable select pwm channel
         */
-       if (!duty)
+       void pwm_hw_disable(PwmDev dev)
        {
-               PWM_PIO_CODR = pwm_map[dev].pwm_pin;
-               PWM_PIO_PER  = pwm_map[dev].pwm_pin;
-               pwm_map[dev].duty_zero = true;
+               PWM_PIO_PER = pwm_map[dev].pwm_pin;
        }
-       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;
+       /**
+        * 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);
        }
 
-       PWM_ENA = BV(dev);
-       LOG_INFO("PWM ch[%d] duty[%d], period[%ld]\n", dev, duty, *pwm_map[dev].period_reg);
-}
+       /**
+        * 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
+                */
+               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;
+
+               /*
+                * 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);
+               }
 
-/**
- * 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
- */
-void pwm_hw_disable(PwmDev dev)
-{
-       PWM_PIO_PER = pwm_map[dev].pwm_pin;
-}
+#else
 
-/**
- * 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);
-}
+       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;
+
+               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);
+       }
 
-       /* Disable all channels. */
-       PWM_DIS = 0xFFFFFFFF;
-       /* Disable prescalers A and B */
-       PWM_MR = 0;
+       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
index 5d362894707c80a79b95110e9b38964f48d6b69c..403b9f52ef6df118a5ac15f7fff2697db2901498 100644 (file)
 #ifndef DRV_PWM_AT91_H
 #define DRV_PWM_AT91_H
 
-#include "hw/pwm_map.h"
-
 #include <cfg/compiler.h>
 #include <cfg/macros.h>
 
+#include "cfg/cfg_pwm.h"
+
 #include <io/arm.h>
 
+#if CFG_PWM_ENABLE_OLD_API
 
-#define PWM_HW_MAX_PRESCALER_STEP         10
-#define PWM_HW_MAX_PERIOD             0xFFFF
+       #include "hw/pwm_map.h"
 
-/**
- * Type definition for pwm period.
- */
-typedef uint16_t pwm_period_t;
+       /**
       * Type definition for pwm period.
       */
+       typedef uint16_t pwm_period_t;
 
-/**
- * Structur definition for pwm driver.
- */
-typedef struct PwmChannel
-{
-       bool duty_zero;         ///< True if duty cyle is zero, false otherwise.
-       bool pol;               ///< PWM polarty flag.
-       int pwm_pin;            ///< PWM pin.
-       reg32_t *mode_reg;      ///< PWM mode register.
-       reg32_t *duty_reg;      ///< PWM duty cycle register.
-       reg32_t *period_reg;    ///< PWM periodic register.
-       reg32_t *update_reg;    ///< Update setting register for PWM.
-
-} PwmChannel;
-
-
-void pwm_hw_init(void);
-void pwm_hw_setFrequency(PwmDev dev, uint32_t freq);
-void pwm_hw_setDutyUnlock(PwmDev dev, uint16_t duty);
-void pwm_hw_disable(PwmDev dev);
-void pwm_hw_enable(PwmDev dev);
-void pwm_hw_setPolarity(PwmDev dev, bool pol);
-pwm_period_t pwm_hw_getPeriod(PwmDev dev);
+       /**
+        * Structur definition for pwm driver.
+        */
+       typedef struct PwmChannel
+       {
+               bool duty_zero;         ///< True if duty cyle is zero, false otherwise.
+               bool pol;               ///< PWM polarty flag.
+               int pwm_pin;            ///< PWM pin.
+               reg32_t *mode_reg;      ///< PWM mode register.
+               reg32_t *duty_reg;      ///< PWM duty cycle register.
+               reg32_t *period_reg;    ///< PWM periodic register.
+               reg32_t *update_reg;    ///< Update setting register for PWM.
+
+       } PwmChannel;
+
+
+       void pwm_hw_init(void);
+       void pwm_hw_setFrequency(PwmDev dev, uint32_t freq);
+       void pwm_hw_setDutyUnlock(PwmDev dev, uint16_t duty);
+       void pwm_hw_disable(PwmDev dev);
+       void pwm_hw_enable(PwmDev dev);
+       void pwm_hw_setPolarity(PwmDev dev, bool pol);
+       pwm_period_t pwm_hw_getPeriod(PwmDev dev);
+
+#else
+       #include <drv/pwm.h>
+
+       typedef uint16_t pwm_hwreg_t;
+
+       struct PwmChannelRegs; //fwd decl
+
+       typedef struct PwmHardware
+       {
+               uint32_t pwm_pin;       ///< PWM pin.
+               volatile struct PwmChannelRegs *base;
+       } PwmHardware;
+
+       pwm_hwreg_t pwm_hw_getPeriod(Pwm *ctx);
+       void pwm_hw_setFrequency(struct Pwm *ctx, pwm_freq_t freq);
+       void pwm_hw_setDuty(Pwm *ctx, pwm_hwreg_t duty);
+       void pwm_hw_init(struct Pwm *ctx, unsigned ch);
+
+#endif
 
 #endif /* DRV_ADC_AT91_H */
index bd0dd0a465d7feafe115bfa8cab64daa758f8009..3609b7bda77e6dc3d7090ea74c370f77dc077b0e 100644 (file)
@@ -46,6 +46,7 @@
 
 #include "dc_motor.h"
 #include "hw/hw_dc_motor.h"
+#include "cfg/cfg_pwm.h"
 
 // Define logging setting (for cfg/log.h module).
 #define LOG_LEVEL         DC_MOTOR_LOG_LEVEL
 
 #include <string.h>
 
+#if CFG_PWM_ENABLE_OLD_API
+       #define PWM_ENABLE(dcm, en)    pwm_enable((dcm)->cfg->pwm_dev, (en))
+       #define PWM_SETDUTY(dcm, duty) pwm_setDuty((dcm)->cfg->pwm_dev, (duty))
+       #define PWM_SETFREQ(dcm, freq) pwm_setFrequency((dcm)->cfg->pwm_dev, (freq))
+       #define PWM_SETPOL(dcm, pol)   pwm_setPolarity((dcm)->cfg->pwm_dev, (pol))
+#else
+       #define PWM_ENABLE(dcm, en)    pwm_enable(&(dcm)->pwm, (en))
+       #define PWM_SETDUTY(dcm, duty) pwm_setDuty(&(dcm)->pwm, (duty))
+       #define PWM_SETFREQ(dcm, freq) pwm_setFrequency(&(dcm)->pwm, (freq))
+       #define PWM_SETPOL(dcm, pol)   pwm_setPolarity(&(dcm)->pwm, (pol))
+#endif
+
 /**
  * Define status bit for DC motor device.
  */
@@ -164,7 +177,7 @@ static void dc_motor_stop(int index)
 
        dcm->status &= ~DC_MOTOR_ACTIVE;
        dcm->expire_time = DC_MOTOR_NO_EXPIRE;
-       pwm_enable(dcm->cfg->pwm_dev, false);
+       PWM_ENABLE(dcm, false);
 
        if (dcm->cfg->braked)
        {
@@ -202,7 +215,7 @@ static void dc_motor_do(int index)
         * To set dc motor direction we must also set the
         * PWM polarity according with dc motor driver chip
         */
-       pwm_setPolarity(dcm->cfg->pwm_dev, dcm->status & DC_MOTOR_DIR);
+       PWM_SETPOL(dcm, dcm->status & DC_MOTOR_DIR);
        DC_MOTOR_SET_DIR(dcm->index, dcm->status & DC_MOTOR_DIR);
 
        //Compute next value for reaching target speed from current position
@@ -235,10 +248,10 @@ static void dc_motor_do(int index)
        );
 
        //Apply the compute duty value
-       pwm_setDuty(dcm->cfg->pwm_dev, new_pid);
+       PWM_SETDUTY(dcm, new_pid);
 
        //Restart dc motor
-       pwm_enable(dcm->cfg->pwm_dev, true);
+       PWM_ENABLE(dcm, true);
 
        DC_MOTOR_ENABLE(dcm->index);
        DC_MOTOR_UNLOCK;
@@ -315,7 +328,7 @@ static void NORETURN dc_motor_poll(void)
                        if (dcm_all[i].status & DC_MOTOR_ACTIVE)
                        {
                                DC_MOTOR_DISABLE(dcm_all[i].index);
-                               pwm_enable(dcm_all[i].cfg->pwm_dev, false);
+                               PWM_ENABLE(&dcm_all[i], false);
                        }
                        DC_MOTOR_UNLOCK;
                }
@@ -440,9 +453,11 @@ void dc_motor_setup(int index, DCMotorConfig *dcm_conf)
         * Clear the status.
         */
        dcm->status = 0;
-
-       pwm_setFrequency(dcm->cfg->pwm_dev, dcm->cfg->freq);
-       pwm_enable(dcm->cfg->pwm_dev, false);
+#if !CFG_PWM_ENABLE_OLD_API
+       pwm_init(&dcm->pwm, dcm_conf->pwm_dev);
+#endif
+       PWM_SETFREQ(dcm, dcm->cfg->freq);
+       PWM_ENABLE(dcm, false);
 
        //Set default direction for DC motor
        DC_MOTOR_SET_DIR(dcm->index, dcm->cfg->dir);
index 949420759747d5674dfbae782c5fe3f9639a7d36..f6fc6c57afa114ba9f354a26d93c28c1c10ed27e 100644 (file)
@@ -46,6 +46,7 @@
 #include "hw/hw_dc_motor.h"
 
 #include "cfg/cfg_dc_motor.h"
+#include "cfg/cfg_pwm.h"
 #include <cfg/macros.h>
 
 #include <algo/pid_control.h>
@@ -70,7 +71,7 @@ typedef struct DCMotorConfig
        PidCfg pid_cfg;         ///< Pid control.
        bool pid_enable;        ///< Flag to disable or enable pid control.
 
-       PwmDev pwm_dev;         ///< Pwm channel.
+       unsigned pwm_dev;       ///< Pwm channel.
        pwm_freq_t freq;        ///< Pwm waveform frequency.
 
        adc_ch_t adc_ch;        ///< ADC channel.
@@ -92,14 +93,18 @@ typedef struct DCMotorConfig
  */
 typedef struct DCMotor
 {
-       const DCMotorConfig *cfg; ///< All configuration for select DC motor.
-       PidContext pid_ctx;       ///< Pid control.
+       const DCMotorConfig *cfg; // All configuration for select DC motor.
+       PidContext pid_ctx;       // Pid control.
 
-       int index;                ///< DC motor id.
-       uint32_t status;          ///< Status of select DC motor
-       dc_speed_t tgt_speed;     ///< Target speed for select DC motor
+#if !CFG_PWM_ENABLE_OLD_API
+       Pwm pwm;                  // Pwm context
+#endif
 
-       ticks_t expire_time;      ///< Amount of time that  dc motor run
+       int index;                // DC motor id.
+       uint32_t status;          // Status of select DC motor
+       dc_speed_t tgt_speed;     // Target speed for select DC motor
+
+       ticks_t expire_time;      // Amount of time that  dc motor run
 
 } DCMotor;
 
index 477d35aa58899c7bf1b55f192d7a122755281718..50901b26c5e02cd72a4a1a0f8ad75969f4031456 100644 (file)
@@ -121,7 +121,9 @@ int dc_motor_testSetUp(void)
        kdbg_init();
        timer_init();
        proc_init();
+#if !CFG_PWM_ENABLE_OLD_API
        pwm_init();
+#endif
        adc_init();
 
        return 0;
index d09d9627caa16a0d9da491ee06409ac376203d94..2448f5681f13c5180c3da70a8dd786cf0714c38e 100644 (file)
 #include <cpu/types.h>
 #include <cpu/irq.h>
 
-
-/**
- * Set duty of pwm channel \p dev.
- */
-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 in Hz.
- */
-void pwm_setFrequency(PwmDev dev, pwm_freq_t freq)
-{
-       pwm_hw_setFrequency(dev, freq);
-}
-
-/**
- * Set duty of pwm channel \p dev.
- */
-void pwm_enable(PwmDev dev, bool state)
-{
-       if (state)
-               pwm_hw_enable(dev);
-       else
-               pwm_hw_disable(dev);
-}
-
-MOD_DEFINE(pwm);
-
-/**
- * Initialize PWM hw.
- */
-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);
-}
-
-
+#include <string.h>
+
+#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
 
 
index db00b62a293ebd35baa419944f9595b93c212a82..4ed32f85183d576126b2b715996d073999690bb9 100644 (file)
@@ -28,8 +28,9 @@
  *
  * Copyright 2005 Develer S.r.l. (http://www.develer.com/)
  * -->
- *
- *
+ * \defgroup pwm_driver PWM driver
+ * \ingroup drivers
+ * \{
  * \brief Pulse Width Modulation (PWM) driver.
  *
  * \author Francesco Sacchi <batt@develer.com>
 #ifndef DRV_PWM_H
 #define DRV_PWM_H
 
-#include "hw/pwm_map.h"
-
 #include <cpu/attr.h>
 
-#include CPU_HEADER(pwm)
-
 #include <cfg/compiler.h>
+#include "cfg/cfg_pwm.h"
 
+/**
+ * Maximum PWM duty cycle value (100%)
+ */
 #define PWM_MAX_DUTY              ((pwm_duty_t)0xFFFF)
 #define PWM_MAX_PERIOD                         0xFFFF
 #define PWM_MAX_PERIOD_LOG2                        16
 
 /**
- * PWM type define.
+ * Type for PWM duty cycle.
+ * The value is represented as a 16bit unsigned integer, so it ranges from 0 (0%)
+ * to PWM_MAX_DUTY (0xFFFF = 100%).
  */
 typedef uint16_t pwm_duty_t;
+
+/**
+ * Type for PWM frequency.
+ * Unit of measure is Hz.
+ */
 typedef uint32_t pwm_freq_t;
 
+#if !CFG_PWM_ENABLE_OLD_API || defined(__doxygen__)
+       /**
+        * \defgroup pwm_api PWM API
+        * With this driver you can control a device with multiple PWM channels.
+        * You can enable/disable each channel indipendently and also set frequency
+        * and duty cycle.
+        *
+        * API usage example:
+        * \code
+        * Pwm pwm; // declare a context structure
+        * pwm_init(&pwm, 0); // init pwm channel 0
+        * pwm_setFrequency(&pwm, 1000); // Set frequency of channel 0 to 1000Hz
+        * pwm_setDuty(&pwm, 0x7FFF); // Set duty to 50% (0xFFFF/2)
+        * pwm_enable(&pwm, true); // Activate the output
+        * \endcode
+        * \{
+        */
 
+       /**
+        * Enum describing PWM polarities.
+        */
+       typedef enum PwmPolarity
+       {
+               /** High pulse: increasing duty increases the part of the signal at high level. */
+               PWM_POL_HIGH_PULSE,
+               /** Positive pulse: same as High pulse. */
+               PWM_POL_POSITIVE = PWM_POL_HIGH_PULSE,
+               /** Low pulse: increasing duty increases the part of the signal at low level. */
+               PWM_POL_LOW_PULSE,
+               /** Negative pulse: same as Low pulse. */
+               PWM_POL_NEGATIVE = PWM_POL_LOW_PULSE,
+       } PwmPolarity;
 
-/**
- * Set PWM polarity of pwm \p dev.
- */
-INLINE void pwm_setPolarity(PwmDev dev, bool pol)
-{
-    pwm_hw_setPolarity(dev, pol);
-}
+       struct PwmHardware; //Fwd declaration
+
+       /**
+        * PWM context structure.
+        */
+       typedef struct Pwm
+       {
+               unsigned ch;
+               pwm_duty_t duty;
+               PwmPolarity pol;
+               bool enabled;
+               struct PwmHardware *hw;
+       } Pwm;
+
+       void pwm_setDuty(Pwm *ctx, pwm_duty_t duty);
+       void pwm_setFrequency(Pwm *ctx, pwm_freq_t freq);
+       void pwm_setPolarity(Pwm *ctx, PwmPolarity pol);
+       void pwm_enable(Pwm *ctx, bool state);
+       void pwm_init(Pwm *ctx, unsigned channel);
+       /** \} */ //defgroup pwm_api
+#endif
+
+
+#if CFG_PWM_ENABLE_OLD_API
+       /**
+        * \defgroup pwm_old_api Old PWM API
+        * This API has strong limititations, so it has been deprecated.
+        * It is active by default for backward compatibility reasons, but
+        * for new projects please use the new PWM API.
+        * In order to disable this API, check CFG_PWM_ENABLE_OLD_API.
+        * \see pwm_api
+        * \see CFG_PWM_ENABLE_OLD_API
+        * \{
+        */
 
-void pwm_setDuty(PwmDev dev, pwm_duty_t duty);
-void pwm_setFrequency(PwmDev dev, pwm_freq_t freq);
-void pwm_setPolarity(PwmDev dev, bool pol);
-void pwm_enable(PwmDev dev, bool state);
-void pwm_init(void);
+       #include CPU_HEADER(pwm)
+       #include "hw/pwm_map.h"
+
+       /**
+        * Set PWM polarity of pwm \p dev.
+        * \param dev PWM channel.
+        * \param pol if false positive polarity pulses are generated,
+        *            if true negative polarity pulses are generated.
+        * \warning This function has to be called with PWM disabled, otherwise
+        *          the output value will be undefined.
+        */
+       INLINE void pwm_setPolarity(PwmDev dev, bool pol)
+       {
+               pwm_hw_setPolarity(dev, pol);
+       }
+
+       void pwm_setDuty(PwmDev dev, pwm_duty_t duty);
+       void pwm_setFrequency(PwmDev dev, pwm_freq_t freq);
+       void pwm_enable(PwmDev dev, bool state);
+       void pwm_init(void);
+       /** \} */ //defgroup pwm_old_api
+#endif
 
 /*
  * Test function prototypes.
@@ -89,4 +172,7 @@ int pwm_testSetup(void);
 #define pwm_testSetUp() pwm_testSetup()
 int pwm_testTearDown(void);
 
+
+/** \} */ //defgroup pwm_driver
+
 #endif /* DRV_PWM_H */