diff --git a/cpu/efm32/include/periph_cpu.h b/cpu/efm32/include/periph_cpu.h index fd6450ec98..b8c8867a15 100644 --- a/cpu/efm32/include/periph_cpu.h +++ b/cpu/efm32/include/periph_cpu.h @@ -348,12 +348,12 @@ typedef struct { * @{ */ typedef struct { - TIMER_TypeDef *dev; /**< Timer device used */ + void *dev; /**< TIMER_TypeDef or LETIMER_TypeDef device used */ CMU_Clock_TypeDef cmu; /**< the device CMU channel */ } timer_dev_t; typedef struct { - timer_dev_t prescaler; /**< the lower numbered neighboring timer */ + timer_dev_t prescaler; /**< the lower neighboring timer (not initialized for LETIMER) */ timer_dev_t timer; /**< the higher numbered timer */ IRQn_Type irq; /**< number of the higher timer IRQ channel */ } timer_conf_t; diff --git a/cpu/efm32/periph/timer.c b/cpu/efm32/periph/timer.c index 8d92d2caed..fd4a50cb61 100644 --- a/cpu/efm32/periph/timer.c +++ b/cpu/efm32/periph/timer.c @@ -16,23 +16,31 @@ * * @author Hauke Petersen * @author Bas Stottelaar + * @author Thomas Stilwell * @} */ #include "cpu.h" - +#include "log.h" +#include "assert.h" #include "periph/timer.h" #include "periph_conf.h" #include "em_cmu.h" #include "em_timer.h" #include "em_timer_utils.h" +#include "em_letimer.h" /** * @brief This timer implementation has three available channels */ #define CC_CHANNELS (3U) +/** + * @brief The LETIMER implementation has two available channels + */ +#define LE_CC_CHANNELS (2U) + /** * @brief Timer state memory */ @@ -47,24 +55,48 @@ static inline bool _is_wtimer(timer_t dev) return ((uint32_t) timer_config[dev].timer.dev) >= WTIMER0_BASE; #else (void) dev; - return false; #endif } -int timer_init(tim_t dev, unsigned long freq, timer_cb_t callback, void *arg) +/** + * @brief Check whether dev is using a LETIMER device + */ +static inline bool _is_letimer(timer_t dev) +{ +#if defined(LETIMER_COUNT) && LETIMER_COUNT > 0 + return ((uint32_t) timer_config[dev].timer.dev) == LETIMER0_BASE; +#else + (void) dev; + return false; +#endif +} + +static void _letimer_init(tim_t dev, unsigned long freq) +{ + (void) freq; + assert(freq == 32768); + + LETIMER_TypeDef *tim = timer_config[dev].timer.dev; + + /* enable clocks */ + CMU_ClockEnable(timer_config[dev].timer.cmu, true); + CMU_ClockEnable(cmuClock_CORELE, true); + + /* disable and clear interrupts */ + LETIMER_IntDisable(tim, LETIMER_IEN_COMP0 | LETIMER_IEN_COMP1); + LETIMER_IntClear(tim, LETIMER_IFC_COMP0 | LETIMER_IFC_COMP1); + + /* initialize timer without starting it yet */ + LETIMER_Init_TypeDef letimerInit = LETIMER_INIT_DEFAULT; + letimerInit.enable = false; + LETIMER_Init(tim, &letimerInit); +} + +static void _timer_init(tim_t dev, unsigned long freq) { TIMER_TypeDef *pre, *tim; - /* test if given timer device is valid */ - if (dev >= TIMER_NUMOF) { - return -1; - } - - /* save callback */ - isr_ctx[dev].cb = callback; - isr_ctx[dev].arg = arg; - /* get timers */ pre = timer_config[dev].prescaler.dev; tim = timer_config[dev].timer.dev; @@ -78,9 +110,9 @@ int timer_init(tim_t dev, unsigned long freq, timer_cb_t callback, void *arg) TIMER_Init_TypeDef init_pre = TIMER_INIT_DEFAULT; TIMER_Init_TypeDef init_tim = TIMER_INIT_DEFAULT; - init_pre.enable = false; + /* leave the prescaler enabled and toggle only the primary timer */ + init_pre.enable = true; init_pre.prescale = timerPrescale1; - init_tim.enable = false; init_tim.clkSel = timerClkSelCascade; @@ -101,68 +133,159 @@ int timer_init(tim_t dev, unsigned long freq, timer_cb_t callback, void *arg) /* enable interrupts for the channels */ TIMER_IntClear(tim, TIMER_IFC_CC0 | TIMER_IFC_CC1 | TIMER_IFC_CC2); TIMER_IntEnable(tim, TIMER_IEN_CC0 | TIMER_IEN_CC1 | TIMER_IEN_CC2); +} + +int timer_init(tim_t dev, unsigned long freq, timer_cb_t callback, void *arg) +{ + /* test if given timer device is valid */ + if (dev >= TIMER_NUMOF) { + return -1; + } + + /* save callback */ + isr_ctx[dev].cb = callback; + isr_ctx[dev].arg = arg; + + if (_is_letimer(dev)) { + _letimer_init(dev, freq); + } else { + _timer_init(dev, freq); + } NVIC_ClearPendingIRQ(timer_config[dev].irq); NVIC_EnableIRQ(timer_config[dev].irq); - /* start the timers */ - TIMER_Enable(tim, true); - TIMER_Enable(pre, true); + timer_start(dev); return 0; } int timer_set_absolute(tim_t dev, int channel, unsigned int value) { - TIMER_TypeDef *tim; + if (!_is_letimer(dev)) { + TIMER_TypeDef *tim = timer_config[dev].timer.dev; - if (channel < 0 || channel >= (int) CC_CHANNELS) { - return -1; + if (channel < 0 || channel >= (int) CC_CHANNELS) { + return -1; + } + + /* this accounts for some timer being 16-bit and others 32-bit */ + if (value > TIMER_TopGet(tim)) { + return -1; + } + + tim->CC[channel].CCV = (uint32_t) value; + tim->CC[channel].CTRL = TIMER_CC_CTRL_MODE_OUTPUTCOMPARE; } + else { + LETIMER_TypeDef *tim = timer_config[dev].timer.dev; - /* this accounts for some timer being 16-bit and others 32-bit */ - if (value > TIMER_TopGet(timer_config[dev].timer.dev)) { - return -1; + /* LETIMER is countdown only, so we invert the value */ + value = 0xffff - value; + LETIMER_CompareSet(tim, channel, value); + + switch (channel) + { + case 0: + LETIMER_IntClear(tim, LETIMER_IFC_COMP0); + LETIMER_IntEnable(tim, LETIMER_IEN_COMP0); + break; + case 1: + LETIMER_IntClear(tim, LETIMER_IFC_COMP1); + LETIMER_IntEnable(tim, LETIMER_IEN_COMP1); + break; + default: + return -2; + break; + } } - tim = timer_config[dev].timer.dev; - tim->CC[channel].CCV = (uint32_t) value; - tim->CC[channel].CTRL = TIMER_CC_CTRL_MODE_OUTPUTCOMPARE; - return 0; } int timer_clear(tim_t dev, int channel) { - timer_config[dev].timer.dev->CC[channel].CTRL = _TIMER_CC_CTRL_MODE_OFF; + if (!_is_letimer(dev)) { + TIMER_TypeDef *tim = timer_config[dev].timer.dev; + tim->CC[channel].CTRL = _TIMER_CC_CTRL_MODE_OFF; + } + else { + LETIMER_TypeDef *tim = timer_config[dev].timer.dev; + switch (channel) + { + case 0: + LETIMER_IntDisable(tim, LETIMER_IEN_COMP0); + LETIMER_IntClear(tim, LETIMER_IFC_COMP0); + break; + case 1: + LETIMER_IntDisable(tim, LETIMER_IEN_COMP1); + LETIMER_IntClear(tim, LETIMER_IFC_COMP1); + break; + default: + return -1; + } + } return 0; } unsigned int timer_read(tim_t dev) { - return (unsigned int) TIMER_CounterGet(timer_config[dev].timer.dev); + if (_is_letimer(dev)) { + /* LETIMER is countdown only, so we invert the value */ + return (unsigned int) 0xffff + - LETIMER_CounterGet(timer_config[dev].timer.dev); + } + else { + return (unsigned int) TIMER_CounterGet(timer_config[dev].timer.dev); + } } void timer_stop(tim_t dev) { - TIMER_Enable(timer_config[dev].timer.dev, false); + if (_is_letimer(dev)) { + LETIMER_Enable(timer_config[dev].timer.dev, false); + } + else { + TIMER_Enable(timer_config[dev].timer.dev, false); + } } void timer_start(tim_t dev) { - TIMER_Enable(timer_config[dev].timer.dev, true); + if (_is_letimer(dev)) { + LETIMER_Enable(timer_config[dev].timer.dev, true); + } + else { + TIMER_Enable(timer_config[dev].timer.dev, true); + } } #ifdef TIMER_0_ISR void TIMER_0_ISR(void) { - TIMER_TypeDef *tim = timer_config[0].timer.dev; + tim_t dev = 0; - for (int i = 0; i < (int) CC_CHANNELS; i++) { - if (tim->IF & (TIMER_IF_CC0 << i)) { - tim->CC[i].CTRL = _TIMER_CC_CTRL_MODE_OFF; - tim->IFC = (TIMER_IFC_CC0 << i); - isr_ctx[0].cb(isr_ctx[0].arg, i); + if (_is_letimer(dev)) { + LETIMER_TypeDef *tim = timer_config[dev].timer.dev; + + for (int i = 0; i < (int) LE_CC_CHANNELS; i++) { + if (tim->IF & (LETIMER_IF_COMP0 << i)) + { + LETIMER_IntDisable(tim, LETIMER_IEN_COMP0 << i); + LETIMER_IntClear(tim, LETIMER_IFC_COMP0 << i); + isr_ctx[dev].cb(isr_ctx[dev].arg, i); + } + } + } + else { + TIMER_TypeDef *tim = timer_config[dev].timer.dev; + + for (int i = 0; i < (int) CC_CHANNELS; i++) { + if (tim->IF & (TIMER_IF_CC0 << i)) { + tim->CC[i].CTRL = _TIMER_CC_CTRL_MODE_OFF; + tim->IFC = (TIMER_IFC_CC0 << i); + isr_ctx[dev].cb(isr_ctx[dev].arg, i); + } } } cortexm_isr_end();