aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple/exti.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmaple/exti.c')
-rw-r--r--libmaple/exti.c292
1 files changed, 168 insertions, 124 deletions
diff --git a/libmaple/exti.c b/libmaple/exti.c
index 150dd05..38d3bba 100644
--- a/libmaple/exti.c
+++ b/libmaple/exti.c
@@ -23,184 +23,228 @@
*****************************************************************************/
/**
+ * @file exti.c
* @brief External interrupt control routines
*/
#include "libmaple.h"
#include "exti.h"
#include "nvic.h"
+#include "bitband.h"
-typedef struct ExtIChannel {
+/*
+ * Internal state
+ */
+
+/* Status bitmaps, for external interrupts with multiplexed IRQs */
+static uint16 exti_9_5_en = 0;
+static uint16 exti_15_10_en = 0;
+
+typedef struct exti_channel {
void (*handler)(void);
uint32 irq_line;
-} ExtIChannel;
-
-static ExtIChannel exti_channels[] = {
- { .handler = NULL, .irq_line = NVIC_EXTI0 }, // EXTI0
- { .handler = NULL, .irq_line = NVIC_EXTI1 }, // EXTI1
- { .handler = NULL, .irq_line = NVIC_EXTI2 }, // EXTI2
- { .handler = NULL, .irq_line = NVIC_EXTI3 }, // EXTI3
- { .handler = NULL, .irq_line = NVIC_EXTI4 }, // EXTI4
- { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI5
- { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI6
- { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI7
- { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI8
- { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI9
- { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI10
- { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI11
- { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI12
- { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI13
- { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI14
- { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI15
+} exti_channel;
+
+static exti_channel exti_channels[] = {
+ { .handler = NULL, .irq_line = NVIC_EXTI0 }, // EXTI0
+ { .handler = NULL, .irq_line = NVIC_EXTI1 }, // EXTI1
+ { .handler = NULL, .irq_line = NVIC_EXTI2 }, // EXTI2
+ { .handler = NULL, .irq_line = NVIC_EXTI3 }, // EXTI3
+ { .handler = NULL, .irq_line = NVIC_EXTI4 }, // EXTI4
+ { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI5
+ { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI6
+ { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI7
+ { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI8
+ { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI9
+ { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI10
+ { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI11
+ { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI12
+ { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI13
+ { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI14
+ { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI15
};
-static inline void clear_pending(int bit) {
- __set_bits(EXTI_PR, BIT(bit));
- /* If the pending bit is cleared as the last instruction in an ISR,
- * it won't actually be cleared in time and the ISR will fire again.
- * Insert a 2-cycle buffer to allow it to take effect. */
- asm volatile("nop");
- asm volatile("nop");
-}
+/*
+ * Convenience routines
+ */
+
+static inline void enable_irq(afio_exti_num exti_num);
+static inline void maybe_disable_irq(afio_exti_num exti_num);
+
+/**
+ * @brief Register a handler to run upon external interrupt.
+ *
+ * This function assumes that the interrupt request corresponding to
+ * the given external interrupt is masked.
+ *
+ * @param num External interrupt line number.
+ * @param port Port to use as source input for external interrupt.
+ * @param handler Function handler to execute when interrupt is triggered.
+ * @param mode Type of transition to trigger on, one of:
+ * EXTI_RISING, EXTI_FALLING, EXTI_RISING_FALLING.
+ * @see exti_num
+ * @see exti_port
+ * @see exti_trigger_mode
+ */
+void exti_attach_interrupt(afio_exti_num num,
+ afio_exti_port port,
+ voidFuncPtr handler,
+ exti_trigger_mode mode) {
+ ASSERT(handler);
-static inline void dispatch_handler(uint32 channel) {
- ASSERT(exti_channels[channel].handler);
- if (exti_channels[channel].handler) {
- (exti_channels[channel].handler)();
+ /* Register the handler */
+ exti_channels[num].handler = handler;
+
+ /* Set trigger mode */
+ switch (mode) {
+ case EXTI_RISING:
+ *bb_perip(&EXTI_BASE->RTSR, num) = 1;
+ break;
+ case EXTI_FALLING:
+ *bb_perip(&EXTI_BASE->FTSR, num) = 1;
+ break;
+ case EXTI_RISING_FALLING:
+ *bb_perip(&EXTI_BASE->RTSR, num) = 1;
+ *bb_perip(&EXTI_BASE->FTSR, num) = 1;
+ break;
}
+
+ /* Map num to port */
+ afio_exti_select(num, port);
+
+ /* Unmask external interrupt request */
+ *bb_perip(&EXTI_BASE->IMR, num) = 1;
+
+ /* Enable the interrupt line */
+ enable_irq(num);
+}
+
+/**
+ * @brief Unregister an external interrupt handler
+ * @param num Number of the external interrupt line to disable.
+ * @see exti_num
+ */
+void exti_detach_interrupt(afio_exti_num num) {
+ /* First, mask the interrupt request */
+ *bb_perip(&EXTI_BASE->IMR, num) = 0;
+
+ /* Then, clear the trigger selection registers */
+ *bb_perip(&EXTI_BASE->FTSR, num) = 0;
+ *bb_perip(&EXTI_BASE->RTSR, num) = 0;
+
+ /* Next, disable the IRQ, unless it's multiplexed and there are
+ * other active external interrupts on the same IRQ line */
+ maybe_disable_irq(num);
+
+ /* Finally, unregister the user's handler */
+ exti_channels[num].handler = NULL;
}
-/* For EXTI0 through EXTI4, only one handler
- * is associated with each channel, so we
- * don't have to keep track of which channel
+/*
+ * Interrupt handlers
+ */
+
+static inline void clear_pending(uint32 exti_num);
+static inline void dispatch_handler(uint32 exti_num);
+
+/* For AFIO_EXTI_0 through AFIO_EXTI_4, only one handler is associated
+ * with each channel, so we don't have to keep track of which channel
* we came from */
-void EXTI0_IRQHandler(void) {
- dispatch_handler(EXTI0);
- clear_pending(EXTI0);
+void __irq_exti0(void) {
+ dispatch_handler(AFIO_EXTI_0);
+ clear_pending(AFIO_EXTI_0);
}
-void EXTI1_IRQHandler(void) {
- dispatch_handler(EXTI1);
- clear_pending(EXTI1);
+void __irq_exti1(void) {
+ dispatch_handler(AFIO_EXTI_1);
+ clear_pending(AFIO_EXTI_1);
}
-void EXTI2_IRQHandler(void) {
- dispatch_handler(EXTI2);
- clear_pending(EXTI2);
+void __irq_exti2(void) {
+ dispatch_handler(AFIO_EXTI_2);
+ clear_pending(AFIO_EXTI_2);
}
-void EXTI3_IRQHandler(void) {
- dispatch_handler(EXTI3);
- clear_pending(EXTI3);
+void __irq_exti3(void) {
+ dispatch_handler(AFIO_EXTI_3);
+ clear_pending(AFIO_EXTI_3);
}
-void EXTI4_IRQHandler(void) {
- dispatch_handler(EXTI4);
- clear_pending(EXTI4);
+void __irq_exti4(void) {
+ dispatch_handler(AFIO_EXTI_4);
+ clear_pending(AFIO_EXTI_4);
}
-void EXTI9_5_IRQHandler(void) {
+void __irq_exti9_5(void) {
/* Figure out which channel it came from */
- uint32 pending;
+ uint32 pending = GET_BITS(EXTI_BASE->PR, 5, 9);
uint32 i;
- pending = REG_GET(EXTI_PR);
- pending = GET_BITS(pending, 5, 9);
/* Dispatch every handler if the pending bit is set */
for (i = 0; i < 5; i++) {
if (pending & 0x1) {
- dispatch_handler(EXTI5 + i);
- clear_pending(EXTI5 + i);
+ dispatch_handler(AFIO_EXTI_5 + i);
+ clear_pending(AFIO_EXTI_5 + i);
}
pending >>= 1;
}
}
-void EXTI15_10_IRQHandler(void) {
+void __irq_exti15_10(void) {
/* Figure out which channel it came from */
- uint32 pending;
+ uint32 pending = GET_BITS(EXTI_BASE->PR, 10, 15);
uint32 i;
- pending = REG_GET(EXTI_PR);
- pending = GET_BITS(pending, 10, 15);
/* Dispatch every handler if the pending bit is set */
for (i = 0; i < 6; i++) {
if (pending & 0x1) {
- dispatch_handler(EXTI10 + i);
- clear_pending(EXTI10 + i);
+ dispatch_handler(AFIO_EXTI_10 + i);
+ clear_pending(AFIO_EXTI_10 + i);
}
pending >>= 1;
}
}
-
-/**
- * @brief Register a handler to run upon external interrupt
- * @param port source port of pin (eg EXTI_CONFIG_PORTA)
- * @param pin pin number on the source port
- * @param handler function handler to execute
- * @param mode type of transition to trigger on
+/*
+ * Auxiliary functions
*/
-void exti_attach_interrupt(uint32 port,
- uint32 pin,
- voidFuncPtr handler,
- uint32 mode) {
- static uint32 afio_regs[] = {
- AFIO_EXTICR1, // EXT0-3
- AFIO_EXTICR2, // EXT4-7
- AFIO_EXTICR3, // EXT8-11
- AFIO_EXTICR4, // EXT12-15
- };
-
- /* Note: All of the following code assumes that EXTI0 = 0 */
- ASSERT(EXTI0 == 0);
- ASSERT(handler);
-
- uint32 channel = pin;
- /* map port to channel */
- __write(afio_regs[pin/4], (port << ((pin % 4) * 4)));
-
- /* Unmask appropriate interrupt line */
- __set_bits(EXTI_IMR, BIT(channel));
-
- /* Set trigger mode */
- switch (mode) {
- case EXTI_RISING:
- __set_bits(EXTI_RTSR, BIT(channel));
- break;
-
- case EXTI_FALLING:
- __set_bits(EXTI_FTSR, BIT(channel));
- break;
+static inline void clear_pending(uint32 exti_num) {
+ *bb_perip(&EXTI_BASE->PR, exti_num) = 1;
+ /* If the pending bit is cleared as the last instruction in an ISR,
+ * it won't actually be cleared in time and the ISR will fire again.
+ * Insert a 2-cycle buffer to allow it to take effect. */
+ asm volatile("nop");
+ asm volatile("nop");
+}
- case EXTI_RISING_FALLING:
- __set_bits(EXTI_RTSR, BIT(channel));
- __set_bits(EXTI_FTSR, BIT(channel));
- break;
+static inline void dispatch_handler(uint32 exti_num) {
+ ASSERT(exti_channels[exti_num].handler);
+ if (exti_channels[exti_num].handler) {
+ (exti_channels[exti_num].handler)();
}
-
- /* Register the handler */
- exti_channels[channel].handler = handler;
-
- /* Configure the enable interrupt bits for the NVIC */
- nvic_irq_enable(exti_channels[channel].irq_line);
}
+static inline void enable_irq(afio_exti_num exti) {
+ /* Maybe twiddle the IRQ bitmap for extis with multiplexed IRQs */
+ if (exti > 4) {
+ uint16 *bitmap = exti < 10 ? &exti_9_5_en : &exti_15_10_en;
+ *bb_sramp(bitmap, exti) = 1;
+ }
-/**
- * @brief Unregister an external interrupt handler
- * @param channel channel to disable (eg EXTI0)
- */
-void exti_detach_interrupt(uint32 channel) {
- ASSERT(channel < NR_EXTI_CHANNELS);
- ASSERT(EXTI0 == 0);
-
- __clear_bits(EXTI_IMR, BIT(channel));
- __clear_bits(EXTI_FTSR, BIT(channel));
- __clear_bits(EXTI_RTSR, BIT(channel));
-
- nvic_irq_disable(exti_channels[channel].irq_line);
+ nvic_irq_enable(exti_channels[exti].irq_line);
+}
- exti_channels[channel].handler = NULL;
+static inline void maybe_disable_irq(afio_exti_num exti) {
+ if (exti > 4) {
+ uint16 *bitmap = exti < 10 ? &exti_9_5_en : &exti_15_10_en;
+ *bb_sramp(bitmap, exti) = 0;
+ if (*bitmap == 0) {
+ /* All of the external interrupts which share this IRQ
+ * line are disabled. */
+ nvic_irq_disable(exti_channels[exti].irq_line);
+ }
+ } else {
+ nvic_irq_disable(exti_channels[exti].irq_line);
+ }
}