[MERGE] Add UART based SpaceMouse Module support (22519)
This commit is contained in:
parent
cf84addeed
commit
bf92d0ef70
@ -120,7 +120,7 @@ ifeq ($(strip $(MOUSEKEY_ENABLE)), yes)
|
||||
MOUSE_ENABLE := yes
|
||||
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 ($(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)
|
||||
@ -154,6 +154,8 @@ ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
|
||||
SRC += $(QUANTUM_DIR)/pointing_device/pointing_device_gestures.c
|
||||
else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), pimoroni_trackball)
|
||||
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),)
|
||||
SPI_DRIVER_REQUIRED = yes
|
||||
SRC += drivers/sensors/pmw33xx_common.c
|
||||
|
@ -368,6 +368,34 @@ report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) {
|
||||
|
||||
```
|
||||
|
||||
### SpaceMouse Module (UART)
|
||||
|
||||
To use the SpaceMouse module to control the pointer, add this to your `rules.mk`
|
||||
|
||||
```make
|
||||
POINTING_DEVICE_DRIVER = spacemouse_module
|
||||
```
|
||||
|
||||
The SpaceMouse Module is a UART driven sensor, with 6 axes of motion.
|
||||
|
||||
| Setting (`config.h`) | Description | Default |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------------------- | ------------- |
|
||||
| `SPACEMOUSE_USE_TILT_AXIS` | Uses the tilt axes for movement rather than the shift axes. | _not_defined_ |
|
||||
|
||||
|
||||
By default, not all of the axes are utilized. If you would like to use more of them, you can do so by using this custom function, which translates the data from the SpaceMouse Module to the pointing device report.
|
||||
|
||||
```c
|
||||
void spacemouse_module_handle_axes(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);
|
||||
|
||||
mouse_report->buttons = pointing_device_handle_buttons(mouse_report->buttons, (space_mouse_data->z < -10), KC_BTN1);
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Driver
|
||||
|
||||
If you have a sensor type that isn't supported above, a custom option is available by adding the following to your `rules.mk`
|
||||
|
133
drivers/sensors/spacemouse_module.c
Normal file
133
drivers/sensors/spacemouse_module.c
Normal file
@ -0,0 +1,133 @@
|
||||
// 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"
|
||||
|
||||
// datasheet available at https://3dconnexion.com/cn/wp-content/uploads/2020/02/HW-Spec-3DX-700039_Rev001_serial.pdf
|
||||
|
||||
/* Datasheet UART settings specify:
|
||||
* baud rate: 38400
|
||||
* data bits: 8
|
||||
* parity: none
|
||||
* stop bits: 1
|
||||
* data rate: max 100/s
|
||||
*/
|
||||
|
||||
#define SPACEMOUSE_BAUD_RATE 38400
|
||||
#define SPACEMOUSE_AXIS_COUNT 6
|
||||
#define SPACEMOUSE_LENGTH_DATA (2 * SPACEMOUSE_AXIS_COUNT)
|
||||
#define SPACEMOUSE_INPUT_OFFSET (8192)
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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, 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.tilt_y = (int16_t)((payload[6] << 7) + payload[7]) - SPACEMOUSE_INPUT_OFFSET;
|
||||
data.twist = (int16_t)((payload[8] << 7) + payload[9]) - SPACEMOUSE_INPUT_OFFSET;
|
||||
data.tilt_x = (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;
|
||||
}
|
20
drivers/sensors/spacemouse_module.h
Normal file
20
drivers/sensors/spacemouse_module.h
Normal file
@ -0,0 +1,20 @@
|
||||
// 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>
|
||||
|
||||
typedef struct {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t z;
|
||||
int16_t twist;
|
||||
int16_t tilt_x;
|
||||
int16_t tilt_y;
|
||||
} 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 "drivers/sensors/pmw33xx_common.h"
|
||||
# define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW
|
||||
#elif defined(POINTING_DEVICE_DRIVER_spacemouse_module)
|
||||
# include "drivers/sensors/spacemouse_module.h"
|
||||
#else
|
||||
void pointing_device_driver_init(void);
|
||||
report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report);
|
||||
|
@ -492,6 +492,44 @@ const pointing_device_driver_t pointing_device_driver = {
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
#elif defined(POINTING_DEVICE_DRIVER_spacemouse_module)
|
||||
|
||||
static bool spacemouse_present = false;
|
||||
|
||||
__attribute__((weak)) void spacemouse_module_handle_axes(spacemouse_data_t* spacemouse_data, report_mouse_t* mouse_report) {
|
||||
# ifdef SPACEMOUSE_USE_TILT_AXIS
|
||||
mouse_report->x = CONSTRAIN_HID_XY(spacemouse_data->tilt_x);
|
||||
mouse_report->y = CONSTRAIN_HID_XY(spacemouse_data->tilt_y);
|
||||
# else
|
||||
mouse_report->x = CONSTRAIN_HID_XY(spacemouse_data->x);
|
||||
mouse_report->y = CONSTRAIN_HID_XY(spacemouse_data->y);
|
||||
# endif
|
||||
}
|
||||
|
||||
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.twist || data.tilt_x || data.tilt_y) {
|
||||
pd_dprintf("Raw ] X: %d, Y: %d, Z: %d, twist: %d, tilt X: %d, tilt Y: %d\n", data.x, data.y, data.z, data.twist, data.tilt_x, data.tilt_y);
|
||||
}
|
||||
spacemouse_module_handle_axes(&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
|
||||
__attribute__((weak)) void pointing_device_driver_init(void) {}
|
||||
__attribute__((weak)) report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) {
|
||||
|
Loading…
Reference in New Issue
Block a user