From c7deded9b24e72c7ed7aa434f3eef7163becc6c5 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sat, 13 May 2023 14:17:32 +0200 Subject: [PATCH] Implement exti's control functions --- drivers/exti.c | 267 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/exti.h | 108 ++++++++++++++++++++ 2 files changed, 375 insertions(+) create mode 100644 drivers/exti.c create mode 100644 drivers/exti.h diff --git a/drivers/exti.c b/drivers/exti.c new file mode 100644 index 0000000..3556864 --- /dev/null +++ b/drivers/exti.c @@ -0,0 +1,267 @@ +/** @file exti.c + * Module handling EXTernal Interrupt lines + * + * The module provides functions to configure the exti lines to generate events + * or interrupts + */ + +//--includes-------------------------------------------------------------------- + +#include "exti.h" +#include "exti_regs.h" + +#include "nvic.h" + + +//--local definitions----------------------------------------------------------- + +//--local variables------------------------------------------------------------- + +static volatile struct EXTI* regs = (struct EXTI*)EXTI_BASE_ADDRESS; +static ExtiCallback callbacks[19]; + + +//--public functions------------------------------------------------------------ + +void exti_configure(enum ExtiLine line_mask, enum GpioPort port, + enum ExtiConfig config_mask, ExtiCallback callback) +{ + exti_reset(line_mask); + +#warning "configure ports mapping" + + //configure edge detections + if (config_mask & EXTI_CONFIG_RISING_EDGE) { + regs->RTSR.word |= line_mask; + } else { + regs->RTSR.word &= ~line_mask; + } + + if (config_mask & EXTI_CONFIG_FALLING_EDGE) { + regs->FTSR.word |= line_mask; + } else { + regs->FTSR.word &= ~line_mask; + } + + //reconfigure events/irqs + regs->EMR.word |= line_mask; + if (callback) { + //register callbacks for each line selected + for (uint8_t i = 0; i < 16; ++i) { + if (line_mask & (0x1 << i)) { + callbacks[i] = callback; + } + } + + //enable interrupts lines in nvic + for (uint8_t i = 0; i < 5; ++i) { + if (line_mask & (0x1 << i)) { + nvic_enable(NVIC_IRQ_EXTI0 + i); + } + } + if (line_mask & 0x3e0) { + nvic_enable(NVIC_IRQ_EXTI9_5); + } + if (line_mask & 0x7c00) { + nvic_enable(NVIC_IRQ_EXTI15_10); + } + + //enable irqs in last to avoid triggers during config + regs->IMR.word |= line_mask; + } +} + +void exti_reset(enum ExtiLine line_mask) +{ + //disable events/irqs + regs->IMR.word &= ~line_mask; + regs->EMR.word &= ~line_mask; + + //clear any pending bits + regs->PR.word |= line_mask; + + //disable interrupts lines in nvic + for (uint8_t i = 0; i < 5; ++i) { + if (line_mask & (0x1 << i)) { + nvic_enable(NVIC_IRQ_EXTI0 + i); + } + } + if (line_mask & 0x3e0) { + nvic_enable(NVIC_IRQ_EXTI9_5); + } + if (line_mask & 0x7c00) { + nvic_enable(NVIC_IRQ_EXTI15_10); + } +} + +void exti_configure_specific(enum ExtiLineSpecific line_mask, + enum ExtiConfig config_mask, ExtiCallback callback) +{ + //reset is identical to normal lines + exti_reset_specific(line_mask); + + //configure edge detections + if (config_mask & EXTI_CONFIG_RISING_EDGE) { + regs->RTSR.word |= line_mask; + } else { + regs->RTSR.word &= ~line_mask; + } + + if (config_mask & EXTI_CONFIG_FALLING_EDGE) { + regs->FTSR.word |= line_mask; + } else { + regs->FTSR.word &= ~line_mask; + } + + //reconfigure events/irqs + regs->EMR.word |= line_mask; + if (callback) { + //register callbacks for each line selected + for (uint8_t i = 0; i < 16; ++i) { + if (line_mask & (0x1 << i)) { + callbacks[i] = callback; + } + } + + //enable interrupts lines in nvic + if (line_mask & EXTI_LINE_PVD) { + nvic_enable(NVIC_IRQ_PVD); + } + if (line_mask & EXTI_LINE_RTC) { + nvic_enable(NVIC_IRQ_RTC_ALARM); + } + if (line_mask & EXTI_LINE_USB) { + nvic_enable(NVIC_IRQ_USB_WAKEUP); + } + + //enable irqs in last to avoid triggers during config + regs->IMR.word |= line_mask; + } +} + +void exti_reset_specific(enum ExtiLineSpecific line_mask) +{ + //disable events/irqs + regs->IMR.word &= ~line_mask; + regs->EMR.word &= ~line_mask; + + //clear any pending bits + regs->PR.word |= line_mask; + + //disable interrupts lines in nvic + if (line_mask & EXTI_LINE_PVD) { + nvic_disable(NVIC_IRQ_PVD); + } + if (line_mask & EXTI_LINE_RTC) { + nvic_disable(NVIC_IRQ_RTC_ALARM); + } + if (line_mask & EXTI_LINE_USB) { + nvic_disable(NVIC_IRQ_USB_WAKEUP); + } +} + +void exti_reset_peripheral(void) +{ + //reset peripheral config + regs->IMR.word = 0; + regs->EMR.word = 0; + regs->RTSR.word = 0; + regs->FTSR.word = 0; + regs->SWIER.word = 0; + regs->PR.word = 0; + + //disable interrupts in nvic + for (uint8_t i = 0; i < 5; ++i) { + nvic_enable(NVIC_IRQ_EXTI0 + i); + } + nvic_enable(NVIC_IRQ_EXTI9_5); + nvic_enable(NVIC_IRQ_EXTI15_10); + nvic_disable(NVIC_IRQ_PVD); + nvic_disable(NVIC_IRQ_RTC_ALARM); + nvic_disable(NVIC_IRQ_USB_WAKEUP); + +#warning "reset afio lines" +} + +//--local functions------------------------------------------------------------- + +//--ISRs ----------------------------------------------------------------------- + +void hdr_exti0(void) +{ + nvic_clear_pending(NVIC_IRQ_EXTI0); + regs->PR.PR0 = 1; + callbacks[0](); +} + +void hdr_exti1(void) +{ + nvic_clear_pending(NVIC_IRQ_EXTI1); + regs->PR.PR1 = 1; + callbacks[1](); +} + +void hdr_exti2(void) +{ + nvic_clear_pending(NVIC_IRQ_EXTI2); + regs->PR.PR2 = 1; + callbacks[2](); +} + +void hdr_exti3(void) +{ + nvic_clear_pending(NVIC_IRQ_EXTI3); + regs->PR.PR3 = 1; + callbacks[3](); +} + +void hdr_exti4(void) +{ + nvic_clear_pending(NVIC_IRQ_EXTI4); + regs->PR.PR4 = 1; + callbacks[4](); +} + +void hdr_exti9_5(void) +{ + nvic_clear_pending(NVIC_IRQ_EXTI9_5); + for (uint8_t i = 5; i < 10; ++i) { + if (regs->PR.word & (0x1 << i)) { + regs->PR.word |= 0x1 << i; + callbacks[i](); + } + } +} + +void hdr_exti15_10(void) +{ + nvic_clear_pending(NVIC_IRQ_EXTI15_10); + for (uint8_t i = 10; i < 16; ++i) { + if (regs->PR.word & (0x1 << i)) { + regs->PR.word |= 0x1 << i; + callbacks[i](); + } + } +} + +void hdr_pvd(void) +{ + nvic_clear_pending(NVIC_IRQ_PVD); + regs->PR.PR16 = 1; + callbacks[16](); +} + +void hdr_rtc_alarm(void) +{ + nvic_clear_pending(NVIC_IRQ_RTC_ALARM); + regs->PR.PR17 = 1; + callbacks[17](); +} + +void hdr_usb_wakeup(void) +{ + nvic_clear_pending(NVIC_IRQ_USB_WAKEUP); + regs->PR.PR18 = 1; + callbacks[18](); +} + diff --git a/drivers/exti.h b/drivers/exti.h new file mode 100644 index 0000000..f942c20 --- /dev/null +++ b/drivers/exti.h @@ -0,0 +1,108 @@ +/** @file exti.h + * Module handling EXTernal Interrupt lines + * + * The module provides functions to configure the exti lines to generate events + * or interrupts + */ + +#ifndef _EXTI_H_ +#define _EXTI_H_ + +//--includes-------------------------------------------------------------------- + +#include "stdint.h" +#include "stdbool.h" + +#include "gpio.h" + + +//--type definitions------------------------------------------------------------ + +/** + * Available exti lines. These lines correspond to gpios + */ +enum ExtiLine { + EXTI_LINE_0 = (0x1 << 0), + EXTI_LINE_1 = (0x1 << 1), + EXTI_LINE_2 = (0x1 << 2), + EXTI_LINE_3 = (0x1 << 3), + EXTI_LINE_4 = (0x1 << 4), + EXTI_LINE_5 = (0x1 << 5), + EXTI_LINE_6 = (0x1 << 6), + EXTI_LINE_7 = (0x1 << 7), + EXTI_LINE_8 = (0x1 << 8), + EXTI_LINE_9 = (0x1 << 9), + EXTI_LINE_10 = (0x1 << 10), + EXTI_LINE_11 = (0x1 << 11), + EXTI_LINE_12 = (0x1 << 12), + EXTI_LINE_13 = (0x1 << 13), + EXTI_LINE_14 = (0x1 << 14), + EXTI_LINE_15 = (0x1 << 15), +}; + +/** + * Available exti lines. These lines correspond to specific peripherals + */ +enum ExtiLineSpecific { + EXTI_LINE_PVD = (0x1 << 16), + EXTI_LINE_RTC = (0x1 << 17), + EXTI_LINE_USB = (0x1 << 18), +}; + +/** + * Available configurations for exti lines. These configurations apply to both + * regular and specific lines and can be used together on a single line + */ +enum ExtiConfig { + EXTI_CONFIG_RISING_EDGE, + EXTI_CONFIG_FALLING_EDGE, +}; + +typedef void (*ExtiCallback)(void); + + +//--functions------------------------------------------------------------------- + +/** + * Configure lines on a single GPIO port. The ExtiLine enum can be used as a + * mask to configure multiple lines at the same time. Every line can only be + * configured for a single port, reconfiguring it will override the previous + * configuration. Each line is linked to the pins of the same id (ex : pin 1 for + * exti 1). When possible, it is recommanded to use the lowest id possible for + * better performance. The ExtiConfig enum can be used as mask. If no callback + * is specified, the interrupt won't be enabled, but an event will still be sent + * to wake the cpu up + */ +void exti_configure(enum ExtiLine line_mask, enum GpioPort port, + enum ExtiConfig config_mask, ExtiCallback callback); + +/** + * Resets lines. The ExtiLine enum can be used as a mask to configure multiple + * lines at the same time + */ +void exti_reset(enum ExtiLine line_mask); + +/** + * Configure lines for specific, non-gpio peripherals. The ExtiLineSpecific enum + * can be used as a mask to configure multiple lines at the same time. The + * ExtiConfig enum can be used as mask. If no callback is specified, the + * interrupt won't be enabled, but an event will still be sent to wake the cpu + * up + */ +void exti_configure_specific(enum ExtiLineSpecific line_mask, + enum ExtiConfig config_mask, ExtiCallback callback); + +/** + * Resets lines for specific, non-gpio peripherals. The ExtiLineSpecific enum + * can be used as a mask to configure multiple lines at the same time. + */ +void exti_reset_specific(enum ExtiLineSpecific line_mask); + +/** + * Resets all lines. The exti peripheral is returned to its reset configuration + */ +void exti_reset_peripheral(void); + + +#endif //_EXTI_H_ +