diff --git a/CMakeLists.txt b/CMakeLists.txt index b72d3d6..edf49d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ) diff --git a/include/gint/drivers/keydev.h b/include/gint/drivers/keydev.h index 0e15645..2b11ce4 100644 --- a/include/gint/drivers/keydev.h +++ b/include/gint/drivers/keydev.h @@ -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 diff --git a/include/gint/drivers/touch.h b/include/gint/drivers/touch.h new file mode 100644 index 0000000..2a48367 --- /dev/null +++ b/include/gint/drivers/touch.h @@ -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 +#include +#include + +#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 */ diff --git a/include/gint/touch.h b/include/gint/touch.h index c761cb7..1197bdf 100644 --- a/include/gint/touch.h +++ b/include/gint/touch.h @@ -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 - -/* 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 */ diff --git a/src/keysc/keydev.c b/src/keysc/keydev.c index 207e2a7..3c1b98a 100644 --- a/src/keysc/keydev.c +++ b/src/keysc/keydev.c @@ -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++; diff --git a/src/keysc/keysc.c b/src/keysc/keysc.c index 0625812..955bcc8 100644 --- a/src/keysc/keysc.c +++ b/src/keysc/keysc.c @@ -14,6 +14,10 @@ #include #include +#if GINT_HW_CP +#include +#endif + #include #include @@ -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; } diff --git a/src/touch/adconv.c b/src/touch/adconv.c deleted file mode 100644 index c12d46d..0000000 --- a/src/touch/adconv.c +++ /dev/null @@ -1,166 +0,0 @@ -//--- -// gint:touch:adconv - 0x84 register data conversion -//--- -#include - -#include -#include -#include - -#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 */ diff --git a/src/touch/adconv.h b/src/touch/adconv.h deleted file mode 100644 index cf64b1b..0000000 --- a/src/touch/adconv.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef GINT_TOUCH_ADCONV_H -#define GINT_TOUCH_ADCONV_H 1 - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#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 */ diff --git a/src/touch/driver.c b/src/touch/driver.c index cdcd535..69652b7 100644 --- a/src/touch/driver.c +++ b/src/touch/driver.c @@ -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 */ diff --git a/src/touch/driver.h b/src/touch/driver.h index f73cd1b..353f8ff 100644 --- a/src/touch/driver.h +++ b/src/touch/driver.h @@ -5,9 +5,10 @@ extern "C" { #endif +#include #include #include -#include +#include #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 diff --git a/src/touch/i2c.c b/src/touch/i2c.c index 517f03a..4df57b7 100644 --- a/src/touch/i2c.c +++ b/src/touch/i2c.c @@ -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 */ diff --git a/src/touch/touch.c b/src/touch/touch.c index dea2779..51fb315 100644 --- a/src/touch/touch.c +++ b/src/touch/touch.c @@ -2,23 +2,139 @@ // gint:touch - touch driver (high-level) //---- #include +#include +#include +#include #include #include -#include #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 */