diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index b30588bad9..b8634ffac2 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -83,10 +83,22 @@ msgstr "" msgid "%q indices must be integers, not %s" msgstr "" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "" @@ -95,7 +107,11 @@ msgstr "" msgid "%q must be 1-255" msgstr "" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "" @@ -111,6 +127,10 @@ msgstr "" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "" @@ -119,6 +139,15 @@ msgstr "" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + +#: shared-bindings/keypad/KeyMatrix.c shared-bindings/keypad/Keys.c +#: shared-bindings/keypad/ShiftRegisterKeys.c +msgid "%q must store bytes" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" 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/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk b/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk index 902e9594e3..fb65d7cd0a 100644 --- a/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk +++ b/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk @@ -14,6 +14,7 @@ LONGINT_IMPL = NONE CIRCUITPY_BUSDEVICE = 1 CIRCUITPY_DISPLAYIO = 0 +CIRCUITPY_KEYPAD = 0 # Include these Python libraries in firmware. FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_CircuitPlayground diff --git a/ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.mk b/ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.mk index 68fb51ab00..3a97c39830 100644 --- a/ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.mk +++ b/ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.mk @@ -11,6 +11,7 @@ EXTERNAL_FLASH_DEVICES = "S25FL216K, GD25Q16C" # Turn off features and optimizations for displayio build to make room for additional frozen libs. LONGINT_IMPL = NONE +CIRCUITPY_KEYPAD = 0 CIRCUITPY_PIXELBUF = 0 CIRCUITPY_ROTARYIO = 0 CIRCUITPY_RTC = 0 diff --git a/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.mk index 300419fa19..d7230df4bb 100644 --- a/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.mk @@ -12,7 +12,6 @@ LONGINT_IMPL = NONE # To keep the build small CIRCUITPY_AUDIOBUSIO = 0 -CIRCUITPY_GAMEPAD = 0 # Include these Python libraries in firmware. FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LIS3DH diff --git a/ports/atmel-samd/boards/kicksat-sprite/mpconfigboard.mk b/ports/atmel-samd/boards/kicksat-sprite/mpconfigboard.mk index d7b8a4bb1e..c2c583022c 100644 --- a/ports/atmel-samd/boards/kicksat-sprite/mpconfigboard.mk +++ b/ports/atmel-samd/boards/kicksat-sprite/mpconfigboard.mk @@ -15,6 +15,7 @@ CIRCUITPY_AUDIOMP3 = 0 CIRCUITPY_BLEIO_HCI = 0 CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_FRAMEBUFFERIO = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_MSGPACK = 0 CIRCUITPY_PS2IO = 0 CIRCUITPY_RGBMATRIX = 0 diff --git a/ports/atmel-samd/boards/pycubed/mpconfigboard.mk b/ports/atmel-samd/boards/pycubed/mpconfigboard.mk index 1eb3cd5239..bbed46dbed 100644 --- a/ports/atmel-samd/boards/pycubed/mpconfigboard.mk +++ b/ports/atmel-samd/boards/pycubed/mpconfigboard.mk @@ -16,8 +16,8 @@ CIRCUITPY_DRIVE_LABEL = "PYCUBED" CIRCUITPY_AUDIOBUSIO = 0 CIRCUITPY_BITMAPTOOLS = 0 CIRCUITPY_DISPLAYIO = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_FRAMEBUFFERIO = 0 -CIRCUITPY_GAMEPAD = 0 CIRCUITPY_RGBMATRIX = 0 CIRCUITPY_PS2IO = 0 diff --git a/ports/atmel-samd/boards/pycubed_mram/mpconfigboard.mk b/ports/atmel-samd/boards/pycubed_mram/mpconfigboard.mk index 0c459c309c..ccf3050d11 100644 --- a/ports/atmel-samd/boards/pycubed_mram/mpconfigboard.mk +++ b/ports/atmel-samd/boards/pycubed_mram/mpconfigboard.mk @@ -17,7 +17,7 @@ CIRCUITPY_AUDIOBUSIO = 0 CIRCUITPY_BITMAPTOOLS = 0 CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_FRAMEBUFFERIO = 0 -CIRCUITPY_GAMEPAD = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_RGBMATRIX = 0 CIRCUITPY_PS2IO = 0 diff --git a/ports/atmel-samd/boards/stringcar_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/stringcar_m0_express/mpconfigboard.mk index 53aeda3c50..a863248ca7 100644 --- a/ports/atmel-samd/boards/stringcar_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/stringcar_m0_express/mpconfigboard.mk @@ -11,4 +11,6 @@ SPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = AT25SF161 LONGINT_IMPL = MPZ +CIRCUITPY_KEYPAD = 0 + CIRCUITPY_BITBANG_APA102 = 1 diff --git a/ports/atmel-samd/boards/winterbloom_big_honking_button/mpconfigboard.mk b/ports/atmel-samd/boards/winterbloom_big_honking_button/mpconfigboard.mk index 1c96e1d235..7364de0cfe 100644 --- a/ports/atmel-samd/boards/winterbloom_big_honking_button/mpconfigboard.mk +++ b/ports/atmel-samd/boards/winterbloom_big_honking_button/mpconfigboard.mk @@ -22,7 +22,6 @@ CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_AUDIOBUSIO = 0 CIRCUITPY_BLEIO = 0 CIRCUITPY_DISPLAYIO = 0 -CIRCUITPY_GAMEPAD = 0 CIRCUITPY_I2CPERIPHERAL = 0 CIRCUITPY_TOUCHIO = 0 CIRCUITPY_RGBMATRIX = 0 diff --git a/ports/atmel-samd/boards/winterbloom_sol/mpconfigboard.mk b/ports/atmel-samd/boards/winterbloom_sol/mpconfigboard.mk index 81956a0b80..bc4fff5018 100644 --- a/ports/atmel-samd/boards/winterbloom_sol/mpconfigboard.mk +++ b/ports/atmel-samd/boards/winterbloom_sol/mpconfigboard.mk @@ -21,7 +21,6 @@ CIRCUITPY_BITMAPTOOLS = 0 CIRCUITPY_BLEIO_HCI = 0 CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_FRAMEBUFFERIO = 0 -CIRCUITPY_GAMEPAD = 0 CIRCUITPY_I2CPERIPHERAL = 0 CIRCUITPY_TOUCHIO = 0 CIRCUITPY_RGBMATRIX = 0 diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index d0ef189f41..cb8fae6f07 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -50,6 +50,7 @@ CIRCUITPY_FRAMEBUFFERIO ?= 0 CIRCUITPY_FREQUENCYIO ?= 0 CIRCUITPY_I2CPERIPHERAL ?= 0 CIRCUITPY_JSON ?= 0 +CIRCUITPY_KEYPAD ?= 0 CIRCUITPY_MSGPACK ?= 0 CIRCUITPY_RE ?= 0 CIRCUITPY_SDCARDIO ?= 0 diff --git a/ports/nrf/boards/pca10100/mpconfigboard.mk b/ports/nrf/boards/pca10100/mpconfigboard.mk index 9fae5ccc4d..3a7407861b 100644 --- a/ports/nrf/boards/pca10100/mpconfigboard.mk +++ b/ports/nrf/boards/pca10100/mpconfigboard.mk @@ -18,6 +18,7 @@ CIRCUITPY_FRAMEBUFFERIO = 0 CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 CIRCUITPY_JSON = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_MSGPACK = 0 CIRCUITPY_NEOPIXEL_WRITE = 0 CIRCUITPY_NVM = 0 diff --git a/ports/nrf/boards/simmel/mpconfigboard.mk b/ports/nrf/boards/simmel/mpconfigboard.mk index e235fd1051..57f83fd3d2 100644 --- a/ports/nrf/boards/simmel/mpconfigboard.mk +++ b/ports/nrf/boards/simmel/mpconfigboard.mk @@ -20,7 +20,7 @@ CIRCUITPY_COUNTIO = 0 CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_ERRNO = 0 CIRCUITPY_FRAMEBUFFERIO = 0 -CIRCUITPY_GAMEPAD = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_MSGPACK = 0 CIRCUITPY_NEOPIXEL_WRITE = 0 CIRCUITPY_NVM = 0 diff --git a/ports/stm/boards/espruino_pico/mpconfigboard.mk b/ports/stm/boards/espruino_pico/mpconfigboard.mk index bc415f07ec..3f3e81d553 100644 --- a/ports/stm/boards/espruino_pico/mpconfigboard.mk +++ b/ports/stm/boards/espruino_pico/mpconfigboard.mk @@ -22,6 +22,7 @@ CIRCUITPY_AUDIOPWMIO = 0 CIRCUITPY_BUSDEVICE = 0 CIRCUITPY_BITMAPTOOLS = 0 CIRCUITPY_FRAMEBUFFERIO = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_MIDI = 0 CIRCUITPY_MSGPACK = 0 CIRCUITPY_ULAB = 0 diff --git a/ports/stm/boards/meowbit_v121/mpconfigboard.mk b/ports/stm/boards/meowbit_v121/mpconfigboard.mk index dc44095a31..d846755c28 100644 --- a/ports/stm/boards/meowbit_v121/mpconfigboard.mk +++ b/ports/stm/boards/meowbit_v121/mpconfigboard.mk @@ -20,4 +20,5 @@ LD_FILE = boards/STM32F401xe_boot.ld # For debugging - also comment BOOTLOADER_OFFSET and BOARD_VTOR_DEFER # LD_FILE = boards/STM32F401xe_fs.ld +CIRCUITPY_BLEIO_HCI = 0 CIRCUITPY_ULAB = 0 diff --git a/ports/stm/boards/pyb_nano_v2/mpconfigboard.mk b/ports/stm/boards/pyb_nano_v2/mpconfigboard.mk index 99af948d7b..d1a627569c 100644 --- a/ports/stm/boards/pyb_nano_v2/mpconfigboard.mk +++ b/ports/stm/boards/pyb_nano_v2/mpconfigboard.mk @@ -16,5 +16,6 @@ LD_FILE = boards/STM32F411_fs.ld # Too big for the flash CIRCUITPY_AUDIOCORE = 0 CIRCUITPY_AUDIOPWMIO = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_MIDI = 0 CIRCUITPY_MSGPACK = 0 diff --git a/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk b/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk index 04c8cc2d45..c8dc80a70c 100644 --- a/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk +++ b/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk @@ -15,5 +15,6 @@ LD_FILE = boards/STM32F411_fs.ld # Too big for the flash CIRCUITPY_AUDIOCORE = 0 CIRCUITPY_AUDIOPWMIO = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_MIDI = 0 CIRCUITPY_MSGPACK = 0 diff --git a/py/argcheck.c b/py/argcheck.c index eb5cd87f8f..0d14c5eff2 100644 --- a/py/argcheck.c +++ b/py/argcheck.c @@ -162,3 +162,56 @@ NORETURN void mp_arg_error_unimpl_kw(void) { mp_raise_NotImplementedError(MP_ERROR_TEXT("keyword argument(s) not yet implemented - use normal args instead")); } #endif + + +mp_int_t mp_arg_validate_int_min(mp_int_t i, mp_int_t min, qstr arg_name) { + if (i < min) { + mp_raise_ValueError_varg(translate("%q must be >= %d"), arg_name, min); + } + return i; +} + +mp_int_t mp_arg_validate_int_max(mp_int_t i, mp_int_t max, qstr arg_name) { + if (i > max) { + mp_raise_ValueError_varg(translate("%q must <= %d"), arg_name, max); + } + return i; +} + +mp_int_t mp_arg_validate_int_range(mp_int_t i, mp_int_t min, mp_int_t max, qstr arg_name) { + if (i < min || i > max) { + mp_raise_ValueError_varg(translate("%q must be %d-%d"), arg_name, min, max); + } + return i; +} + +mp_float_t mp_arg_validate_obj_float_non_negative(mp_obj_t float_in, mp_float_t default_for_null, qstr arg_name) { + const mp_float_t f = (float_in == MP_OBJ_NULL) + ? default_for_null + : mp_obj_get_float(float_in); + if (f <= (mp_float_t)0.0) { + mp_raise_ValueError_varg(translate("%q must be >= 0"), arg_name); + } + return f; +} + +size_t mp_arg_validate_length_with_name(mp_int_t i, size_t length, qstr arg_name, qstr length_name) { + if (i != (mp_int_t)length) { + mp_raise_ValueError_varg(translate("%q length must be %q"), MP_QSTR_pressed, MP_QSTR_num_keys); + } + return (size_t)i; +} + +mp_obj_t mp_arg_validate_type(mp_obj_t obj, const mp_obj_type_t *type, qstr arg_name) { + if (!mp_obj_is_type(obj, type)) { + mp_raise_TypeError_varg(translate("%q must of type %q"), arg_name, type->name); + } + return obj; +} + +mp_obj_t mp_arg_validate_string(mp_obj_t obj, qstr arg_name) { + if (!mp_obj_is_str(obj)) { + mp_raise_TypeError_varg(translate("%q must be a string"), arg_name); + } + return obj; +} diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 41f8d71abd..9756329ac5 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -178,6 +178,9 @@ endif ifeq ($(CIRCUITPY_FRAMEBUFFERIO),1) SRC_PATTERNS += framebufferio/% endif +ifeq ($(CIRCUITPY__EVE),1) +SRC_PATTERNS += _eve/% +endif ifeq ($(CIRCUITPY_FREQUENCYIO),1) SRC_PATTERNS += frequencyio/% endif @@ -196,12 +199,12 @@ endif ifeq ($(CIRCUITPY_IPADDRESS),1) SRC_PATTERNS += ipaddress/% endif +ifeq ($(CIRCUITPY_KEYPAD),1) +SRC_PATTERNS += keypad/% +endif ifeq ($(CIRCUITPY_MATH),1) SRC_PATTERNS += math/% endif -ifeq ($(CIRCUITPY__EVE),1) -SRC_PATTERNS += _eve/% -endif ifeq ($(CIRCUITPY_MEMORYMONITOR),1) SRC_PATTERNS += memorymonitor/% endif @@ -512,6 +515,12 @@ SRC_SHARED_MODULE_ALL = \ framebufferio/__init__.c \ ipaddress/IPv4Address.c \ ipaddress/__init__.c \ + keypad/__init__.c \ + keypad/Event.c \ + keypad/EventQueue.c \ + keypad/KeyMatrix.c \ + keypad/ShiftRegisterKeys.c \ + keypad/Keys.c \ sdcardio/SDCard.c \ sdcardio/__init__.c \ gamepad/GamePad.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 009b428645..fdb78d885c 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -440,6 +440,13 @@ extern const struct _mp_obj_module_t espidf_module; #define ESPIDF_MODULE #endif +#if CIRCUITPY__EVE +extern const struct _mp_obj_module_t _eve_module; +#define _EVE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__eve), (mp_obj_t)&_eve_module }, +#else +#define _EVE_MODULE +#endif + #if CIRCUITPY_FRAMEBUFFERIO extern const struct _mp_obj_module_t framebufferio_module; #define FRAMEBUFFERIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_framebufferio), (mp_obj_t)&framebufferio_module }, @@ -523,6 +530,23 @@ extern const struct _mp_obj_module_t ipaddress_module; #define JSON_MODULE #endif +#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_scanners_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 extern const struct _mp_obj_module_t math_module; #define MATH_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_math), (mp_obj_t)&math_module }, @@ -530,13 +554,6 @@ extern const struct _mp_obj_module_t math_module; #define MATH_MODULE #endif -#if CIRCUITPY__EVE -extern const struct _mp_obj_module_t _eve_module; -#define _EVE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__eve), (mp_obj_t)&_eve_module }, -#else -#define _EVE_MODULE -#endif - #if CIRCUITPY_MEMORYMONITOR extern const struct _mp_obj_module_t memorymonitor_module; #define MEMORYMONITOR_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_memorymonitor), (mp_obj_t)&memorymonitor_module }, @@ -877,6 +894,7 @@ extern const struct _mp_obj_module_t msgpack_module; VECTORIO_MODULE \ ERRNO_MODULE \ ESPIDF_MODULE \ + _EVE_MODULE \ FRAMEBUFFERIO_MODULE \ FREQUENCYIO_MODULE \ GAMEPAD_MODULE \ @@ -886,8 +904,8 @@ extern const struct _mp_obj_module_t msgpack_module; IPADDRESS_MODULE \ IMAGECAPTURE_MODULE \ JSON_MODULE \ + KEYPAD_MODULE \ MATH_MODULE \ - _EVE_MODULE \ MEMORYMONITOR_MODULE \ MICROCONTROLLER_MODULE \ MSGPACK_MODULE \ @@ -954,6 +972,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/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 077470d936..f4075f0d5a 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -178,10 +178,13 @@ CFLAGS += -DCIRCUITPY_ERRNO=$(CIRCUITPY_ERRNO) CIRCUITPY_ESPIDF ?= 0 CFLAGS += -DCIRCUITPY_ESPIDF=$(CIRCUITPY_ESPIDF) +CIRCUITPY__EVE ?= 0 +CFLAGS += -DCIRCUITPY__EVE=$(CIRCUITPY__EVE) + CIRCUITPY_FREQUENCYIO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_FREQUENCYIO=$(CIRCUITPY_FREQUENCYIO) -CIRCUITPY_GAMEPAD ?= $(CIRCUITPY_FULL_BUILD) +CIRCUITPY_GAMEPAD ?= 0 CFLAGS += -DCIRCUITPY_GAMEPAD=$(CIRCUITPY_GAMEPAD) CIRCUITPY_GAMEPADSHIFT ?= 0 @@ -199,12 +202,12 @@ CFLAGS += -DCIRCUITPY_IPADDRESS=$(CIRCUITPY_IPADDRESS) CIRCUITPY_JSON ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_JSON=$(CIRCUITPY_JSON) +CIRCUITPY_KEYPAD ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_KEYPAD=$(CIRCUITPY_KEYPAD) + CIRCUITPY_MATH ?= 1 CFLAGS += -DCIRCUITPY_MATH=$(CIRCUITPY_MATH) -CIRCUITPY__EVE ?= 0 -CFLAGS += -DCIRCUITPY__EVE=$(CIRCUITPY__EVE) - CIRCUITPY_MEMORYMONITOR ?= 0 CFLAGS += -DCIRCUITPY_MEMORYMONITOR=$(CIRCUITPY_MEMORYMONITOR) diff --git a/py/runtime.h b/py/runtime.h index 332718b54e..fc6434ca72 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -91,6 +91,14 @@ void mp_arg_parse_all_kw_array(size_t n_pos, size_t n_kw, const mp_obj_t *args, NORETURN void mp_arg_error_terse_mismatch(void); NORETURN void mp_arg_error_unimpl_kw(void); +mp_int_t mp_arg_validate_int_min(mp_int_t i, mp_int_t min, qstr arg_name); +mp_int_t mp_arg_validate_int_max(mp_int_t i, mp_int_t j, qstr arg_name); +mp_int_t mp_arg_validate_int_range(mp_int_t i, mp_int_t min, mp_int_t max, qstr arg_name); +mp_float_t mp_arg_validate_obj_float_non_negative(mp_obj_t float_in, mp_float_t default_for_null, qstr arg_name); +size_t mp_arg_validate_length_with_name(mp_int_t i, size_t length, qstr arg_name, qstr length_name); +mp_obj_t mp_arg_validate_type(mp_obj_t obj, const mp_obj_type_t *type, qstr arg_name); +mp_obj_t mp_arg_validate_string(mp_obj_t obj, qstr arg_name); + static inline mp_obj_dict_t *PLACE_IN_ITCM(mp_locals_get)(void) { return MP_STATE_THREAD(dict_locals); } diff --git a/shared-bindings/imagecapture/ParallelImageCapture.c b/shared-bindings/imagecapture/ParallelImageCapture.c index 8e8991339b..59a6f256e6 100644 --- a/shared-bindings/imagecapture/ParallelImageCapture.c +++ b/shared-bindings/imagecapture/ParallelImageCapture.c @@ -43,7 +43,7 @@ //| clock: microcontroller.Pin, //| vsync: Optional[microcontroller.Pin], //| href: Optional[microcontroller.Pin], -//| ): +//| ) -> None: //| """Create a parallel image capture object //| //| :param List[microcontroller.Pin] data_pins: The data pins. diff --git a/shared-bindings/keypad/Event.c b/shared-bindings/keypad/Event.c new file mode 100644 index 0000000000..11cd0c120c --- /dev/null +++ b/shared-bindings/keypad/Event.c @@ -0,0 +1,180 @@ +/* + * 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 "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "shared-bindings/keypad/Event.h" + +//| class Event: +//| """A key transition event.""" +//| def __init__(self, key_number: int=0, pressed: bool=True) -> None: +//| """Create a key transition event, which reports a key-pressed or key-released transition. +//| +//| :param int key_number: the key number +//| :param bool pressed: ``True`` if the key was pressed; ``False`` if it was released. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_event_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + keypad_event_obj_t *self = m_new_obj(keypad_event_obj_t); + self->base.type = &keypad_event_type; + enum { ARG_key_number, ARG_pressed }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_key_number, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_pressed, MP_ARG_BOOL, {.u_bool = true} }, + }; + 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); + + const mp_uint_t key_number = + (mp_uint_t)mp_arg_validate_int_min(args[ARG_key_number].u_int, 0, MP_QSTR_key_number); + + common_hal_keypad_event_construct(self, key_number, args[ARG_pressed].u_bool); + return MP_OBJ_FROM_PTR(self); +} + +//| key_number: int +//| """The key number.""" +//| +STATIC mp_obj_t keypad_event_get_key_number(mp_obj_t self_in) { + keypad_event_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_keypad_event_get_key_number(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_event_get_key_number_obj, keypad_event_get_key_number); + +const mp_obj_property_t keypad_event_key_number_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_event_get_key_number_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +//| pressed: bool +//| """``True`` if the event represents a key down (pressed) transition. +//| The opposite of `released`. +//| """ +//| +STATIC mp_obj_t keypad_event_get_pressed(mp_obj_t self_in) { + keypad_event_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_keypad_event_get_pressed(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_event_get_pressed_obj, keypad_event_get_pressed); + +const mp_obj_property_t keypad_event_pressed_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_event_get_pressed_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +//| released: bool +//| """``True`` if the event represents a key up (released) transition. +//| The opposite of `pressed`. +//| """ +//| +STATIC mp_obj_t keypad_event_get_released(mp_obj_t self_in) { + keypad_event_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_keypad_event_get_released(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_event_get_released_obj, keypad_event_get_released); + +const mp_obj_property_t keypad_event_released_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_event_get_released_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +//| def __eq__(self, other: object) -> bool: +//| """Two `Event` objects are equal if their `key_number` +//| and `pressed`/`released` values are equal. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_event_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + switch (op) { + case MP_BINARY_OP_EQUAL: + if (mp_obj_is_type(rhs_in, &keypad_event_type)) { + keypad_event_obj_t *lhs = MP_OBJ_TO_PTR(lhs_in); + keypad_event_obj_t *rhs = MP_OBJ_TO_PTR(rhs_in); + return mp_obj_new_bool( + (common_hal_keypad_event_get_key_number(lhs) == + common_hal_keypad_event_get_key_number(rhs)) && + (common_hal_keypad_event_get_pressed(lhs) == + common_hal_keypad_event_get_pressed(rhs)) + ); + } else { + return mp_const_false; + } + + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| def __hash__(self) -> int: +//| """Returns a hash for the `Event`, so it can be used in dictionaries, etc..""" +//| ... +//| +STATIC mp_obj_t keypad_event_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + keypad_event_obj_t *self = MP_OBJ_TO_PTR(self); + switch (op) { + case MP_UNARY_OP_HASH: { + const mp_int_t key_number = common_hal_keypad_event_get_key_number(self); + const bool pressed = common_hal_keypad_event_get_pressed(self); + return MP_OBJ_NEW_SMALL_INT((pressed << 15) + key_number); + } + default: + return MP_OBJ_NULL; // op not supported + } +} + +STATIC void keypad_event_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + keypad_event_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "", + common_hal_keypad_event_get_key_number(self), + common_hal_keypad_event_get_pressed(self) ? "pressed" : "released"); +} + +STATIC const mp_rom_map_elem_t keypad_event_locals_dict_table[] = { + // Properties + { MP_ROM_QSTR(MP_QSTR_key_number), MP_ROM_PTR(&keypad_event_key_number_obj) }, + { MP_ROM_QSTR(MP_QSTR_pressed), MP_ROM_PTR(&keypad_event_pressed_obj) }, + { MP_ROM_QSTR(MP_QSTR_released), MP_ROM_PTR(&keypad_event_released_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(keypad_event_locals_dict, keypad_event_locals_dict_table); + +const mp_obj_type_t keypad_event_type = { + { &mp_type_type }, + .name = MP_QSTR_Event, + .make_new = keypad_event_make_new, + .print = keypad_event_print, + .unary_op = keypad_event_unary_op, + .binary_op = keypad_event_binary_op, + .locals_dict = (mp_obj_dict_t *)&keypad_event_locals_dict, +}; diff --git a/shared-bindings/keypad/Event.h b/shared-bindings/keypad/Event.h new file mode 100644 index 0000000000..35cfdcdc64 --- /dev/null +++ b/shared-bindings/keypad/Event.h @@ -0,0 +1,40 @@ +/* + * 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_SHARED_BINDINGS_KEYPAD_EVENT__H +#define MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_EVENT__H + +#include "py/obj.h" +#include "shared-module/keypad/Event.h" + +extern const mp_obj_type_t keypad_event_type; + +void common_hal_keypad_event_construct(keypad_event_obj_t *self, mp_uint_t key_number, bool pressed); +mp_int_t common_hal_keypad_event_get_key_number(keypad_event_obj_t *self); +bool common_hal_keypad_event_get_pressed(keypad_event_obj_t *self); +bool common_hal_keypad_event_get_released(keypad_event_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_EVENT__H diff --git a/shared-bindings/keypad/EventQueue.c b/shared-bindings/keypad/EventQueue.c new file mode 100644 index 0000000000..e47ee0772c --- /dev/null +++ b/shared-bindings/keypad/EventQueue.c @@ -0,0 +1,152 @@ +/* + * 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/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/keypad/Event.h" +#include "shared-bindings/keypad/EventQueue.h" + +//| class EventQueue: +//| """A queue of `Event` objects, filled by a `keypad` scanner such as `Keys` or `KeyMatrix`. +//| +//| You cannot create an instance of `EventQueue` directly. Each scanner creates an +//| instance when it is created. +//| """ +//| ... + +//| def get(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 of +//| a scanner such as `Keys` or `KeyMatrix`. +//| If a new event arrives when the queue is full, the event is discarded, and +//| `overflowed` is set to ``True``. +//| +//| :return: the next queued key transition `Event` +//| :rtype: Optional[Event] +//| """ +//| ... +//| +STATIC mp_obj_t keypad_eventqueue_get(mp_obj_t self_in) { + keypad_eventqueue_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_keypad_eventqueue_get(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_eventqueue_get_obj, keypad_eventqueue_get); + +//| def get_into(self, event: Event) -> bool: +//| """Store the next key transition event in the supplied event, if available, +//| and return ``True``. +//| If there are no queued events, do not touch ``event`` and return ``False``. +//| +//| The advantage of this method over ``get()`` is that it does not allocate storage. +//| Instead you can reuse an existing ``Event`` object. +//| +//| Note that the queue size is limited; see ``max_events`` in the constructor of +//| a scanner such as `Keys` or `KeyMatrix`. +//| +//| :return ``True`` if an event was available and stored, ``False`` if not. +//| :rtype: bool +//| """ +//| ... +//| +STATIC mp_obj_t keypad_eventqueue_get_into(mp_obj_t self_in, mp_obj_t event_in) { + keypad_eventqueue_obj_t *self = MP_OBJ_TO_PTR(self_in); + + keypad_event_obj_t *event = MP_OBJ_TO_PTR(mp_arg_validate_type(event_in, &keypad_event_type, MP_QSTR_event)); + + return mp_obj_new_bool(common_hal_keypad_eventqueue_get_into(self, event)); +} +MP_DEFINE_CONST_FUN_OBJ_2(keypad_eventqueue_get_into_obj, keypad_eventqueue_get_into); + +//| def clear(self) -> None: +//| """Clear any queued key transition events. Also sets `overflowed` to ``False``. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_eventqueue_clear(mp_obj_t self_in) { + keypad_eventqueue_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_keypad_eventqueue_clear(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_eventqueue_clear_obj, keypad_eventqueue_clear); + +//| def __bool__(self) -> bool: +//| """``True`` if `len()` is greater than zero. +//| This is an easy way to check if the queue is empty. +//| """ +//| ... +//| +//| def __len__(self) -> int: +//| """Return the number of events currently in the queue. Used to implement ``len()``.""" +//| ... +//| +STATIC mp_obj_t keypad_eventqueue_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + keypad_eventqueue_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint16_t len = common_hal_keypad_eventqueue_get_length(self); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(len != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(len); + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| overflowed: bool +//| """``True`` if an event could not be added to the event queue because it was full. (read-only) +//| Set to ``False`` by `clear()`. +//| """ +//| +STATIC mp_obj_t keypad_eventqueue_get_overflowed(mp_obj_t self_in) { + keypad_eventqueue_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_keypad_eventqueue_get_overflowed(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_eventqueue_get_overflowed_obj, keypad_eventqueue_get_overflowed); + +const mp_obj_property_t keypad_eventqueue_overflowed_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_eventqueue_get_overflowed_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +STATIC const mp_rom_map_elem_t keypad_eventqueue_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&keypad_eventqueue_clear_obj) }, + { MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&keypad_eventqueue_get_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_into), MP_ROM_PTR(&keypad_eventqueue_get_into_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(keypad_eventqueue_locals_dict, keypad_eventqueue_locals_dict_table); + +const mp_obj_type_t keypad_eventqueue_type = { + { &mp_type_type }, + .name = MP_QSTR_Keys, + .unary_op = keypad_eventqueue_unary_op, + .locals_dict = (mp_obj_t)&keypad_eventqueue_locals_dict, +}; diff --git a/shared-bindings/keypad/EventQueue.h b/shared-bindings/keypad/EventQueue.h new file mode 100644 index 0000000000..fbbd9df0cb --- /dev/null +++ b/shared-bindings/keypad/EventQueue.h @@ -0,0 +1,45 @@ +/* + * 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_EVENTQUEUE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_EVENTQUEUE_H + +#include "shared-module/keypad/Event.h" +#include "shared-module/keypad/EventQueue.h" + +extern const mp_obj_type_t keypad_eventqueue_type; + +void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events); + +void common_hal_keypad_eventqueue_clear(keypad_eventqueue_obj_t *self); +size_t common_hal_keypad_eventqueue_get_length(keypad_eventqueue_obj_t *self); +mp_obj_t common_hal_keypad_eventqueue_get(keypad_eventqueue_obj_t *self); +bool common_hal_keypad_eventqueue_get_into(keypad_eventqueue_obj_t *self, keypad_event_obj_t *event); + +bool common_hal_keypad_eventqueue_get_overflowed(keypad_eventqueue_obj_t *self); +void common_hal_keypad_eventqueue_set_overflowed(keypad_eventqueue_obj_t *self, bool overflowed); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_EVENTQUEUE_H diff --git a/shared-bindings/keypad/KeyMatrix.c b/shared-bindings/keypad/KeyMatrix.c new file mode 100644 index 0000000000..0b854ef475 --- /dev/null +++ b/shared-bindings/keypad/KeyMatrix.c @@ -0,0 +1,269 @@ +/* + * 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/binary.h" +#include "py/objproperty.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], column_pins: Sequence[microcontroller.Pin], columns_to_anodes: bool = True, interval: float = 0.020, max_events: int = 64) -> None: +//| """ +//| Create a `Keys` object that will scan the key matrix attached to the given row and column pins. +//| There should not be any external pull-ups or pull-downs on the matrix: +//| ``KeyMatrix`` enables internal pull-ups or pull-downs on the pins as necessary. +//| +//| The keys are numbered sequentially from zero. A key number can be computed +//| by ``row * len(column_pins) + column``. +//| +//| An `EventQueue` is created when this object is created and is available in the `events` attribute. +//| +//| :param Sequence[microcontroller.Pin] row_pins: The pins attached to the rows. +//| :param Sequence[microcontroller.Pin] column_pins: The pins attached to the colums. +//| :param bool columns_to_anodes: Default ``True``. +//| If the matrix uses diodes, the diode anodes are typically connected to the column pins, +//| and the cathodes should be connected to the row pins. If your diodes are reversed, +//| set ``columns_to_anodes`` to ``False``. +//| :param float interval: Scan keys no more often than ``interval`` to allow for debouncing. +//| ``interval`` is in float seconds. The default is 0.020 (20 msecs). +//| :param int max_events: maximum size of `events` `EventQueue`: +//| 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_column_pins, ARG_columns_to_anodes, ARG_interval, ARG_max_events }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_row_pins, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_column_pins, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_columns_to_anodes, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_max_events, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} }, + }; + 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 column_pins = args[ARG_column_pins].u_obj; + const size_t num_column_pins = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(column_pins)); + + const mp_float_t interval = + mp_arg_validate_obj_float_non_negative(args[ARG_interval].u_obj, 0.020f, MP_QSTR_interval); + const size_t max_events = (size_t)mp_arg_validate_int_min(args[ARG_max_events].u_int, 1, MP_QSTR_max_events); + + mcu_pin_obj_t *row_pins_array[num_row_pins]; + mcu_pin_obj_t *column_pins_array[num_column_pins]; + + for (size_t row = 0; row < num_row_pins; row++) { + mcu_pin_obj_t *pin = + validate_obj_is_free_pin(mp_obj_subscr(row_pins, MP_OBJ_NEW_SMALL_INT(row), MP_OBJ_SENTINEL)); + row_pins_array[row] = pin; + } + + for (size_t column = 0; column < num_column_pins; column++) { + mcu_pin_obj_t *pin = + validate_obj_is_free_pin(mp_obj_subscr(column_pins, MP_OBJ_NEW_SMALL_INT(column), MP_OBJ_SENTINEL)); + column_pins_array[column] = pin; + } + + common_hal_keypad_keymatrix_construct(self, num_row_pins, row_pins_array, num_column_pins, column_pins_array, args[ARG_columns_to_anodes].u_bool, interval, 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 reset(self) -> None: +//| """Reset the internal state of the scanner to assume that all keys are now released. +//| Any key that is already pressed at the time of this call will therefore immediately cause +//| a new key-pressed event to occur. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_keymatrix_reset(mp_obj_t self_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_keypad_keymatrix_reset(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keymatrix_reset_obj, keypad_keymatrix_reset); + +//| key_count: int +//| """The number of keys that are being scanned. (read-only) +//| """ +//| +STATIC mp_obj_t keypad_keymatrix_get_key_count(mp_obj_t self_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return MP_OBJ_NEW_SMALL_INT(common_hal_keypad_keymatrix_get_key_count(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keymatrix_get_key_count_obj, keypad_keymatrix_get_key_count); + +const mp_obj_property_t keypad_keymatrix_key_count_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_keymatrix_get_key_count_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +//| def key_number_to_row_column(self, row: int, column: int) -> Tuple[int]: +//| """Return the row and column for the given key number. +//| The row is ``key_number // len(column_pins)``. +//| The column is ``key_number % len(column_pins)``. +//| +//| :return: ``(row, column)`` +//| :rtype: Tuple[int] +//| """ +//| ... +//| +STATIC mp_obj_t keypad_keymatrix_key_number_to_row_column(mp_obj_t self_in, mp_obj_t key_number_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + const mp_uint_t key_number = (mp_uint_t)mp_arg_validate_int_range( + mp_obj_get_int(key_number_in), + 0, (mp_int_t)common_hal_keypad_keymatrix_get_key_count(self), + MP_QSTR_key_number); + + mp_uint_t row; + mp_uint_t column; + common_hal_keypad_keymatrix_key_number_to_row_column(self, key_number, &row, &column); + + mp_obj_t row_column[2]; + row_column[0] = MP_OBJ_NEW_SMALL_INT(row); + row_column[1] = MP_OBJ_NEW_SMALL_INT(column); + + return mp_obj_new_tuple(2, row_column); +} +MP_DEFINE_CONST_FUN_OBJ_2(keypad_keymatrix_key_number_to_row_column_obj, keypad_keymatrix_key_number_to_row_column); + +//| def row_column_to_key_number(self, row: int, column: int) -> int: +//| """Return the key number for a given row and column. +//| The key number is ``row * len(column_pins) + column``. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_keymatrix_row_column_to_key_number(mp_obj_t self_in, mp_obj_t row_in, mp_obj_t column_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + const mp_uint_t row = (mp_uint_t)mp_arg_validate_int_range( + mp_obj_get_int(row_in), 0, (mp_int_t)common_hal_keypad_keymatrix_get_row_count(self), MP_QSTR_row); + + const mp_int_t column = (mp_uint_t)mp_arg_validate_int_range( + mp_obj_get_int(column_in), 0, (mp_int_t)common_hal_keypad_keymatrix_get_column_count(self), MP_QSTR_column); + + return MP_OBJ_NEW_SMALL_INT( + (mp_int_t)common_hal_keypad_keymatrix_row_column_to_key_number(self, row, column)); +} +MP_DEFINE_CONST_FUN_OBJ_3(keypad_keymatrix_row_column_to_key_number_obj, keypad_keymatrix_row_column_to_key_number); + +//| events: EventQueue +//| """The `EventQueue` associated with this `Keys` object. (read-only) +//| """ +//| +STATIC mp_obj_t keypad_keymatrix_get_events(mp_obj_t self_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return common_hal_keypad_keymatrix_get_events(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keymatrix_get_events_obj, keypad_keymatrix_get_events); + +const mp_obj_property_t keypad_keymatrix_events_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_keymatrix_get_events_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +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_events), MP_ROM_PTR(&keypad_keymatrix_events_obj) }, + { MP_ROM_QSTR(MP_QSTR_key_count), MP_ROM_PTR(&keypad_keymatrix_key_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&keypad_keymatrix_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_key_number_to_row_column), MP_ROM_PTR(&keypad_keymatrix_key_number_to_row_column_obj) }, + { MP_ROM_QSTR(MP_QSTR_row_column_to_key_number), MP_ROM_PTR(&keypad_keymatrix_row_column_to_key_number_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_KeyMatrix, + .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..7ebe1af5b7 --- /dev/null +++ b/shared-bindings/keypad/KeyMatrix.h @@ -0,0 +1,50 @@ +/* + * 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_column_pins, mcu_pin_obj_t *column_pins[], bool columns_to_anodes, mp_float_t interval, 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); + +void common_hal_keypad_keymatrix_key_number_to_row_column(keypad_keymatrix_obj_t *self, mp_uint_t key_number, mp_uint_t *row, mp_uint_t *column); +mp_uint_t common_hal_keypad_keymatrix_row_column_to_key_number(keypad_keymatrix_obj_t *self, mp_uint_t row, mp_uint_t column); + +size_t common_hal_keypad_keymatrix_get_key_count(keypad_keymatrix_obj_t *self); +size_t common_hal_keypad_keymatrix_get_column_count(keypad_keymatrix_obj_t *self); +size_t common_hal_keypad_keymatrix_get_row_count(keypad_keymatrix_obj_t *self); + +mp_obj_t common_hal_keypad_keymatrix_get_events(keypad_keymatrix_obj_t *self); +void common_hal_keypad_keymatrix_reset(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 new file mode 100644 index 0000000000..d616bcff84 --- /dev/null +++ b/shared-bindings/keypad/Keys.c @@ -0,0 +1,209 @@ +/* + * 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/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/keypad/Event.h" +#include "shared-bindings/keypad/Keys.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" + +//| class Keys: +//| """Manage a set of independent keys.""" +//| +//| def __init__(self, pins: Sequence[microcontroller.Pin], *, value_when_pressed: bool, pull: bool = True, interval: float = 0.020, max_events: int = 64) -> None: +//| """ +//| Create a `Keys` object that will scan keys attached to the given sequence of pins. +//| Each key is independent and attached to its own pin. +//| +//| An `EventQueue` is created when this object is created and is available in the `events` attribute. +//| +//| :param Sequence[microcontroller.Pin] pins: The pins attached to the keys. +//| The key numbers correspond to indices into this sequence. +//| :param bool value_when_pressed: ``True`` if the pin reads high when the key is pressed. +//| ``False`` if the pin reads low (is grounded) when the key is pressed. +//| All the pins must be connected in the same way. +//| :param bool pull: ``True`` if an internal pull-up or pull-down should be +//| enabled on each pin. A pull-up will be used if ``value_when_pressed`` is ``False``; +//| a pull-down will be used if it is ``True``. +//| If an external pull is already provided for all the pins, you can set ``pull`` to ``False``. +//| However, enabling an internal pull when an external one is already present is not a problem; +//| it simply uses slightly more current. +//| :param float interval: Scan keys no more often than ``interval`` to allow for debouncing. +//| ``interval`` is in float seconds. The default is 0.020 (20 msecs). +//| :param int max_events: maximum size of `events` `EventQueue`: +//| 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_keys_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + keypad_keys_obj_t *self = m_new_obj(keypad_keys_obj_t); + self->base.type = &keypad_keys_type; + enum { ARG_pins, ARG_value_when_pressed, ARG_pull, ARG_interval, ARG_max_events }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pins, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_value_when_pressed, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_BOOL }, + { MP_QSTR_pull, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL } }, + { MP_QSTR_max_events, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} }, + }; + 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 pins = args[ARG_pins].u_obj; + // mp_obj_len() will be >= 0. + const size_t num_pins = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(pins)); + + const bool value_when_pressed = args[ARG_value_when_pressed].u_bool; + const mp_float_t interval = + mp_arg_validate_obj_float_non_negative(args[ARG_interval].u_obj, 0.020f, MP_QSTR_interval); + const size_t max_events = (size_t)mp_arg_validate_int_min(args[ARG_max_events].u_int, 1, MP_QSTR_max_events); + + mcu_pin_obj_t *pins_array[num_pins]; + + for (mp_uint_t i = 0; i < num_pins; i++) { + mcu_pin_obj_t *pin = + validate_obj_is_free_pin(mp_obj_subscr(pins, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL)); + pins_array[i] = pin; + } + + common_hal_keypad_keys_construct(self, num_pins, pins_array, value_when_pressed, args[ARG_pull].u_bool, interval, 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 reset(self) -> None: +//| """Reset the internal state of the scanner to assume that all keys are now released. +//| Any key that is already pressed at the time of this call will therefore immediately cause +//| a new key-pressed event to occur. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_keys_reset(mp_obj_t self_in) { + keypad_keys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_keypad_keys_reset(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keys_reset_obj, keypad_keys_reset); + +//| key_count: int +//| """The number of keys that are being scanned. (read-only) +//| """ +//| +STATIC mp_obj_t keypad_keys_get_key_count(mp_obj_t self_in) { + keypad_keys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return MP_OBJ_NEW_SMALL_INT(common_hal_keypad_keys_get_key_count(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keys_get_key_count_obj, keypad_keys_get_key_count); + +const mp_obj_property_t keypad_keys_key_count_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_keys_get_key_count_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +//| events: EventQueue +//| """The `EventQueue` associated with this `Keys` object. (read-only) +//| """ +//| +STATIC mp_obj_t keypad_keys_get_events(mp_obj_t self_in) { + keypad_keys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return common_hal_keypad_keys_get_events(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keys_get_events_obj, keypad_keys_get_events); + +const mp_obj_property_t keypad_keys_events_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_keys_get_events_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +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_events), MP_ROM_PTR(&keypad_keys_events_obj) }, + { MP_ROM_QSTR(MP_QSTR_key_count), MP_ROM_PTR(&keypad_keys_key_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&keypad_keys_reset_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(keypad_keys_locals_dict, keypad_keys_locals_dict_table); + +const mp_obj_type_t keypad_keys_type = { + { &mp_type_type }, + .name = MP_QSTR_Keys, + .make_new = keypad_keys_make_new, + .locals_dict = (mp_obj_t)&keypad_keys_locals_dict, +}; diff --git a/shared-bindings/keypad/Keys.h b/shared-bindings/keypad/Keys.h new file mode 100644 index 0000000000..094220e588 --- /dev/null +++ b/shared-bindings/keypad/Keys.h @@ -0,0 +1,44 @@ +/* + * 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_KEYS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_KEYS_H + +#include "py/objlist.h" +#include "shared-module/keypad/Keys.h" + +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, mp_float_t interval, 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_obj_t common_hal_keypad_keys_get_events(keypad_keys_obj_t *self); +size_t common_hal_keypad_keys_get_key_count(keypad_keys_obj_t *self); +void common_hal_keypad_keys_reset(keypad_keys_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_KEYS_H diff --git a/shared-bindings/keypad/ShiftRegisterKeys.c b/shared-bindings/keypad/ShiftRegisterKeys.c new file mode 100644 index 0000000000..c81e127601 --- /dev/null +++ b/shared-bindings/keypad/ShiftRegisterKeys.c @@ -0,0 +1,213 @@ +/* + * 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/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/keypad/Event.h" +#include "shared-bindings/keypad/ShiftRegisterKeys.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" + +//| class ShiftRegisterKeys: +//| """Manage a set of keys attached to an incoming shift register.""" +//| +//| def __init__(self, *, clock: microcontroller.Pin, data: microcontroller.Pin, latch: microcontroller.Pin, value_to_latch: bool = True, key_count: int, value_when_pressed: bool, interval: float = 0.020, max_events: int = 64) -> None: +//| """ +//| Create a `Keys` object that will scan keys attached to a parallel-in serial-out shift register +//| like the 74HC165 or CD4021. +//| Note that you may chain shift registers to load in as many values as you need. +//| +//| Key number 0 is the first (or more properly, the zero-th) bit read. In the +//| 74HC165, this bit is labeled ``Q7``. Key number 1 will be the value of ``Q6``, etc. +//| +//| An `EventQueue` is created when this object is created and is available in the `events` attribute. +//| +//| :param microcontroller.Pin clock: The shift register clock pin. +//| The shift register should clock on a low-to-high transition. +//| :param microcontroller.Pin data: the incoming shift register data pin +//| :param microcontroller.Pin latch: +//| Pin used to latch parallel data going into the shift register. +//| :param bool value_to_latch: Pin state to latch data being read. +//| ``True`` if the data is latched when ``latch`` goes high +//| ``False`` if the data is latched when ``latch goes low. +//| The default is ``True``, which is how the 74HC165 operates. The CD4021 latch is the opposite. +//| Once the data is latched, it will be shifted out by toggling the clock pin. +//| :param int key_count: number of data lines to clock in +//| :param bool value_when_pressed: ``True`` if the pin reads high when the key is pressed. +//| ``False`` if the pin reads low (is grounded) when the key is pressed. +//| :param float interval: Scan keys no more often than ``interval`` to allow for debouncing. +//| ``interval`` is in float seconds. The default is 0.020 (20 msecs). +//| :param int max_events: maximum size of `events` `EventQueue`: +//| 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_shiftregisterkeys_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + keypad_shiftregisterkeys_obj_t *self = m_new_obj(keypad_shiftregisterkeys_obj_t); + self->base.type = &keypad_shiftregisterkeys_type; + enum { ARG_clock, ARG_data, ARG_latch, ARG_value_to_latch, ARG_key_count, ARG_value_when_pressed, ARG_interval, ARG_max_events }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_clock, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_data, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_latch, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_value_to_latch, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_key_count, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_value_when_pressed, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_BOOL }, + { MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_max_events, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} }, + }; + 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); + + mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj); + mcu_pin_obj_t *data = validate_obj_is_free_pin(args[ARG_data].u_obj); + mcu_pin_obj_t *latch = validate_obj_is_free_pin(args[ARG_latch].u_obj); + const bool value_to_latch = args[ARG_value_to_latch].u_bool; + + const size_t key_count = (size_t)mp_arg_validate_int_min(args[ARG_key_count].u_int, 1, MP_QSTR_key_count); + const bool value_when_pressed = args[ARG_value_when_pressed].u_bool; + const mp_float_t interval = + mp_arg_validate_obj_float_non_negative(args[ARG_interval].u_obj, 0.020f, MP_QSTR_interval); + const size_t max_events = (size_t)mp_arg_validate_int_min(args[ARG_max_events].u_int, 1, MP_QSTR_max_events); + + common_hal_keypad_shiftregisterkeys_construct( + self, clock, data, latch, value_to_latch, key_count, value_when_pressed, interval, max_events); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Stop scanning and release the pins.""" +//| ... +//| +STATIC mp_obj_t keypad_shiftregisterkeys_deinit(mp_obj_t self_in) { + keypad_shiftregisterkeys_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_keypad_shiftregisterkeys_deinit(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_shiftregisterkeys_deinit_obj, keypad_shiftregisterkeys_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_shiftregisterkeys___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_keypad_shiftregisterkeys_deinit(args[0]); + return MP_ROM_NONE; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(keypad_shiftregisterkeys___exit___obj, 4, 4, keypad_shiftregisterkeys___exit__); + +STATIC void check_for_deinit(keypad_shiftregisterkeys_obj_t *self) { + if (common_hal_keypad_shiftregisterkeys_deinited(self)) { + raise_deinited_error(); + } +} + +//| def reset(self) -> None: +//| """Reset the internal state of the scanner to assume that all keys are now released. +//| Any key that is already pressed at the time of this call will therefore immediately cause +//| a new key-pressed event to occur. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_shiftregisterkeys_reset(mp_obj_t self_in) { + keypad_shiftregisterkeys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_keypad_shiftregisterkeys_reset(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_shiftregisterkeys_reset_obj, keypad_shiftregisterkeys_reset); + +//| key_count: int +//| """The number of keys that are being scanned. (read-only) +//| """ +//| +STATIC mp_obj_t keypad_shiftregisterkeys_get_key_count(mp_obj_t self_in) { + keypad_shiftregisterkeys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return MP_OBJ_NEW_SMALL_INT(common_hal_keypad_shiftregisterkeys_get_key_count(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_shiftregisterkeys_get_key_count_obj, keypad_shiftregisterkeys_get_key_count); + +const mp_obj_property_t keypad_shiftregisterkeys_key_count_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_shiftregisterkeys_get_key_count_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +//| events: EventQueue +//| """The `EventQueue` associated with this `Keys` object. (read-only) +//| """ +//| +STATIC mp_obj_t keypad_shiftregisterkeys_get_events(mp_obj_t self_in) { + keypad_shiftregisterkeys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return common_hal_keypad_shiftregisterkeys_get_events(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_shiftregisterkeys_get_events_obj, keypad_shiftregisterkeys_get_events); + +const mp_obj_property_t keypad_shiftregisterkeys_events_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_shiftregisterkeys_get_events_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +STATIC const mp_rom_map_elem_t keypad_shiftregisterkeys_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&keypad_shiftregisterkeys_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&keypad_shiftregisterkeys___exit___obj) }, + + { MP_ROM_QSTR(MP_QSTR_events), MP_ROM_PTR(&keypad_shiftregisterkeys_events_obj) }, + { MP_ROM_QSTR(MP_QSTR_key_count), MP_ROM_PTR(&keypad_shiftregisterkeys_key_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&keypad_shiftregisterkeys_reset_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(keypad_shiftregisterkeys_locals_dict, keypad_shiftregisterkeys_locals_dict_table); + +const mp_obj_type_t keypad_shiftregisterkeys_type = { + { &mp_type_type }, + .name = MP_QSTR_ShiftRegisterKeys, + .make_new = keypad_shiftregisterkeys_make_new, + .locals_dict = (mp_obj_t)&keypad_shiftregisterkeys_locals_dict, +}; diff --git a/shared-bindings/keypad/ShiftRegisterKeys.h b/shared-bindings/keypad/ShiftRegisterKeys.h new file mode 100644 index 0000000000..f1432f1069 --- /dev/null +++ b/shared-bindings/keypad/ShiftRegisterKeys.h @@ -0,0 +1,44 @@ +/* + * 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_SHIFTREGISTERKEYS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_SHIFTREGISTERKEYS_H + +#include "py/objlist.h" +#include "shared-module/keypad/ShiftRegisterKeys.h" + +extern const mp_obj_type_t keypad_shiftregisterkeys_type; + +void common_hal_keypad_shiftregisterkeys_construct(keypad_shiftregisterkeys_obj_t *self, mcu_pin_obj_t *clock_pin, mcu_pin_obj_t *data_pin, mcu_pin_obj_t *latch_pin, bool value_to_latch, size_t key_count, bool value_when_pressed, mp_float_t interval, size_t max_events); + +void common_hal_keypad_shiftregisterkeys_deinit(keypad_shiftregisterkeys_obj_t *self); +bool common_hal_keypad_shiftregisterkeys_deinited(keypad_shiftregisterkeys_obj_t *self); + +mp_obj_t common_hal_keypad_shiftregisterkeys_get_events(keypad_shiftregisterkeys_obj_t *self); +size_t common_hal_keypad_shiftregisterkeys_get_key_count(keypad_shiftregisterkeys_obj_t *self); +void common_hal_keypad_shiftregisterkeys_reset(keypad_shiftregisterkeys_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_SHIFTREGISTERKEYS_H diff --git a/shared-bindings/keypad/__init__.c b/shared-bindings/keypad/__init__.c new file mode 100644 index 0000000000..30d3dd49f0 --- /dev/null +++ b/shared-bindings/keypad/__init__.c @@ -0,0 +1,59 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2011 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/obj.h" + +#include "shared-bindings/keypad/__init__.h" +#include "shared-bindings/keypad/Event.h" +#include "shared-bindings/keypad/EventQueue.h" +#include "shared-bindings/keypad/KeyMatrix.h" +#include "shared-bindings/keypad/Keys.h" +#include "shared-bindings/keypad/ShiftRegisterKeys.h" + +//| """Support for scanning keys and key matrices +//| +//| The `keypad` module provides native support to scan sets of keys or buttons, +//| connected independently to individual pins, +//| connected to a shift register, +//| or connected in a row-and-column matrix. +//| """ +//| + +STATIC mp_map_elem_t keypad_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_keypad) }, + { MP_ROM_QSTR(MP_QSTR_Event), MP_OBJ_FROM_PTR(&keypad_event_type) }, + { MP_ROM_QSTR(MP_QSTR_EventQueue), MP_OBJ_FROM_PTR(&keypad_eventqueue_type) }, + { MP_ROM_QSTR(MP_QSTR_KeyMatrix), MP_OBJ_FROM_PTR(&keypad_keymatrix_type) }, + { MP_ROM_QSTR(MP_QSTR_Keys), MP_OBJ_FROM_PTR(&keypad_keys_type) }, + { MP_ROM_QSTR(MP_QSTR_ShiftRegisterKeys), MP_OBJ_FROM_PTR(&keypad_shiftregisterkeys_type) }, +}; + +STATIC MP_DEFINE_MUTABLE_DICT(keypad_module_globals, keypad_module_globals_table); + +const mp_obj_module_t keypad_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&keypad_module_globals, +}; diff --git a/shared-bindings/keypad/__init__.h b/shared-bindings/keypad/__init__.h new file mode 100644 index 0000000000..5e484dc5ed --- /dev/null +++ b/shared-bindings/keypad/__init__.h @@ -0,0 +1,41 @@ +/* + * 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 SHARED_BINDINGS_KEYPAD_H +#define SHARED_BINDINGS_KEYPAD_H + +#include "py/obj.h" +#include "py/objtuple.h" +#include "shared-module/keypad/__init__.h" + +extern mp_obj_tuple_t common_hal_keypad_devices; + +void keypad_set_devices(mp_obj_t devices); + +bool common_hal_keypad_disable(void); +bool common_hal_keypad_enable(const mp_obj_t devices_seq); + +#endif // SHARED_BINDINGS_KEYPAD_H diff --git a/shared-module/keypad/Event.c b/shared-module/keypad/Event.c new file mode 100644 index 0000000000..d938002cc5 --- /dev/null +++ b/shared-module/keypad/Event.c @@ -0,0 +1,44 @@ +/* + * 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 "shared-module/keypad/Event.h" + +void common_hal_keypad_event_construct(keypad_event_obj_t *self, mp_int_t key_number, bool pressed) { + self->key_number = key_number; + self->pressed = pressed; +} + +mp_int_t common_hal_keypad_event_get_key_number(keypad_event_obj_t *self) { + return self->key_number; +} + +bool common_hal_keypad_event_get_pressed(keypad_event_obj_t *self) { + return self->pressed; +} + +bool common_hal_keypad_event_get_released(keypad_event_obj_t *self) { + return !self->pressed; +} diff --git a/shared-module/keypad/Event.h b/shared-module/keypad/Event.h new file mode 100644 index 0000000000..3c47b0e636 --- /dev/null +++ b/shared-module/keypad/Event.h @@ -0,0 +1,39 @@ +/* + * 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_EVENT_H +#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_EVENT_H + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + uint16_t key_number; + bool pressed; +} keypad_event_obj_t; + + +#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_EVENT_H diff --git a/shared-module/keypad/EventQueue.c b/shared-module/keypad/EventQueue.c new file mode 100644 index 0000000000..5986a29ce8 --- /dev/null +++ b/shared-module/keypad/EventQueue.c @@ -0,0 +1,94 @@ +/* + * 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 "shared-bindings/keypad/Event.h" +#include "shared-module/keypad/EventQueue.h" + +// Key number is lower 15 bits of a 16-bit value. +#define EVENT_PRESSED (1 << 15) +#define EVENT_KEY_NUM_MASK ((1 << 15) - 1) + +void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events) { + // Event queue is 16-bit values. + ringbuf_alloc(&self->encoded_events, max_events * 2, false); + self->overflowed = false; +} + +mp_obj_t common_hal_keypad_eventqueue_get(keypad_eventqueue_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); + event->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); +} + +bool common_hal_keypad_eventqueue_get_into(keypad_eventqueue_obj_t *self, keypad_event_obj_t *event) { + int encoded_event = ringbuf_get16(&self->encoded_events); + if (encoded_event == -1) { + return false; + } + + // "Construct" using the existing event. + common_hal_keypad_event_construct(event, encoded_event & EVENT_KEY_NUM_MASK, encoded_event & EVENT_PRESSED); + return true; +} + +bool common_hal_keypad_eventqueue_get_overflowed(keypad_eventqueue_obj_t *self) { + return self->overflowed; +} + +void common_hal_keypad_eventqueue_set_overflowed(keypad_eventqueue_obj_t *self, bool overflowed) { + self->overflowed = overflowed; +} + +void common_hal_keypad_eventqueue_clear(keypad_eventqueue_obj_t *self) { + ringbuf_clear(&self->encoded_events); + common_hal_keypad_eventqueue_set_overflowed(self, false); +} + +size_t common_hal_keypad_eventqueue_get_length(keypad_eventqueue_obj_t *self) { + return ringbuf_num_filled(&self->encoded_events); +} + +bool keypad_eventqueue_record(keypad_eventqueue_obj_t *self, mp_uint_t key_number, bool pressed) { + if (ringbuf_num_empty(&self->encoded_events) == 0) { + // Queue is full. Set the overflow flag. The caller will decide what else to do. + common_hal_keypad_eventqueue_set_overflowed(self, true); + return false; + } + + uint16_t encoded_event = key_number & EVENT_KEY_NUM_MASK; + if (pressed) { + encoded_event |= EVENT_PRESSED; + } + ringbuf_put16(&self->encoded_events, encoded_event); + + return true; +} diff --git a/shared-module/keypad/EventQueue.h b/shared-module/keypad/EventQueue.h new file mode 100644 index 0000000000..ce5e55ef3b --- /dev/null +++ b/shared-module/keypad/EventQueue.h @@ -0,0 +1,41 @@ +/* + * 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_EVENTQUEUE_H +#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_EVENTQUEUE_H + +#include "py/obj.h" +#include "py/ringbuf.h" + +typedef struct { + mp_obj_base_t base; + ringbuf_t encoded_events; + bool overflowed; +} keypad_eventqueue_obj_t; + +bool keypad_eventqueue_record(keypad_eventqueue_obj_t *self, mp_uint_t key_number, bool pressed); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_EVENTQUEUE_H diff --git a/shared-module/keypad/KeyMatrix.c b/shared-module/keypad/KeyMatrix.c new file mode 100644 index 0000000000..677a0b7142 --- /dev/null +++ b/shared-module/keypad/KeyMatrix.c @@ -0,0 +1,182 @@ +/* + * 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 + +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/keypad/EventQueue.h" +#include "shared-bindings/keypad/KeyMatrix.h" +#include "shared-bindings/keypad/__init__.h" +#include "shared-bindings/util.h" +#include "supervisor/port.h" +#include "supervisor/shared/tick.h" + +static mp_uint_t row_column_to_key_number(keypad_keymatrix_obj_t *self, mp_uint_t row, mp_uint_t column) { + return row * self->column_digitalinouts->len + column; +} + +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_column_pins, mcu_pin_obj_t *column_pins[], bool columns_to_anodes, mp_float_t interval, size_t max_events) { + + mp_obj_t row_dios[num_row_pins]; + for (size_t row = 0; row < num_row_pins; row++) { + 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[row]); + common_hal_digitalio_digitalinout_switch_to_input(dio, columns_to_anodes ? PULL_UP : PULL_DOWN); + row_dios[row] = dio; + } + self->row_digitalinouts = mp_obj_new_tuple(num_row_pins, row_dios); + + mp_obj_t column_dios[num_column_pins]; + for (size_t column = 0; column < num_column_pins; column++) { + digitalio_digitalinout_obj_t *dio = m_new_obj(digitalio_digitalinout_obj_t); + dio->base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(dio, column_pins[column]); + common_hal_digitalio_digitalinout_switch_to_input(dio, columns_to_anodes ? PULL_UP : PULL_DOWN); + column_dios[column] = dio; + } + self->column_digitalinouts = mp_obj_new_tuple(num_column_pins, column_dios); + + self->currently_pressed = (bool *)gc_alloc(sizeof(bool) * num_row_pins * num_column_pins, false, false); + self->previously_pressed = (bool *)gc_alloc(sizeof(bool) * num_row_pins * num_column_pins, false, false); + + self->columns_to_anodes = columns_to_anodes; + + keypad_eventqueue_obj_t *events = m_new_obj(keypad_eventqueue_obj_t); + events->base.type = &keypad_eventqueue_type; + common_hal_keypad_eventqueue_construct(events, max_events); + self->events = events; + + self->interval_ticks = (mp_uint_t)(interval * 1024); // interval * 1000 * (1024/1000) + self->last_scan_ticks = port_get_raw_ticks(NULL); + + // Add self to the list of active keypad scanners. + keypad_register_scanner((keypad_scanner_obj_t *)self); + + supervisor_enable_tick(); +} + +void common_hal_keypad_keymatrix_deinit(keypad_keymatrix_obj_t *self) { + if (common_hal_keypad_keymatrix_deinited(self)) { + return; + } + + // Remove self from the list of active keypad scanners first. + keypad_deregister_scanner((keypad_scanner_obj_t *)self); + + for (size_t row = 0; row < common_hal_keypad_keymatrix_get_row_count(self); row++) { + common_hal_digitalio_digitalinout_deinit(self->row_digitalinouts->items[row]); + } + self->row_digitalinouts = MP_ROM_NONE; + + for (size_t column = 0; column < common_hal_keypad_keymatrix_get_column_count(self); column++) { + common_hal_digitalio_digitalinout_deinit(self->column_digitalinouts->items[column]); + } + self->column_digitalinouts = MP_ROM_NONE; +} + +bool common_hal_keypad_keymatrix_deinited(keypad_keymatrix_obj_t *self) { + return self->row_digitalinouts == MP_ROM_NONE; +} + +size_t common_hal_keypad_keymatrix_get_key_count(keypad_keymatrix_obj_t *self) { + return common_hal_keypad_keymatrix_get_row_count(self) * common_hal_keypad_keymatrix_get_column_count(self); +} + +size_t common_hal_keypad_keymatrix_get_row_count(keypad_keymatrix_obj_t *self) { + return self->row_digitalinouts->len; +} + +size_t common_hal_keypad_keymatrix_get_column_count(keypad_keymatrix_obj_t *self) { + return self->column_digitalinouts->len; +} + +mp_uint_t common_hal_keypad_keymatrix_row_column_to_key_number(keypad_keymatrix_obj_t *self, mp_uint_t row, mp_uint_t column) { + return row_column_to_key_number(self, row, column); +} + +void common_hal_keypad_keymatrix_key_number_to_row_column(keypad_keymatrix_obj_t *self, mp_uint_t key_number, mp_uint_t *row, mp_uint_t *column) { + const size_t num_columns = common_hal_keypad_keymatrix_get_column_count(self); + *row = key_number / num_columns; + *column = key_number % num_columns; +} + +mp_obj_t common_hal_keypad_keymatrix_get_events(keypad_keymatrix_obj_t *self) { + return MP_OBJ_FROM_PTR(self->events); +} + +void common_hal_keypad_keymatrix_reset(keypad_keymatrix_obj_t *self) { + const size_t key_count = common_hal_keypad_keymatrix_get_key_count(self); + + supervisor_acquire_lock(&keypad_scanners_linked_list_lock); + memset(self->previously_pressed, false, key_count); + memset(self->currently_pressed, false, key_count); + supervisor_release_lock(&keypad_scanners_linked_list_lock); +} + +void keypad_keymatrix_scan(keypad_keymatrix_obj_t *self) { + uint64_t now = port_get_raw_ticks(NULL); + if (now - self->last_scan_ticks < self->interval_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 or pull-down, + // depending on the diode orientation. + for (size_t row = 0; row < common_hal_keypad_keymatrix_get_row_count(self); row++) { + // Switch this row to an output and set level appropriately + // Set low if columns_to_anodes is true, else set high. + common_hal_digitalio_digitalinout_switch_to_output( + self->row_digitalinouts->items[row], !self->columns_to_anodes, DRIVE_MODE_PUSH_PULL); + + for (size_t column = 0; column < common_hal_keypad_keymatrix_get_column_count(self); column++) { + mp_uint_t key_number = row_column_to_key_number(self, row, column); + const bool previous = self->currently_pressed[key_number]; + self->previously_pressed[key_number] = previous; + + // Get the current state, by reading whether the column got pulled to the row value or not. + // If low and columns_to_anodes is true, the key is pressed. + // If high and columns_to_anodes is false, the key is pressed. + const bool current = + common_hal_digitalio_digitalinout_get_value(self->column_digitalinouts->items[column]) != + self->columns_to_anodes; + self->currently_pressed[key_number] = current; + + // Record any transitions. + if (previous != current) { + keypad_eventqueue_record(self->events, key_number, current); + } + } + + // Switch the row back to an input, pulled appropriately + common_hal_digitalio_digitalinout_switch_to_input( + self->row_digitalinouts->items[row], self->columns_to_anodes ? PULL_UP : PULL_DOWN); + } +} diff --git a/shared-module/keypad/KeyMatrix.h b/shared-module/keypad/KeyMatrix.h new file mode 100644 index 0000000000..bd7e2d23e2 --- /dev/null +++ b/shared-module/keypad/KeyMatrix.h @@ -0,0 +1,53 @@ +/* + * 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 "py/obj.h" +#include "py/objtuple.h" + +#include "common-hal/digitalio/DigitalInOut.h" +#include "shared-module/keypad/__init__.h" +#include "shared-module/keypad/EventQueue.h" + +typedef struct { + mp_obj_base_t base; + // All scanners have a next field here, to keep a linked list of active scanners. + keypad_scanner_obj_t *next; + mp_obj_tuple_t *row_digitalinouts; + mp_obj_tuple_t *column_digitalinouts; + mp_uint_t interval_ticks; + uint64_t last_scan_ticks; + bool *previously_pressed; + bool *currently_pressed; + keypad_eventqueue_obj_t *events; + bool columns_to_anodes; +} keypad_keymatrix_obj_t; + +void keypad_keymatrix_scan(keypad_keymatrix_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYMATRIX_H diff --git a/shared-module/keypad/Keys.c b/shared-module/keypad/Keys.c new file mode 100644 index 0000000000..1f232a03ed --- /dev/null +++ b/shared-module/keypad/Keys.c @@ -0,0 +1,133 @@ +/* + * 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 + +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/keypad/EventQueue.h" +#include "shared-bindings/keypad/Keys.h" +#include "shared-bindings/keypad/__init__.h" +#include "supervisor/port.h" +#include "supervisor/shared/tick.h" + +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, mp_float_t interval, size_t max_events) { + mp_obj_t dios[num_pins]; + + for (size_t i = 0; i < num_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, pins[i]); + if (pull) { + common_hal_digitalio_digitalinout_set_pull(dio, value_when_pressed ? PULL_DOWN : PULL_UP); + } + dios[i] = dio; + } + + self->digitalinouts = mp_obj_new_tuple(num_pins, dios); + self->currently_pressed = (bool *)gc_alloc(sizeof(bool) * num_pins, false, false); + self->previously_pressed = (bool *)gc_alloc(sizeof(bool) * num_pins, false, false); + self->value_when_pressed = value_when_pressed; + + self->interval_ticks = (mp_uint_t)(interval * 1024); // interval * 1000 * (1024/1000) + self->last_scan_ticks = port_get_raw_ticks(NULL); + + keypad_eventqueue_obj_t *events = m_new_obj(keypad_eventqueue_obj_t); + events->base.type = &keypad_eventqueue_type; + common_hal_keypad_eventqueue_construct(events, max_events); + self->events = events; + + // Add self to the list of active keypad scanners. + keypad_register_scanner((keypad_scanner_obj_t *)self); + + supervisor_enable_tick(); +} + +void common_hal_keypad_keys_deinit(keypad_keys_obj_t *self) { + if (common_hal_keypad_keys_deinited(self)) { + return; + } + + // Remove self from the list of active keypad scanners first. + keypad_deregister_scanner((keypad_scanner_obj_t *)self); + + for (size_t key = 0; key < common_hal_keypad_keys_get_key_count(self); key++) { + common_hal_digitalio_digitalinout_deinit(self->digitalinouts->items[key]); + } + self->digitalinouts = MP_ROM_NONE; + +} + +bool common_hal_keypad_keys_deinited(keypad_keys_obj_t *self) { + return self->digitalinouts == MP_ROM_NONE; +} + +size_t common_hal_keypad_keys_get_key_count(keypad_keys_obj_t *self) { + return self->digitalinouts->len; +} + +mp_obj_t common_hal_keypad_keys_get_events(keypad_keys_obj_t *self) { + return MP_OBJ_FROM_PTR(self->events); +} + +void common_hal_keypad_keys_reset(keypad_keys_obj_t *self) { + const size_t key_count = common_hal_keypad_keys_get_key_count(self); + + supervisor_acquire_lock(&keypad_scanners_linked_list_lock); + memset(self->previously_pressed, false, key_count); + memset(self->currently_pressed, false, key_count); + supervisor_release_lock(&keypad_scanners_linked_list_lock); +} + +void keypad_keys_scan(keypad_keys_obj_t *self) { + uint64_t now = port_get_raw_ticks(NULL); + if (now - self->last_scan_ticks < self->interval_ticks) { + // Too soon. Wait longer to debounce. + return; + } + + self->last_scan_ticks = now; + + const size_t key_count = common_hal_keypad_keys_get_key_count(self); + + for (mp_uint_t key_number = 0; key_number < key_count; key_number++) { + // Remember the previous up/down state. + const bool previous = self->currently_pressed[key_number]; + self->previously_pressed[key_number] = previous; + + // Get the current state. + const bool current = + common_hal_digitalio_digitalinout_get_value(self->digitalinouts->items[key_number]) == + self->value_when_pressed; + self->currently_pressed[key_number] = current; + + // Record any transitions. + if (previous != current) { + keypad_eventqueue_record(self->events, key_number, current); + } + } +} diff --git a/shared-module/keypad/Keys.h b/shared-module/keypad/Keys.h new file mode 100644 index 0000000000..3edae32439 --- /dev/null +++ b/shared-module/keypad/Keys.h @@ -0,0 +1,52 @@ +/* + * 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_KEYS_H +#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYS_H + +#include "py/obj.h" +#include "py/objtuple.h" + +#include "common-hal/digitalio/DigitalInOut.h" +#include "shared-module/keypad/__init__.h" +#include "shared-module/keypad/EventQueue.h" + +typedef struct { + mp_obj_base_t base; + // All scanners have a next field here, to keep a linked list of active scanners. + keypad_scanner_obj_t *next; + mp_obj_tuple_t *digitalinouts; + mp_uint_t interval_ticks; + uint64_t last_scan_ticks; + bool *previously_pressed; + bool *currently_pressed; + keypad_eventqueue_obj_t *events; + bool value_when_pressed; +} keypad_keys_obj_t; + +void keypad_keys_scan(keypad_keys_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYS_H diff --git a/shared-module/keypad/ShiftRegisterKeys.c b/shared-module/keypad/ShiftRegisterKeys.c new file mode 100644 index 0000000000..074f226998 --- /dev/null +++ b/shared-module/keypad/ShiftRegisterKeys.c @@ -0,0 +1,156 @@ +/* + * 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 + +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/keypad/EventQueue.h" +#include "shared-bindings/keypad/ShiftRegisterKeys.h" +#include "shared-bindings/keypad/__init__.h" +#include "supervisor/port.h" +#include "supervisor/shared/tick.h" + +void common_hal_keypad_shiftregisterkeys_construct(keypad_shiftregisterkeys_obj_t *self, mcu_pin_obj_t *clock_pin, mcu_pin_obj_t *data_pin, mcu_pin_obj_t *latch_pin, bool value_to_latch, size_t key_count, bool value_when_pressed, mp_float_t interval, size_t max_events) { + + digitalio_digitalinout_obj_t *clock = m_new_obj(digitalio_digitalinout_obj_t); + clock->base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(clock, clock_pin); + common_hal_digitalio_digitalinout_switch_to_output(clock, false, DRIVE_MODE_PUSH_PULL); + self->clock = clock; + + digitalio_digitalinout_obj_t *data = m_new_obj(digitalio_digitalinout_obj_t); + data->base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(data, data_pin); + common_hal_digitalio_digitalinout_switch_to_input(data, PULL_NONE); + self->data = data; + + digitalio_digitalinout_obj_t *latch = m_new_obj(digitalio_digitalinout_obj_t); + latch->base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(latch, latch_pin); + common_hal_digitalio_digitalinout_switch_to_output(latch, true, DRIVE_MODE_PUSH_PULL); + self->latch = latch; + self->value_to_latch = value_to_latch; + + self->currently_pressed = (bool *)gc_alloc(sizeof(bool) * key_count, false, false); + self->previously_pressed = (bool *)gc_alloc(sizeof(bool) * key_count, false, false); + self->value_when_pressed = value_when_pressed; + self->key_count = key_count; + + self->interval_ticks = (mp_uint_t)(interval * 1024); // interval * 1000 * (1024/1000) + self->last_scan_ticks = port_get_raw_ticks(NULL); + + keypad_eventqueue_obj_t *events = m_new_obj(keypad_eventqueue_obj_t); + events->base.type = &keypad_eventqueue_type; + common_hal_keypad_eventqueue_construct(events, max_events); + self->events = events; + + // Add self to the list of active keypad scanners. + keypad_register_scanner((keypad_scanner_obj_t *)self); + + supervisor_enable_tick(); +} + +void common_hal_keypad_shiftregisterkeys_deinit(keypad_shiftregisterkeys_obj_t *self) { + if (common_hal_keypad_shiftregisterkeys_deinited(self)) { + return; + } + + // Remove self from the list of active keypad scanners first. + keypad_deregister_scanner((keypad_scanner_obj_t *)self); + + + common_hal_digitalio_digitalinout_deinit(self->clock); + self->clock = MP_ROM_NONE; + + common_hal_digitalio_digitalinout_deinit(self->data); + self->data = MP_ROM_NONE; + + common_hal_digitalio_digitalinout_deinit(self->latch); + self->latch = MP_ROM_NONE; +} + +bool common_hal_keypad_shiftregisterkeys_deinited(keypad_shiftregisterkeys_obj_t *self) { + return self->clock == MP_ROM_NONE; +} + +size_t common_hal_keypad_shiftregisterkeys_get_key_count(keypad_shiftregisterkeys_obj_t *self) { + return self->key_count; +} + +mp_obj_t common_hal_keypad_shiftregisterkeys_get_events(keypad_shiftregisterkeys_obj_t *self) { + return MP_OBJ_FROM_PTR(self->events); +} + +void common_hal_keypad_shiftregisterkeys_reset(keypad_shiftregisterkeys_obj_t *self) { + const size_t key_count = common_hal_keypad_shiftregisterkeys_get_key_count(self); + + supervisor_acquire_lock(&keypad_scanners_linked_list_lock); + memset(self->previously_pressed, false, key_count); + memset(self->currently_pressed, false, key_count); + supervisor_release_lock(&keypad_scanners_linked_list_lock); +} + +void keypad_shiftregisterkeys_scan(keypad_shiftregisterkeys_obj_t *self) { + uint64_t now = port_get_raw_ticks(NULL); + if (now - self->last_scan_ticks < self->interval_ticks) { + // Too soon. Wait longer to debounce. + return; + } + + self->last_scan_ticks = now; + + // Latch (freeze) the current state of the input pins. + common_hal_digitalio_digitalinout_set_value(self->latch, self->value_to_latch); + + const size_t key_count = common_hal_keypad_shiftregisterkeys_get_key_count(self); + + for (mp_uint_t key_number = 0; key_number < key_count; key_number++) { + // Zero-th data appears on on the data pin immediately, without shifting. + common_hal_digitalio_digitalinout_set_value(self->clock, false); + + // Remember the previous up/down state. + const bool previous = self->currently_pressed[key_number]; + self->previously_pressed[key_number] = previous; + + // Get the current state. + const bool current = + common_hal_digitalio_digitalinout_get_value(self->data) == self->value_when_pressed; + self->currently_pressed[key_number] = current; + + // Trigger a shift to get the next bit. + common_hal_digitalio_digitalinout_set_value(self->clock, true); + + // Record any transitions. + if (previous != current) { + keypad_eventqueue_record(self->events, key_number, current); + } + } + + // Start reading the input pins again. + common_hal_digitalio_digitalinout_set_value(self->latch, !self->value_to_latch); +} diff --git a/shared-module/keypad/ShiftRegisterKeys.h b/shared-module/keypad/ShiftRegisterKeys.h new file mode 100644 index 0000000000..b04c622679 --- /dev/null +++ b/shared-module/keypad/ShiftRegisterKeys.h @@ -0,0 +1,56 @@ +/* + * 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_SHIFTREGISTERKEYS_H +#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_SHIFTREGISTERKEYS_H + +#include "py/obj.h" +#include "py/objtuple.h" + +#include "common-hal/digitalio/DigitalInOut.h" +#include "shared-module/keypad/__init__.h" +#include "shared-module/keypad/EventQueue.h" + +typedef struct { + mp_obj_base_t base; + // All scanners have a next field here, to keep a linked list of active scanners. + keypad_scanner_obj_t *next; + digitalio_digitalinout_obj_t *clock; + digitalio_digitalinout_obj_t *data; + digitalio_digitalinout_obj_t *latch; + size_t key_count; + mp_uint_t interval_ticks; + uint64_t last_scan_ticks; + bool *previously_pressed; + bool *currently_pressed; + keypad_eventqueue_obj_t *events; + bool value_when_pressed; + bool value_to_latch; +} keypad_shiftregisterkeys_obj_t; + +void keypad_shiftregisterkeys_scan(keypad_shiftregisterkeys_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_SHIFTREGISTERKEYS_H diff --git a/shared-module/keypad/__init__.c b/shared-module/keypad/__init__.c new file mode 100644 index 0000000000..fc41396617 --- /dev/null +++ b/shared-module/keypad/__init__.c @@ -0,0 +1,96 @@ +/* + * 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/keypad/Keys.h" +#include "shared-bindings/keypad/KeyMatrix.h" +#include "shared-bindings/keypad/ShiftRegisterKeys.h" +#include "supervisor/shared/lock.h" +#include "supervisor/shared/tick.h" + +supervisor_lock_t keypad_scanners_linked_list_lock; + +void keypad_tick(void) { + // Fast path. Return immediately there are no scanners. + if (!MP_STATE_VM(keypad_scanners_linked_list)) { + return; + } + + // Skip scanning if someone else has the lock. Don't wait for the lock. + if (supervisor_try_lock(&keypad_scanners_linked_list_lock)) { + mp_obj_t scanner = MP_STATE_VM(keypad_scanners_linked_list); + while (scanner) { + if (mp_obj_is_type(scanner, &keypad_keys_type)) { + keypad_keys_scan((keypad_keys_obj_t *)scanner); + } else if (mp_obj_is_type(scanner, &keypad_keymatrix_type)) { + keypad_keymatrix_scan((keypad_keymatrix_obj_t *)scanner); + } else if (mp_obj_is_type(scanner, &keypad_shiftregisterkeys_type)) { + keypad_shiftregisterkeys_scan((keypad_shiftregisterkeys_obj_t *)scanner); + } + + scanner = ((keypad_scanner_obj_t *)scanner)->next; + } + supervisor_release_lock(&keypad_scanners_linked_list_lock); + } +} + +void keypad_reset(void) { + if (MP_STATE_VM(keypad_scanners_linked_list)) { + supervisor_disable_tick(); + } + + MP_STATE_VM(keypad_scanners_linked_list) = NULL; + keypad_scanners_linked_list_lock = false; +} + +// Register a Keys, KeyMatrix, etc. that will be scanned in the background +void keypad_register_scanner(keypad_scanner_obj_t *scanner) { + supervisor_acquire_lock(&keypad_scanners_linked_list_lock); + scanner->next = MP_STATE_VM(keypad_scanners_linked_list); + MP_STATE_VM(keypad_scanners_linked_list) = scanner; + supervisor_release_lock(&keypad_scanners_linked_list_lock); +} + +// Remove scanner from the list of active scanners. +void keypad_deregister_scanner(keypad_scanner_obj_t *scanner) { + supervisor_acquire_lock(&keypad_scanners_linked_list_lock); + if (MP_STATE_VM(keypad_scanners_linked_list) == scanner) { + // Scanner is at the front; splice it out. + MP_STATE_VM(keypad_scanners_linked_list) = scanner->next; + scanner->next = NULL; + } else { + keypad_scanner_obj_t *current = MP_STATE_VM(keypad_scanners_linked_list); + while (current) { + if (current->next == scanner) { + // Splice myself out. + current->next = scanner->next; + scanner->next = NULL; + break; + } + current = current->next; + } + } + supervisor_release_lock(&keypad_scanners_linked_list_lock); +} diff --git a/shared-module/keypad/__init__.h b/shared-module/keypad/__init__.h new file mode 100644 index 0000000000..c5b8fe3a0a --- /dev/null +++ b/shared-module/keypad/__init__.h @@ -0,0 +1,50 @@ +/* + * 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 SHARED_MODULE_KEYPAD_H +#define SHARED_MODULE_KEYPAD_H + +#include "py/obj.h" +#include "supervisor/shared/lock.h" + +// All scanners must have a next field immediately following base. +// This is an ad hoc "superclass" struct for scanners, though they do +// not actually have a superclass relationship. +typedef struct _keypad_scanner_obj_t { + mp_obj_base_t base; + struct _keypad_scanner_obj_t *next; +} keypad_scanner_obj_t; + +extern supervisor_lock_t keypad_scanners_linked_list_lock; + +void keypad_tick(void); +void keypad_reset(void); + +void keypad_register_scanner(keypad_scanner_obj_t *scanner); +void keypad_deregister_scanner(keypad_scanner_obj_t *scanner); + + +#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..d36b975c21 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -7,7 +7,8 @@ SRC_SUPERVISOR = \ supervisor/shared/cpu.c \ supervisor/shared/filesystem.c \ supervisor/shared/flash.c \ - supervisor/shared/memory.c \ + supervisor/shared/lock.c \ + supervisor/shared/memory.c \ supervisor/shared/micropython.c \ supervisor/shared/safe_mode.c \ supervisor/shared/stack.c \