[MERGE] Add UART based SpaceMouse Module support (22519)
This commit is contained in:
parent
54e78ad86f
commit
26ff67ff78
5 changed files with 222 additions and 1 deletions
169
drivers/sensors/spacemouse_module.c
Normal file
169
drivers/sensors/spacemouse_module.c
Normal file
|
@ -0,0 +1,169 @@
|
|||
// 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 "pointing_device.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;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
const pointing_device_driver_t pointing_device_driver = {
|
||||
.init = init,
|
||||
.get_report = spacemouse_get_report,
|
||||
.set_cpi = NULL,
|
||||
.get_cpi = NULL,
|
||||
};
|
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);
|
Loading…
Add table
Add a link
Reference in a new issue