touch: plug into event system (+ refactoring)

* Interrupt now at level 15 so it can work in the input timer callback
* Remember last raw/conv/dots and expose unstable API to use it via new
  include <gint/drivers/touch.h>
* Put number of touches in the structures
This commit is contained in:
Lephe 2025-04-13 11:23:17 +02:00
parent 4e20f7bc5b
commit bc8b9863ae
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
12 changed files with 272 additions and 313 deletions

View file

@ -256,7 +256,6 @@ set(SOURCES
# Touch-screen driver
src/touch/i2c.c
src/touch/i2c_inth.c
src/touch/adconv.c
src/touch/touch.c
src/touch/driver.c
)

View file

@ -254,6 +254,10 @@ void keydev_tick(keydev_t *d, uint us);
// Low-level API to read events from the device
//---
/* keydev_queue_event(): Add an event in a device's buffer
Returns false if the event cannot be pushed. */
bool keydev_queue_event(keydev_t *d, key_event_t ev);
/* keydev_unqueue_event(): Retrieve the next keyboard event in queue
This source provides the queued KEYEV_UP, KEYEV_DOWN and KEYEV_HOLD events

View file

@ -0,0 +1,70 @@
//---
// gint:drivers:touch - Internals of the touch driver
//---
#ifndef GINT_DRIVERS_TOUCH
#define GINT_DRIVERS_TOUCH
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/config.h>
#include <gint/defs/types.h>
#include <gint/keyboard.h>
#if GINT_HW_CP
/* Access to the latest data of each stage of the touch input sequence.
* First stage is the raw output of the 0x84 command from I2C.
* Second stage is the same data after adjusting resolution and bit formats,
then determining then number of touches based on multi-touch parameters.
* Third stage is converting to screen-space coordinates based on calibration
parameters. */
/* First stage: raw output of command 0x84. There may not be data at all if the
input port indicates there are no touches. */
struct _touch_adraw {
/* Response to command 0x84 */
u16 x1, y1, z1, gh;
u16 x2, y2, z2, dm;
/* Whether data is present (if false, everything is 0 and no touches) */
bool has_data;
};
/* Second stage: decoded form of command 0x84 output. */
struct _touch_adconv {
/* Number of touches (0, 1 or 2) */
int touches;
int x1, y1;
int x2, y2;
int z1, z2;
u16 gh, dm;
};
/* Third stage: screen-space dots information. */
struct _touch_addots {
/* Number of touches (0, 1 or 2) */
int touches;
/* Valid if touches >= 1 */
int x1, y1, z1;
/* Valid if touches >= 2 */
int x2, y2, z2;
};
/* Run a scanning round and return the next event. This function is not
reentrant and is already called by the input timer. Don't call it! */
key_event_t touch_scan(void);
/* Get the last captured data from any of the 3 stages. All pointers can be
NULL; fills the structures that are provided. */
void touch_get_last_scan(struct _touch_adraw *raw,
struct _touch_adconv *conv, struct _touch_addots *dots);
#endif /* GINT_HW_CP */
#ifdef __cplusplus
}
#endif
#endif /* GINT_DRIVERS_TOUCH */

View file

@ -33,12 +33,8 @@ extern int touch_calib_get_os(touch_calibration_t *calib);
/* touch_calib_set() - set calibration information */
extern int touch_calib_set(touch_calibration_t *calib);
// low-level API
#include <gint/keyboard.h>
/* touch_next_event() - get the next touchscreen event */
extern key_event_t touch_next_event(void);
// TODO: Functions to query the immediate state of the touch display
// TODO| As per queue? Or real-time?
#endif /* GINT_HW_CP */

View file

