diff --git a/boards/ikea-tradfri/include/board.h b/boards/ikea-tradfri/include/board.h index 6ca31bb52f..108f827236 100644 --- a/boards/ikea-tradfri/include/board.h +++ b/boards/ikea-tradfri/include/board.h @@ -31,10 +31,14 @@ extern "C" { /** * @name Xtimer configuration * - * The timer runs at 250 KHz to increase accuracy. + * The timer runs at 250 kHz to increase accuracy or 32768 Hz for LETIMER. * @{ */ +#ifdef EFM32_USE_LETIMER +#define XTIMER_HZ (32768UL) +#else #define XTIMER_HZ (250000UL) +#endif #define XTIMER_WIDTH (16) /** @} */ diff --git a/boards/ikea-tradfri/include/periph_conf.h b/boards/ikea-tradfri/include/periph_conf.h index 2e8b22c794..77e72a3f9c 100644 --- a/boards/ikea-tradfri/include/periph_conf.h +++ b/boards/ikea-tradfri/include/periph_conf.h @@ -48,7 +48,6 @@ extern "C" { #endif /** @} */ - /** * @name RTT configuration * @{ @@ -82,25 +81,41 @@ static const spi_dev_t spi_config[] = { /** * @name Timer configuration * - * The implementation uses two timers in cascade mode. + * The implementation can use one low-energy timer + * or two regular timers in cascade mode. * @{ */ +#if EFM32_USE_LETIMER static const timer_conf_t timer_config[] = { { - { + .timer = { + .dev = LETIMER0, + .cmu = cmuClock_LETIMER0 + }, + .irq = LETIMER0_IRQn + } +}; +#define TIMER_0_ISR isr_letimer0 + +#else +static const timer_conf_t timer_config[] = { + { + .prescaler = { .dev = TIMER0, .cmu = cmuClock_TIMER0 }, - { + .timer = { .dev = TIMER1, .cmu = cmuClock_TIMER1 }, .irq = TIMER1_IRQn } }; +#define TIMER_0_ISR isr_timer1 + +#endif /* EFM32_USE_LETIMER */ #define TIMER_NUMOF ARRAY_SIZE(timer_config) -#define TIMER_0_ISR isr_timer1 /** @} */ /** diff --git a/boards/slstk3401a/include/board.h b/boards/slstk3401a/include/board.h index 645149e122..40a9157694 100644 --- a/boards/slstk3401a/include/board.h +++ b/boards/slstk3401a/include/board.h @@ -32,10 +32,14 @@ extern "C" { /** * @name Xtimer configuration * - * The timer runs at 250 KHz to increase accuracy. + * The timer runs at 250 KHz to increase accuracy or 32768 Hz for LETIMER. * @{ */ +#ifdef EFM32_USE_LETIMER +#define XTIMER_HZ (32768UL) +#else #define XTIMER_HZ (250000UL) +#endif #define XTIMER_WIDTH (16) /** @} */ diff --git a/boards/slstk3401a/include/periph_conf.h b/boards/slstk3401a/include/periph_conf.h index 77201b0b7f..2a3ad6ba5f 100644 --- a/boards/slstk3401a/include/periph_conf.h +++ b/boards/slstk3401a/include/periph_conf.h @@ -141,25 +141,41 @@ static const spi_dev_t spi_config[] = { /** * @name Timer configuration * - * The implementation uses two timers in cascade mode. + * The implementation can use one low-energy timer + * or two regular timers in cascade mode. * @{ */ +#if EFM32_USE_LETIMER static const timer_conf_t timer_config[] = { { - { + .timer = { + .dev = LETIMER0, + .cmu = cmuClock_LETIMER0 + }, + .irq = LETIMER0_IRQn + } +}; +#define TIMER_0_ISR isr_letimer0 + +#else +static const timer_conf_t timer_config[] = { + { + .prescaler = { .dev = TIMER0, .cmu = cmuClock_TIMER0 }, - { + .timer = { .dev = TIMER1, .cmu = cmuClock_TIMER1 }, .irq = TIMER1_IRQn } }; +#define TIMER_0_ISR isr_timer1 + +#endif /* EFM32_USE_LETIMER */ #define TIMER_NUMOF ARRAY_SIZE(timer_config) -#define TIMER_0_ISR isr_timer1 /** @} */ /** diff --git a/boards/slstk3402a/include/board.h b/boards/slstk3402a/include/board.h index 5ebff1cb2d..b4149c1f00 100644 --- a/boards/slstk3402a/include/board.h +++ b/boards/slstk3402a/include/board.h @@ -33,8 +33,13 @@ extern "C" { * @name Xtimer configuration * @{ */ +#ifdef EFM32_USE_LETIMER +#define XTIMER_HZ (32768UL) +#define XTIMER_WIDTH (16) +#else #define XTIMER_HZ (1000000UL) #define XTIMER_WIDTH (32) +#endif /** @} */ /** diff --git a/boards/slstk3402a/include/periph_conf.h b/boards/slstk3402a/include/periph_conf.h index a93391a9f8..dfefe0bd5c 100644 --- a/boards/slstk3402a/include/periph_conf.h +++ b/boards/slstk3402a/include/periph_conf.h @@ -132,25 +132,41 @@ static const spi_dev_t spi_config[] = { /** * @name Timer configuration * - * The implementation uses two timers in cascade mode. + * The implementation can use one low-energy timer + * or two regular timers in cascade mode. * @{ */ +#if EFM32_USE_LETIMER static const timer_conf_t timer_config[] = { { - { + .timer = { + .dev = LETIMER0, + .cmu = cmuClock_LETIMER0 + }, + .irq = LETIMER0_IRQn + } +}; +#define TIMER_0_ISR isr_letimer0 + +#else +static const timer_conf_t timer_config[] = { + { + .prescaler = { .dev = WTIMER0, .cmu = cmuClock_WTIMER0 }, - { + .timer = { .dev = WTIMER1, .cmu = cmuClock_WTIMER1 }, .irq = WTIMER1_IRQn } }; +#define TIMER_0_ISR isr_wtimer1 + +#endif /* EFM32_USE_LETIMER */ #define TIMER_NUMOF ARRAY_SIZE(timer_config) -#define TIMER_0_ISR isr_wtimer1 /** @} */ /** diff --git a/boards/sltb001a/include/board.h b/boards/sltb001a/include/board.h index dc59748dbc..1291ca66e9 100644 --- a/boards/sltb001a/include/board.h +++ b/boards/sltb001a/include/board.h @@ -32,11 +32,16 @@ extern "C" { /** * @name Xtimer configuration * - * The timer runs at 250 KHz to increase accuracy. + * The timer runs at 250 KHz to increase accuracy or 32768 Hz for LETIMER. * @{ */ +#ifdef EFM32_USE_LETIMER +#define XTIMER_HZ (32768UL) +#define XTIMER_WIDTH (16) +#else #define XTIMER_HZ (250000UL) #define XTIMER_WIDTH (16) +#endif /** @} */ /** diff --git a/boards/sltb001a/include/periph_conf.h b/boards/sltb001a/include/periph_conf.h index f43a84b353..ddc13e9645 100644 --- a/boards/sltb001a/include/periph_conf.h +++ b/boards/sltb001a/include/periph_conf.h @@ -141,25 +141,41 @@ static const spi_dev_t spi_config[] = { /** * @name Timer configuration * - * The implementation uses two timers in cascade mode. + * The implementation can use one low-energy timer + * or two regular timers in cascade mode. * @{ */ +#if EFM32_USE_LETIMER static const timer_conf_t timer_config[] = { { - { + .timer = { + .dev = LETIMER0, + .cmu = cmuClock_LETIMER0 + }, + .irq = LETIMER0_IRQn + } +}; +#define TIMER_0_ISR isr_letimer0 + +#else +static const timer_conf_t timer_config[] = { + { + .prescaler = { .dev = TIMER0, .cmu = cmuClock_TIMER0 }, - { + .timer = { .dev = TIMER1, .cmu = cmuClock_TIMER1 }, .irq = TIMER1_IRQn } }; +#define TIMER_0_ISR isr_timer1 + +#endif /* EFM32_USE_LETIMER */ #define TIMER_NUMOF ARRAY_SIZE(timer_config) -#define TIMER_0_ISR isr_timer1 /** @} */ /** diff --git a/boards/stk3600/include/board.h b/boards/stk3600/include/board.h index e692bd8dca..52e0869e08 100644 --- a/boards/stk3600/include/board.h +++ b/boards/stk3600/include/board.h @@ -32,11 +32,16 @@ extern "C" { /** * @name Xtimer configuration * - * The timer runs at 250 KHz to increase accuracy. + * The timer runs at 250 KHz to increase accuracy or 32768 Hz for LETIMER. * @{ */ +#ifdef EFM32_USE_LETIMER +#define XTIMER_HZ (32768UL) +#define XTIMER_WIDTH (16) +#else #define XTIMER_HZ (250000UL) #define XTIMER_WIDTH (16) +#endif /** @} */ /** diff --git a/boards/stk3600/include/periph_conf.h b/boards/stk3600/include/periph_conf.h index 9d6e3a4e18..bbef6a9327 100644 --- a/boards/stk3600/include/periph_conf.h +++ b/boards/stk3600/include/periph_conf.h @@ -155,7 +155,6 @@ static const pwm_conf_t pwm_config[] = { #define PWM_NUMOF ARRAY_SIZE(pwm_channel_config) /** @} */ - /** * @name RTT configuration * @{ @@ -196,25 +195,41 @@ static const spi_dev_t spi_config[] = { /** * @name Timer configuration * - * The implementation uses two timers in cascade mode. + * The implementation can use one low-energy timer + * or two regular timers in cascade mode. * @{ */ +#if EFM32_USE_LETIMER static const timer_conf_t timer_config[] = { { - { + .timer = { + .dev = LETIMER0, + .cmu = cmuClock_LETIMER0 + }, + .irq = LETIMER0_IRQn + } +}; +#define TIMER_0_ISR isr_letimer0 + +#else +static const timer_conf_t timer_config[] = { + { + .prescaler = { .dev = TIMER0, .cmu = cmuClock_TIMER0 }, - { + .timer = { .dev = TIMER1, .cmu = cmuClock_TIMER1 }, .irq = TIMER1_IRQn } }; +#define TIMER_0_ISR isr_timer1 + +#endif /* EFM32_USE_LETIMER */ #define TIMER_NUMOF ARRAY_SIZE(timer_config) -#define TIMER_0_ISR isr_timer1 /** @} */ /** diff --git a/boards/stk3700/include/board.h b/boards/stk3700/include/board.h index 6d79312310..973c6b2399 100644 --- a/boards/stk3700/include/board.h +++ b/boards/stk3700/include/board.h @@ -32,11 +32,16 @@ extern "C" { /** * @name Xtimer configuration * - * The timer runs at 250 KHz to increase accuracy. + * The timer runs at 250 KHz to increase accuracy or 32768 Hz for LETIMER. * @{ */ +#ifdef EFM32_USE_LETIMER +#define XTIMER_HZ (32768UL) +#define XTIMER_WIDTH (16) +#else #define XTIMER_HZ (250000UL) #define XTIMER_WIDTH (16) +#endif /** @} */ /** diff --git a/boards/stk3700/include/periph_conf.h b/boards/stk3700/include/periph_conf.h index ee54dec572..01829e99a9 100644 --- a/boards/stk3700/include/periph_conf.h +++ b/boards/stk3700/include/periph_conf.h @@ -155,7 +155,6 @@ static const pwm_conf_t pwm_config[] = { #define PWM_NUMOF ARRAY_SIZE(pwm_channel_config) /** @} */ - /** * @name RTT configuration * @{ @@ -196,25 +195,41 @@ static const spi_dev_t spi_config[] = { /** * @name Timer configuration * - * The implementation uses two timers in cascade mode. + * The implementation can use one low-energy timer + * or two regular timers in cascade mode. * @{ */ +#if EFM32_USE_LETIMER static const timer_conf_t timer_config[] = { { - { + .timer = { + .dev = LETIMER0, + .cmu = cmuClock_LETIMER0 + }, + .irq = LETIMER0_IRQn + } +}; +#define TIMER_0_ISR isr_letimer0 + +#else +static const timer_conf_t timer_config[] = { + { + .prescaler = { .dev = TIMER0, .cmu = cmuClock_TIMER0 }, - { + .timer = { .dev = TIMER1, .cmu = cmuClock_TIMER1 }, .irq = TIMER1_IRQn } }; +#define TIMER_0_ISR isr_timer1 + +#endif /* EFM32_USE_LETIMER */ #define TIMER_NUMOF ARRAY_SIZE(timer_config) -#define TIMER_0_ISR isr_timer1 /** @} */ /** diff --git a/cpu/efm32/include/periph_cpu.h b/cpu/efm32/include/periph_cpu.h index fd6450ec98..6ef3507a33 100644 --- a/cpu/efm32/include/periph_cpu.h +++ b/cpu/efm32/include/periph_cpu.h @@ -348,17 +348,37 @@ 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; /** @} */ + +/** + * @brief The implementation can use one LETIMER or two regular timers cascaded + */ +#ifndef EFM32_USE_LETIMER +#define EFM32_USE_LETIMER 0 +#endif + +#ifdef EFM32_USE_LETIMER +/** + * @brief This timer implementation has two available channels + */ +#define TIMER_CHANNEL_NUMOF (2) +#else +/** + * @brief This timer implementation has three available channels + */ +#define TIMER_CHANNEL_NUMOF (3) +#endif + /** * @brief UART device configuration. */ diff --git a/cpu/efm32/periph/timer.c b/cpu/efm32/periph/timer.c index 8d92d2caed..ff291340f9 100644 --- a/cpu/efm32/periph/timer.c +++ b/cpu/efm32/periph/timer.c @@ -16,22 +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 "pm_layered.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 + * @brief These power modes will be blocked while the timer is running */ -#define CC_CHANNELS (3U) +#ifndef EFM32_TIMER_PM_BLOCKER +#define EFM32_TIMER_PM_BLOCKER 1 +#endif +#ifndef EFM32_LETIMER_PM_BLOCKER +#define EFM32_LETIMER_PM_BLOCKER 0 +#endif /** * @brief Timer state memory @@ -47,24 +56,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 +111,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 +134,175 @@ 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 >= TIMER_CHANNEL_NUMOF) { + 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_TypeDef *tim = timer_config[dev].timer.dev; + if (tim->STATUS & LETIMER_STATUS_RUNNING) { + pm_unblock(EFM32_LETIMER_PM_BLOCKER); + } + LETIMER_Enable(timer_config[dev].timer.dev, false); + } + else { + TIMER_TypeDef *tim = timer_config[dev].timer.dev; + if (tim->STATUS & TIMER_STATUS_RUNNING) { + pm_unblock(EFM32_TIMER_PM_BLOCKER); + } + 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_TypeDef *tim = timer_config[dev].timer.dev; + if (!(tim->STATUS & LETIMER_STATUS_RUNNING)) { + pm_block(EFM32_LETIMER_PM_BLOCKER); + } + LETIMER_Enable(timer_config[dev].timer.dev, true); + } + else { + TIMER_TypeDef *tim = timer_config[dev].timer.dev; + if (!(tim->STATUS & TIMER_STATUS_RUNNING)) { + pm_block(EFM32_TIMER_PM_BLOCKER); + } + 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 < TIMER_CHANNEL_NUMOF; 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 < TIMER_CHANNEL_NUMOF; 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();