Skip to content
Snippets Groups Projects
Commit d837cf76 authored by moon2's avatar moon2 :speech_balloon:
Browse files

captouch driver: various post processing changes

- petal pressed takes now raw data from all segments into account
- use ratios for positional captouch, improves "couch effect"
- position filter changes: comb->pole, before->after nonlinearity
- slightly better petal 5 missing data simulator
- troubleshooting section in docs
parent 76a9d8a7
Branches
Tags
1 merge request!599captouch: improved signal processing
......@@ -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;
}
}
......
......@@ -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 {
......
......@@ -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.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment