diff options
Diffstat (limited to 'drivers/sensors')
-rw-r--r-- | drivers/sensors/cirque_pinnacle.c | 232 | ||||
-rw-r--r-- | drivers/sensors/cirque_pinnacle.h | 62 | ||||
-rw-r--r-- | drivers/sensors/cirque_pinnacle_gestures.c | 227 | ||||
-rw-r--r-- | drivers/sensors/cirque_pinnacle_gestures.h | 107 | ||||
-rw-r--r-- | drivers/sensors/cirque_pinnacle_i2c.c | 4 | ||||
-rw-r--r-- | drivers/sensors/cirque_pinnacle_regdefs.h | 405 | ||||
-rw-r--r-- | drivers/sensors/cirque_pinnacle_spi.c | 4 | ||||
-rw-r--r-- | drivers/sensors/paw3204.c | 172 | ||||
-rw-r--r-- | drivers/sensors/paw3204.h | 68 | ||||
-rw-r--r-- | drivers/sensors/pimoroni_trackball.c | 13 | ||||
-rw-r--r-- | drivers/sensors/pimoroni_trackball.h | 1 | ||||
-rw-r--r-- | drivers/sensors/pmw3360.c | 567 | ||||
-rw-r--r-- | drivers/sensors/pmw3360.h | 134 | ||||
-rw-r--r-- | drivers/sensors/pmw3360_firmware.h | 288 | ||||
-rw-r--r-- | drivers/sensors/pmw3389.c | 580 | ||||
-rw-r--r-- | drivers/sensors/pmw3389.h | 135 | ||||
-rw-r--r-- | drivers/sensors/pmw3389_firmware.h | 307 | ||||
-rw-r--r-- | drivers/sensors/pmw33xx_common.c | 219 | ||||
-rw-r--r-- | drivers/sensors/pmw33xx_common.h | 150 |
19 files changed, 2249 insertions, 1426 deletions
diff --git a/drivers/sensors/cirque_pinnacle.c b/drivers/sensors/cirque_pinnacle.c index 2db7f916fe..c50d5a2525 100644 --- a/drivers/sensors/cirque_pinnacle.c +++ b/drivers/sensors/cirque_pinnacle.c @@ -1,52 +1,24 @@ // 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/Circular_Trackpad +// with modifications and changes for QMK +// refer to documentation: Gen2 and Gen3 (Pinnacle ASIC) at https://www.cirque.com/documentation + #include "cirque_pinnacle.h" #include "print.h" #include "debug.h" #include "wait.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 - -// Register config values for this demo -#define SYSCONFIG_1_VALUE 0x00 -#define FEEDCONFIG_1_VALUE 0x03 // 0x03 for absolute mode 0x01 for relative mode -#define FEEDCONFIG_2_VALUE 0x1C // 0x1F for normal functionality 0x1E for intellimouse disabled -#define Z_IDLE_COUNT_VALUE 0x05 -// clang-format on +#include "timer.h" + +#ifndef CIRQUE_PINNACLE_ATTENUATION +# 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; -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); @@ -103,45 +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 - RAP_Write(0x04, temp); + feedconfig1 |= HOSTREG__FEEDCONFIG1__FEED_ENABLE; } else { - temp &= ~0x01; // Clear Feed Enable bit - RAP_Write(0x04, temp); + feedconfig1 &= ~HOSTREG__FEEDCONFIG1__FEED_ENABLE; } + 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(); } @@ -149,49 +121,83 @@ 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); } -/* Pinnacle-based TM040040 Functions */ +// 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 */ void cirque_pinnacle_init(void) { #if defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) spi_init(); @@ -200,39 +206,71 @@ void cirque_pinnacle_init(void) { #endif touchpad_init = true; + // Host clears SW_CC flag cirque_pinnacle_clear_flags(); - // Host configures bits of registers 0x03 and 0x05 - RAP_Write(SYSCONFIG_1, SYSCONFIG_1_VALUE); - RAP_Write(FEEDCONFIG_2, FEEDCONFIG_2_VALUE); + // send a RESET command now, in case QMK had a soft-reset without a power cycle + RAP_Write(HOSTREG__SYSCONFIG1, HOSTREG__SYSCONFIG1__RESET); + wait_ms(30); // Pinnacle needs 10-15ms to boot, so wait long enough before configuring + RAP_Write(HOSTREG__SYSCONFIG1, HOSTREG__SYSCONFIG1_DEFVAL); + wait_us(50); + + // FeedConfig2 (Feature flags for Relative Mode Only) + RAP_Write(HOSTREG__FEEDCONFIG2, HOSTREG__FEEDCONFIG2_DEFVAL); - // Host enables preferred output mode (absolute) - RAP_Write(FEEDCONFIG_1, FEEDCONFIG_1_VALUE); + // FeedConfig1 (Data Output Flags) + 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 30) - RAP_Write(Z_IDLE_COUNT, Z_IDLE_COUNT_VALUE); + // Host sets z-idle packet count to 5 (default is 0x1E/30) + RAP_Write(HOSTREG__ZIDLE, 5); - cirque_pinnacle_set_adc_attenuation(0xFF); + 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); } -// Reads XYZ data from Pinnacle registers 0x14 through 0x17 -// Stores result in pinnacle_data_t struct with xValue, yValue, and zValue members pinnacle_data_t cirque_pinnacle_read_data(void) { - uint8_t data[6] = {0}; - pinnacle_data_t result = {0}; - RAP_ReadBytes(PACKET_BYTE_0, data, 6); + uint8_t data_ready = 0; + uint8_t data[6] = {0}; + pinnacle_data_t result = {0}; + + // Check if there is valid data available + 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; + } - cirque_pinnacle_clear_flags(); + // Read all data bytes + RAP_ReadBytes(HOSTREG__PACKETBYTE_0, data, 6); - result.buttonFlags = data[0] & 0x3F; - result.xValue = data[2] | ((data[4] & 0x0F) << 8); - result.yValue = data[3] | ((data[4] & 0xF0) << 4); - result.zValue = data[5] & 0x3F; + // Get ready for the next data sample + cirque_pinnacle_clear_flags(); - result.touchDown = (result.xValue != 0 || result.yValue != 0); +#if CIRQUE_PINNACLE_POSITION_MODE + // Decode data for absolute mode + // Register 0x13 is unused in this mode (palm detection area) + result.buttonFlags = data[0] & 0x3F; // bit0 to bit5 are switch 0-5, only hardware button presses (from input pin on the Pinnacle chip) + result.xValue = data[2] | ((data[4] & 0x0F) << 8); // merge high and low bits for X + result.yValue = data[3] | ((data[4] & 0xF0) << 4); // merge high and low bits for Y + result.zValue = data[5] & 0x3F; // Z is only lower 6 bits, upper 2 bits are reserved/unused + result.touchDown = (result.xValue != 0 || result.yValue != 0); // (0,0) is a "magic coordinate" to indicate "finger touched down" +#else + // Decode data for relative mode + // Registers 0x16 and 0x17 are unused in this mode + result.buttons = data[0] & 0x07; // bit0 = primary button, bit1 = secondary button, bit2 = auxilary button, if Taps enabled then also software-recognized taps are reported + result.xDelta = data[1]; + result.yDelta = data[2]; + result.wheelCount = data[3]; +#endif + result.valid = true; return result; } diff --git a/drivers/sensors/cirque_pinnacle.h b/drivers/sensors/cirque_pinnacle.h index c8cb360e03..1c9bf06fd3 100644 --- a/drivers/sensors/cirque_pinnacle.h +++ b/drivers/sensors/cirque_pinnacle.h @@ -2,26 +2,23 @@ #pragma once +#include "cirque_pinnacle_regdefs.h" #include <stdint.h> #include <stdbool.h> -// Convenient way to store and access measurements -typedef struct { - uint16_t xValue; - uint16_t yValue; - uint16_t zValue; - uint8_t buttonFlags; - bool touchDown; -} pinnacle_data_t; +#ifndef CIRQUE_PINNACLE_TIMEOUT +# define CIRQUE_PINNACLE_TIMEOUT 20 // I2C timeout in milliseconds +#endif -void cirque_pinnacle_init(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); -void cirque_pinnacle_set_scale(uint16_t scale); +#define CIRQUE_PINNACLE_ABSOLUTE_MODE 1 +#define CIRQUE_PINNACLE_RELATIVE_MODE 0 +#ifndef CIRQUE_PINNACLE_POSITION_MODE +# define CIRQUE_PINNACLE_POSITION_MODE CIRQUE_PINNACLE_ABSOLUTE_MODE +#endif -#ifndef CIRQUE_PINNACLE_TIMEOUT -# define CIRQUE_PINNACLE_TIMEOUT 20 +#define CIRQUE_PINNACLE_DEFAULT_SCALE 1024 +#ifndef CIRQUE_PINNACLE_DIAMETER_MM +# define CIRQUE_PINNACLE_DIAMETER_MM 40 #endif // Coordinate scaling values @@ -43,12 +40,14 @@ void cirque_pinnacle_set_scale(uint16_t scale); #ifndef CIRQUE_PINNACLE_Y_RANGE # define CIRQUE_PINNACLE_Y_RANGE (CIRQUE_PINNACLE_Y_UPPER - CIRQUE_PINNACLE_Y_LOWER) #endif - +#if !defined(POINTING_DEVICE_TASK_THROTTLE_MS) +# define POINTING_DEVICE_TASK_THROTTLE_MS 10 // Cirque Pinnacle in normal operation produces data every 10ms. Advanced configuration for pen/stylus usage might require lower values. +#endif #if defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) # 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" @@ -72,3 +71,32 @@ void cirque_pinnacle_set_scale(uint16_t scale); # endif # 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 +#if CIRQUE_PINNACLE_POSITION_MODE + uint16_t xValue; + uint16_t yValue; + uint16_t zValue; + uint8_t buttonFlags; + bool touchDown; +#else + uint8_t xDelta; + uint8_t yDelta; + uint8_t wheelCount; + uint8_t buttons; +#endif +} 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); +void cirque_pinnacle_set_scale(uint16_t scale); diff --git a/drivers/sensors/cirque_pinnacle_gestures.c b/drivers/sensors/cirque_pinnacle_gestures.c new file mode 100644 index 0000000000..70f16527ba --- /dev/null +++ b/drivers/sensors/cirque_pinnacle_gestures.c @@ -0,0 +1,227 @@ +/* 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" +#include "wait.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_i2c.c b/drivers/sensors/cirque_pinnacle_i2c.c index 8a38f1dcea..b328dd9a7a 100644 --- a/drivers/sensors/cirque_pinnacle_i2c.c +++ b/drivers/sensors/cirque_pinnacle_i2c.c @@ -19,7 +19,7 @@ void RAP_ReadBytes(uint8_t address, uint8_t* data, uint8_t count) { i2c_writeReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, NULL, 0, CIRQUE_PINNACLE_TIMEOUT); if (i2c_readReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, data, count, CIRQUE_PINNACLE_TIMEOUT) != I2C_STATUS_SUCCESS) { #ifdef CONSOLE_ENABLE - dprintf("error right touchpad\n"); + dprintf("error cirque_pinnacle i2c_readReg\n"); #endif touchpad_init = false; } @@ -34,7 +34,7 @@ void RAP_Write(uint8_t address, uint8_t data) { if (touchpad_init) { if (i2c_writeReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, &data, sizeof(data), CIRQUE_PINNACLE_TIMEOUT) != I2C_STATUS_SUCCESS) { #ifdef CONSOLE_ENABLE - dprintf("error right touchpad\n"); + dprintf("error cirque_pinnacle i2c_writeReg\n"); #endif touchpad_init = false; } 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) |