From c3946aa680a46e466e33c797d9ee93c5433964c4 Mon Sep 17 00:00:00 2001 From: asterix Date: Wed, 30 Apr 2008 13:56:21 +0000 Subject: [PATCH] Add pwm driver. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@1235 38d2e660-2303-0410-9eaa-f027e97ec537 --- bertos/cpu/arm/drv/pwm_at91.c | 216 ++++++++++++++++++++++++++++++++++ bertos/cpu/arm/drv/pwm_at91.h | 83 +++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 bertos/cpu/arm/drv/pwm_at91.c create mode 100644 bertos/cpu/arm/drv/pwm_at91.h diff --git a/bertos/cpu/arm/drv/pwm_at91.c b/bertos/cpu/arm/drv/pwm_at91.c new file mode 100644 index 00000000..b1fda46f --- /dev/null +++ b/bertos/cpu/arm/drv/pwm_at91.c @@ -0,0 +1,216 @@ +/** + * \file + * + * + * + * \brief PWM hardware-specific implementation + * + * \version $Id$ + * + * \author Daniele Basile + */ + +#include "pwm_at91.h" + +#include + +#include +#include +#include + +#include "appconfig.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, + .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, + .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, + .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, + .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; + + for(int i = 0; i <= PWM_HW_MAX_PRESCALER_STEP; i++) + { + period = CLOCK_FREQ / (BV(i) * freq); +// TRACEMSG("period[%d], prescale[%d]", 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; + } + } + + PWM_ENA = BV(dev); + +// TRACEMSG("PWM ch[%d] period[%d]", 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); + + + /* + * 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_PER = pwm_map[dev].pwm_pin; + pwm_map[dev].duty_zero = true; + } + else + { + ASSERT(PWM_CCNT0); + PWM_PIO_PDR = pwm_map[dev].pwm_pin; + *pwm_map[dev].update_reg = duty; + pwm_map[dev].duty_zero = false; + } + +// TRACEMSG("PWM ch[%d] duty[%d], period[%ld]", dev, duty, *pwm_map[dev].period_reg); +} + + +/** + * Enable select pwm channel + */ +void pwm_hw_enable(PwmDev dev) +{ + if (!pwm_map[dev].duty_zero) + PWM_PIO_PDR = pwm_map[dev].pwm_pin; +} + +/** + * Disable select pwm channel + */ +void pwm_hw_disable(PwmDev dev) +{ + PWM_PIO_PER = pwm_map[dev].pwm_pin; +} + + +/** + * 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 low level + * - allow duty cycle modify at next period event + */ + for (int ch = 0; ch < PWM_CNT; ch++) + *pwm_map[ch].mode_reg = 0; +} + diff --git a/bertos/cpu/arm/drv/pwm_at91.h b/bertos/cpu/arm/drv/pwm_at91.h new file mode 100644 index 00000000..50cac043 --- /dev/null +++ b/bertos/cpu/arm/drv/pwm_at91.h @@ -0,0 +1,83 @@ +/** + * \file + * + * + * + * \brief PWM hardware-specific definition + * + * \version $Id$ + * + * \author Daniele Basile + */ + +#ifndef DRV_PWM_AT91_H +#define DRV_PWM_AT91_H + +#include + +#include +#include + +#include + +#include "appconfig.h" + +#define PWM_HW_MAX_PRESCALER_STEP 10 +#define PWM_HW_MAX_PERIOD 0xFFFF + +/** + * 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. + 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); +pwm_period_t pwm_hw_getPeriod(PwmDev dev); + +#endif /* DRV_ADC_AT91_H */ -- 2.25.1