Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for POINTING_DEVICE_DRIVER #11075

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions common_features.mk
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,28 @@ ifeq ($(strip $(MOUSEKEY_ENABLE)), yes)
SRC += $(QUANTUM_DIR)/mousekey.c
endif


VALID_POINTING_DEVICE_DRIVER_TYPES := custom pwm3360 pimoroni_trackball
POINTING_DEVICE_DRIVER ?= custom
ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
OPT_DEFS += -DPOINTING_DEVICE_ENABLE
OPT_DEFS += -DMOUSE_ENABLE
SRC += $(QUANTUM_DIR)/pointing_device.c
ifeq ($(filter $(POINTING_DEVICE_DRIVER),$(VALID_POINTING_DEVICE_DRIVER_TYPES)),)
$(error POINTING_DEVICE_DRIVER="$(POINTING_DEVICE_DRIVER)" is not a valid pointing device type)
else
OPT_DEFS += -DPOINTING_DEVICE_ENABLE
OPT_DEFS += -DMOUSE_ENABLE
SRC += $(QUANTUM_DIR)/pointing_device.c
VPATH += drivers/optical
SRC += $(QUANTUM_DIR)/pointing_device_drivers.c
ifeq ($(strip $(POINTING_DEVICE_DRIVER)), pmw3360)
QUANTUM_LIB_SRC += spi_master.c
SRC += pmw3360.c
else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), pimoroni_trackball)
QUANTUM_LIB_SRC += i2c_master.c
SRC += pimoroni_trackball.c
endif
OPT_DEFS += -DPOINTING_DEVICE_DRIVER_$(strip $(POINTING_DEVICE_DRIVER))

endif
endif

VALID_EEPROM_DRIVER_TYPES := vendor custom transient i2c spi
Expand Down
39 changes: 39 additions & 0 deletions docs/optical_sensor_driver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Optical Sensor Driver

## PixArt PMW3360 Optical Sensor

The PixArt PMW3360 Motion Sensor is an optical sensor that is commonly used in mice and trackballs, but can be used for other motion input devices, as well. But it works exceptionally well with QMK's Pointing Device feature.

The PMW3360 Motion Sensor interfaces via SPI, and does require SPI to be configured properly, as well. Details on how to do so are in the [SPI Driver doc](spi_driver.md).

### Configuration Options

`config.h` override | Description | AVR Default
-------------------------------------|-------------------------------------------------------------------------------|--------------
`#define SPI_DIVISOR` | The divisor used (docs target 2.0MHz) | `8`
`#define SS_CS_PIN` | The chip select pin used to "select" the PMW3360 sensor. Can be any GPIO pin. | `hi`
`#define PMW3360_FIRMWARE_H` | If you have a custom firmware blob for the sensor, you can specify it here. | _not defined_
`#define PMW3360_CPI` | Sets the default CPI/DPI for the sensor (any value between 100 and 12000). | `1600`
`#define ROTATIONAL_TRANSFORM_ANGLE` | Sets the rotation for the sensor (between -30 and 30) in degrees. | _not defined_

If you include a custom firmware blob, you must include these settings:

Setting | Description | Default
----------------------------------------|---------------------------------|--------------
`#define PMW3360_PRODUCT_ID` | Product ID of firmware | `0x42`
`#define PMW3360_INVERSE_PRODUCT_ID` | Bitwise invesion of Product ID | `0xBD`
`#define PMW3360_SROM_VERSION` | Firmware version | `0x04`
`const uint16_t firmware_length` | Length of array for firmware |
`const uint8_t PROGMEM firmware_data[]` | array of firmware data |

### Functions

#### `void pmw3360_spi_init(void)`

This initializes both SPI, in general, and the PMW3360 sensor. It also uploads the firmware, and verifies the firmware signature (to ensure proper upload).

#### `report_pmw_t pmw3360_read_burst(void)`

This reads from the sensor, and returns motion data.

Mostly, the dx and dy data structures are what you want to read.
10 changes: 3 additions & 7 deletions drivers/avr/spi_master.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,15 @@ typedef int16_t spi_status_t;
#ifdef __cplusplus
extern "C" {
#endif
void spi_init(void);

bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Member Author

@drashna drashna Feb 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still there, just ... formatted, below. yay autoformat

void spi_init(void);
bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor);
spi_status_t spi_write(uint8_t data);

spi_status_t spi_read(void);

spi_status_t spi_transmit(const uint8_t *data, uint16_t length);

spi_status_t spi_receive(uint8_t *data, uint16_t length);
void spi_stop(void);

void spi_stop(void);
#ifdef __cplusplus
}
#endif
10 changes: 3 additions & 7 deletions drivers/chibios/spi_master.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,15 @@ typedef int16_t spi_status_t;
#ifdef __cplusplus
extern "C" {
#endif
void spi_init(void);

bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor);

void spi_init(void);
bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor);
spi_status_t spi_write(uint8_t data);

spi_status_t spi_read(void);

spi_status_t spi_transmit(const uint8_t *data, uint16_t length);

spi_status_t spi_receive(uint8_t *data, uint16_t length);
void spi_stop(void);

