From 37013b656f29ca128a56f60f342c856750cc623c Mon Sep 17 00:00:00 2001 From: moon2 <moon2protonmail@protonmail.com> Date: Tue, 17 Dec 2024 16:13:08 +0100 Subject: [PATCH] captouch: tuplify, docs --- components/flow3r_bsp/flow3r_bsp_ad7147.c | 20 ++++--- .../micropython/usermodule/mp_captouch.c | 46 ++++++++++++--- docs/api/captouch.rst | 58 ++++++++++++++++--- python_payload/apps/fil3s/reader.py | 2 +- 4 files changed, 101 insertions(+), 25 deletions(-) diff --git a/components/flow3r_bsp/flow3r_bsp_ad7147.c b/components/flow3r_bsp/flow3r_bsp_ad7147.c index 387cb25729..714b9e2ff2 100644 --- a/components/flow3r_bsp/flow3r_bsp_ad7147.c +++ b/components/flow3r_bsp/flow3r_bsp_ad7147.c @@ -33,6 +33,10 @@ BABYどこまででも */ +#define MAX_POS_AFE (63) +#define MAX_NEG_AFE (63) +#define MAX_AFE (MAX_POS_AFE + MAX_NEG_AFE) + static ad7147_chip_t _top = { .name = "top", .is_bot = false, @@ -417,7 +421,7 @@ static int _get_calib_data_index(ad7147_chip_t *chip, int i, int *petal_ret, void get_stage_hw_config(ad7147_chip_t *chip, ad7147_sequence_t *seq_out, uint8_t i, uint16_t channel_mask) { int offset = chip->stages[i].afe_offset; - offset = offset > 126 ? 126 : offset; + offset = offset > (MAX_AFE) ? (MAX_AFE) : offset; seq_out->stages[i].channel_mask = channel_mask; // DATASHEET VIOLATION 2 // the datasheet recommends to connect captouch pads to the internal bias @@ -449,12 +453,12 @@ void get_stage_hw_config(ad7147_chip_t *chip, ad7147_sequence_t *seq_out, // seq_out->stages[i].idle_to_bias = !chip->is_bot; // experiment C: none (winner) seq_out->stages[i].idle_to_bias = false; - if (offset < 63) { + if (offset <= MAX_NEG_AFE) { seq_out->stages[i].neg_afe_offset = offset; seq_out->stages[i].pos_afe_offset = 0; } else { - seq_out->stages[i].neg_afe_offset = 63; - seq_out->stages[i].pos_afe_offset = offset - 63; + seq_out->stages[i].neg_afe_offset = MAX_NEG_AFE; + seq_out->stages[i].pos_afe_offset = offset - MAX_NEG_AFE; } } @@ -558,15 +562,15 @@ static bool _stage_afe_tweak(ad7147_chip_t *chip, size_t cix) { if (offset <= 0 && diff < 0) { return false; } - if (offset >= 126 && diff > 0) { + if (offset >= (MAX_AFE) && diff > 0) { return false; } offset += diff; if (offset < 0) { offset = 0; } - if (offset > 126) { - offset = 126; + if (offset > (MAX_AFE)) { + offset = (MAX_AFE); } chip->stages[cix].afe_offset = offset; return true; @@ -1196,7 +1200,7 @@ static uint16_t amb_limit(int32_t data) { } static uint8_t afe_limit(int32_t data) { - return data > 126 ? 126 : (data < 0 ? 0 : data); + return data > (MAX_AFE) ? (MAX_AFE) : (data < 0 ? 0 : data); } void flow3r_bsp_ad7147_set_calibration_data(int32_t *data) { diff --git a/components/micropython/usermodule/mp_captouch.c b/components/micropython/usermodule/mp_captouch.c index 73e2f9821e..88a24d6dae 100644 --- a/components/micropython/usermodule/mp_captouch.c +++ b/components/micropython/usermodule/mp_captouch.c @@ -330,7 +330,7 @@ STATIC void mp_captouch_petal_state_attr(mp_obj_t self_in, qstr attr, switch (attr) { case MP_QSTR_log: { mp_obj_list_t *log = MP_OBJ_TO_PTR(self->log); - dest[0] = mp_obj_new_list(log->len, log->items); + dest[0] = mp_obj_new_tuple(log->len, log->items); } break; case MP_QSTR_top: dest[0] = mp_obj_new_bool(top); @@ -438,15 +438,16 @@ mp_captouch_state_new(const flow3r_bsp_captouch_data_t *underlying) { memcpy(&captouch->underlying, underlying, sizeof(flow3r_bsp_captouch_data_t)); - captouch->petals = mp_obj_new_list(0, NULL); + mp_obj_t petals[10]; for (int i = 0; i < 10; i++) { mp_captouch_petal_state_t *petal = m_new_obj(mp_captouch_petal_state_t); petal->base.type = &captouch_petal_state_type; petal->captouch = MP_OBJ_FROM_PTR(captouch); petal->ix = i; petal->log = _get_list_from_log(i); - mp_obj_list_append(captouch->petals, MP_OBJ_FROM_PTR(petal)); + petals[i] = MP_OBJ_FROM_PTR(petal); } + captouch->petals = mp_obj_new_tuple(10, petals); return MP_OBJ_FROM_PTR(captouch); } @@ -669,9 +670,40 @@ typedef struct { mp_obj_t petals; } captouch_config_obj_t; +#define ALLOW_COMBINED_TOP + +static int _check_petal_mode(int mode, int petal) { + if (petal & 1) { + if ((mode < 0) || (mode > 2)) { + mp_raise_ValueError(MP_ERROR_TEXT( + "mode not allowed (bottom petal modes: 0, 1, 2)")); + } + } else { +#ifdef ALLOW_COMBINED_TOP + if ((mode < 0) || (mode > 3)) { + mp_raise_ValueError( + MP_ERROR_TEXT("mode not allowed (top petals modes: 0, 3)")); + } +#else + if ((mode != 0) && (mode != 3)) { + mp_raise_ValueError( + MP_ERROR_TEXT("mode not allowed (top petals modes: 0, 3)")); + } +#endif + } + return mode; +} + static int _limit_petal_mode(int mode, int petal) { - int limit = 3 - (petal & 1); - return mode > limit ? limit : (mode < 0 ? 0 : mode); + if (petal & 1) { + return mode > 2 ? 2 : (mode < 0 ? 0 : mode); + } else { +#ifdef ALLOW_COMBINED_TOP + return mode > 3 ? 3 : (mode < 0 ? 0 : mode); +#else + return mode > 0 ? 3 : 0; +#endif + } } STATIC void captouch_petal_config_attr(mp_obj_t self_in, qstr attr, @@ -680,7 +712,7 @@ STATIC void captouch_petal_config_attr(mp_obj_t self_in, qstr attr, if (dest[0] != MP_OBJ_NULL) { if (attr == MP_QSTR_mode) { self->mode = - _limit_petal_mode(mp_obj_get_int(dest[1]), self->index); + _check_petal_mode(mp_obj_get_int(dest[1]), self->index); dest[0] = MP_OBJ_NULL; } else if (attr == MP_QSTR_logging) { self->logging = mp_obj_is_true(dest[1]); @@ -763,7 +795,7 @@ static mp_obj_t captouch_config_new(int mode, mp_obj_type_t *type) { } petals[petal] = MP_OBJ_FROM_PTR(petalconf); } - self->petals = mp_obj_new_list(10, petals); + self->petals = mp_obj_new_tuple(10, petals); return MP_OBJ_FROM_PTR(self); } diff --git a/docs/api/captouch.rst b/docs/api/captouch.rst index 38a626d914..6bc68e61da 100644 --- a/docs/api/captouch.rst +++ b/docs/api/captouch.rst @@ -17,7 +17,7 @@ You cannot instantiate this object directly, but for REPL experiments there is a .. py:class:: CaptouchState .. py:attribute:: petals - :type: List[CaptouchPetalState] + :type: Tuple[CaptouchPetalState] State of individual petals. @@ -99,9 +99,36 @@ You cannot instantiate this object directly, but for REPL experiments there is a May be affected by ``captouch.Config``. + .. py:attribute:: log + :type: Tuple[PetalLogFrame] + + Raw frame output of the captouch driver. Must be enabled by ``captouch.Config``. + + Since micropython and the captouch driver are running asynchronously we're providing + a list of all raw data points collected since the last ``.think()`` call. + + The lowest indices are the oldest frames, so that you could compile a complete log + (or one cropped to arbitrary length) simply by appending new data: + + .. code-block:: python + + def on_enter(self, vm): + super().on_enter(vm) + conf = captouch.Config.default() + conf.petals[0].logging = True + conf.apply() + self.log = list() + + def think(self, ins, delta_ms): + super().think(ins, delta_ms) + # append new frames to end of log + self.log += ins.captouch.petals[0].log + # crop old frames + self.log = self.log[-100:] + .. py:data:: PETAL_ANGLES - :type: list[complex] + :type: Tuple[complex] List of constants that can be used to rotate the output of the `.pos` attribute of both `CaptouchPetalState` and `PetalLogFrame` to align with the display (x, y) coordinates. @@ -171,7 +198,7 @@ except for 2 and 8 for a "dual joystick" mode, you increase your frame rate to u if you already have one around. .. py:attribute:: petals - :type: List[PetalConfig] + :type: Tuple[PetalConfig] Config of individual petals, indexed as in the ``CaptouchState`` object. @@ -180,17 +207,24 @@ except for 2 and 8 for a "dual joystick" mode, you increase your frame rate to u .. py:attribute:: mode :type: int - What kind of data should be collected for this petal. + What kind of data should be collected for this petal. Raises ``ValueError`` when set to an unallowed value. + + 0: No data at all. Allowed for all petals. + + 1: Button Mode: All pads combined, no positional output. Only allowed for bottom petals. - 0: No data at all + 2: 1D: Only radial position is provided. Only allowed for bottom petals. - 1: Button Mode: All pads combined, no positional output + 3: 2D: Full positional output. Only allowed for top petals. - 2: 1D: Only radial position is provided + Defaults to the maximum allowed value. - 3: 2D: Full positional output. Only available for top petals. + The integer value corresponds to the number of active chip channels. Data rate scales linearily per chip + at 0.75ms per channel plus a noisy overhead of 2-4ms typically. Bottom petals and petal 2 are connected to + one chip, the remaining top petals to another. - Default: 3 (top petals), 2 (bottom petals) + *Note: We discovered last-minute that modes 1 and 2 are not functioning properly for top petals, so they are + currently unavailable. We will try to fix them up in the future.* .. py:attribute:: logging :type: bool @@ -201,6 +235,12 @@ except for 2 and 8 for a "dual joystick" mode, you increase your frame rate to u Default: False + .. py:method:: set_min_mode(mode: int) -> None: + + If the current mode is lower than the argument, it gets increased to that value if allowed. If the value + is not allowed it is either set to the next-biggest allowed value or, if no such value exists, to the + largest allowed value. + Gestures -------- diff --git a/python_payload/apps/fil3s/reader.py b/python_payload/apps/fil3s/reader.py index b63c6005ef..dae2bdde98 100644 --- a/python_payload/apps/fil3s/reader.py +++ b/python_payload/apps/fil3s/reader.py @@ -63,7 +63,7 @@ class Reader(ActionView): self.captouch_config = captouch.Config.empty() for x in range(2, 10, 2): - self.captouch_config.petals[x].mode = 2 + self.captouch_config.petals[x].mode = 3 self.scroller = widgets.Scroller( self.captouch_config, -- GitLab