@ -108,9 +108,7 @@ static int standard_repeater(GUNUSED int key, GUNUSED int duration, int count)
// Driver event generation
//---
/* keydev_queue_push(): Add an event in a device's buffer
Returns false if the event cannot be pushed. */
bool keydev_queue_push(keydev_t *d, key_event_t ev)
bool keydev_queue_event(keydev_t *d, key_event_t ev)
{
if(d->async_filter && !d->async_filter(ev))
return true;
@ -165,7 +163,7 @@ void keydev_process_state(keydev_t *d, uint8_t scan[12])
ev.col = col;
ev.key = keymatrix_to_keycode(row, col);
/* Update state only if the push succeeds */
if((diff & mask) && keydev_queue_push(d, ev))
if((diff & mask) && keydev_queue_event(d, ev))
d->state_now[row] = mode
? d->state_now[row] | mask
: d->state_now[row] & ~mask;
@ -221,7 +219,7 @@ void keydev_tick(keydev_t *d, uint us)
/* Generate the next repeat */
key_event_t repeat = keydev_repeat_event(d);
if(repeat.type != KEYEV_NONE)
keydev_queue_push(d, repeat);
keydev_queue_event(d, repeat);
d->time++;

View file

@ -14,6 +14,10 @@
#include <gint/drivers/iokbd.h>
#include <gint/hardware.h>
#if GINT_HW_CP
#include <gint/drivers/touch.h>
#endif
#include <string.h>
#include <stdlib.h>
@ -71,6 +75,12 @@ int keysc_tick(void)
abort();
}
#if GINT_HW_CP
key_event_t ev = touch_scan();
if(ev.type != KEYEV_NONE)
keydev_queue_event(&keysc_dev, ev);
#endif
return TIMER_CONTINUE;
}

View file

@ -1,166 +0,0 @@
//---
// gint:touch:adconv - 0x84 register data conversion
//---
#include <stdlib.h>
#include <gint/defs/util.h>
#include <gint/cpu.h>
#include <gint/config.h>
#if GINT_HW_CP
#include "./adconv.h"
#include "./i2c.h"
#include "./driver.h"
//---
// Internals
//---
/* __touch_drv_info - global driver information */
extern struct _touch_drv_info __touch_drv_info;
/* _adconv_check_dual() - check if it is dual or single */
static int _adconv_check_dual(
struct _touch_adconv *adconv,
struct _touch_adraw *adraw
) {
extern struct _touch_drv_info __touch_drv_info;
bool is_dual;
int x2;
int y2;
int z2;
int val;
cpu_atomic_start();
x2 = ((int)((uint)(adraw->x2) >> 6)) + ((adraw->x2 & 1) * -0x400);
y2 = ((int)((uint)(adraw->y2) >> 6)) + ((adraw->y2 & 1) * -0x400);
z2 = ((int)((uint)(adraw->z2) >> 4)) + ((adraw->z2 & 1) * -0x1000);
adconv->x2 = x2;
adconv->y2 = y2;
adconv->z2 = z2;
val = __touch_drv_info.calibration.dual_sensi_entry;
if (__touch_drv_info.adinfo.prev_is_dual)
val = __touch_drv_info.calibration.dual_sensi_leave;
is_dual = ((abs(z2) >= val) || (max(abs(x2),abs(y2)) >= val));
__touch_drv_info.adinfo.prev_is_dual = is_dual;
cpu_atomic_end();
return is_dual;
}
//---
// Public
//---
/* touch_adconv_get_raw() - read 0x84 register using I2C */
int touch_adconv_get_raw(struct _touch_adraw *adraw)
{
volatile uint8_t *IO_PRDR = (void*)0xa405013c;
if (((*IO_PRDR) & 0x20) != 0)
{
adraw->x1 = 0;
adraw->y1 = 0;
adraw->z1 = 0;
adraw->x2 = 0;
adraw->y2 = 0;
adraw->z2 = 0;
adraw->gh = 0;
cpu_atomic_start();
__touch_drv_info.adinfo.prev_is_dual = false;
cpu_atomic_end();
return 0;
}
i2c_reg_read(0x84, adraw, 16);
return 1;
}
/* touch_adconv_convert() - perform the raw conversion */
int touch_adconv_get_conv(
struct _touch_adconv *adconv,
struct _touch_adraw *adraw,
int type
) {
if (type == 0)
return 0;
adconv->x1 = adraw->x1 >> 4;
adconv->y1 = adraw->y1 >> 4;
adconv->z1 = adraw->z1 >> 4;
adconv->gh = adraw->gh >> 4;
adconv->dm = adraw->dm >> 6;
if (_adconv_check_dual(adconv, adraw) == 0)
return 1;
return 2;
}
/* touch_adconv_get_dots() - generate dots information */
int touch_adconv_get_dots(
struct _touch_addots *dots,
struct _touch_adconv *adconv,
int type
) {
int x_div;
int x_base;
int y_div;
int y_base;
cpu_atomic_start();
x_div = __touch_drv_info.calibration.x_div;
x_base = __touch_drv_info.calibration.x_base;
y_div = __touch_drv_info.calibration.y_div;
y_base = __touch_drv_info.calibration.y_base;
switch (type)
{
case 0:
dots->type = TS_DOTS_TYPE_OFF;
dots->x1 = 0;
dots->y1 = 0;
dots->z1 = 0;
dots->x2 = 0;
dots->y2 = 0;
dots->z2 = 0;
break;
case 1:
dots->type = TS_DOTS_TYPE_SINGLE;
dots->x1 = ((adconv->x1 - x_base) * 0x100) / x_div;
dots->y1 = ((adconv->y1 - y_base) * 0x100) / y_div;
dots->z1 = adconv->z1;
dots->x2 = 0;
dots->y2 = 0;
dots->z2 = 0;
break;
case 2: {
dots->type = TS_DOTS_TYPE_DUAL;
int xmin = ((adconv->x1 - x_base) * 0x100) / x_div;
int ymin = ((adconv->y1 - y_base) * 0x100) / y_div;
// x2 end-to-end: ~185 (changes along the height)
// y2 end-to-end: 470
// x2 *= 1.64
// y2 *= 0.96
int xmax = xmin + adconv->x2 * 0x1ba / 0x100;
int ymax = ymin + adconv->y2 * 0x11f / 0x100;
dots->z1 = adconv->z1;
dots->z2 = adconv->z2;
if(dots->z2 >= 0) {
dots->x1 = xmin;
dots->y1 = ymin;
dots->x2 = xmax;
dots->y2 = ymax;
}
else {
dots->x1 = xmin;
dots->y1 = ymax;
dots->x2 = xmax;
dots->y2 = ymin;
}
dots->z1 = adconv->x2;
dots->z2 = adconv->y2;
break;
}
}
cpu_atomic_end();
return type;
}
#endif /* GINT_HW_CP */

