Skip to content

Commit

Permalink
Feature: joystick weights (qmk#21883)
Browse files Browse the repository at this point in the history
Co-authored-by: Nick Brassel <nick@tzarc.org>
  • Loading branch information
2 people authored and itsjonny96 committed Jan 6, 2024
1 parent fd43734 commit a959bc7
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 10 deletions.
19 changes: 19 additions & 0 deletions docs/feature_pointing_device.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,29 @@ The Analog Joystick is an analog (ADC) driven sensor. There are a variety of jo
| `ANALOG_JOYSTICK_Y_AXIS_PIN` | (Required) The pin used for the horizontal/Y axis. | _not defined_ |
| `ANALOG_JOYSTICK_AXIS_MIN` | (Optional) Sets the lower range to be considered movement. | `0` |
| `ANALOG_JOYSTICK_AXIS_MAX` | (Optional) Sets the upper range to be considered movement. | `1023` |
| `ANALOG_JOYSTICK_AUTO_AXIS` | (Optional) Sets ranges to be considered movement automatically. | _not defined_ |
| `ANALOG_JOYSTICK_SPEED_REGULATOR` | (Optional) The divisor used to slow down movement. (lower makes it faster) | `20` |
| `ANALOG_JOYSTICK_READ_INTERVAL` | (Optional) The interval in milliseconds between reads. | `10` |
| `ANALOG_JOYSTICK_SPEED_MAX` | (Optional) The maximum value used for motion. | `2` |
| `ANALOG_JOYSTICK_CLICK_PIN` | (Optional) The pin wired up to the press switch of the analog stick. | _not defined_ |
| `ANALOG_JOYSTICK_WEIGHTS` | (Optional) Use custom weights for lever positions. | _not defined_ |
| `ANALOG_JOYSTICK_CUTOFF` | (Optional) Cut off movement when joystick returns to start position. | _not defined_ |

If `ANALOG_JOYSTICK_AUTO_AXIS` is used, then `ANALOG_JOYSTICK_AXIS_MIN` and `ANALOG_JOYSTICK_AXIS_MAX` are ignored.

By default analog joystick implementation uses `x^2` weighting for lever positions. `ANALOG_JOYSTICK_WEIGHTS` allows to experiment with different configurations that might feel better.

E.g. This is weights for `((x-0.4)^3+0.064)/0.282`:

```c
#define ANALOG_JOYSTICK_WEIGHTS {0,2,4,5,7,8,9,10,12,13,14,15,15,16,17,18,18,19,19,20,20,21,21,21,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,25,25,25,26,26,26,27,28,28,29,29,30,31,32,33,34,35,36,37,38,40,41,43,44,46,48,49,51,53,56,58,60,62,65,68,70,73,76,79,82,85,89,92,96,100}
```
You can use following JS code to generate weights for different formulas:
```js
JSON.stringify(Array.from(Array(101).keys()).map(x => Math.ceil((((x/100-0.4)**3+0.064)/0.282*100))))
```

### Azoteq IQS5XX Trackpad

Expand Down
68 changes: 58 additions & 10 deletions drivers/sensors/analog_joystick.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,28 @@
#include <stdlib.h>

// Set Parameters
#ifndef ANALOG_JOYSTICK_AUTO_AXIS
uint16_t minAxisValue = ANALOG_JOYSTICK_AXIS_MIN;
uint16_t maxAxisValue = ANALOG_JOYSTICK_AXIS_MAX;
#else
int16_t minAxisValues[2];
int16_t maxAxisValues[2];
#endif

uint8_t maxCursorSpeed = ANALOG_JOYSTICK_SPEED_MAX;
uint8_t speedRegulator = ANALOG_JOYSTICK_SPEED_REGULATOR; // Lower Values Create Faster Movement

#ifdef ANALOG_JOYSTICK_WEIGHTS
int8_t weights[101] = ANALOG_JOYSTICK_WEIGHTS;
#endif

int16_t xOrigin, yOrigin;

uint16_t lastCursor = 0;

int16_t axisCoordinate(pin_t pin, uint16_t origin) {
uint8_t prevValues[2] = {0, 0};

int16_t axisCoordinate(pin_t pin, uint16_t origin, uint8_t axis) {
int8_t direction;
int16_t distanceFromOrigin;
int16_t range;
Expand All @@ -43,12 +54,27 @@ int16_t axisCoordinate(pin_t pin, uint16_t origin) {
return 0;
} else if (origin > position) {
distanceFromOrigin = origin - position;
range = origin - minAxisValue;
direction = -1;
#ifdef ANALOG_JOYSTICK_AUTO_AXIS
if (position < minAxisValues[axis]) {
minAxisValues[axis] = position;
}
range = origin - minAxisValues[axis];
#else
range = origin - minAxisValue;
#endif
direction = -1;
} else {
distanceFromOrigin = position - origin;
range = maxAxisValue - origin;
direction = 1;

#ifdef ANALOG_JOYSTICK_AUTO_AXIS
if (position > maxAxisValues[axis]) {
maxAxisValues[axis] = position;
}
range = maxAxisValues[axis] - origin;
#else
range = maxAxisValue - origin;
#endif
direction = 1;
}

float percent = (float)distanceFromOrigin / range;
Expand All @@ -62,23 +88,38 @@ int16_t axisCoordinate(pin_t pin, uint16_t origin) {
}
}

int8_t axisToMouseComponent(pin_t pin, int16_t origin, uint8_t maxSpeed) {
int16_t coordinate = axisCoordinate(pin, origin);
int8_t axisToMouseComponent(pin_t pin, int16_t origin, uint8_t maxSpeed, uint8_t axis) {
int16_t coordinate = axisCoordinate(pin, origin, axis);
int8_t result;
#ifndef ANALOG_JOYSTICK_WEIGHTS
if (coordinate != 0) {
float percent = (float)coordinate / 100;
return percent * maxCursorSpeed * (abs(coordinate) / speedRegulator);
result = percent * maxCursorSpeed * (abs(coordinate) / speedRegulator);
} else {
return 0;
}
#else
result = weights[abs(coordinate)] * (coordinate < 0 ? -1 : 1) * maxCursorSpeed / speedRegulator;
#endif

#ifdef ANALOG_JOYSTICK_CUTOFF
uint8_t pv = prevValues[axis];
prevValues[axis] = abs(result);
if (pv > abs(result)) {
return 0;
}
#endif

return result;
}

report_analog_joystick_t analog_joystick_read(void) {
report_analog_joystick_t report = {0};

if (timer_elapsed(lastCursor) > ANALOG_JOYSTICK_READ_INTERVAL) {
lastCursor = timer_read();
report.x = axisToMouseComponent(ANALOG_JOYSTICK_X_AXIS_PIN, xOrigin, maxCursorSpeed);
report.y = axisToMouseComponent(ANALOG_JOYSTICK_Y_AXIS_PIN, yOrigin, maxCursorSpeed);
report.x = axisToMouseComponent(ANALOG_JOYSTICK_X_AXIS_PIN, xOrigin, maxCursorSpeed, 0);
report.y = axisToMouseComponent(ANALOG_JOYSTICK_Y_AXIS_PIN, yOrigin, maxCursorSpeed, 1);
}
#ifdef ANALOG_JOYSTICK_CLICK_PIN
report.button = !readPin(ANALOG_JOYSTICK_CLICK_PIN);
Expand All @@ -93,4 +134,11 @@ void analog_joystick_init(void) {
// Account for drift
xOrigin = analogReadPin(ANALOG_JOYSTICK_X_AXIS_PIN);
yOrigin = analogReadPin(ANALOG_JOYSTICK_Y_AXIS_PIN);

#ifdef ANALOG_JOYSTICK_AUTO_AXIS
minAxisValues[0] = xOrigin - 100;
minAxisValues[1] = yOrigin - 100;
maxAxisValues[0] = xOrigin + 100;
maxAxisValues[1] = yOrigin + 100;
#endif
}

0 comments on commit a959bc7

Please sign in to comment.