improvements for Cirque Pinnacle trackpads (#17091)

master
Thomas Kriechbaumer 2022-06-25 22:22:28 +02:00 committed by GitHub
parent 60cbb25148
commit 01bc974365
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 154 additions and 68 deletions

View File

@ -89,15 +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_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` |
| 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_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` |
**`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,6 +122,9 @@ Default attenuation is set to 4X, although if you are using a thicker overlay (s
Default Scaling/CPI is 1024.
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.
### Pimoroni Trackball
To use the Pimoroni Trackball module, add this to your `rules.mk`:

View File

@ -1,8 +1,13 @@
// 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"
#include "timer.h"
// Registers for RAP
// clang-format off
@ -41,12 +46,6 @@
#ifndef CIRQUE_PINNACLE_ATTENUATION
# define CIRQUE_PINNACLE_ATTENUATION ADC_ATTENUATE_4X
#endif
// 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
bool touchpad_init;
@ -114,16 +113,14 @@ void cirque_pinnacle_clear_flags() {
// 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
if (feedEnable) {
temp |= 0x01; // Set Feed Enable bit
RAP_Write(0x04, temp);
} else {
temp &= ~0x01; // Clear Feed Enable bit
RAP_Write(0x04, temp);
}
RAP_Write(FEEDCONFIG_1, temp);
}
/* ERA (Extended Register Access) Functions */
@ -195,7 +192,7 @@ void cirque_pinnacle_tune_edge_sensitivity(void) {
ERA_ReadBytes(0x0168, &temp, 1);
}
/* Pinnacle-based TM040040 Functions */
/* Pinnacle-based TM040040/TM035035/TM023023 Functions */
void cirque_pinnacle_init(void) {
#if defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi)
spi_init();
@ -204,18 +201,44 @@ 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);
// 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);
wait_ms(30); // Pinnacle needs 10-15ms to boot, so wait long enough before configuring
RAP_Write(SYSCONFIG_1, 0x00);
wait_us(50);
// Host enables preferred output mode (absolute)
RAP_Write(FEEDCONFIG_1, FEEDCONFIG_1_VALUE);
// 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);
// Host sets z-idle packet count to 5 (default is 30)
RAP_Write(Z_IDLE_COUNT, Z_IDLE_COUNT_VALUE);
// 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);
cirque_pinnacle_set_adc_attenuation(CIRQUE_PINNACLE_ATTENUATION);
@ -223,21 +246,42 @@ void cirque_pinnacle_init(void) {
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};
uint8_t data_ready = 0;
uint8_t data[6] = {0};
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) {
// no data available yet
result.valid = false; // be explicit
return result;
}
// Read all data bytes
RAP_ReadBytes(PACKET_BYTE_0, data, 6);
// Get ready for the next data sample
cirque_pinnacle_clear_flags();
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;
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;
}

View File

@ -5,23 +5,14 @@
#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;
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);
#ifndef CIRQUE_PINNACLE_TIMEOUT
# define CIRQUE_PINNACLE_TIMEOUT 20
# define CIRQUE_PINNACLE_TIMEOUT 20 // I2C timeout in milliseconds
#endif
#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
// Coordinate scaling values
@ -43,7 +34,9 @@ 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
@ -72,3 +65,26 @@ void cirque_pinnacle_set_scale(uint16_t scale);
# endif
# endif
#endif
// 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);
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);

View File

@ -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;
}

View File

@ -25,7 +25,7 @@ void RAP_ReadBytes(uint8_t address, uint8_t* data, uint8_t count) {
}
} else {
#ifdef CONSOLE_ENABLE
dprintf("error right touchpad\n");
dprintf("error cirque_pinnacle spi_start read\n");
#endif
touchpad_init = false;
}
@ -43,7 +43,7 @@ void RAP_Write(uint8_t address, uint8_t data) {
spi_write(data);
} else {
#ifdef CONSOLE_ENABLE
dprintf("error right touchpad\n");
dprintf("error cirque_pinnacle spi_start write\n");
#endif
touchpad_init = false;
}

View File

@ -50,6 +50,7 @@ const pointing_device_driver_t pointing_device_driver = {
.get_cpi = adns5050_get_cpi,
};
// clang-format on
#elif defined(POINTING_DEVICE_DRIVER_adns9800)
report_mouse_t adns9800_get_report_driver(report_mouse_t mouse_report) {
@ -69,6 +70,7 @@ const pointing_device_driver_t pointing_device_driver = {
.get_cpi = adns9800_get_cpi
};
// clang-format on
#elif defined(POINTING_DEVICE_DRIVER_analog_joystick)
report_mouse_t analog_joystick_get_report(report_mouse_t mouse_report) {
report_analog_joystick_t data = analog_joystick_read();
@ -93,6 +95,7 @@ const pointing_device_driver_t pointing_device_driver = {
.get_cpi = NULL
};
// 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"
@ -104,22 +107,40 @@ const pointing_device_driver_t pointing_device_driver = {
# endif
report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) {
pinnacle_data_t touchData = cirque_pinnacle_read_data();
static uint16_t x = 0, y = 0, mouse_timer = 0;
mouse_xy_report_t report_x = 0, report_y = 0;
static bool is_z_down = false;
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;
cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale()); // Scale coordinates to arbitrary X, Y resolution
# if !CIRQUE_PINNACLE_POSITION_MODE
# error Cirque Pinnacle with relative mode not implemented yet.
# endif
if (!touchData.valid) {
return mouse_report;
}
# if CONSOLE_ENABLE
if (debug_mouse && touchData.touchDown) {
dprintf("cirque_pinnacle touchData x=%4d y=%4d z=%2d\n", touchData.xValue, touchData.yValue, touchData.zValue);
}
# endif
// Scale coordinates to arbitrary X, Y resolution
cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale());
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;
x = touchData.xValue;
y = touchData.yValue;
mouse_report.x = report_x;
mouse_report.y = report_y;
if ((bool)touchData.zValue != is_z_down) {
is_z_down = (bool)touchData.zValue;
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);
@ -138,8 +159,6 @@ report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) {
if (timer_elapsed(mouse_timer) > (CIRQUE_PINNACLE_TOUCH_DEBOUNCE)) {
mouse_timer = 0;
}
mouse_report.x = report_x;
mouse_report.y = report_y;
return mouse_report;
}
@ -210,6 +229,7 @@ const pointing_device_driver_t pointing_device_driver = {
.get_cpi = pimoroni_trackball_get_cpi
};
// clang-format on
#elif defined(POINTING_DEVICE_DRIVER_pmw3360)
static void pmw3360_device_init(void) {
pmw3360_init(0);
@ -248,6 +268,7 @@ const pointing_device_driver_t pointing_device_driver = {
.get_cpi = pmw3360_get_cpi
};
// clang-format on
#elif defined(POINTING_DEVICE_DRIVER_pmw3389)
static void pmw3389_device_init(void) {
pmw3389_init();
@ -286,6 +307,7 @@ const pointing_device_driver_t pointing_device_driver = {
.get_cpi = pmw3389_get_cpi
};
// clang-format on
#else
__attribute__((weak)) void pointing_device_driver_init(void) {}
__attribute__((weak)) report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) {
@ -304,4 +326,5 @@ const pointing_device_driver_t pointing_device_driver = {
.set_cpi = pointing_device_driver_set_cpi
};
// clang-format on
#endif