void spi_stop(void);
#ifdef __cplusplus
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,36 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifdef POINTING_DEVICE_ENABLE

#include "pmw3360.h"
#include "pmw3360_firmware.h"
#ifndef PMW3360_FIRMWARE_H
# include "pmw3360_firmware.h"
#endif

#ifdef CONSOLE_ENABLE
# include "print.h"
#endif
bool _inBurst = false;

#ifndef PMW_CPI
# define PMW_CPI 1600
#ifndef PMW3360_CPI
# define PMW3360_CPI 1600
#endif
#ifndef SPI_DIVISOR
# define SPI_DIVISOR 2

#ifdef __AVR__
# ifndef SPI_DIVISOR
# define SPI_DIVISOR 8
# endif
# ifndef SPI_CS_PIN
# define SPI_CS_PIN SPI_SS_PIN
# endif
#else
# ifndef SPI_DIVISOR
# error SPI_DIVISOR not defined. Please define the divider.
# endif
# ifndef SPI_CS_PIN
# error No Cable Select pin defined. Please define SPI_CS_PIN
# endif
#endif

#ifndef ROTATIONAL_TRANSFORM_ANGLE
# define ROTATIONAL_TRANSFORM_ANGLE 0x00
#endif
Expand All @@ -40,24 +54,25 @@ bool _inBurst = false;
void print_byte(uint8_t byte) { dprintf("%c%c%c%c%c%c%c%c|", (byte & 0x80 ? '1' : '0'), (byte & 0x40 ? '1' : '0'), (byte & 0x20 ? '1' : '0'), (byte & 0x10 ? '1' : '0'), (byte & 0x08 ? '1' : '0'), (byte & 0x04 ? '1' : '0'), (byte & 0x02 ? '1' : '0'), (byte & 0x01 ? '1' : '0')); }
#endif

// some advanced SPI functions. Would port to spi_master, but some of this is VERY dependant on pmw3389 code

