summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--stmhal/extint.c78
-rw-r--r--stmhal/extint.h1
-rw-r--r--stmhal/pin.c28
3 files changed, 105 insertions, 2 deletions
diff --git a/stmhal/extint.c b/stmhal/extint.c
index 642ecfd85e..70023557f4 100644
--- a/stmhal/extint.c
+++ b/stmhal/extint.c
@@ -95,9 +95,13 @@
// The USB_FS_WAKUP event is a direct type and there is no support for it.
#define EXTI_Mode_Interrupt offsetof(EXTI_TypeDef, IMR1)
#define EXTI_Mode_Event offsetof(EXTI_TypeDef, EMR1)
+#define EXTI_RTSR EXTI->RTSR1
+#define EXTI_FTSR EXTI->FTSR1
#else
#define EXTI_Mode_Interrupt offsetof(EXTI_TypeDef, IMR)
#define EXTI_Mode_Event offsetof(EXTI_TypeDef, EMR)
+#define EXTI_RTSR EXTI->RTSR
+#define EXTI_FTSR EXTI->FTSR
#endif
#define EXTI_SWIER_BB(line) (*(__IO uint32_t *)(PERIPH_BB_BASE + ((EXTI_OFFSET + offsetof(EXTI_TypeDef, SWIER)) * 32) + ((line) * 4)))
@@ -107,7 +111,11 @@ typedef struct {
mp_int_t line;
} extint_obj_t;
-STATIC uint32_t pyb_extint_mode[EXTI_NUM_VECTORS];
+STATIC uint8_t pyb_extint_mode[EXTI_NUM_VECTORS];
+STATIC bool pyb_extint_hard_irq[EXTI_NUM_VECTORS];
+
+// The callback arg is a small-int or a ROM Pin object, so no need to scan by GC
+STATIC mp_obj_t pyb_extint_callback_arg[EXTI_NUM_VECTORS];
#if !defined(ETH)
#define ETH_WKUP_IRQn 62 // Some MCUs don't have ETH, but we want a value to put in our table
@@ -187,6 +195,8 @@ uint extint_register(mp_obj_t pin_obj, uint32_t mode, uint32_t pull, mp_obj_t ca
EXTI_Mode_Interrupt : EXTI_Mode_Event;
if (*cb != mp_const_none) {
+ pyb_extint_hard_irq[v_line] = true;
+ pyb_extint_callback_arg[v_line] = MP_OBJ_NEW_SMALL_INT(v_line);
mp_hal_gpio_clock_enable(pin->gpio);
GPIO_InitTypeDef exti;
@@ -205,6 +215,64 @@ uint extint_register(mp_obj_t pin_obj, uint32_t mode, uint32_t pull, mp_obj_t ca
return v_line;
}
+// This function is intended to be used by the Pin.irq() method
+void extint_register_pin(const pin_obj_t *pin, uint32_t mode, bool hard_irq, mp_obj_t callback_obj) {
+ uint32_t line = pin->pin;
+
+ // Check if the ExtInt line is already in use by another Pin/ExtInt
+ mp_obj_t *cb = &MP_STATE_PORT(pyb_extint_callback)[line];
+ if (*cb != mp_const_none && MP_OBJ_FROM_PTR(pin) != pyb_extint_callback_arg[line]) {
+ if (MP_OBJ_IS_SMALL_INT(pyb_extint_callback_arg[line])) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "ExtInt vector %d is already in use", line));
+ } else {
+ const pin_obj_t *other_pin = (const pin_obj_t*)pyb_extint_callback_arg[line];
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "IRQ resource already taken by Pin('%q')", other_pin->name));
+ }
+ }
+
+ extint_disable(line);
+
+ *cb = callback_obj;
+ pyb_extint_mode[line] = (mode & 0x00010000) ? // GPIO_MODE_IT == 0x00010000
+ EXTI_Mode_Interrupt : EXTI_Mode_Event;
+
+ if (*cb != mp_const_none) {
+ // Configure and enable the callback
+
+ pyb_extint_hard_irq[line] = hard_irq;
+ pyb_extint_callback_arg[line] = MP_OBJ_FROM_PTR(pin);
+
+ // Route the GPIO to EXTI
+ __HAL_RCC_SYSCFG_CLK_ENABLE();
+ SYSCFG->EXTICR[line >> 2] =
+ (SYSCFG->EXTICR[line >> 2] & ~(0x0f << (4 * (line & 0x03))))
+ | ((uint32_t)(GPIO_GET_INDEX(pin->gpio)) << (4 * (line & 0x03)));
+
+ // Enable or disable the rising detector
+ if ((mode & GPIO_MODE_IT_RISING) == GPIO_MODE_IT_RISING) {
+ EXTI_RTSR |= 1 << line;
+ } else {
+ EXTI_RTSR &= ~(1 << line);
+ }
+
+ // Enable or disable the falling detector
+ if ((mode & GPIO_MODE_IT_FALLING) == GPIO_MODE_IT_FALLING) {
+ EXTI_FTSR |= 1 << line;
+ } else {
+ EXTI_FTSR &= ~(1 << line);
+ }
+
+ // Configure the NVIC
+ HAL_NVIC_SetPriority(nvic_irq_channel[line], IRQ_PRI_EXTINT, IRQ_SUBPRI_EXTINT);
+ HAL_NVIC_EnableIRQ(nvic_irq_channel[line]);
+
+ // Enable the interrupt
+ extint_enable(line);
+ }
+}
+
void extint_enable(uint line) {
if (line >= EXTI_NUM_VECTORS) {
return;
@@ -411,13 +479,19 @@ void Handle_EXTI_Irq(uint32_t line) {
if (line < EXTI_NUM_VECTORS) {
mp_obj_t *cb = &MP_STATE_PORT(pyb_extint_callback)[line];
if (*cb != mp_const_none) {
+ // If it's a soft IRQ handler then just schedule callback for later
+ if (!pyb_extint_hard_irq[line]) {
+ mp_sched_schedule(*cb, pyb_extint_callback_arg[line]);
+ return;
+ }
+
mp_sched_lock();
// When executing code within a handler we must lock the GC to prevent
// any memory allocations. We must also catch any exceptions.
gc_lock();
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
- mp_call_function_1(*cb, MP_OBJ_NEW_SMALL_INT(line));
+ mp_call_function_1(*cb, pyb_extint_callback_arg[line]);
nlr_pop();
} else {
// Uncaught exception; disable the callback so it doesn't run again.
diff --git a/stmhal/extint.h b/stmhal/extint.h
index f0764aef25..b04224c401 100644
--- a/stmhal/extint.h
+++ b/stmhal/extint.h
@@ -52,6 +52,7 @@
void extint_init0(void);
uint extint_register(mp_obj_t pin_obj, uint32_t mode, uint32_t pull, mp_obj_t callback_obj, bool override_callback_obj);
+void extint_register_pin(const pin_obj_t *pin, uint32_t mode, bool hard_irq, mp_obj_t callback_obj);
void extint_enable(uint line);
void extint_disable(uint line);
diff --git a/stmhal/pin.c b/stmhal/pin.c
index 4c0d49e7d3..f30474e1f5 100644
--- a/stmhal/pin.c
+++ b/stmhal/pin.c
@@ -33,6 +33,7 @@
#include "py/mphal.h"
#include "extmod/virtpin.h"
#include "pin.h"
+#include "extint.h"
/// \moduleref pyb
/// \class Pin - control I/O pins
@@ -414,6 +415,29 @@ STATIC mp_obj_t pin_on(mp_obj_t self_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_on_obj, pin_on);
+// pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING, hard=False)
+STATIC mp_obj_t pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_handler, ARG_trigger, ARG_hard };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_handler, MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ { MP_QSTR_trigger, MP_ARG_INT, {.u_int = GPIO_MODE_IT_RISING | GPIO_MODE_IT_FALLING} },
+ { MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} },
+ };
+ pin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ if (n_args > 1 || kw_args->used != 0) {
+ // configure irq
+ extint_register_pin(self, args[ARG_trigger].u_int,
+ args[ARG_hard].u_bool, args[ARG_handler].u_obj);
+ }
+
+ // TODO should return an IRQ object
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pin_irq_obj, 1, pin_irq);
+
/// \method name()
/// Get the pin name.
STATIC mp_obj_t pin_name(mp_obj_t self_in) {
@@ -498,6 +522,8 @@ STATIC const mp_rom_map_elem_t pin_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&pin_value_obj) },
{ MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&pin_off_obj) },
{ MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&pin_on_obj) },
+ { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&pin_irq_obj) },
+
// Legacy names as used by pyb.Pin
{ MP_ROM_QSTR(MP_QSTR_low), MP_ROM_PTR(&pin_off_obj) },
{ MP_ROM_QSTR(MP_QSTR_high), MP_ROM_PTR(&pin_on_obj) },
@@ -529,6 +555,8 @@ STATIC const mp_rom_map_elem_t pin_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_ANALOG), MP_ROM_INT(GPIO_MODE_ANALOG) },
{ MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(GPIO_PULLUP) },
{ MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULLDOWN) },
+ { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(GPIO_MODE_IT_RISING) },
+ { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(GPIO_MODE_IT_FALLING) },
// legacy class constants
{ MP_ROM_QSTR(MP_QSTR_OUT_PP), MP_ROM_INT(GPIO_MODE_OUTPUT_PP) },