From d7cf9b39121315ce5c2779f94e13f8857f281d05 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Tue, 15 Sep 2015 16:54:23 +0200 Subject: [PATCH] cpu/stm32l1: optimized and fixed timer driver --- cpu/stm32l1/include/periph_cpu.h | 10 +- cpu/stm32l1/include/stm32l1xx.h | 5 +- cpu/stm32l1/periph/timer.c | 400 ++++++------------------------- 3 files changed, 89 insertions(+), 326 deletions(-) diff --git a/cpu/stm32l1/include/periph_cpu.h b/cpu/stm32l1/include/periph_cpu.h index 2d6e0a2c57..133f716460 100644 --- a/cpu/stm32l1/include/periph_cpu.h +++ b/cpu/stm32l1/include/periph_cpu.h @@ -22,7 +22,6 @@ #define PERIPH_CPU_H_ #include "cpu.h" -#include "periph/dev_enums.h" #ifdef __cplusplus extern "C" { @@ -81,6 +80,15 @@ typedef enum { GPIO_AF14 /**< use alternate function 14 */ } gpio_af_t; +/** + * @brief Timer configuration data structure + */ +typedef struct { + TIM_TypeDef *dev; /**< timer device */ + uint8_t rcc; /**< bit in the RCC register */ + uint8_t irqn; /**< IRQ vector entry number */ +} timer_conf_t; + /** * @brief declare needed generic SPI functions * @{ diff --git a/cpu/stm32l1/include/stm32l1xx.h b/cpu/stm32l1/include/stm32l1xx.h index 91a5bafa8d..0f6d9e3834 100644 --- a/cpu/stm32l1/include/stm32l1xx.h +++ b/cpu/stm32l1/include/stm32l1xx.h @@ -852,10 +852,7 @@ typedef struct uint16_t RESERVED10; /*!< Reserved, 0x2A */ __IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */ uint32_t RESERVED12; /*!< Reserved, 0x30 */ - __IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */ - __IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */ - __IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */ - __IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */ + __IO uint32_t CCR[4]; /*!< TIM capture/compare registers 1-4, Address offset: 0x34 ++ */ uint32_t RESERVED17; /*!< Reserved, 0x44 */ __IO uint16_t DCR; /*!< TIM DMA control register, Address offset: 0x48 */ uint16_t RESERVED18; /*!< Reserved, 0x4A */ diff --git a/cpu/stm32l1/periph/timer.c b/cpu/stm32l1/periph/timer.c index 0bc04e54fb..8dcf330592 100644 --- a/cpu/stm32l1/periph/timer.c +++ b/cpu/stm32l1/periph/timer.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014-2015 Freie Universität Berlin * * 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 @@ -14,100 +14,62 @@ * @brief Low-level timer driver implementation * * @author Thomas Eichinger + * @author Hauke Petersen * * @} */ -#include - #include "cpu.h" -#include "board.h" +#include "sched.h" +#include "thread.h" #include "periph_conf.h" #include "periph/timer.h" -#include "thread.h" - -#define ENABLE_DEBUG (0) -#include "debug.h" - -/* guard file in case no TIMER device is defined */ -#if TIMER_0_EN || TIMER_1_EN - -static inline void irq_handler(tim_t timer, TIM_TypeDef *dev0, TIM_TypeDef *dev1); - -typedef struct { - void (*cb)(int); -} timer_conf_t; +/** + * @brief All timers on this CPU have 4 channels + */ +#define CHANNEL_NUMOF (4U) /** - * Timer state memory + * @brief Interrupt state */ -static timer_conf_t config[TIMER_NUMOF]; +static timer_isr_ctx_t isr_ctx[TIMER_NUMOF]; + +/** + * @brief Get the timers base register + */ +static inline TIM_TypeDef *_tim(tim_t dev) +{ + return timer_config[dev].dev; +} int timer_init(tim_t dev, unsigned int ticks_per_us, void (*callback)(int)) { - TIM_TypeDef *timer0; - TIM_TypeDef *timer1; - uint8_t trigger_selector; + TIM_TypeDef *tim; - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - /* enable timer peripheral clock */ - TIMER_0_CLKEN(); - /* set timer's IRQ priority */ - NVIC_SetPriority(TIMER_0_IRQ_CHAN_0, TIMER_0_IRQ_PRIO); - NVIC_SetPriority(TIMER_0_IRQ_CHAN_1, TIMER_0_IRQ_PRIO); - /* select timer */ - timer0 = TIMER_0_DEV_0; - timer1 = TIMER_0_DEV_1; - trigger_selector = TIMER_0_TRIG_SEL; - break; -#endif -#if TIMER_1_EN - case TIMER_1: - /* enable timer peripheral clock */ - TIMER_1_CLKEN(); - /* set timer's IRQ priority */ - NVIC_SetPriority(TIMER_1_IRQ_CHAN_0, TIMER_1_IRQ_PRIO); - NVIC_SetPriority(TIMER_1_IRQ_CHAN_1, TIMER_1_IRQ_PRIO); - /* select timer */ - timer0 = TIMER_1_DEV_0; - timer1 = TIMER_1_DEV_1; - trigger_selector = TIMER_1_TRIG_SEL; - break; -#endif - case TIMER_UNDEFINED: - default: - return -1; + /* check if given timer exists */ + if (dev >= TIMER_NUMOF) { + return -1; } - /* set callback function */ - config[dev].cb = callback; - - /* set timer to run in counter mode */ - timer0->CR1 = (TIM_CR1_ARPE | TIM_CR1_URS); - timer1->CR1 = TIM_CR1_URS; - - /* configure master timer0 */ - /* send update event as trigger output */ - timer0->CR2 |= TIM_CR2_MMS_1; - /* set auto-reload and prescaler values and load new values */ - timer0->ARR = TIMER_0_MAX_VALUE; - timer0->PSC = TIMER_0_PRESCALER * ticks_per_us; - - /* configure slave timer1 */ - /* get input trigger */ - timer1->SMCR |= trigger_selector; - /* external clock mode 1 */ - timer1->SMCR |= TIM_SMCR_SMS; - - /* enable the timer's interrupt */ + /* get base register */ + tim = _tim(dev); + /* save callback */ + isr_ctx[dev].cb = callback; + /* enable peripheral clock */ + RCC->APB1ENR |= (1 << timer_config[dev].rcc); + /* reset timer and configure to up-counting mode */ + tim->CR1 = 0; + tim->CR2 = 0; + tim->SR = 0; + /* configure reload and pre-scaler values */ + tim->ARR = 0xffffffff; + tim->PSC = (((CLOCK_CORECLOCK / 1000000) * ticks_per_us) - 1); + /* trigger update event to make pre-scaler value effective */ + tim->EGR = TIM_EGR_UG; + /* enable interrupts and start the timer */ timer_irq_enable(dev); - - /* start the timer */ timer_start(dev); - return 0; } @@ -119,291 +81,87 @@ int timer_set(tim_t dev, int channel, unsigned int timeout) int timer_set_absolute(tim_t dev, int channel, unsigned int value) { - TIM_TypeDef *timer0; - TIM_TypeDef *timer1; + TIM_TypeDef *tim; - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - /* select timer */ - timer0 = TIMER_0_DEV_0; - timer1 = TIMER_0_DEV_1; - break; -#endif -#if TIMER_1_EN - case TIMER_1: - /* select timer */ - timer0 = TIMER_1_DEV_0; - timer1 = TIMER_1_DEV_1; - break; -#endif - case TIMER_UNDEFINED: - default: - return -1; + if (dev >= TIMER_NUMOF || channel >= CHANNEL_NUMOF || channel < 0) { + return -1; } - timer0->DIER &= ~TIM_DIER_UIE; - - switch (channel) { - case 0: - timer0->CCR1 = (0xffff & value); - timer1->CCR1 = (value >> 16); - timer0->SR &= ~TIM_SR_CC1IF; - timer0->DIER |= TIM_DIER_CC1IE; - DEBUG("Channel 1 set to %x\n", value); - break; - case 1: - timer0->CCR2 = (0xffff & value); - timer1->CCR2 = (value >> 16); - timer0->SR &= ~TIM_SR_CC2IF; - timer0->DIER |= TIM_DIER_CC2IE; - DEBUG("Channel 2 set to %x\n", value); - break; - case 2: - timer0->CCR3 = (0xffff & value); - timer1->CCR3 = (value >> 16); - timer0->SR &= ~TIM_SR_CC3IF; - timer0->DIER |= TIM_DIER_CC3IE; - DEBUG("Channel 3 set to %x\n", value); - break; - case 3: - timer0->CCR4 = (0xffff & value); - timer1->CCR4 = (value >> 16); - timer0->SR &= ~TIM_SR_CC4IF; - timer0->DIER |= TIM_DIER_CC4IE; - DEBUG("Channel 4 set to %x\n", value); - break; - default: - return -1; - } + tim = _tim(dev); + tim->CCR[channel] = value; + tim->SR &= ~(1 << (channel + 1)); + tim->DIER |= (1 << (channel + 1)); return 0; } int timer_clear(tim_t dev, int channel) { - TIM_TypeDef *timer0; - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - timer0 = TIMER_0_DEV_0; - break; -#endif -#if TIMER_1_EN - case TIMER_1: - timer0 = TIMER_1_DEV_0; - break; -#endif - case TIMER_UNDEFINED: - default: - return -1; - } - switch (channel) { - case 0: - timer0->DIER &= ~TIM_DIER_CC1IE; - break; - case 1: - timer0->DIER &= ~TIM_DIER_CC2IE; - break; - case 2: - timer0->DIER &= ~TIM_DIER_CC3IE; - break; - case 3: - timer0->DIER &= ~TIM_DIER_CC4IE; - break; - default: - return -1; + TIM_TypeDef *tim; + + if (dev >= TIMER_NUMOF || channel >= CHANNEL_NUMOF || channel < 0) { + return -1; } + + tim = _tim(dev); + tim->DIER &= ~(1 << (channel + 1)); return 0; } unsigned int timer_read(tim_t dev) { - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - return (((unsigned int)(0xffff & TIMER_0_DEV_0->CNT)) | (TIMER_0_DEV_1->CNT<<16)); - break; -#endif -#if TIMER_1_EN - case TIMER_1: - return (((unsigned int)(0xffff & TIMER_1_DEV_0->CNT)) | (TIMER_1_DEV_1->CNT<<16)); - break; -#endif - case TIMER_UNDEFINED: - default: - return 0; - } + return (unsigned int)_tim(dev)->CNT; } void timer_start(tim_t dev) { - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - /* slave has to be enabled first */ - TIMER_0_DEV_1->CR1 |= TIM_CR1_CEN; - TIMER_0_DEV_0->CR1 |= TIM_CR1_CEN; - break; -#endif -#if TIMER_1_EN - case TIMER_1: - /* slave has to be enabled first */ - TIMER_1_DEV_1->CR1 |= TIM_CR1_CEN; - TIMER_1_DEV_0->CR1 |= TIM_CR1_CEN; - break; -#endif - case TIMER_UNDEFINED: - break; - } + _tim(dev)->CR1 |= TIM_CR1_CEN; } void timer_stop(tim_t dev) { - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - TIMER_0_DEV_0->CR1 &= ~TIM_CR1_CEN; - TIMER_0_DEV_1->CR1 &= ~TIM_CR1_CEN; - break; -#endif -#if TIMER_1_EN - case TIMER_1: - TIMER_1_DEV_0->CR1 &= ~TIM_CR1_CEN; - TIMER_1_DEV_1->CR1 &= ~TIM_CR1_CEN; - break; -#endif - case TIMER_UNDEFINED: - break; - } + _tim(dev)->CR1 &= ~(TIM_CR1_CEN); } void timer_irq_enable(tim_t dev) { - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - NVIC_EnableIRQ(TIMER_0_IRQ_CHAN_0); - NVIC_EnableIRQ(TIMER_0_IRQ_CHAN_1); - break; -#endif -#if TIMER_1_EN - case TIMER_1: - NVIC_EnableIRQ(TIMER_1_IRQ_CHAN_0); - NVIC_EnableIRQ(TIMER_1_IRQ_CHAN_1); - break; -#endif - case TIMER_UNDEFINED: - break; - } + NVIC_EnableIRQ(timer_config[dev].irqn); } void timer_irq_disable(tim_t dev) { - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - NVIC_DisableIRQ(TIMER_0_IRQ_CHAN_0); - NVIC_DisableIRQ(TIMER_0_IRQ_CHAN_1); - break; -#endif -#if TIMER_1_EN - case TIMER_1: - NVIC_DisableIRQ(TIMER_1_IRQ_CHAN_0); - NVIC_DisableIRQ(TIMER_1_IRQ_CHAN_1); - break; -#endif - case TIMER_UNDEFINED: - break; - } + NVIC_DisableIRQ(timer_config[dev].irqn); } void timer_reset(tim_t dev) { - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - TIMER_0_DEV_0->CNT = 0; - TIMER_0_DEV_1->CNT = 0; - break; -#endif -#if TIMER_1_EN - case TIMER_1: - TIMER_1_DEV_0->CNT = 0; - TIMER_1_DEV_1->CNT = 0; - break; -#endif - case TIMER_UNDEFINED: - break; - } + _tim(dev)->CNT = 0; } - -#if TIMER_0_EN -void TIMER_0_ISR_0(void) +static inline void irq_handler(tim_t num, TIM_TypeDef *tim) { - DEBUG("\nenter ISR\n"); - irq_handler(TIMER_0, TIMER_0_DEV_0, TIMER_0_DEV_1); - DEBUG("leave ISR\n\n"); -} -#endif - -#if TIMER_1_EN -void TIMER_1_ISR_0(void) -{ - irq_handler(TIMER_0, TIMER_1_DEV_0, TIMER_1_DEV_1); -} -#endif - -static inline void irq_handler(tim_t timer, TIM_TypeDef *dev0, TIM_TypeDef *dev1) -{ - DEBUG("CNT: %08x SR/DIER: %08x\n", ((dev1->CNT<<16) | (0xffff & dev0->CNT)), - ((dev0->SR<<16) | (0xffff & dev0->DIER))); - - if ((dev0->SR & TIM_SR_CC1IF) && (dev0->DIER & TIM_DIER_CC1IE)) { - /* clear interrupt anyway */ - dev0->SR &= ~TIM_SR_CC1IF; - /* if higher 16bit also match */ - if (dev1->CNT >= dev1->CCR1) { - dev0->DIER &= ~TIM_DIER_CC1IE; - config[timer].cb(0); + for (int i = 0; i < CHANNEL_NUMOF; i++) { + uint16_t bit = (1 << (i + 1)); + if ((tim->SR & bit) && (tim->DIER & bit)) { + tim->SR &= ~(bit); + tim->DIER &= ~(bit); + isr_ctx[num].cb(i); } - DEBUG("channel 1 CCR: %08x\n", ((dev1->CCR1<<16) | (0xffff & dev0->CCR1))); - } - else if ((dev0->SR & TIM_SR_CC2IF) && (dev0->DIER & TIM_DIER_CC2IE)) { - /* clear interrupt anyway */ - dev0->SR &= ~TIM_SR_CC2IF; - /* if higher 16bit also match */ - if (dev1->CNT >= dev1->CCR2) { - dev0->DIER &= ~TIM_DIER_CC2IE; - config[timer].cb(1); - } - DEBUG("channel 2 CCR: %08x\n", ((dev1->CCR2<<16) | (0xffff & dev0->CCR2))); - } - else if ((dev0->SR & TIM_SR_CC3IF) && (dev0->DIER & TIM_DIER_CC3IE)) { - /* clear interrupt anyway */ - dev0->SR &= ~TIM_SR_CC3IF; - /* if higher 16bit also match */ - if (dev1->CNT >= dev1->CCR3) { - dev0->DIER &= ~TIM_DIER_CC3IE; - config[timer].cb(2); - } - DEBUG("channel 3 CCR: %08x\n", ((dev1->CCR3<<16) | (0xffff & dev0->CCR3))); - } - else if ((dev0->SR & TIM_SR_CC4IF) && (dev0->DIER & TIM_DIER_CC4IE)) { - /* clear interrupt anyway */ - dev0->SR &= ~TIM_SR_CC4IF; - /* if higher 16bit also match */ - if (dev1->CNT >= dev1->CCR4) { - dev0->DIER &= ~TIM_DIER_CC4IE; - config[timer].cb(3); - } - DEBUG("channel 4 CCR: %08x\n", ((dev1->CCR4<<16) | (0xffff & dev0->CCR4))); - } - else { - dev0->SR = 0; } if (sched_context_switch_request) { thread_yield(); } } -#endif /* TIMER_0_EN || TIMER_1_EN */ + +#ifdef TIMER_0_ISR +void TIMER_0_ISR(void) +{ + irq_handler(0, timer_config[0].dev); +} +#endif + +#ifdef TIMER_1_ISR +void TIMER_1_ISR(void) +{ + irq_handler(0, timer_config[0].dev); +} +#endif