From d3ca543b6685d290bb2444a6c37820d530123b74 Mon Sep 17 00:00:00 2001 From: batt Date: Thu, 10 Mar 2011 15:52:16 +0000 Subject: [PATCH] Refactor pwm driver; add documentation. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@4761 38d2e660-2303-0410-9eaa-f027e97ec537 --- bertos/cfg/cfg_pwm.h | 8 + bertos/cpu/arm/drv/pwm_at91.c | 455 ++++++++++++++++++++++------------ bertos/cpu/arm/drv/pwm_at91.h | 82 +++--- bertos/drv/dc_motor.c | 31 ++- bertos/drv/dc_motor.h | 19 +- bertos/drv/dc_motor_hwtest.c | 2 + bertos/drv/pwm.c | 248 +++++++++++++----- bertos/drv/pwm.h | 124 +++++++-- 8 files changed, 684 insertions(+), 285 deletions(-) diff --git a/bertos/cfg/cfg_pwm.h b/bertos/cfg/cfg_pwm.h index 595189e6..68d62837 100644 --- a/bertos/cfg/cfg_pwm.h +++ b/bertos/cfg/cfg_pwm.h @@ -53,4 +53,12 @@ */ #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 */ diff --git a/bertos/cpu/arm/drv/pwm_at91.c b/bertos/cpu/arm/drv/pwm_at91.c index 397240ec..c5780c3b 100644 --- a/bertos/cpu/arm/drv/pwm_at91.c +++ b/bertos/cpu/arm/drv/pwm_at91.c @@ -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/) * * --> * @@ -34,10 +34,11 @@ * \brief PWM hardware-specific implementation * * \author Daniele Basile + * \author Francesco Sacchi */ +#include #include "pwm_at91.h" -#include "hw/pwm_map.h" #include #include "cfg/cfg_pwm.h" @@ -50,201 +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! + */ + 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 diff --git a/bertos/cpu/arm/drv/pwm_at91.h b/bertos/cpu/arm/drv/pwm_at91.h index 5d362894..403b9f52 100644 --- a/bertos/cpu/arm/drv/pwm_at91.h +++ b/bertos/cpu/arm/drv/pwm_at91.h @@ -40,44 +40,64 @@ #ifndef DRV_PWM_AT91_H #define DRV_PWM_AT91_H -#include "hw/pwm_map.h" - #include #include +#include "cfg/cfg_pwm.h" + #include +#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 + + 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 */ diff --git a/bertos/drv/dc_motor.c b/bertos/drv/dc_motor.c index bd0dd0a4..3609b7bd 100644 --- a/bertos/drv/dc_motor.c +++ b/bertos/drv/dc_motor.c @@ -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 @@ -64,6 +65,18 @@ #include +#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); diff --git a/bertos/drv/dc_motor.h b/bertos/drv/dc_motor.h index 94942075..f6fc6c57 100644 --- a/bertos/drv/dc_motor.h +++ b/bertos/drv/dc_motor.h @@ -46,6 +46,7 @@ #include "hw/hw_dc_motor.h" #include "cfg/cfg_dc_motor.h" +#include "cfg/cfg_pwm.h" #include #include @@ -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; diff --git a/bertos/drv/dc_motor_hwtest.c b/bertos/drv/dc_motor_hwtest.c index 477d35aa..50901b26 100644 --- a/bertos/drv/dc_motor_hwtest.c +++ b/bertos/drv/dc_motor_hwtest.c @@ -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; diff --git a/bertos/drv/pwm.c b/bertos/drv/pwm.c index d09d9627..2448f568 100644 --- a/bertos/drv/pwm.c +++ b/bertos/drv/pwm.c @@ -56,66 +56,192 @@ #include #include - -/** - * 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 + +#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 diff --git a/bertos/drv/pwm.h b/bertos/drv/pwm.h index db00b62a..4ed32f85 100644 --- a/bertos/drv/pwm.h +++ b/bertos/drv/pwm.h @@ -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 @@ -44,39 +45,121 @@ #ifndef DRV_PWM_H #define DRV_PWM_H -#include "hw/pwm_map.h" - #include -#include CPU_HEADER(pwm) - #include +#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 */ -- 2.25.1