aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple/timer.c
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@leaflabs.com>2012-06-26 18:24:49 -0400
committerMarti Bolivar <mbolivar@leaflabs.com>2012-06-26 18:32:57 -0400
commitf005bd3a5c087e3d5559f2858a1e7898a4f92a8d (patch)
tree0701628a68056f7b5f92d5a5af5f281f58e6a71e /libmaple/timer.c
parent761e059962e8f53f3cceef61d65bf2bf3025319a (diff)
parentc6073e4886da4606679bc3e9d770c9cff9390597 (diff)
downloadlibrambutan-f005bd3a5c087e3d5559f2858a1e7898a4f92a8d.tar.gz
librambutan-f005bd3a5c087e3d5559f2858a1e7898a4f92a8d.zip
Merge branch 'wip-family-support'
Merge the long-lived (too long; future changes like these will need to proceed more incrementally) development branch of libmaple, containing experimental STM32F2 and STM32F1 value line support, into master. This required many changes to the structure of the library. The most important structural reorganizations occurred in: - 954f9e5: moves public headers to include directories - 3efa313: uses "series" instead of "family" - c0d60e3: adds board files to the build system, to make it easier to add new boards - 096d86c: adds build logic for targeting different STM32 series (e.g. STM32F1, STM32F2) This last commit in particular (096d86c) is the basis for the repartitioning of libmaple into portable sections, which work on all supported MCUs, and nonportable sections, which are segregated into separate directories and contain all series-specific code. Moving existing STM32F1-only code into libmaple/stm32f1 and wirish/stm32f1, along with adding equivalents under .../stm32f2 directories, was the principal project of this branch. Important API changes occur in several places. Existing code is still expected to work on STM32F1 targets, but there have been many deprecations. A detailed changelog explaining the situation needs to be prepared. F2 and F1 value line support is not complete; the merge is proceeding prematurely in this respect. We've been getting more libmaple patches from the community lately, and I'm worried that the merge conflicts with the old tree structure will become painful to manage. Conflicts: Makefile Resolved Makefile conflicts manually; this required propagating -Xlinker usage into support/make/target-config.mk. Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
Diffstat (limited to 'libmaple/timer.c')
-rw-r--r--libmaple/timer.c476
1 files changed, 204 insertions, 272 deletions
diff --git a/libmaple/timer.c b/libmaple/timer.c
index 83e9ace..24ae7fa 100644
--- a/libmaple/timer.c
+++ b/libmaple/timer.c
@@ -25,108 +25,150 @@
*****************************************************************************/
/**
- * @file timer.c
+ * @file libmaple/timer.c
* @author Marti Bolivar <mbolivar@leaflabs.com>
- * @brief New-style timer interface
+ * @brief Portable timer routines.
*/
-#include "timer.h"
+#include <libmaple/timer.h>
+#include <libmaple/stm32.h>
+#include "timer_private.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; <junk>; trigger. */
-#define NR_GEN_HANDLERS 7
-/* Update only. */
-#define NR_BAS_HANDLERS 1
+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);
-static timer_dev timer1 = {
- .regs = { .adv = TIMER1_BASE },
- .clk_id = RCC_TIMER1,
- .type = TIMER_ADVANCED,
- .handlers = { [NR_ADV_HANDLERS - 1] = 0 },
-};
+/*
+ * Devices
+ *
+ * Defer to the timer_private API for declaring these.
+ */
+
+#if STM32_HAVE_TIMER(1)
+static timer_dev timer1 = ADVANCED_TIMER(1);
/** 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 },
-};
+#endif
+#if STM32_HAVE_TIMER(2)
+static timer_dev timer2 = GENERAL_TIMER(2);
/** 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 },
-};
+#endif
+#if STM32_HAVE_TIMER(3)
+static timer_dev timer3 = GENERAL_TIMER(3);
/** 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 },
-};
+#endif
+#if STM32_HAVE_TIMER(4)
+static timer_dev timer4 = GENERAL_TIMER(4);
/** 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 },
-};
+#endif
+#if STM32_HAVE_TIMER(5)
+static timer_dev timer5 = GENERAL_TIMER(5);
/** 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 },
-};
+#endif
+#if STM32_HAVE_TIMER(6)
+static timer_dev timer6 = BASIC_TIMER(6);
/** 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 },
-};
+#endif
+#if STM32_HAVE_TIMER(7)
+static timer_dev timer7 = BASIC_TIMER(7);
/** 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 },
-};
+#endif
+#if STM32_HAVE_TIMER(8)
+static timer_dev timer8 = ADVANCED_TIMER(8);
/** Timer 8 device (advanced) */
timer_dev *TIMER8 = &timer8;
#endif
+#if STM32_HAVE_TIMER(9)
+static timer_dev timer9 = RESTRICTED_GENERAL_TIMER(9, TIMER_DIER_TIE_BIT);
+/** Timer 9 device (general-purpose) */
+timer_dev *TIMER9 = &timer9;
+#endif
+#if STM32_HAVE_TIMER(10)
+static timer_dev timer10 = RESTRICTED_GENERAL_TIMER(10, TIMER_DIER_CC1IE_BIT);
+/** Timer 10 device (general-purpose) */
+timer_dev *TIMER10 = &timer10;
+#endif
+#if STM32_HAVE_TIMER(11)
+static timer_dev timer11 = RESTRICTED_GENERAL_TIMER(11, TIMER_DIER_CC1IE_BIT);
+/** Timer 11 device (general-purpose) */
+timer_dev *TIMER11 = &timer11;
+#endif
+#if STM32_HAVE_TIMER(12)
+static timer_dev timer12 = RESTRICTED_GENERAL_TIMER(12, TIMER_DIER_TIE_BIT);
+/** Timer 12 device (general-purpose) */
+timer_dev *TIMER12 = &timer12;
+#endif
+#if STM32_HAVE_TIMER(13)
+static timer_dev timer13 = RESTRICTED_GENERAL_TIMER(13, TIMER_DIER_CC1IE_BIT);
+/** Timer 13 device (general-purpose) */
+timer_dev *TIMER13 = &timer13;
+#endif
+#if STM32_HAVE_TIMER(14)
+static timer_dev timer14 = RESTRICTED_GENERAL_TIMER(14, TIMER_DIER_CC1IE_BIT);
+/** Timer 14 device (general-purpose) */
+timer_dev *TIMER14 = &timer14;
+#endif
/*
- * Convenience routines
+ * 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);
+/**
+ * @brief Call a function on timer devices.
+ * @param fn Function to call on each timer device.
+ */
+void timer_foreach(void (*fn)(timer_dev*)) {
+#if STM32_HAVE_TIMER(1)
+ fn(TIMER1);
+#endif
+#if STM32_HAVE_TIMER(2)
+ fn(TIMER2);
+#endif
+#if STM32_HAVE_TIMER(3)
+ fn(TIMER3);
+#endif
+#if STM32_HAVE_TIMER(4)
+ fn(TIMER4);
+#endif
+#if STM32_HAVE_TIMER(5)
+ fn(TIMER5);
+#endif
+#if STM32_HAVE_TIMER(6)
+ fn(TIMER6);
+#endif
+#if STM32_HAVE_TIMER(7)
+ fn(TIMER7);
+#endif
+#if STM32_HAVE_TIMER(8)
+ fn(TIMER8);
+#endif
+#if STM32_HAVE_TIMER(9)
+ fn(TIMER9);
+#endif
+#if STM32_HAVE_TIMER(10)
+ fn(TIMER10);
+#endif
+#if STM32_HAVE_TIMER(11)
+ fn(TIMER11);
+#endif
+#if STM32_HAVE_TIMER(12)
+ fn(TIMER12);
+#endif
+#if STM32_HAVE_TIMER(13)
+ fn(TIMER13);
+#endif
+#if STM32_HAVE_TIMER(14)
+ fn(TIMER14);
+#endif
+}
/**
* Initialize a timer, and reset its register map.
@@ -191,20 +233,31 @@ void timer_set_mode(timer_dev *dev, uint8 channel, timer_mode mode) {
}
/**
- * @brief Call a function on timer devices.
- * @param fn Function to call on each timer device.
+ * @brief Determine whether a timer has a particular capture/compare channel.
+ *
+ * Different timers have different numbers of capture/compare channels
+ * (and some have none at all). Use this function to test whether a
+ * given timer/channel combination will work.
+ *
+ * @param dev Timer device
+ * @param channel Capture/compare channel, from 1 to 4
+ * @return Nonzero if dev has channel, zero otherwise.
*/
-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
+int timer_has_cc_channel(timer_dev *dev, uint8 channel) {
+ /* On all currently supported series: advanced and "full-featured"
+ * general purpose timers have all four channels. Of the
+ * restricted general timers, timers 9 and 12 have channels 1 and
+ * 2; the others have channel 1 only. Basic timers have none. */
+ rcc_clk_id id = dev->clk_id;
+ ASSERT((1 <= channel) && (channel <= 4));
+ if (id <= RCC_TIMER5 || id == RCC_TIMER8) {
+ return 1; /* 1 and 8 are advanced, 2-5 are "full" general */
+ } else if (id <= RCC_TIMER7) {
+ return 0; /* 6 and 7 are basic */
+ }
+ /* The rest are restricted general. */
+ return (((id == RCC_TIMER9 || id == RCC_TIMER12) && channel <= 2) ||
+ channel == 1);
}
/**
@@ -240,164 +293,6 @@ void timer_detach_interrupt(timer_dev *dev, uint8 interrupt) {
}
/*
- * 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
-
-/* Note: the following dispatch routines make use of the fact that
- * DIER interrupt enable bits and SR interrupt flags have common bit
- * positions. Thus, ANDing DIER and SR lets us check if an interrupt
- * is enabled and if it has occurred simultaneously.
- */
-
-/* A special-case dispatch routine for single-interrupt NVIC lines.
- * This function assumes that the interrupt corresponding to `iid' has
- * in fact occurred (i.e., it doesn't check DIER & SR). */
-static inline void dispatch_single_irq(timer_dev *dev,
- timer_interrupt_id iid,
- uint32 irq_mask) {
- timer_bas_reg_map *regs = (dev->regs).bas;
- void (*handler)(void) = dev->handlers[iid];
- if (handler) {
- handler();
- regs->SR &= ~irq_mask;
- }
-}
-
-/* For dispatch routines which service multiple interrupts. */
-#define handle_irq(dier_sr, irq_mask, handlers, iid, handled_irq) do { \
- if ((dier_sr) & (irq_mask)) { \
- void (*__handler)(void) = (handlers)[iid]; \
- if (__handler) { \
- __handler(); \
- handled_irq |= (irq_mask); \
- } \
- } \
- } while (0)
-
-static inline void dispatch_adv_brk(timer_dev *dev) {
- dispatch_single_irq(dev, TIMER_BREAK_INTERRUPT, TIMER_SR_BIF);
-}
-
-static inline void dispatch_adv_up(timer_dev *dev) {
- dispatch_single_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF);
-}
-
-static inline void dispatch_adv_trg_com(timer_dev *dev) {
- timer_adv_reg_map *regs = (dev->regs).adv;
- uint32 dsr = regs->DIER & regs->SR;
- void (**hs)(void) = dev->handlers;
- uint32 handled = 0; /* Logical OR of SR interrupt flags we end up
- * handling. We clear these. User handlers
- * must clear overcapture flags, to avoid
- * wasting time in output mode. */
-
- handle_irq(dsr, TIMER_SR_TIF, hs, TIMER_TRG_INTERRUPT, handled);
- handle_irq(dsr, TIMER_SR_COMIF, hs, TIMER_COM_INTERRUPT, handled);
-
- regs->SR &= ~handled;
-}
-
-static inline void dispatch_adv_cc(timer_dev *dev) {
- timer_adv_reg_map *regs = (dev->regs).adv;
- uint32 dsr = regs->DIER & regs->SR;
- void (**hs)(void) = dev->handlers;
- uint32 handled = 0;
-
- handle_irq(dsr, TIMER_SR_CC4IF, hs, TIMER_CC4_INTERRUPT, handled);
- handle_irq(dsr, TIMER_SR_CC3IF, hs, TIMER_CC3_INTERRUPT, handled);
- handle_irq(dsr, TIMER_SR_CC2IF, hs, TIMER_CC2_INTERRUPT, handled);
- handle_irq(dsr, TIMER_SR_CC1IF, hs, TIMER_CC1_INTERRUPT, handled);
-
- regs->SR &= ~handled;
-}
-
-static inline void dispatch_general(timer_dev *dev) {
- timer_gen_reg_map *regs = (dev->regs).gen;
- uint32 dsr = regs->DIER & regs->SR;
- void (**hs)(void) = dev->handlers;
- uint32 handled = 0;
-
- handle_irq(dsr, TIMER_SR_TIF, hs, TIMER_TRG_INTERRUPT, handled);
- handle_irq(dsr, TIMER_SR_CC4IF, hs, TIMER_CC4_INTERRUPT, handled);
- handle_irq(dsr, TIMER_SR_CC3IF, hs, TIMER_CC3_INTERRUPT, handled);
- handle_irq(dsr, TIMER_SR_CC2IF, hs, TIMER_CC2_INTERRUPT, handled);
- handle_irq(dsr, TIMER_SR_CC1IF, hs, TIMER_CC1_INTERRUPT, handled);
- handle_irq(dsr, TIMER_SR_UIF, hs, TIMER_UPDATE_INTERRUPT, handled);
-
- regs->SR &= ~handled;
-}
-
-static inline void dispatch_basic(timer_dev *dev) {
- dispatch_single_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF);
-}
-
-/*
* Utilities
*/
@@ -417,64 +312,101 @@ static void output_compare_mode(timer_dev *dev, uint8 channel) {
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 void enable_adv_irq(timer_dev *dev, timer_interrupt_id id);
+static void enable_bas_gen_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);
+ enable_adv_irq(dev, iid);
} else {
- enable_nonmuxed_irq(dev);
+ enable_bas_gen_irq(dev);
}
}
-static void enable_advanced_irq(timer_dev *dev, timer_interrupt_id id) {
- uint8 is_timer1 = dev->clk_id == RCC_TIMER1;
-
+/* Advanced control timers have several IRQ lines corresponding to
+ * different timer interrupts.
+ *
+ * Note: This function assumes that the only advanced timers are TIM1
+ * and TIM8, and needs the obvious changes if that assumption is
+ * violated by a later STM32 series. */
+static void enable_adv_irq(timer_dev *dev, timer_interrupt_id id) {
+ uint8 is_tim1 = dev->clk_id == RCC_TIMER1;
+ nvic_irq_num irq_num;
switch (id) {
case TIMER_UPDATE_INTERRUPT:
- nvic_irq_enable(is_timer1 ? NVIC_TIMER1_UP : NVIC_TIMER8_UP);
+ irq_num = (is_tim1 ?
+ NVIC_TIMER1_UP_TIMER10 :
+ NVIC_TIMER8_UP_TIMER13);
break;
- case TIMER_CC1_INTERRUPT:
- case TIMER_CC2_INTERRUPT:
- case TIMER_CC3_INTERRUPT:
+ case TIMER_CC1_INTERRUPT: /* Fall through */
+ case TIMER_CC2_INTERRUPT: /* ... */
+ case TIMER_CC3_INTERRUPT: /* ... */
case TIMER_CC4_INTERRUPT:
- nvic_irq_enable(is_timer1 ? NVIC_TIMER1_CC : NVIC_TIMER8_CC);
+ irq_num = is_tim1 ? NVIC_TIMER1_CC : NVIC_TIMER8_CC;
break;
- case TIMER_COM_INTERRUPT:
+ case TIMER_COM_INTERRUPT: /* Fall through */
case TIMER_TRG_INTERRUPT:
- nvic_irq_enable(is_timer1 ? NVIC_TIMER1_TRG_COM : NVIC_TIMER8_TRG_COM);
+ irq_num = (is_tim1 ?
+ NVIC_TIMER1_TRG_COM_TIMER11 :
+ NVIC_TIMER8_TRG_COM_TIMER14);
break;
case TIMER_BREAK_INTERRUPT:
- nvic_irq_enable(is_timer1 ? NVIC_TIMER1_BRK : NVIC_TIMER8_BRK);
+ irq_num = (is_tim1 ?
+ NVIC_TIMER1_BRK_TIMER9 :
+ NVIC_TIMER8_BRK_TIMER12);
break;
+ default:
+ /* Can't happen, but placate the compiler */
+ ASSERT(0);
+ return;
}
+ nvic_irq_enable(irq_num);
}
-static void enable_nonmuxed_irq(timer_dev *dev) {
+/* Basic and general purpose timers have a single IRQ line, which is
+ * shared by all interrupts supported by a particular timer. */
+static void enable_bas_gen_irq(timer_dev *dev) {
+ nvic_irq_num irq_num;
switch (dev->clk_id) {
case RCC_TIMER2:
- nvic_irq_enable(NVIC_TIMER2);
+ irq_num = NVIC_TIMER2;
break;
case RCC_TIMER3:
- nvic_irq_enable(NVIC_TIMER3);
+ irq_num = NVIC_TIMER3;
break;
case RCC_TIMER4:
- nvic_irq_enable(NVIC_TIMER4);
+ irq_num = NVIC_TIMER4;
break;
-#ifdef STM32_HIGH_DENSITY
case RCC_TIMER5:
- nvic_irq_enable(NVIC_TIMER5);
+ irq_num = NVIC_TIMER5;
break;
case RCC_TIMER6:
- nvic_irq_enable(NVIC_TIMER6);
+ irq_num = NVIC_TIMER6;
break;
case RCC_TIMER7:
- nvic_irq_enable(NVIC_TIMER7);
+ irq_num = NVIC_TIMER7;
+ break;
+ case RCC_TIMER9:
+ irq_num = NVIC_TIMER1_BRK_TIMER9;
+ break;
+ case RCC_TIMER10:
+ irq_num = NVIC_TIMER1_UP_TIMER10;
+ break;
+ case RCC_TIMER11:
+ irq_num = NVIC_TIMER1_TRG_COM_TIMER11;
+ break;
+ case RCC_TIMER12:
+ irq_num = NVIC_TIMER8_BRK_TIMER12;
+ break;
+ case RCC_TIMER13:
+ irq_num = NVIC_TIMER8_UP_TIMER13;
+ break;
+ case RCC_TIMER14:
+ irq_num = NVIC_TIMER8_TRG_COM_TIMER14;
break;
-#endif
default:
ASSERT_FAULT(0);
- break;
+ return;
}
+ nvic_irq_enable(irq_num);
}