diff --git a/lib/utils/linked_list.h b/lib/utils/linked_list.h new file mode 100644 index 0000000000..cd5ea0fc7c --- /dev/null +++ b/lib/utils/linked_list.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_LINKED_LIST_H +#define MICROPY_INCLUDED_LINKED_LIST_H + +#define LINKED_LIST_PREPEND(LIST_HEAD, OBJECT, NEXT_FIELD) \ + OBJECT->NEXT_FIELD = LIST_HEAD; \ + LIST_HEAD = OBJECT; + +#define LINKED_LIST_DELETE(LIST_HEAD, OBJECT, + +#endif // MICROPY_INCLUDED_LINKED_LIST_H diff --git a/main.c b/main.c index 94d35fb8d7..a11157a5fb 100755 --- a/main.c +++ b/main.c @@ -86,6 +86,10 @@ #include "shared-module/displayio/__init__.h" #endif +#if CIRCUITPY_KEYPAD +#include "shared-module/keypad/__init__.h" +#endif + #if CIRCUITPY_MEMORYMONITOR #include "shared-module/memorymonitor/__init__.h" #endif @@ -230,9 +234,11 @@ STATIC void cleanup_after_vm(supervisor_allocation* heap) { #if CIRCUITPY_DISPLAYIO reset_displays(); #endif + #if CIRCUITPY_MEMORYMONITOR memorymonitor_reset(); #endif + filesystem_flush(); stop_mp(); free_memory(heap); @@ -242,6 +248,10 @@ STATIC void cleanup_after_vm(supervisor_allocation* heap) { common_hal_canio_reset(); #endif + #if CIRCUITPY_KEYPAD + keypad_reset(); + #endif + // reset_board_busses() first because it may release pins from the never_reset state, so that // reset_port() can reset them. #if CIRCUITPY_BOARD diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 86321d5d02..4b6585c2a4 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -533,8 +533,20 @@ extern const struct _mp_obj_module_t ipaddress_module; #if CIRCUITPY_KEYPAD extern const struct _mp_obj_module_t keypad_module; #define KEYPAD_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_keypad), (mp_obj_t)&keypad_module }, +#define KEYPAD_ROOT_POINTERS \ + mp_obj_t keypad_keys_linked_list; \ + mp_obj_t keypad_keymatrix_linked_list; #else #define KEYPAD_MODULE +#define KEYPAD_ROOT_POINTERS +#endif + +#if CIRCUITPY_GAMEPAD || CIRCUITPY_GAMEPADSHIFT +// Scan gamepad every 32ms +#define CIRCUITPY_GAMEPAD_TICKS 0x1f +#define GAMEPAD_ROOT_POINTERS mp_obj_t gamepad_singleton; +#else +#define GAMEPAD_ROOT_POINTERS #endif #if CIRCUITPY_MATH @@ -962,6 +974,7 @@ struct _supervisor_allocation_node; vstr_t *repl_line; \ mp_obj_t rtc_time_source; \ GAMEPAD_ROOT_POINTERS \ + KEYPAD_ROOT_POINTERS \ mp_obj_t pew_singleton; \ BOARD_UART_ROOT_POINTER \ FLASH_ROOT_POINTERS \ diff --git a/shared-bindings/keypad/KeyMatrix.c b/shared-bindings/keypad/KeyMatrix.c new file mode 100644 index 0000000000..b6b09a9a0a --- /dev/null +++ b/shared-bindings/keypad/KeyMatrix.c @@ -0,0 +1,228 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "lib/utils/context_manager_helpers.h" +#include "py/runtime.h" +#include "shared-bindings/keypad/Event.h" +#include "shared-bindings/keypad/KeyMatrix.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" + +//| class KeyMatrix: +//| """Manage a 2D matrix of keys with row and column pins.""" +//| +//| def __init__(self, row_pins: Sequence[microcontroller.Pin], col_pins: Sequence[microcontroller.Pin], max_events: int = 16) -> None: +//| """ +//| Create a `Keys` object that will scan key matrix attached to the given row and column pins. +//| If the matrix uses diodes, the diode anodes should be connected to the column pins, +//| and the cathodes should be connected to the row pins. +//| +//| The keys are numbered sequentially from zero. A key number can be computed +//| by ``col * len(row_pins) + row``. +//| +//| :param Sequence[microcontroller.Pin] row_pins: The pins attached to rows. +//| :param Sequence[microcontroller.Pin] col_pins: The pins attached to rows. +//| :param int max_events: Size of key event queue: +//| maximum number of key transition events that are saved. +//| Must be >= 1. +//| If a new event arrives when the queue is full, the oldest event is discarded. +//| """ +//| ... + +STATIC mp_obj_t keypad_keymatrix_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + keypad_keymatrix_obj_t *self = m_new_obj(keypad_keymatrix_obj_t); + self->base.type = &keypad_keymatrix_type; + enum { ARG_row_pins, ARG_col_pins, ARG_max_events }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_row_pins, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_col_pins, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_BOOL }, + { MP_QSTR_max_events, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 16} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t row_pins = args[ARG_row_pins].u_obj; + // mp_obj_len() will be >= 0. + const size_t num_row_pins = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(row_pins)); + + mp_obj_t col_pins = args[ARG_col_pins].u_obj; + const size_t num_col_pins = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(col_pins)); + + if (args[ARG_max_events].u_int < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_max_events); + } + const size_t max_events = (size_t)args[ARG_max_events].u_int; + + mcu_pin_obj_t *row_pins_array[num_row_pins]; + mcu_pin_obj_t *col_pins_array[num_col_pins]; + + for (mp_uint_t i = 0; i < num_row_pins; i++) { + mcu_pin_obj_t *pin = + validate_obj_is_free_pin(mp_obj_subscr(row_pins, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL)); + row_pins_array[i] = pin; + } + + for (mp_uint_t i = 0; i < num_col_pins; i++) { + mcu_pin_obj_t *pin = + validate_obj_is_free_pin(mp_obj_subscr(col_pins, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL)); + col_pins_array[i] = pin; + } + + common_hal_keypad_keymatrix_construct(self, num_row_pins, row_pins_array, num_col_pins, col_pins_array, max_events); + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Stop scanning and release the pins.""" +//| ... +//| +STATIC mp_obj_t keypad_keymatrix_deinit(mp_obj_t self_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_keypad_keymatrix_deinit(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keymatrix_deinit_obj, keypad_keymatrix_deinit); + +//| def __enter__(self) -> KeyMatrix: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +STATIC mp_obj_t keypad_keymatrix___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_keypad_keymatrix_deinit(args[0]); + return MP_ROM_NONE; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(keypad_keymatrix___exit___obj, 4, 4, keypad_keymatrix___exit__); + +STATIC void check_for_deinit(keypad_keymatrix_obj_t *self) { + if (common_hal_keypad_keymatrix_deinited(self)) { + raise_deinited_error(); + } +} + +//| def next_event(self) -> Optional[Event]: +//| """Return the next key transition event. Return ``None` if no events are pending. +//| +//| Note that the queue size is limited; see ``max_events`` in the constructor. +//| If a new event arrives when the queue is full, the oldest event is discarded. +//| +//| :return: the next queued key transition `Event` +//| :rtype: Optional[Event] +//| """ +//| ... +//| +STATIC mp_obj_t keypad_keymatrix_next_event(mp_obj_t self_in, mp_obj_t event_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return common_hal_keypad_keymatrix_next_event(self); +} +MP_DEFINE_CONST_FUN_OBJ_2(keypad_keymatrix_next_event_obj, keypad_keymatrix_next_event); + +//| def clear_events(self) -> None: +//| """Clear any queued key transition events. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_keymatrix_clear_events(mp_obj_t self_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_keypad_keymatrix_clear_events(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keymatrix_clear_events_obj, keypad_keymatrix_clear_events); + +//| def pressed(self, key_num: int) -> None: +//| """Return ``True`` if the given key is pressed. This is a debounced read +//| of the key state which bypasses the event queue. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_keymatrix_pressed(mp_obj_t self_in, mp_obj_t key_num_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + mp_int_t key_num = mp_obj_get_int(key_num_in); + if (key_num < 0 || key_num >= common_hal_keypad_keymatrix_num_keys(self)) { + mp_raise_ValueError_varg(translate("%q out of range"), MP_QSTR_key_num); + } + + return mp_obj_new_bool(common_hal_keypad_keymatrix_pressed(self, (mp_uint_t)key_num)); +} +MP_DEFINE_CONST_FUN_OBJ_2(keypad_keymatrix_pressed_obj, keypad_keymatrix_pressed); + +//| def key_num(self, row: int, col: int) -> int: +//| """Return the key number for a given row and column. +//| The key number is calculated by `row * number_of_columns + col`. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_keymatrix_key_num(mp_obj_t self_in, mp_obj_t row_in, mp_obj_t col_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + const mp_int_t row = mp_obj_get_int(row_in); + if (row < 0 || row >= common_hal_keypad_keymatrix_num_rows(self)) { + mp_raise_ValueError_varg(translate("%q out of range"), MP_QSTR_row_num); + } + + const mp_int_t col = mp_obj_get_int(col_in); + if (col < 0 || col >= common_hal_keypad_keymatrix_num_cols(self)) { + mp_raise_ValueError_varg(translate("%q out of range"), MP_QSTR_col_num); + } + + return MP_OBJ_NEW_SMALL_INT( + (mp_int_t)common_hal_keypad_keymatrix_key_num(self, (mp_uint_t)row, (mp_uint_t)col)); +} +MP_DEFINE_CONST_FUN_OBJ_3(keypad_keymatrix_key_num_obj, keypad_keymatrix_key_num); + +STATIC const mp_rom_map_elem_t keypad_keymatrix_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&keypad_keymatrix_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&keypad_keymatrix___exit___obj) }, + + { MP_ROM_QSTR(MP_QSTR_clear_events), MP_ROM_PTR(&keypad_keymatrix_clear_events_obj) }, + { MP_ROM_QSTR(MP_QSTR_key_num), MP_ROM_PTR(&keypad_keymatrix_key_num_obj) }, + { MP_ROM_QSTR(MP_QSTR_next_event), MP_ROM_PTR(&keypad_keymatrix_next_event_obj) }, + { MP_ROM_QSTR(MP_QSTR_pressed), MP_ROM_PTR(&keypad_keymatrix_pressed_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(keypad_keymatrix_locals_dict, keypad_keymatrix_locals_dict_table); + +const mp_obj_type_t keypad_keymatrix_type = { + { &mp_type_type }, + .name = MP_QSTR_Keys, + .make_new = keypad_keymatrix_make_new, + .locals_dict = (mp_obj_t)&keypad_keymatrix_locals_dict, +}; diff --git a/shared-bindings/keypad/KeyMatrix.h b/shared-bindings/keypad/KeyMatrix.h new file mode 100644 index 0000000000..f0ce8761bc --- /dev/null +++ b/shared-bindings/keypad/KeyMatrix.h @@ -0,0 +1,49 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_KEYMATRIX_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_KEYMATRIX_H + +#include "py/objlist.h" +#include "shared-module/keypad/KeyMatrix.h" + +extern const mp_obj_type_t keypad_keymatrix_type; + +void common_hal_keypad_keymatrix_construct(keypad_keymatrix_obj_t *self, mp_uint_t num_row_pins, mcu_pin_obj_t *row_pins[], mp_uint_t num_col_pins, mcu_pin_obj_t *col_pins[], size_t max_events); +void common_hal_keypad_keymatrix_deinit(keypad_keymatrix_obj_t *self); +bool common_hal_keypad_keymatrix_deinited(keypad_keymatrix_obj_t *self); + +mp_uint_t common_hal_keypad_keymatrix_key_num(keypad_keymatrix_obj_t *self, mp_uint_t row, mp_uint_t col); + +mp_uint_t common_hal_keypad_keymatrix_num_keys(keypad_keymatrix_obj_t *self); +mp_uint_t common_hal_keypad_keymatrix_num_cols(keypad_keymatrix_obj_t *self); +mp_uint_t common_hal_keypad_keymatrix_num_rows(keypad_keymatrix_obj_t *self); + +bool common_hal_keypad_keymatrix_pressed(keypad_keymatrix_obj_t *self, mp_uint_t key_num); +mp_obj_t common_hal_keypad_keymatrix_next_event(keypad_keymatrix_obj_t *self); +void common_hal_keypad_keymatrix_clear_events(keypad_keymatrix_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_KEYMATRIX_H diff --git a/shared-bindings/keypad/Keys.c b/shared-bindings/keypad/Keys.c index e92d91ce14..a54cff0ba9 100644 --- a/shared-bindings/keypad/Keys.c +++ b/shared-bindings/keypad/Keys.c @@ -24,10 +24,12 @@ * THE SOFTWARE. */ +#include "lib/utils/context_manager_helpers.h" +#include "py/runtime.h" #include "shared-bindings/keypad/Event.h" #include "shared-bindings/keypad/Keys.h" #include "shared-bindings/microcontroller/Pin.h" -#include "py/runtime.h" +#include "shared-bindings/util.h" //| class Keys: //| """Manage a set of independent keys.""" @@ -88,9 +90,46 @@ STATIC mp_obj_t keypad_keys_make_new(const mp_obj_type_t *type, size_t n_args, c } common_hal_keypad_keys_construct(self, num_pins, pins_array, value_when_pressed, args[ARG_pull].u_bool, max_events); + return MP_OBJ_FROM_PTR(self); } +//| def deinit(self) -> None: +//| """Stop scanning and release the pins.""" +//| ... +//| +STATIC mp_obj_t keypad_keys_deinit(mp_obj_t self_in) { + keypad_keys_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_keypad_keys_deinit(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keys_deinit_obj, keypad_keys_deinit); + +//| def __enter__(self) -> Keys: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +STATIC mp_obj_t keypad_keys___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_keypad_keys_deinit(args[0]); + return MP_ROM_NONE; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(keypad_keys___exit___obj, 4, 4, keypad_keys___exit__); + +STATIC void check_for_deinit(keypad_keys_obj_t *self) { + if (common_hal_keypad_keys_deinited(self)) { + raise_deinited_error(); + } +} + //| def next_event(self) -> Optional[Event]: //| """Return the next key transition event. Return ``None` if no events are pending. //| @@ -104,6 +143,8 @@ STATIC mp_obj_t keypad_keys_make_new(const mp_obj_type_t *type, size_t n_args, c //| STATIC mp_obj_t keypad_keys_next_event(mp_obj_t self_in, mp_obj_t event_in) { keypad_keys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return common_hal_keypad_keys_next_event(self); } MP_DEFINE_CONST_FUN_OBJ_2(keypad_keys_next_event_obj, keypad_keys_next_event); @@ -115,6 +156,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(keypad_keys_next_event_obj, keypad_keys_next_event); //| STATIC mp_obj_t keypad_keys_clear_events(mp_obj_t self_in) { keypad_keys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); common_hal_keypad_keys_clear_events(self); return MP_ROM_NONE; @@ -129,6 +171,8 @@ MP_DEFINE_CONST_FUN_OBJ_1(keypad_keys_clear_events_obj, keypad_keys_clear_events //| STATIC mp_obj_t keypad_keys_pressed(mp_obj_t self_in, mp_obj_t key_num_in) { keypad_keys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + mp_int_t key_num = mp_obj_get_int(key_num_in); if (key_num < 0 || key_num >= common_hal_keypad_keys_num_keys(self)) { mp_raise_ValueError_varg(translate("%q out of range"), MP_QSTR_key_num); @@ -139,6 +183,10 @@ STATIC mp_obj_t keypad_keys_pressed(mp_obj_t self_in, mp_obj_t key_num_in) { MP_DEFINE_CONST_FUN_OBJ_2(keypad_keys_pressed_obj, keypad_keys_pressed); STATIC const mp_rom_map_elem_t keypad_keys_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&keypad_keys_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&keypad_keys___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_clear_events), MP_ROM_PTR(&keypad_keys_clear_events_obj) }, { MP_ROM_QSTR(MP_QSTR_next_event), MP_ROM_PTR(&keypad_keys_next_event_obj) }, { MP_ROM_QSTR(MP_QSTR_pressed), MP_ROM_PTR(&keypad_keys_pressed_obj) }, diff --git a/shared-bindings/keypad/Keys.h b/shared-bindings/keypad/Keys.h index e9ab0b91e5..f7ec2302d8 100644 --- a/shared-bindings/keypad/Keys.h +++ b/shared-bindings/keypad/Keys.h @@ -33,6 +33,8 @@ extern const mp_obj_type_t keypad_keys_type; void common_hal_keypad_keys_construct(keypad_keys_obj_t *self, mp_uint_t num_pins, mcu_pin_obj_t *pins[], bool value_when_pressed, bool pull, size_t max_events); +void common_hal_keypad_keys_deinit(keypad_keys_obj_t *self); +bool common_hal_keypad_keys_deinited(keypad_keys_obj_t *self); mp_uint_t common_hal_keypad_keys_num_keys(keypad_keys_obj_t *self); bool common_hal_keypad_keys_pressed(keypad_keys_obj_t *self, mp_uint_t key_num); @@ -40,6 +42,4 @@ bool common_hal_keypad_keys_pressed(keypad_keys_obj_t *self, mp_uint_t key_num); mp_obj_t common_hal_keypad_keys_next_event(keypad_keys_obj_t *self); void common_hal_keypad_keys_clear_events(keypad_keys_obj_t *self); -void keypad_keys_scan(keypad_keys_obj_t *self); - #endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_KEYS_H diff --git a/shared-module/keypad/KeyMatrix.c b/shared-module/keypad/KeyMatrix.c new file mode 100644 index 0000000000..4164de050f --- /dev/null +++ b/shared-module/keypad/KeyMatrix.c @@ -0,0 +1,225 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/keypad/Event.h" +#include "shared-bindings/keypad/KeyMatrix.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/util.h" +#include "supervisor/port.h" +#include "supervisor/shared/lock.h" +#include "supervisor/shared/tick.h" + +static supervisor_lock_t keypad_keymatrix_linked_list_lock; + +#define DEBOUNCE_TICKS (20) + +// Top bit of 16-bit event indicates pressed or released. Rest is key_num. +#define EVENT_PRESSED (1 << 15) +#define EVENT_RELEASED (0) +#define EVENT_KEY_NUM_MASK (~EVENT_PRESSED) + +static mp_uint_t row_col_to_key_num(keypad_keymatrix_obj_t *self, mp_uint_t row, mp_uint_t col) { + return row * self->col_digitalinouts->len + col; +} + +void common_hal_keypad_keymatrix_construct(keypad_keymatrix_obj_t *self, mp_uint_t num_row_pins, mcu_pin_obj_t *row_pins[], mp_uint_t num_col_pins, mcu_pin_obj_t *col_pins[], size_t max_events) { + + mp_obj_t row_dios[num_row_pins]; + for (size_t i = 0; i < num_row_pins; i++) { + digitalio_digitalinout_obj_t *dio = m_new_obj(digitalio_digitalinout_obj_t); + dio->base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(dio, row_pins[i]); + common_hal_digitalio_digitalinout_switch_to_input(dio, PULL_UP); + row_dios[i] = dio; + } + self->row_digitalinouts = mp_obj_new_tuple(num_col_pins, row_dios); + + mp_obj_t col_dios[num_col_pins]; + for (size_t i = 0; i < num_col_pins; i++) { + digitalio_digitalinout_obj_t *dio = m_new_obj(digitalio_digitalinout_obj_t); + dio->base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(dio, col_pins[i]); + common_hal_digitalio_digitalinout_switch_to_input(dio, PULL_UP); + col_dios[i] = dio; + } + self->col_digitalinouts = mp_obj_new_tuple(num_row_pins, col_dios); + + self->currently_pressed = (bool *)gc_alloc(sizeof(bool) * num_row_pins * num_col_pins, false, false); + self->previously_pressed = (bool *)gc_alloc(sizeof(bool) * num_row_pins * num_col_pins, false, false); + + // Event queue is 16-bit values. + ringbuf_alloc(self->encoded_events, max_events * 2, false); + + // Add self to the list of active Keys objects. + + supervisor_acquire_lock(&keypad_keymatrix_linked_list_lock); + self->next = MP_STATE_VM(keypad_keymatrix_linked_list); + MP_STATE_VM(keypad_keymatrix_linked_list) = self; + supervisor_release_lock(&keypad_keymatrix_linked_list_lock); +} + +void common_hal_keypad_keymatrix_deinit(keypad_keymatrix_obj_t *self) { + if (common_hal_keypad_keymatrix_deinited(self)) { + return; + } + + for (size_t row = 0; row < common_hal_keypad_keymatrix_num_rows(self); row++) { + common_hal_digitalio_digitalinout_deinit(self->row_digitalinouts->items[row]); + } + self->row_digitalinouts = MP_ROM_NONE; + + for (size_t col = 0; col < common_hal_keypad_keymatrix_num_cols(self); col++) { + common_hal_digitalio_digitalinout_deinit(self->col_digitalinouts->items[col]); + } + self->col_digitalinouts = MP_ROM_NONE; + + // Remove self from the list of active KeyMatrix objects. + + supervisor_acquire_lock(&keypad_keymatrix_linked_list_lock); + if (MP_STATE_VM(keypad_keymatrix_linked_list) == self) { + // I'm at the front; splice myself out. + MP_STATE_VM(keypad_keymatrix_linked_list) = self->next; + } else { + keypad_keymatrix_obj_t *current = MP_STATE_VM(keypad_keymatrix_linked_list); + while (current) { + if (current->next == self) { + // Splice myself out. + current->next = self->next; + break; + } + current = current->next; + } + } + supervisor_release_lock(&keypad_keymatrix_linked_list_lock); +} + +bool common_hal_keypad_keymatrix_deinited(keypad_keymatrix_obj_t *self) { + return self->row_digitalinouts == MP_ROM_NONE; +} + +size_t common_hal_keypad_keymatrix_num_keys(keypad_keymatrix_obj_t *self) { + return common_hal_keypad_keymatrix_num_rows(self) * common_hal_keypad_keymatrix_num_cols(self); +} + +size_t common_hal_keypad_keymatrix_num_rows(keypad_keymatrix_obj_t *self) { + return self->row_digitalinouts->len; +} + +size_t common_hal_keypad_keymatrix_num_cols(keypad_keymatrix_obj_t *self) { + return self->col_digitalinouts->len; +} + +bool common_hal_keypad_keymatrix_pressed(keypad_keymatrix_obj_t *self, mp_uint_t key_num) { + return self->currently_pressed[key_num]; +} + +mp_obj_t common_hal_keypad_keymatrix_next_event(keypad_keymatrix_obj_t *self) { + int encoded_event = ringbuf_get16(self->encoded_events); + if (encoded_event == -1) { + return MP_ROM_NONE; + } + + keypad_event_obj_t *event = m_new_obj(keypad_event_obj_t); + self->base.type = &keypad_event_type; + common_hal_keypad_event_construct(event, encoded_event & EVENT_KEY_NUM_MASK, encoded_event & EVENT_PRESSED); + return MP_OBJ_FROM_PTR(event); +} + +void common_hal_keypad_keymatrix_clear_events(keypad_keymatrix_obj_t *self) { + ringbuf_clear(self->encoded_events); +} + +mp_uint_t common_hal_keypad_keymatrix_key_num(keypad_keymatrix_obj_t *self, mp_uint_t row, mp_uint_t col) { + return row_col_to_key_num(self, row, col); +} + +static void keypad_keymatrix_scan(keypad_keymatrix_obj_t *self) { + uint64_t now = port_get_raw_ticks(NULL); + if (now - self->last_scan_ticks < DEBOUNCE_TICKS) { + // Too soon. Wait longer to debounce. + return; + } + + self->last_scan_ticks = now; + + // On entry, all pins are set to inputs with a pull-up. + for (size_t row = 0; row < common_hal_keypad_keymatrix_num_rows(self); row++) { + // Switch this row to an output and set to low. + common_hal_digitalio_digitalinout_switch_to_output( + self->row_digitalinouts->items[row], false, DRIVE_MODE_PUSH_PULL); + + for (size_t col = 0; col < common_hal_keypad_keymatrix_num_cols(self); col++) { + mp_uint_t key_num = row_col_to_key_num(self, row, col); + const bool previous = self->currently_pressed[key_num]; + self->previously_pressed[key_num] = previous; + + // Get the current state, by reading whether the col got pulled down or not. + const bool current = + common_hal_digitalio_digitalinout_get_value(self->col_digitalinouts->items[key_num]); + self->currently_pressed[key_num] = current; + + // Record any transitions. + if (previous != current) { + if (ringbuf_num_empty(self->encoded_events) == 0) { + // Discard oldest if full. + ringbuf_get16(self->encoded_events); + } + ringbuf_put16(self->encoded_events, key_num | (current ? EVENT_PRESSED : EVENT_RELEASED)); + } + + } + + // Switch the row back to an input, pulled up. + common_hal_digitalio_digitalinout_switch_to_input(self->row_digitalinouts->items[row], PULL_UP); + } +} + +void keypad_keymatrix_tick(void) { + // Fast path. + if (!MP_STATE_VM(keypad_keymatrix_linked_list)) { + return; + } + + if (supervisor_try_lock(&keypad_keymatrix_linked_list_lock)) { + keypad_keymatrix_obj_t *keypad_keymatrix = MP_STATE_VM(keypad_keymatrix_linked_list); + while (keypad_keymatrix) { + keypad_keymatrix_scan(keypad_keymatrix); + keypad_keymatrix = keypad_keymatrix->next; + } + supervisor_release_lock(&keypad_keymatrix_linked_list_lock); + } +} + +void keypad_keymatrix_reset(void) { + if (MP_STATE_VM(keypad_keymatrix_linked_list)) { + supervisor_disable_tick(); + } + + MP_STATE_VM(keypad_keys_linked_list) = NULL; + keypad_keymatrix_linked_list_lock = false; +} diff --git a/shared-module/keypad/KeyMatrix.h b/shared-module/keypad/KeyMatrix.h new file mode 100644 index 0000000000..d598f3941e --- /dev/null +++ b/shared-module/keypad/KeyMatrix.h @@ -0,0 +1,51 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYMATRIX_H +#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYMATRIX_H + +#include "common-hal/digitalio/DigitalInOut.h" + +#include "py/obj.h" +#include "py/objtuple.h" +#include "py/ringbuf.h" + +typedef struct _keypad_keymatrix_obj_t { + mp_obj_base_t base; + mp_obj_tuple_t *row_digitalinouts; + mp_obj_tuple_t *col_digitalinouts; + uint64_t last_scan_ticks; + bool *previously_pressed; + bool *currently_pressed; + ringbuf_t *encoded_events; + // Keep a linked list of active KeyMatrix objects. + struct _keypad_keymatrix_obj_t *next; +} keypad_keymatrix_obj_t; + +void keypad_keymatrix_tick(); +void keypad_keymatrix_reset(); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYMATRIX_H diff --git a/shared-module/keypad/Keys.c b/shared-module/keypad/Keys.c index 582668bce9..d7e0288907 100644 --- a/shared-module/keypad/Keys.c +++ b/shared-module/keypad/Keys.c @@ -25,11 +25,15 @@ */ #include "py/gc.h" +#include "py/runtime.h" #include "shared-bindings/keypad/Event.h" #include "shared-bindings/keypad/Keys.h" #include "shared-bindings/digitalio/DigitalInOut.h" -#include "py/runtime.h" #include "supervisor/port.h" +#include "supervisor/shared/lock.h" +#include "supervisor/shared/tick.h" + +static supervisor_lock_t keypad_keys_linked_list_lock; #define DEBOUNCE_TICKS (20) @@ -58,13 +62,73 @@ void common_hal_keypad_keys_construct(keypad_keys_obj_t *self, mp_uint_t num_pin // Event queue is 16-bit values. ringbuf_alloc(self->encoded_events, max_events * 2, false); + + // Add self to the list of active Keys objects. + + supervisor_acquire_lock(&keypad_keys_linked_list_lock); + self->next = MP_STATE_VM(keypad_keys_linked_list); + MP_STATE_VM(keypad_keys_linked_list) = self; + supervisor_release_lock(&keypad_keys_linked_list_lock); +} + +void common_hal_keypad_keys_deinit(keypad_keys_obj_t *self) { + if (common_hal_keypad_keys_deinited(self)) { + return; + } + + for (size_t key = 0; key < common_hal_keypad_keys_num_keys(self); key++) { + common_hal_digitalio_digitalinout_deinit(self->digitalinouts->items[key]); + } + self->digitalinouts = MP_ROM_NONE; + + // Remove self from the list of active Keys objects. + + supervisor_acquire_lock(&keypad_keys_linked_list_lock); + if (MP_STATE_VM(keypad_keys_linked_list) == self) { + // I'm at the front; splice myself out. + MP_STATE_VM(keypad_keys_linked_list) = self->next; + } else { + keypad_keys_obj_t *current = MP_STATE_VM(keypad_keys_linked_list); + while (current) { + if (current->next == self) { + // Splice myself out. + current->next = self->next; + break; + } + current = current->next; + } + } + supervisor_release_lock(&keypad_keys_linked_list_lock); +} + +bool common_hal_keypad_keys_deinited(keypad_keys_obj_t *self) { + return self->digitalinouts == MP_ROM_NONE; } size_t common_hal_keypad_keys_num_keys(keypad_keys_obj_t *self) { return self->digitalinouts->len; } +bool common_hal_keypad_keys_pressed(keypad_keys_obj_t *self, mp_uint_t key_num) { + return self->currently_pressed[key_num]; +} -void keypad_keys_scan(keypad_keys_obj_t *self) { +mp_obj_t common_hal_keypad_keys_next_event(keypad_keys_obj_t *self) { + int encoded_event = ringbuf_get16(self->encoded_events); + if (encoded_event == -1) { + return MP_ROM_NONE; + } + + keypad_event_obj_t *event = m_new_obj(keypad_event_obj_t); + self->base.type = &keypad_event_type; + common_hal_keypad_event_construct(event, encoded_event & EVENT_KEY_NUM_MASK, encoded_event & EVENT_PRESSED); + return MP_OBJ_FROM_PTR(event); +} + +void common_hal_keypad_keys_clear_events(keypad_keys_obj_t *self) { + ringbuf_clear(self->encoded_events); +} + +static void keypad_keys_scan(keypad_keys_obj_t *self) { uint64_t now = port_get_raw_ticks(NULL); if (now - self->last_scan_ticks < DEBOUNCE_TICKS) { // Too soon. Wait longer to debounce. @@ -94,22 +158,27 @@ void keypad_keys_scan(keypad_keys_obj_t *self) { } } -bool common_hal_keypad_keys_pressed(keypad_keys_obj_t *self, mp_uint_t key_num) { - return self->currently_pressed[key_num]; -} - -mp_obj_t common_hal_keypad_keys_next_event(keypad_keys_obj_t *self) { - int encoded_event = ringbuf_get16(self->encoded_events); - if (encoded_event == -1) { - return MP_ROM_NONE; +void keypad_keys_tick(void) { + // Fast path. + if (!MP_STATE_VM(keypad_keys_linked_list)) { + return; } - keypad_event_obj_t *event = m_new_obj(keypad_event_obj_t); - self->base.type = &keypad_event_type; - common_hal_keypad_event_construct(event, encoded_event & EVENT_KEY_NUM_MASK, encoded_event & EVENT_PRESSED); - return MP_OBJ_FROM_PTR(event); + if (supervisor_try_lock(&keypad_keys_linked_list_lock)) { + keypad_keys_obj_t *keypad_keys = MP_STATE_VM(keypad_keys_linked_list); + while (keypad_keys) { + keypad_keys_scan(keypad_keys); + keypad_keys = keypad_keys->next; + } + supervisor_release_lock(&keypad_keys_linked_list_lock); + } } -void common_hal_keypad_keys_clear_events(keypad_keys_obj_t *self) { - ringbuf_clear(self->encoded_events); +void keypad_keys_reset(void) { + if (MP_STATE_VM(keypad_keys_linked_list)) { + supervisor_disable_tick(); + } + + MP_STATE_VM(keypad_keys_linked_list) = NULL; + keypad_keys_linked_list_lock = false; } diff --git a/shared-module/keypad/Keys.h b/shared-module/keypad/Keys.h index 67822dd6d9..556f465a98 100644 --- a/shared-module/keypad/Keys.h +++ b/shared-module/keypad/Keys.h @@ -33,7 +33,7 @@ #include "py/objtuple.h" #include "py/ringbuf.h" -typedef struct { +typedef struct _keypad_keys_obj_t { mp_obj_base_t base; mp_obj_tuple_t *digitalinouts; uint64_t last_scan_ticks; @@ -41,7 +41,11 @@ typedef struct { bool *previously_pressed; bool *currently_pressed; ringbuf_t *encoded_events; + // Keep a linked list of active Keys objects. + struct _keypad_keys_obj_t *next; } keypad_keys_obj_t; +void keypad_keys_tick(); +void keypad_keys_reset(); #endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYS_H diff --git a/shared-module/keypad/__init__.c b/shared-module/keypad/__init__.c index e69de29bb2..9b08dc9bb5 100644 --- a/shared-module/keypad/__init__.c +++ b/shared-module/keypad/__init__.c @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-module/keypad/Keys.h" +#include "shared-module/keypad/KeyMatrix.h" + +void keypad_tick(void) { + keypad_keys_tick(); + keypad_keymatrix_tick(); +} + +void keypad_reset(void) { + keypad_keys_reset(); + keypad_keymatrix_reset(); +} diff --git a/shared-module/keypad/__init__.h b/shared-module/keypad/__init__.h index 4220a3df63..2dc3fdca8e 100644 --- a/shared-module/keypad/__init__.h +++ b/shared-module/keypad/__init__.h @@ -27,4 +27,7 @@ #ifndef SHARED_MODULE_KEYPAD_H #define SHARED_MODULE_KEYPAD_H +void keypad_tick(void); +void keypad_reset(void); + #endif // SHARED_MODULE_KEYPAD_H diff --git a/supervisor/shared/lock.c b/supervisor/shared/lock.c new file mode 100644 index 0000000000..36971badad --- /dev/null +++ b/supervisor/shared/lock.c @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-bindings/microcontroller/__init__.h" +#include "supervisor/shared/lock.h" + +void supervisor_acquire_lock(supervisor_lock_t *lock) { + while (!supervisor_try_lock(lock)) { + RUN_BACKGROUND_TASKS; + } +} + +bool supervisor_try_lock(supervisor_lock_t *lock) { + bool grabbed_lock = false; + common_hal_mcu_disable_interrupts(); + if (!*lock) { + *lock = true; + grabbed_lock = true; + } + common_hal_mcu_enable_interrupts(); + return grabbed_lock; +} + +void supervisor_release_lock(supervisor_lock_t *lock) { + common_hal_mcu_disable_interrupts(); + *lock = false; + common_hal_mcu_enable_interrupts(); +} diff --git a/supervisor/shared/lock.h b/supervisor/shared/lock.h new file mode 100644 index 0000000000..89f81cfdfb --- /dev/null +++ b/supervisor/shared/lock.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SUPERVISOR_LOCK_H +#define MICROPY_INCLUDED_SUPERVISOR_LOCK_H + +typedef volatile bool supervisor_lock_t; + +void supervisor_acquire_lock(supervisor_lock_t *lock); +bool supervisor_try_lock(supervisor_lock_t *lock); +void supervisor_release_lock(supervisor_lock_t *lock); + +#endif // MICROPY_INCLUDED_SUPERVISOR_LOCK_H diff --git a/supervisor/shared/tick.c b/supervisor/shared/tick.c index f40151988e..77cdf8aba2 100644 --- a/supervisor/shared/tick.c +++ b/supervisor/shared/tick.c @@ -53,6 +53,10 @@ #include "shared-module/gamepadshift/__init__.h" #endif +#if CIRCUITPY_KEYPAD +#include "shared-module/keypad/__init__.h" +#endif + #if CIRCUITPY_NETWORK #include "shared-module/network/__init__.h" #endif @@ -108,9 +112,11 @@ void supervisor_tick(void) { #if CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS > 0 filesystem_tick(); #endif + #ifdef CIRCUITPY_AUTORELOAD_DELAY_MS autoreload_tick(); #endif + #ifdef CIRCUITPY_GAMEPAD_TICKS if (!(port_get_raw_ticks(NULL) & CIRCUITPY_GAMEPAD_TICKS)) { #if CIRCUITPY_GAMEPAD @@ -121,6 +127,11 @@ void supervisor_tick(void) { #endif } #endif + + #if CIRCUITPY_KEYPAD + keypad_tick(); + #endif + background_callback_add(&tick_callback, supervisor_background_tasks, NULL); } diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index 0815a9ffc4..d32436cdb0 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -7,6 +7,7 @@ SRC_SUPERVISOR = \ supervisor/shared/cpu.c \ supervisor/shared/filesystem.c \ supervisor/shared/flash.c \ + supervisor/shared/lock.c \ supervisor/shared/memory.c \ supervisor/shared/micropython.c \ supervisor/shared/safe_mode.c \