From 26bbb93d24a955e8d27d2cd0500b8fa3d66a9e0a Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sat, 25 Jun 2022 20:53:21 -0700 Subject: [PATCH 01/27] cirque_pinnacle: Add timeout for potential loops ERAControlValue set to 0xFF and waits for read value as 0x00. If sensor not connected, it may never change. No timeout specified for extended register access in datasheet, use driver default timeout. --- drivers/sensors/cirque_pinnacle.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/sensors/cirque_pinnacle.c b/drivers/sensors/cirque_pinnacle.c index 1d1e4ccfc6c6..ca18aa13e273 100644 --- a/drivers/sensors/cirque_pinnacle.c +++ b/drivers/sensors/cirque_pinnacle.c @@ -127,7 +127,8 @@ void cirque_pinnacle_enable_feed(bool feedEnable) { // Reads bytes from an extended register at
(16-bit address), // stores values in <*data> void ERA_ReadBytes(uint16_t address, uint8_t* data, uint16_t count) { - uint8_t ERAControlValue = 0xFF; + uint8_t ERAControlValue = 0xFF; + uint16_t timeout_timer; cirque_pinnacle_enable_feed(false); // Disable feed @@ -138,9 +139,10 @@ void ERA_ReadBytes(uint16_t address, uint8_t* data, uint16_t count) { RAP_Write(ERA_CONTROL, 0x05); // Signal ERA-read (auto-increment) to Pinnacle // Wait for status register 0x1E to clear + timeout_timer = timer_read(); do { RAP_ReadBytes(ERA_CONTROL, &ERAControlValue, 1); - } while (ERAControlValue != 0x00); + } while ((ERAControlValue != 0x00) && (timer_elapsed(timeout_timer) <= CIRQUE_PINNACLE_TIMEOUT)); RAP_ReadBytes(ERA_VALUE, data + i, 1); @@ -150,7 +152,8 @@ void ERA_ReadBytes(uint16_t address, uint8_t* data, uint16_t count) { // Writes a byte, , to an extended register at
(16-bit address) void ERA_WriteByte(uint16_t address, uint8_t data) { - uint8_t ERAControlValue = 0xFF; + uint8_t ERAControlValue = 0xFF; + uint16_t timeout_timer; cirque_pinnacle_enable_feed(false); // Disable feed @@ -162,9 +165,10 @@ void ERA_WriteByte(uint16_t address, uint8_t data) { RAP_Write(ERA_CONTROL, 0x02); // Signal an ERA-write to Pinnacle // Wait for status register 0x1E to clear + timeout_timer = timer_read(); do { RAP_ReadBytes(ERA_CONTROL, &ERAControlValue, 1); - } while (ERAControlValue != 0x00); + } while ((ERAControlValue != 0x00) && (timer_elapsed(timeout_timer) <= CIRQUE_PINNACLE_TIMEOUT)); cirque_pinnacle_clear_flags(); } From 20f677fe8265680ef1544e20a14be9f383d61442 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sat, 25 Jun 2022 21:21:34 -0700 Subject: [PATCH 02/27] cirque_pinnacle: recalibrate during initialization Pinnacle automatically captures baseline on powerup, that baseline is invalid after changing ADC attenuation so recapture it. Expose function in header so calibration can be triggered by user. e.g. Press a key combo to recalibrate after finger gets captured in baseline --- drivers/sensors/cirque_pinnacle.c | 27 +++++++++++++++++++++++++++ drivers/sensors/cirque_pinnacle.h | 1 + 2 files changed, 28 insertions(+) diff --git a/drivers/sensors/cirque_pinnacle.c b/drivers/sensors/cirque_pinnacle.c index ca18aa13e273..9b10f3da59a7 100644 --- a/drivers/sensors/cirque_pinnacle.c +++ b/drivers/sensors/cirque_pinnacle.c @@ -196,6 +196,31 @@ void cirque_pinnacle_tune_edge_sensitivity(void) { ERA_ReadBytes(0x0168, &temp, 1); } +// Perform calibration +void cirque_pinnacle_calibrate(void) { + uint8_t calconfig; + uint16_t timeout_timer; + + // CalConfig1 (Compensation) + // Bit 0: Calibrate, 1=calibrate, 0=complete + // Bit 1: Background Comp Enable, 1=enable, 0=disable + // Bit 2: NERD Comp Enable (No, I don't know what NERD means), 1=enable, 0=disable + // Bit 3: Track Error Comp Enable, 1=enable, 0=disable + // Bit 4: Tap Comp Enable, 1=enable, 0=disable + // Bit 6: Calibration Matrix Disable, 1=disabled, 0=enabled + RAP_ReadBytes(CALIBRATION_CONFIG_1, &calconfig, 1); + calconfig |= 0x01; + RAP_Write(CALIBRATION_CONFIG_1, calconfig); + + // Calibration takes ~100ms according to GT-AN-090624, doubling the timeout just to be safe + timeout_timer = timer_read(); + do { + RAP_ReadBytes(CALIBRATION_CONFIG_1, &calconfig, 1); + } while ((calconfig & 0x01) && (timer_elapsed(timeout_timer) <= 200)); + + cirque_pinnacle_clear_flags(); +} + /* Pinnacle-based TM040040/TM035035/TM023023 Functions */ void cirque_pinnacle_init(void) { #if defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) @@ -245,6 +270,8 @@ void cirque_pinnacle_init(void) { RAP_Write(Z_IDLE_COUNT, 5); cirque_pinnacle_set_adc_attenuation(CIRQUE_PINNACLE_ATTENUATION); + // Perform manual calibration after setting ADC attenuation + cirque_pinnacle_calibrate(); cirque_pinnacle_tune_edge_sensitivity(); cirque_pinnacle_enable_feed(true); diff --git a/drivers/sensors/cirque_pinnacle.h b/drivers/sensors/cirque_pinnacle.h index d65bdb41b640..c4882d376209 100644 --- a/drivers/sensors/cirque_pinnacle.h +++ b/drivers/sensors/cirque_pinnacle.h @@ -84,6 +84,7 @@ typedef struct { } pinnacle_data_t; void cirque_pinnacle_init(void); +void cirque_pinnacle_calibrate(void); pinnacle_data_t cirque_pinnacle_read_data(void); void cirque_pinnacle_scale_data(pinnacle_data_t* coordinates, uint16_t xResolution, uint16_t yResolution); uint16_t cirque_pinnacle_get_scale(void); From 421c2f973d42cd9dfe7d14c9c5d95917e400b44d Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sat, 25 Jun 2022 23:37:28 -0700 Subject: [PATCH 03/27] cirque_pinnacle: Function to control smoothing --- drivers/sensors/cirque_pinnacle.c | 23 +++++++++++++++++++++++ drivers/sensors/cirque_pinnacle.h | 1 + 2 files changed, 24 insertions(+) diff --git a/drivers/sensors/cirque_pinnacle.c b/drivers/sensors/cirque_pinnacle.c index 9b10f3da59a7..b0c216204ced 100644 --- a/drivers/sensors/cirque_pinnacle.c +++ b/drivers/sensors/cirque_pinnacle.c @@ -17,6 +17,7 @@ #define SYSCONFIG_1 0x03 #define FEEDCONFIG_1 0x04 #define FEEDCONFIG_2 0x05 +#define FEEDCONFIG_3 0x06 #define CALIBRATION_CONFIG_1 0x07 #define PS2_AU_CONTROL 0x08 #define SAMPLE_RATE 0x09 @@ -221,6 +222,28 @@ void cirque_pinnacle_calibrate(void) { cirque_pinnacle_clear_flags(); } +// Enable/disable cursor smoothing, smoothing is enabled by default +void cirque_pinnacle_cursor_smoothing(bool enable) { + uint8_t feedconfig3; + + // FeedConfig3 (Advanced feature flags) + // Bit 0: DualPoint Buttons, 1=enable, 0=disable + // Bit 1: Smoothing Disable, 1=disable, 0=enable + // Bit 2: Palm/NERD measurements Disable, 1=disable, 0=enable + // Bit 3: Noise Avoidance Disable, 1=disable, 0=enable + // Bit 4: WRAP lockout Disable + // Bit 5: Dynamic EMI adjust Disable + // Bit 6: HW EMI detection Disable + // Bit 7: SW EMI detection Disable + RAP_ReadBytes(FEEDCONFIG_3, &feedconfig3, 1); + if (enable) { + feedconfig3 &= ~0x02; + } else { + feedconfig3 |= 0x02; + } + RAP_Write(FEEDCONFIG_3, feedconfig3); +} + /* Pinnacle-based TM040040/TM035035/TM023023 Functions */ void cirque_pinnacle_init(void) { #if defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) diff --git a/drivers/sensors/cirque_pinnacle.h b/drivers/sensors/cirque_pinnacle.h index c4882d376209..3b515bf2db55 100644 --- a/drivers/sensors/cirque_pinnacle.h +++ b/drivers/sensors/cirque_pinnacle.h @@ -85,6 +85,7 @@ typedef struct { void cirque_pinnacle_init(void); void cirque_pinnacle_calibrate(void); +void cirque_pinnacle_cursor_smoothing(bool enable); pinnacle_data_t cirque_pinnacle_read_data(void); void cirque_pinnacle_scale_data(pinnacle_data_t* coordinates, uint16_t xResolution, uint16_t yResolution); uint16_t cirque_pinnacle_get_scale(void); From 0f2d2322dcba5e428392f9a66e1bebe72bacb78b Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sun, 26 Jun 2022 00:25:11 -0700 Subject: [PATCH 04/27] cirque_pinnacle: Convert CPI according to diameter Set trackpad diameter with CIRQUE_PINNACLE_DIAMETER_MM. --- docs/feature_pointing_device.md | 1 + drivers/sensors/cirque_pinnacle.c | 2 +- drivers/sensors/cirque_pinnacle.h | 5 +++++ quantum/pointing_device_drivers.c | 11 +++++++++-- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/feature_pointing_device.md b/docs/feature_pointing_device.md index 264362ea77fa..4d2a5dab25a1 100644 --- a/docs/feature_pointing_device.md +++ b/docs/feature_pointing_device.md @@ -95,6 +95,7 @@ This supports the Cirque Pinnacle 1CA027 Touch Controller, which is used in the |`CIRQUE_PINNACLE_X_UPPER` | (Optional) The maximum reachable X value on the sensor. | `1919` | |`CIRQUE_PINNACLE_Y_LOWER` | (Optional) The minimum reachable Y value on the sensor. | `63` | |`CIRQUE_PINNACLE_Y_UPPER` | (Optional) The maximum reachable Y value on the sensor. | `1471` | +|`CIRQUE_PINNACLE_DIAMETER_MM` | (Optional) Diameter of the trackpad sensor in millimeters. | `40` | |`CIRQUE_PINNACLE_ATTENUATION` | (Optional) Sets the attenuation of the sensor data. | `ADC_ATTENUATE_4X` | |`CIRQUE_PINNACLE_TAPPING_TERM` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` | |`CIRQUE_PINNACLE_TOUCH_DEBOUNCE` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` | diff --git a/drivers/sensors/cirque_pinnacle.c b/drivers/sensors/cirque_pinnacle.c index b0c216204ced..62f1aea7e8a2 100644 --- a/drivers/sensors/cirque_pinnacle.c +++ b/drivers/sensors/cirque_pinnacle.c @@ -50,7 +50,7 @@ // clang-format on bool touchpad_init; -uint16_t scale_data = 1024; +uint16_t scale_data = CIRQUE_PINNACLE_DEFAULT_SCALE; void cirque_pinnacle_clear_flags(void); void cirque_pinnacle_enable_feed(bool feedEnable); diff --git a/drivers/sensors/cirque_pinnacle.h b/drivers/sensors/cirque_pinnacle.h index 3b515bf2db55..0aba712abc07 100644 --- a/drivers/sensors/cirque_pinnacle.h +++ b/drivers/sensors/cirque_pinnacle.h @@ -15,6 +15,11 @@ # define CIRQUE_PINNACLE_POSITION_MODE CIRQUE_PINNACLE_ABSOLUTE_MODE #endif +#define CIRQUE_PINNACLE_DEFAULT_SCALE 1024 +#ifndef CIRQUE_PINNACLE_DIAMETER_MM +# define CIRQUE_PINNACLE_DIAMETER_MM 40 +#endif + // Coordinate scaling values #ifndef CIRQUE_PINNACLE_X_LOWER # define CIRQUE_PINNACLE_X_LOWER 127 // min "reachable" X value diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index 435fcabf7cc9..a5bc188636fe 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -163,12 +163,19 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { return mouse_report; } +uint16_t cirque_pinnacle_get_cpi(void) { + return (uint16_t)roundf((float)cirque_pinnacle_get_scale() * 25.4f / (float)CIRQUE_PINNACLE_DIAMETER_MM); +} +void cirque_pinnacle_set_cpi(uint16_t cpi) { + cirque_pinnacle_set_scale(roundf((float)cpi * (float)CIRQUE_PINNACLE_DIAMETER_MM / 25.4f)); +} + // clang-format off const pointing_device_driver_t pointing_device_driver = { .init = cirque_pinnacle_init, .get_report = cirque_pinnacle_get_report, - .set_cpi = cirque_pinnacle_set_scale, - .get_cpi = cirque_pinnacle_get_scale + .set_cpi = cirque_pinnacle_set_cpi, + .get_cpi = cirque_pinnacle_get_cpi }; // clang-format on From fbee3bbf975ec1fb3a4bd4d5022ad043e0319fe0 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sun, 26 Jun 2022 00:56:17 -0700 Subject: [PATCH 05/27] cirque pointing driver: Make tap optional Extract tap logic into a separate function, and add the ability to disable it at runtime. --- quantum/pointing_device_drivers.c | 66 +++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index a5bc188636fe..62bbe297dc03 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -106,12 +106,53 @@ const pointing_device_driver_t pointing_device_driver = { # define CIRQUE_PINNACLE_TOUCH_DEBOUNCE (CIRQUE_PINNACLE_TAPPING_TERM * 8) # endif +typedef struct { + bool tap_enable; +} cirque_pinnacle_features_t; + +static cirque_pinnacle_features_t features = {.tap_enable = true}; + +typedef struct { + uint16_t timer; + bool touchDown; +} trackpad_tap_context_t; + +static trackpad_tap_context_t tap; + +static report_mouse_t trackpad_tap(report_mouse_t mouse_report, pinnacle_data_t touchData) { + if (touchData.touchDown != tap.touchDown) { + tap.touchDown = touchData.touchDown; + if (!touchData.zValue) { + if (timer_elapsed(tap.timer) < CIRQUE_PINNACLE_TAPPING_TERM && tap.timer != 0) { + mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1); + pointing_device_set_report(mouse_report); + pointing_device_send(); +# if TAP_CODE_DELAY > 0 + wait_ms(TAP_CODE_DELAY); +# endif + mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1); + pointing_device_set_report(mouse_report); + pointing_device_send(); + } + } + tap.timer = timer_read(); + } + if (timer_elapsed(tap.timer) > (CIRQUE_PINNACLE_TOUCH_DEBOUNCE)) { + tap.timer = 0; + } + + return mouse_report; +} + +// extern this function for now +void cirque_pinnacle_enable_tap(bool enable) { + features.tap_enable = enable; +} + report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { pinnacle_data_t touchData = cirque_pinnacle_read_data(); mouse_xy_report_t report_x = 0, report_y = 0; static mouse_xy_report_t x = 0, y = 0; - static uint16_t mouse_timer = 0; - static bool is_z_down = false; # if !CIRQUE_PINNACLE_POSITION_MODE # error Cirque Pinnacle with relative mode not implemented yet. @@ -139,25 +180,8 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { mouse_report.x = report_x; mouse_report.y = report_y; - if (touchData.touchDown != is_z_down) { - is_z_down = touchData.touchDown; - if (!touchData.zValue) { - if (timer_elapsed(mouse_timer) < CIRQUE_PINNACLE_TAPPING_TERM && mouse_timer != 0) { - mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1); - pointing_device_set_report(mouse_report); - pointing_device_send(); -# if TAP_CODE_DELAY > 0 - wait_ms(TAP_CODE_DELAY); -# endif - mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1); - pointing_device_set_report(mouse_report); - pointing_device_send(); - } - } - mouse_timer = timer_read(); - } - if (timer_elapsed(mouse_timer) > (CIRQUE_PINNACLE_TOUCH_DEBOUNCE)) { - mouse_timer = 0; + if (features.tap_enable) { + mouse_report = trackpad_tap(mouse_report, touchData); } return mouse_report; From 6e4a63a0cc7763a6ce32980ede78b81f3a7c9cee Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sun, 26 Jun 2022 01:47:07 -0700 Subject: [PATCH 06/27] cirque pointing driver: Circular scroll feature Scroll initiates from touch to the outer ring. Left half starts vertical scroll, right half starts horizontal scroll. Rotate clockwise to scroll down/right, keep rotating to keep scrolling. Disabled by default, can be configured at runtime. --- quantum/pointing_device_drivers.c | 137 ++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index 62bbe297dc03..9187b10a5cfb 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -108,6 +108,7 @@ const pointing_device_driver_t pointing_device_driver = { typedef struct { bool tap_enable; + bool circular_scroll_enable; } cirque_pinnacle_features_t; static cirque_pinnacle_features_t features = {.tap_enable = true}; @@ -149,8 +150,134 @@ void cirque_pinnacle_enable_tap(bool enable) { features.tap_enable = enable; } +typedef enum { + SCROLL_UNINITIALIZED, + SCROLL_DETECTING, + SCROLL_VALID, + NOT_SCROLL, +} circular_scroll_status_t; + +typedef struct { + int8_t v; + int8_t h; + bool suppress_touch; +} circular_scroll_t; + +typedef struct { + float mag; + int16_t x; + int16_t y; + uint16_t z; + circular_scroll_status_t state; + bool axis; + // settings + float outer_ring_pct; // width of outer ring + float movement_pct; // amount of movement before triggering scroll validation + float movement_ratio; // ratio of movement along perimeter / movement towards center + uint8_t wheel_clicks; // how many clicks to report in a half circle +} circular_scroll_context_t; + +static circular_scroll_context_t scroll = {.outer_ring_pct = 33, .movement_pct = 6, .movement_ratio = 1.2, .wheel_clicks = 9}; + +static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { + circular_scroll_t report = {0, 0, false}; + int16_t x, y; + uint16_t center = cirque_pinnacle_get_scale() / 2; + int8_t wheel_clicks; + float mag, ang, scalar_projection, scalar_rejection, parallel_movement, perpendicular_movement; + int32_t dot, det; + + if (touchData.zValue) { + // place origin at center of trackpad + x = (int16_t)(touchData.xValue - center); + y = (int16_t)(touchData.yValue - center); + + // check if first touch + if (!scroll.z) { + report.suppress_touch = false; + // check if touch falls within outer ring + mag = hypotf(x, y); + if (mag / center >= (100.0 - scroll.outer_ring_pct) / 100.0) { + scroll.state = SCROLL_DETECTING; + scroll.x = x; + scroll.y = y; + scroll.mag = mag; + // decide scroll axis: + // vertical if started from righ half + // horizontal if started from left half + // reverse for left hand? TODO? +# if defined(POINTING_DEVICE_ROTATION_90) + scroll.axis = y < 0; +# elif defined(POINTING_DEVICE_ROTATION_180) + scroll.axis = x > 0; +# elif defined(POINTING_DEVICE_ROTATION_270) + scroll.axis = y > 0; +# else + scroll.axis = x < 0; +# endif + } + } else if (scroll.state == SCROLL_DETECTING) { + report.suppress_touch = true; + // already detecting scroll, check movement from touchdown location + mag = hypotf(x - scroll.x, y - scroll.y); + if (mag >= scroll.movement_pct / 100.0 * center) { + // check ratio of movement towards center vs. along perimeter + // this distinguishes circular scroll from swipes that start from edge of trackpad + dot = (int32_t)scroll.x * (int32_t)x + (int32_t)scroll.y * (int32_t)y; + det = (int32_t)scroll.x * (int32_t)y - (int32_t)scroll.y * (int32_t)x; + scalar_projection = dot / scroll.mag; + scalar_rejection = det / scroll.mag; + parallel_movement = fabs(scroll.mag - fabs(scalar_projection)); + perpendicular_movement = fabs(scalar_rejection); + if (parallel_movement * scroll.movement_ratio > perpendicular_movement) { + // not a scroll, release coordinates + report.suppress_touch = false; + scroll.state = NOT_SCROLL; + } else { + // scroll detected + scroll.state = SCROLL_VALID; + } + } + } + if (scroll.state == SCROLL_VALID) { + report.suppress_touch = true; + dot = (int32_t)scroll.x * (int32_t)x + (int32_t)scroll.y * (int32_t)y; + det = (int32_t)scroll.x * (int32_t)y - (int32_t)scroll.y * (int32_t)x; + ang = atan2f(det, dot); + wheel_clicks = roundf(ang * (float)scroll.wheel_clicks / M_PI); + if (wheel_clicks >= 1 || wheel_clicks <= -1) { + if (scroll.axis == 0) { + report.v = -wheel_clicks; + } else { + report.h = wheel_clicks; + } + scroll.x = x; + scroll.y = y; + } + } + } + + scroll.z = touchData.zValue; + if (!scroll.z) scroll.state = SCROLL_UNINITIALIZED; + + return report; +} + +// extern this for now +void cirque_pinnacle_enable_circular_scroll(bool enable) { + features.circular_scroll_enable = enable; +} + +void cirque_pinnacle_set_circular_scroll_settings(float outer_ring_pct, float movement_pct, float movement_ratio, uint8_t wheel_clicks) { + scroll.outer_ring_pct = outer_ring_pct; + scroll.movement_pct = movement_pct; + scroll.movement_ratio = movement_ratio; + scroll.wheel_clicks = wheel_clicks; +} + report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { pinnacle_data_t touchData = cirque_pinnacle_read_data(); + circular_scroll_t scroll_report; mouse_xy_report_t report_x = 0, report_y = 0; static mouse_xy_report_t x = 0, y = 0; @@ -171,12 +298,22 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { // Scale coordinates to arbitrary X, Y resolution cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale()); + if (features.circular_scroll_enable) { + scroll_report = circular_scroll(touchData); + mouse_report.v = scroll_report.v; + mouse_report.h = scroll_report.h; + if (scroll_report.suppress_touch) { + goto mouse_report_update; + } + } if (x && y && touchData.xValue && touchData.yValue) { report_x = (mouse_xy_report_t)(touchData.xValue - x); report_y = (mouse_xy_report_t)(touchData.yValue - y); } x = touchData.xValue; y = touchData.yValue; + +mouse_report_update: mouse_report.x = report_x; mouse_report.y = report_y; From 0c35461c7fe33125c4510aab36ca65bf39938898 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sun, 26 Jun 2022 02:36:27 -0700 Subject: [PATCH 07/27] cirque pointing driver: Inertial cursor (glide) Cursor continues moving after flick, slows down by kinetic friction, similar to trackball emulation on Steam Controller. Based on https://hal.archives-ouvertes.fr/hal-00989252. Disabled by default, can be enabled at runtime. --- quantum/pointing_device_drivers.c | 112 ++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index 9187b10a5cfb..1806d5c957da 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -109,6 +109,7 @@ const pointing_device_driver_t pointing_device_driver = { typedef struct { bool tap_enable; bool circular_scroll_enable; + bool cursor_glide_enable; } cirque_pinnacle_features_t; static cirque_pinnacle_features_t features = {.tap_enable = true}; @@ -275,9 +276,99 @@ void cirque_pinnacle_set_circular_scroll_settings(float outer_ring_pct, float mo scroll.wheel_clicks = wheel_clicks; } +typedef struct { + mouse_xy_report_t dx; + mouse_xy_report_t dy; + bool valid; +} cursor_glide_t; + +typedef struct { + float trigger_pct; + float coef; + float v0; + int16_t x; + int16_t y; + uint16_t z; + uint16_t timer; + uint16_t interval; + uint16_t counter; + mouse_xy_report_t dx0; + mouse_xy_report_t dy0; +} cursor_glide_context_t; + +static cursor_glide_context_t glide; + +static cursor_glide_t cursor_glide(void) { + cursor_glide_t report; + float p; + int16_t x, y; + + glide.counter++; + // calculate current position + p = glide.v0 * glide.counter - glide.coef * glide.counter * glide.counter / 2; + x = (int16_t)(p * glide.dx0 / glide.v0); + y = (int16_t)(p * glide.dy0 / glide.v0); + report.dx = (mouse_xy_report_t)(x - glide.x); + report.dy = (mouse_xy_report_t)(y - glide.y); + report.valid = true; + if (report.dx <= 1 && report.dx >= -1 && report.dy <= 1 && report.dy >= -1) { + glide.dx0 = 0; + glide.dy0 = 0; + } + glide.x = x; + glide.y = y; + glide.timer = timer_read(); + + return report; +} + +static cursor_glide_t cursor_glide_check(void) { + cursor_glide_t invalid_report = {0, 0, false}; + if (glide.z || (glide.dx0 == 0 && glide.dy0 == 0) || timer_elapsed(glide.timer) < glide.interval) { + return invalid_report; + } else { + return cursor_glide(); + } +} + +static cursor_glide_t cursor_glide_start(void) { + cursor_glide_t invalid_report = {0, 0, false}; + + glide.trigger_pct = 2; // good enough default + glide.coef = 0.4; // good enough default + glide.interval = 10; // hardcode for 100sps + glide.timer = timer_read(); + glide.counter = 0; + glide.v0 = (glide.dx0 == 0 && glide.dy0 == 0) ? 0.0 : hypotf(glide.dx0, glide.dy0); // skip trigonometry if not needed + glide.x = 0; + glide.y = 0; + glide.z = 0; + + if (glide.v0 < glide.trigger_pct * cirque_pinnacle_get_scale() / 100) { + // not enough velocity to be worth gliding, abort + glide.dx0 = 0; + glide.dy0 = 0; + return invalid_report; + } + + return cursor_glide(); +} + +static void cursor_glide_update(mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z) { + glide.dx0 = dx; + glide.dy0 = dy; + glide.z = z; +} + +// extern this for now +void cirque_pinnacle_enable_cursor_glide(bool enable) { + features.cursor_glide_enable = enable; +} + report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { pinnacle_data_t touchData = cirque_pinnacle_read_data(); circular_scroll_t scroll_report; + cursor_glide_t glide_report; mouse_xy_report_t report_x = 0, report_y = 0; static mouse_xy_report_t x = 0, y = 0; @@ -285,7 +376,16 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { # error Cirque Pinnacle with relative mode not implemented yet. # endif + if (features.cursor_glide_enable) { + glide_report = cursor_glide_check(); + } + if (!touchData.valid) { + if (features.cursor_glide_enable && glide_report.valid) { + report_x = glide_report.dx; + report_y = glide_report.dy; + goto mouse_report_update; + } return mouse_report; } @@ -313,6 +413,18 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { x = touchData.xValue; y = touchData.yValue; + if (features.cursor_glide_enable) { + if (touchData.touchDown) { + cursor_glide_update(report_x, report_y, touchData.zValue); + } else if (!glide_report.valid) { + glide_report = cursor_glide_start(); + if (glide_report.valid) { + report_x = glide_report.dx; + report_y = glide_report.dy; + } + } + } + mouse_report_update: mouse_report.x = report_x; mouse_report.y = report_y; From f8738161b05d2aae0c5a3ff1eecd39a6cb02f32c Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sun, 26 Jun 2022 04:43:09 -0700 Subject: [PATCH 08/27] cirque pointing driver: formatting --- quantum/pointing_device_drivers.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index 1806d5c957da..af23a5d61bf8 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -172,10 +172,10 @@ typedef struct { circular_scroll_status_t state; bool axis; // settings - float outer_ring_pct; // width of outer ring - float movement_pct; // amount of movement before triggering scroll validation - float movement_ratio; // ratio of movement along perimeter / movement towards center - uint8_t wheel_clicks; // how many clicks to report in a half circle + float outer_ring_pct; // width of outer ring + float movement_pct; // amount of movement before triggering scroll validation + float movement_ratio; // ratio of movement along perimeter / movement towards center + uint8_t wheel_clicks; // how many clicks to report in a half circle } circular_scroll_context_t; static circular_scroll_context_t scroll = {.outer_ring_pct = 33, .movement_pct = 6, .movement_ratio = 1.2, .wheel_clicks = 9}; @@ -271,9 +271,9 @@ void cirque_pinnacle_enable_circular_scroll(bool enable) { void cirque_pinnacle_set_circular_scroll_settings(float outer_ring_pct, float movement_pct, float movement_ratio, uint8_t wheel_clicks) { scroll.outer_ring_pct = outer_ring_pct; - scroll.movement_pct = movement_pct; + scroll.movement_pct = movement_pct; scroll.movement_ratio = movement_ratio; - scroll.wheel_clicks = wheel_clicks; + scroll.wheel_clicks = wheel_clicks; } typedef struct { @@ -410,8 +410,8 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { report_x = (mouse_xy_report_t)(touchData.xValue - x); report_y = (mouse_xy_report_t)(touchData.yValue - y); } - x = touchData.xValue; - y = touchData.yValue; + x = touchData.xValue; + y = touchData.yValue; if (features.cursor_glide_enable) { if (touchData.touchDown) { From 7428dc1a2663294a5903bdfd6373141a20fb934d Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sun, 26 Jun 2022 13:49:03 -0700 Subject: [PATCH 09/27] cirque pointing driver: Adjust configuration functions --- quantum/pointing_device_drivers.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index af23a5d61bf8..0ff3f293d7b5 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -269,7 +269,8 @@ void cirque_pinnacle_enable_circular_scroll(bool enable) { features.circular_scroll_enable = enable; } -void cirque_pinnacle_set_circular_scroll_settings(float outer_ring_pct, float movement_pct, float movement_ratio, uint8_t wheel_clicks) { +// To set a trackpad exclusively as scroll wheel: outer_ring_pct = 100, movement_pct = 0, movement_ratio = 0 +void cirque_pinnacle_configure_circular_scroll(float outer_ring_pct, float movement_pct, float movement_ratio, uint8_t wheel_clicks) { scroll.outer_ring_pct = outer_ring_pct; scroll.movement_pct = movement_pct; scroll.movement_ratio = movement_ratio; @@ -296,7 +297,7 @@ typedef struct { mouse_xy_report_t dy0; } cursor_glide_context_t; -static cursor_glide_context_t glide; +static cursor_glide_context_t glide = {.trigger_pct = 2}; static cursor_glide_t cursor_glide(void) { cursor_glide_t report; @@ -334,15 +335,14 @@ static cursor_glide_t cursor_glide_check(void) { static cursor_glide_t cursor_glide_start(void) { cursor_glide_t invalid_report = {0, 0, false}; - glide.trigger_pct = 2; // good enough default - glide.coef = 0.4; // good enough default - glide.interval = 10; // hardcode for 100sps - glide.timer = timer_read(); - glide.counter = 0; - glide.v0 = (glide.dx0 == 0 && glide.dy0 == 0) ? 0.0 : hypotf(glide.dx0, glide.dy0); // skip trigonometry if not needed - glide.x = 0; - glide.y = 0; - glide.z = 0; + glide.coef = 0.4; // good enough default + glide.interval = 10; // hardcode for 100sps + glide.timer = timer_read(); + glide.counter = 0; + glide.v0 = (glide.dx0 == 0 && glide.dy0 == 0) ? 0.0 : hypotf(glide.dx0, glide.dy0); // skip trigonometry if not needed + glide.x = 0; + glide.y = 0; + glide.z = 0; if (glide.v0 < glide.trigger_pct * cirque_pinnacle_get_scale() / 100) { // not enough velocity to be worth gliding, abort @@ -365,6 +365,10 @@ void cirque_pinnacle_enable_cursor_glide(bool enable) { features.cursor_glide_enable = enable; } +void cirque_pinnacle_configure_cursor_glide(float trigger_pct) { + glide.trigger_pct = trigger_pct; +} + report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { pinnacle_data_t touchData = cirque_pinnacle_read_data(); circular_scroll_t scroll_report; From 43b2f9948b6d107e587d8c5955297a2999e0bc6a Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sun, 26 Jun 2022 13:50:21 -0700 Subject: [PATCH 10/27] cirque_pinnacle: Reword comment on calibration --- drivers/sensors/cirque_pinnacle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/sensors/cirque_pinnacle.c b/drivers/sensors/cirque_pinnacle.c index 62f1aea7e8a2..c39cfbd873f4 100644 --- a/drivers/sensors/cirque_pinnacle.c +++ b/drivers/sensors/cirque_pinnacle.c @@ -293,7 +293,7 @@ void cirque_pinnacle_init(void) { RAP_Write(Z_IDLE_COUNT, 5); cirque_pinnacle_set_adc_attenuation(CIRQUE_PINNACLE_ATTENUATION); - // Perform manual calibration after setting ADC attenuation + // Force a calibration after setting ADC attenuation cirque_pinnacle_calibrate(); cirque_pinnacle_tune_edge_sensitivity(); From 4d46e592835f409e39d58010c42f0b0a3d08cfc3 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Tue, 28 Jun 2022 22:32:08 -0700 Subject: [PATCH 11/27] cirque_pinnacle: Import register definition header Register definitions header based on Pinnacle.h from Cirque's Arduino AnyMeas_Example as that is the most complete definition anywhere. Extended register details taken from GT-AN-090625. Default values from a register dump on TM040040. --- drivers/sensors/cirque_pinnacle.c | 163 +++------ drivers/sensors/cirque_pinnacle.h | 3 +- drivers/sensors/cirque_pinnacle_regdefs.h | 405 ++++++++++++++++++++++ 3 files changed, 453 insertions(+), 118 deletions(-) create mode 100644 drivers/sensors/cirque_pinnacle_regdefs.h diff --git a/drivers/sensors/cirque_pinnacle.c b/drivers/sensors/cirque_pinnacle.c index c39cfbd873f4..8524e5e767d6 100644 --- a/drivers/sensors/cirque_pinnacle.c +++ b/drivers/sensors/cirque_pinnacle.c @@ -9,45 +9,9 @@ #include "wait.h" #include "timer.h" -// Registers for RAP -// clang-format off -#define FIRMWARE_ID 0x00 -#define FIRMWARE_VERSION_C 0x01 -#define STATUS_1 0x02 -#define SYSCONFIG_1 0x03 -#define FEEDCONFIG_1 0x04 -#define FEEDCONFIG_2 0x05 -#define FEEDCONFIG_3 0x06 -#define CALIBRATION_CONFIG_1 0x07 -#define PS2_AU_CONTROL 0x08 -#define SAMPLE_RATE 0x09 -#define Z_IDLE_COUNT 0x0A -#define Z_SCALER 0x0B -#define SLEEP_INTERVAL 0x0C -#define SLEEP_TIMER 0x0D -#define PACKET_BYTE_0 0x12 -#define PACKET_BYTE_1 0x13 -#define PACKET_BYTE_2 0x14 -#define PACKET_BYTE_3 0x15 -#define PACKET_BYTE_4 0x16 -#define PACKET_BYTE_5 0x17 - -#define ERA_VALUE 0x1B -#define ERA_HIGH_BYTE 0x1C -#define ERA_LOW_BYTE 0x1D -#define ERA_CONTROL 0x1E - -// ADC-attenuation settings (held in BIT_7 and BIT_6) -// 1X = most sensitive, 4X = least sensitive -#define ADC_ATTENUATE_1X 0x00 -#define ADC_ATTENUATE_2X 0x40 -#define ADC_ATTENUATE_3X 0x80 -#define ADC_ATTENUATE_4X 0xC0 - #ifndef CIRQUE_PINNACLE_ATTENUATION -# define CIRQUE_PINNACLE_ATTENUATION ADC_ATTENUATE_4X +# define CIRQUE_PINNACLE_ATTENUATION EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_4X #endif -// clang-format on bool touchpad_init; uint16_t scale_data = CIRQUE_PINNACLE_DEFAULT_SCALE; @@ -107,21 +71,21 @@ void cirque_pinnacle_scale_data(pinnacle_data_t* coordinates, uint16_t xResoluti // Clears Status1 register flags (SW_CC and SW_DR) void cirque_pinnacle_clear_flags() { - RAP_Write(STATUS_1, 0x00); + RAP_Write(HOSTREG__STATUS1, HOSTREG__STATUS1_DEFVAL & ~(HOSTREG__STATUS1__COMMAND_COMPLETE | HOSTREG__STATUS1__DATA_READY)); wait_us(50); } // Enables/Disables the feed void cirque_pinnacle_enable_feed(bool feedEnable) { uint8_t temp; - RAP_ReadBytes(FEEDCONFIG_1, &temp, 1); // Store contents of FeedConfig1 register + RAP_ReadBytes(HOSTREG__FEEDCONFIG1, &temp, 1); if (feedEnable) { - temp |= 0x01; // Set Feed Enable bit + temp |= HOSTREG__FEEDCONFIG1__FEED_ENABLE; } else { - temp &= ~0x01; // Clear Feed Enable bit + temp &= ~HOSTREG__FEEDCONFIG1__FEED_ENABLE; } - RAP_Write(FEEDCONFIG_1, temp); + RAP_Write(HOSTREG__FEEDCONFIG1, temp); } /* ERA (Extended Register Access) Functions */ @@ -133,19 +97,19 @@ void ERA_ReadBytes(uint16_t address, uint8_t* data, uint16_t count) { cirque_pinnacle_enable_feed(false); // Disable feed - RAP_Write(ERA_HIGH_BYTE, (uint8_t)(address >> 8)); // Send upper byte of ERA address - RAP_Write(ERA_LOW_BYTE, (uint8_t)(address & 0x00FF)); // Send lower byte of ERA address + RAP_Write(HOSTREG__EXT_REG_AXS_ADDR_HIGH, (uint8_t)(address >> 8)); // Send upper byte of ERA address + RAP_Write(HOSTREG__EXT_REG_AXS_ADDR_LOW, (uint8_t)(address & 0x00FF)); // Send lower byte of ERA address for (uint16_t i = 0; i < count; i++) { - RAP_Write(ERA_CONTROL, 0x05); // Signal ERA-read (auto-increment) to Pinnacle + RAP_Write(HOSTREG__EXT_REG_AXS_CTRL, HOSTREG__EREG_AXS__INC_ADDR_READ | HOSTREG__EREG_AXS__READ); // Signal ERA-read (auto-increment) to Pinnacle // Wait for status register 0x1E to clear timeout_timer = timer_read(); do { - RAP_ReadBytes(ERA_CONTROL, &ERAControlValue, 1); + RAP_ReadBytes(HOSTREG__EXT_REG_AXS_CTRL, &ERAControlValue, 1); } while ((ERAControlValue != 0x00) && (timer_elapsed(timeout_timer) <= CIRQUE_PINNACLE_TIMEOUT)); - RAP_ReadBytes(ERA_VALUE, data + i, 1); + RAP_ReadBytes(HOSTREG__EXT_REG_AXS_VALUE, data + i, 1); cirque_pinnacle_clear_flags(); } @@ -158,17 +122,17 @@ void ERA_WriteByte(uint16_t address, uint8_t data) { cirque_pinnacle_enable_feed(false); // Disable feed - RAP_Write(ERA_VALUE, data); // Send data byte to be written + RAP_Write(HOSTREG__EXT_REG_AXS_VALUE, data); // Send data byte to be written - RAP_Write(ERA_HIGH_BYTE, (uint8_t)(address >> 8)); // Upper byte of ERA address - RAP_Write(ERA_LOW_BYTE, (uint8_t)(address & 0x00FF)); // Lower byte of ERA address + RAP_Write(HOSTREG__EXT_REG_AXS_ADDR_HIGH, (uint8_t)(address >> 8)); // Upper byte of ERA address + RAP_Write(HOSTREG__EXT_REG_AXS_ADDR_LOW, (uint8_t)(address & 0x00FF)); // Lower byte of ERA address - RAP_Write(ERA_CONTROL, 0x02); // Signal an ERA-write to Pinnacle + RAP_Write(HOSTREG__EXT_REG_AXS_CTRL, HOSTREG__EREG_AXS__WRITE); // Signal an ERA-write to Pinnacle // Wait for status register 0x1E to clear timeout_timer = timer_read(); do { - RAP_ReadBytes(ERA_CONTROL, &ERAControlValue, 1); + RAP_ReadBytes(HOSTREG__EXT_REG_AXS_CTRL, &ERAControlValue, 1); } while ((ERAControlValue != 0x00) && (timer_elapsed(timeout_timer) <= CIRQUE_PINNACLE_TIMEOUT)); cirque_pinnacle_clear_flags(); @@ -177,24 +141,25 @@ void ERA_WriteByte(uint16_t address, uint8_t data) { void cirque_pinnacle_set_adc_attenuation(uint8_t adcGain) { uint8_t temp = 0x00; - ERA_ReadBytes(0x0187, &temp, 1); - temp &= 0x3F; // clear top two bits + ERA_ReadBytes(EXTREG__TRACK_ADCCONFIG, &temp, 1); + temp &= EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_MASK; temp |= adcGain; - ERA_WriteByte(0x0187, temp); - ERA_ReadBytes(0x0187, &temp, 1); + ERA_WriteByte(EXTREG__TRACK_ADCCONFIG, temp); + ERA_ReadBytes(EXTREG__TRACK_ADCCONFIG, &temp, 1); } // Changes thresholds to improve detection of fingers +// Not needed for flat overlay? void cirque_pinnacle_tune_edge_sensitivity(void) { uint8_t temp = 0x00; - ERA_ReadBytes(0x0149, &temp, 1); - ERA_WriteByte(0x0149, 0x04); - ERA_ReadBytes(0x0149, &temp, 1); + ERA_ReadBytes(EXTREG__XAXIS_WIDEZMIN, &temp, 1); + ERA_WriteByte(EXTREG__XAXIS_WIDEZMIN, 0x04); // magic number from Cirque sample code + ERA_ReadBytes(EXTREG__XAXIS_WIDEZMIN, &temp, 1); - ERA_ReadBytes(0x0168, &temp, 1); - ERA_WriteByte(0x0168, 0x03); - ERA_ReadBytes(0x0168, &temp, 1); + ERA_ReadBytes(EXTREG__YAXIS_WIDEZMIN, &temp, 1); + ERA_WriteByte(EXTREG__YAXIS_WIDEZMIN, 0x03); // magic number from Cirque sample code + ERA_ReadBytes(EXTREG__YAXIS_WIDEZMIN, &temp, 1); } // Perform calibration @@ -202,22 +167,15 @@ void cirque_pinnacle_calibrate(void) { uint8_t calconfig; uint16_t timeout_timer; - // CalConfig1 (Compensation) - // Bit 0: Calibrate, 1=calibrate, 0=complete - // Bit 1: Background Comp Enable, 1=enable, 0=disable - // Bit 2: NERD Comp Enable (No, I don't know what NERD means), 1=enable, 0=disable - // Bit 3: Track Error Comp Enable, 1=enable, 0=disable - // Bit 4: Tap Comp Enable, 1=enable, 0=disable - // Bit 6: Calibration Matrix Disable, 1=disabled, 0=enabled - RAP_ReadBytes(CALIBRATION_CONFIG_1, &calconfig, 1); - calconfig |= 0x01; - RAP_Write(CALIBRATION_CONFIG_1, calconfig); + RAP_ReadBytes(HOSTREG__CALCONFIG1, &calconfig, 1); + calconfig |= HOSTREG__CALCONFIG1__CALIBRATE; + RAP_Write(HOSTREG__CALCONFIG1, calconfig); // Calibration takes ~100ms according to GT-AN-090624, doubling the timeout just to be safe timeout_timer = timer_read(); do { - RAP_ReadBytes(CALIBRATION_CONFIG_1, &calconfig, 1); - } while ((calconfig & 0x01) && (timer_elapsed(timeout_timer) <= 200)); + RAP_ReadBytes(HOSTREG__CALCONFIG1, &calconfig, 1); + } while ((calconfig & HOSTREG__CALCONFIG1__CALIBRATE) && (timer_elapsed(timeout_timer) <= 200)); cirque_pinnacle_clear_flags(); } @@ -226,22 +184,13 @@ void cirque_pinnacle_calibrate(void) { void cirque_pinnacle_cursor_smoothing(bool enable) { uint8_t feedconfig3; - // FeedConfig3 (Advanced feature flags) - // Bit 0: DualPoint Buttons, 1=enable, 0=disable - // Bit 1: Smoothing Disable, 1=disable, 0=enable - // Bit 2: Palm/NERD measurements Disable, 1=disable, 0=enable - // Bit 3: Noise Avoidance Disable, 1=disable, 0=enable - // Bit 4: WRAP lockout Disable - // Bit 5: Dynamic EMI adjust Disable - // Bit 6: HW EMI detection Disable - // Bit 7: SW EMI detection Disable - RAP_ReadBytes(FEEDCONFIG_3, &feedconfig3, 1); + RAP_ReadBytes(HOSTREG__FEEDCONFIG3, &feedconfig3, 1); if (enable) { - feedconfig3 &= ~0x02; + feedconfig3 &= ~HOSTREG__FEEDCONFIG3__DISABLE_CROSS_RATE_SMOOTHING; } else { - feedconfig3 |= 0x02; + feedconfig3 |= HOSTREG__FEEDCONFIG3__DISABLE_CROSS_RATE_SMOOTHING; } - RAP_Write(FEEDCONFIG_3, feedconfig3); + RAP_Write(HOSTREG__FEEDCONFIG3, feedconfig3); } /* Pinnacle-based TM040040/TM035035/TM023023 Functions */ @@ -257,40 +206,20 @@ void cirque_pinnacle_init(void) { // Host clears SW_CC flag cirque_pinnacle_clear_flags(); - // SysConfig1 (Low Power Mode) - // Bit 0: Reset, 1=Reset - // Bit 1: Shutdown, 1=Shutdown, 0=Active - // Bit 2: Sleep Enable, 1=low power mode, 0=normal mode // send a RESET command now, in case QMK had a soft-reset without a power cycle - RAP_Write(SYSCONFIG_1, 0x01); + RAP_Write(HOSTREG__SYSCONFIG1, HOSTREG__SYSCONFIG1__RESET); wait_ms(30); // Pinnacle needs 10-15ms to boot, so wait long enough before configuring - RAP_Write(SYSCONFIG_1, 0x00); + RAP_Write(HOSTREG__SYSCONFIG1, HOSTREG__SYSCONFIG1_DEFVAL); wait_us(50); // FeedConfig2 (Feature flags for Relative Mode Only) - // Bit 0: IntelliMouse Enable, 1=enable, 0=disable - // Bit 1: All Taps Disable, 1=disable, 0=enable - // Bit 2: Secondary Tap Disable, 1=disable, 0=enable - // Bit 3: Scroll Disable, 1=disable, 0=enable - // Bit 4: GlideExtend® Disable, 1=disable, 0=enable - // Bit 5: reserved - // Bit 6: reserved - // Bit 7: Swap X & Y, 1=90° rotation, 0=0° rotation - RAP_Write(FEEDCONFIG_2, 0x00); + RAP_Write(HOSTREG__FEEDCONFIG2, HOSTREG__FEEDCONFIG2_DEFVAL); // FeedConfig1 (Data Output Flags) - // Bit 0: Feed enable, 1=feed, 0=no feed - // Bit 1: Data mode, 1=absolute, 0=relative - // Bit 2: Filter disable, 1=no filter, 0=filter - // Bit 3: X disable, 1=no X data, 0=X data - // Bit 4: Y disable, 1=no Y data, 0=Y data - // Bit 5: reserved - // Bit 6: X data Invert, 1=X max to 0, 0=0 to Y max - // Bit 7: Y data Invert, 1=Y max to 0, 0=0 to Y max - RAP_Write(FEEDCONFIG_1, CIRQUE_PINNACLE_POSITION_MODE << 1); - - // Host sets z-idle packet count to 5 (default is 0x1F/30) - RAP_Write(Z_IDLE_COUNT, 5); + RAP_Write(HOSTREG__FEEDCONFIG1, CIRQUE_PINNACLE_POSITION_MODE ? HOSTREG__FEEDCONFIG1__DATA_TYPE__REL0_ABS1 : HOSTREG__FEEDCONFIG1_DEFVAL); + + // Host sets z-idle packet count to 5 (default is 0x1E/30) + RAP_Write(HOSTREG__ZIDLE, 5); cirque_pinnacle_set_adc_attenuation(CIRQUE_PINNACLE_ATTENUATION); // Force a calibration after setting ADC attenuation @@ -306,15 +235,15 @@ pinnacle_data_t cirque_pinnacle_read_data(void) { pinnacle_data_t result = {0}; // Check if there is valid data available - RAP_ReadBytes(STATUS_1, &data_ready, 1); // bit2 is Software Data Ready, bit3 is Command Complete, bit0 and bit1 are reserved/unused - if ((data_ready & 0x04) == 0) { + RAP_ReadBytes(HOSTREG__STATUS1, &data_ready, 1); + if ((data_ready & HOSTREG__STATUS1__DATA_READY) == 0) { // no data available yet result.valid = false; // be explicit return result; } // Read all data bytes - RAP_ReadBytes(PACKET_BYTE_0, data, 6); + RAP_ReadBytes(HOSTREG__PACKETBYTE_0, data, 6); // Get ready for the next data sample cirque_pinnacle_clear_flags(); diff --git a/drivers/sensors/cirque_pinnacle.h b/drivers/sensors/cirque_pinnacle.h index 0aba712abc07..553c6ce065ce 100644 --- a/drivers/sensors/cirque_pinnacle.h +++ b/drivers/sensors/cirque_pinnacle.h @@ -2,6 +2,7 @@ #pragma once +#include "cirque_pinnacle_regdefs.h" #include #include @@ -46,7 +47,7 @@ # include "i2c_master.h" // Cirque's 7-bit I2C Slave Address # ifndef CIRQUE_PINNACLE_ADDR -# define CIRQUE_PINNACLE_ADDR 0x2A +# define CIRQUE_PINNACLE_ADDR I2C_ADDRESS_DEFAULT # endif #elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) # include "spi_master.h" diff --git a/drivers/sensors/cirque_pinnacle_regdefs.h b/drivers/sensors/cirque_pinnacle_regdefs.h new file mode 100644 index 000000000000..993da1e757e3 --- /dev/null +++ b/drivers/sensors/cirque_pinnacle_regdefs.h @@ -0,0 +1,405 @@ +// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license +// based on https://github.com/cirque-corp/Cirque_Pinnacle_1CA027/tree/master/Additional_Examples +// with modifications and changes for QMK +// refer to documentation: Gen2 and Gen3 (Pinnacle ASIC) at https://www.cirque.com/gen2gen3-asic-details + +#pragma once + +// clang-format off + +#define HostReg__0 (0x00) +#define HostReg__1 (0x01) +#define HostReg__2 (0x02) +#define HostReg__3 (0x03) +#define HostReg__4 (0x04) +#define HostReg__5 (0x05) +#define HostReg__6 (0x06) +#define HostReg__7 (0x07) +#define HostReg__8 (0x08) +#define HostReg__9 (0x09) +#define HostReg__10 (0x0A) +#define HostReg__11 (0x0B) +#define HostReg__12 (0x0C) +#define HostReg__13 (0x0D) +#define HostReg__14 (0x0E) +#define HostReg__15 (0x0F) +#define HostReg__16 (0x10) +#define HostReg__17 (0x11) +#define HostReg__18 (0x12) +#define HostReg__19 (0x13) +#define HostReg__20 (0x14) +#define HostReg__21 (0x15) +#define HostReg__22 (0x16) +#define HostReg__23 (0x17) +#define HostReg__24 (0x18) +#define HostReg__25 (0x19) +#define HostReg__26 (0x1A) +#define HostReg__27 (0x1B) +#define HostReg__28 (0x1C) +#define HostReg__29 (0x1D) +#define HostReg__30 (0x1E) +#define HostReg__31 (0x1F) + +// ---------------- Register Assignments ------------------------------------- + +/*--------------------------------------------------------------------------*\ + Chip ID / Version +\*--------------------------------------------------------------------------*/ +// Chip ID Register +#define HOSTREG__CHIPID HostReg__0 + +// Chip Version Register +#define HOSTREG__VERSION HostReg__1 + +/*--------------------------------------------------------------------------*\ + Status Register +\*--------------------------------------------------------------------------*/ +// Status 1 Register -- MUST BE HOSTREG__2 +#define HOSTREG__STATUS1 HostReg__2 +# define HOSTREG__STATUS1__DATA_READY 0x04 +# define HOSTREG__STATUS1__COMMAND_COMPLETE 0x08 +#define HOSTREG__STATUS1_DEFVAL 0x00 + +/*--------------------------------------------------------------------------*\ + System Config Register +\*--------------------------------------------------------------------------*/ +#define HOSTREG__SYSCONFIG1 HostReg__3 +# define HOSTREG__SYSCONFIG1__RESET 0x01 +# define HOSTREG__SYSCONFIG1__STANDBY 0x02 +# define HOSTREG__SYSCONFIG1__AUTO_SLEEP 0x04 +# define HOSTREG__SYSCONFIG1__TRACK_DISABLE 0x08 +# define HOSTREG__SYSCONFIG1__ANYMEAS_ENABLE 0x10 +# define HOSTREG__SYSCONFIG1__GPIO_CTRL_ENABLE 0x20 +# define HOSTREG__SYSCONFIG1__WAKEUP_TOGGLE 0x40 +# define HOSTREG__SYSCONFIG1__FORCE_WAKEUP 0x80 +#define HOSTREG__SYSCONFIG1_DEFVAL 0x00 + +/*--------------------------------------------------------------------------*\ + Feed Config Registers +\*--------------------------------------------------------------------------*/ +// Feed Config Register1 +#define HOSTREG__FEEDCONFIG1 HostReg__4 +# define HOSTREG__FEEDCONFIG1__FEED_ENABLE 0x01 +# define HOSTREG__FEEDCONFIG1__DATA_TYPE__REL0_ABS1 0x02 +# define HOSTREG__FEEDCONFIG1__FILTER_DISABLE 0x04 +# define HOSTREG__FEEDCONFIG1__X_AXIS_DISABLE 0x08 +# define HOSTREG__FEEDCONFIG1__Y_AXIS_DISABLE 0x10 +# define HOSTREG__FEEDCONFIG1__AXIS_FOR_Z__Y0_X1 0x20 +# define HOSTREG__FEEDCONFIG1__X_DATA_INVERT 0x40 +# define HOSTREG__FEEDCONFIG1__Y_DATA_INVERT 0x80 +#define HOSTREG__FEEDCONFIG1_DEFVAL 0x00 + +// Feed Config Register2 +#define HOSTREG__FEEDCONFIG2 HostReg__5 +# define HOSTREG__FEEDCONFIG2__INTELLIMOUSE_MODE 0x01 +# define HOSTREG__FEEDCONFIG2__ALL_TAP_DISABLE 0x02 +# define HOSTREG__FEEDCONFIG2__SECONDARY_TAP_DISABLE 0x04 +# define HOSTREG__FEEDCONFIG2__SCROLL_DISABLE 0x08 +# define HOSTREG__FEEDCONFIG2__GLIDE_EXTEND_DISABLE 0x10 +# define HOSTREG__FEEDCONFIG2__PALM_BEFORE_Z_ENABLE 0x20 +# define HOSTREG__FEEDCONFIG2__BUTNS_46_SCROLL_5_MIDDLE 0x40 +# define HOSTREG__FEEDCONFIG2__SWAP_XY_RELATIVE 0x80 +#define HOSTREG__FEEDCONFIG2_DEFVAL 0x00 + +// Feed Config Register3 +#define HOSTREG__FEEDCONFIG3 HostReg__6 +# define HOSTREG__FEEDCONFIG3__BTNS_456_TO_123_IN_REL 0x01 +# define HOSTREG__FEEDCONFIG3__DISABLE_CROSS_RATE_SMOOTHING 0x02 +# define HOSTREG__FEEDCONFIG3__DISABLE_PALM_NERD_MEAS 0x04 +# define HOSTREG__FEEDCONFIG3__DISABLE_NOISE_AVOIDANCE 0x08 +# define HOSTREG__FEEDCONFIG3__DISABLE_WRAP_LOCKOUT 0x10 +# define HOSTREG__FEEDCONFIG3__DISABLE_DYNAMIC_EMI_ADJUST 0x20 +# define HOSTREG__FEEDCONFIG3__DISABLE_HW_EMI_DETECT 0x40 +# define HOSTREG__FEEDCONFIG3__DISABLE_SW_EMI_DETECT 0x80 +#define HOSTREG__FEEDCONFIG3_DEFVAL 0x00 + +/*--------------------------------------------------------------------------*\ + Calibration Config +\*--------------------------------------------------------------------------*/ +#define HOSTREG__CALCONFIG1 HostReg__7 +# define HOSTREG__CALCONFIG1__CALIBRATE 0x01 +# define HOSTREG__CALCONFIG1__BACKGROUND_COMP_ENABLE 0x02 +# define HOSTREG__CALCONFIG1__NERD_COMP_ENABLE 0x04 +# define HOSTREG__CALCONFIG1__TRACK_ERROR_COMP_ENABLE 0x08 +# define HOSTREG__CALCONFIG1__TAP_COMP_ENABLE 0x10 +# define HOSTREG__CALCONFIG1__PALM_ERROR_COMP_ENABLE 0x20 +# define HOSTREG__CALCONFIG1__CALIBRATION_MATRIX_DISABLE 0x40 +# define HOSTREG__CALCONFIG1__FORCE_PRECALIBRATION_NOISE_CHECK 0x80 +#define HOSTREG__CALCONFIG1_DEFVAL (HOSTREG__CALCONFIG1__BACKGROUND_COMP_ENABLE | HOSTREG__CALCONFIG1__NERD_COMP_ENABLE | HOSTREG__CALCONFIG1__TRACK_ERROR_COMP_ENABLE | HOSTREG__CALCONFIG1__TAP_COMP_ENABLE | HOSTREG__CALCONFIG1__PALM_ERROR_COMP_ENABLE) + +/*--------------------------------------------------------------------------*\ + PS2 Aux Control Register +\*--------------------------------------------------------------------------*/ +#define HOSTREG__PS2AUX_CTRL HostReg__8 +# define HOSTREG__PS2AUX_CTRL__CMD_PASSTHRU_ENABLE 0x01 +# define HOSTREG__PS2AUX_CTRL__SP_EXTENDED_MODE 0x02 +# define HOSTREG__PS2AUX_CTRL__GS_DISABLE 0x04 +# define HOSTREG__PS2AUX_CTRL__SP_DISABLE 0x08 +# define HOSTREG__PS2AUX_CTRL__GS_COORDINATE_DISABLE 0x10 +# define HOSTREG__PS2AUX_CTRL__SP_COORDINATE_DISABLE 0x20 +# define HOSTREG__PS2AUX_CTRL__DISABLE_AA00_DETECT 0x40 +# define HOSTREG__PS2AUX_CTRL__AUX_PRESENT 0x80 +#define HOSTREG__PR2AUX_CTRL_DEFVAL 0x00 + +/*--------------------------------------------------------------------------*\ + Sample Rate Value +\*--------------------------------------------------------------------------*/ +#define HOSTREG__SAMPLERATE HostReg__9 +# define HOSTREG__SAMPLERATE__10_SPS 0x0A +# define HOSTREG__SAMPLERATE__20_SPS 0x14 +# define HOSTREG__SAMPLERATE__40_SPS 0x28 +# define HOSTREG__SAMPLERATE__60_SPS 0x3C +# define HOSTREG__SAMPLERATE__80_SPS 0x50 +# define HOSTREG__SAMPLERATE__100_SPS 0x64 +# define HOSTREG__SAMPLERATE__200_SPS 0xC8 // 200sps not supported + // only for ps2 compatibility + // rate set to 100sps +#define HOSTREG__SAMPLERATE_DEFVAL HOSTREG__SAMPLERATE__100_SPS + +/*--------------------------------------------------------------------------*\ + Z Idle Value +\*--------------------------------------------------------------------------*/ +#define HOSTREG__ZIDLE HostReg__10 +#define HOSTREG__ZIDLE_DEFVAL 30 // 0x1E + +/*--------------------------------------------------------------------------*\ + Z Scaler Value +\*--------------------------------------------------------------------------*/ +#define HOSTREG__ZSCALER HostReg__11 +#define HOSTREG__ZSCALER_DEFVAL 8 // 0x08 + +/*--------------------------------------------------------------------------*\ + Sleep Interval Value +\*--------------------------------------------------------------------------*/ +#define HOSTREG__SLEEP_INTERVAL HostReg__12 +#define HOSTREG__SLEEP_INTERVAL_DEFVAL 73 // 0x49 + +/*--------------------------------------------------------------------------*\ + Sleep Delay Value +\*--------------------------------------------------------------------------*/ +#define HOSTREG__SLEEP_DELAY HostReg__13 +#define HOSTREG__SLEEP_DELAY_DEFVAL 39 // 0x27 + +/*--------------------------------------------------------------------------*\ + Dynamic EMI Bad Channel Count Thresholds +\*--------------------------------------------------------------------------*/ +#define HOSTREG__DYNAMIC_EMI_ADJUST_THRESHOLD HostReg__14 +#define HOSTREG__DYNAMIC_EMI_ADJUST_THRESHOLD_DEFVAL 66 // 0x42 + +/*--------------------------------------------------------------------------*\ + Packet Registers +\*--------------------------------------------------------------------------*/ +#define HOSTREG__PACKETBYTE_0 HostReg__18 +#define HOSTREG__PACKETBYTE_1 HostReg__19 +#define HOSTREG__PACKETBYTE_2 HostReg__20 +#define HOSTREG__PACKETBYTE_3 HostReg__21 +#define HOSTREG__PACKETBYTE_4 HostReg__22 +#define HOSTREG__PACKETBYTE_5 HostReg__23 + +/*--------------------------------------------------------------------------*\ + Port A GPIO Control +\*--------------------------------------------------------------------------*/ +#define HOSTREG__PORTA_GPIO_CTRL HostReg__24 +#define HOSTREG__PORTA_GPIO_CTRL_DEFVAL 0xFF + +/*--------------------------------------------------------------------------*\ + Port A GPIO Data +\*--------------------------------------------------------------------------*/ +#define HOSTREG__PORTA_GPIO_DATA HostReg__25 +#define HOSTREG__PORTA_GPIO_DATA_DEFVAL 0x00 + +/*--------------------------------------------------------------------------*\ + Port B GPIO Control And Data +\*--------------------------------------------------------------------------*/ + +#define HOSTREG__PORTB_GPIO_CTRL_DATA HostReg__26 +# define HOSTREG__PORTB_GPIO_DATA__PB0 0x01 +# define HOSTREG__PORTB_GPIO_DATA__PB1 0x02 +# define HOSTREG__PORTB_GPIO_DATA__PB2 0x04 +# define HOSTREG__PORTB_GPIO_CTRL__PB0 0x08 +# define HOSTREG__PORTB_GPIO_CTRL__PB1 0x10 +# define HOSTREG__PORTB_GPIO_CTRL__PB2 0x20 +# define HOSTREG__PORTB_GPIO_RSVD_0 0x40 +# define HOSTREG__PORTB_GPIO_READ1_WRITE0 0x80 +#define HOSTREG__PORTB_GPIO_CTRL_DATA_DEFVAL (HOSTREG__PORTB_GPIO_CTRL__PB0 | HOSTREG__PORTB_GPIO_CTRL__PB1 | HOSTREG__PORTB_GPIO_CTRL__PB2) + +/*--------------------------------------------------------------------------*\ + Extended Register Access +\*--------------------------------------------------------------------------*/ +#define HOSTREG__EXT_REG_AXS_VALUE HostReg__27 + +#define HOSTREG__EXT_REG_AXS_ADDR_HIGH HostReg__28 +#define HOSTREG__EXT_REG_AXS_ADDR_LOW HostReg__29 + +#define HOSTREG__EXT_REG_AXS_CTRL HostReg__30 +# define HOSTREG__EREG_AXS__READ 0x01 +# define HOSTREG__EREG_AXS__WRITE 0x02 +# define HOSTREG__EREG_AXS__INC_ADDR_READ 0x04 +# define HOSTREG__EREG_AXS__INC_ADDR_WRITE 0x08 +# define HOSTREG__EREG_AXS__RSVD_3 0x10 +# define HOSTREG__EREG_AXS__RSVD_2 0x20 +# define HOSTREG__EREG_AXS__RSVD_1 0x40 +# define HOSTREG__EREG_AXS__RSVD_0 0x80 + +#define HOSTREG__EXT_REG_AXS_VALUE_DEFVAL 0x00 +#define HOSTREG__EXT_REG_AXS_ADDR_HIGH_DEFVAL 0x00 +#define HOSTREG__EXT_REG_AXS_ADDR_LOW_DEFVAL 0x00 +#define HOSTREG__EXT_REG_AXS_CTRL_DEFVAL 0x00 + +/*--------------------------------------------------------------------------*\ + Product ID +\*--------------------------------------------------------------------------*/ +#define HOSTREG__PRODUCT_ID HostReg__31 + + + +//Some useful values +#define I2C_ADDRESS_DEFAULT 0x2A +#define FIRMWARE_ID 0x07 +#define FIRMWARE_VERSION 0x9D + +//Anymeas config options +//First setting is HostReg 5. This sets toggle frequency (EF) and gain. +//Gain is upper two bits (0xC0), frequency is lower 6 bits (0x3F) +#define AnyMeas_AccumBits_ElecFreq HostReg__5 +# define ADCCNFG_ELEC_FREQ 0x3F /* Bit 4, 3, 2, 1, 0 */ +# define ADCCNFG_EF_0 0x02 // 500,000Hz +# define ADCCNFG_EF_1 0x03 // 444,444Hz +# define ADCCNFG_EF_2 0x04 // 400,000Hz +# define ADCCNFG_EF_3 0x05 // 363,636Hz +# define ADCCNFG_EF_4 0x06 // 333,333Hz +# define ADCCNFG_EF_5 0x07 // 307,692Hz +# define ADCCNFG_EF_6 0x09 // 267,000Hz +# define ADCCNFG_EF_7 0x0B // 235,000Hz +# define ADCCNFG_ACCUMBITSSELECT 0xC0 /* Bit 7, 6 */ +# define ADCCNFG_ACCBITS_17_14_0 0x00 //This is about 2x gain +# define ADCCNFG_ACCBITS_17_15_1 0x40 //This is about 1.6x gain +# define ADCCNFG_ACCBITS_17_2__80 0x80 //This is about 1.3x gain +# define ADCCNFG_ACCBITS_17_2__C0 0xC0 //This is lowest gain +//Note, all frequencies above are based on default 500ns aperture. If aperture is shorter the frequencies will be faster and if aperture is longer the frequencies will be slower. + +//Next is HostReg 6. This sets the sample length. There are four possible settings to bit length. All other settings are not normally used and should be a 0. +#define AnyMeas_BitLength HostReg__6 +# define ADCCTRL_BIT_LENGTH 0x03 /* Bit 1, 0 */ +# define ADCCTRL_SAMPLES_32 0x00 //Note: this does not work. +# define ADCCTRL_SAMPLES_128 0x01 +# define ADCCTRL_SAMPLES_256 0x02 +# define ADCCTRL_SAMPLES_512 0x03 +# define ADCCTRL_ENABLE 0x20 /* Bit 5 */ +# define ADCCTRL_INT_FLAG 0x40 /* Bit 6 */ +# define ADCCTRL_START_BUSY 0x80 /* Bit 7 */ +//The smaller the sample length the faster the measurement but the lower the SNR. For high SNR requirements 512 sample length is recommended. Alternatively, multiple 128 or 256 length measurements could be averaged. + +//Next is HostReg 7. This sets the sense mux. Pinnacle has 2 sense lines, Sense N and Sense P1. There is also a Sense P2 but it is not bonded out, it is only internal. +//Signal on Sense N will be inverted from signal on Sense P1. Other than sign inversion, signal strength should be the same. +#define AnyMeas_ADC_MuxControl HostReg__7 +# define ADCMUXCTRL_SENSEP1GATE 0x01 //Enables Sense P1. Can be combined with Sense N input or exclusivly Sense P1 alone. +# define ADCMUXCTRL_SENSEP2GATE 0x02 //Not used. +# define ADCMUXCTRL_SENSENGATE 0x04 //Enables Sense N. Can be combined with Sense P inputs or exclusivly Sense N alone. +# define ADCMUXCTRL_REF0GATE 0x08 //This enables the RefCap0. This is a capacitor inside the chip that is roughly 0.25pF. It is also controlled with the toggle and polarity bits so those bits must be set properly as well in order to use it. +# define ADCMUXCTRL_REF1GATE 0x10 //This enables the RefCap1. This is a capacitor inside the chip that is roughly 0.5pF. It is also controlled with the toggle and polarity bits so those bits must be set properly as well in order to use it. +# define ADCMUXCTRL_OSCMEASEN 0x80 //this is a test mode for measuring the internal oscillator. It is for IC test only. + +//Next is HostReg 8. This contains various ADC config settings that are not likely to be used. +#define AnyMeas_ADC_Config2 HostReg__8 +# define ADCCNFG2_ADC_CLK_SELECT 0x01 /* Bit 0 */ //If 0 use the standard 8Mhz clock. If 1 use a divide by 2, 4Mhz clock. Only used if extra slow toggle frequencies are required. +# define ADCCNFG2_EMI_FLAG 0x02 /* Bit 1 */ //EMI flag threshold only used with internal FW. Not valid in anymeas mode. +# define ADCCNFG2_EMI_FLAG_THRESHOLD_0 0x04 /* Bit 2 */ //EMI flag threshold only used with internal FW. Not valid in anymeas mode. +# define ADCCNFG2_EMI_FLAG_THRESHOLD_1 0x08 /* Bit 3 */ //EMI flag threshold only used with internal FW. Not valid in anymeas mode. +# define ADCCNFG2_DSX2_EXTEND 0x10 /* Bit 4 */ //extend one signal on the receive. Could also be helpful in situations where sensor cap is extremely high. +# define ADCCNFG2_ETOGGLE_DELAY 0x20 /* Bit 5 */ //delay a bit before toggling electrodes. Could be helpful in situations where sensor cap is extremely high. + +//Next is HostReg 9. This sets the aperture length. Bottom 4 bits set the aperture width +#define AnyMeas_ADC_AWidth HostReg__9 +# define ADCAWIDTH_AWIDTHMASK 0x0F +# define ADCAWIDTH_APERTURE_OPEN 0x00 //does not work +# define ADCAWIDTH_APERTURE_125NS 0x01 //does not work +# define ADCAWIDTH_APERTURE_250NS 0x02 +# define ADCAWIDTH_APERTURE_375NS 0x03 +# define ADCAWIDTH_APERTURE_500NS 0x04 +# define ADCAWIDTH_APERTURE_625NS 0x05 +# define ADCAWIDTH_APERTURE_750NS 0x06 +# define ADCAWIDTH_APERTURE_875NS 0x07 +# define ADCAWIDTH_APERTURE_1000NS 0x08 +# define ADCAWIDTH_APERTURE_1125NS 0x09 +# define ADCAWIDTH_APERTURE_1250NS 0x0A +# define ADCAWIDTH_APERTURE_1375NS 0x0B +# define ADCAWIDTH_APERTURE_1500NS 0x0C +# define ADCAWIDTH_APERTURE_1625NS 0x0D +# define ADCAWIDTH_APERTURE_1750NS 0x0E +# define ADCAWIDTH_APERTURE_1875NS 0x0F +# define ADCAWIDTH_AWIDTHPLUSHALF 0x10 +# define ADCAWIDTH_AOPEN 0x20 +# define ADCAWIDTH_W2WAIT 0x40 + +//next two registers give the high and low bytes to the 16 bit address where Pinnacle will pull the measurement data. Normally these addresses are within the base 32 registers. +#define AnyMeas_pADCMeasInfoStart_High_Byte HostReg__10 +#define AnyMeas_pADCMeasInfoStart_Low_Byte HostReg__11 + +//Next is the measurement index, this sets the measurement state machine to the start and should be a 0 at start. +#define AnyMeas_MeasIndex HostReg__12 +# define ANYMEASSTATE_RESET_START 0x00 +# define ANYMEASSTATE_START_MEASUREMENT 0x01 +# define ANYMEASSTATE_WAIT_FOR_MEASUREMENT_AND_HOST 0x02 + +//next is the state itself of the measurement, should always be 0. +#define AnyMeas_State HostReg__13 + +//next is the number of measurements. Use 0x80 to repeat the single measurement or repeat a number of measurements. +//0x40 will turn the ADC off after measurements. This will result in longer startup time for a subsequent measurement, but lower idle power draw. +#define AnyMeas_Control_NumMeas HostReg__14 +# define ANYMEAS_CONTROL__NUM_MEAS_MASK 0x3F +# define ANYMEAS_CONTROL__ADC_POST_MEAS_PWR 0x40 +# define ANYMEAS_CONTROL__REPEAT 0x80 + +//These are not used +#define AnyMeas_pADCMeasInfo_High_Byte HostReg__15 +#define AnyMeas_pADCMeasInfo_Low_Byte HostReg__16 + +//16 bit result of measurement will be found in these two registers. +#define AnyMeas_Result_High_Byte HostReg__17 +#define AnyMeas_Result_Low_Byte HostReg__18 + +// ---------------- Extended Register Assignments ---------------------------- +/*--------------------------------------------------------------------------*\ + ADC Mux Control +\*--------------------------------------------------------------------------*/ +#define EXTREG__ADCMUX_CTRL 0x00EB +# define EXTREG__ADCMUX_CTRL__SNSP_ENABLE 0x01 +# define EXTREG__ADCMUX_CTRL__SNSN_ENABLE 0x04 + +/*--------------------------------------------------------------------------*\ + Timer Reload Registers +\*--------------------------------------------------------------------------*/ +#define EXTREG__PACKET_TIMER_RELOAD 0x019F +#define EXTREG__TRACK_TIMER_RELOAD 0x019E +// These two registers should have matching content. +# define EXTREG__TIMER_RELOAD__300_SPS 0x06 +# define EXTREG__TIMER_RELOAD__200_SPS 0x09 +# define EXTREG__TIMER_RELOAD__100_SPS 0x13 + +/*--------------------------------------------------------------------------*\ + Track ADC Config +\*--------------------------------------------------------------------------*/ +#define EXTREG__TRACK_ADCCONFIG 0x0187 +// ADC-attenuation settings (held in BIT_7 and BIT_6) +// 1X = most sensitive, 4X = least sensitive +# define EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_MASK 0x3F +# define EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_1X 0x00 +# define EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_2X 0x40 +# define EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_3X 0x80 +# define EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_4X 0xC0 +#define EXTREG__TRACK_ADCCONFIG_DEFVAL 0x4E + + +/*--------------------------------------------------------------------------*\ + Tune Edge Sensitivity +\*--------------------------------------------------------------------------*/ +// These registers are not detailed in any publically available documentation +// Names inferred from debug prints in https://github.com/cirque-corp/Cirque_Pinnacle_1CA027/blob/master/Circular_Trackpad +#define EXTREG__XAXIS_WIDEZMIN 0x0149 +#define EXTREG__YAXIS_WIDEZMIN 0x0168 +#define EXTREG__XAXIS_WIDEZMIN_DEFVAL 0x06 +#define EXTREG__YAXIS_WIDEZMIN_DEFVAL 0x05 + +// clang-format on From f85df632265efd360842f874eb00ec9f06d6cac7 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Tue, 28 Jun 2022 22:48:09 -0700 Subject: [PATCH 12/27] cirque_pinnacle: Compile option for curved overlay tuneEdgeSensitivity() in Cirque examples were only called for curved overlay. Disabling cirque_pinnacle_tune_edge_sensitivity() for flat overlay seems to help with the issue where finger near the outer perimeter is captured into baseline and renders the trackpad unusable until baseline recovers. Default ADC attenuation in Cirque's curved overlay examples is 2x. --- docs/feature_pointing_device.md | 1 + drivers/sensors/cirque_pinnacle.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/feature_pointing_device.md b/docs/feature_pointing_device.md index 4d2a5dab25a1..b168a291574a 100644 --- a/docs/feature_pointing_device.md +++ b/docs/feature_pointing_device.md @@ -97,6 +97,7 @@ This supports the Cirque Pinnacle 1CA027 Touch Controller, which is used in the |`CIRQUE_PINNACLE_Y_UPPER` | (Optional) The maximum reachable Y value on the sensor. | `1471` | |`CIRQUE_PINNACLE_DIAMETER_MM` | (Optional) Diameter of the trackpad sensor in millimeters. | `40` | |`CIRQUE_PINNACLE_ATTENUATION` | (Optional) Sets the attenuation of the sensor data. | `ADC_ATTENUATE_4X` | +|`CIRQUE_PINNACLE_CURVED_OVERLAY` | (Optional) Applies settings tuned for curved overlay. | _not defined_ | |`CIRQUE_PINNACLE_TAPPING_TERM` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` | |`CIRQUE_PINNACLE_TOUCH_DEBOUNCE` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` | diff --git a/drivers/sensors/cirque_pinnacle.c b/drivers/sensors/cirque_pinnacle.c index 8524e5e767d6..2b3cd77ebeca 100644 --- a/drivers/sensors/cirque_pinnacle.c +++ b/drivers/sensors/cirque_pinnacle.c @@ -10,7 +10,11 @@ #include "timer.h" #ifndef CIRQUE_PINNACLE_ATTENUATION -# define CIRQUE_PINNACLE_ATTENUATION EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_4X +# ifdef CIRQUE_PINNACLE_CURVED_OVERLAY +# define CIRQUE_PINNACLE_ATTENUATION EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_2X +# else +# define CIRQUE_PINNACLE_ATTENUATION EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_4X +# endif #endif bool touchpad_init; @@ -225,7 +229,9 @@ void cirque_pinnacle_init(void) { // Force a calibration after setting ADC attenuation cirque_pinnacle_calibrate(); +#ifdef CIRQUE_PINNACLE_CURVED_OVERLAY cirque_pinnacle_tune_edge_sensitivity(); +#endif cirque_pinnacle_enable_feed(true); } From 8344eaaf76df53227c73c490648d707792bdec3e Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Wed, 29 Jun 2022 00:12:46 -0700 Subject: [PATCH 13/27] cirque_pinnacle: Adjust calibration sequence Follow sequence from Cirque sample code. Set ADC attenuation, tune edge sensitivity, then calibrate. --- drivers/sensors/cirque_pinnacle.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/sensors/cirque_pinnacle.c b/drivers/sensors/cirque_pinnacle.c index 2b3cd77ebeca..842b16e54544 100644 --- a/drivers/sensors/cirque_pinnacle.c +++ b/drivers/sensors/cirque_pinnacle.c @@ -226,12 +226,12 @@ void cirque_pinnacle_init(void) { RAP_Write(HOSTREG__ZIDLE, 5); cirque_pinnacle_set_adc_attenuation(CIRQUE_PINNACLE_ATTENUATION); - // Force a calibration after setting ADC attenuation - cirque_pinnacle_calibrate(); - #ifdef CIRQUE_PINNACLE_CURVED_OVERLAY cirque_pinnacle_tune_edge_sensitivity(); #endif + // Force a calibration after setting ADC attenuation + cirque_pinnacle_calibrate(); + cirque_pinnacle_enable_feed(true); } From 97d1644ea584fc4c7dd0b06a88e4228600337a1d Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Thu, 30 Jun 2022 02:20:48 -0700 Subject: [PATCH 14/27] cirque pointing driver: Move fancy processing code Inertial cursor is usable by any sensor with a lift status. Extracted into quantum/pointing_device_gestures.[ch], to be extracted into other get_report() functions if desired. For example, one could mount a PMW3360 upside down with a clear sheet over the lens, then use !(report.motion & 0x08) as Z and flick a finger on said clear surface as a pointing device. Circular scroll is specific to a circle trackpad reporting absolute coordinates, Cirque is currently the only sensor of this type in QMK. Tap could be generalized to multiple sensors, keeping it to lower level for now since tap enable will be a register write for relative mode. These two live in drivers/sensors/cirque_pinnacle_gestures.[ch]. --- builddefs/common_features.mk | 4 + drivers/sensors/cirque_pinnacle_gestures.c | 173 +++++++++++ drivers/sensors/cirque_pinnacle_gestures.h | 71 +++++ quantum/pointing_device.h | 2 + quantum/pointing_device_drivers.c | 319 ++------------------- quantum/pointing_device_gestures.c | 77 +++++ quantum/pointing_device_gestures.h | 44 +++ 7 files changed, 395 insertions(+), 295 deletions(-) create mode 100644 drivers/sensors/cirque_pinnacle_gestures.c create mode 100644 drivers/sensors/cirque_pinnacle_gestures.h create mode 100644 quantum/pointing_device_gestures.c create mode 100644 quantum/pointing_device_gestures.h diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index b9ee0a30a75b..d97b78b252ba 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -150,10 +150,14 @@ ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes) else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), cirque_pinnacle_i2c) OPT_DEFS += -DSTM32_I2C -DHAL_USE_I2C=TRUE SRC += drivers/sensors/cirque_pinnacle.c + SRC += drivers/sensors/cirque_pinnacle_gestures.c + SRC += $(QUANTUM_DIR)/pointing_device_gestures.c QUANTUM_LIB_SRC += i2c_master.c else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), cirque_pinnacle_spi) OPT_DEFS += -DSTM32_SPI -DHAL_USE_SPI=TRUE SRC += drivers/sensors/cirque_pinnacle.c + SRC += drivers/sensors/cirque_pinnacle_gestures.c + SRC += $(QUANTUM_DIR)/pointing_device_gestures.c QUANTUM_LIB_SRC += spi_master.c else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), pimoroni_trackball) OPT_DEFS += -DSTM32_SPI -DHAL_USE_I2C=TRUE diff --git a/drivers/sensors/cirque_pinnacle_gestures.c b/drivers/sensors/cirque_pinnacle_gestures.c new file mode 100644 index 000000000000..c255bf09d740 --- /dev/null +++ b/drivers/sensors/cirque_pinnacle_gestures.c @@ -0,0 +1,173 @@ +/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) + * Copyright 2022 Daniel Kao + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "cirque_pinnacle_gestures.h" +#include "pointing_device.h" +#include "timer.h" + +#ifdef POINTING_DEVICE_MOTION_PIN +# error POINTING_DEVICE_MOTION_PIN not supported when using inertial cursor. Need repeated calls to get_report() to generate glide events. +#endif + +static cirque_pinnacle_features_t features = {.tap_enable = true}; +static trackpad_tap_context_t tap; + +static report_mouse_t trackpad_tap(report_mouse_t mouse_report, pinnacle_data_t touchData) { + if (touchData.touchDown != tap.touchDown) { + tap.touchDown = touchData.touchDown; + if (!touchData.zValue) { + if (timer_elapsed(tap.timer) < CIRQUE_PINNACLE_TAPPING_TERM && tap.timer != 0) { + mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1); + pointing_device_set_report(mouse_report); + pointing_device_send(); +#if TAP_CODE_DELAY > 0 + wait_ms(TAP_CODE_DELAY); +#endif + mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1); + pointing_device_set_report(mouse_report); + pointing_device_send(); + } + } + tap.timer = timer_read(); + } + if (timer_elapsed(tap.timer) > (CIRQUE_PINNACLE_TOUCH_DEBOUNCE)) { + tap.timer = 0; + } + + return mouse_report; +} + +// extern this function for now +void cirque_pinnacle_enable_tap(bool enable) { + features.tap_enable = enable; +} + +static circular_scroll_context_t scroll = {.outer_ring_pct = 33, .movement_pct = 6, .movement_ratio = 1.2, .wheel_clicks = 9}; + +static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { + circular_scroll_t report = {0, 0, false}; + int16_t x, y; + uint16_t center = cirque_pinnacle_get_scale() / 2; + int8_t wheel_clicks; + float mag, ang, scalar_projection, scalar_rejection, parallel_movement, perpendicular_movement; + int32_t dot, det; + + if (touchData.zValue) { + // place origin at center of trackpad + x = (int16_t)(touchData.xValue - center); + y = (int16_t)(touchData.yValue - center); + + // check if first touch + if (!scroll.z) { + report.suppress_touch = false; + // check if touch falls within outer ring + mag = hypotf(x, y); + if (mag / center >= (100.0 - scroll.outer_ring_pct) / 100.0) { + scroll.state = SCROLL_DETECTING; + scroll.x = x; + scroll.y = y; + scroll.mag = mag; + // decide scroll axis: + // vertical if started from righ half + // horizontal if started from left half + // reverse for left hand? TODO? +#if defined(POINTING_DEVICE_ROTATION_90) + scroll.axis = y < 0; +#elif defined(POINTING_DEVICE_ROTATION_180) + scroll.axis = x > 0; +#elif defined(POINTING_DEVICE_ROTATION_270) + scroll.axis = y > 0; +#else + scroll.axis = x < 0; +#endif + } + } else if (scroll.state == SCROLL_DETECTING) { + report.suppress_touch = true; + // already detecting scroll, check movement from touchdown location + mag = hypotf(x - scroll.x, y - scroll.y); + if (mag >= scroll.movement_pct / 100.0 * center) { + // check ratio of movement towards center vs. along perimeter + // this distinguishes circular scroll from swipes that start from edge of trackpad + dot = (int32_t)scroll.x * (int32_t)x + (int32_t)scroll.y * (int32_t)y; + det = (int32_t)scroll.x * (int32_t)y - (int32_t)scroll.y * (int32_t)x; + scalar_projection = dot / scroll.mag; + scalar_rejection = det / scroll.mag; + parallel_movement = fabs(scroll.mag - fabs(scalar_projection)); + perpendicular_movement = fabs(scalar_rejection); + if (parallel_movement * scroll.movement_ratio > perpendicular_movement) { + // not a scroll, release coordinates + report.suppress_touch = false; + scroll.state = NOT_SCROLL; + } else { + // scroll detected + scroll.state = SCROLL_VALID; + } + } + } + if (scroll.state == SCROLL_VALID) { + report.suppress_touch = true; + dot = (int32_t)scroll.x * (int32_t)x + (int32_t)scroll.y * (int32_t)y; + det = (int32_t)scroll.x * (int32_t)y - (int32_t)scroll.y * (int32_t)x; + ang = atan2f(det, dot); + wheel_clicks = roundf(ang * (float)scroll.wheel_clicks / M_PI); + if (wheel_clicks >= 1 || wheel_clicks <= -1) { + if (scroll.axis == 0) { + report.v = -wheel_clicks; + } else { + report.h = wheel_clicks; + } + scroll.x = x; + scroll.y = y; + } + } + } + + scroll.z = touchData.zValue; + if (!scroll.z) scroll.state = SCROLL_UNINITIALIZED; + + return report; +} + +// extern this for now +void cirque_pinnacle_enable_circular_scroll(bool enable) { + features.circular_scroll_enable = enable; +} + +// To set a trackpad exclusively as scroll wheel: outer_ring_pct = 100, movement_pct = 0, movement_ratio = 0 +void cirque_pinnacle_configure_circular_scroll(float outer_ring_pct, float movement_pct, float movement_ratio, uint8_t wheel_clicks) { + scroll.outer_ring_pct = outer_ring_pct; + scroll.movement_pct = movement_pct; + scroll.movement_ratio = movement_ratio; + scroll.wheel_clicks = wheel_clicks; +} + +bool cirque_pinnacle_gestures(report_mouse_t* mouse_report, pinnacle_data_t touchData) { + bool suppress_mouse_update = false; + circular_scroll_t scroll_report; + + if (features.circular_scroll_enable) { + scroll_report = circular_scroll(touchData); + mouse_report->v = scroll_report.v; + mouse_report->h = scroll_report.h; + suppress_mouse_update = scroll_report.suppress_touch; + } + if (features.tap_enable) { + *mouse_report = trackpad_tap(*mouse_report, touchData); + } + + return suppress_mouse_update; +} diff --git a/drivers/sensors/cirque_pinnacle_gestures.h b/drivers/sensors/cirque_pinnacle_gestures.h new file mode 100644 index 000000000000..9368aeeac47d --- /dev/null +++ b/drivers/sensors/cirque_pinnacle_gestures.h @@ -0,0 +1,71 @@ +/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) + * Copyright 2022 Daniel Kao + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "cirque_pinnacle.h" +#include "report.h" + +#ifndef CIRQUE_PINNACLE_TAPPING_TERM +# include "action.h" +# include "action_tapping.h" +# define CIRQUE_PINNACLE_TAPPING_TERM GET_TAPPING_TERM(KC_BTN1, &(keyrecord_t){}) +#endif +#ifndef CIRQUE_PINNACLE_TOUCH_DEBOUNCE +# define CIRQUE_PINNACLE_TOUCH_DEBOUNCE (CIRQUE_PINNACLE_TAPPING_TERM * 8) +#endif + +typedef enum { + SCROLL_UNINITIALIZED, + SCROLL_DETECTING, + SCROLL_VALID, + NOT_SCROLL, +} circular_scroll_status_t; + +typedef struct { + int8_t v; + int8_t h; + bool suppress_touch; +} circular_scroll_t; + +typedef struct { + float mag; + int16_t x; + int16_t y; + uint16_t z; + circular_scroll_status_t state; + bool axis; + // settings + float outer_ring_pct; // width of outer ring + float movement_pct; // amount of movement before triggering scroll validation + float movement_ratio; // ratio of movement along perimeter / movement towards center + uint8_t wheel_clicks; // how many clicks to report in a half circle +} circular_scroll_context_t; + +typedef struct { + uint16_t timer; + bool touchDown; +} trackpad_tap_context_t; + +typedef struct { + bool tap_enable; + bool circular_scroll_enable; +} cirque_pinnacle_features_t; + +bool cirque_pinnacle_gestures(report_mouse_t* mouse_report, pinnacle_data_t touchData); +void cirque_pinnacle_enable_tap(bool enable); +void cirque_pinnacle_enable_circular_scroll(bool enable); +void cirque_pinnacle_configure_circular_scroll(float outer_ring_pct, float movement_pct, float movement_ratio, uint8_t wheel_clicks); diff --git a/quantum/pointing_device.h b/quantum/pointing_device.h index 1e5ef9590c9f..8225e55aa26b 100644 --- a/quantum/pointing_device.h +++ b/quantum/pointing_device.h @@ -31,6 +31,8 @@ along with this program. If not, see . # include "drivers/sensors/analog_joystick.h" #elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) # include "drivers/sensors/cirque_pinnacle.h" +# include "drivers/sensors/cirque_pinnacle_gestures.h" +# include "pointing_device_gestures.h" #elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball) # include "i2c_master.h" # include "drivers/sensors/pimoroni_trackball.h" diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index 0ff3f293d7b5..bb6cffa16aae 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -97,281 +97,20 @@ const pointing_device_driver_t pointing_device_driver = { // clang-format on #elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) -# ifndef CIRQUE_PINNACLE_TAPPING_TERM -# include "action.h" -# include "action_tapping.h" -# define CIRQUE_PINNACLE_TAPPING_TERM GET_TAPPING_TERM(KC_BTN1, &(keyrecord_t){}) -# endif -# ifndef CIRQUE_PINNACLE_TOUCH_DEBOUNCE -# define CIRQUE_PINNACLE_TOUCH_DEBOUNCE (CIRQUE_PINNACLE_TAPPING_TERM * 8) -# endif - -typedef struct { - bool tap_enable; - bool circular_scroll_enable; - bool cursor_glide_enable; -} cirque_pinnacle_features_t; - -static cirque_pinnacle_features_t features = {.tap_enable = true}; - -typedef struct { - uint16_t timer; - bool touchDown; -} trackpad_tap_context_t; - -static trackpad_tap_context_t tap; - -static report_mouse_t trackpad_tap(report_mouse_t mouse_report, pinnacle_data_t touchData) { - if (touchData.touchDown != tap.touchDown) { - tap.touchDown = touchData.touchDown; - if (!touchData.zValue) { - if (timer_elapsed(tap.timer) < CIRQUE_PINNACLE_TAPPING_TERM && tap.timer != 0) { - mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1); - pointing_device_set_report(mouse_report); - pointing_device_send(); -# if TAP_CODE_DELAY > 0 - wait_ms(TAP_CODE_DELAY); -# endif - mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1); - pointing_device_set_report(mouse_report); - pointing_device_send(); - } - } - tap.timer = timer_read(); - } - if (timer_elapsed(tap.timer) > (CIRQUE_PINNACLE_TOUCH_DEBOUNCE)) { - tap.timer = 0; - } - - return mouse_report; -} - -// extern this function for now -void cirque_pinnacle_enable_tap(bool enable) { - features.tap_enable = enable; -} - -typedef enum { - SCROLL_UNINITIALIZED, - SCROLL_DETECTING, - SCROLL_VALID, - NOT_SCROLL, -} circular_scroll_status_t; - -typedef struct { - int8_t v; - int8_t h; - bool suppress_touch; -} circular_scroll_t; - -typedef struct { - float mag; - int16_t x; - int16_t y; - uint16_t z; - circular_scroll_status_t state; - bool axis; - // settings - float outer_ring_pct; // width of outer ring - float movement_pct; // amount of movement before triggering scroll validation - float movement_ratio; // ratio of movement along perimeter / movement towards center - uint8_t wheel_clicks; // how many clicks to report in a half circle -} circular_scroll_context_t; - -static circular_scroll_context_t scroll = {.outer_ring_pct = 33, .movement_pct = 6, .movement_ratio = 1.2, .wheel_clicks = 9}; - -static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { - circular_scroll_t report = {0, 0, false}; - int16_t x, y; - uint16_t center = cirque_pinnacle_get_scale() / 2; - int8_t wheel_clicks; - float mag, ang, scalar_projection, scalar_rejection, parallel_movement, perpendicular_movement; - int32_t dot, det; - - if (touchData.zValue) { - // place origin at center of trackpad - x = (int16_t)(touchData.xValue - center); - y = (int16_t)(touchData.yValue - center); - - // check if first touch - if (!scroll.z) { - report.suppress_touch = false; - // check if touch falls within outer ring - mag = hypotf(x, y); - if (mag / center >= (100.0 - scroll.outer_ring_pct) / 100.0) { - scroll.state = SCROLL_DETECTING; - scroll.x = x; - scroll.y = y; - scroll.mag = mag; - // decide scroll axis: - // vertical if started from righ half - // horizontal if started from left half - // reverse for left hand? TODO? -# if defined(POINTING_DEVICE_ROTATION_90) - scroll.axis = y < 0; -# elif defined(POINTING_DEVICE_ROTATION_180) - scroll.axis = x > 0; -# elif defined(POINTING_DEVICE_ROTATION_270) - scroll.axis = y > 0; -# else - scroll.axis = x < 0; -# endif - } - } else if (scroll.state == SCROLL_DETECTING) { - report.suppress_touch = true; - // already detecting scroll, check movement from touchdown location - mag = hypotf(x - scroll.x, y - scroll.y); - if (mag >= scroll.movement_pct / 100.0 * center) { - // check ratio of movement towards center vs. along perimeter - // this distinguishes circular scroll from swipes that start from edge of trackpad - dot = (int32_t)scroll.x * (int32_t)x + (int32_t)scroll.y * (int32_t)y; - det = (int32_t)scroll.x * (int32_t)y - (int32_t)scroll.y * (int32_t)x; - scalar_projection = dot / scroll.mag; - scalar_rejection = det / scroll.mag; - parallel_movement = fabs(scroll.mag - fabs(scalar_projection)); - perpendicular_movement = fabs(scalar_rejection); - if (parallel_movement * scroll.movement_ratio > perpendicular_movement) { - // not a scroll, release coordinates - report.suppress_touch = false; - scroll.state = NOT_SCROLL; - } else { - // scroll detected - scroll.state = SCROLL_VALID; - } - } - } - if (scroll.state == SCROLL_VALID) { - report.suppress_touch = true; - dot = (int32_t)scroll.x * (int32_t)x + (int32_t)scroll.y * (int32_t)y; - det = (int32_t)scroll.x * (int32_t)y - (int32_t)scroll.y * (int32_t)x; - ang = atan2f(det, dot); - wheel_clicks = roundf(ang * (float)scroll.wheel_clicks / M_PI); - if (wheel_clicks >= 1 || wheel_clicks <= -1) { - if (scroll.axis == 0) { - report.v = -wheel_clicks; - } else { - report.h = wheel_clicks; - } - scroll.x = x; - scroll.y = y; - } - } - } - - scroll.z = touchData.zValue; - if (!scroll.z) scroll.state = SCROLL_UNINITIALIZED; - - return report; -} - -// extern this for now -void cirque_pinnacle_enable_circular_scroll(bool enable) { - features.circular_scroll_enable = enable; -} - -// To set a trackpad exclusively as scroll wheel: outer_ring_pct = 100, movement_pct = 0, movement_ratio = 0 -void cirque_pinnacle_configure_circular_scroll(float outer_ring_pct, float movement_pct, float movement_ratio, uint8_t wheel_clicks) { - scroll.outer_ring_pct = outer_ring_pct; - scroll.movement_pct = movement_pct; - scroll.movement_ratio = movement_ratio; - scroll.wheel_clicks = wheel_clicks; -} - -typedef struct { - mouse_xy_report_t dx; - mouse_xy_report_t dy; - bool valid; -} cursor_glide_t; - -typedef struct { - float trigger_pct; - float coef; - float v0; - int16_t x; - int16_t y; - uint16_t z; - uint16_t timer; - uint16_t interval; - uint16_t counter; - mouse_xy_report_t dx0; - mouse_xy_report_t dy0; -} cursor_glide_context_t; - -static cursor_glide_context_t glide = {.trigger_pct = 2}; - -static cursor_glide_t cursor_glide(void) { - cursor_glide_t report; - float p; - int16_t x, y; - - glide.counter++; - // calculate current position - p = glide.v0 * glide.counter - glide.coef * glide.counter * glide.counter / 2; - x = (int16_t)(p * glide.dx0 / glide.v0); - y = (int16_t)(p * glide.dy0 / glide.v0); - report.dx = (mouse_xy_report_t)(x - glide.x); - report.dy = (mouse_xy_report_t)(y - glide.y); - report.valid = true; - if (report.dx <= 1 && report.dx >= -1 && report.dy <= 1 && report.dy >= -1) { - glide.dx0 = 0; - glide.dy0 = 0; - } - glide.x = x; - glide.y = y; - glide.timer = timer_read(); - - return report; -} - -static cursor_glide_t cursor_glide_check(void) { - cursor_glide_t invalid_report = {0, 0, false}; - if (glide.z || (glide.dx0 == 0 && glide.dy0 == 0) || timer_elapsed(glide.timer) < glide.interval) { - return invalid_report; - } else { - return cursor_glide(); - } -} - -static cursor_glide_t cursor_glide_start(void) { - cursor_glide_t invalid_report = {0, 0, false}; - - glide.coef = 0.4; // good enough default - glide.interval = 10; // hardcode for 100sps - glide.timer = timer_read(); - glide.counter = 0; - glide.v0 = (glide.dx0 == 0 && glide.dy0 == 0) ? 0.0 : hypotf(glide.dx0, glide.dy0); // skip trigonometry if not needed - glide.x = 0; - glide.y = 0; - glide.z = 0; - - if (glide.v0 < glide.trigger_pct * cirque_pinnacle_get_scale() / 100) { - // not enough velocity to be worth gliding, abort - glide.dx0 = 0; - glide.dy0 = 0; - return invalid_report; - } - - return cursor_glide(); -} - -static void cursor_glide_update(mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z) { - glide.dx0 = dx; - glide.dy0 = dy; - glide.z = z; -} +static bool cursor_glide_enable; +static cursor_glide_context_t glide = {.coef = 0.4 /* good default friction coef */, .interval = 10 /* 100sps */}; // extern this for now void cirque_pinnacle_enable_cursor_glide(bool enable) { - features.cursor_glide_enable = enable; + cursor_glide_enable = enable; } -void cirque_pinnacle_configure_cursor_glide(float trigger_pct) { - glide.trigger_pct = trigger_pct; +void cirque_pinnacle_configure_cursor_glide(float trigger_inches) { + glide.trigger_px = roundf((float)trigger_inches * (float)CIRQUE_PINNACLE_DIAMETER_MM / 25.4f); } report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { pinnacle_data_t touchData = cirque_pinnacle_read_data(); - circular_scroll_t scroll_report; cursor_glide_t glide_report; mouse_xy_report_t report_x = 0, report_y = 0; static mouse_xy_report_t x = 0, y = 0; @@ -380,12 +119,12 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { # error Cirque Pinnacle with relative mode not implemented yet. # endif - if (features.cursor_glide_enable) { - glide_report = cursor_glide_check(); + if (cursor_glide_enable) { + glide_report = cursor_glide_check(&glide); } if (!touchData.valid) { - if (features.cursor_glide_enable && glide_report.valid) { + if (cursor_glide_enable && glide_report.valid) { report_x = glide_report.dx; report_y = glide_report.dy; goto mouse_report_update; @@ -402,29 +141,23 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { // Scale coordinates to arbitrary X, Y resolution cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale()); - if (features.circular_scroll_enable) { - scroll_report = circular_scroll(touchData); - mouse_report.v = scroll_report.v; - mouse_report.h = scroll_report.h; - if (scroll_report.suppress_touch) { - goto mouse_report_update; + if (!cirque_pinnacle_gestures(&mouse_report, touchData)) { + if (x && y && touchData.xValue && touchData.yValue) { + report_x = (mouse_xy_report_t)(touchData.xValue - x); + report_y = (mouse_xy_report_t)(touchData.yValue - y); } - } - if (x && y && touchData.xValue && touchData.yValue) { - report_x = (mouse_xy_report_t)(touchData.xValue - x); - report_y = (mouse_xy_report_t)(touchData.yValue - y); - } - x = touchData.xValue; - y = touchData.yValue; - - if (features.cursor_glide_enable) { - if (touchData.touchDown) { - cursor_glide_update(report_x, report_y, touchData.zValue); - } else if (!glide_report.valid) { - glide_report = cursor_glide_start(); - if (glide_report.valid) { - report_x = glide_report.dx; - report_y = glide_report.dy; + x = touchData.xValue; + y = touchData.yValue; + + if (cursor_glide_enable) { + if (touchData.touchDown) { + cursor_glide_update(&glide, report_x, report_y, touchData.zValue); + } else if (!glide_report.valid) { + glide_report = cursor_glide_start(&glide); + if (glide_report.valid) { + report_x = glide_report.dx; + report_y = glide_report.dy; + } } } } @@ -433,10 +166,6 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { mouse_report.x = report_x; mouse_report.y = report_y; - if (features.tap_enable) { - mouse_report = trackpad_tap(mouse_report, touchData); - } - return mouse_report; } diff --git a/quantum/pointing_device_gestures.c b/quantum/pointing_device_gestures.c new file mode 100644 index 000000000000..d0302cdef4c9 --- /dev/null +++ b/quantum/pointing_device_gestures.c @@ -0,0 +1,77 @@ +/* Copyright 2022 Daniel Kao + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "pointing_device_gestures.h" +#include "timer.h" +#include + +cursor_glide_t cursor_glide(cursor_glide_context_t* glide) { + cursor_glide_t report; + float p; + int16_t x, y; + + glide->counter++; + // calculate current position + p = glide->v0 * glide->counter - glide->coef * glide->counter * glide->counter / 2; + x = (int16_t)(p * glide->dx0 / glide->v0); + y = (int16_t)(p * glide->dy0 / glide->v0); + report.dx = (mouse_xy_report_t)(x - glide->x); + report.dy = (mouse_xy_report_t)(y - glide->y); + report.valid = true; + if (report.dx <= 1 && report.dx >= -1 && report.dy <= 1 && report.dy >= -1) { + glide->dx0 = 0; + glide->dy0 = 0; + } + glide->x = x; + glide->y = y; + glide->timer = timer_read(); + + return report; +} + +cursor_glide_t cursor_glide_check(cursor_glide_context_t* glide) { + cursor_glide_t invalid_report = {0, 0, false}; + if (glide->z || (glide->dx0 == 0 && glide->dy0 == 0) || timer_elapsed(glide->timer) < glide->interval) { + return invalid_report; + } else { + return cursor_glide(glide); + } +} + +cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide) { + cursor_glide_t invalid_report = {0, 0, false}; + + glide->timer = timer_read(); + glide->counter = 0; + glide->v0 = (glide->dx0 == 0 && glide->dy0 == 0) ? 0.0 : hypotf(glide->dx0, glide->dy0); // skip trigonometry if not needed + glide->x = 0; + glide->y = 0; + glide->z = 0; + + if (glide->v0 < glide->trigger_px) { + // not enough velocity to be worth gliding, abort + glide->dx0 = 0; + glide->dy0 = 0; + return invalid_report; + } + + return cursor_glide(glide); +} + +void cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z) { + glide->dx0 = dx; + glide->dy0 = dy; + glide->z = z; +} diff --git a/quantum/pointing_device_gestures.h b/quantum/pointing_device_gestures.h new file mode 100644 index 000000000000..8fdac047bde5 --- /dev/null +++ b/quantum/pointing_device_gestures.h @@ -0,0 +1,44 @@ +/* Copyright 2022 Daniel Kao + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include "report.h" + +typedef struct { + mouse_xy_report_t dx; + mouse_xy_report_t dy; + bool valid; +} cursor_glide_t; + +typedef struct { + uint16_t trigger_px; + float coef; + float v0; + int16_t x; + int16_t y; + uint16_t z; + uint16_t timer; + uint16_t interval; + uint16_t counter; + mouse_xy_report_t dx0; + mouse_xy_report_t dy0; +} cursor_glide_context_t; + +cursor_glide_t cursor_glide(cursor_glide_context_t* glide); +cursor_glide_t cursor_glide_check(cursor_glide_context_t* glide); +cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide); +void cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z); From de833e851180ef8216c1fa6907a1117169ea0027 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Thu, 30 Jun 2022 03:13:24 -0700 Subject: [PATCH 15/27] cirque pointing driver: Integer math for inch<->pixel conversion --- drivers/sensors/cirque_pinnacle.h | 4 ++++ quantum/pointing_device_drivers.c | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/sensors/cirque_pinnacle.h b/drivers/sensors/cirque_pinnacle.h index 553c6ce065ce..1c9bf06fd350 100644 --- a/drivers/sensors/cirque_pinnacle.h +++ b/drivers/sensors/cirque_pinnacle.h @@ -72,6 +72,10 @@ # endif #endif +#define DIVIDE_UNSIGNED_ROUND(numerator, denominator) (((numerator) + ((denominator) / 2)) / (denominator)) +#define CIRQUE_PINNACLE_INCH_TO_PX(inch) (DIVIDE_UNSIGNED_ROUND((inch) * (uint32_t)CIRQUE_PINNACLE_DIAMETER_MM * 10, 254)) +#define CIRQUE_PINNACLE_PX_TO_INCH(px) (DIVIDE_UNSIGNED_ROUND((px) * (uint32_t)254, CIRQUE_PINNACLE_DIAMETER_MM * 10)) + // Convenient way to store and access measurements typedef struct { bool valid; // true if valid data was read, false if no data was ready diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index bb6cffa16aae..a60578810e3e 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -105,8 +105,8 @@ void cirque_pinnacle_enable_cursor_glide(bool enable) { cursor_glide_enable = enable; } -void cirque_pinnacle_configure_cursor_glide(float trigger_inches) { - glide.trigger_px = roundf((float)trigger_inches * (float)CIRQUE_PINNACLE_DIAMETER_MM / 25.4f); +void cirque_pinnacle_configure_cursor_glide(float trigger_px) { + glide.trigger_px = trigger_px; } report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { @@ -170,10 +170,10 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { } uint16_t cirque_pinnacle_get_cpi(void) { - return (uint16_t)roundf((float)cirque_pinnacle_get_scale() * 25.4f / (float)CIRQUE_PINNACLE_DIAMETER_MM); + return CIRQUE_PINNACLE_PX_TO_INCH(cirque_pinnacle_get_scale()); } void cirque_pinnacle_set_cpi(uint16_t cpi) { - cirque_pinnacle_set_scale(roundf((float)cpi * (float)CIRQUE_PINNACLE_DIAMETER_MM / 25.4f)); + cirque_pinnacle_set_scale(CIRQUE_PINNACLE_INCH_TO_PX(cpi)); } // clang-format off From 969e612df2b92bd55a674b8e460ea4ff7c41a984 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sun, 3 Jul 2022 00:51:05 -0700 Subject: [PATCH 16/27] pointing_device_gestures: Adjust cursor glide types dx & dy can be int16_t, up the accumulated position elements to 32-bits. Change coef to Q8. --- quantum/pointing_device_drivers.c | 2 +- quantum/pointing_device_gestures.c | 8 ++++---- quantum/pointing_device_gestures.h | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index a60578810e3e..f7256910cfa9 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -98,7 +98,7 @@ const pointing_device_driver_t pointing_device_driver = { #elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) static bool cursor_glide_enable; -static cursor_glide_context_t glide = {.coef = 0.4 /* good default friction coef */, .interval = 10 /* 100sps */}; +static cursor_glide_context_t glide = {.coef = 102 /* good default friction coef */, .interval = 10 /* 100sps */}; // extern this for now void cirque_pinnacle_enable_cursor_glide(bool enable) { diff --git a/quantum/pointing_device_gestures.c b/quantum/pointing_device_gestures.c index d0302cdef4c9..23f0ea33332b 100644 --- a/quantum/pointing_device_gestures.c +++ b/quantum/pointing_device_gestures.c @@ -20,13 +20,13 @@ cursor_glide_t cursor_glide(cursor_glide_context_t* glide) { cursor_glide_t report; float p; - int16_t x, y; + int32_t x, y; glide->counter++; // calculate current position - p = glide->v0 * glide->counter - glide->coef * glide->counter * glide->counter / 2; - x = (int16_t)(p * glide->dx0 / glide->v0); - y = (int16_t)(p * glide->dy0 / glide->v0); + p = glide->v0 * glide->counter - (int32_t)glide->coef * glide->counter * glide->counter / 256 / 2; + x = (int32_t)(p * glide->dx0 / glide->v0); + y = (int32_t)(p * glide->dy0 / glide->v0); report.dx = (mouse_xy_report_t)(x - glide->x); report.dy = (mouse_xy_report_t)(y - glide->y); report.valid = true; diff --git a/quantum/pointing_device_gestures.h b/quantum/pointing_device_gestures.h index 8fdac047bde5..74b3a28f01c0 100644 --- a/quantum/pointing_device_gestures.h +++ b/quantum/pointing_device_gestures.h @@ -26,10 +26,10 @@ typedef struct { typedef struct { uint16_t trigger_px; - float coef; + uint16_t coef; float v0; - int16_t x; - int16_t y; + int32_t x; + int32_t y; uint16_t z; uint16_t timer; uint16_t interval; From dbe15aff19d14490d4cac27d9d5b6f5952c5f730 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sun, 3 Jul 2022 01:08:41 -0700 Subject: [PATCH 17/27] pointing_device_gestures: Q8 fixed point cursor glide Floating point is actually faster on AVR; cursor_glide_start() is around 33% faster with the call to hypotf() (~218us vs. ~327us), cursor_glide() is about equal between floating point and fixed point. Fixed point is much faster on a CPU with native 32-bit words and no FPU, such as ARM Cortex-M0. (~55us vs. ~18us for cursor_glide_start on an M0) Did not have a microcontroller board with Cortex-M4F to test performance with FPU. --- quantum/pointing_device_gestures.c | 33 ++++++++++++++++++++++++++---- quantum/pointing_device_gestures.h | 2 +- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/quantum/pointing_device_gestures.c b/quantum/pointing_device_gestures.c index 23f0ea33332b..c9cbe3bcc65d 100644 --- a/quantum/pointing_device_gestures.c +++ b/quantum/pointing_device_gestures.c @@ -19,12 +19,12 @@ cursor_glide_t cursor_glide(cursor_glide_context_t* glide) { cursor_glide_t report; - float p; + int32_t p; int32_t x, y; glide->counter++; // calculate current position - p = glide->v0 * glide->counter - (int32_t)glide->coef * glide->counter * glide->counter / 256 / 2; + p = glide->v0 * glide->counter - (int32_t)glide->coef * glide->counter * glide->counter / 2; x = (int32_t)(p * glide->dx0 / glide->v0); y = (int32_t)(p * glide->dy0 / glide->v0); report.dx = (mouse_xy_report_t)(x - glide->x); @@ -50,17 +50,42 @@ cursor_glide_t cursor_glide_check(cursor_glide_context_t* glide) { } } +static inline uint16_t sqrt32(uint32_t x) { + uint32_t l; + uint32_t m; + uint32_t h; + + if (x == 0) { + return 0; + } else if (x > (UINT16_MAX >> 2)) { + h = UINT16_MAX; + } else { + h = (1 << (((__builtin_clzl(1) - __builtin_clzl(x) + 1) + 1) >> 1)); + } + l = (1 << ((__builtin_clzl(1) - __builtin_clzl(x)) >> 1)); + + while (l != h - 1) { + m = (l + h) / 2; + if (m * m <= x) { + l = m; + } else { + h = m; + } + } + return l; +} + cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide) { cursor_glide_t invalid_report = {0, 0, false}; glide->timer = timer_read(); glide->counter = 0; - glide->v0 = (glide->dx0 == 0 && glide->dy0 == 0) ? 0.0 : hypotf(glide->dx0, glide->dy0); // skip trigonometry if not needed + glide->v0 = (glide->dx0 == 0 && glide->dy0 == 0) ? 0.0 : sqrt32(((int32_t)glide->dx0 * 256 * glide->dx0 * 256) + ((int32_t)glide->dy0 * 256 * glide->dy0 * 256)); // skip trigonometry if not needed glide->x = 0; glide->y = 0; glide->z = 0; - if (glide->v0 < glide->trigger_px) { + if (glide->v0 < ((uint32_t)glide->trigger_px << 8)) { // not enough velocity to be worth gliding, abort glide->dx0 = 0; glide->dy0 = 0; diff --git a/quantum/pointing_device_gestures.h b/quantum/pointing_device_gestures.h index 74b3a28f01c0..b80c79b57769 100644 --- a/quantum/pointing_device_gestures.h +++ b/quantum/pointing_device_gestures.h @@ -27,7 +27,7 @@ typedef struct { typedef struct { uint16_t trigger_px; uint16_t coef; - float v0; + int32_t v0; int32_t x; int32_t y; uint16_t z; From 516677ae9e1ab4dc49251ae12355acdffbc8d21f Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sun, 3 Jul 2022 17:00:53 -0700 Subject: [PATCH 18/27] cirque_pinnacle_gestures: Integer math circular scroll Scroll wheel clicks are a rather coarse output, make use of lib8tion to reduce code size and speed up the process. atan2_8() did not produce enough resolution for correct scroll movement, replaced with a clone of that function in higher precision. Adjusted configuration to be easier to understand, scroll validation criteria now expressed in pixels and angles. --- drivers/sensors/cirque_pinnacle_gestures.c | 82 ++++++++++++++-------- drivers/sensors/cirque_pinnacle_gestures.h | 16 ++--- 2 files changed, 60 insertions(+), 38 deletions(-) diff --git a/drivers/sensors/cirque_pinnacle_gestures.c b/drivers/sensors/cirque_pinnacle_gestures.c index c255bf09d740..3514ce04f48c 100644 --- a/drivers/sensors/cirque_pinnacle_gestures.c +++ b/drivers/sensors/cirque_pinnacle_gestures.c @@ -15,6 +15,7 @@ * along with this program. If not, see . */ #include +#include #include "cirque_pinnacle_gestures.h" #include "pointing_device.h" #include "timer.h" @@ -56,27 +57,50 @@ void cirque_pinnacle_enable_tap(bool enable) { features.tap_enable = enable; } -static circular_scroll_context_t scroll = {.outer_ring_pct = 33, .movement_pct = 6, .movement_ratio = 1.2, .wheel_clicks = 9}; +// To set a trackpad exclusively as scroll wheel: outer_ring_pct = 100, trigger_px = 0, trigger_ang = 0 +static circular_scroll_context_t scroll = {.outer_ring_pct = 33, .trigger_px = 16, .trigger_ang = 9102 /* 50 degrees */, .wheel_clicks = 18}; + +static inline uint16_t atan2_16(int32_t dy, int32_t dx) +{ + if (dy == 0) + { + if (dx >= 0) + return 0; + else + return 32768; + } + + int32_t abs_y = dy > 0 ? dy : -dy; + int16_t a; + + if (dx >= 0) + a = 8192 - (8192 * (dx - abs_y) / (dx + abs_y)); + else + a = 24576 - (8192 * (dx + abs_y) / (abs_y - dx)); + + if (dy < 0) + return -a; // negate if in quad III or IV + return a; +} static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { circular_scroll_t report = {0, 0, false}; - int16_t x, y; - uint16_t center = cirque_pinnacle_get_scale() / 2; - int8_t wheel_clicks; - float mag, ang, scalar_projection, scalar_rejection, parallel_movement, perpendicular_movement; - int32_t dot, det; + int8_t x, y, wheel_clicks; + uint8_t center = 256 / 2, mag; + int16_t ang, dot, det, opposite_side, adjacent_side; if (touchData.zValue) { - // place origin at center of trackpad - x = (int16_t)(touchData.xValue - center); - y = (int16_t)(touchData.yValue - center); + // place origin at center of trackpad, treat coordinates as vectors + // scale to fixed int8_t size, angles are independent of resolution + x = (int8_t)((int32_t)touchData.xValue * 256 / cirque_pinnacle_get_scale() - center); + y = (int8_t)((int32_t)touchData.yValue * 256 / cirque_pinnacle_get_scale() - center); // check if first touch if (!scroll.z) { report.suppress_touch = false; // check if touch falls within outer ring - mag = hypotf(x, y); - if (mag / center >= (100.0 - scroll.outer_ring_pct) / 100.0) { + mag = sqrt16(x * x + y * y); + if (mag * 100 / center >= 100 - scroll.outer_ring_pct) { scroll.state = SCROLL_DETECTING; scroll.x = x; scroll.y = y; @@ -98,17 +122,16 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { } else if (scroll.state == SCROLL_DETECTING) { report.suppress_touch = true; // already detecting scroll, check movement from touchdown location - mag = hypotf(x - scroll.x, y - scroll.y); - if (mag >= scroll.movement_pct / 100.0 * center) { - // check ratio of movement towards center vs. along perimeter - // this distinguishes circular scroll from swipes that start from edge of trackpad - dot = (int32_t)scroll.x * (int32_t)x + (int32_t)scroll.y * (int32_t)y; - det = (int32_t)scroll.x * (int32_t)y - (int32_t)scroll.y * (int32_t)x; - scalar_projection = dot / scroll.mag; - scalar_rejection = det / scroll.mag; - parallel_movement = fabs(scroll.mag - fabs(scalar_projection)); - perpendicular_movement = fabs(scalar_rejection); - if (parallel_movement * scroll.movement_ratio > perpendicular_movement) { + mag = sqrt16((x - scroll.x) * (x - scroll.x) + (y - scroll.y) * (y - scroll.y)); + if (mag >= scroll.trigger_px) { +#define ABS(a) (a > 0 ? a : -a) + // find angle of movement with 0 being movement towards center of circle + dot = scroll.x * x + scroll.y * y; + det = scroll.x * y - scroll.y * x; + opposite_side = ABS(det); // based on scalar rejection + adjacent_side = ABS(scroll.mag * scroll.mag - ABS(dot)); // based on scalar projection + ang = (int16_t)atan2_16(opposite_side, adjacent_side); + if (ang < scroll.trigger_ang) { // not a scroll, release coordinates report.suppress_touch = false; scroll.state = NOT_SCROLL; @@ -120,10 +143,10 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { } if (scroll.state == SCROLL_VALID) { report.suppress_touch = true; - dot = (int32_t)scroll.x * (int32_t)x + (int32_t)scroll.y * (int32_t)y; - det = (int32_t)scroll.x * (int32_t)y - (int32_t)scroll.y * (int32_t)x; - ang = atan2f(det, dot); - wheel_clicks = roundf(ang * (float)scroll.wheel_clicks / M_PI); + dot = scroll.x * x + scroll.y * y; + det = scroll.x * y - scroll.y * x; + ang = (int16_t)atan2_16(det, dot); + wheel_clicks = ((int32_t)ang * scroll.wheel_clicks) / 65536; if (wheel_clicks >= 1 || wheel_clicks <= -1) { if (scroll.axis == 0) { report.v = -wheel_clicks; @@ -147,11 +170,10 @@ void cirque_pinnacle_enable_circular_scroll(bool enable) { features.circular_scroll_enable = enable; } -// To set a trackpad exclusively as scroll wheel: outer_ring_pct = 100, movement_pct = 0, movement_ratio = 0 -void cirque_pinnacle_configure_circular_scroll(float outer_ring_pct, float movement_pct, float movement_ratio, uint8_t wheel_clicks) { +void cirque_pinnacle_configure_circular_scroll(uint8_t outer_ring_pct, uint8_t trigger_px, uint16_t trigger_ang, uint8_t wheel_clicks) { scroll.outer_ring_pct = outer_ring_pct; - scroll.movement_pct = movement_pct; - scroll.movement_ratio = movement_ratio; + scroll.trigger_px = trigger_px; + scroll.trigger_ang = trigger_ang; scroll.wheel_clicks = wheel_clicks; } diff --git a/drivers/sensors/cirque_pinnacle_gestures.h b/drivers/sensors/cirque_pinnacle_gestures.h index 9368aeeac47d..c217b4bb8d2f 100644 --- a/drivers/sensors/cirque_pinnacle_gestures.h +++ b/drivers/sensors/cirque_pinnacle_gestures.h @@ -42,17 +42,17 @@ typedef struct { } circular_scroll_t; typedef struct { - float mag; - int16_t x; - int16_t y; + uint8_t mag; + int8_t x; + int8_t y; uint16_t z; circular_scroll_status_t state; bool axis; // settings - float outer_ring_pct; // width of outer ring - float movement_pct; // amount of movement before triggering scroll validation - float movement_ratio; // ratio of movement along perimeter / movement towards center - uint8_t wheel_clicks; // how many clicks to report in a half circle + uint8_t outer_ring_pct; // width of outer ring, given as a percentage of the radius + uint8_t trigger_px; // amount of movement before triggering scroll validation, in pixels 0~127 + uint16_t trigger_ang; // angle required to validate scroll, in radians where pi = 32768 + uint8_t wheel_clicks; // how many clicks to report in a circle } circular_scroll_context_t; typedef struct { @@ -68,4 +68,4 @@ typedef struct { bool cirque_pinnacle_gestures(report_mouse_t* mouse_report, pinnacle_data_t touchData); void cirque_pinnacle_enable_tap(bool enable); void cirque_pinnacle_enable_circular_scroll(bool enable); -void cirque_pinnacle_configure_circular_scroll(float outer_ring_pct, float movement_pct, float movement_ratio, uint8_t wheel_clicks); +void cirque_pinnacle_configure_circular_scroll(uint8_t outer_ring_pct, uint8_t trigger_px, uint16_t trigger_ang, uint8_t wheel_clicks); From 7158ed60c4dbd3f2a5cac2176f263d4dbf79e606 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sun, 3 Jul 2022 17:53:06 -0700 Subject: [PATCH 19/27] pointing device gestures: Update comments --- drivers/sensors/cirque_pinnacle_gestures.c | 19 +++++++---------- drivers/sensors/cirque_pinnacle_gestures.h | 12 +++++++++++ quantum/pointing_device_gestures.c | 24 ++++++++++++++-------- quantum/pointing_device_gestures.h | 15 +++++++++----- 4 files changed, 45 insertions(+), 25 deletions(-) diff --git a/drivers/sensors/cirque_pinnacle_gestures.c b/drivers/sensors/cirque_pinnacle_gestures.c index 3514ce04f48c..b1042079df34 100644 --- a/drivers/sensors/cirque_pinnacle_gestures.c +++ b/drivers/sensors/cirque_pinnacle_gestures.c @@ -20,10 +20,6 @@ #include "pointing_device.h" #include "timer.h" -#ifdef POINTING_DEVICE_MOTION_PIN -# error POINTING_DEVICE_MOTION_PIN not supported when using inertial cursor. Need repeated calls to get_report() to generate glide events. -#endif - static cirque_pinnacle_features_t features = {.tap_enable = true}; static trackpad_tap_context_t tap; @@ -60,10 +56,8 @@ void cirque_pinnacle_enable_tap(bool enable) { // To set a trackpad exclusively as scroll wheel: outer_ring_pct = 100, trigger_px = 0, trigger_ang = 0 static circular_scroll_context_t scroll = {.outer_ring_pct = 33, .trigger_px = 16, .trigger_ang = 9102 /* 50 degrees */, .wheel_clicks = 18}; -static inline uint16_t atan2_16(int32_t dy, int32_t dx) -{ - if (dy == 0) - { +static inline uint16_t atan2_16(int32_t dy, int32_t dx) { + if (dy == 0) { if (dx >= 0) return 0; else @@ -78,8 +72,9 @@ static inline uint16_t atan2_16(int32_t dy, int32_t dx) else a = 24576 - (8192 * (dx + abs_y) / (abs_y - dx)); - if (dy < 0) - return -a; // negate if in quad III or IV + if (dy < 0) { + return -a; // negate if in quad III or IV + } return a; } @@ -172,8 +167,8 @@ void cirque_pinnacle_enable_circular_scroll(bool enable) { void cirque_pinnacle_configure_circular_scroll(uint8_t outer_ring_pct, uint8_t trigger_px, uint16_t trigger_ang, uint8_t wheel_clicks) { scroll.outer_ring_pct = outer_ring_pct; - scroll.trigger_px = trigger_px; - scroll.trigger_ang = trigger_ang; + scroll.trigger_px = trigger_px; + scroll.trigger_ang = trigger_ang; scroll.wheel_clicks = wheel_clicks; } diff --git a/drivers/sensors/cirque_pinnacle_gestures.h b/drivers/sensors/cirque_pinnacle_gestures.h index c217b4bb8d2f..e5ca6c00e926 100644 --- a/drivers/sensors/cirque_pinnacle_gestures.h +++ b/drivers/sensors/cirque_pinnacle_gestures.h @@ -65,7 +65,19 @@ typedef struct { bool circular_scroll_enable; } cirque_pinnacle_features_t; +// Process through available gestures bool cirque_pinnacle_gestures(report_mouse_t* mouse_report, pinnacle_data_t touchData); + +// Enable/disable tap gesture void cirque_pinnacle_enable_tap(bool enable); + +// Enable/disable circular scroll gesture void cirque_pinnacle_enable_circular_scroll(bool enable); + +// Configure circular scroll gesture. +// Trackpad can be configured to act exclusively as a scroll wheel with outer_ring_pct = 0, trigger_px = 0, trigger_ang = 0. +// @param outer_ring_pct Width of outer ring from which to begin scroll validation, given as a percentage of the radius. +// @param trigger_px Amount of movement before triggering scroll validation. Expressed in pixels, trackpad coordinates are scaled to radius of 128 pixels for circular scroll. +// @param triger_ang Angle required to validate scroll, angle smaller than this will invalidate scroll. In radians where pi = 32768, 0 means movement towards center of trackpad, 16384 means movement perpendicular to center. +// @param wheel_clicks Number of scroll wheel clicks to report in a full rotation. void cirque_pinnacle_configure_circular_scroll(uint8_t outer_ring_pct, uint8_t trigger_px, uint16_t trigger_ang, uint8_t wheel_clicks); diff --git a/quantum/pointing_device_gestures.c b/quantum/pointing_device_gestures.c index c9cbe3bcc65d..7f0eecced929 100644 --- a/quantum/pointing_device_gestures.c +++ b/quantum/pointing_device_gestures.c @@ -17,20 +17,26 @@ #include "timer.h" #include -cursor_glide_t cursor_glide(cursor_glide_context_t* glide) { +#ifdef POINTING_DEVICE_MOTION_PIN +# error POINTING_DEVICE_MOTION_PIN not supported when using inertial cursor. Need repeated calls to get_report() to generate glide events. +#endif + +static cursor_glide_t cursor_glide(cursor_glide_context_t* glide) { cursor_glide_t report; int32_t p; int32_t x, y; glide->counter++; - // calculate current position - p = glide->v0 * glide->counter - (int32_t)glide->coef * glide->counter * glide->counter / 2; + // calculate current 1D position + p = glide->v0 * glide->counter - (int32_t)glide->coef * glide->counter * glide->counter / 2; + // translate to x & y axes x = (int32_t)(p * glide->dx0 / glide->v0); y = (int32_t)(p * glide->dy0 / glide->v0); report.dx = (mouse_xy_report_t)(x - glide->x); report.dy = (mouse_xy_report_t)(y - glide->y); report.valid = true; if (report.dx <= 1 && report.dx >= -1 && report.dy <= 1 && report.dy >= -1) { + // stop gliding once speed is low enough glide->dx0 = 0; glide->dy0 = 0; } @@ -51,19 +57,21 @@ cursor_glide_t cursor_glide_check(cursor_glide_context_t* glide) { } static inline uint16_t sqrt32(uint32_t x) { - uint32_t l; - uint32_t m; - uint32_t h; + uint32_t l, m, h; if (x == 0) { return 0; } else if (x > (UINT16_MAX >> 2)) { + // safe upper bound to avoid integer overflow with m * m h = UINT16_MAX; } else { + // upper bound based on closest log2 h = (1 << (((__builtin_clzl(1) - __builtin_clzl(x) + 1) + 1) >> 1)); } + // lower bound based on closest log2 l = (1 << ((__builtin_clzl(1) - __builtin_clzl(x)) >> 1)); + // binary search to find integer square root while (l != h - 1) { m = (l + h) / 2; if (m * m <= x) { @@ -80,12 +88,12 @@ cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide) { glide->timer = timer_read(); glide->counter = 0; - glide->v0 = (glide->dx0 == 0 && glide->dy0 == 0) ? 0.0 : sqrt32(((int32_t)glide->dx0 * 256 * glide->dx0 * 256) + ((int32_t)glide->dy0 * 256 * glide->dy0 * 256)); // skip trigonometry if not needed + glide->v0 = (glide->dx0 == 0 && glide->dy0 == 0) ? 0.0 : sqrt32(((int32_t)glide->dx0 * 256 * glide->dx0 * 256) + ((int32_t)glide->dy0 * 256 * glide->dy0 * 256)); // skip trigonometry if not needed, calculate distance in Q8 glide->x = 0; glide->y = 0; glide->z = 0; - if (glide->v0 < ((uint32_t)glide->trigger_px << 8)) { + if (glide->v0 < ((uint32_t)glide->trigger_px * 256)) { // Q8 comparison // not enough velocity to be worth gliding, abort glide->dx0 = 0; glide->dy0 = 0; diff --git a/quantum/pointing_device_gestures.h b/quantum/pointing_device_gestures.h index b80c79b57769..131b3d70cdea 100644 --- a/quantum/pointing_device_gestures.h +++ b/quantum/pointing_device_gestures.h @@ -25,20 +25,25 @@ typedef struct { } cursor_glide_t; typedef struct { - uint16_t trigger_px; - uint16_t coef; int32_t v0; int32_t x; int32_t y; uint16_t z; uint16_t timer; - uint16_t interval; uint16_t counter; mouse_xy_report_t dx0; mouse_xy_report_t dy0; + // settings + uint16_t trigger_px; // pixels of movement needed to trigger cursor glide + uint16_t coef; // coefficient of friction + uint16_t interval; // glide report interval, in milliseconds } cursor_glide_context_t; -cursor_glide_t cursor_glide(cursor_glide_context_t* glide); +// Check glide report conditions, calculates glide coordinates cursor_glide_t cursor_glide_check(cursor_glide_context_t* glide); + +// Start glide reporting, gives first set of glide coordinates cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide); -void cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z); + +// Update glide engine on the latest cursor movement, cursor glide is based on the final movement +void cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z); From f08d5e7c09879889797ebf0eb2de562dac111737 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sun, 3 Jul 2022 18:27:55 -0700 Subject: [PATCH 20/27] pointing device gestures: div0 checks --- drivers/sensors/cirque_pinnacle_gestures.c | 10 ++++++++-- quantum/pointing_device_gestures.c | 10 ++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/sensors/cirque_pinnacle_gestures.c b/drivers/sensors/cirque_pinnacle_gestures.c index b1042079df34..7182fa2b97af 100644 --- a/drivers/sensors/cirque_pinnacle_gestures.c +++ b/drivers/sensors/cirque_pinnacle_gestures.c @@ -83,12 +83,18 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { int8_t x, y, wheel_clicks; uint8_t center = 256 / 2, mag; int16_t ang, dot, det, opposite_side, adjacent_side; + uint16_t scale = cirque_pinnacle_get_scale(); if (touchData.zValue) { // place origin at center of trackpad, treat coordinates as vectors // scale to fixed int8_t size, angles are independent of resolution - x = (int8_t)((int32_t)touchData.xValue * 256 / cirque_pinnacle_get_scale() - center); - y = (int8_t)((int32_t)touchData.yValue * 256 / cirque_pinnacle_get_scale() - center); + if (scale) { + x = (int8_t)((int32_t)touchData.xValue * 256 / scale - center); + y = (int8_t)((int32_t)touchData.yValue * 256 / scale - center); + } else { + x = 0; + y = 0; + } // check if first touch if (!scroll.z) { diff --git a/quantum/pointing_device_gestures.c b/quantum/pointing_device_gestures.c index 7f0eecced929..78ed63b57523 100644 --- a/quantum/pointing_device_gestures.c +++ b/quantum/pointing_device_gestures.c @@ -26,6 +26,15 @@ static cursor_glide_t cursor_glide(cursor_glide_context_t* glide) { int32_t p; int32_t x, y; + if (glide->v0 == 0) { + report.dx = 0; + report.dy = 0; + report.valid = false; + glide->dx0 = 0; + glide->dy0 = 0; + goto exit; + } + glide->counter++; // calculate current 1D position p = glide->v0 * glide->counter - (int32_t)glide->coef * glide->counter * glide->counter / 2; @@ -44,6 +53,7 @@ static cursor_glide_t cursor_glide(cursor_glide_context_t* glide) { glide->y = y; glide->timer = timer_read(); +exit: return report; } From 3ff32b0d708800158a57900867c5370bca761a20 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Mon, 4 Jul 2022 16:25:42 -0700 Subject: [PATCH 21/27] pointing device gestures: Extract configurations to explicit struct Make configurations explicit. Also make the cursor glide cleanup more complete by memsetting to 0. --- drivers/sensors/cirque_pinnacle_gestures.c | 18 +++--- drivers/sensors/cirque_pinnacle_gestures.h | 15 +++-- quantum/pointing_device_drivers.c | 4 +- quantum/pointing_device_gestures.c | 74 ++++++++++++---------- quantum/pointing_device_gestures.h | 15 +++-- 5 files changed, 72 insertions(+), 54 deletions(-) diff --git a/drivers/sensors/cirque_pinnacle_gestures.c b/drivers/sensors/cirque_pinnacle_gestures.c index 7182fa2b97af..ea2c32e6f073 100644 --- a/drivers/sensors/cirque_pinnacle_gestures.c +++ b/drivers/sensors/cirque_pinnacle_gestures.c @@ -54,7 +54,7 @@ void cirque_pinnacle_enable_tap(bool enable) { } // To set a trackpad exclusively as scroll wheel: outer_ring_pct = 100, trigger_px = 0, trigger_ang = 0 -static circular_scroll_context_t scroll = {.outer_ring_pct = 33, .trigger_px = 16, .trigger_ang = 9102 /* 50 degrees */, .wheel_clicks = 18}; +static circular_scroll_context_t scroll = {.config = {.outer_ring_pct = 33, .trigger_px = 16, .trigger_ang = 9102 /* 50 degrees */, .wheel_clicks = 18}}; static inline uint16_t atan2_16(int32_t dy, int32_t dx) { if (dy == 0) { @@ -101,7 +101,7 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { report.suppress_touch = false; // check if touch falls within outer ring mag = sqrt16(x * x + y * y); - if (mag * 100 / center >= 100 - scroll.outer_ring_pct) { + if (mag * 100 / center >= 100 - scroll.config.outer_ring_pct) { scroll.state = SCROLL_DETECTING; scroll.x = x; scroll.y = y; @@ -124,7 +124,7 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { report.suppress_touch = true; // already detecting scroll, check movement from touchdown location mag = sqrt16((x - scroll.x) * (x - scroll.x) + (y - scroll.y) * (y - scroll.y)); - if (mag >= scroll.trigger_px) { + if (mag >= scroll.config.trigger_px) { #define ABS(a) (a > 0 ? a : -a) // find angle of movement with 0 being movement towards center of circle dot = scroll.x * x + scroll.y * y; @@ -132,7 +132,7 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { opposite_side = ABS(det); // based on scalar rejection adjacent_side = ABS(scroll.mag * scroll.mag - ABS(dot)); // based on scalar projection ang = (int16_t)atan2_16(opposite_side, adjacent_side); - if (ang < scroll.trigger_ang) { + if (ang < scroll.config.trigger_ang) { // not a scroll, release coordinates report.suppress_touch = false; scroll.state = NOT_SCROLL; @@ -147,7 +147,7 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { dot = scroll.x * x + scroll.y * y; det = scroll.x * y - scroll.y * x; ang = (int16_t)atan2_16(det, dot); - wheel_clicks = ((int32_t)ang * scroll.wheel_clicks) / 65536; + wheel_clicks = ((int32_t)ang * scroll.config.wheel_clicks) / 65536; if (wheel_clicks >= 1 || wheel_clicks <= -1) { if (scroll.axis == 0) { report.v = -wheel_clicks; @@ -172,10 +172,10 @@ void cirque_pinnacle_enable_circular_scroll(bool enable) { } void cirque_pinnacle_configure_circular_scroll(uint8_t outer_ring_pct, uint8_t trigger_px, uint16_t trigger_ang, uint8_t wheel_clicks) { - scroll.outer_ring_pct = outer_ring_pct; - scroll.trigger_px = trigger_px; - scroll.trigger_ang = trigger_ang; - scroll.wheel_clicks = wheel_clicks; + scroll.config.outer_ring_pct = outer_ring_pct; + scroll.config.trigger_px = trigger_px; + scroll.config.trigger_ang = trigger_ang; + scroll.config.wheel_clicks = wheel_clicks; } bool cirque_pinnacle_gestures(report_mouse_t* mouse_report, pinnacle_data_t touchData) { diff --git a/drivers/sensors/cirque_pinnacle_gestures.h b/drivers/sensors/cirque_pinnacle_gestures.h index e5ca6c00e926..986908d55be6 100644 --- a/drivers/sensors/cirque_pinnacle_gestures.h +++ b/drivers/sensors/cirque_pinnacle_gestures.h @@ -42,17 +42,20 @@ typedef struct { } circular_scroll_t; typedef struct { + uint8_t outer_ring_pct; // width of outer ring, given as a percentage of the radius + uint8_t trigger_px; // amount of movement before triggering scroll validation, in pixels 0~127 + uint16_t trigger_ang; // angle required to validate scroll, in radians where pi = 32768 + uint8_t wheel_clicks; // how many clicks to report in a circle +} circular_scroll_config_t; + +typedef struct { + circular_scroll_config_t config; + circular_scroll_status_t state; uint8_t mag; int8_t x; int8_t y; uint16_t z; - circular_scroll_status_t state; bool axis; - // settings - uint8_t outer_ring_pct; // width of outer ring, given as a percentage of the radius - uint8_t trigger_px; // amount of movement before triggering scroll validation, in pixels 0~127 - uint16_t trigger_ang; // angle required to validate scroll, in radians where pi = 32768 - uint8_t wheel_clicks; // how many clicks to report in a circle } circular_scroll_context_t; typedef struct { diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index f7256910cfa9..e673d33bfe5d 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -98,7 +98,7 @@ const pointing_device_driver_t pointing_device_driver = { #elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) static bool cursor_glide_enable; -static cursor_glide_context_t glide = {.coef = 102 /* good default friction coef */, .interval = 10 /* 100sps */}; +static cursor_glide_context_t glide = {.config = {.coef = 102 /* good default friction coef */, .interval = 10 /* 100sps */}}; // extern this for now void cirque_pinnacle_enable_cursor_glide(bool enable) { @@ -106,7 +106,7 @@ void cirque_pinnacle_enable_cursor_glide(bool enable) { } void cirque_pinnacle_configure_cursor_glide(float trigger_px) { - glide.trigger_px = trigger_px; + glide.config.trigger_px = trigger_px; } report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { diff --git a/quantum/pointing_device_gestures.c b/quantum/pointing_device_gestures.c index 78ed63b57523..0c1c466b1d24 100644 --- a/quantum/pointing_device_gestures.c +++ b/quantum/pointing_device_gestures.c @@ -13,53 +13,59 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include #include "pointing_device_gestures.h" #include "timer.h" -#include #ifdef POINTING_DEVICE_MOTION_PIN # error POINTING_DEVICE_MOTION_PIN not supported when using inertial cursor. Need repeated calls to get_report() to generate glide events. #endif +static void cursor_glide_stop(cursor_glide_context_t* glide) { + memset(&glide->status, 0, sizeof(glide->status)); +} + static cursor_glide_t cursor_glide(cursor_glide_context_t* glide) { - cursor_glide_t report; - int32_t p; - int32_t x, y; + cursor_glide_status_t* status = &glide->status; + cursor_glide_t report; + int32_t p; + int32_t x, y; - if (glide->v0 == 0) { + if (status->v0 == 0) { report.dx = 0; report.dy = 0; report.valid = false; - glide->dx0 = 0; - glide->dy0 = 0; + cursor_glide_stop(glide); goto exit; } - glide->counter++; + status->counter++; // calculate current 1D position - p = glide->v0 * glide->counter - (int32_t)glide->coef * glide->counter * glide->counter / 2; + p = status->v0 * status->counter - (int32_t)glide->config.coef * status->counter * status->counter / 2; // translate to x & y axes - x = (int32_t)(p * glide->dx0 / glide->v0); - y = (int32_t)(p * glide->dy0 / glide->v0); - report.dx = (mouse_xy_report_t)(x - glide->x); - report.dy = (mouse_xy_report_t)(y - glide->y); + x = (int32_t)(p * status->dx0 / status->v0); + y = (int32_t)(p * status->dy0 / status->v0); + report.dx = (mouse_xy_report_t)(x - status->x); + report.dy = (mouse_xy_report_t)(y - status->y); report.valid = true; if (report.dx <= 1 && report.dx >= -1 && report.dy <= 1 && report.dy >= -1) { // stop gliding once speed is low enough - glide->dx0 = 0; - glide->dy0 = 0; + cursor_glide_stop(glide); + goto exit; } - glide->x = x; - glide->y = y; - glide->timer = timer_read(); + status->x = x; + status->y = y; + status->timer = timer_read(); exit: return report; } cursor_glide_t cursor_glide_check(cursor_glide_context_t* glide) { - cursor_glide_t invalid_report = {0, 0, false}; - if (glide->z || (glide->dx0 == 0 && glide->dy0 == 0) || timer_elapsed(glide->timer) < glide->interval) { + cursor_glide_t invalid_report = {0, 0, false}; + cursor_glide_status_t* status = &glide->status; + + if (status->z || (status->dx0 == 0 && status->dy0 == 0) || timer_elapsed(status->timer) < glide->config.interval) { return invalid_report; } else { return cursor_glide(glide); @@ -94,19 +100,19 @@ static inline uint16_t sqrt32(uint32_t x) { } cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide) { - cursor_glide_t invalid_report = {0, 0, false}; + cursor_glide_t invalid_report = {0, 0, false}; + cursor_glide_status_t* status = &glide->status; - glide->timer = timer_read(); - glide->counter = 0; - glide->v0 = (glide->dx0 == 0 && glide->dy0 == 0) ? 0.0 : sqrt32(((int32_t)glide->dx0 * 256 * glide->dx0 * 256) + ((int32_t)glide->dy0 * 256 * glide->dy0 * 256)); // skip trigonometry if not needed, calculate distance in Q8 - glide->x = 0; - glide->y = 0; - glide->z = 0; + status->timer = timer_read(); + status->counter = 0; + status->v0 = (status->dx0 == 0 && status->dy0 == 0) ? 0.0 : sqrt32(((int32_t)status->dx0 * 256 * status->dx0 * 256) + ((int32_t)status->dy0 * 256 * status->dy0 * 256)); // skip trigonometry if not needed, calculate distance in Q8 + status->x = 0; + status->y = 0; + status->z = 0; - if (glide->v0 < ((uint32_t)glide->trigger_px * 256)) { // Q8 comparison + if (status->v0 < ((uint32_t)glide->config.trigger_px * 256)) { // Q8 comparison // not enough velocity to be worth gliding, abort - glide->dx0 = 0; - glide->dy0 = 0; + cursor_glide_stop(glide); return invalid_report; } @@ -114,7 +120,9 @@ cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide) { } void cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z) { - glide->dx0 = dx; - glide->dy0 = dy; - glide->z = z; + cursor_glide_status_t* status = &glide->status; + + status->dx0 = dx; + status->dy0 = dy; + status->z = z; } diff --git a/quantum/pointing_device_gestures.h b/quantum/pointing_device_gestures.h index 131b3d70cdea..b68a44ca6391 100644 --- a/quantum/pointing_device_gestures.h +++ b/quantum/pointing_device_gestures.h @@ -24,6 +24,12 @@ typedef struct { bool valid; } cursor_glide_t; +typedef struct { + uint16_t trigger_px; // pixels of movement needed to trigger cursor glide + uint16_t coef; // coefficient of friction + uint16_t interval; // glide report interval, in milliseconds +} cursor_glide_config_t; + typedef struct { int32_t v0; int32_t x; @@ -33,10 +39,11 @@ typedef struct { uint16_t counter; mouse_xy_report_t dx0; mouse_xy_report_t dy0; - // settings - uint16_t trigger_px; // pixels of movement needed to trigger cursor glide - uint16_t coef; // coefficient of friction - uint16_t interval; // glide report interval, in milliseconds +} cursor_glide_status_t; + +typedef struct { + cursor_glide_config_t config; + cursor_glide_status_t status; } cursor_glide_context_t; // Check glide report conditions, calculates glide coordinates From 60da7780cc3b86162ff0a7df2765e8bd884ebc95 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Mon, 4 Jul 2022 22:23:47 -0700 Subject: [PATCH 22/27] circular scroll: Add left-handed mode --- drivers/sensors/cirque_pinnacle_gestures.c | 19 ++++++++++++++----- drivers/sensors/cirque_pinnacle_gestures.h | 4 +++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/sensors/cirque_pinnacle_gestures.c b/drivers/sensors/cirque_pinnacle_gestures.c index ea2c32e6f073..d0a0f099da4f 100644 --- a/drivers/sensors/cirque_pinnacle_gestures.c +++ b/drivers/sensors/cirque_pinnacle_gestures.c @@ -109,7 +109,7 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { // decide scroll axis: // vertical if started from righ half // horizontal if started from left half - // reverse for left hand? TODO? + // flipped for left-handed #if defined(POINTING_DEVICE_ROTATION_90) scroll.axis = y < 0; #elif defined(POINTING_DEVICE_ROTATION_180) @@ -149,10 +149,18 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { ang = (int16_t)atan2_16(det, dot); wheel_clicks = ((int32_t)ang * scroll.config.wheel_clicks) / 65536; if (wheel_clicks >= 1 || wheel_clicks <= -1) { - if (scroll.axis == 0) { - report.v = -wheel_clicks; + if (scroll.config.left_handed) { + if (scroll.axis == 0) { + report.h = -wheel_clicks; + } else { + report.v = wheel_clicks; + } } else { - report.h = wheel_clicks; + if (scroll.axis == 0) { + report.v = -wheel_clicks; + } else { + report.h = wheel_clicks; + } } scroll.x = x; scroll.y = y; @@ -171,11 +179,12 @@ void cirque_pinnacle_enable_circular_scroll(bool enable) { features.circular_scroll_enable = enable; } -void cirque_pinnacle_configure_circular_scroll(uint8_t outer_ring_pct, uint8_t trigger_px, uint16_t trigger_ang, uint8_t wheel_clicks) { +void cirque_pinnacle_configure_circular_scroll(uint8_t outer_ring_pct, uint8_t trigger_px, uint16_t trigger_ang, uint8_t wheel_clicks, bool left_handed) { scroll.config.outer_ring_pct = outer_ring_pct; scroll.config.trigger_px = trigger_px; scroll.config.trigger_ang = trigger_ang; scroll.config.wheel_clicks = wheel_clicks; + scroll.config.left_handed = left_handed; } bool cirque_pinnacle_gestures(report_mouse_t* mouse_report, pinnacle_data_t touchData) { diff --git a/drivers/sensors/cirque_pinnacle_gestures.h b/drivers/sensors/cirque_pinnacle_gestures.h index 986908d55be6..ad1ee39fbc6c 100644 --- a/drivers/sensors/cirque_pinnacle_gestures.h +++ b/drivers/sensors/cirque_pinnacle_gestures.h @@ -46,6 +46,7 @@ typedef struct { uint8_t trigger_px; // amount of movement before triggering scroll validation, in pixels 0~127 uint16_t trigger_ang; // angle required to validate scroll, in radians where pi = 32768 uint8_t wheel_clicks; // how many clicks to report in a circle + bool left_handed; // whether scrolling should be flipped for left handed use } circular_scroll_config_t; typedef struct { @@ -83,4 +84,5 @@ void cirque_pinnacle_enable_circular_scroll(bool enable); // @param trigger_px Amount of movement before triggering scroll validation. Expressed in pixels, trackpad coordinates are scaled to radius of 128 pixels for circular scroll. // @param triger_ang Angle required to validate scroll, angle smaller than this will invalidate scroll. In radians where pi = 32768, 0 means movement towards center of trackpad, 16384 means movement perpendicular to center. // @param wheel_clicks Number of scroll wheel clicks to report in a full rotation. -void cirque_pinnacle_configure_circular_scroll(uint8_t outer_ring_pct, uint8_t trigger_px, uint16_t trigger_ang, uint8_t wheel_clicks); +// @param left_handed Whether scrolling should be flipped for left-handed use. +void cirque_pinnacle_configure_circular_scroll(uint8_t outer_ring_pct, uint8_t trigger_px, uint16_t trigger_ang, uint8_t wheel_clicks, bool left_handed); From 7bf6c68a8d82833f4b0dc7b17806beab598f10b7 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sat, 9 Jul 2022 15:38:30 -0700 Subject: [PATCH 23/27] pointing device gestures: Conditional compilation Make all gestures opt-in at compile time. This includes tap which was previously enabled by default. Also quashed a -Werror=maybe-uninitialized in cirque_pinnacle_get_report() --- docs/feature_pointing_device.md | 33 +++++++----- drivers/sensors/cirque_pinnacle_gestures.c | 36 ++++++++----- drivers/sensors/cirque_pinnacle_gestures.h | 61 ++++++++++++++-------- quantum/pointing_device_drivers.c | 29 +++++++--- quantum/pointing_device_gestures.c | 8 +-- quantum/pointing_device_gestures.h | 2 + 6 files changed, 111 insertions(+), 58 deletions(-) diff --git a/docs/feature_pointing_device.md b/docs/feature_pointing_device.md index b168a291574a..70129dfc9aca 100644 --- a/docs/feature_pointing_device.md +++ b/docs/feature_pointing_device.md @@ -89,17 +89,15 @@ POINTING_DEVICE_DRIVER = cirque_pinnacle_spi This supports the Cirque Pinnacle 1CA027 Touch Controller, which is used in the TM040040, TM035035 and the TM023023 trackpads. These are I2C or SPI compatible, and both configurations are supported. -| Setting | Description | Default | -|-------------------------------- |-----------------------------------------------------------------------|--------------------- | -|`CIRQUE_PINNACLE_X_LOWER` | (Optional) The minimum reachable X value on the sensor. | `127` | -|`CIRQUE_PINNACLE_X_UPPER` | (Optional) The maximum reachable X value on the sensor. | `1919` | -|`CIRQUE_PINNACLE_Y_LOWER` | (Optional) The minimum reachable Y value on the sensor. | `63` | -|`CIRQUE_PINNACLE_Y_UPPER` | (Optional) The maximum reachable Y value on the sensor. | `1471` | -|`CIRQUE_PINNACLE_DIAMETER_MM` | (Optional) Diameter of the trackpad sensor in millimeters. | `40` | -|`CIRQUE_PINNACLE_ATTENUATION` | (Optional) Sets the attenuation of the sensor data. | `ADC_ATTENUATE_4X` | -|`CIRQUE_PINNACLE_CURVED_OVERLAY` | (Optional) Applies settings tuned for curved overlay. | _not defined_ | -|`CIRQUE_PINNACLE_TAPPING_TERM` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` | -|`CIRQUE_PINNACLE_TOUCH_DEBOUNCE` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` | +| Setting | Description | Default | +|-------------------------------- |------------------------------------------------------------|--------------------| +|`CIRQUE_PINNACLE_X_LOWER` | (Optional) The minimum reachable X value on the sensor. | `127` | +|`CIRQUE_PINNACLE_X_UPPER` | (Optional) The maximum reachable X value on the sensor. | `1919` | +|`CIRQUE_PINNACLE_Y_LOWER` | (Optional) The minimum reachable Y value on the sensor. | `63` | +|`CIRQUE_PINNACLE_Y_UPPER` | (Optional) The maximum reachable Y value on the sensor. | `1471` | +|`CIRQUE_PINNACLE_DIAMETER_MM` | (Optional) Diameter of the trackpad sensor in millimeters. | `40` | +|`CIRQUE_PINNACLE_ATTENUATION` | (Optional) Sets the attenuation of the sensor data. | `ADC_ATTENUATE_4X` | +|`CIRQUE_PINNACLE_CURVED_OVERLAY` | (Optional) Applies settings tuned for curved overlay. | _not defined_ | **`CIRQUE_PINNACLE_ATTENUATION`** is a measure of how much data is suppressed in regards to sensitivity. The higher the attenuation, the less sensitive the touchpad will be. @@ -122,10 +120,21 @@ Default attenuation is set to 4X, although if you are using a thicker overlay (s |`CIRQUE_PINNACLE_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ | |`CIRQUE_PINNACLE_SPI_CS_PIN` | (Required) Sets the Cable Select pin connected to the sensor. | _not defined_ | -Default Scaling/CPI is 1024. +Default Scaling is 1024. Actual CPI depends on trackpad diameter. Also see the `POINTING_DEVICE_TASK_THROTTLE_MS`, which defaults to 10ms when using Cirque Pinnacle, which matches the internal update rate of the position registers (in standard configuration). Advanced configuration for pen/stylus usage might require lower values. +#### Cirque Trackpad gestures + +| Gesture Setting | Description | Default | +|-----------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------| +|`POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE` | (Optional) Enable inertial cursor. Cursor continues moving after a flick gesture and slows down by kinetic friction | _not defined_ | +|`CIRQUE_PINNACLE_CIRCULAR_SCROLL_ENABLE` | (Optional) Enable circular scroll. Touch originating in outer ring can trigger scroll by moving along the perimeter. Near side triggers vertical scroll and far side triggers horizontal scroll. | _not defined_ | +|`CIRQUE_PINNACLE_TAP_ENABLE` | (Optional) Enable tap to click. This currently only works on the master side. | _not defined_ | +|`CIRQUE_PINNACLE_TAPPING_TERM` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` | +|`CIRQUE_PINNACLE_TOUCH_DEBOUNCE` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` | + +**`POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE`** is not specific to Cirque trackpad; any pointing device with a lift/contact status can integrate this gesture into its driver. e.g. PMW3360 can use Lift_Stat from Motion register. Note that `POINTING_DEVICE_MOTION_PIN` cannot be used with this feature; continuous polling of `pointing_device_get_report()` is needed to generate glide reports. ### Pimoroni Trackball diff --git a/drivers/sensors/cirque_pinnacle_gestures.c b/drivers/sensors/cirque_pinnacle_gestures.c index d0a0f099da4f..5e501ac6adda 100644 --- a/drivers/sensors/cirque_pinnacle_gestures.c +++ b/drivers/sensors/cirque_pinnacle_gestures.c @@ -20,8 +20,12 @@ #include "pointing_device.h" #include "timer.h" -static cirque_pinnacle_features_t features = {.tap_enable = true}; -static trackpad_tap_context_t tap; +#if defined(CIRQUE_PINNACLE_TAP_ENABLE) || defined(CIRQUE_PINNACLE_CIRCULAR_SCROLL_ENABLE) +static cirque_pinnacle_features_t features = {.tap_enable = true, .circular_scroll_enable = true}; +#endif + +#ifdef CIRQUE_PINNACLE_TAP_ENABLE +static trackpad_tap_context_t tap; static report_mouse_t trackpad_tap(report_mouse_t mouse_report, pinnacle_data_t touchData) { if (touchData.touchDown != tap.touchDown) { @@ -31,9 +35,9 @@ static report_mouse_t trackpad_tap(report_mouse_t mouse_report, pinnacle_data_t mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1); pointing_device_set_report(mouse_report); pointing_device_send(); -#if TAP_CODE_DELAY > 0 +# if TAP_CODE_DELAY > 0 wait_ms(TAP_CODE_DELAY); -#endif +# endif mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1); pointing_device_set_report(mouse_report); pointing_device_send(); @@ -52,7 +56,9 @@ static report_mouse_t trackpad_tap(report_mouse_t mouse_report, pinnacle_data_t void cirque_pinnacle_enable_tap(bool enable) { features.tap_enable = enable; } +#endif +#ifdef CIRQUE_PINNACLE_CIRCULAR_SCROLL_ENABLE // To set a trackpad exclusively as scroll wheel: outer_ring_pct = 100, trigger_px = 0, trigger_ang = 0 static circular_scroll_context_t scroll = {.config = {.outer_ring_pct = 33, .trigger_px = 16, .trigger_ang = 9102 /* 50 degrees */, .wheel_clicks = 18}}; @@ -110,22 +116,22 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { // vertical if started from righ half // horizontal if started from left half // flipped for left-handed -#if defined(POINTING_DEVICE_ROTATION_90) +# if defined(POINTING_DEVICE_ROTATION_90) scroll.axis = y < 0; -#elif defined(POINTING_DEVICE_ROTATION_180) +# elif defined(POINTING_DEVICE_ROTATION_180) scroll.axis = x > 0; -#elif defined(POINTING_DEVICE_ROTATION_270) +# elif defined(POINTING_DEVICE_ROTATION_270) scroll.axis = y > 0; -#else +# else scroll.axis = x < 0; -#endif +# endif } } else if (scroll.state == SCROLL_DETECTING) { report.suppress_touch = true; // already detecting scroll, check movement from touchdown location mag = sqrt16((x - scroll.x) * (x - scroll.x) + (y - scroll.y) * (y - scroll.y)); if (mag >= scroll.config.trigger_px) { -#define ABS(a) (a > 0 ? a : -a) +# define ABS(a) (a > 0 ? a : -a) // find angle of movement with 0 being movement towards center of circle dot = scroll.x * x + scroll.y * y; det = scroll.x * y - scroll.y * x; @@ -186,20 +192,26 @@ void cirque_pinnacle_configure_circular_scroll(uint8_t outer_ring_pct, uint8_t t scroll.config.wheel_clicks = wheel_clicks; scroll.config.left_handed = left_handed; } +#endif bool cirque_pinnacle_gestures(report_mouse_t* mouse_report, pinnacle_data_t touchData) { - bool suppress_mouse_update = false; - circular_scroll_t scroll_report; + bool suppress_mouse_update = false; +#ifdef CIRQUE_PINNACLE_CIRCULAR_SCROLL_ENABLE + circular_scroll_t scroll_report; if (features.circular_scroll_enable) { scroll_report = circular_scroll(touchData); mouse_report->v = scroll_report.v; mouse_report->h = scroll_report.h; suppress_mouse_update = scroll_report.suppress_touch; } +#endif + +#ifdef CIRQUE_PINNACLE_TAP_ENABLE if (features.tap_enable) { *mouse_report = trackpad_tap(*mouse_report, touchData); } +#endif return suppress_mouse_update; } diff --git a/drivers/sensors/cirque_pinnacle_gestures.h b/drivers/sensors/cirque_pinnacle_gestures.h index ad1ee39fbc6c..72cc4c69b589 100644 --- a/drivers/sensors/cirque_pinnacle_gestures.h +++ b/drivers/sensors/cirque_pinnacle_gestures.h @@ -19,15 +19,31 @@ #include "cirque_pinnacle.h" #include "report.h" -#ifndef CIRQUE_PINNACLE_TAPPING_TERM -# include "action.h" -# include "action_tapping.h" -# define CIRQUE_PINNACLE_TAPPING_TERM GET_TAPPING_TERM(KC_BTN1, &(keyrecord_t){}) -#endif -#ifndef CIRQUE_PINNACLE_TOUCH_DEBOUNCE -# define CIRQUE_PINNACLE_TOUCH_DEBOUNCE (CIRQUE_PINNACLE_TAPPING_TERM * 8) +typedef struct { + bool tap_enable; + bool circular_scroll_enable; +} cirque_pinnacle_features_t; + +#ifdef CIRQUE_PINNACLE_TAP_ENABLE +# ifndef CIRQUE_PINNACLE_TAPPING_TERM +# include "action.h" +# include "action_tapping.h" +# define CIRQUE_PINNACLE_TAPPING_TERM GET_TAPPING_TERM(KC_BTN1, &(keyrecord_t){}) +# endif +# ifndef CIRQUE_PINNACLE_TOUCH_DEBOUNCE +# define CIRQUE_PINNACLE_TOUCH_DEBOUNCE (CIRQUE_PINNACLE_TAPPING_TERM * 8) +# endif + +typedef struct { + uint16_t timer; + bool touchDown; +} trackpad_tap_context_t; + +// Enable/disable tap gesture +void cirque_pinnacle_enable_tap(bool enable); #endif +#ifdef CIRQUE_PINNACLE_CIRCULAR_SCROLL_ENABLE typedef enum { SCROLL_UNINITIALIZED, SCROLL_DETECTING, @@ -59,22 +75,6 @@ typedef struct { bool axis; } circular_scroll_context_t; -typedef struct { - uint16_t timer; - bool touchDown; -} trackpad_tap_context_t; - -typedef struct { - bool tap_enable; - bool circular_scroll_enable; -} cirque_pinnacle_features_t; - -// Process through available gestures -bool cirque_pinnacle_gestures(report_mouse_t* mouse_report, pinnacle_data_t touchData); - -// Enable/disable tap gesture -void cirque_pinnacle_enable_tap(bool enable); - // Enable/disable circular scroll gesture void cirque_pinnacle_enable_circular_scroll(bool enable); @@ -86,3 +86,18 @@ void cirque_pinnacle_enable_circular_scroll(bool enable); // @param wheel_clicks Number of scroll wheel clicks to report in a full rotation. // @param left_handed Whether scrolling should be flipped for left-handed use. void cirque_pinnacle_configure_circular_scroll(uint8_t outer_ring_pct, uint8_t trigger_px, uint16_t trigger_ang, uint8_t wheel_clicks, bool left_handed); +#endif + +#ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +// Implementation in pointing_device_drivers.c + +// Enable/disable inertial cursor +void cirque_pinnacle_enable_cursor_glide(bool enable); + +// Configure inertial cursor. +// @param trigger_px Movement required to trigger cursor glide, set this to non-zero if you have some amount of hover. +void cirque_pinnacle_configure_cursor_glide(float trigger_px); +#endif + +// Process through available gestures +bool cirque_pinnacle_gestures(report_mouse_t* mouse_report, pinnacle_data_t touchData); diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index e673d33bfe5d..cf4a6353804d 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -97,10 +97,14 @@ const pointing_device_driver_t pointing_device_driver = { // clang-format on #elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) -static bool cursor_glide_enable; -static cursor_glide_context_t glide = {.config = {.coef = 102 /* good default friction coef */, .interval = 10 /* 100sps */}}; +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +static bool cursor_glide_enable = true; + +static cursor_glide_context_t glide = {.config = { + .coef = 102, /* good default friction coef */ + .interval = 10 /* 100sps */ + }}; -// extern this for now void cirque_pinnacle_enable_cursor_glide(bool enable) { cursor_glide_enable = enable; } @@ -108,27 +112,32 @@ void cirque_pinnacle_enable_cursor_glide(bool enable) { void cirque_pinnacle_configure_cursor_glide(float trigger_px) { glide.config.trigger_px = trigger_px; } +# endif report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { pinnacle_data_t touchData = cirque_pinnacle_read_data(); - cursor_glide_t glide_report; mouse_xy_report_t report_x = 0, report_y = 0; static mouse_xy_report_t x = 0, y = 0; - -# if !CIRQUE_PINNACLE_POSITION_MODE -# error Cirque Pinnacle with relative mode not implemented yet. -# endif +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE + cursor_glide_t glide_report = {0}; if (cursor_glide_enable) { glide_report = cursor_glide_check(&glide); } +# endif + +# if !CIRQUE_PINNACLE_POSITION_MODE +# error Cirque Pinnacle with relative mode not implemented yet. +# endif if (!touchData.valid) { +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE if (cursor_glide_enable && glide_report.valid) { report_x = glide_report.dx; report_y = glide_report.dy; goto mouse_report_update; } +# endif return mouse_report; } @@ -149,6 +158,7 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { x = touchData.xValue; y = touchData.yValue; +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE if (cursor_glide_enable) { if (touchData.touchDown) { cursor_glide_update(&glide, report_x, report_y, touchData.zValue); @@ -160,9 +170,12 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { } } } +# endif } +# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE mouse_report_update: +# endif mouse_report.x = report_x; mouse_report.y = report_y; diff --git a/quantum/pointing_device_gestures.c b/quantum/pointing_device_gestures.c index 0c1c466b1d24..15f1a6aceb71 100644 --- a/quantum/pointing_device_gestures.c +++ b/quantum/pointing_device_gestures.c @@ -17,9 +17,10 @@ #include "pointing_device_gestures.h" #include "timer.h" -#ifdef POINTING_DEVICE_MOTION_PIN -# error POINTING_DEVICE_MOTION_PIN not supported when using inertial cursor. Need repeated calls to get_report() to generate glide events. -#endif +#ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE +# ifdef POINTING_DEVICE_MOTION_PIN +# error POINTING_DEVICE_MOTION_PIN not supported when using inertial cursor. Need repeated calls to get_report() to generate glide events. +# endif static void cursor_glide_stop(cursor_glide_context_t* glide) { memset(&glide->status, 0, sizeof(glide->status)); @@ -126,3 +127,4 @@ void cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mo status->dy0 = dy; status->z = z; } +#endif diff --git a/quantum/pointing_device_gestures.h b/quantum/pointing_device_gestures.h index b68a44ca6391..105e2e8a10c1 100644 --- a/quantum/pointing_device_gestures.h +++ b/quantum/pointing_device_gestures.h @@ -18,6 +18,7 @@ #include #include "report.h" +#ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE typedef struct { mouse_xy_report_t dx; mouse_xy_report_t dy; @@ -54,3 +55,4 @@ cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide); // Update glide engine on the latest cursor movement, cursor glide is based on the final movement void cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z); +#endif From 064d9e452586267dab94a705376c7e4892085a73 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sat, 9 Jul 2022 15:44:17 -0700 Subject: [PATCH 24/27] cirque pointing driver: Add a default triggir_px for inertial cursor --- quantum/pointing_device_drivers.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index cf4a6353804d..274c97a45eb0 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -101,8 +101,9 @@ const pointing_device_driver_t pointing_device_driver = { static bool cursor_glide_enable = true; static cursor_glide_context_t glide = {.config = { - .coef = 102, /* good default friction coef */ - .interval = 10 /* 100sps */ + .coef = 102, /* good default friction coef */ + .interval = 10, /* 100sps */ + .trigger_px = 10, /* default threshold in case of hover, set to 0 if you'd like */ }}; void cirque_pinnacle_enable_cursor_glide(bool enable) { From e48a3d144631d36477a1ad09cae5aa6fdecf103f Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sat, 9 Jul 2022 16:20:55 -0700 Subject: [PATCH 25/27] pointing device gestures: Style --- drivers/sensors/cirque_pinnacle_gestures.c | 54 +++++++++++++--------- drivers/sensors/cirque_pinnacle_gestures.h | 42 +++++++++-------- quantum/pointing_device_drivers.c | 4 +- quantum/pointing_device_gestures.c | 21 +++++---- quantum/pointing_device_gestures.h | 12 ++--- 5 files changed, 75 insertions(+), 58 deletions(-) diff --git a/drivers/sensors/cirque_pinnacle_gestures.c b/drivers/sensors/cirque_pinnacle_gestures.c index 5e501ac6adda..6ad71c1b5725 100644 --- a/drivers/sensors/cirque_pinnacle_gestures.c +++ b/drivers/sensors/cirque_pinnacle_gestures.c @@ -52,31 +52,35 @@ static report_mouse_t trackpad_tap(report_mouse_t mouse_report, pinnacle_data_t return mouse_report; } -// extern this function for now void cirque_pinnacle_enable_tap(bool enable) { features.tap_enable = enable; } #endif #ifdef CIRQUE_PINNACLE_CIRCULAR_SCROLL_ENABLE -// To set a trackpad exclusively as scroll wheel: outer_ring_pct = 100, trigger_px = 0, trigger_ang = 0 -static circular_scroll_context_t scroll = {.config = {.outer_ring_pct = 33, .trigger_px = 16, .trigger_ang = 9102 /* 50 degrees */, .wheel_clicks = 18}}; +/* To set a trackpad exclusively as scroll wheel: outer_ring_pct = 100, trigger_px = 0, trigger_ang = 0 */ +static circular_scroll_context_t scroll = {.config = {.outer_ring_pct = 33, + .trigger_px = 16, + .trigger_ang = 9102, /* 50 degrees */ + .wheel_clicks = 18}}; static inline uint16_t atan2_16(int32_t dy, int32_t dx) { if (dy == 0) { - if (dx >= 0) + if (dx >= 0) { return 0; - else + } else { return 32768; + } } int32_t abs_y = dy > 0 ? dy : -dy; int16_t a; - if (dx >= 0) + if (dx >= 0) { a = 8192 - (8192 * (dx - abs_y) / (dx + abs_y)); - else + } else { a = 24576 - (8192 * (dx + abs_y) / (abs_y - dx)); + } if (dy < 0) { return -a; // negate if in quad III or IV @@ -92,8 +96,10 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { uint16_t scale = cirque_pinnacle_get_scale(); if (touchData.zValue) { - // place origin at center of trackpad, treat coordinates as vectors - // scale to fixed int8_t size, angles are independent of resolution + /* + * Place origin at center of trackpad, treat coordinates as vectors. + * Scale to fixed int8_t size; angles are independent of resolution. + */ if (scale) { x = (int8_t)((int32_t)touchData.xValue * 256 / scale - center); y = (int8_t)((int32_t)touchData.yValue * 256 / scale - center); @@ -102,20 +108,22 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { y = 0; } - // check if first touch + /* Check if first touch */ if (!scroll.z) { report.suppress_touch = false; - // check if touch falls within outer ring + /* Check if touch falls within outer ring */ mag = sqrt16(x * x + y * y); if (mag * 100 / center >= 100 - scroll.config.outer_ring_pct) { scroll.state = SCROLL_DETECTING; scroll.x = x; scroll.y = y; scroll.mag = mag; - // decide scroll axis: - // vertical if started from righ half - // horizontal if started from left half - // flipped for left-handed + /* + * Decide scroll axis: + * Vertical if started from righ half + * Horizontal if started from left half + * Flipped for left-handed + */ # if defined(POINTING_DEVICE_ROTATION_90) scroll.axis = y < 0; # elif defined(POINTING_DEVICE_ROTATION_180) @@ -128,22 +136,25 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { } } else if (scroll.state == SCROLL_DETECTING) { report.suppress_touch = true; - // already detecting scroll, check movement from touchdown location + /* Already detecting scroll, check movement from touchdown location */ mag = sqrt16((x - scroll.x) * (x - scroll.x) + (y - scroll.y) * (y - scroll.y)); if (mag >= scroll.config.trigger_px) { # define ABS(a) (a > 0 ? a : -a) - // find angle of movement with 0 being movement towards center of circle + /* + * Find angle of movement. + * 0 degrees here means movement towards center of circle + */ dot = scroll.x * x + scroll.y * y; det = scroll.x * y - scroll.y * x; - opposite_side = ABS(det); // based on scalar rejection - adjacent_side = ABS(scroll.mag * scroll.mag - ABS(dot)); // based on scalar projection + opposite_side = ABS(det); /* Based on scalar rejection */ + adjacent_side = ABS(scroll.mag * scroll.mag - ABS(dot)); /* Based on scalar projection */ ang = (int16_t)atan2_16(opposite_side, adjacent_side); if (ang < scroll.config.trigger_ang) { - // not a scroll, release coordinates + /* Not a scroll, release coordinates */ report.suppress_touch = false; scroll.state = NOT_SCROLL; } else { - // scroll detected + /* Scroll detected */ scroll.state = SCROLL_VALID; } } @@ -180,7 +191,6 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { return report; } -// extern this for now void cirque_pinnacle_enable_circular_scroll(bool enable) { features.circular_scroll_enable = enable; } diff --git a/drivers/sensors/cirque_pinnacle_gestures.h b/drivers/sensors/cirque_pinnacle_gestures.h index 72cc4c69b589..f39782b46744 100644 --- a/drivers/sensors/cirque_pinnacle_gestures.h +++ b/drivers/sensors/cirque_pinnacle_gestures.h @@ -39,7 +39,7 @@ typedef struct { bool touchDown; } trackpad_tap_context_t; -// Enable/disable tap gesture +/* Enable/disable tap gesture */ void cirque_pinnacle_enable_tap(bool enable); #endif @@ -58,11 +58,11 @@ typedef struct { } circular_scroll_t; typedef struct { - uint8_t outer_ring_pct; // width of outer ring, given as a percentage of the radius - uint8_t trigger_px; // amount of movement before triggering scroll validation, in pixels 0~127 - uint16_t trigger_ang; // angle required to validate scroll, in radians where pi = 32768 - uint8_t wheel_clicks; // how many clicks to report in a circle - bool left_handed; // whether scrolling should be flipped for left handed use + uint8_t outer_ring_pct; /* Width of outer ring, given as a percentage of the radius */ + uint8_t trigger_px; /* Amount of movement before triggering scroll validation, in pixels 0~127 */ + uint16_t trigger_ang; /* Angle required to validate scroll, in radians where pi = 32768 */ + uint8_t wheel_clicks; /* How many clicks to report in a circle */ + bool left_handed; /* Whether scrolling should be flipped for left handed use */ } circular_scroll_config_t; typedef struct { @@ -75,29 +75,33 @@ typedef struct { bool axis; } circular_scroll_context_t; -// Enable/disable circular scroll gesture +/* Enable/disable circular scroll gesture */ void cirque_pinnacle_enable_circular_scroll(bool enable); -// Configure circular scroll gesture. -// Trackpad can be configured to act exclusively as a scroll wheel with outer_ring_pct = 0, trigger_px = 0, trigger_ang = 0. -// @param outer_ring_pct Width of outer ring from which to begin scroll validation, given as a percentage of the radius. -// @param trigger_px Amount of movement before triggering scroll validation. Expressed in pixels, trackpad coordinates are scaled to radius of 128 pixels for circular scroll. -// @param triger_ang Angle required to validate scroll, angle smaller than this will invalidate scroll. In radians where pi = 32768, 0 means movement towards center of trackpad, 16384 means movement perpendicular to center. -// @param wheel_clicks Number of scroll wheel clicks to report in a full rotation. -// @param left_handed Whether scrolling should be flipped for left-handed use. +/* + * Configure circular scroll gesture. + * Trackpad can be configured to act exclusively as a scroll wheel with outer_ring_pct = 0, trigger_px = 0, trigger_ang = 0. + * @param outer_ring_pct Width of outer ring from which to begin scroll validation, given as a percentage of the radius. + * @param trigger_px Amount of movement before triggering scroll validation. Expressed in pixels, trackpad coordinates are scaled to radius of 128 pixels for circular scroll. + * @param triger_ang Angle required to validate scroll, angle smaller than this will invalidate scroll. In radians where pi = 32768, 0 means movement towards center of trackpad, 16384 means movement perpendicular to center. + * @param wheel_clicks Number of scroll wheel clicks to report in a full rotation. + * @param left_handed Whether scrolling should be flipped for left-handed use. + */ void cirque_pinnacle_configure_circular_scroll(uint8_t outer_ring_pct, uint8_t trigger_px, uint16_t trigger_ang, uint8_t wheel_clicks, bool left_handed); #endif #ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE -// Implementation in pointing_device_drivers.c +/* Implementation in pointing_device_drivers.c */ -// Enable/disable inertial cursor +/* Enable/disable inertial cursor */ void cirque_pinnacle_enable_cursor_glide(bool enable); -// Configure inertial cursor. -// @param trigger_px Movement required to trigger cursor glide, set this to non-zero if you have some amount of hover. +/* + * Configure inertial cursor. + * @param trigger_px Movement required to trigger cursor glide, set this to non-zero if you have some amount of hover. + */ void cirque_pinnacle_configure_cursor_glide(float trigger_px); #endif -// Process through available gestures +/* Process available gestures */ bool cirque_pinnacle_gestures(report_mouse_t* mouse_report, pinnacle_data_t touchData); diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c index 274c97a45eb0..8f510b3a98f7 100644 --- a/quantum/pointing_device_drivers.c +++ b/quantum/pointing_device_drivers.c @@ -101,9 +101,9 @@ const pointing_device_driver_t pointing_device_driver = { static bool cursor_glide_enable = true; static cursor_glide_context_t glide = {.config = { - .coef = 102, /* good default friction coef */ + .coef = 102, /* Good default friction coef */ .interval = 10, /* 100sps */ - .trigger_px = 10, /* default threshold in case of hover, set to 0 if you'd like */ + .trigger_px = 10, /* Default threshold in case of hover, set to 0 if you'd like */ }}; void cirque_pinnacle_enable_cursor_glide(bool enable) { diff --git a/quantum/pointing_device_gestures.c b/quantum/pointing_device_gestures.c index 15f1a6aceb71..02b11ebe3fd1 100644 --- a/quantum/pointing_device_gestures.c +++ b/quantum/pointing_device_gestures.c @@ -41,16 +41,19 @@ static cursor_glide_t cursor_glide(cursor_glide_context_t* glide) { } status->counter++; - // calculate current 1D position + /* Calculate current 1D position */ p = status->v0 * status->counter - (int32_t)glide->config.coef * status->counter * status->counter / 2; - // translate to x & y axes + /* + * Translate to x & y axes + * Done this way instead of applying friction to each axis separately, so we don't end up with the shorter axis stuck at 0 towards the end of diagonal movements. + */ x = (int32_t)(p * status->dx0 / status->v0); y = (int32_t)(p * status->dy0 / status->v0); report.dx = (mouse_xy_report_t)(x - status->x); report.dy = (mouse_xy_report_t)(y - status->y); report.valid = true; if (report.dx <= 1 && report.dx >= -1 && report.dy <= 1 && report.dy >= -1) { - // stop gliding once speed is low enough + /* Stop gliding once speed is low enough */ cursor_glide_stop(glide); goto exit; } @@ -79,16 +82,16 @@ static inline uint16_t sqrt32(uint32_t x) { if (x == 0) { return 0; } else if (x > (UINT16_MAX >> 2)) { - // safe upper bound to avoid integer overflow with m * m + /* Safe upper bound to avoid integer overflow with m * m */ h = UINT16_MAX; } else { - // upper bound based on closest log2 + /* Upper bound based on closest log2 */ h = (1 << (((__builtin_clzl(1) - __builtin_clzl(x) + 1) + 1) >> 1)); } - // lower bound based on closest log2 + /* Lower bound based on closest log2 */ l = (1 << ((__builtin_clzl(1) - __builtin_clzl(x)) >> 1)); - // binary search to find integer square root + /* Binary search to find integer square root */ while (l != h - 1) { m = (l + h) / 2; if (m * m <= x) { @@ -111,8 +114,8 @@ cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide) { status->y = 0; status->z = 0; - if (status->v0 < ((uint32_t)glide->config.trigger_px * 256)) { // Q8 comparison - // not enough velocity to be worth gliding, abort + if (status->v0 < ((uint32_t)glide->config.trigger_px * 256)) { /* Q8 comparison */ + /* Not enough velocity to be worth gliding, abort */ cursor_glide_stop(glide); return invalid_report; } diff --git a/quantum/pointing_device_gestures.h b/quantum/pointing_device_gestures.h index 105e2e8a10c1..d2ea44971b80 100644 --- a/quantum/pointing_device_gestures.h +++ b/quantum/pointing_device_gestures.h @@ -26,9 +26,9 @@ typedef struct { } cursor_glide_t; typedef struct { - uint16_t trigger_px; // pixels of movement needed to trigger cursor glide - uint16_t coef; // coefficient of friction - uint16_t interval; // glide report interval, in milliseconds + uint16_t trigger_px; /* Pixels of movement needed to trigger cursor glide */ + uint16_t coef; /* Coefficient of friction */ + uint16_t interval; /* Glide report interval, in milliseconds */ } cursor_glide_config_t; typedef struct { @@ -47,12 +47,12 @@ typedef struct { cursor_glide_status_t status; } cursor_glide_context_t; -// Check glide report conditions, calculates glide coordinates +/* Check glide report conditions, calculates glide coordinates */ cursor_glide_t cursor_glide_check(cursor_glide_context_t* glide); -// Start glide reporting, gives first set of glide coordinates +/* Start glide reporting, gives first set of glide coordinates */ cursor_glide_t cursor_glide_start(cursor_glide_context_t* glide); -// Update glide engine on the latest cursor movement, cursor glide is based on the final movement +/* Update glide engine on the latest cursor movement, cursor glide is based on the final movement */ void cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z); #endif From 18aa3a1f06a38b6132d712045fc5bde87b7d37e1 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sat, 9 Jul 2022 16:27:02 -0700 Subject: [PATCH 26/27] circular scroll: Use stdlib abs() Remove stale math.h include --- drivers/sensors/cirque_pinnacle_gestures.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/sensors/cirque_pinnacle_gestures.c b/drivers/sensors/cirque_pinnacle_gestures.c index 6ad71c1b5725..f6f9177e08b7 100644 --- a/drivers/sensors/cirque_pinnacle_gestures.c +++ b/drivers/sensors/cirque_pinnacle_gestures.c @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include +#include #include #include "cirque_pinnacle_gestures.h" #include "pointing_device.h" @@ -139,15 +139,14 @@ static circular_scroll_t circular_scroll(pinnacle_data_t touchData) { /* Already detecting scroll, check movement from touchdown location */ mag = sqrt16((x - scroll.x) * (x - scroll.x) + (y - scroll.y) * (y - scroll.y)); if (mag >= scroll.config.trigger_px) { -# define ABS(a) (a > 0 ? a : -a) /* * Find angle of movement. * 0 degrees here means movement towards center of circle */ dot = scroll.x * x + scroll.y * y; det = scroll.x * y - scroll.y * x; - opposite_side = ABS(det); /* Based on scalar rejection */ - adjacent_side = ABS(scroll.mag * scroll.mag - ABS(dot)); /* Based on scalar projection */ + opposite_side = abs(det); /* Based on scalar rejection */ + adjacent_side = abs(scroll.mag * scroll.mag - abs(dot)); /* Based on scalar projection */ ang = (int16_t)atan2_16(opposite_side, adjacent_side); if (ang < scroll.config.trigger_ang) { /* Not a scroll, release coordinates */ From 8bd6c92ab04d1f0c6e5540f4fc061b7eaf8db395 Mon Sep 17 00:00:00 2001 From: Daniel Kao Date: Sat, 9 Jul 2022 16:50:39 -0700 Subject: [PATCH 27/27] cirque_pinnacle: Minor change to read-modify-write variable names --- drivers/sensors/cirque_pinnacle.c | 32 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/sensors/cirque_pinnacle.c b/drivers/sensors/cirque_pinnacle.c index 842b16e54544..c50d5a252581 100644 --- a/drivers/sensors/cirque_pinnacle.c +++ b/drivers/sensors/cirque_pinnacle.c @@ -81,15 +81,15 @@ void cirque_pinnacle_clear_flags() { // Enables/Disables the feed void cirque_pinnacle_enable_feed(bool feedEnable) { - uint8_t temp; - RAP_ReadBytes(HOSTREG__FEEDCONFIG1, &temp, 1); + uint8_t feedconfig1; + RAP_ReadBytes(HOSTREG__FEEDCONFIG1, &feedconfig1, 1); if (feedEnable) { - temp |= HOSTREG__FEEDCONFIG1__FEED_ENABLE; + feedconfig1 |= HOSTREG__FEEDCONFIG1__FEED_ENABLE; } else { - temp &= ~HOSTREG__FEEDCONFIG1__FEED_ENABLE; + feedconfig1 &= ~HOSTREG__FEEDCONFIG1__FEED_ENABLE; } - RAP_Write(HOSTREG__FEEDCONFIG1, temp); + RAP_Write(HOSTREG__FEEDCONFIG1, feedconfig1); } /* ERA (Extended Register Access) Functions */ @@ -143,27 +143,27 @@ void ERA_WriteByte(uint16_t address, uint8_t data) { } void cirque_pinnacle_set_adc_attenuation(uint8_t adcGain) { - uint8_t temp = 0x00; + uint8_t adcconfig = 0x00; - ERA_ReadBytes(EXTREG__TRACK_ADCCONFIG, &temp, 1); - temp &= EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_MASK; - temp |= adcGain; - ERA_WriteByte(EXTREG__TRACK_ADCCONFIG, temp); - ERA_ReadBytes(EXTREG__TRACK_ADCCONFIG, &temp, 1); + ERA_ReadBytes(EXTREG__TRACK_ADCCONFIG, &adcconfig, 1); + adcconfig &= EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_MASK; + adcconfig |= adcGain; + ERA_WriteByte(EXTREG__TRACK_ADCCONFIG, adcconfig); + ERA_ReadBytes(EXTREG__TRACK_ADCCONFIG, &adcconfig, 1); } // Changes thresholds to improve detection of fingers // Not needed for flat overlay? void cirque_pinnacle_tune_edge_sensitivity(void) { - uint8_t temp = 0x00; + uint8_t widezmin = 0x00; - ERA_ReadBytes(EXTREG__XAXIS_WIDEZMIN, &temp, 1); + ERA_ReadBytes(EXTREG__XAXIS_WIDEZMIN, &widezmin, 1); ERA_WriteByte(EXTREG__XAXIS_WIDEZMIN, 0x04); // magic number from Cirque sample code - ERA_ReadBytes(EXTREG__XAXIS_WIDEZMIN, &temp, 1); + ERA_ReadBytes(EXTREG__XAXIS_WIDEZMIN, &widezmin, 1); - ERA_ReadBytes(EXTREG__YAXIS_WIDEZMIN, &temp, 1); + ERA_ReadBytes(EXTREG__YAXIS_WIDEZMIN, &widezmin, 1); ERA_WriteByte(EXTREG__YAXIS_WIDEZMIN, 0x03); // magic number from Cirque sample code - ERA_ReadBytes(EXTREG__YAXIS_WIDEZMIN, &temp, 1); + ERA_ReadBytes(EXTREG__YAXIS_WIDEZMIN, &widezmin, 1); } // Perform calibration