diff --git a/components/st3m/st3m_captouch.c b/components/st3m/st3m_captouch.c index 446dd744956416aa5f12396d77947ffa9ed6a2ce..fb90c0a53d226424843471424aa21a3d680d5285 100644 --- a/components/st3m/st3m_captouch.c +++ b/components/st3m/st3m_captouch.c @@ -12,6 +12,15 @@ #include <string.h> +// note: at some point there should be a calibration config load/save api +// that also allows users to set their own threshold. before doing that it +// would be more useful to start with the existing dynamic calibration data +// tho :D + +#define TOP_PETAL_THRESHOLD 8000 +#define BOTTOM_PETAL_THRESHOLD 12000 +#define PETAL_HYSTERESIS 1000 + static const char *TAG = "st3m-captouch"; static SemaphoreHandle_t _mu = NULL; @@ -20,10 +29,13 @@ static bool _request_calibration = false; static bool _calibrating = false; static inline void _pad_feed(st3m_petal_pad_state_t *pad, uint16_t data, - bool top) { - ringbuffer_write(&pad->rb, data); - int32_t thres = top ? 8000 : 12000; - thres = pad->pressed_prev ? thres - 1000 : thres; // some hysteresis + uint8_t index) { + bool top = (index % 2) == 0; + int32_t thres = + top ? (TOP_PETAL_THRESHOLD) : (BOTTOM_PETAL_THRESHOLD); + thres = pad->pressed_prev ? thres - (PETAL_HYSTERESIS) + : thres; // some hysteresis + pad->raw = data; pad->pressed = data > thres; pad->pressed_prev = pad->pressed; @@ -39,29 +51,71 @@ static inline void _pad_feed(st3m_petal_pad_state_t *pad, uint16_t data, } } -static inline void _petal_process(st3m_petal_state_t *petal, bool top) { +// roughly matches the behavior of the legacy api. someday we should have more +// meaningful output units. +#define POS_AMPLITUDE 40000 +#define POS_AMPLITUDE_SHIFT 2 +#define POS_DIV_MIN 1000 +static inline void _petal_process(st3m_petal_state_t *petal, uint8_t index) { + bool top = (index % 2) == 0; + int32_t thres = top ? (TOP_PETAL_THRESHOLD) : (BOTTOM_PETAL_THRESHOLD); + thres = + petal->pressed ? thres - (PETAL_HYSTERESIS) : thres; // some hysteresis + int32_t distance; + int32_t angle; if (top) { - petal->pressed = - petal->base.pressed || petal->ccw.pressed || petal->cw.pressed; petal->pressure = (petal->base.pressure + petal->ccw.pressure + petal->cw.pressure) / 3; - int32_t left = ringbuffer_avg(&petal->ccw.rb); - int32_t right = ringbuffer_avg(&petal->cw.rb); - int32_t base = ringbuffer_avg(&petal->base.rb); - petal->pos_distance = (left + right) / 2 - base; - petal->pos_angle = right - left; + int32_t raw = petal->base.raw + petal->ccw.raw + petal->cw.raw; + petal->pressed = raw > thres; + int32_t left = petal->ccw.raw; + int32_t right = petal->cw.raw; + int32_t base = petal->base.raw; + int32_t tip = (left + right) >> 1; + distance = tip - base; + distance *= (POS_AMPLITUDE) >> (POS_AMPLITUDE_SHIFT); + distance /= ((tip + base) >> (POS_AMPLITUDE_SHIFT)) + (POS_DIV_MIN); + angle = right - left; + angle *= (POS_AMPLITUDE) >> (POS_AMPLITUDE_SHIFT); + angle /= ((right + left) >> (POS_AMPLITUDE_SHIFT)) + (POS_DIV_MIN); #if defined(CONFIG_FLOW3R_HW_GEN_P3) - petal->pos_distance = -petal->pos_distance; + distance = -pos_distance; #endif } else { - petal->pressed = petal->base.pressed || petal->tip.pressed; petal->pressure = (petal->base.pressure + petal->tip.pressure) / 2; - int32_t base = ringbuffer_avg(&petal->base.rb); - int32_t tip = ringbuffer_avg(&petal->tip.rb); - petal->pos_distance = tip - base; - petal->pos_angle = 0; + int32_t raw = petal->base.raw + petal->tip.raw; + if(index == 5) raw *= 2; + petal->pressed = raw > thres; + int32_t base = petal->base.raw; + int32_t tip = petal->tip.raw; + if (index == 5) { + distance = + petal->pressed ? (tip * 5) / 4 - 40000 : 0; // bad hack pt2 + } else { + distance = tip - base; + distance *= (POS_AMPLITUDE) >> (POS_AMPLITUDE_SHIFT); + distance /= ((tip + base) >> (POS_AMPLITUDE_SHIFT)) + (POS_DIV_MIN); + } + angle = 0; + } + if ((!petal->press_event_new) || petal->fresh) { + petal->press_event_new = petal->pressed; + petal->fresh = false; } + // moved filter behind the nonlinearity to get more consistent response + // times, also replaced comb with pole for slightly lower lag with similar + // noise. maybe switch to higher order in the future for even lower lag but + // not sure if that's a good idea. graphical display looks good for now, so + // we're leaving fine tuning for when the mapping is a bit more polished. + int8_t f_div_pow = 4; + // widescreen ratio for better graphics + int8_t f_mult_old = 9; + int8_t f_mult_new = (1 << f_div_pow) - f_mult_old; + petal->pos_distance = + (f_mult_old * petal->pos_distance + f_mult_new * distance) >> f_div_pow; + petal->pos_angle = + (f_mult_old * petal->pos_angle + f_mult_new * angle) >> f_div_pow; } static void _on_data(const flow3r_bsp_captouch_state_t *st) { @@ -73,17 +127,17 @@ static void _on_data(const flow3r_bsp_captouch_state_t *st) { if (top) { #if defined(CONFIG_FLOW3R_HW_GEN_P3) // Hack for P3 badges, pretend tip is base. - _pad_feed(&_state.petals[ix].base, st->petals[ix].tip.raw, true); + _pad_feed(&_state.petals[ix].base, st->petals[ix].tip.raw, ix); #else - _pad_feed(&_state.petals[ix].base, st->petals[ix].base.raw, true); + _pad_feed(&_state.petals[ix].base, st->petals[ix].base.raw, ix); #endif - _pad_feed(&_state.petals[ix].cw, st->petals[ix].cw.raw, true); - _pad_feed(&_state.petals[ix].ccw, st->petals[ix].ccw.raw, true); - _petal_process(&_state.petals[ix], true); + _pad_feed(&_state.petals[ix].cw, st->petals[ix].cw.raw, ix); + _pad_feed(&_state.petals[ix].ccw, st->petals[ix].ccw.raw, ix); + _petal_process(&_state.petals[ix], ix); } else { - _pad_feed(&_state.petals[ix].base, st->petals[ix].base.raw, false); - _pad_feed(&_state.petals[ix].tip, st->petals[ix].tip.raw, false); - _petal_process(&_state.petals[ix], false); + _pad_feed(&_state.petals[ix].base, st->petals[ix].base.raw, ix); + _pad_feed(&_state.petals[ix].tip, st->petals[ix].tip.raw, ix); + _petal_process(&_state.petals[ix], ix); } } @@ -126,17 +180,18 @@ static void _refresh_petal_events(uint8_t petal_ix) { pt->cw.press_event = pt->cw.press_event_new; pt->ccw.press_event = pt->ccw.press_event_new; pt->base.press_event = pt->base.press_event_new; - pt->press_event = - pt->base.press_event || pt->ccw.press_event || pt->cw.press_event; + pt->press_event = pt->press_event_new; pt->cw.fresh = true; pt->ccw.fresh = true; pt->base.fresh = true; + pt->fresh = true; } else { pt->tip.press_event = pt->tip.press_event_new; pt->base.press_event = pt->base.press_event_new; - pt->press_event = pt->tip.press_event || pt->base.press_event; + pt->press_event = pt->press_event_new; pt->tip.fresh = true; pt->base.fresh = true; + pt->fresh = true; } } diff --git a/components/st3m/st3m_captouch.h b/components/st3m/st3m_captouch.h index 170b9c6da1df9a96d6ec98fef89ba5128a1ff332..c2dd1e67cb63479c4b3656b77532119a15a2183c 100644 --- a/components/st3m/st3m_captouch.h +++ b/components/st3m/st3m_captouch.h @@ -37,11 +37,12 @@ // touch. Top petals have two degrees of freedom, bottom petals have a // single degree of freedom (distance from center). -#include "st3m_ringbuffer.h" - // NOTE: keep the enum definitions below in-sync with flow3r_bsp_captouch.h, as // they are converted by numerical value internally. +#include <stdbool.h> +#include <stdint.h> + // One of the four possible touch points (pads) on a petal. Top petals have // base/cw/ccw. Bottom petals have base/tip. typedef enum { @@ -66,8 +67,8 @@ typedef enum { // State of capacitive touch for a petal's pad. typedef struct { - // Raw data ringbuffer. - st3m_ringbuffer_t rb; + // Raw data. + uint16_t raw; // Whether the pad is currently being touched. Calculated from ringbuffer // data. bool pressed; @@ -82,6 +83,8 @@ typedef struct { // State of capacitive touch for a petal. typedef struct { + bool press_event_new; + bool fresh; // Is this a top or bottom petal? st3m_petal_kind_t kind; @@ -108,8 +111,11 @@ typedef struct { // // Arbitrary units around (0, 0). // TODO(q3k): normalize and document. - float pos_distance; - float pos_angle; + + // note moon2: changed these back to int16, no idea what was the motivation + // here, there wasn't even a division to warrant it. + int32_t pos_distance; + int32_t pos_angle; } st3m_petal_state_t; typedef struct { diff --git a/docs/badge/usage.rst b/docs/badge/usage.rst index 2225953f88471b45b4bfbf4c7973d0013f3dd5c7..60c3ae7ebdb0fe766272f7fc5c77b78cdf1a4f87 100644 --- a/docs/badge/usage.rst +++ b/docs/badge/usage.rst @@ -249,3 +249,18 @@ Reboot ^^^^^^ Reboot flow3r. + +.. _usage_troubleshooting: + +Troubleshooting +--------------- + +Captouch doesn't register inputs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +At this point in time the captouch driver is silently calibrated once during every boot. This calibration expects no petals to be pressed, so if you hold flow3r in such a way that a press should be registered during calibration the petal will function poorly during this boot cycle. From experimental data we have found that similar effects are present when flow3r is face-up on a couch or similar surface. This is all in all not ideal, we're planning to add explicit calibration and persistent data storage in a future firmware release. For now we personally find it easiest to boot the badge while holding it from the back with one hand or while it is lying on a non-couchy surface. Apologies for the inconvenience. PS: Fingertips just so curling around the edge of the badge to get a proper grip is usually not an issue. + +Captouch of petal 5 seems to be only working in the lower half +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This not a defect but a driver peculiarity and will be fixed in the future.