diff --git a/epicardium/api/common.h b/epicardium/api/common.h index a929f301cabb530ed3e430a021dd4dd441acf372..c884f37c48aaa7c6b26b64d5961f99df7a07c6f2 100644 --- a/epicardium/api/common.h +++ b/epicardium/api/common.h @@ -1,3 +1,6 @@ +#pragma once +#include "epicardium.h" + #include <stdint.h> #include <stdbool.h> @@ -26,6 +29,9 @@ struct api_call_mem { /* ID if the ongoing API call */ api_id_t id; + /* ID of the current interrupt */ + api_int_id_t int_id; + /* * Buffer for arguments/return value. This buffer will be * *overflown*, because there is guaranteed space behind it. diff --git a/epicardium/api/interrupt-receiver.c b/epicardium/api/interrupt-receiver.c new file mode 100644 index 0000000000000000000000000000000000000000..95c5038d12ee10bdc53a087ea121a568bb44d86a --- /dev/null +++ b/epicardium/api/interrupt-receiver.c @@ -0,0 +1,35 @@ +#include "max32665.h" +#include "tmr.h" +#include "api/common.h" +#include "epicardium.h" + +void api_interrupt_handler_ctrl_c(api_int_id_t id) + __attribute__((weak, alias("api_interrupt_handler_default"))); +void api_interrupt_handler_bhi160(api_int_id_t id) + __attribute__((weak, alias("api_interrupt_handler_default"))); + +/* Timer Interrupt used for control char notification */ +void TMR5_IRQHandler(void) +{ + TMR_IntClear(MXC_TMR5); + + switch (API_CALL_MEM->int_id) { + case API_INT_CTRL_C: + api_interrupt_handler_ctrl_c(API_CALL_MEM->int_id); + break; + case API_INT_BHI160: + api_interrupt_handler_bhi160(API_CALL_MEM->int_id); + break; + } + + API_CALL_MEM->int_id = 0; +} + +__attribute__((weak)) void api_interrupt_handler_catch_all(api_int_id_t id) +{ +} + +void api_interrupt_handler_default(api_int_id_t id) +{ + api_interrupt_handler_catch_all(id); +} diff --git a/epicardium/api/interrupt-sender.c b/epicardium/api/interrupt-sender.c new file mode 100644 index 0000000000000000000000000000000000000000..fb0c65a7c189fbdfa329e8a6a8acdfb67db97c64 --- /dev/null +++ b/epicardium/api/interrupt-sender.c @@ -0,0 +1,50 @@ +#include "api/interrupt-sender.h" +#include "api/common.h" +#include "tmr_utils.h" + +static bool enabled[API_INT_MAX + 1]; + +int api_interrupt_trigger(api_int_id_t id) +{ + if (id > API_INT_MAX) { + return -EINVAL; + } + + if (enabled[id]) { + while (API_CALL_MEM->int_id) + ; + API_CALL_MEM->int_id = id; + TMR_TO_Start(MXC_TMR5, 1, 0); + } + return 0; +} + +void api_interrupt_init(void) +{ + int i; + API_CALL_MEM->int_id = 0; + + for (i = 0; i <= API_INT_MAX; i++) { + enabled[i] = false; + } +} + +int epic_interrupt_enable(api_int_id_t int_id) +{ + if (int_id > API_INT_MAX) { + return -EINVAL; + } + + enabled[int_id] = true; + return 0; +} + +int epic_interrupt_disable(api_int_id_t int_id) +{ + if (int_id > API_INT_MAX) { + return -EINVAL; + } + + enabled[int_id] = false; + return 0; +} diff --git a/epicardium/api/interrupt-sender.h b/epicardium/api/interrupt-sender.h new file mode 100644 index 0000000000000000000000000000000000000000..4f690167ceff8622418b4983da17b29085a446f4 --- /dev/null +++ b/epicardium/api/interrupt-sender.h @@ -0,0 +1,4 @@ +#pragma once +#include "api/common.h" +void api_interrupt_init(void); +int api_interrupt_trigger(api_int_id_t id); diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index 4e52d85c51c38d897f38dbee03ed96cbbb7b0de1..9add57508e7aa91b3bb0f718bec2aaeddb990817 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -4,17 +4,24 @@ #include <stddef.h> #include <errno.h> +/* clang-format off */ +#define API_INT_CTRL_C 1 +#define API_INT_BHI160 2 +#define API_INT_MAX API_INT_BHI160 + #ifndef API #define API(id, def) def #endif -/* clang-format off */ #define API_UART_WRITE 0x1 #define API_UART_READ 0x2 #define API_LEDS_SET 0x3 #define API_VIBRA_SET 0x4 #define API_VIBRA_VIBRATE 0x5 #define API_STREAM_READ 0x6 +#define API_INTERRUPT_ENABLE 0x7 +#define API_INTERRUPT_DISABLE 0x8 + /* clang-format on */ /** @@ -139,4 +146,23 @@ API(API_VIBRA_SET, void epic_vibra_set(int status)); */ API(API_VIBRA_VIBRATE, void epic_vibra_vibrate(int millis)); +/** + * API interrupt type + */ +typedef uint32_t api_int_id_t; + +/** + * Enable/unmask an API interrupt + * + * :param int_id: The interrupt to be enabled + */ +API(API_INTERRUPT_ENABLE, int epic_interrupt_enable(api_int_id_t int_id)); + +/** + * Disable/mask an API interrupt + * + * :param int_id: The interrupt to be disabled + */ +API(API_INTERRUPT_DISABLE, int epic_interrupt_disable(api_int_id_t int_id)); + #endif /* _EPICARDIUM_H */ diff --git a/epicardium/main.c b/epicardium/main.c index 51d365f2c0a754b1432dfe5ae14ddf95de620aee..75bb9c79537a82de067d2618d1f3071348d2b032 100644 --- a/epicardium/main.c +++ b/epicardium/main.c @@ -12,6 +12,7 @@ #include "modules/modules.h" #include "modules/log.h" #include "modules/stream.h" +#include "api/interrupt-sender.h" #include <Heart.h> #include "GUI_Paint.h" @@ -54,6 +55,7 @@ int main(void) } fatfs_init(); + api_interrupt_init(); stream_init(); LOG_INFO("startup", "Initializing tasks ..."); diff --git a/epicardium/meson.build b/epicardium/meson.build index 26f36cd47e471bf658b35b6447a69489110197ea..a6ba27ce781fce91b639529351cc8b07620dbeb5 100644 --- a/epicardium/meson.build +++ b/epicardium/meson.build @@ -22,6 +22,7 @@ api = custom_target( api_caller_lib = static_library( 'api-caller', 'api/caller.c', + 'api/interrupt-receiver.c', api[0], # Caller dependencies: periphdriver, ) @@ -35,6 +36,7 @@ api_caller = declare_dependency( api_dispatcher_lib = static_library( 'api-dispatcher', 'api/dispatcher.c', + 'api/interrupt-sender.c', api[1], # Dispatcher dependencies: periphdriver, ) diff --git a/epicardium/modules/serial.c b/epicardium/modules/serial.c index 9e41efed4e75db20fcbb56d3022b276c44b770c7..bb9c325ce3933287307093d3e4caefc28ef3a0e8 100644 --- a/epicardium/modules/serial.c +++ b/epicardium/modules/serial.c @@ -4,7 +4,6 @@ #include "max32665.h" #include "cdcacm.h" #include "uart.h" -#include "tmr_utils.h" #include "FreeRTOS.h" #include "task.h" @@ -12,6 +11,7 @@ #include "modules.h" #include "modules/log.h" +#include "api/interrupt-sender.h" /* Task ID for the serial handler */ TaskHandle_t serial_task_id = NULL; @@ -24,7 +24,7 @@ static QueueHandle_t read_queue; /* * API-call to write a string. Output goes to both CDCACM and UART */ -void epic_uart_write_str(char *str, intptr_t length) +void epic_uart_write_str(const char *str, intptr_t length) { UART_Write(ConsoleUart, (uint8_t *)str, length); cdcacm_write((uint8_t *)str, length); @@ -57,7 +57,12 @@ static void enqueue_char(char chr) { if (chr == 0x3) { /* Control-C */ - TMR_TO_Start(MXC_TMR5, 1, 0); + api_interrupt_trigger(API_INT_CTRL_C); + } + + if (chr == 0x0e) { + /* Control-N */ + api_interrupt_trigger(API_INT_BHI160); } if (xQueueSend(read_queue, &chr, 100) == errQUEUE_FULL) { diff --git a/pycardium/main.c b/pycardium/main.c index 737fe1ad519ba373befe3700aa669779190f01b1..da08475b82820fc25fd72bb04032aa3f26fc2461 100644 --- a/pycardium/main.c +++ b/pycardium/main.c @@ -19,7 +19,7 @@ int main(void) NVIC_EnableIRQ(TMR5_IRQn); while (1) { - gc_init(&__HeapBase, &__HeapLimit); + gc_init(&__HeapBase + 1024 * 10, &__HeapLimit); mp_init(); pyexec_friendly_repl(); diff --git a/pycardium/meson.build b/pycardium/meson.build index 67698112ed8be52f569fec2d4d087371bbe5cb91..50398d58d43022015feb04e98bb69eee176e7f83 100644 --- a/pycardium/meson.build +++ b/pycardium/meson.build @@ -4,6 +4,7 @@ modsrc = files( 'modules/utime.c', 'modules/leds.c', 'modules/vibra.c', + 'modules/interrupt.c', ) ################################# @@ -75,7 +76,7 @@ elf = executable( include_directories: micropython_includes, dependencies: [max32665_startup_core1, periphdriver, api_caller], link_with: upy, - link_whole: [max32665_startup_core1_lib], + link_whole: [max32665_startup_core1_lib, api_caller_lib], link_args: [ '-Wl,-Map=' + meson.current_build_dir() + '/' + name + '.map', ], diff --git a/pycardium/modules/interrupt.c b/pycardium/modules/interrupt.c new file mode 100644 index 0000000000000000000000000000000000000000..b45680c723a8150fd440665065ff8170f8e3e6bc --- /dev/null +++ b/pycardium/modules/interrupt.c @@ -0,0 +1,97 @@ +#include "py/obj.h" +#include "py/runtime.h" +#include "py/builtin.h" +#include "epicardium.h" +#include "api/common.h" +#include "mphalport.h" + +// TODO: these should be intialized as mp_const_none +mp_obj_t callbacks[API_INT_MAX + 1] = { + 0, +}; + +void api_interrupt_handler_catch_all(api_int_id_t id) +{ + // TODO: check if id is out of rante + // TOOD: check against mp_const_none + if (id <= API_INT_MAX) { + if (callbacks[id]) { + mp_sched_schedule( + callbacks[id], MP_OBJ_NEW_SMALL_INT(id) + ); + } + } +} + +STATIC mp_obj_t mp_interrupt_set_callback(mp_obj_t id_in, mp_obj_t callback_obj) +{ + api_int_id_t id = mp_obj_get_int(id_in); + if (callback_obj != mp_const_none && + !mp_obj_is_callable(callback_obj)) { + mp_raise_ValueError("handler must be None or callable"); + } + + // TODO: throw error if id is out of range + if (id <= API_INT_MAX) { + callbacks[id] = callback_obj; + } + + return mp_const_none; +} + +STATIC mp_obj_t mp_interrupt_enable_callback(mp_obj_t id_in) +{ + api_int_id_t id = mp_obj_get_int(id_in); + + // TODO: throw error if id is out of range or epic_interrupt_enable failed + if (epic_interrupt_enable(id) < 0) { + } + + return mp_const_none; +} + +STATIC mp_obj_t mp_interrupt_disable_callback(mp_obj_t id_in) +{ + api_int_id_t id = mp_obj_get_int(id_in); + + // TODO: throw error if id is out of range or epic_interrupt_enable failed + if (epic_interrupt_disable(id) < 0) { + } + + return mp_const_none; +} + + +STATIC MP_DEFINE_CONST_FUN_OBJ_2( + interrupt_set_callback_obj, mp_interrupt_set_callback +); +STATIC MP_DEFINE_CONST_FUN_OBJ_1( + interrupt_enable_callback_obj, mp_interrupt_enable_callback +); +STATIC MP_DEFINE_CONST_FUN_OBJ_1( + interrupt_disable_callback_obj, mp_interrupt_disable_callback +); + +STATIC const mp_rom_map_elem_t interrupt_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_interrupt) }, + { MP_ROM_QSTR(MP_QSTR_set_callback), + MP_ROM_PTR(&interrupt_set_callback_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_callback), + MP_ROM_PTR(&interrupt_enable_callback_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable_callback), + MP_ROM_PTR(&interrupt_disable_callback_obj) }, + { MP_ROM_QSTR(MP_QSTR_BHI160), MP_OBJ_NEW_SMALL_INT(2) }, +}; +STATIC MP_DEFINE_CONST_DICT( + interrupt_module_globals, interrupt_module_globals_table +); + +// Define module object. +const mp_obj_module_t interrupt_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&interrupt_module_globals, +}; + +/* clang-format off */ +// Register the module to make it available in Python +MP_REGISTER_MODULE(MP_QSTR_interrupt, interrupt_module, MODULE_INTERRUPT_ENABLED); diff --git a/pycardium/modules/qstrdefs.h b/pycardium/modules/qstrdefs.h index 2a6dcbe6beb7ebe5a4a5a29226b7eca0a6b9c901..4c7e2f753eb0f12f98f8ea1d4490297284be5408 100644 --- a/pycardium/modules/qstrdefs.h +++ b/pycardium/modules/qstrdefs.h @@ -25,3 +25,8 @@ Q(ticks_diff) /* vibra */ Q(vibra) Q(vibrate) + +Q(set_callback) +Q(enable_callback) +Q(disable_callback) +Q(BHI160) diff --git a/pycardium/mpconfigport.h b/pycardium/mpconfigport.h index a99c63acfe3d50a083b6a428e1b511d41edf13e5..d71bf5d4d34d8aec39eff5f027c52fa69f8df14b 100644 --- a/pycardium/mpconfigport.h +++ b/pycardium/mpconfigport.h @@ -19,6 +19,8 @@ #define MICROPY_HELPER_REPL (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG) +#define MICROPY_ENABLE_SCHEDULER (1) + /* Builtin function and modules */ #define MICROPY_PY_ALL_SPECIAL_METHODS (1) #define MICROPY_PY_BUILTINS_HELP (1) @@ -38,6 +40,7 @@ #define MODULE_UTIME_ENABLED (1) #define MODULE_LEDS_ENABLED (1) #define MODULE_VIBRA_ENABLED (1) +#define MODULE_INTERRUPT_ENABLED (1) /* * This port is intended to be 32-bit, but unfortunately, int32_t for diff --git a/pycardium/mphalport.c b/pycardium/mphalport.c index fdfb592d8830699f8d222952d9a4f99095b60ae3..4ca84e13027f55c434ece35cf741447284498450 100644 --- a/pycardium/mphalport.c +++ b/pycardium/mphalport.c @@ -14,7 +14,7 @@ #include "tmr.h" #include "epicardium.h" - +#include "api/common.h" /****************************************************************************** * Serial Communication */ @@ -62,23 +62,16 @@ long _write(int fd, const char *buf, size_t cnt) return cnt; } -bool do_interrupt = false; - -/* Timer Interrupt used for control char notification */ -void TMR5_IRQHandler(void) +void api_interrupt_handler_ctrl_c(void) { - TMR_IntClear(MXC_TMR5); - - if (do_interrupt) { - /* Taken from lib/micropython/micropython/lib/utils/interrupt_char.c */ - MP_STATE_VM(mp_pending_exception) = - MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)); + /* Taken from lib/micropython/micropython/lib/utils/interrupt_char.c */ + MP_STATE_VM(mp_pending_exception) = + MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)); #if MICROPY_ENABLE_SCHEDULER - if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { - MP_STATE_VM(sched_state) = MP_SCHED_PENDING; - } -#endif + if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { + MP_STATE_VM(sched_state) = MP_SCHED_PENDING; } +#endif } void mp_hal_set_interrupt_char(char c) @@ -88,7 +81,12 @@ void mp_hal_set_interrupt_char(char c) MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)) ); } - do_interrupt = (c == 0x03); + + if (c == 0x03) { + epic_interrupt_enable(API_INT_CTRL_C); + } else { + epic_interrupt_disable(API_INT_CTRL_C); + } } /****************************************************************************** @@ -137,7 +135,7 @@ mp_import_stat_t mp_import_stat(const char *path) mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { /* TODO: Once fs is implemented, get this working as well */ - mp_raise_NotImplementedError ("FS is not yet implemented"); + mp_raise_NotImplementedError("FS is not yet implemented"); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open);