summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ports/samd/Makefile2
-rw-r--r--ports/samd/machine_pin.c200
-rw-r--r--ports/samd/main.c2
-rw-r--r--ports/samd/mpconfigport.h6
-rw-r--r--ports/samd/samd_isr.c3
5 files changed, 207 insertions, 6 deletions
diff --git a/ports/samd/Makefile b/ports/samd/Makefile
index 514c92ba1..163971cde 100644
--- a/ports/samd/Makefile
+++ b/ports/samd/Makefile
@@ -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)
diff --git a/ports/samd/machine_pin.c b/ports/samd/machine_pin.c
index 98317abbf..a70e9ad01 100644
--- a/ports/samd/machine_pin.c
+++ b/ports/samd/machine_pin.c
@@ -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]);
diff --git a/ports/samd/main.c b/ports/samd/main.c
index 4af4b2b4f..520763b5a 100644
--- a/ports/samd/main.c
+++ b/ports/samd/main.c
@@ -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();
diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h
index 70bd202a3..574d47b03 100644
--- a/ports/samd/mpconfigport.h
+++ b/ports/samd/mpconfigport.h
@@ -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
diff --git a/ports/samd/samd_isr.c b/ports/samd/samd_isr.c
index e16018c53..a4e1dd708 100644
--- a/ports/samd/samd_isr.c
+++ b/ports/samd/samd_isr.c
@@ -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) {
}