View file

@ -1,90 +0,0 @@
#ifndef GINT_TOUCH_ADCONV_H
#define GINT_TOUCH_ADCONV_H 1
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/config.h>
#if GINT_HW_CP
//---
// Internals
//---
/* _touch_adraw - raw 0x84 register information */
struct _touch_adraw
{
uint16_t x1;
uint16_t y1;
uint16_t z1;
uint16_t gh;
uint16_t x2;
uint16_t y2;
uint16_t z2;
uint16_t dm;
};
/* _touch_adconv - post-conversion raw 0x84 register information */
struct _touch_adconv
{
int x1;
int y1;
int x2;
int y2;
int z1;
int z2;
uint16_t gh;
uint16_t dm;
};
/* _touch_addots_type - type of dots */
enum _touch_addots_type
{
TS_DOTS_TYPE_OFF = 0,
TS_DOTS_TYPE_SINGLE = 1,
TS_DOTS_TYPE_DUAL = 2,
};
/* _touch_addots - touchscreen dots information */
struct _touch_addots
{
enum _touch_addots_type type;
int x1;
int y1;
int z1;
int x2;
int y2;
int z2;
};
//---
// Public
//---
/* touch_adconv_get_raw() - read 0x84 register using I2C */
extern int touch_adconv_get_raw(struct _touch_adraw *adraw);
/* touch_adconv_get_conv() - perform the raw conversion */
extern int touch_adconv_get_conv(
struct _touch_adconv *adconv,
struct _touch_adraw *adraw,
int type
);
/* touch_adconv_get_dots() - generate dots information */
extern int touch_adconv_get_dots(
struct _touch_addots *dots,
struct _touch_adconv *adconv,
int type
);
#endif /* GINT_HW_CP */
#ifdef __cplusplus
}
#endif
#endif /* GINT_TOUCH_ADCONV_H */

View file

@ -109,6 +109,6 @@ gint_driver_t drv_touch = {
.funbind = _touch_funbind,
.state_size = sizeof(touch_state_t),
};
GINT_DECLARE_DRIVER(24, drv_touch);
GINT_DECLARE_DRIVER(22, drv_touch);
#endif /* GINT_HW_CP */

View file

@ -5,9 +5,10 @@
extern "C" {
#endif
#include <gint/config.h>
#include <gint/keyboard.h>
#include <gint/touch.h>
#include <gint/config.h>
#include <gint/drivers/touch.h>
#if GINT_HW_CP
@ -21,6 +22,20 @@ struct _touch_drv_info
} adinfo;
};
/* Gets raw data from I2C command 0x84. This function is *NOT REENTRANT* and
must have only one user (from the scheduled input tick). */
extern void touch_adconv_get_raw(struct _touch_adraw *adraw);
/* touch_adconv_get_conv() - perform the raw conversion */
extern void touch_adconv_get_conv(
struct _touch_adconv *adconv,
struct _touch_adraw *adraw);
/* touch_adconv_get_dots() - generate dots information */
extern void touch_adconv_get_dots(
struct _touch_addots *dots,
struct _touch_adconv *adconv);
#endif /* GINT_HW_CP */
#ifdef __cplusplus

