mirror of
https://github.com/qmk/qmk_firmware.git
synced 2025-07-21 07:02:01 +00:00
[Pointing] Add space mouse module uart support
This commit is contained in:
parent
e381f91c6e
commit
300fb2bc1d
@ -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
|
||||||
|
138
drivers/sensors/spacemouse_module.c
Normal file
138
drivers/sensors/spacemouse_module.c
Normal 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;
|
||||||
|
}
|
48
drivers/sensors/spacemouse_module.h
Normal file
48
drivers/sensors/spacemouse_module.h
Normal 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);
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user