[Pointing] Add space mouse module uart support

This commit is contained in:
Drashna Jael're 2023-11-20 16:57:19 -08:00
parent e381f91c6e
commit 300fb2bc1d
No known key found for this signature in database
GPG Key ID: DBA1FD3A860D1B11
5 changed files with 226 additions and 1 deletions

View File

@ -120,7 +120,7 @@ ifeq ($(strip $(MOUSEKEY_ENABLE)), yes)
MOUSE_ENABLE := yes MOUSE_ENABLE := yes
endif endif
VALID_POINTING_DEVICE_DRIVER_TYPES := adns5050 adns9800 analog_joystick azoteq_iqs5xx cirque_pinnacle_i2c cirque_pinnacle_spi paw3204 pmw3320 pmw3360 pmw3389 pimoroni_trackball custom VALID_POINTING_DEVICE_DRIVER_TYPES := adns5050 adns9800 analog_joystick azoteq_iqs5xx cirque_pinnacle_i2c cirque_pinnacle_spi paw3204 pmw3320 pmw3360 pmw3389 pimoroni_trackball spacemouse_module custom
ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes) ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
ifeq ($(filter $(POINTING_DEVICE_DRIVER),$(VALID_POINTING_DEVICE_DRIVER_TYPES)),) ifeq ($(filter $(POINTING_DEVICE_DRIVER),$(VALID_POINTING_DEVICE_DRIVER_TYPES)),)
$(call CATASTROPHIC_ERROR,Invalid POINTING_DEVICE_DRIVER,POINTING_DEVICE_DRIVER="$(POINTING_DEVICE_DRIVER)" is not a valid pointing device type) $(call CATASTROPHIC_ERROR,Invalid POINTING_DEVICE_DRIVER,POINTING_DEVICE_DRIVER="$(POINTING_DEVICE_DRIVER)" is not a valid pointing device type)
@ -154,6 +154,8 @@ ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
SRC += $(QUANTUM_DIR)/pointing_device/pointing_device_gestures.c SRC += $(QUANTUM_DIR)/pointing_device/pointing_device_gestures.c
else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), pimoroni_trackball) else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), pimoroni_trackball)
I2C_DRIVER_REQUIRED = yes I2C_DRIVER_REQUIRED = yes
else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), spacemouse_module)
UART_DRIVER_REQUIRED = yes
else ifneq ($(filter $(strip $(POINTING_DEVICE_DRIVER)),pmw3360 pmw3389),) else ifneq ($(filter $(strip $(POINTING_DEVICE_DRIVER)),pmw3360 pmw3389),)
SPI_DRIVER_REQUIRED = yes SPI_DRIVER_REQUIRED = yes
SRC += drivers/sensors/pmw33xx_common.c SRC += drivers/sensors/pmw33xx_common.c

View File

