stmhal: Add initial implementation of Pin.irq() method.

This method follows the new HW API and allows to set a hard or soft IRQ
callback when a Pin has a level change.  It still remains to make this
method return a IRQ object.
This commit is contained in:
Damien George 2017-06-15 17:17:36 +10:00
parent e269cabe3e
commit 4abe3731e3
3 changed files with 105 additions and 2 deletions

View File

@ -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.

View File

@ -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);

View File

@ -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) },