View file

@ -224,10 +224,10 @@ void i2c_configure(void)
__i2c_request.status = I2C_REQ_STATUS_FINISHED;
// Enable interrupt
intc_priority(INTC_I2C_AL, 1);
intc_priority(INTC_I2C_TACK, 1);
intc_priority(INTC_I2C_WAIT, 1);
intc_priority(INTC_I2C_DTE, 1);
intc_priority(INTC_I2C_AL, 15);
intc_priority(INTC_I2C_TACK, 15);
intc_priority(INTC_I2C_WAIT, 15);
intc_priority(INTC_I2C_DTE, 15);
}
/* i2c_unbind() - unbind from gint to casio */

View file

@ -2,23 +2,139 @@
// gint:touch - touch driver (high-level)
//----
#include <string.h>
#include <stdlib.h>
#include <gint/config.h>
#include <gint/defs/util.h>
#include <gint/touch.h>
#include <gint/cpu.h>
#include <gint/config.h>
#if GINT_HW_CP
#include "./i2c.h"
#include "./adconv.h"
#include "./driver.h"
/* __touch_drv_info - internal driver information */
extern struct _touch_drv_info __touch_drv_info;
// TODO[touch]: Get rid of global last_cong by making raw -> conv stateless
static struct _touch_adraw last_raw;
static struct _touch_adconv last_conv;
static struct _touch_addots last_dots;
//---
// Internals
//---
/* __touch_drv_info - internal driver information */
extern struct _touch_drv_info __touch_drv_info;
void touch_adconv_get_raw(struct _touch_adraw *adraw)
{
volatile uint8_t *IO_PRDR = (void*)0xa405013c;
memset(adraw, 0, sizeof *adraw);
if ((*IO_PRDR & 0x20) == 0) {
i2c_reg_read(0x84, adraw, 16);
adraw->has_data = true;
}
}
void touch_adconv_get_conv(
struct _touch_adconv *adconv,
struct _touch_adraw *adraw)
{
if (!adraw->has_data) {
memset(adconv, 0, sizeof *adconv);
cpu_atomic_start();
__touch_drv_info.adinfo.prev_is_dual = false;
cpu_atomic_end();
return;
}
adconv->x1 = adraw->x1 >> 4;
adconv->y1 = adraw->y1 >> 4;
adconv->z1 = adraw->z1 >> 4;
adconv->x2 = ((int)((uint)(adraw->x2) >> 6)) + ((adraw->x2 & 1) * -0x400);
adconv->y2 = ((int)((uint)(adraw->y2) >> 6)) + ((adraw->y2 & 1) * -0x400);
adconv->z2 = ((int)((uint)(adraw->z2) >> 4)) + ((adraw->z2 & 1) * -0x1000);
adconv->gh = adraw->gh >> 4;
adconv->dm = adraw->dm >> 6;
cpu_atomic_start();
int dual_threshold = __touch_drv_info.calibration.dual_sensi_entry;
if (__touch_drv_info.adinfo.prev_is_dual)
dual_threshold = __touch_drv_info.calibration.dual_sensi_leave;
bool is_dual = (abs(adconv->z2) >= dual_threshold ||
max(abs(adconv->x2), abs(adconv->y2)) >= dual_threshold);
__touch_drv_info.adinfo.prev_is_dual = is_dual;
cpu_atomic_end();
adconv->touches = is_dual ? 2 : 1;
}
void touch_adconv_get_dots(
struct _touch_addots *dots,
struct _touch_adconv *adconv)
{
int x_div;
int x_base;
int y_div;
int y_base;
cpu_atomic_start();
x_div = __touch_drv_info.calibration.x_div;
x_base = __touch_drv_info.calibration.x_base;
y_div = __touch_drv_info.calibration.y_div;
y_base = __touch_drv_info.calibration.y_base;
cpu_atomic_end();
dots->touches = adconv->touches;
switch (adconv->touches)
{
case 0:
dots->x1 = 0;
dots->y1 = 0;
dots->z1 = 0;
dots->x2 = 0;
dots->y2 = 0;
dots->z2 = 0;
break;
case 1:
dots->x1 = ((adconv->x1 - x_base) * 0x100) / x_div;
dots->y1 = ((adconv->y1 - y_base) * 0x100) / y_div;
dots->z1 = adconv->z1;
dots->x2 = 0;
dots->y2 = 0;
dots->z2 = 0;
break;
case 2: {
int xmin = ((adconv->x1 - x_base) * 0x100) / x_div;
int ymin = ((adconv->y1 - y_base) * 0x100) / y_div;
// x2 end-to-end: ~185 (changes along the height)
// y2 end-to-end: 470
// x2 *= 1.64
// y2 *= 0.96
int xmax = xmin + adconv->x2 * 0x1ba / 0x100;
int ymax = ymin + adconv->y2 * 0x11f / 0x100;
dots->z1 = adconv->z1;
dots->z2 = adconv->z2;
if(dots->z2 >= 0) {
dots->x1 = xmin;
dots->y1 = ymin;
dots->x2 = xmax;
dots->y2 = ymax;
}
else {
dots->x1 = xmin;
dots->y1 = ymax;
dots->x2 = xmax;
dots->y2 = ymin;
}
dots->z1 = adconv->x2;
dots->z2 = adconv->y2;
break;
}
}
}
//---
// Public
@ -62,50 +178,57 @@ int touch_calib_set(touch_calibration_t *calib)
// low-level API
/* touch_next_event() - get the next touch event */
key_event_t touch_next_event(void)
static key_event_t make_event(struct _touch_addots const *dots)
{
struct _touch_adconv adconv;
struct _touch_addots addots;
struct _touch_adraw adraw;
key_event_t evt;
int type;
evt.type = KEYEV_NONE;
evt.type = KEYEV_TOUCH_RELEASE;
type = touch_adconv_get_raw(&adraw);
if (type != 0)
{
type = touch_adconv_get_conv(&adconv, &adraw, type);
type = touch_adconv_get_dots(&addots, &adconv, type);
if (type == 1)
{
evt.type = KEYEV_TOUCH_DRAG;
evt.x = addots.x1;
evt.y = addots.y1;
}
}
cpu_atomic_start();
if (evt.type == KEYEV_TOUCH_DRAG) {
if (
(__touch_drv_info.prev_evt.type == KEYEV_NONE) ||
(__touch_drv_info.prev_evt.type == KEYEV_TOUCH_RELEASE)
) {
int prev_type = __touch_drv_info.prev_evt.type;
int prev_x = __touch_drv_info.prev_evt.x;
int prev_y = __touch_drv_info.prev_evt.y;
// TODO: Handle dual touch here
if(dots->touches != 1) {
if(prev_type != KEYEV_TOUCH_RELEASE && prev_type != KEYEV_NONE)
evt.type = KEYEV_TOUCH_RELEASE;
}
else {
if(prev_type == KEYEV_TOUCH_RELEASE || prev_type == KEYEV_NONE)
evt.type = KEYEV_TOUCH_PRESSED;
/* Don't emit successive DRAG events at the same location */
else if(dots->x1 != prev_x || dots->y1 != prev_y)
evt.type = KEYEV_TOUCH_DRAG;
if(evt.type != KEYEV_NONE) {
evt.x = dots->x1;
evt.y = dots->y1;
}
}
if (evt.type == KEYEV_TOUCH_RELEASE) {
if (
(__touch_drv_info.prev_evt.type == KEYEV_NONE) ||
(__touch_drv_info.prev_evt.type == KEYEV_TOUCH_RELEASE)
) {
evt.type = KEYEV_NONE;
}
}
__touch_drv_info.prev_evt.type = evt.type;
__touch_drv_info.prev_evt.x = evt.x;
__touch_drv_info.prev_evt.y = evt.y;
if(evt.type != KEYEV_NONE)
__touch_drv_info.prev_evt = evt;
cpu_atomic_end();
return evt;
}
key_event_t touch_scan(void)
{
touch_adconv_get_raw(&last_raw);
touch_adconv_get_conv(&last_conv, &last_raw);
touch_adconv_get_dots(&last_dots, &last_conv);
return make_event(&last_dots);
}
void touch_get_last_scan(struct _touch_adraw *raw,
struct _touch_adconv *conv, struct _touch_addots *dots)
{
if(raw)
*raw = last_raw;
if(conv)
*conv = last_conv;
if(dots)
*dots = last_dots;
}
#endif /* GINT_HW_CP */