@ -0,0 +1,138 @@
// Copyright 2023 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "spacemouse_module.h"
#include "pointing_device_internal.h"
#include "uart.h"
// REQUEST_DATA (the important part)
// Function: requests position data from the 3D-Sensor Command: 172 (0xAC)
// Returns: 16 bytes data
// Structure: B1 B2 ... B16
// Byte 1: start-byte 0x96 (150 decimal); every data set starts with this byte Byte 2: high byte of X value
// Byte 3: low byte of X value
// Byte 4: high byte of Y value
// Byte 5: low byte of Y value
// Byte 6: high byte of Z value
// Byte 7: low byte of Z value
// Byte 8: high byte of A value (X rotation) Byte 9: low byte of A value (X rotation) Byte 10: high byte of B value (Y rotation) Byte 11: low byte of B value (Y rotation) Byte 12: high byte of C value (Z rotation) Byte 13: low byte of C value (Z rotation) Byte 14: high byte of Checksum
// Byte 15: low byte of Checksum
// Byte 16: end-byte 0x8D; every response ends with this byte
//
// X, Y, Z, A, B, C values and the Checksum are transmitted as unsigned 14-Bit values. This is due to the fact, that the MSB of payload data is always cleared (logic 0).
// Calculating a value:
// high byte (X) low byte (X)
// 14-bit value (unsigned)
// Xvalue = (high byte (X) * 128 + low byte (X)) - 8192
// Transmitted Checksum:
// Checksumtrans = (high byte (Checksumtrans) * 128 + low byte (Checksumtrans))
// Calculating the Checksum:
// Checksumcalc = (Byte1 + Byte2 + ... + Byte13) & 0x3FFF.
// By masking the Checksum with 0x3FFF (logic AND operation), the value is reduced to a 14-Bit value.
// The value range for X, Y, Z, A, B, C values is -350 up to +350.
#define SPACEMOUSE_INPUT_OFFSET (8192)
bool spacemouse_send_command(uint8_t cmd) {
uart_write(cmd);
uint8_t buf[2];
uart_receive(buf, 2);
return (buf[0] == cmd && buf[1] == SPACEMOUSE_CMD_END);
}
/**
* @brief Set the zero position of the module
*
* @return true command ran successfully
* @return false command failed
*/
bool spacemouse_cmd_set_zero_position(void) {
return spacemouse_send_command(SPACEMOUSE_CMD_SET_ZERO_POSITION);
}
/**
* @brief Starts automatic transmission of data, at 30ms invervals
* Automatic data transmission happens at 30 ms intervals, but device can be polled at 100/s or 10ms intervals
* Since 10ms is what pointing device polling defaults to, we don't need need the stream command, but
* it is here for completeness, in case somebody wants to implement it elsewhere.
*
* @return true command ran successfully
* @return false command failed
*/
bool spacemouse_cmd_enable_stream(void) {
return spacemouse_send_command(SPACEMOUSE_CMD_AUTO_DATA_ON);
}
/**
* @brief Stops automatic transmission of data, at 30ms invervals
*
* @return true command ran successfully
* @return false command failed
*/
bool spacemouse_cmd_disable_stream(void) {
return spacemouse_send_command(SPACEMOUSE_CMD_AUTO_DATA_OFF);
}
/**
* @brief Initialize UART connection and send command to zero out starting position.
*
* @return true
* @return false
*/
bool spacemouse_init(void) {
uart_init(SPACEMOUSE_BAUD_RATE);
// position is zeroed out during device start, but re-zero it out to ensure that the
// device is present and working properly.
return spacemouse_cmd_set_zero_position();
}
spacemouse_data_t spacemouse_get_data(void) {
spacemouse_data_t data = {0};
uint8_t retry_attempts = 0, index = 0, payload[SPACEMOUSE_LENGTH_DATA + SPACEMOUSE_LENGTH_CHECKSUM] = {0};
uint16_t checksum = 0, checksum_received = 0;
bool has_started = false;
uart_write(SPACEMOUSE_CMD_REQUEST_DATA);
while (retry_attempts <= 15) {
uint8_t buf = uart_read();
if (buf == SPACEMOUSE_DATA_REQUEST_START) {
has_started = true;
checksum = buf;
retry_attempts = 0;
continue;
} else if (has_started) {
if (buf == SPACEMOUSE_CMD_END) {
break;
} else {
if (index >= SPACEMOUSE_LENGTH_DATA) {
if (index == SPACEMOUSE_LENGTH_DATA) {
checksum_received = buf << 7;
} else {
checksum_received += buf;
}
} else {
payload[index] = buf;
checksum += buf;
}
index++;
}
}
retry_attempts++;
};
checksum &= 0x3FFF;
if (has_started) {
if (checksum_received == checksum) {
data.x = (int16_t)((payload[0] << 7) + payload[1]) - SPACEMOUSE_INPUT_OFFSET;
data.z = (int16_t)((payload[2] << 7) + payload[3]) - SPACEMOUSE_INPUT_OFFSET;
data.y = (int16_t)((payload[4] << 7) + payload[5]) - SPACEMOUSE_INPUT_OFFSET;
data.a = (int16_t)((payload[6] << 7) + payload[7]) - SPACEMOUSE_INPUT_OFFSET;
data.b = (int16_t)((payload[8] << 7) + payload[9]) - SPACEMOUSE_INPUT_OFFSET;
data.c = (int16_t)((payload[10] << 7) + payload[11]) - SPACEMOUSE_INPUT_OFFSET;
} else {
pd_dprintf("Space Mouse Checksum error: 0x%04x != 0x%04x \n", checksum_received, checksum);
}
}
return data;
}

