diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index de7bb6a364..32dc42e8f3 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -23,6 +23,7 @@ #include "cpu.h" #include "exti_config.h" +#include "timer_config.h" #ifdef __cplusplus extern "C" { @@ -212,22 +213,55 @@ typedef struct { uint8_t gclk_src; /**< GCLK source which supplys SERCOM */ } uart_conf_t; +/** + * @brief Common configuration for timer devices + */ +typedef struct { +#ifdef REV_TCC + Tcc *dev; /**< TCC device to use */ +#endif +#ifdef MCLK + volatile uint32_t *mclk; /**< Pointer to MCLK->APBxMASK.reg */ + uint32_t mclk_mask; /**< MCLK_APBxMASK bits to enable Timer */ +#else + uint32_t pm_mask; /**< PM_APBCMASK bits to enable Timer */ +#endif + uint16_t gclk_id; /**< TCn_GCLK_ID */ +} tcc_cfg_t; + +/** + * @brief Static initializer for timer configuration + */ +#ifdef MCLK +#define TCC_CONFIG(tim) { \ + .dev = tim, \ + .mclk = MCLK_ ## tim, \ + .mclk_mask = MCLK_ ## tim ## _MASK, \ + .gclk_id = tim ## _GCLK_ID, } +#else +#define TCC_CONFIG(tim) { \ + .dev = tim, \ + .pm_mask = PM_APBCMASK_ ## tim, \ + .gclk_id = tim ## _GCLK_ID, } +#endif + /** * @brief PWM channel configuration data structure */ typedef struct { - gpio_t pin; /**< GPIO pin */ - gpio_mux_t mux; /**< pin function multiplex value */ - uint8_t chan; /**< TCC channel to use */ + gpio_t pin; /**< GPIO pin */ + gpio_mux_t mux; /**< pin function multiplex value */ + uint8_t chan; /**< TCC channel to use */ } pwm_conf_chan_t; /** * @brief PWM device configuration data structure */ typedef struct { - Tcc *dev; /**< TCC device to use */ - const pwm_conf_chan_t *chan;/**< channel configuration */ - const uint8_t chan_numof; /**< number of channels */ + tcc_cfg_t tim; /**< timer configuration */ + const pwm_conf_chan_t *chan; /**< channel configuration */ + uint8_t chan_numof; /**< number of channels */ + uint8_t gclk_src; /**< GCLK source which clocks TIMER */ } pwm_conf_t; /** diff --git a/cpu/sam0_common/include/timer_config.h b/cpu/sam0_common/include/timer_config.h new file mode 100644 index 0000000000..4d833bbf3f --- /dev/null +++ b/cpu/sam0_common/include/timer_config.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2020 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_sam0_common + * @brief Generic Timer MCLK masks. + * @{ + * + * @author Benjamin Valentin + */ + +#ifndef TIMER_CONFIG_H +#define TIMER_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Automatically generated helper defines + * @{ + */ +#ifdef MCLK_APBAMASK_TC0 +#define MCLK_TC0 (&MCLK->APBAMASK.reg) +#define MCLK_TC0_MASK (MCLK_APBAMASK_TC0) +#endif +#ifdef MCLK_APBBMASK_TC0 +#define MCLK_TC0 (&MCLK->APBBMASK.reg) +#define MCLK_TC0_MASK (MCLK_APBBMASK_TC0) +#endif +#ifdef MCLK_APBCMASK_TC0 +#define MCLK_TC0 (&MCLK->APBCMASK.reg) +#define MCLK_TC0_MASK (MCLK_APBCMASK_TC0) +#endif +#ifdef MCLK_APBDMASK_TC0 +#define MCLK_TC0 (&MCLK->APBDMASK.reg) +#define MCLK_TC0_MASK (MCLK_APBDMASK_TC0) +#endif + +#ifdef MCLK_APBAMASK_TC1 +#define MCLK_TC1 (&MCLK->APBAMASK.reg) +#define MCLK_TC1_MASK (MCLK_APBAMASK_TC1) +#endif +#ifdef MCLK_APBBMASK_TC1 +#define MCLK_TC1 (&MCLK->APBBMASK.reg) +#define MCLK_TC1_MASK (MCLK_APBBMASK_TC1) +#endif +#ifdef MCLK_APBCMASK_TC1 +#define MCLK_TC1 (&MCLK->APBCMASK.reg) +#define MCLK_TC1_MASK (MCLK_APBCMASK_TC1) +#endif +#ifdef MCLK_APBDMASK_TC1 +#define MCLK_TC1 (&MCLK->APBDMASK.reg) +#define MCLK_TC1_MASK (MCLK_APBDMASK_TC1) +#endif + +#ifdef MCLK_APBAMASK_TC2 +#define MCLK_TC2 (&MCLK->APBAMASK.reg) +#define MCLK_TC2_MASK (MCLK_APBAMASK_TC2) +#endif +#ifdef MCLK_APBBMASK_TC2 +#define MCLK_TC2 (&MCLK->APBBMASK.reg) +#define MCLK_TC2_MASK (MCLK_APBBMASK_TC2) +#endif +#ifdef MCLK_APBCMASK_TC2 +#define MCLK_TC2 (&MCLK->APBCMASK.reg) +#define MCLK_TC2_MASK (MCLK_APBCMASK_TC2) +#endif +#ifdef MCLK_APBDMASK_TC2 +#define MCLK_TC2 (&MCLK->APBDMASK.reg) +#define MCLK_TC2_MASK (MCLK_APBDMASK_TC2) +#endif + +#ifdef MCLK_APBAMASK_TC3 +#define MCLK_TC3 (&MCLK->APBAMASK.reg) +#define MCLK_TC3_MASK (MCLK_APBAMASK_TC3) +#endif +#ifdef MCLK_APBBMASK_TC3 +#define MCLK_TC3 (&MCLK->APBBMASK.reg) +#define MCLK_TC3_MASK (MCLK_APBBMASK_TC3) +#endif +#ifdef MCLK_APBCMASK_TC3 +#define MCLK_TC3 (&MCLK->APBCMASK.reg) +#define MCLK_TC3_MASK (MCLK_APBCMASK_TC3) +#endif +#ifdef MCLK_APBDMASK_TC3 +#define MCLK_TC3 (&MCLK->APBDMASK.reg) +#define MCLK_TC3_MASK (MCLK_APBDMASK_TC3) +#endif + +#ifdef MCLK_APBAMASK_TC4 +#define MCLK_TC4 (&MCLK->APBAMASK.reg) +#define MCLK_TC4_MASK (MCLK_APBAMASK_TC4) +#endif +#ifdef MCLK_APBBMASK_TC4 +#define MCLK_TC4 (&MCLK->APBBMASK.reg) +#define MCLK_TC4_MASK (MCLK_APBBMASK_TC4) +#endif +#ifdef MCLK_APBCMASK_TC4 +#define MCLK_TC4 (&MCLK->APBCMASK.reg) +#define MCLK_TC4_MASK (MCLK_APBCMASK_TC4) +#endif +#ifdef MCLK_APBDMASK_TC4 +#define MCLK_TC4 (&MCLK->APBDMASK.reg) +#define MCLK_TC4_MASK (MCLK_APBDMASK_TC4) +#endif + +#ifdef MCLK_APBAMASK_TC5 +#define MCLK_TC5 (&MCLK->APBAMASK.reg) +#define MCLK_TC5_MASK (MCLK_APBAMASK_TC5) +#endif +#ifdef MCLK_APBBMASK_TC5 +#define MCLK_TC5 (&MCLK->APBBMASK.reg) +#define MCLK_TC5_MASK (MCLK_APBBMASK_TC5) +#endif +#ifdef MCLK_APBCMASK_TC5 +#define MCLK_TC5 (&MCLK->APBCMASK.reg) +#define MCLK_TC5_MASK (MCLK_APBCMASK_TC5) +#endif +#ifdef MCLK_APBDMASK_TC5 +#define MCLK_TC5 (&MCLK->APBDMASK.reg) +#define MCLK_TC5_MASK (MCLK_APBDMASK_TC5) +#endif + +#ifdef MCLK_APBAMASK_TC6 +#define MCLK_TC6 (&MCLK->APBAMASK.reg) +#define MCLK_TC6_MASK (MCLK_APBAMASK_TC6) +#endif +#ifdef MCLK_APBBMASK_TC6 +#define MCLK_TC6 (&MCLK->APBBMASK.reg) +#define MCLK_TC6_MASK (MCLK_APBBMASK_TC6) +#endif +#ifdef MCLK_APBCMASK_TC6 +#define MCLK_TC6 (&MCLK->APBCMASK.reg) +#define MCLK_TC6_MASK (MCLK_APBCMASK_TC6) +#endif +#ifdef MCLK_APBDMASK_TC6 +#define MCLK_TC6 (&MCLK->APBDMASK.reg) +#define MCLK_TC6_MASK (MCLK_APBDMASK_TC6) +#endif + +#ifdef MCLK_APBAMASK_TC7 +#define MCLK_TC7 (&MCLK->APBAMASK.reg) +#define MCLK_TC7_MASK (MCLK_APBAMASK_TC7) +#endif +#ifdef MCLK_APBBMASK_TC7 +#define MCLK_TC7 (&MCLK->APBBMASK.reg) +#define MCLK_TC7_MASK (MCLK_APBBMASK_TC7) +#endif +#ifdef MCLK_APBCMASK_TC7 +#define MCLK_TC7 (&MCLK->APBCMASK.reg) +#define MCLK_TC7_MASK (MCLK_APBCMASK_TC7) +#endif +#ifdef MCLK_APBDMASK_TC7 +#define MCLK_TC7 (&MCLK->APBDMASK.reg) +#define MCLK_TC7_MASK (MCLK_APBDMASK_TC7) +#endif + +#ifdef MCLK_APBAMASK_TCC0 +#define MCLK_TCC0 (&MCLK->APBAMASK.reg) +#define MCLK_TCC0_MASK (MCLK_APBAMASK_TCC0) +#endif +#ifdef MCLK_APBBMASK_TCC0 +#define MCLK_TCC0 (&MCLK->APBBMASK.reg) +#define MCLK_TCC0_MASK (MCLK_APBBMASK_TCC0) +#endif +#ifdef MCLK_APBCMASK_TCC0 +#define MCLK_TCC0 (&MCLK->APBCMASK.reg) +#define MCLK_TCC0_MASK (MCLK_APBCMASK_TCC0) +#endif +#ifdef MCLK_APBDMASK_TCC0 +#define MCLK_TCC0 (&MCLK->APBDMASK.reg) +#define MCLK_TCC0_MASK (MCLK_APBDMASK_TCC0) +#endif + +#ifdef MCLK_APBAMASK_TCC1 +#define MCLK_TCC1 (&MCLK->APBAMASK.reg) +#define MCLK_TCC1_MASK (MCLK_APBAMASK_TCC1) +#endif +#ifdef MCLK_APBBMASK_TCC1 +#define MCLK_TCC1 (&MCLK->APBBMASK.reg) +#define MCLK_TCC1_MASK (MCLK_APBBMASK_TCC1) +#endif +#ifdef MCLK_APBCMASK_TCC1 +#define MCLK_TCC1 (&MCLK->APBCMASK.reg) +#define MCLK_TCC1_MASK (MCLK_APBCMASK_TCC1) +#endif +#ifdef MCLK_APBDMASK_TCC1 +#define MCLK_TCC1 (&MCLK->APBDMASK.reg) +#define MCLK_TCC1_MASK (MCLK_APBDMASK_TCC1) +#endif + +#ifdef MCLK_APBAMASK_TCC2 +#define MCLK_TCC2 (&MCLK->APBAMASK.reg) +#define MCLK_TCC2_MASK (MCLK_APBAMASK_TCC2) +#endif +#ifdef MCLK_APBBMASK_TCC2 +#define MCLK_TCC2 (&MCLK->APBBMASK.reg) +#define MCLK_TCC2_MASK (MCLK_APBBMASK_TCC2) +#endif +#ifdef MCLK_APBCMASK_TCC2 +#define MCLK_TCC2 (&MCLK->APBCMASK.reg) +#define MCLK_TCC2_MASK (MCLK_APBCMASK_TCC2) +#endif +#ifdef MCLK_APBDMASK_TCC2 +#define MCLK_TCC2 (&MCLK->APBDMASK.reg) +#define MCLK_TCC2_MASK (MCLK_APBDMASK_TCC2) +#endif + +#ifdef MCLK_APBAMASK_TCC3 +#define MCLK_TCC3 (&MCLK->APBAMASK.reg) +#define MCLK_TCC3_MASK (MCLK_APBAMASK_TCC3) +#endif +#ifdef MCLK_APBBMASK_TCC3 +#define MCLK_TCC3 (&MCLK->APBBMASK.reg) +#define MCLK_TCC3_MASK (MCLK_APBBMASK_TCC3) +#endif +#ifdef MCLK_APBCMASK_TCC3 +#define MCLK_TCC3 (&MCLK->APBCMASK.reg) +#define MCLK_TCC3_MASK (MCLK_APBCMASK_TCC3) +#endif +#ifdef MCLK_APBDMASK_TCC3 +#define MCLK_TCC3 (&MCLK->APBDMASK.reg) +#define MCLK_TCC3_MASK (MCLK_APBDMASK_TCC3) +#endif + +#ifdef MCLK_APBAMASK_TCC4 +#define MCLK_TCC4 (&MCLK->APBAMASK.reg) +#define MCLK_TCC4_MASK (MCLK_APBAMASK_TCC4) +#endif +#ifdef MCLK_APBBMASK_TCC4 +#define MCLK_TCC4 (&MCLK->APBBMASK.reg) +#define MCLK_TCC4_MASK (MCLK_APBBMASK_TCC4) +#endif +#ifdef MCLK_APBCMASK_TCC4 +#define MCLK_TCC4 (&MCLK->APBCMASK.reg) +#define MCLK_TCC4_MASK (MCLK_APBCMASK_TCC4) +#endif +#ifdef MCLK_APBDMASK_TCC4 +#define MCLK_TCC4 (&MCLK->APBDMASK.reg) +#define MCLK_TCC4_MASK (MCLK_APBDMASK_TCC4) +#endif +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* TIMER_CONFIG_H */ +/** @} */ diff --git a/cpu/sam0_common/periph/pwm.c b/cpu/sam0_common/periph/pwm.c index 1806df1f13..7610b8f7ea 100644 --- a/cpu/sam0_common/periph/pwm.c +++ b/cpu/sam0_common/periph/pwm.c @@ -17,14 +17,11 @@ * * @author Peter Kietzmann * @author Hauke Petersen + * @author Benjamin Valentin * * @} */ -#include -#include - -#include "log.h" #include "cpu.h" #include "board.h" #include "periph/gpio.h" @@ -32,7 +29,7 @@ static inline Tcc *_tcc(pwm_t dev) { - return pwm_config[dev].dev; + return pwm_config[dev].tim.dev; } static inline uint8_t _chan(pwm_t dev, int chan) @@ -40,57 +37,7 @@ static inline uint8_t _chan(pwm_t dev, int chan) return pwm_config[dev].chan[chan].chan; } -static int _clk_id(pwm_t dev) -{ - Tcc *tcc = _tcc(dev); - - if (tcc == TCC0) { - return TCC0_GCLK_ID; - } - - if (tcc == TCC1) { - return TCC1_GCLK_ID; - } - - if (tcc == TCC2) { - return TCC2_GCLK_ID; - } -#ifdef TCC3 - if (tcc == TCC3) { - return TCC3_GCLK_ID; - } -#endif - - assert(0); - return 0; -} - -static uint32_t _apbcmask_tcc(pwm_t dev) -{ - Tcc *tcc = _tcc(dev); - - if (tcc == TCC0) { - return PM_APBCMASK_TCC0; - } - - if (tcc == TCC1) { - return PM_APBCMASK_TCC1; - } - - if (tcc == TCC2) { - return PM_APBCMASK_TCC2; - } -#ifdef TCC3 - if (tcc == TCC3) { - return PM_APBCMASK_TCC3; - } -#endif - - assert(0); - return 0; -} - -static uint8_t get_prescaler(unsigned int target, int *scale) +static uint8_t _get_prescaler(unsigned int target, int *scale) { if (target == 0) { return 0xff; @@ -124,13 +71,68 @@ static uint8_t get_prescaler(unsigned int target, int *scale) return target - 1; } +static uint8_t _get_cc_numof(Tcc *tcc) +{ + switch ((uintptr_t) tcc) { +#ifdef TCC0_CC_NUM + case (uintptr_t)TCC0: + return TCC0_CC_NUM; +#endif +#ifdef TCC1_CC_NUM + case (uintptr_t)TCC1: + return TCC1_CC_NUM; +#endif +#ifdef TCC2_CC_NUM + case (uintptr_t)TCC2: + return TCC2_CC_NUM; +#endif +#ifdef TCC3_CC_NUM + case (uintptr_t)TCC3: + return TCC3_CC_NUM; +#endif +#ifdef TCC4_CC_NUM + case (uintptr_t)TCC4: + return TCC4_CC_NUM; +#endif +#ifdef TCC5_CC_NUM + case (uintptr_t)TCC5: + return TCC5_CC_NUM; +#endif + } + + assert(0); + return 0; +} + static void poweron(pwm_t dev) { - PM->APBCMASK.reg |= _apbcmask_tcc(dev); - GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_CLKEN | - GCLK_CLKCTRL_GEN_GCLK0 | - GCLK_CLKCTRL_ID(_clk_id(dev))); - while (GCLK->STATUS.bit.SYNCBUSY) {} + const pwm_conf_t *cfg = &pwm_config[dev]; + + sam0_gclk_enable(cfg->gclk_src); +#ifdef MCLK + GCLK->PCHCTRL[cfg->tim.gclk_id].reg = GCLK_PCHCTRL_GEN(cfg->gclk_src) + | GCLK_PCHCTRL_CHEN; + *cfg->tim.mclk |= cfg->tim.mclk_mask; +#else + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN + | GCLK_CLKCTRL_GEN(cfg->gclk_src) + | GCLK_CLKCTRL_ID(cfg->tim.gclk_id); + PM->APBCMASK.reg |= cfg->tim.pm_mask; +#endif +} + +static void poweroff(pwm_t dev) +{ + const pwm_conf_t *cfg = &pwm_config[dev]; + +#ifdef MCLK + GCLK->PCHCTRL[cfg->tim.gclk_id].reg = 0; + *cfg->tim.mclk &= ~cfg->tim.mclk_mask; +#else + PM->APBCMASK.reg &= ~cfg->tim.pm_mask; + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_GEN_GCLK7 + | GCLK_CLKCTRL_ID(cfg->tim.gclk_id); +#endif } uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) @@ -143,12 +145,14 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) return 0; } + const uint32_t f_src = sam0_gclk_freq(pwm_config[dev].gclk_src); + /* calculate the closest possible clock presacler */ - prescaler = get_prescaler(CLOCK_CORECLOCK / (freq * res), &scale); + prescaler = _get_prescaler(f_src / (freq * res), &scale); if (prescaler == 0xff) { return 0; } - f_real = (CLOCK_CORECLOCK / (scale * res)); + f_real = f_src / (scale * res); /* configure the used pins */ for (unsigned i = 0; i < pwm_config[dev].chan_numof; i++) { @@ -164,6 +168,7 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) /* reset TCC module */ _tcc(dev)->CTRLA.reg = TCC_CTRLA_SWRST; while (_tcc(dev)->SYNCBUSY.reg & TCC_SYNCBUSY_SWRST) {} + /* set PWM mode */ switch (mode) { case PWM_LEFT: @@ -179,16 +184,20 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) while (_tcc(dev)->SYNCBUSY.reg & TCC_SYNCBUSY_CTRLB) {} /* configure the TCC device */ - _tcc(dev)->CTRLA.reg = (TCC_CTRLA_PRESCSYNC_GCLK_Val - | TCC_CTRLA_PRESCALER(prescaler)); + _tcc(dev)->CTRLA.reg = TCC_CTRLA_PRESCSYNC_GCLK_Val + | TCC_CTRLA_PRESCALER(prescaler); + /* select the waveform generation mode -> normal PWM */ _tcc(dev)->WAVE.reg = (TCC_WAVE_WAVEGEN_NPWM); while (_tcc(dev)->SYNCBUSY.reg & TCC_SYNCBUSY_WAVE) {} + /* set the selected period */ _tcc(dev)->PER.reg = (res - 1); while (_tcc(dev)->SYNCBUSY.reg & TCC_SYNCBUSY_PER) {} + /* start PWM operation */ - _tcc(dev)->CTRLA.reg |= (TCC_CTRLA_ENABLE); + _tcc(dev)->CTRLA.reg |= TCC_CTRLA_ENABLE; + /* return the actual frequency the PWM is running at */ return f_real; } @@ -206,14 +215,12 @@ void pwm_set(pwm_t dev, uint8_t channel, uint16_t value) } uint8_t chan = _chan(dev, channel); - if (chan < 4) { - _tcc(dev)->CC[chan].reg = value; - while (_tcc(dev)->SYNCBUSY.reg & (TCC_SYNCBUSY_CC0 << chan)) {} - } else { - chan -= 4; - _tcc(dev)->CCB[chan].reg = value; - while (_tcc(dev)->SYNCBUSY.reg & (TCC_SYNCBUSY_CCB0 << chan)) {} - } + + /* TODO: use OTMX for pin remapping */ + chan %= _get_cc_numof(_tcc(dev)); + + _tcc(dev)->CC[chan].reg = value; + while (_tcc(dev)->SYNCBUSY.reg & (TCC_SYNCBUSY_CC0 << chan)) {} } void pwm_poweron(pwm_t dev) @@ -225,9 +232,5 @@ void pwm_poweron(pwm_t dev) void pwm_poweroff(pwm_t dev) { _tcc(dev)->CTRLA.reg &= ~(TCC_CTRLA_ENABLE); - - PM->APBCMASK.reg &= ~_apbcmask_tcc(dev); - GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_GEN_GCLK7 | - GCLK_CLKCTRL_ID(_clk_id(dev))); - while (GCLK->STATUS.bit.SYNCBUSY) {} + poweroff(dev); }