*/
#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 */
* 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
#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 */
#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.
*/
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)
{
* 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
);
//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;
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;
}
* 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);
#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>
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.
*/
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;
kdbg_init();
timer_init();
proc_init();
+#if !CFG_PWM_ENABLE_OLD_API
pwm_init();
+#endif
adc_init();
return 0;
#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
*
* 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.
#define pwm_testSetUp() pwm_testSetup()
int pwm_testTearDown(void);
+
+/** \} */ //defgroup pwm_driver
+
#endif /* DRV_PWM_H */