View File

@ -0,0 +1,48 @@
// Copyright 2023 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <stdint.h>
#include <stdbool.h>
// Datasheet UART settings specify:
// - 38400 baud
// - 8 data bits
// - 1 stop bit
// - no parity
// - 100/s data rate
#ifndef SPACEMOUSE_BAUD_RATE
# define SPACEMOUSE_BAUD_RATE 38400
#endif
#define SPACEMOUSE_AXIS_COUNT 6
#define SPACEMOUSE_LENGTH_HEADER 1
#define SPACEMOUSE_LENGTH_DATA (2 * SPACEMOUSE_AXIS_COUNT)
#define SPACEMOUSE_LENGTH_CHECKSUM 2
#define SPACEMOUSE_LENGTH_FOOTER 1
#define SPACEMOUSE_LENGTH_PACKET (SPACEMOUSE_LENGTH_HEADER + SPACEMOUSE_LENGTH_DATA + SPACEMOUSE_LENGTH_CHECKSUM + SPACEMOUSE_LENGTH_FOOTER)
enum spacemouse_commands {
SPACEMOUSE_CMD_REQUEST_DATA = 0xAC,
SPACEMOUSE_CMD_SET_ZERO_POSITION = 0xAD,
SPACEMOUSE_CMD_AUTO_DATA_ON = 0xAE,
SPACEMOUSE_CMD_AUTO_DATA_OFF = 0xAF,
SPACEMOUSE_CMD_END = 0x8D,
SPACEMOUSE_DATA_REQUEST_START = 0x96,
};
typedef struct {
int16_t x;
int16_t y;
int16_t z;
int16_t a;
int16_t b;
int16_t c;
} spacemouse_data_t;
bool spacemouse_send_command(uint8_t cmd);
bool spacemouse_init(void);
spacemouse_data_t spacemouse_get_data(void);

View File

@ -67,6 +67,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
# include "spi_master.h" # include "spi_master.h"
# include "drivers/sensors/pmw33xx_common.h" # include "drivers/sensors/pmw33xx_common.h"
# define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW # define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW
#elif defined(POINTING_DEVICE_DRIVER_spacemouse_module)
# include "drivers/sensors/spacemouse_module.h"
#else #else
void pointing_device_driver_init(void); void pointing_device_driver_init(void);
report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report); report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report);

View File

@ -492,6 +492,41 @@ const pointing_device_driver_t pointing_device_driver = {
}; };
// clang-format on // clang-format on
#elif defined(POINTING_DEVICE_DRIVER_spacemouse_module)
static bool spacemouse_present = false;
__attribute__((weak)) void spacemouse_module_handle_axises(spacemouse_data_t *spacemouse_data, report_mouse_t* mouse_report) {
mouse_report->x = CONSTRAIN_HID_XY(spacemouse_data->x);
mouse_report->y = CONSTRAIN_HID_XY(spacemouse_data->y);
// mouse_report->h = CONSTRAIN_HID(spacemouse_data->b);
// mouse_report->v = CONSTRAIN_HID(spacemouse_data->c);
}
static report_mouse_t spacemouse_get_report(report_mouse_t mouse_report) {
if (spacemouse_present) {
spacemouse_data_t data = spacemouse_get_data();
if (data.x || data.y || data.z || data.a || data.b || data.c) {
pd_dprintf("Raw ] X: %d, Y: %d, Z: %d, A: %d, B: %d, C: %d\n", data.x, data.y, data.z, data.a, data.b, data.c);
}
spacemouse_module_handle_axises(&data, &mouse_report);
}
return mouse_report;
}
static void init(void) {
spacemouse_present = spacemouse_init();
}
// clang-format off
const pointing_device_driver_t pointing_device_driver = {
.init = init,
.get_report = spacemouse_get_report,
.set_cpi = NULL,
.get_cpi = NULL
};
// clang-format on
#else #else
__attribute__((weak)) void pointing_device_driver_init(void) {} __attribute__((weak)) void pointing_device_driver_init(void) {}
__attribute__((weak)) report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) { __attribute__((weak)) report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) {