samd/machine_pin: Add pin.irq() to the machine.Pin class.

Its API conforms to the docs.  There are 16 IRQ channels available, which
will be used as assignable to the GPIO numbers.  In most cases, the irq
channel is GPIO_no % 16.
This commit is contained in:
robert-hh 2022-06-05 14:08:44 +02:00 committed by Damien George
parent a9eef1b276
commit 4b6f6ccf88
5 changed files with 207 additions and 6 deletions

View File

@ -118,6 +118,7 @@ SRC_C = \
lib/tinyusb/src/portable/microchip/samd/dcd_samd.c \
lib/tinyusb/src/tusb.c \
drivers/bus/softspi.c \
shared/runtime/mpirq.c \
shared/libc/printf.c \
shared/libc/string0.c \
shared/readline/readline.c \
@ -148,6 +149,7 @@ SRC_QSTR += \
modsamd.c \
samd_flash.c \
shared/readline/readline.c \
shared/runtime/mpirq.c \
SRC_QSTR += $(SRC_MOD) $(SRC_CXX)

View File

@ -4,6 +4,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2016-2021 Damien P. George
* Copyright (c) 2022 Robert Hammelrath (pin.irq)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -29,10 +30,12 @@
#include "string.h"
#include "py/runtime.h"
#include "py/mphal.h"
#include "shared/runtime/mpirq.h"
#include "extmod/virtpin.h"
#include "modmachine.h"
#include "samd_soc.h"
#include "pins.h"
#include "pin_af.h"
#include "hal_gpio.h"
@ -42,6 +45,17 @@
#define GPIO_STRENGTH_2MA (0)
#define GPIO_STRENGTH_8MA (1)
#define GPIO_IRQ_EDGE_RISE (1)
#define GPIO_IRQ_EDGE_FALL (2)
typedef struct _machine_pin_irq_obj_t {
mp_irq_obj_t base;
uint32_t flags;
uint32_t trigger;
uint8_t pin_id;
} machine_pin_irq_obj_t;
STATIC const mp_irq_methods_t machine_pin_irq_methods;
uint32_t machine_pin_open_drain_mask[4];
@ -262,6 +276,141 @@ STATIC mp_obj_t machine_pin_drive(size_t n_args, const mp_obj_t *args) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_drive_obj, 1, 2, machine_pin_drive);
// pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING, hard=False)
STATIC mp_obj_t machine_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_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_trigger, MP_ARG_INT, {.u_int = 3} },
{ MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} },
};
machine_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);
// Get the IRQ object.
uint8_t eic_id = get_pin_af_info(self->id)->eic;
machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_objects[eic_id]);
if (irq != NULL && irq->pin_id != self->id) {
mp_raise_ValueError(MP_ERROR_TEXT("IRQ already used"));
}
// Allocate the IRQ object if it doesn't already exist.
if (irq == NULL) {
irq = m_new_obj(machine_pin_irq_obj_t);
irq->base.base.type = &mp_irq_type;
irq->base.methods = (mp_irq_methods_t *)&machine_pin_irq_methods;
irq->base.parent = MP_OBJ_FROM_PTR(self);
irq->base.handler = mp_const_none;
irq->base.ishard = false;
irq->pin_id = 0xff;
MP_STATE_PORT(machine_pin_irq_objects[eic_id]) = irq;
}
// (Re-)configure the irq.
if (n_args > 1 || kw_args->used != 0) {
// set the mux config of the pin.
mp_hal_set_pin_mux(self->id, ALT_FCT_EIC);
// Configure IRQ.
#if defined(MCU_SAMD21)
uint32_t irq_num = 4;
// Disable all IRQs from the affected source while data is updated.
NVIC_DisableIRQ(irq_num);
// Disable EIC
EIC->CTRL.bit.ENABLE = 0;
while (EIC->STATUS.bit.SYNCBUSY != 0) {
}
EIC->INTENCLR.reg = (1 << eic_id);
// Enable the clocks
PM->APBAMASK.bit.EIC_ |= 1;
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK2 | EIC_GCLK_ID;
#elif defined(MCU_SAMD51)
uint32_t irq_num = eic_id + 12;
// Disable all IRQs from the affected source while data is updated.
NVIC_DisableIRQ(irq_num);
// Disable EIC
EIC->CTRLA.bit.ENABLE = 0;
while (EIC->SYNCBUSY.bit.ENABLE != 0) {
}
EIC->INTENCLR.reg = (1 << eic_id);
// Enable the clocks
MCLK->APBAMASK.bit.EIC_ |= 1;
GCLK->PCHCTRL[EIC_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK2;
#endif
// Clear the pending interrupts flag
EIC->INTENCLR.reg = (1 << eic_id);
// Update IRQ data.
irq->base.handler = args[ARG_handler].u_obj;
irq->base.ishard = args[ARG_hard].u_bool;
irq->flags = 0;
irq->trigger = args[ARG_trigger].u_int;
irq->pin_id = self->id;
// Enable IRQ if a handler is given.
if (args[ARG_handler].u_obj != mp_const_none) {
// Set EIC channel mode
EIC->CONFIG[eic_id / 8].reg |= irq->trigger << ((eic_id % 8) * 4);
EIC->INTENSET.reg = (1 << eic_id);
EIC->INTFLAG.reg |= (1 << eic_id);
}
// Enable EIC (again)
#if defined(MCU_SAMD21)
EIC->CTRL.bit.ENABLE = 1;
while (EIC->STATUS.bit.SYNCBUSY != 0) {
}
#elif defined(MCU_SAMD51)
EIC->CTRLA.bit.ENABLE = 1;
while (EIC->SYNCBUSY.bit.ENABLE != 0) {
}
#endif
// Enable interrupt again
NVIC_EnableIRQ(irq_num);
}
return MP_OBJ_FROM_PTR(irq);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_irq_obj, 1, machine_pin_irq);
void pin_irq_deinit_all(void) {
EIC->INTENCLR.reg = 0xffff; // Disable all interrupts from the EIC.
for (int i = 0; i < 16; i++) { // Clear all irq object pointers
MP_STATE_PORT(machine_pin_irq_objects[i]) = NULL;
}
// Disable all irq's at the NVIC controller
#if defined(MCU_SAMD21)
NVIC_DisableIRQ(4);
#elif defined(MCU_SAMD51)
for (int i = 12; i < 20; i++) {
NVIC_DisableIRQ(i);
}
#endif
}
// Common EIC handler for all events.
void EIC_Handler() {
uint32_t mask = 1;
uint32_t isr = EIC->INTFLAG.reg;
for (int eic_id = 0; eic_id < 16; eic_id++, mask <<= 1) {
// Did the ISR fire?
if (isr & mask) {
EIC->INTFLAG.reg |= mask; // clear the ISR flag
machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_objects[eic_id]);
if (irq != NULL) {
irq->flags = irq->trigger;
mp_irq_handler(&irq->base);
break;
}
}
}
}
STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
// instance methods
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pin_init_obj) },
@ -273,6 +422,7 @@ STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&machine_pin_toggle_obj) },
{ MP_ROM_QSTR(MP_QSTR_disable), MP_ROM_PTR(&machine_pin_disable_obj) },
{ MP_ROM_QSTR(MP_QSTR_drive), MP_ROM_PTR(&machine_pin_drive_obj) },
{ MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_pin_irq_obj) },
// class constants
{ MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(GPIO_MODE_IN) },
@ -283,6 +433,8 @@ STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULL_DOWN) },
{ MP_ROM_QSTR(MP_QSTR_LOW_POWER), MP_ROM_INT(GPIO_STRENGTH_2MA) },
{ MP_ROM_QSTR(MP_QSTR_HIGH_POWER), MP_ROM_INT(GPIO_STRENGTH_8MA) },
{ MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(GPIO_IRQ_EDGE_RISE) },
{ MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(GPIO_IRQ_EDGE_FALL) },
};
STATIC MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table);
@ -292,10 +444,10 @@ STATIC mp_uint_t pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, i
switch (request) {
case MP_PIN_READ: {
return gpio_get_pin_level(self->id);
return mp_hal_pin_read(self->id);
}
case MP_PIN_WRITE: {
gpio_set_pin_level(self->id, arg);
mp_hal_pin_write(self->id, arg);
return 0;
}
}
@ -317,6 +469,48 @@ MP_DEFINE_CONST_OBJ_TYPE(
locals_dict, &machine_pin_locals_dict
);
static uint8_t find_eic_id(int pin) {
for (int eic_id = 0; eic_id < 16; eic_id++) {
machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_objects[eic_id]);
if (irq != NULL && irq->pin_id == pin) {
return eic_id;
}
}
return 0xff;
}
STATIC mp_uint_t machine_pin_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) {
machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint8_t eic_id = find_eic_id(self->id);
if (eic_id != 0xff) {
machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_objects[eic_id]);
EIC->INTENCLR.reg |= (1 << eic_id);
irq->flags = 0;
irq->trigger = new_trigger;
EIC->INTENSET.reg |= (1 << eic_id);
}
return 0;
}
STATIC mp_uint_t machine_pin_irq_info(mp_obj_t self_in, mp_uint_t info_type) {
machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint8_t eic_id = find_eic_id(self->id);
if (eic_id != 0xff) {
machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_objects[eic_id]);
if (info_type == MP_IRQ_INFO_FLAGS) {
return irq->flags;
} else if (info_type == MP_IRQ_INFO_TRIGGERS) {
return irq->trigger;
}
}
return 0;
}
STATIC const mp_irq_methods_t machine_pin_irq_methods = {
.trigger = machine_pin_irq_trigger,
.info = machine_pin_irq_info,
};
mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t obj) {
if (!mp_obj_is_type(obj, &machine_pin_type)) {
mp_raise_ValueError(MP_ERROR_TEXT("expecting a Pin"));
@ -324,3 +518,5 @@ mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t obj) {
machine_pin_obj_t *pin = MP_OBJ_TO_PTR(obj);
return pin->id;
}
MP_REGISTER_ROOT_POINTER(void *machine_pin_irq_objects[16]);

View File

@ -34,6 +34,7 @@
extern uint8_t _sstack, _estack, _sheap, _eheap;
extern void adc_deinit_all(void);
extern void pin_irq_deinit_all(void);
extern void pwm_deinit_all(void);
void samd_main(void) {
@ -65,6 +66,7 @@ void samd_main(void) {
mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n");
adc_deinit_all();
pin_irq_deinit_all();
pwm_deinit_all();
gc_sweep_all();
mp_deinit();

View File

@ -54,7 +54,7 @@
#define MICROPY_PY_BUILTINS_HELP (1)
#define MICROPY_PY_BUILTINS_HELP_TEXT samd_help_text
#define MICROPY_PY_BUILTINS_HELP_MODULES (1)
#define MICROPY_ENABLE_SCHEDULER (1)
// fixes sys/usys import issue
#define MICROPY_MODULE_WEAK_LINKS (1)
// Control over Python builtins
@ -104,6 +104,10 @@
#define MICROPY_PY_MACHINE_PWM_DUTY_U16_NS (1)
#define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/samd/machine_pwm.c"
// Use VfsLfs's types for fileio/textio
#define mp_type_fileio mp_type_vfs_lfs1_fileio
#define mp_type_textio mp_type_vfs_lfs1_textio
#define MP_STATE_PORT MP_STATE_VM
// Miscellaneous settings

View File

@ -104,9 +104,6 @@ void SysTick_Handler(void) {
// Temporary Handlers to allow builds.
// Will be removed when the respecitve module is added.
void EIC_Handler(void) {
}
void PendSV_Handler(void) {
}