/****************************************************************************** * The MIT License * * Copyright (c) 2011 LeafLabs, LLC. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. *****************************************************************************/ /** * @file timer.c * @author Marti Bolivar * @brief New-style timer interface */ #include "timer.h" /* Just like the corresponding DIER bits: * [0] = Update handler; * [1,2,3,4] = capture/compare 1,2,3,4 handlers, respectively; * [5] = COM; * [6] = TRG; * [7] = BRK. */ #define NR_ADV_HANDLERS 8 /* Update, capture/compare 1,2,3,4; ; trigger. */ #define NR_GEN_HANDLERS 7 /* Update only. */ #define NR_BAS_HANDLERS 1 static timer_dev timer1 = { .regs = { .adv = TIMER1_BASE }, .clk_id = RCC_TIMER1, .type = TIMER_ADVANCED, .handlers = { [NR_ADV_HANDLERS - 1] = 0 }, }; /** Timer 1 device (advanced) */ timer_dev *TIMER1 = &timer1; static timer_dev timer2 = { .regs = { .gen = TIMER2_BASE }, .clk_id = RCC_TIMER2, .type = TIMER_GENERAL, .handlers = { [NR_GEN_HANDLERS - 1] = 0 }, }; /** Timer 2 device (general-purpose) */ timer_dev *TIMER2 = &timer2; static timer_dev timer3 = { .regs = { .gen = TIMER3_BASE }, .clk_id = RCC_TIMER3, .type = TIMER_GENERAL, .handlers = { [NR_GEN_HANDLERS - 1] = 0 }, }; /** Timer 3 device (general-purpose) */ timer_dev *TIMER3 = &timer3; static timer_dev timer4 = { .regs = { .gen = TIMER4_BASE }, .clk_id = RCC_TIMER4, .type = TIMER_GENERAL, .handlers = { [NR_GEN_HANDLERS - 1] = 0 }, }; /** Timer 4 device (general-purpose) */ timer_dev *TIMER4 = &timer4; #ifdef STM32_HIGH_DENSITY static timer_dev timer5 = { .regs = { .gen = TIMER5_BASE }, .clk_id = RCC_TIMER5, .type = TIMER_GENERAL, .handlers = { [NR_GEN_HANDLERS - 1] = 0 }, }; /** Timer 5 device (general-purpose) */ timer_dev *TIMER5 = &timer5; static timer_dev timer6 = { .regs = { .bas = TIMER6_BASE }, .clk_id = RCC_TIMER6, .type = TIMER_BASIC, .handlers = { [NR_BAS_HANDLERS - 1] = 0 }, }; /** Timer 6 device (basic) */ timer_dev *TIMER6 = &timer6; static timer_dev timer7 = { .regs = { .bas = TIMER7_BASE }, .clk_id = RCC_TIMER7, .type = TIMER_BASIC, .handlers = { [NR_BAS_HANDLERS - 1] = 0 }, }; /** Timer 7 device (basic) */ timer_dev *TIMER7 = &timer7; static timer_dev timer8 = { .regs = { .adv = TIMER8_BASE }, .clk_id = RCC_TIMER8, .type = TIMER_ADVANCED, .handlers = { [NR_ADV_HANDLERS - 1] = 0 }, }; /** Timer 8 device (advanced) */ timer_dev *TIMER8 = &timer8; #endif /* * Convenience routines */ static void disable_channel(timer_dev *dev, uint8 channel); static void pwm_mode(timer_dev *dev, uint8 channel); static void output_compare_mode(timer_dev *dev, uint8 channel); static inline void enable_irq(timer_dev *dev, uint8 interrupt); /** * Initialize a timer, and reset its register map. * @param dev Timer to initialize */ void timer_init(timer_dev *dev) { rcc_clk_enable(dev->clk_id); rcc_reset_dev(dev->clk_id); } /** * @brief Disable a timer. * * The timer will stop counting, all DMA requests and interrupts will * be disabled, and no state changes will be output. * * @param dev Timer to disable. */ void timer_disable(timer_dev *dev) { (dev->regs).bas->CR1 = 0; (dev->regs).bas->DIER = 0; switch (dev->type) { case TIMER_ADVANCED: /* fall-through */ case TIMER_GENERAL: (dev->regs).gen->CCER = 0; break; case TIMER_BASIC: break; } } /** * Sets the mode of an individual timer channel. * * Note that not all timers can be configured in every mode. For * example, basic timers cannot be configured to output compare mode. * Be sure to use a timer which is appropriate for the mode you want. * * @param dev Timer whose channel mode to set * @param channel Relevant channel * @param mode New timer mode for channel */ void timer_set_mode(timer_dev *dev, uint8 channel, timer_mode mode) { ASSERT_FAULT(channel > 0 && channel <= 4); /* TODO decide about the basic timers */ ASSERT(dev->type != TIMER_BASIC); if (dev->type == TIMER_BASIC) return; switch (mode) { case TIMER_DISABLED: disable_channel(dev, channel); break; case TIMER_PWM: pwm_mode(dev, channel); break; case TIMER_OUTPUT_COMPARE: output_compare_mode(dev, channel); break; } } /** * @brief Call a function on timer devices. * @param fn Function to call on each timer device. */ void timer_foreach(void (*fn)(timer_dev*)) { fn(TIMER1); fn(TIMER2); fn(TIMER3); fn(TIMER4); #ifdef STM32_HIGH_DENSITY fn(TIMER5); fn(TIMER6); fn(TIMER7); fn(TIMER8); #endif } /** * @brief Attach a timer interrupt. * @param dev Timer device * @param interrupt Interrupt number to attach to; this may be any * timer_interrupt_id or timer_channel value appropriate * for the timer. * @param handler Handler to attach to the given interrupt. * @see timer_interrupt_id * @see timer_channel */ void timer_attach_interrupt(timer_dev *dev, uint8 interrupt, voidFuncPtr handler) { dev->handlers[interrupt] = handler; timer_enable_irq(dev, interrupt); enable_irq(dev, interrupt); } /** * @brief Detach a timer interrupt. * @param dev Timer device * @param interrupt Interrupt number to detach; this may be any * timer_interrupt_id or timer_channel value appropriate * for the timer. * @see timer_interrupt_id * @see timer_channel */ void timer_detach_interrupt(timer_dev *dev, uint8 interrupt) { timer_disable_irq(dev, interrupt); dev->handlers[interrupt] = NULL; } /* * IRQ handlers */ static inline void dispatch_adv_brk(timer_dev *dev); static inline void dispatch_adv_up(timer_dev *dev); static inline void dispatch_adv_trg_com(timer_dev *dev); static inline void dispatch_adv_cc(timer_dev *dev); static inline void dispatch_general(timer_dev *dev); static inline void dispatch_basic(timer_dev *dev); void __irq_tim1_brk(void) { dispatch_adv_brk(TIMER1); } void __irq_tim1_up(void) { dispatch_adv_up(TIMER1); } void __irq_tim1_trg_com(void) { dispatch_adv_trg_com(TIMER1); } void __irq_tim1_cc(void) { dispatch_adv_cc(TIMER1); } void __irq_tim2(void) { dispatch_general(TIMER2); } void __irq_tim3(void) { dispatch_general(TIMER3); } void __irq_tim4(void) { dispatch_general(TIMER4); } #if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY) void __irq_tim5(void) { dispatch_general(TIMER5); } void __irq_tim6(void) { dispatch_basic(TIMER6); } void __irq_tim7(void) { dispatch_basic(TIMER7); } void __irq_tim8_brk(void) { dispatch_adv_brk(TIMER8); } void __irq_tim8_up(void) { dispatch_adv_up(TIMER8); } void __irq_tim8_trg_com(void) { dispatch_adv_trg_com(TIMER8); } void __irq_tim8_cc(void) { dispatch_adv_cc(TIMER8); } #endif static inline void dispatch_irq(timer_dev *dev, uint8 iid, uint8 sr_bit); static inline void dispatch_cc_irqs(timer_dev *dev); static inline void dispatch_adv_brk(timer_dev *dev) { dispatch_irq(dev, TIMER_BREAK_INTERRUPT, TIMER_SR_BIF_BIT); } static inline void dispatch_adv_up(timer_dev *dev) { dispatch_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF_BIT); } static inline void dispatch_adv_trg_com(timer_dev *dev) { dispatch_irq(dev, TIMER_TRG_INTERRUPT, TIMER_SR_TIF_BIT); dispatch_irq(dev, TIMER_COM_INTERRUPT, TIMER_SR_COMIF_BIT); } static inline void dispatch_adv_cc(timer_dev *dev) { dispatch_cc_irqs(dev); } static inline void dispatch_general(timer_dev *dev) { dispatch_irq(dev, TIMER_TRG_INTERRUPT, TIMER_SR_TIF_BIT); dispatch_cc_irqs(dev); dispatch_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF_BIT); } static inline void dispatch_basic(timer_dev *dev) { dispatch_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF_BIT); } static inline void dispatch_irq(timer_dev *dev, uint8 iid, uint8 sr_bit) { __io uint32 *sr = &(dev->regs).bas->SR; if (bb_peri_get_bit(sr, sr_bit)) { if (dev->handlers[iid]) (dev->handlers[iid])(); bb_peri_set_bit(sr, sr_bit, 0); } } static inline void dispatch_cc_irqs(timer_dev *dev) { uint32 sr = (dev->regs).gen->SR; uint32 sr_clear = 0; uint32 b; ASSERT_FAULT(sr & (TIMER_SR_CC1IF | TIMER_SR_CC2IF | TIMER_SR_CC3IF | TIMER_SR_CC4IF)); for (b = TIMER_SR_CC1IF_BIT; b <= TIMER_SR_CC4IF_BIT; b++) { uint32 mask = BIT(b); if (sr & mask) { if (dev->handlers[b]) (dev->handlers[b])(); sr_clear |= mask; } } (dev->regs).gen->SR &= ~sr_clear; } /* * Utilities */ static void disable_channel(timer_dev *dev, uint8 channel) { timer_detach_interrupt(dev, channel); timer_cc_disable(dev, channel); } static void pwm_mode(timer_dev *dev, uint8 channel) { timer_disable_irq(dev, channel); timer_oc_set_mode(dev, channel, TIMER_OC_MODE_PWM_1, TIMER_OC_PE); timer_cc_enable(dev, channel); } static void output_compare_mode(timer_dev *dev, uint8 channel) { timer_oc_set_mode(dev, channel, TIMER_OC_MODE_ACTIVE_ON_MATCH, 0); timer_cc_enable(dev, channel); } static void enable_advanced_irq(timer_dev *dev, timer_interrupt_id id); static void enable_nonmuxed_irq(timer_dev *dev); static inline void enable_irq(timer_dev *dev, timer_interrupt_id iid) { if (dev->type == TIMER_ADVANCED) { enable_advanced_irq(dev, iid); } else { enable_nonmuxed_irq(dev); } } static void enable_advanced_irq(timer_dev *dev, timer_interrupt_id id) { uint8 is_timer1 = dev->clk_id == RCC_TIMER1; switch (id) { case TIMER_UPDATE_INTERRUPT: nvic_irq_enable(is_timer1 ? NVIC_TIMER1_UP : NVIC_TIMER8_UP); break; case TIMER_CC1_INTERRUPT: case TIMER_CC2_INTERRUPT: case TIMER_CC3_INTERRUPT: case TIMER_CC4_INTERRUPT: nvic_irq_enable(is_timer1 ? NVIC_TIMER1_CC : NVIC_TIMER8_CC); break; case TIMER_COM_INTERRUPT: case TIMER_TRG_INTERRUPT: nvic_irq_enable(is_timer1 ? NVIC_TIMER1_TRG_COM : NVIC_TIMER8_TRG_COM); break; case TIMER_BREAK_INTERRUPT: nvic_irq_enable(is_timer1 ? NVIC_TIMER1_BRK : NVIC_TIMER8_BRK); break; } } static void enable_nonmuxed_irq(timer_dev *dev) { switch (dev->clk_id) { case RCC_TIMER2: nvic_irq_enable(NVIC_TIMER2); break; case RCC_TIMER3: nvic_irq_enable(NVIC_TIMER3); break; case RCC_TIMER4: nvic_irq_enable(NVIC_TIMER4); break; #ifdef STM32_HIGH_DENSITY case RCC_TIMER5: nvic_irq_enable(NVIC_TIMER5); break; case RCC_TIMER6: nvic_irq_enable(NVIC_TIMER6); break; case RCC_TIMER7: nvic_irq_enable(NVIC_TIMER7); break; #endif default: ASSERT_FAULT(0); break; } }