diff options
author | Daniel Kao <daniel.m.kao@gmail.com> | 2022-07-12 21:17:40 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-12 21:17:40 -0700 |
commit | 5db705d054e54a901f8968e3380e13c791991ab3 (patch) | |
tree | 078bf54330b45672d15532c38b010510da50ab74 /drivers/sensors | |
parent | 904ec0ce7855e6dc62d2db2cd851f67fcc0f323f (diff) |
Cirque trackpad features: circular scroll, inertial cursor (#17482)
Diffstat (limited to 'drivers/sensors')
-rw-r--r-- | drivers/sensors/cirque_pinnacle.c | 191 | ||||
-rw-r--r-- | drivers/sensors/cirque_pinnacle.h | 14 | ||||
-rw-r--r-- | drivers/sensors/cirque_pinnacle_gestures.c | 226 | ||||
-rw-r--r-- | drivers/sensors/cirque_pinnacle_gestures.h | 107 | ||||
-rw-r--r-- | drivers/sensors/cirque_pinnacle_regdefs.h | 405 |
5 files changed, 841 insertions, 102 deletions
diff --git a/drivers/sensors/cirque_pinnacle.c b/drivers/sensors/cirque_pinnacle.c index 1d1e4ccfc6..c50d5a2525 100644 --- a/drivers/sensors/cirque_pinnacle.c +++ b/drivers/sensors/cirque_pinnacle.c @@ -9,47 +9,16 @@ #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 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 +# 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 -// 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); @@ -106,43 +75,45 @@ 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 + uint8_t feedconfig1; + RAP_ReadBytes(HOSTREG__FEEDCONFIG1, &feedconfig1, 1); if (feedEnable) { - temp |= 0x01; // Set Feed Enable bit + feedconfig1 |= HOSTREG__FEEDCONFIG1__FEED_ENABLE; } else { - temp &= ~0x01; // Clear Feed Enable bit + feedconfig1 &= ~HOSTREG__FEEDCONFIG1__FEED_ENABLE; } - RAP_Write(FEEDCONFIG_1, temp); + RAP_Write(HOSTREG__FEEDCONFIG1, feedconfig1); } /* ERA (Extended Register Access) Functions */ // Reads <count> bytes from an extended register at <address> (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 - 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); - } while (ERAControlValue != 0x00); + 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(); } @@ -150,46 +121,80 @@ void ERA_ReadBytes(uint16_t address, uint8_t* data, uint16_t count) { // Writes a byte, <data>, to an extended register at <address> (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 - 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); - } while (ERAControlValue != 0x00); + RAP_ReadBytes(HOSTREG__EXT_REG_AXS_CTRL, &ERAControlValue, 1); + } while ((ERAControlValue != 0x00) && (timer_elapsed(timeout_timer) <= CIRQUE_PINNACLE_TIMEOUT)); cirque_pinnacle_clear_flags(); } void cirque_pinnacle_set_adc_attenuation(uint8_t adcGain) { - uint8_t temp = 0x00; + uint8_t adcconfig = 0x00; - ERA_ReadBytes(0x0187, &temp, 1); - temp &= 0x3F; // clear top two bits - temp |= adcGain; - ERA_WriteByte(0x0187, temp); - ERA_ReadBytes(0x0187, &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(0x0149, &temp, 1); - ERA_WriteByte(0x0149, 0x04); - ERA_ReadBytes(0x0149, &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, &widezmin, 1); - ERA_ReadBytes(0x0168, &temp, 1); - ERA_WriteByte(0x0168, 0x03); - ERA_ReadBytes(0x0168, &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, &widezmin, 1); +} + +// Perform calibration +void cirque_pinnacle_calibrate(void) { + uint8_t calconfig; + uint16_t timeout_timer; + + 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(HOSTREG__CALCONFIG1, &calconfig, 1); + } while ((calconfig & HOSTREG__CALCONFIG1__CALIBRATE) && (timer_elapsed(timeout_timer) <= 200)); + + cirque_pinnacle_clear_flags(); +} + +// Enable/disable cursor smoothing, smoothing is enabled by default +void cirque_pinnacle_cursor_smoothing(bool enable) { + uint8_t feedconfig3; + + RAP_ReadBytes(HOSTREG__FEEDCONFIG3, &feedconfig3, 1); + if (enable) { + feedconfig3 &= ~HOSTREG__FEEDCONFIG3__DISABLE_CROSS_RATE_SMOOTHING; + } else { + feedconfig3 |= HOSTREG__FEEDCONFIG3__DISABLE_CROSS_RATE_SMOOTHING; + } + RAP_Write(HOSTREG__FEEDCONFIG3, feedconfig3); } /* Pinnacle-based TM040040/TM035035/TM023023 Functions */ @@ -205,44 +210,28 @@ 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); - cirque_pinnacle_set_adc_attenuation(CIRQUE_PINNACLE_ATTENUATION); + // 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); +#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); } @@ -252,15 +241,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 d65bdb41b6..1c9bf06fd3 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 <stdint.h> #include <stdbool.h> @@ -15,6 +16,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 @@ -41,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" @@ -66,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 @@ -84,6 +94,8 @@ typedef struct { } pinnacle_data_t; 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); diff --git a/drivers/sensors/cirque_pinnacle_gestures.c b/drivers/sensors/cirque_pinnacle_gestures.c new file mode 100644 index 0000000000..f6f9177e08 --- /dev/null +++ b/drivers/sensors/cirque_pinnacle_gestures.c @@ -0,0 +1,226 @@ +/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com> + * Copyright 2022 Daniel Kao <daniel.m.kao@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ +#include <stdlib.h> +#include <lib/lib8tion/lib8tion.h> +#include "cirque_pinnacle_gestures.h" +#include "pointing_device.h" +#include "timer.h" + +#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) { + 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; +} + +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}}; + +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}; + 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. + */ + 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) { + report.suppress_touch = false; + /* 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 + */ +# 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 = sqrt16((x - scroll.x) * (x - scroll.x) + (y - scroll.y) * (y - scroll.y)); + if (mag >= scroll.config.trigger_px) { + /* + * 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 */ + ang = (int16_t)atan2_16(opposite_side, adjacent_side); + if (ang < scroll.config.trigger_ang) { + /* 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 = 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.config.wheel_clicks) / 65536; + if (wheel_clicks >= 1 || wheel_clicks <= -1) { + if (scroll.config.left_handed) { + if (scroll.axis == 0) { + report.h = -wheel_clicks; + } else { + report.v = wheel_clicks; + } + } else { + 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; +} + +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, 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; +} +#endif + +bool cirque_pinnacle_gestures(report_mouse_t* mouse_report, pinnacle_data_t touchData) { + 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 new file mode 100644 index 0000000000..f39782b467 --- /dev/null +++ b/drivers/sensors/cirque_pinnacle_gestures.h @@ -0,0 +1,107 @@ +/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com> + * Copyright 2022 Daniel Kao <daniel.m.kao@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ +#pragma once + +#include "cirque_pinnacle.h" +#include "report.h" + +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, + SCROLL_VALID, + NOT_SCROLL, +} circular_scroll_status_t; + +typedef struct { + int8_t v; + int8_t h; + bool suppress_touch; +} 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 */ +} 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; + bool axis; +} circular_scroll_context_t; + +/* 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. + */ +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 available gestures */ +bool cirque_pinnacle_gestures(report_mouse_t* mouse_report, pinnacle_data_t touchData); diff --git a/drivers/sensors/cirque_pinnacle_regdefs.h b/drivers/sensors/cirque_pinnacle_regdefs.h new file mode 100644 index 0000000000..993da1e757 --- /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 |