|
| 1 | +/* |
| 2 | + * This file is part of the Micro Python project, http://micropython.org/ |
| 3 | + * |
| 4 | + * The MIT License (MIT) |
| 5 | + * |
| 6 | + * Copyright (c) 2021 Dan Halbert for Adafruit Industries |
| 7 | + * |
| 8 | + * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 9 | + * of this software and associated documentation files (the "Software"), to deal |
| 10 | + * in the Software without restriction, including without limitation the rights |
| 11 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 12 | + * copies of the Software, and to permit persons to whom the Software is |
| 13 | + * furnished to do so, subject to the following conditions: |
| 14 | + * |
| 15 | + * The above copyright notice and this permission notice shall be included in |
| 16 | + * all copies or substantial portions of the Software. |
| 17 | + * |
| 18 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 19 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 20 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 21 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 22 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 23 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 24 | + * THE SOFTWARE. |
| 25 | + */ |
| 26 | + |
| 27 | +#include "py/enum.h" |
| 28 | +#include "py/objproperty.h" |
| 29 | +#include "shared-bindings/keypad/Keys.h" |
| 30 | +#include "shared-bindings/keypad/State.h" |
| 31 | +#include "shared-bindings/microcontroller/Pin.h" |
| 32 | +#include "py/runtime.h" |
| 33 | + |
| 34 | +//| class Keys: |
| 35 | +//| """Manage a set of independent keys.""" |
| 36 | +//| |
| 37 | +//| def __init__(self, pins: Sequence[microcontroller.Pin], *, level_when_pressed: bool, pull: bool = True) -> None: |
| 38 | +//| """ |
| 39 | +//| Create a `Keys` object that will scan keys attached to the given sequence of pins. |
| 40 | +//| Each key is independent and attached to its own pin. |
| 41 | +//| |
| 42 | +//| :param Sequence[microcontroller.Pin] pins: The pins attached to the keys. |
| 43 | +//| The key numbers correspond to indices into this sequence. |
| 44 | +//| :param bool value_when_pressed: ``True`` if the pin reads high when the key is pressed. |
| 45 | +//| ``False`` if the pin reads low (is grounded) when the key is pressed. |
| 46 | +//| All the pins must be connected in the same way. |
| 47 | +//| :param bool pull: ``True`` if an internal pull-up or pull-down should be |
| 48 | +//| enabled on each pin. A pull-up will be used if ``value_when_pressed`` is ``False``; |
| 49 | +//| a pull-down will be used if it is ``True``. |
| 50 | +//| If an external pull is already provided for all the pins, you can set ``pull`` to ``False``. |
| 51 | +//| However, enabling an internal pull when an external one is already present is not a problem; |
| 52 | +//| it simply uses slightly more current. |
| 53 | +//| |
| 54 | +//| Calls `scan()` once before returning, to initialize internal state. |
| 55 | +//| """ |
| 56 | +//| ... |
| 57 | + |
| 58 | +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) { |
| 59 | + keypad_keys_obj_t *self = m_new_obj(keypad_keys_obj_t); |
| 60 | + self->base.type = &keypad_keys_type; |
| 61 | + enum { ARG_pins, ARG_value_when_pressed, ARG_pull }; |
| 62 | + static const mp_arg_t allowed_args[] = { |
| 63 | + { MP_QSTR_pins, MP_ARG_REQUIRED | MP_ARG_OBJ }, |
| 64 | + { MP_QSTR_value_when_pressed, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_BOOL }, |
| 65 | + { MP_QSTR_pull, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, |
| 66 | + }; |
| 67 | + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; |
| 68 | + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); |
| 69 | + |
| 70 | + mp_obj_t pins = args[ARG_pins].u_obj; |
| 71 | + const mp_uint_t num_pins = mp_obj_int_get_uint_checked(mp_obj_len(pins)); |
| 72 | + const bool value_when_pressed = args[ARG_value_when_pressed].u_bool; |
| 73 | + |
| 74 | + mcu_pin_obj_t *pins_array[num_pins]; |
| 75 | + |
| 76 | + for (mp_uint_t i = 0; i < num_pins; i++) { |
| 77 | + mcu_pin_obj_t *pin = |
| 78 | + validate_obj_is_free_pin(mp_obj_subscr(pins, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL)); |
| 79 | + pins_array[i] = pin; |
| 80 | + } |
| 81 | + |
| 82 | + common_hal_keypad_keys_construct(self, num_pins, pins_array, value_when_pressed, args[ARG_pull].u_bool); |
| 83 | + return MP_OBJ_FROM_PTR(self); |
| 84 | +} |
| 85 | + |
| 86 | +//| def scan(self) -> None: |
| 87 | +//| """Scan the keys and record which are newly pressed, still pressed, |
| 88 | +//| newly released, and still released. For convenient activity checking, |
| 89 | +//| """ |
| 90 | +//| ... |
| 91 | +//| |
| 92 | +STATIC mp_obj_t keypad_keys_scan(mp_obj_t self_in) { |
| 93 | + keypad_keys_obj_t *self = MP_OBJ_TO_PTR(self_in); |
| 94 | + |
| 95 | + common_hal_keypad_keys_scan(self); |
| 96 | + return MP_ROM_NONE; |
| 97 | +} |
| 98 | +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keys_scan_obj, keypad_keys_scan); |
| 99 | + |
| 100 | +//| def state(self, key_num: int) -> keypad.State: |
| 101 | +//| """Return the state for the given ``key_num``, based |
| 102 | +//| on the results of the most recent `scan()`. |
| 103 | +//| |
| 104 | +//| :param int key_num: Key number: corresponds to the sequence of pins |
| 105 | +//| :return: state of key number ``key_num`` |
| 106 | +//| :rtype: keypad.State: One of `State.JUST_PRESSED`, `State.STILL_PRESSED`, |
| 107 | +//| `State.JUST_RELEASED`, or `State.STILL_RELEASED`. |
| 108 | +//| The inclusive states `State.PRESSED` and `State.RELEASED` will *not* be returned. |
| 109 | +//| """ |
| 110 | +//| ... |
| 111 | +//| |
| 112 | +STATIC mp_obj_t keypad_keys_state(mp_obj_t self_in, mp_obj_t key_num_obj) { |
| 113 | + keypad_keys_obj_t *self = MP_OBJ_TO_PTR(self_in); |
| 114 | + mp_int_t key_num = mp_obj_int_get_checked(key_num_obj); |
| 115 | + if (key_num < 0 || key_num >= common_hal_keypad_keys_length(self)) { |
| 116 | + mp_raise_ValueError_varg(translate("%q out of range"), MP_QSTR_key_num); |
| 117 | + } |
| 118 | + |
| 119 | + return cp_enum_find(&keypad_state_type, common_hal_keypad_keys_state(self, (mp_uint_t)key_num)); |
| 120 | +} |
| 121 | +MP_DEFINE_CONST_FUN_OBJ_2(keypad_keys_state_obj, keypad_keys_state); |
| 122 | + |
| 123 | +//| def keys_with_state(self, state: State, into_list: List[Optional[int]]) -> None: |
| 124 | +//| """Store key numbers of keys with state ``state`` in ``into_list``. |
| 125 | +//| The states checked are based on the results of the most recent `scan()`. |
| 126 | +//| |
| 127 | +//| You can use the inclusive states `State.PRESSED` and `State.RELEASED`. |
| 128 | +//| `State.PRESSED` includes states `State.JUST_PRESSED` and `State.STILL_PRESSED`. |
| 129 | +//| `State.RELEASED` includes `State.JUST_RELEASED` and `State.STILL_RELEASED`. |
| 130 | +//| |
| 131 | +//| The key numbers are stored in ``into_list`` consecutively, up to ``len(into_list)``. |
| 132 | +//| The ``into_list`` is not extended if there are more keys with the given |
| 133 | +//| state than list slots. Instead, leftover key numbers are discarded. |
| 134 | +//| If there are fewer keys with the given state, the rest of ``into_list`` |
| 135 | +//| is padded with ``None``. For example, |
| 136 | +//| if four keys are being monitored, and only key numbers 0 and 2 have the given state, |
| 137 | +//| ``into_list`` will be set to ``[0, 2, None, None]``. You can iterate over |
| 138 | +//| ``into_list`` and stop when you find the first ``None``. |
| 139 | +//| """ |
| 140 | +//| ... |
| 141 | +//| |
| 142 | +STATIC mp_obj_t keypad_keys_keys_with_state(mp_obj_t self_in, mp_obj_t state_in, mp_obj_t into_list_in) { |
| 143 | + keypad_keys_obj_t *self = MP_OBJ_TO_PTR(self_in); |
| 144 | + |
| 145 | + if (!mp_obj_is_type(state_in, &keypad_state_type)) { |
| 146 | + mp_raise_ValueError_varg(translate("Expected a %q"), keypad_state_type.name); |
| 147 | + } |
| 148 | + |
| 149 | + if (!mp_obj_is_type(into_list_in, &mp_type_list)) { |
| 150 | + mp_raise_ValueError_varg(translate("Expected a %q"), mp_type_list.name); |
| 151 | + } |
| 152 | + |
| 153 | + int state = cp_enum_value(&keypad_state_type, state_in); |
| 154 | + mp_obj_list_t *into_list = MP_OBJ_TO_PTR(into_list_in); |
| 155 | + |
| 156 | + common_hal_keypad_keys_keys_with_state(self, state, into_list); |
| 157 | + |
| 158 | + return MP_ROM_NONE; |
| 159 | +} |
| 160 | +MP_DEFINE_CONST_FUN_OBJ_3(keypad_keys_keys_with_state_obj, keypad_keys_keys_with_state); |
| 161 | + |
| 162 | +STATIC const mp_rom_map_elem_t keypad_keys_locals_dict_table[] = { |
| 163 | + { MP_ROM_QSTR(MP_QSTR_keys_with_state), MP_ROM_PTR(&keypad_keys_keys_with_state_obj) }, |
| 164 | + { MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&keypad_keys_scan_obj) }, |
| 165 | + { MP_ROM_QSTR(MP_QSTR_state), MP_ROM_PTR(&keypad_keys_state_obj) }, |
| 166 | +}; |
| 167 | + |
| 168 | +STATIC MP_DEFINE_CONST_DICT(keypad_keys_locals_dict, keypad_keys_locals_dict_table); |
| 169 | + |
| 170 | +const mp_obj_type_t keypad_keys_type = { |
| 171 | + { &mp_type_type }, |
| 172 | + .name = MP_QSTR_Keys, |
| 173 | + .make_new = keypad_keys_make_new, |
| 174 | + .locals_dict = (mp_obj_t)&keypad_keys_locals_dict, |
| 175 | +}; |
0 commit comments