diff options
Diffstat (limited to 'libmaple/exti.c')
| -rw-r--r-- | libmaple/exti.c | 292 | 
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); +    }  }  | 
