Skip to content

Commit 01bc974

Browse files
authored
improvements for Cirque Pinnacle trackpads (#17091)
1 parent 60cbb25 commit 01bc974

6 files changed

Lines changed: 155 additions & 69 deletions

File tree

docs/feature_pointing_device.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,15 @@ POINTING_DEVICE_DRIVER = cirque_pinnacle_spi
8989

9090
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.
9191

92-
| Setting | Description | Default |
93-
|---------------------------------|---------------------------------------------------------------------------------|-----------------------|
94-
|`CIRQUE_PINNACLE_X_LOWER` | (Optional) The minimum reachable X value on the sensor. | `127` |
95-
|`CIRQUE_PINNACLE_X_UPPER` | (Optional) The maximum reachable X value on the sensor. | `1919` |
96-
|`CIRQUE_PINNACLE_Y_LOWER` | (Optional) The minimum reachable Y value on the sensor. | `63` |
97-
|`CIRQUE_PINNACLE_Y_UPPER` | (Optional) The maximum reachable Y value on the sensor. | `1471` |
98-
|`CIRQUE_PINNACLE_ATTENUATION` | (Optional) Sets the attenuation of the sensor data. | `ADC_ATTENUATE_4X` |
99-
|`CIRQUE_PINNACLE_TAPPING_TERM` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` |
100-
|`CIRQUE_PINNACLE_TOUCH_DEBOUNCE` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` |
92+
| Setting | Description | Default |
93+
|-------------------------------- |-----------------------------------------------------------------------|--------------------- |
94+
|`CIRQUE_PINNACLE_X_LOWER` | (Optional) The minimum reachable X value on the sensor. | `127` |
95+
|`CIRQUE_PINNACLE_X_UPPER` | (Optional) The maximum reachable X value on the sensor. | `1919` |
96+
|`CIRQUE_PINNACLE_Y_LOWER` | (Optional) The minimum reachable Y value on the sensor. | `63` |
97+
|`CIRQUE_PINNACLE_Y_UPPER` | (Optional) The maximum reachable Y value on the sensor. | `1471` |
98+
|`CIRQUE_PINNACLE_ATTENUATION` | (Optional) Sets the attenuation of the sensor data. | `ADC_ATTENUATE_4X` |
99+
|`CIRQUE_PINNACLE_TAPPING_TERM` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` |
100+
|`CIRQUE_PINNACLE_TOUCH_DEBOUNCE` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` |
101101

102102
**`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.
103103

@@ -122,6 +122,9 @@ Default attenuation is set to 4X, although if you are using a thicker overlay (s
122122

123123
Default Scaling/CPI is 1024.
124124

125+
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.
126+
127+
125128
### Pimoroni Trackball
126129

127130
To use the Pimoroni Trackball module, add this to your `rules.mk`:

drivers/sensors/cirque_pinnacle.c

Lines changed: 72 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license
2+
// based on https://github.com/cirque-corp/Cirque_Pinnacle_1CA027/tree/master/Circular_Trackpad
3+
// with modifications and changes for QMK
4+
// refer to documentation: Gen2 and Gen3 (Pinnacle ASIC) at https://www.cirque.com/documentation
5+
26
#include "cirque_pinnacle.h"
37
#include "print.h"
48
#include "debug.h"
59
#include "wait.h"
10+
#include "timer.h"
611

712
// Registers for RAP
813
// clang-format off
@@ -41,12 +46,6 @@
4146
#ifndef CIRQUE_PINNACLE_ATTENUATION
4247
# define CIRQUE_PINNACLE_ATTENUATION ADC_ATTENUATE_4X
4348
#endif
44-
45-
// Register config values for this demo
46-
#define SYSCONFIG_1_VALUE 0x00
47-
#define FEEDCONFIG_1_VALUE 0x03 // 0x03 for absolute mode 0x01 for relative mode
48-
#define FEEDCONFIG_2_VALUE 0x1C // 0x1F for normal functionality 0x1E for intellimouse disabled
49-
#define Z_IDLE_COUNT_VALUE 0x05
5049
// clang-format on
5150

5251
bool touchpad_init;
@@ -114,16 +113,14 @@ void cirque_pinnacle_clear_flags() {
114113
// Enables/Disables the feed
115114
void cirque_pinnacle_enable_feed(bool feedEnable) {
116115
uint8_t temp;
117-
118116
RAP_ReadBytes(FEEDCONFIG_1, &temp, 1); // Store contents of FeedConfig1 register
119117

120118
if (feedEnable) {
121119
temp |= 0x01; // Set Feed Enable bit
122-
RAP_Write(0x04, temp);
123120
} else {
124121
temp &= ~0x01; // Clear Feed Enable bit
125-
RAP_Write(0x04, temp);
126122
}
123+
RAP_Write(FEEDCONFIG_1, temp);
127124
}
128125

129126
/* ERA (Extended Register Access) Functions */
@@ -195,7 +192,7 @@ void cirque_pinnacle_tune_edge_sensitivity(void) {
195192
ERA_ReadBytes(0x0168, &temp, 1);
196193
}
197194

198-
/* Pinnacle-based TM040040 Functions */
195+
/* Pinnacle-based TM040040/TM035035/TM023023 Functions */
199196
void cirque_pinnacle_init(void) {
200197
#if defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi)
201198
spi_init();
@@ -204,40 +201,87 @@ void cirque_pinnacle_init(void) {
204201
#endif
205202

206203
touchpad_init = true;
204+
207205
// Host clears SW_CC flag
208206
cirque_pinnacle_clear_flags();
209207

210-
// Host configures bits of registers 0x03 and 0x05
211-
RAP_Write(SYSCONFIG_1, SYSCONFIG_1_VALUE);
212-
RAP_Write(FEEDCONFIG_2, FEEDCONFIG_2_VALUE);
213-
214-
// Host enables preferred output mode (absolute)
215-
RAP_Write(FEEDCONFIG_1, FEEDCONFIG_1_VALUE);
208+
// SysConfig1 (Low Power Mode)
209+
// Bit 0: Reset, 1=Reset
210+
// Bit 1: Shutdown, 1=Shutdown, 0=Active
211+
// Bit 2: Sleep Enable, 1=low power mode, 0=normal mode
212+
// send a RESET command now, in case QMK had a soft-reset without a power cycle
213+
RAP_Write(SYSCONFIG_1, 0x01);
214+
wait_ms(30); // Pinnacle needs 10-15ms to boot, so wait long enough before configuring
215+
RAP_Write(SYSCONFIG_1, 0x00);
216+
wait_us(50);
216217

217-
// Host sets z-idle packet count to 5 (default is 30)
218-
RAP_Write(Z_IDLE_COUNT, Z_IDLE_COUNT_VALUE);
218+
// FeedConfig2 (Feature flags for Relative Mode Only)
219+
// Bit 0: IntelliMouse Enable, 1=enable, 0=disable
220+
// Bit 1: All Taps Disable, 1=disable, 0=enable
221+
// Bit 2: Secondary Tap Disable, 1=disable, 0=enable
222+
// Bit 3: Scroll Disable, 1=disable, 0=enable
223+
// Bit 4: GlideExtend® Disable, 1=disable, 0=enable
224+
// Bit 5: reserved
225+
// Bit 6: reserved
226+
// Bit 7: Swap X & Y, 1=90° rotation, 0=0° rotation
227+
RAP_Write(FEEDCONFIG_2, 0x00);
228+
229+
// FeedConfig1 (Data Output Flags)
230+
// Bit 0: Feed enable, 1=feed, 0=no feed
231+
// Bit 1: Data mode, 1=absolute, 0=relative
232+
// Bit 2: Filter disable, 1=no filter, 0=filter
233+
// Bit 3: X disable, 1=no X data, 0=X data
234+
// Bit 4: Y disable, 1=no Y data, 0=Y data
235+
// Bit 5: reserved
236+
// Bit 6: X data Invert, 1=X max to 0, 0=0 to Y max
237+
// Bit 7: Y data Invert, 1=Y max to 0, 0=0 to Y max
238+
RAP_Write(FEEDCONFIG_1, CIRQUE_PINNACLE_POSITION_MODE << 1);
239+
240+
// Host sets z-idle packet count to 5 (default is 0x1F/30)
241+
RAP_Write(Z_IDLE_COUNT, 5);
219242

220243
cirque_pinnacle_set_adc_attenuation(CIRQUE_PINNACLE_ATTENUATION);
221244

222245
cirque_pinnacle_tune_edge_sensitivity();
223246
cirque_pinnacle_enable_feed(true);
224247
}
225248

226-
// Reads XYZ data from Pinnacle registers 0x14 through 0x17
227-
// Stores result in pinnacle_data_t struct with xValue, yValue, and zValue members
228249
pinnacle_data_t cirque_pinnacle_read_data(void) {
229-
uint8_t data[6] = {0};
230-
pinnacle_data_t result = {0};
250+
uint8_t data_ready = 0;
251+
uint8_t data[6] = {0};
252+
pinnacle_data_t result = {0};
253+
254+
// Check if there is valid data available
255+
RAP_ReadBytes(STATUS_1, &data_ready, 1); // bit2 is Software Data Ready, bit3 is Command Complete, bit0 and bit1 are reserved/unused
256+
if ((data_ready & 0x04) == 0) {
257+
// no data available yet
258+
result.valid = false; // be explicit
259+
return result;
260+
}
261+
262+
// Read all data bytes
231263
RAP_ReadBytes(PACKET_BYTE_0, data, 6);
232264

265+
// Get ready for the next data sample
233266
cirque_pinnacle_clear_flags();
234267

235-
result.buttonFlags = data[0] & 0x3F;
236-
result.xValue = data[2] | ((data[4] & 0x0F) << 8);
237-
result.yValue = data[3] | ((data[4] & 0xF0) << 4);
238-
result.zValue = data[5] & 0x3F;
239-
240-
result.touchDown = (result.xValue != 0 || result.yValue != 0);
268+
#if CIRQUE_PINNACLE_POSITION_MODE
269+
// Decode data for absolute mode
270+
// Register 0x13 is unused in this mode (palm detection area)
271+
result.buttonFlags = data[0] & 0x3F; // bit0 to bit5 are switch 0-5, only hardware button presses (from input pin on the Pinnacle chip)
272+
result.xValue = data[2] | ((data[4] & 0x0F) << 8); // merge high and low bits for X
273+
result.yValue = data[3] | ((data[4] & 0xF0) << 4); // merge high and low bits for Y
274+
result.zValue = data[5] & 0x3F; // Z is only lower 6 bits, upper 2 bits are reserved/unused
275+
result.touchDown = (result.xValue != 0 || result.yValue != 0); // (0,0) is a "magic coordinate" to indicate "finger touched down"
276+
#else
277+
// Decode data for relative mode
278+
// Registers 0x16 and 0x17 are unused in this mode
279+
result.buttons = data[0] & 0x07; // bit0 = primary button, bit1 = secondary button, bit2 = auxilary button, if Taps enabled then also software-recognized taps are reported
280+
result.xDelta = data[1];
281+
result.yDelta = data[2];
282+
result.wheelCount = data[3];
283+
#endif
241284

285+
result.valid = true;
242286
return result;
243287
}

drivers/sensors/cirque_pinnacle.h

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,14 @@
55
#include <stdint.h>
66
#include <stdbool.h>
77

8-
// Convenient way to store and access measurements
9-
typedef struct {
10-
uint16_t xValue;
11-
uint16_t yValue;
12-
uint16_t zValue;
13-
uint8_t buttonFlags;
14-
bool touchDown;
15-
} pinnacle_data_t;
16-
17-
void cirque_pinnacle_init(void);
18-
pinnacle_data_t cirque_pinnacle_read_data(void);
19-
void cirque_pinnacle_scale_data(pinnacle_data_t* coordinates, uint16_t xResolution, uint16_t yResolution);
20-
uint16_t cirque_pinnacle_get_scale(void);
21-
void cirque_pinnacle_set_scale(uint16_t scale);
22-
238
#ifndef CIRQUE_PINNACLE_TIMEOUT
24-
# define CIRQUE_PINNACLE_TIMEOUT 20
9+
# define CIRQUE_PINNACLE_TIMEOUT 20 // I2C timeout in milliseconds
10+
#endif
11+
12+
#define CIRQUE_PINNACLE_ABSOLUTE_MODE 1
13+
#define CIRQUE_PINNACLE_RELATIVE_MODE 0
14+
#ifndef CIRQUE_PINNACLE_POSITION_MODE
15+
# define CIRQUE_PINNACLE_POSITION_MODE CIRQUE_PINNACLE_ABSOLUTE_MODE
2516
#endif
2617

2718
// Coordinate scaling values
@@ -43,7 +34,9 @@ void cirque_pinnacle_set_scale(uint16_t scale);
4334
#ifndef CIRQUE_PINNACLE_Y_RANGE
4435
# define CIRQUE_PINNACLE_Y_RANGE (CIRQUE_PINNACLE_Y_UPPER - CIRQUE_PINNACLE_Y_LOWER)
4536
#endif
46-
37+
#if !defined(POINTING_DEVICE_TASK_THROTTLE_MS)
38+
# 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.
39+
#endif
4740
#if defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c)
4841
# include "i2c_master.h"
4942
// Cirque's 7-bit I2C Slave Address
@@ -72,3 +65,26 @@ void cirque_pinnacle_set_scale(uint16_t scale);
7265
# endif
7366
# endif
7467
#endif
68+
69+
// Convenient way to store and access measurements
70+
typedef struct {
71+
bool valid; // true if valid data was read, false if no data was ready
72+
#if CIRQUE_PINNACLE_POSITION_MODE
73+
uint16_t xValue;
74+
uint16_t yValue;
75+
uint16_t zValue;
76+
uint8_t buttonFlags;
77+
bool touchDown;
78+
#else
79+
uint8_t xDelta;
80+
uint8_t yDelta;
81+
uint8_t wheelCount;
82+
uint8_t buttons;
83+
#endif
84+
} pinnacle_data_t;
85+
86+
void cirque_pinnacle_init(void);
87+
pinnacle_data_t cirque_pinnacle_read_data(void);
88+
void cirque_pinnacle_scale_data(pinnacle_data_t* coordinates, uint16_t xResolution, uint16_t yResolution);
89+
uint16_t cirque_pinnacle_get_scale(void);
90+
void cirque_pinnacle_set_scale(uint16_t scale);

drivers/sensors/cirque_pinnacle_i2c.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ void RAP_ReadBytes(uint8_t address, uint8_t* data, uint8_t count) {
1919
i2c_writeReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, NULL, 0, CIRQUE_PINNACLE_TIMEOUT);
2020
if (i2c_readReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, data, count, CIRQUE_PINNACLE_TIMEOUT) != I2C_STATUS_SUCCESS) {
2121
#ifdef CONSOLE_ENABLE
22-
dprintf("error right touchpad\n");
22+
dprintf("error cirque_pinnacle i2c_readReg\n");
2323
#endif
2424
touchpad_init = false;
2525
}
@@ -34,7 +34,7 @@ void RAP_Write(uint8_t address, uint8_t data) {
3434
if (touchpad_init) {
3535
if (i2c_writeReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, &data, sizeof(data), CIRQUE_PINNACLE_TIMEOUT) != I2C_STATUS_SUCCESS) {
3636
#ifdef CONSOLE_ENABLE
37-
dprintf("error right touchpad\n");
37+
dprintf("error cirque_pinnacle i2c_writeReg\n");
3838
#endif
3939
touchpad_init = false;
4040
}

drivers/sensors/cirque_pinnacle_spi.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ void RAP_ReadBytes(uint8_t address, uint8_t* data, uint8_t count) {
2525
}
2626
} else {
2727
#ifdef CONSOLE_ENABLE
28-
dprintf("error right touchpad\n");
28+
dprintf("error cirque_pinnacle spi_start read\n");
2929
#endif
3030
touchpad_init = false;
3131
}
@@ -43,7 +43,7 @@ void RAP_Write(uint8_t address, uint8_t data) {
4343
spi_write(data);
4444
} else {
4545
#ifdef CONSOLE_ENABLE
46-
dprintf("error right touchpad\n");
46+
dprintf("error cirque_pinnacle spi_start write\n");
4747
#endif
4848
touchpad_init = false;
4949
}

0 commit comments

Comments
 (0)