bool spi_start_adv(void) {
bool status = spi_start(SPI_SS_PIN, false, 3, SPI_DIVISOR);
bool spi_start_pmw3360(void) {
bool status = spi_start(SPI_CS_PIN, false, 3, SPI_DIVISOR);
wait_us(1);
return status;
}

void spi_stop_adv(void) {
void spi_stop_pmw3360(void) {
wait_us(1);
spi_stop();
}

spi_status_t spi_write_adv(uint8_t reg_addr, uint8_t data) {
spi_status_t spi_write_pmw3360(uint8_t reg_addr, uint8_t data) {
if (reg_addr != REG_Motion_Burst) {
_inBurst = false;
}

spi_start_adv();
spi_start_pmw3360();
// send address of the register, with MSBit = 1 to indicate it's a write
spi_status_t status = spi_write(reg_addr | 0x80);
status = spi_write(data);
Expand All @@ -71,8 +86,8 @@ spi_status_t spi_write_adv(uint8_t reg_addr, uint8_t data) {
return status;
}

uint8_t spi_read_adv(uint8_t reg_addr) {
spi_start_adv();
uint8_t spi_read_pmw3360(uint8_t reg_addr) {
spi_start_pmw3360();
// send adress of the register, with MSBit = 0 to indicate it's a read
spi_write(reg_addr & 0x7f);

Expand All @@ -88,63 +103,63 @@ uint8_t spi_read_adv(uint8_t reg_addr) {
return data;
}

void pmw_set_cpi(uint16_t cpi) {
void pmw3360_set_cpi(uint16_t cpi) {
int cpival = constrain((cpi / 100) - 1, 0, 0x77); // limits to 0--119

spi_start_adv();
spi_write_adv(REG_Config1, cpival);
spi_start_pmw3360();
spi_write_pmw3360(REG_Config1, cpival);
spi_stop();
}

bool pmw_spi_init(void) {
bool pmw3360_spi_init(void) {
spi_init();
_inBurst = false;

spi_stop();
spi_start_adv();
spi_start_pmw3360();
spi_stop();

spi_write_adv(REG_Shutdown, 0xb6); // Shutdown first
spi_write_pmw3360(REG_Shutdown, 0xb6); // Shutdown first
wait_ms(300);

spi_start_adv();
spi_start_pmw3360();
wait_us(40);
spi_stop_adv();
spi_stop_pmw3360();
wait_us(40);

spi_write_adv(REG_Power_Up_Reset, 0x5a);
spi_write_pmw3360(REG_Power_Up_Reset, 0x5a);
wait_ms(50);

spi_read_adv(REG_Motion);
spi_read_adv(REG_Delta_X_L);
spi_read_adv(REG_Delta_X_H);
spi_read_adv(REG_Delta_Y_L);
spi_read_adv(REG_Delta_Y_H);
spi_read_pmw3360(REG_Motion);
spi_read_pmw3360(REG_Delta_X_L);
spi_read_pmw3360(REG_Delta_X_H);
spi_read_pmw3360(REG_Delta_Y_L);
spi_read_pmw3360(REG_Delta_Y_H);

pmw_upload_firmware();
pmw3360_upload_firmware();

spi_stop_adv();
spi_stop_pmw3360();

wait_ms(10);
pmw_set_cpi(PMW_CPI);
pmw3360_set_cpi(PMW3360_CPI);

wait_ms(1);

return pmw_check_signature();
return pmw3360_check_signature();
}

void pmw_upload_firmware(void) {
spi_write_adv(REG_Config2, 0x00);
void pmw3360_upload_firmware(void) {
spi_write_pmw3360(REG_Config2, 0x00);

spi_write_adv(REG_Angle_Tune, constrain(ROTATIONAL_TRANSFORM_ANGLE, -30, 30));
spi_write_pmw3360(REG_Angle_Tune, constrain(ROTATIONAL_TRANSFORM_ANGLE, -30, 30));

spi_write_adv(REG_SROM_Enable, 0x1d);
spi_write_pmw3360(REG_SROM_Enable, 0x1d);

wait_ms(10);

spi_write_adv(REG_SROM_Enable, 0x18);
spi_write_pmw3360(REG_SROM_Enable, 0x18);

spi_start_adv();
spi_start_pmw3360();
spi_write(REG_SROM_Load_Burst | 0x80);
wait_us(15);

Expand All @@ -156,31 +171,31 @@ void pmw_upload_firmware(void) {
}
wait_us(200);

spi_read_adv(REG_SROM_ID);
spi_read_pmw3360(REG_SROM_ID);

spi_write_adv(REG_Config2, 0x00);
spi_write_pmw3360(REG_Config2, 0x00);

spi_stop();
wait_ms(10);
}

bool pmw_check_signature(void) {
uint8_t pid = spi_read_adv(REG_Product_ID);
uint8_t iv_pid = spi_read_adv(REG_Inverse_Product_ID);
uint8_t SROM_ver = spi_read_adv(REG_SROM_ID);
return (pid == 0x42 && iv_pid == 0xBD && SROM_ver == 0x04); // signature for SROM 0x04
bool pmw3360_check_signature(void) {
uint8_t pid = spi_read_pmw3360(REG_Product_ID);
uint8_t iv_pid = spi_read_pmw3360(REG_Inverse_Product_ID);
uint8_t SROM_ver = spi_read_pmw3360(REG_SROM_ID);
return (pid == PMW3360_PRODUCT_ID && iv_pid == PMW3360_INVERSE_PRODUCT_ID && SROM_ver == PMW3360_SROM_VERSION);
}

report_pmw_t pmw_read_burst(void) {
report_pmw_t pmw3360_read_burst(void) {
if (!_inBurst) {
#ifdef CONSOLE_ENABLE
dprintf("burst on");
#endif
spi_write_adv(REG_Motion_Burst, 0x00);
spi_write_pmw3360(REG_Motion_Burst, 0x00);
_inBurst = true;
}

spi_start_adv();
spi_start_pmw3360();
spi_write(REG_Motion_Burst);
wait_us(35); // waits for tSRAD

Expand Down Expand Up @@ -224,5 +239,3 @@ report_pmw_t pmw_read_burst(void) {

return data;
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,12 @@ bool spi_start_adv(void);
void spi_stop_adv(void);
spi_status_t spi_write_adv(uint8_t reg_addr, uint8_t data);
uint8_t spi_read_adv(uint8_t reg_addr);
bool pmw_spi_init(void);
void pmw_set_cpi(uint16_t cpi);
void pmw_upload_firmware(void);
bool pmw_check_signature(void);
report_pmw_t pmw_read_burst(void);

bool pmw3360_spi_init(void);
void pmw3360_set_cpi(uint16_t cpi);
void pmw3360_upload_firmware(void);
bool pmw3360_check_signature(void);
report_pmw_t pmw3360_read_burst(void);


#define degToRad(angleInDegrees) ((angleInDegrees)*M_PI / 180.0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/* original source from https://github.com/mrjohnk/PMW3360DM-T2QU */

#pragma once

#define PMW3360_PRODUCT_ID 0x42
drashna marked this conversation as resolved.
Show resolved Hide resolved
#define PMW3360_INVERSE_PRODUCT_ID 0xBD
#define PMW3360_SROM_VERSION 0x04
// clang-format off
// Firmware Blob foor PMW3360
const uint16_t firmware_length = 4094;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ __attribute__((weak)) void process_mouse_user(report_mouse_t* mouse_report, int1
}

__attribute__((weak)) void process_mouse(report_mouse_t* mouse_report) {
report_pmw_t data = pmw_read_burst();
report_pmw_t data = pmw3360_read_burst();
if (data.isOnSurface && data.isMotion) {
// Reset timer if stopped moving
if (!data.isMotion) {
Expand Down Expand Up @@ -141,7 +141,7 @@ void keyboard_pre_init_kb(void) {
void pointing_device_init(void) {
if (!is_keyboard_left()) {
// initialize ball sensor
pmw_spi_init();
pmw3360_spi_init();
}
trackball_set_cpi(dpi_array[keyboard_config.dpi_config]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/* serial.c configuration for split keyboard */
#undef SOFT_SERIAL_PIN
#define SOFT_SERIAL_PIN D2

#define SPI_DIVISOR 8
#define SPI_CS_PIN B0
Loading