From 56e3f06a26851976e559aacf7a096c61403304be Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Sun, 14 Nov 2021 22:03:24 -0800 Subject: Rework and expand Pointing Device support (#14343) Co-authored-by: Dasky <32983009+daskygit@users.noreply.github.com> --- common_features.mk | 39 ++- docs/feature_pointing_device.md | 202 +++++++++++++- drivers/sensors/adns5050.c | 157 ++++++----- drivers/sensors/adns5050.h | 75 +++-- drivers/sensors/adns9800.c | 175 ++++++------ drivers/sensors/adns9800.h | 42 ++- drivers/sensors/adns9800_srom_A6.h | 2 +- drivers/sensors/analog_joystick.c | 94 +++++++ drivers/sensors/analog_joystick.h | 51 ++++ drivers/sensors/cirque_pinnacle.c | 232 ++++++++++++++++ drivers/sensors/cirque_pinnacle.h | 74 +++++ drivers/sensors/cirque_pinnacle_i2c.c | 43 +++ drivers/sensors/cirque_pinnacle_spi.c | 52 ++++ drivers/sensors/pimoroni_trackball.c | 187 +++---------- drivers/sensors/pimoroni_trackball.h | 48 +++- drivers/sensors/pmw3360.c | 140 +++++----- drivers/sensors/pmw3360.h | 38 ++- drivers/sensors/pmw3360_firmware.h | 5 + drivers/sensors/pmw3389_firmware.h | 303 +++++++++++++++++++++ .../40percentclub/nano/keymaps/drashna/config.h | 22 ++ .../40percentclub/nano/keymaps/drashna/keymap.c | 95 ------- .../40percentclub/nano/keymaps/drashna/rules.mk | 11 +- .../handwired/tractyl_manuform/5x6_right/config.h | 1 + .../5x6_right/keymaps/drashna/keymap.c | 11 +- .../handwired/tractyl_manuform/5x6_right/rules.mk | 4 +- keyboards/handwired/tractyl_manuform/tm_sync.c | 3 +- .../handwired/tractyl_manuform/tractyl_manuform.c | 81 +----- keyboards/oddball/config.h | 3 + keyboards/oddball/keymaps/default/config.h | 1 - keyboards/oddball/keymaps/default/rules.mk | 1 + keyboards/oddball/keymaps/pmw3360/config.h | 1 - keyboards/oddball/keymaps/pmw3360/rules.mk | 1 + keyboards/oddball/oddball.c | 54 ++-- keyboards/oddball/optical_sensor/optical_sensor.h | 53 ---- keyboards/oddball/pmw/pmw.c | 226 --------------- keyboards/oddball/pmw/pmw.h | 35 --- keyboards/oddball/pmw/pmw3360_srom_0x04.h | 280 ------------------- keyboards/oddball/rules.mk | 4 - keyboards/ploopyco/mouse/mouse.c | 117 ++------ keyboards/ploopyco/mouse/mouse.h | 6 - keyboards/ploopyco/mouse/rules.mk | 5 +- keyboards/ploopyco/trackball/rules.mk | 5 +- keyboards/ploopyco/trackball/trackball.c | 105 ++----- keyboards/ploopyco/trackball/trackball.h | 6 - keyboards/ploopyco/trackball_mini/config.h | 6 + .../trackball_mini/keymaps/drag_scroll/keymap.c | 2 +- keyboards/ploopyco/trackball_mini/rules.mk | 6 +- keyboards/ploopyco/trackball_mini/trackball_mini.c | 108 ++------ keyboards/ploopyco/trackball_mini/trackball_mini.h | 5 - keyboards/ploopyco/trackball_nano/config.h | 6 + .../trackball_nano/keymaps/maddie/keymap.c | 22 +- keyboards/ploopyco/trackball_nano/rules.mk | 7 +- keyboards/ploopyco/trackball_nano/trackball_nano.c | 116 +------- keyboards/ploopyco/trackball_nano/trackball_nano.h | 13 - keyboards/tkw/grandiceps/rev2/rules.mk | 3 +- layouts/community/ergodox/drashna/keymap.c | 32 +-- quantum/pointing_device.c | 123 ++++++--- quantum/pointing_device.h | 59 ++++ quantum/pointing_device_drivers.c | 262 ++++++++++++++++++ quantum/quantum.c | 4 + quantum/quantum.h | 4 + 61 files changed, 2135 insertions(+), 1733 deletions(-) create mode 100644 drivers/sensors/analog_joystick.c create mode 100644 drivers/sensors/analog_joystick.h create mode 100644 drivers/sensors/cirque_pinnacle.c create mode 100644 drivers/sensors/cirque_pinnacle.h create mode 100644 drivers/sensors/cirque_pinnacle_i2c.c create mode 100644 drivers/sensors/cirque_pinnacle_spi.c create mode 100644 drivers/sensors/pmw3389_firmware.h create mode 100644 keyboards/40percentclub/nano/keymaps/drashna/config.h delete mode 100644 keyboards/oddball/keymaps/default/config.h create mode 100644 keyboards/oddball/keymaps/default/rules.mk delete mode 100644 keyboards/oddball/keymaps/pmw3360/config.h create mode 100644 keyboards/oddball/keymaps/pmw3360/rules.mk delete mode 100644 keyboards/oddball/optical_sensor/optical_sensor.h delete mode 100644 keyboards/oddball/pmw/pmw.c delete mode 100644 keyboards/oddball/pmw/pmw.h delete mode 100644 keyboards/oddball/pmw/pmw3360_srom_0x04.h create mode 100644 quantum/pointing_device_drivers.c diff --git a/common_features.mk b/common_features.mk index 7abdce302c..896fbb0129 100644 --- a/common_features.mk +++ b/common_features.mk @@ -107,10 +107,43 @@ ifeq ($(strip $(MOUSEKEY_ENABLE)), yes) SRC += $(QUANTUM_DIR)/mousekey.c endif +VALID_POINTING_DEVICE_DRIVER_TYPES := adns5050 adns9800 analog_joystick cirque_pinnacle_i2c cirque_pinnacle_spi pmw3360 pimoroni_trackball custom +POINTING_DEVICE_DRIVER ?= custom ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes) - OPT_DEFS += -DPOINTING_DEVICE_ENABLE - MOUSE_ENABLE := yes - 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 + MOUSE_ENABLE := yes + SRC += $(QUANTUM_DIR)/pointing_device.c + SRC += $(QUANTUM_DIR)/pointing_device_drivers.c + ifneq ($(strip $(POINTING_DEVICE_DRIVER)), custom) + SRC += drivers/sensors/$(strip $(POINTING_DEVICE_DRIVER)).c + OPT_DEFS += -DPOINTING_DEVICE_DRIVER_$(strip $(shell echo $(POINTING_DEVICE_DRIVER) | tr '[:lower:]' '[:upper:]')) + endif + OPT_DEFS += -DPOINTING_DEVICE_DRIVER_$(strip $(POINTING_DEVICE_DRIVER)) + ifeq ($(strip $(POINTING_DEVICE_DRIVER)), adns9800) + OPT_DEFS += -DSTM32_SPI -DHAL_USE_SPI=TRUE + QUANTUM_LIB_SRC += spi_master.c + else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), analog_joystick) + OPT_DEFS += -DSTM32_ADC -DHAL_USE_ADC=TRUE + LIB_SRC += analog.c + else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), cirque_pinnacle_i2c) + OPT_DEFS += -DSTM32_I2C -DHAL_USE_I2C=TRUE + SRC += drivers/sensors/cirque_pinnacle.c + QUANTUM_LIB_SRC += i2c_master.c + else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), cirque_pinnacle_spi) + OPT_DEFS += -DSTM32_SPI -DHAL_USE_SPI=TRUE + SRC += drivers/sensors/cirque_pinnacle.c + QUANTUM_LIB_SRC += spi_master.c + else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), pimoroni_trackball) + OPT_DEFS += -DSTM32_SPI -DHAL_USE_I2C=TRUE + QUANTUM_LIB_SRC += i2c_master.c + else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), pmw3360) + OPT_DEFS += -DSTM32_SPI -DHAL_USE_SPI=TRUE + QUANTUM_LIB_SRC += spi_master.c + endif + endif endif VALID_EEPROM_DRIVER_TYPES := vendor custom transient i2c spi diff --git a/docs/feature_pointing_device.md b/docs/feature_pointing_device.md index badeadc12b..031ee52c1c 100644 --- a/docs/feature_pointing_device.md +++ b/docs/feature_pointing_device.md @@ -1,6 +1,6 @@ # Pointing Device :id=pointing-device -Pointing Device is a generic name for a feature intended to be generic: moving the system pointer around. There are certainly other options for it - like mousekeys - but this aims to be easily modifiable and lightweight. You can implement custom keys to control functionality, or you can gather information from other peripherals and insert it directly here - let QMK handle the processing for you. +Pointing Device is a generic name for a feature intended to be generic: moving the system pointer around. There are certainly other options for it - like mousekeys - but this aims to be easily modifiable and hardware driven. You can implement custom keys to control functionality, or you can gather information from other peripherals and insert it directly here - let QMK handle the processing for you. To enable Pointing Device, uncomment the following line in your rules.mk: @@ -8,12 +8,198 @@ To enable Pointing Device, uncomment the following line in your rules.mk: POINTING_DEVICE_ENABLE = yes ``` -To manipulate the mouse report, you can use the following functions: +## Sensor Drivers + +There are a number of sensors that are supported by default. Note that only one sensor can be enabled by `POINTING_DEVICE_DRIVER` at a time. If you need to enable more than one sensor, then you need to implement it manually. + +### ADNS 5050 Sensor + +To use the ADNS 5050 sensor, add this to your `rules.mk` + +```make +POINTING_DEVICE_DRIVER = adns5050 +``` + +The ADNS 5050 sensor uses a serial type protocol for communication, and requires an additional light source. + +| Setting | Description | +|--------------------|---------------------------------------------------------------------| +|`ADNS5050_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. | +|`ADNS5050_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. | +|`ADNS5050_CS_PIN` | (Required) The pin connected to the cable select pin of the sensor. | + +The CPI range is 125-1375, in increments of 125. Defaults to 500 CPI. + +### ADSN 9800 Sensor + +To use the ADNS 9800 sensor, add this to your `rules.mk` + +```make +POINTING_DEVICE_DRIVER = adns9800 +``` + +The ADNS 9800 is an SPI driven optical sensor, that uses laser output for surface tracking. + +| Setting | Description | Default | +|------------------------|------------------------------------------------------------------------|---------------| +|`ADNS9800_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `2000000` | +|`ADNS9800_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` | +|`ADNS9800_SPI_MODE` | (Optional) Sets the SPI Mode for the sensor. | `3` | +|`ADNS9800_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ | +|`ADNS9800_CS_PIN` | (Required) Sets the Cable Select pin connected to the sensor. | _not defined_ | + + +The CPI range is 800-8200, in increments of 200. Defaults to 1800 CPI. + +### Analog Joystick + +To use an analog joystick to control the pointer, add this to your `rules.mk` + +```make +POINTING_DEVICE_DRIVER = analog_joystick +``` + +The Analog Joystick is an analog (ADC) driven sensor. There are a variety of joysticks that you can use for this. + +| Setting | Description | Default | +|----------------------------------|----------------------------------------------------------------------------|---------------| +|`ANALOG_JOYSTICK_X_AXIS_PIN` | (Required) The pin used for the vertical/X axis. | _not defined_ | +|`ANALOG_JOYSTICK_Y_AXIS_PIN` | (Required) The pin used for the horizontal/Y axis. | _not defined_ | +|`ANALOG_JOYSTICK_AXIS_MIN` | (Optional) Sets the lower range to be considered movement. | `0` | +|`ANALOG_JOYSTICK_AXIS_MAX` | (Optional) Sets the upper range to be considered movement. | `1023` | +|`ANALOG_JOYSTICK_SPEED_REGULATOR` | (Optional) The divisor used to slow down movement. (lower makes it faster) | `20` | +|`ANALOG_JOYSTICK_READ_INTERVAL` | (Optional) The interval in milliseconds between reads. | `10` | +|`ANALOG_JOYSTICK_SPEED_MAX` | (Optional) The maxiumum value used for motion. | `2` | +|`ANALOG_JOYSTICK_CLICK_PIN` | (Optional) The pin wired up to the press switch of the analog stick. | _not defined_ | + + +### Cirque Trackpad + +To use the Cirque Trackpad sensor, add this to your `rules.mk`: + +```make +POINTING_DEVICE_DRIVER = cirque_pinnacle_i2c +``` + +or + +```make +POINTING_DEVICE_DRIVER = cirque_pinnacle_spi +``` -* `pointing_device_get_report()` - Returns the current report_mouse_t that represents the information sent to the host computer -* `pointing_device_set_report(report_mouse_t newMouseReport)` - Overrides and saves the report_mouse_t to be sent to the host computer -Keep in mind that a report_mouse_t (here "mouseReport") has the following properties: +This supports the Cirque Pinnacle 1CA027 Touch Controller, which is used in the TM040040, TM035035 and the TM023023 trackpads. These are I2C or SPI compatible, and both configurations are supported. + +| Setting | Description | Default | +|---------------------------------|---------------------------------------------------------------------------------|-----------------------| +|`CIRQUE_PINNACLE_X_LOWER` | (Optional) The minimum reachable X value on the sensor. | `127` | +|`CIRQUE_PINNACLE_X_UPPER` | (Optional) The maximum reachable X value on the sensor. | `1919` | +|`CIRQUE_PINNACLE_Y_LOWER` | (Optional) The minimum reachable Y value on the sensor. | `63` | +|`CIRQUE_PINNACLE_Y_UPPER` | (Optional) The maximum reachable Y value on the sensor. | `1471` | +|`CIRQUE_PINNACLE_TAPPING_TERM` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` | +|`CIRQUE_PINNACLE_TOUCH_DEBOUNCE` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` | + +| I2C Setting | Description | Default | +|--------------------------|---------------------------------------------------------------------------------|---------| +|`CIRQUE_PINNACLE_ADDR` | (Required) Sets the I2C Address for the Cirque Trackpad | `0x2A` | +|`CIRQUE_PINNACLE_TIMEOUT` | (Optional) The timeout for i2c communication with the trackpad in milliseconds. | `20` | + +| SPI Setting | Description | Default | +|-------------------------------|------------------------------------------------------------------------|---------------| +|`CIRQUE_PINNACLE_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `1000000` | +|`CIRQUE_PINNACLE_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` | +|`CIRQUE_PINNACLE_SPI_MODE` | (Optional) Sets the SPI Mode for the sensor. | `1` | +|`CIRQUE_PINNACLE_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ | +|`CIRQUE_PINNACLE_SPI_CS_PIN` | (Required) Sets the Cable Select pin connected to the sensor. | _not defined_ | + +Default Scaling/CPI is 1024. + +### Pimoroni Trackball + +To use the Pimoroni Trackball module, add this to your `rules.mk`: + +```make +POINTING_DEVICE_DRIVER = pimoroni_trackball +``` + +The Pimoroni Trackball module is a I2C based breakout board with an RGB enable trackball. + +| Setting | Description | Default | +|-------------------------------------|------------------------------------------------------------------------------------|---------| +|`PIMORONI_TRACKBALL_ADDRESS` | (Required) Sets the I2C Address for the Pimoroni Trackball. | `0x0A` | +|`PIMORONI_TRACKBALL_TIMEOUT` | (Optional) The timeout for i2c communication with the trackpad in milliseconds. | `100` | +|`PIMORONI_TRACKBALL_INTERVAL_MS` | (Optional) The update/read interval for the sensor in milliseconds. | `8` | +|`PIMORONI_TRACKBALL_SCALE` | (Optional) The multiplier used to generate reports from the sensor. | `5` | +|`PIMORONI_TRACKBALL_DEBOUNCE_CYCLES` | (Optional) The number of scan cycles used for debouncing on the ball press. | `20` | +|`PIMORONI_TRACKBALL_ERROR_COUNT` | (Optional) Specifies the number of read/write errors until the sensor is disabled. | `10` | + +### PMW 3360 Sensor + +To use the PMW 3360 sensor, add this to your `rules.mk` + +```make +POINTING_DEVICE_DRIVER = pmw3360 +``` + +The PMW 3360 is an SPI driven optical sensor, that uses a built in IR LED for surface tracking. + +| Setting | Description | Default | +|-----------------------------|--------------------------------------------------------------------------------------------|---------------| +|`PMW3360_CS_PIN` | (Required) Sets the Cable Select pin connected to the sensor. | _not defined_ | +|`PMW3360_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `2000000` | +|`PMW3360_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` | +|`PMW3360_SPI_MODE` | (Optional) Sets the SPI Mode for the sensor. | `3` | +|`PMW3360_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ | +|`ROTATIONAL_TRANSFORM_ANGLE` | (Optional) Allows for the sensor data to be rotated +/- 30 degrees directly in the sensor. | `0` | + +The CPI range is 100-12000, in increments of 100. Defaults to 1600 CPI. + + +### Custom Driver + +If you have a sensor type that isn't supported here, you can manually implement it, by adding these functions (with the correct implementation for your device): + +```c +void pointing_device_driver_init(void) {} +report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) { return mouse_report; } +uint16_t pointing_device_driver_get_cpi(void) { return 0; } +void pointing_device_driver_set_cpi(uint16_t cpi) {} +``` + +!> Ideally, new sensor hardware should be added to `drivers/sensors/` and `quantum/pointing_device_drivers.c`, but there may be cases where it's very specific to the hardware. So these functions are provided, just in case. + +## Common Configuration + +| Setting | Description | Default | +|-------------------------------|-----------------------------------------------------------------------|---------------| +|`POINTING_DEVICE_ROTATION_90` | (Optional) Rotates the X and Y data by 90 degrees. | _not defined_ | +|`POINTING_DEVICE_ROTATION_180` | (Optional) Rotates the X and Y data by 180 degrees. | _not defined_ | +|`POINTING_DEVICE_ROTATION_270` | (Optional) Rotates the X and Y data by 270 degrees. | _not defined_ | +|`POINTING_DEVICE_INVERT_X` | (Optional) Inverts the X axis report. | _not defined_ | +|`POINTING_DEVICE_INVERT_Y` | (Optional) Inverts the Y axis report. | _not defined_ | +|`POINTING_DEVICE_MOTION_PIN` | (Optional) If supported, will only read from sensor if pin is active. | _not defined_ | + + +## Callbacks and Functions + +| Function | Description | +|-----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `pointing_device_init_kb(void)` | Callback to allow for keyboard level initialization. Useful for additional hardware sensors. | +| `pointing_device_init_user(void)` | Callback to allow for user level initialization. Useful for additional hardware sensors. | +| `pointing_device_task_kb(mouse_report)` | Callback that sends sensor data, so keyboard code can intercept and modify the data. Returns a mouse report. | +| `pointing_device_task_user(mouse_report)` | Callback that sends sensor data, so user coe can intercept and modify the data. Returns a mouse report. | +| `pointing_device_handle_buttons(buttons, pressed, button)` | Callback to handle hardware button presses. Returns a `uint8_t`. | +| `pointing_device_get_cpi(void)` | Gets the current CPI/DPI setting from the sensor, if supported. | +| `pointing_device_set_cpi(uint16_t)` | Sets the CPI/DPI, if supported. | +| `pointing_device_get_report(void)` | Returns the current mouse report (as a `mouse_report_t` data structure). | +| `pointing_device_set_report(mouse_report)` | Sets the mouse report to the assigned `mouse_report_t` data structured passed to the function. | +| `pointing_device_send(void)` | Sends the current mouse report to the host system. Function can be replaced. | +| `has_mouse_report_changed(old, new)` | Compares the old and new `mouse_report_t` data and returns true only if it has changed. | + + +# Manipulating Mouse Reports + +The report_mouse_t (here "mouseReport") has the following properties: * `mouseReport.x` - this is a signed int from -127 to 127 (not 128, this is defined in USB HID spec) representing movement (+ to the right, - to the left) on the x axis. * `mouseReport.y` - this is a signed int from -127 to 127 (not 128, this is defined in USB HID spec) representing movement (+ upward, - downward) on the y axis. @@ -21,8 +207,10 @@ Keep in mind that a report_mouse_t (here "mouseReport") has the following proper * `mouseReport.h` - this is a signed int from -127 to 127 (not 128, this is defined in USB HID spec) representing horizontal scrolling (+ right, - left). * `mouseReport.buttons` - this is a uint8_t in which all 8 bits are used. These bits represent the mouse button state - bit 0 is mouse button 1, and bit 7 is mouse button 8. -Once you have made the necessary changes to the mouse report, you need to send it: +To manually manipulate the mouse reports outside of the `pointing_device_task_*` functions, you can use: +* `pointing_device_get_report()` - Returns the current report_mouse_t that represents the information sent to the host computer +* `pointing_device_set_report(report_mouse_t newMouseReport)` - Overrides and saves the report_mouse_t to be sent to the host computer * `pointing_device_send()` - Sends the mouse report to the host and zeroes out the report. When the mouse report is sent, the x, y, v, and h values are set to 0 (this is done in `pointing_device_send()`, which can be overridden to avoid this behavior). This way, button states persist, but movement will only occur once. For further customization, both `pointing_device_init` and `pointing_device_task` can be overridden. @@ -31,6 +219,8 @@ Additionally, by default, `pointing_device_send()` will only send a report when Also, you use the `has_mouse_report_changed(new, old)` function to check to see if the report has changed. +## Example + In the following example, a custom key is used to click the mouse and scroll 127 units vertically and horizontally, then undo all of that when released - because that's a totally useful function. Listen, this is an example: ```c diff --git a/drivers/sensors/adns5050.c b/drivers/sensors/adns5050.c index 254ef2ee87..c23d24d5af 100644 --- a/drivers/sensors/adns5050.c +++ b/drivers/sensors/adns5050.c @@ -20,81 +20,95 @@ #include "adns5050.h" #include "wait.h" #include "debug.h" -#include "print.h" #include "gpio.h" -#ifndef OPTIC_ROTATED -# define OPTIC_ROTATED false -#endif - -// Definitions for the ADNS serial line. -#ifndef ADNS_SCLK_PIN -# define ADNS_SCLK_PIN B7 -#endif - -#ifndef ADNS_SDIO_PIN -# define ADNS_SDIO_PIN C6 -#endif - -#ifndef ADNS_CS_PIN -# define ADNS_CS_PIN B4 -#endif - -#ifdef CONSOLE_ENABLE -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 - -// Initialize the ADNS serial pins. -void adns_init(void) { - setPinOutput(ADNS_SCLK_PIN); - setPinOutput(ADNS_SDIO_PIN); - setPinOutput(ADNS_CS_PIN); +// Registers +// clang-format off +#define REG_PRODUCT_ID 0x00 +#define REG_REVISION_ID 0x01 +#define REG_MOTION 0x02 +#define REG_DELTA_X 0x03 +#define REG_DELTA_Y 0x04 +#define REG_SQUAL 0x05 +#define REG_SHUTTER_UPPER 0x06 +#define REG_SHUTTER_LOWER 0x07 +#define REG_MAXIMUM_PIXEL 0x08 +#define REG_PIXEL_SUM 0x09 +#define REG_MINIMUM_PIXEL 0x0a +#define REG_PIXEL_GRAB 0x0b +#define REG_MOUSE_CONTROL 0x0d +#define REG_MOUSE_CONTROL2 0x19 +#define REG_LED_DC_MODE 0x22 +#define REG_CHIP_RESET 0x3a +#define REG_PRODUCT_ID2 0x3e +#define REG_INV_REV_ID 0x3f +#define REG_MOTION_BURST 0x63 +// clang-format on + +void adns5050_init(void) { + // Initialize the ADNS serial pins. + setPinOutput(ADNS5050_SCLK_PIN); + setPinOutput(ADNS5050_SDIO_PIN); + setPinOutput(ADNS5050_CS_PIN); + + // reboot the adns. + // if the adns hasn't initialized yet, this is harmless. + adns5050_write_reg(REG_CHIP_RESET, 0x5a); + + // wait maximum time before adns is ready. + // this ensures that the adns is actuall ready after reset. + wait_ms(55); + + // read a burst from the adns and then discard it. + // gets the adns ready for write commands + // (for example, setting the dpi). + adns5050_read_burst(); } // Perform a synchronization with the ADNS. // Just as with the serial protocol, this is used by the slave to send a // synchronization signal to the master. -void adns_sync(void) { - writePinLow(ADNS_CS_PIN); +void adns5050_sync(void) { + writePinLow(ADNS5050_CS_PIN); wait_us(1); - writePinHigh(ADNS_CS_PIN); + writePinHigh(ADNS5050_CS_PIN); } -void adns_cs_select(void) { writePinLow(ADNS_CS_PIN); } +void adns5050_cs_select(void) { writePinLow(ADNS5050_CS_PIN); } -void adns_cs_deselect(void) { writePinHigh(ADNS_CS_PIN); } +void adns5050_cs_deselect(void) { writePinHigh(ADNS5050_CS_PIN); } -uint8_t adns_serial_read(void) { - setPinInput(ADNS_SDIO_PIN); +uint8_t adns5050_serial_read(void) { + setPinInput(ADNS5050_SDIO_PIN); uint8_t byte = 0; for (uint8_t i = 0; i < 8; ++i) { - writePinLow(ADNS_SCLK_PIN); + writePinLow(ADNS5050_SCLK_PIN); wait_us(1); - byte = (byte << 1) | readPin(ADNS_SDIO_PIN); + byte = (byte << 1) | readPin(ADNS5050_SDIO_PIN); - writePinHigh(ADNS_SCLK_PIN); + writePinHigh(ADNS5050_SCLK_PIN); wait_us(1); } return byte; } -void adns_serial_write(uint8_t data) { - setPinOutput(ADNS_SDIO_PIN); +void adns5050_serial_write(uint8_t data) { + setPinOutput(ADNS5050_SDIO_PIN); for (int8_t b = 7; b >= 0; b--) { - writePinLow(ADNS_SCLK_PIN); + writePinLow(ADNS5050_SCLK_PIN); if (data & (1 << b)) - writePinHigh(ADNS_SDIO_PIN); + writePinHigh(ADNS5050_SDIO_PIN); else - writePinLow(ADNS_SDIO_PIN); + writePinLow(ADNS5050_SDIO_PIN); wait_us(2); - writePinHigh(ADNS_SCLK_PIN); + writePinHigh(ADNS5050_SCLK_PIN); } // tSWR. See page 15 of the ADNS spec sheet. @@ -108,17 +122,17 @@ void adns_serial_write(uint8_t data) { // Read a byte of data from a register on the ADNS. // Don't forget to use the register map (as defined in the header file). -uint8_t adns_read_reg(uint8_t reg_addr) { - adns_cs_select(); +uint8_t adns5050_read_reg(uint8_t reg_addr) { + adns5050_cs_select(); - adns_serial_write(reg_addr); + adns5050_serial_write(reg_addr); // We don't need a minimum tSRAD here. That's because a 4ms wait time is - // already included in adns_serial_write(), so we're good. + // already included in adns5050_serial_write(), so we're good. // See page 10 and 15 of the ADNS spec sheet. // wait_us(4); - uint8_t byte = adns_serial_read(); + uint8_t byte = adns5050_serial_read(); // tSRW & tSRR. See page 15 of the ADNS spec sheet. // Technically, this is only necessary if the next operation is an SDIO @@ -126,38 +140,38 @@ uint8_t adns_read_reg(uint8_t reg_addr) { // Honestly, this wait could probably be removed. wait_us(1); - adns_cs_deselect(); + adns5050_cs_deselect(); return byte; } -void adns_write_reg(uint8_t reg_addr, uint8_t data) { - adns_cs_select(); - adns_serial_write(0b10000000 | reg_addr); - adns_serial_write(data); - adns_cs_deselect(); +void adns5050_write_reg(uint8_t reg_addr, uint8_t data) { + adns5050_cs_select(); + adns5050_serial_write(0b10000000 | reg_addr); + adns5050_serial_write(data); + adns5050_cs_deselect(); } -report_adns_t adns_read_burst(void) { - adns_cs_select(); +report_adns5050_t adns5050_read_burst(void) { + adns5050_cs_select(); - report_adns_t data; + report_adns5050_t data; data.dx = 0; data.dy = 0; - adns_serial_write(REG_MOTION_BURST); + adns5050_serial_write(REG_MOTION_BURST); // We don't need a minimum tSRAD here. That's because a 4ms wait time is - // already included in adns_serial_write(), so we're good. + // already included in adns5050_serial_write(), so we're good. // See page 10 and 15 of the ADNS spec sheet. // wait_us(4); - uint8_t x = adns_serial_read(); - uint8_t y = adns_serial_read(); + uint8_t x = adns5050_serial_read(); + uint8_t y = adns5050_serial_read(); // Burst mode returns a bunch of other shit that we don't really need. // Setting CS to high ends burst mode early. - adns_cs_deselect(); + adns5050_cs_deselect(); data.dx = convert_twoscomp(x); data.dy = convert_twoscomp(y); @@ -175,12 +189,21 @@ int8_t convert_twoscomp(uint8_t data) { } // Don't forget to use the definitions for CPI in the header file. -void adns_set_cpi(uint8_t cpi) { adns_write_reg(REG_MOUSE_CONTROL2, cpi); } +void adns5050_set_cpi(uint16_t cpi) { + uint8_t cpival = constrain((cpi / 125), 0x1, 0xD); // limits to 0--119 + + adns5050_write_reg(REG_MOUSE_CONTROL2, 0b10000 | cpival); +} + +uint16_t adns5050_get_cpi(void) { + uint8_t cpival = adns5050_read_reg(REG_MOUSE_CONTROL2); + return (uint16_t)((cpival & 0b10000) * 125); +} -bool adns_check_signature(void) { - uint8_t pid = adns_read_reg(REG_PRODUCT_ID); - uint8_t rid = adns_read_reg(REG_REVISION_ID); - uint8_t pid2 = adns_read_reg(REG_PRODUCT_ID2); +bool adns5050_check_signature(void) { + uint8_t pid = adns5050_read_reg(REG_PRODUCT_ID); + uint8_t rid = adns5050_read_reg(REG_REVISION_ID); + uint8_t pid2 = adns5050_read_reg(REG_PRODUCT_ID2); return (pid == 0x12 && rid == 0x01 && pid2 == 0x26); } diff --git a/drivers/sensors/adns5050.h b/drivers/sensors/adns5050.h index 5e9edc2962..e45a250196 100644 --- a/drivers/sensors/adns5050.h +++ b/drivers/sensors/adns5050.h @@ -21,59 +21,52 @@ #include -// Registers -#define REG_PRODUCT_ID 0x00 -#define REG_REVISION_ID 0x01 -#define REG_MOTION 0x02 -#define REG_DELTA_X 0x03 -#define REG_DELTA_Y 0x04 -#define REG_SQUAL 0x05 -#define REG_SHUTTER_UPPER 0x06 -#define REG_SHUTTER_LOWER 0x07 -#define REG_MAXIMUM_PIXEL 0x08 -#define REG_PIXEL_SUM 0x09 -#define REG_MINIMUM_PIXEL 0x0a -#define REG_PIXEL_GRAB 0x0b -#define REG_MOUSE_CONTROL 0x0d -#define REG_MOUSE_CONTROL2 0x19 -#define REG_LED_DC_MODE 0x22 -#define REG_CHIP_RESET 0x3a -#define REG_PRODUCT_ID2 0x3e -#define REG_INV_REV_ID 0x3f -#define REG_MOTION_BURST 0x63 - // CPI values -#define CPI125 0x11 -#define CPI250 0x12 -#define CPI375 0x13 -#define CPI500 0x14 -#define CPI625 0x15 -#define CPI750 0x16 -#define CPI875 0x17 +// clang-format off +#define CPI125 0x11 +#define CPI250 0x12 +#define CPI375 0x13 +#define CPI500 0x14 +#define CPI625 0x15 +#define CPI750 0x16 +#define CPI875 0x17 #define CPI1000 0x18 #define CPI1125 0x19 #define CPI1250 0x1a #define CPI1375 0x1b +// clang-format on + +#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt))) + +// Definitions for the ADNS serial line. +#ifndef ADNS5050_SCLK_PIN +# error "No clock pin defined -- missing ADNS5050_SCLK_PIN" +#endif + +#ifndef ADNS5050_SDIO_PIN +# error "No data pin defined -- missing ADNS5050_SDIO_PIN" +#endif -#ifdef CONSOLE_ENABLE -void print_byte(uint8_t byte); +#ifndef ADNS5050_CS_PIN +# error "No chip select pin defined -- missing ADNS5050_CS_PIN" #endif typedef struct { int8_t dx; int8_t dy; -} report_adns_t; +} report_adns5050_t; // A bunch of functions to implement the ADNS5050-specific serial protocol. // Note that the "serial.h" driver is insufficient, because it does not // manually manipulate a serial clock signal. -void adns_init(void); -void adns_sync(void); -uint8_t adns_serial_read(void); -void adns_serial_write(uint8_t data); -uint8_t adns_read_reg(uint8_t reg_addr); -void adns_write_reg(uint8_t reg_addr, uint8_t data); -report_adns_t adns_read_burst(void); -int8_t convert_twoscomp(uint8_t data); -void adns_set_cpi(uint8_t cpi); -bool adns_check_signature(void); +void adns5050_init(void); +void adns5050_sync(void); +uint8_t adns5050_serial_read(void); +void adns5050_serial_write(uint8_t data); +uint8_t adns5050_read_reg(uint8_t reg_addr); +void adns5050_write_reg(uint8_t reg_addr, uint8_t data); +report_adns5050_t adns5050_read_burst(void); +void adns5050_set_cpi(uint16_t cpi); +uint16_t adns5050_get_cpi(void); +int8_t convert_twoscomp(uint8_t data); +bool adns5050_check_signature(void); diff --git a/drivers/sensors/adns9800.c b/drivers/sensors/adns9800.c index b4f683452c..c52f991804 100644 --- a/drivers/sensors/adns9800.c +++ b/drivers/sensors/adns9800.c @@ -15,83 +15,80 @@ */ #include "spi_master.h" -#include "quantum.h" #include "adns9800_srom_A6.h" #include "adns9800.h" +#include "wait.h" // registers -#define REG_Product_ID 0x00 -#define REG_Revision_ID 0x01 -#define REG_Motion 0x02 -#define REG_Delta_X_L 0x03 -#define REG_Delta_X_H 0x04 -#define REG_Delta_Y_L 0x05 -#define REG_Delta_Y_H 0x06 -#define REG_SQUAL 0x07 -#define REG_Pixel_Sum 0x08 -#define REG_Maximum_Pixel 0x09 -#define REG_Minimum_Pixel 0x0a -#define REG_Shutter_Lower 0x0b -#define REG_Shutter_Upper 0x0c -#define REG_Frame_Period_Lower 0x0d -#define REG_Frame_Period_Upper 0x0e -#define REG_Configuration_I 0x0f -#define REG_Configuration_II 0x10 -#define REG_Frame_Capture 0x12 -#define REG_SROM_Enable 0x13 -#define REG_Run_Downshift 0x14 -#define REG_Rest1_Rate 0x15 -#define REG_Rest1_Downshift 0x16 -#define REG_Rest2_Rate 0x17 -#define REG_Rest2_Downshift 0x18 -#define REG_Rest3_Rate 0x19 +// clang-format off +#define REG_Product_ID 0x00 +#define REG_Revision_ID 0x01 +#define REG_Motion 0x02 +#define REG_Delta_X_L 0x03 +#define REG_Delta_X_H 0x04 +#define REG_Delta_Y_L 0x05 +#define REG_Delta_Y_H 0x06 +#define REG_SQUAL 0x07 +#define REG_Pixel_Sum 0x08 +#define REG_Maximum_Pixel 0x09 +#define REG_Minimum_Pixel 0x0a +#define REG_Shutter_Lower 0x0b +#define REG_Shutter_Upper 0x0c +#define REG_Frame_Period_Lower 0x0d +#define REG_Frame_Period_Upper 0x0e +#define REG_Configuration_I 0x0f +#define REG_Configuration_II 0x10 +#define REG_Frame_Capture 0x12 +#define REG_SROM_Enable 0x13 +#define REG_Run_Downshift 0x14 +#define REG_Rest1_Rate 0x15 +#define REG_Rest1_Downshift 0x16 +#define REG_Rest2_Rate 0x17 +#define REG_Rest2_Downshift 0x18 +#define REG_Rest3_Rate 0x19 #define REG_Frame_Period_Max_Bound_Lower 0x1a #define REG_Frame_Period_Max_Bound_Upper 0x1b #define REG_Frame_Period_Min_Bound_Lower 0x1c #define REG_Frame_Period_Min_Bound_Upper 0x1d -#define REG_Shutter_Max_Bound_Lower 0x1e -#define REG_Shutter_Max_Bound_Upper 0x1f -#define REG_LASER_CTRL0 0x20 -#define REG_Observation 0x24 -#define REG_Data_Out_Lower 0x25 -#define REG_Data_Out_Upper 0x26 -#define REG_SROM_ID 0x2a -#define REG_Lift_Detection_Thr 0x2e -#define REG_Configuration_V 0x2f -#define REG_Configuration_IV 0x39 -#define REG_Power_Up_Reset 0x3a -#define REG_Shutdown 0x3b -#define REG_Inverse_Product_ID 0x3f -#define REG_Motion_Burst 0x50 -#define REG_SROM_Load_Burst 0x62 -#define REG_Pixel_Burst 0x64 - -#define ADNS_CLOCK_SPEED 2000000 -#define MIN_CPI 200 -#define MAX_CPI 8200 -#define CPI_STEP 200 -#define CLAMP_CPI(value) value MAX_CPI ? MAX_CPI : value -#define SPI_MODE 3 -#define SPI_DIVISOR (F_CPU / ADNS_CLOCK_SPEED) +#define REG_Shutter_Max_Bound_Lower 0x1e +#define REG_Shutter_Max_Bound_Upper 0x1f +#define REG_LASER_CTRL0 0x20 +#define REG_Observation 0x24 +#define REG_Data_Out_Lower 0x25 +#define REG_Data_Out_Upper 0x26 +#define REG_SROM_ID 0x2a +#define REG_Lift_Detection_Thr 0x2e +#define REG_Configuration_V 0x2f +#define REG_Configuration_IV 0x39 +#define REG_Power_Up_Reset 0x3a +#define REG_Shutdown 0x3b +#define REG_Inverse_Product_ID 0x3f +#define REG_Motion_Burst 0x50 +#define REG_SROM_Load_Burst 0x62 +#define REG_Pixel_Burst 0x64 + +#define MIN_CPI 200 +#define MAX_CPI 8200 +#define CPI_STEP 200 +#define CLAMP_CPI(value) value MAX_CPI ? MAX_CPI : value #define US_BETWEEN_WRITES 120 -#define US_BETWEEN_READS 20 -#define US_BEFORE_MOTION 100 -#define MSB1 0x80 +#define US_BETWEEN_READS 20 +#define US_BEFORE_MOTION 100 +#define MSB1 0x80 +// clang-format on -extern const uint8_t firmware_data[]; +void adns9800_spi_start(void) { spi_start(ADNS9800_CS_PIN, false, ADNS9800_SPI_MODE, ADNS9800_SPI_DIVISOR); } -void adns_spi_start(void) { spi_start(SPI_SS_PIN, false, SPI_MODE, SPI_DIVISOR); } - -void adns_write(uint8_t reg_addr, uint8_t data) { - adns_spi_start(); +void adns9800_write(uint8_t reg_addr, uint8_t data) { + adns9800_spi_start(); spi_write(reg_addr | MSB1); spi_write(data); spi_stop(); wait_us(US_BETWEEN_WRITES); } -uint8_t adns_read(uint8_t reg_addr) { - adns_spi_start(); +uint8_t adns9800_read(uint8_t reg_addr) { + adns9800_spi_start(); spi_write(reg_addr & 0x7f); uint8_t data = spi_read(); spi_stop(); @@ -100,39 +97,39 @@ uint8_t adns_read(uint8_t reg_addr) { return data; } -void adns_init() { - setPinOutput(SPI_SS_PIN); +void adns9800_init() { + setPinOutput(ADNS9800_CS_PIN); spi_init(); // reboot - adns_write(REG_Power_Up_Reset, 0x5a); + adns9800_write(REG_Power_Up_Reset, 0x5a); wait_ms(50); // read registers and discard - adns_read(REG_Motion); - adns_read(REG_Delta_X_L); - adns_read(REG_Delta_X_H); - adns_read(REG_Delta_Y_L); - adns_read(REG_Delta_Y_H); + adns9800_read(REG_Motion); + adns9800_read(REG_Delta_X_L); + adns9800_read(REG_Delta_X_H); + adns9800_read(REG_Delta_Y_L); + adns9800_read(REG_Delta_Y_H); // upload firmware // 3k firmware mode - adns_write(REG_Configuration_IV, 0x02); + adns9800_write(REG_Configuration_IV, 0x02); // enable initialisation - adns_write(REG_SROM_Enable, 0x1d); + adns9800_write(REG_SROM_Enable, 0x1d); // wait a frame wait_ms(10); // start SROM download - adns_write(REG_SROM_Enable, 0x18); + adns9800_write(REG_SROM_Enable, 0x18); // write the SROM file - adns_spi_start(); + adns9800_spi_start(); spi_write(REG_SROM_Load_Burst | 0x80); wait_us(15); @@ -140,7 +137,7 @@ void adns_init() { // send all bytes of the firmware unsigned char c; for (int i = 0; i < FIRMWARE_LENGTH; i++) { - c = (unsigned char)pgm_read_byte(firmware_data + i); + c = (unsigned char)pgm_read_byte(adns9800_firmware_data + i); spi_write(c); wait_us(15); } @@ -150,18 +147,30 @@ void adns_init() { wait_ms(10); // enable laser - uint8_t laser_ctrl0 = adns_read(REG_LASER_CTRL0); - adns_write(REG_LASER_CTRL0, laser_ctrl0 & 0xf0); + uint8_t laser_ctrl0 = adns9800_read(REG_LASER_CTRL0); + adns9800_write(REG_LASER_CTRL0, laser_ctrl0 & 0xf0); + + adns9800_set_cpi(ADNS9800_CPI); } -config_adns_t adns_get_config(void) { - uint8_t config_1 = adns_read(REG_Configuration_I); - return (config_adns_t){(config_1 & 0xFF) * CPI_STEP}; +config_adns9800_t adns9800_get_config(void) { + uint8_t config_1 = adns9800_read(REG_Configuration_I); + return (config_adns9800_t){(config_1 & 0xFF) * CPI_STEP}; } -void adns_set_config(config_adns_t config) { +void adns9800_set_config(config_adns9800_t config) { uint8_t config_1 = (CLAMP_CPI(config.cpi) / CPI_STEP) & 0xFF; - adns_write(REG_Configuration_I, config_1); + adns9800_write(REG_Configuration_I, config_1); +} + +uint16_t adns9800_get_cpi(void) { + uint8_t config_1 = adns9800_read(REG_Configuration_I); + return (uint16_t){(config_1 & 0xFF) * CPI_STEP}; +} + +void adns9800_set_cpi(uint16_t cpi) { + uint8_t config_1 = (CLAMP_CPI(cpi) / CPI_STEP) & 0xFF; + adns9800_write(REG_Configuration_I, config_1); } static int16_t convertDeltaToInt(uint8_t high, uint8_t low) { @@ -174,10 +183,10 @@ static int16_t convertDeltaToInt(uint8_t high, uint8_t low) { return twos_comp; } -report_adns_t adns_get_report(void) { - report_adns_t report = {0, 0}; +report_adns9800_t adns9800_get_report(void) { + report_adns9800_t report = {0, 0}; - adns_spi_start(); + adns9800_spi_start(); // start burst mode spi_write(REG_Motion_Burst & 0x7f); diff --git a/drivers/sensors/adns9800.h b/drivers/sensors/adns9800.h index d19ded4012..e75a869c03 100644 --- a/drivers/sensors/adns9800.h +++ b/drivers/sensors/adns9800.h @@ -18,18 +18,48 @@ #include +#ifndef ADNS9800_CPI +# define ADNS9800_CPI 1600 +#endif + +#ifndef ADNS9800_CLOCK_SPEED +# define ADNS9800_CLOCK_SPEED 2000000 +#endif + +#ifndef ADNS9800_SPI_LSBFIRST +# define ADNS9800_SPI_LSBFIRST false +#endif + +#ifndef ADNS9800_SPI_MODE +# define ADNS9800_SPI_MODE 3 +#endif + +#ifndef ADNS9800_SPI_DIVISOR +# ifdef __AVR__ +# define ADNS9800_SPI_DIVISOR (F_CPU / ADNS9800_CLOCK_SPEED) +# else +# define ADNS9800_SPI_DIVISOR 64 +# endif +#endif + +#ifndef ADNS9800_CS_PIN +# error "No chip select pin defined -- missing ADNS9800_CS_PIN" +#endif + typedef struct { /* 200 - 8200 CPI supported */ uint16_t cpi; -} config_adns_t; +} config_adns9800_t; typedef struct { int16_t x; int16_t y; -} report_adns_t; +} report_adns9800_t; -void adns_init(void); -config_adns_t adns_get_config(void); -void adns_set_config(config_adns_t); +void adns9800_init(void); +config_adns9800_t adns9800_get_config(void); +void adns9800_set_config(config_adns9800_t); +uint16_t adns9800_get_cpi(void); +void adns9800_set_cpi(uint16_t cpi); /* Reads and clears the current delta values on the ADNS sensor */ -report_adns_t adns_get_report(void); +report_adns9800_t adns9800_get_report(void); diff --git a/drivers/sensors/adns9800_srom_A6.h b/drivers/sensors/adns9800_srom_A6.h index e698a401b9..d86ecbbd9e 100644 --- a/drivers/sensors/adns9800_srom_A6.h +++ b/drivers/sensors/adns9800_srom_A6.h @@ -6,7 +6,7 @@ // clang-format off -const uint8_t firmware_data[FIRMWARE_LENGTH] PROGMEM = { +const uint8_t adns9800_firmware_data[FIRMWARE_LENGTH] PROGMEM = { 0x03, 0xA6, 0x68, 0x1E, 0x7D, 0x10, 0x7E, 0x7E, 0x5F, 0x1C, 0xB8, 0xF2, 0x47, 0x0C, 0x7B, 0x74, 0x4B, 0x14, 0x8B, 0x75, 0x66, 0x51, 0x0B, 0x8C, 0x76, 0x74, 0x4B, 0x14, 0xAA, 0xD6, 0x0F, 0x9C, 0xBA, 0xF6, 0x6E, 0x3F, 0xDD, 0x38, 0xD5, 0x02, 0x80, 0x9B, 0x82, 0x6D, 0x58, 0x13, 0xA4, 0xAB, diff --git a/drivers/sensors/analog_joystick.c b/drivers/sensors/analog_joystick.c new file mode 100644 index 0000000000..0f4d1d7a4c --- /dev/null +++ b/drivers/sensors/analog_joystick.c @@ -0,0 +1,94 @@ +/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "analog_joystick.h" +#include "analog.h" +#include "gpio.h" +#include "wait.h" + +// Set Parameters +uint16_t minAxisValue = ANALOG_JOYSTICK_AXIS_MIN; +uint16_t maxAxisValue = ANALOG_JOYSTICK_AXIS_MAX; + +uint8_t maxCursorSpeed = ANALOG_JOYSTICK_SPEED_MAX; +uint8_t speedRegulator = ANALOG_JOYSTICK_SPEED_REGULATOR; // Lower Values Create Faster Movement + +int16_t xOrigin, yOrigin; + +uint16_t lastCursor = 0; + +int16_t axisCoordinate(uint8_t pin, uint16_t origin) { + int8_t direction; + int16_t distanceFromOrigin; + int16_t range; + + int16_t position = analogReadPin(pin); + + if (origin == position) { + return 0; + } else if (origin > position) { + distanceFromOrigin = origin - position; + range = origin - minAxisValue; + direction = -1; + } else { + distanceFromOrigin = position - origin; + range = maxAxisValue - origin; + direction = 1; + } + + float percent = (float)distanceFromOrigin / range; + int16_t coordinate = (int16_t)(percent * 100); + if (coordinate < 0) { + return 0; + } else if (coordinate > 100) { + return 100 * direction; + } else { + return coordinate * direction; + } +} + +int8_t axisToMouseComponent(uint8_t pin, int16_t origin, uint8_t maxSpeed) { + int16_t coordinate = axisCoordinate(pin, origin); + if (coordinate != 0) { + float percent = (float)coordinate / 100; + return percent * maxCursorSpeed * (abs(coordinate) / speedRegulator); + } else { + return 0; + } +} + +report_analog_joystick_t analog_joystick_read(void) { + report_analog_joystick_t report = {0}; + + if (timer_elapsed(lastCursor) > ANALOG_JOYSTICK_READ_INTERVAL) { + lastCursor = timer_read(); + report.x = axisToMouseComponent(ANALOG_JOYSTICK_X_AXIS_PIN, xOrigin, maxCursorSpeed); + report.y = axisToMouseComponent(ANALOG_JOYSTICK_Y_AXIS_PIN, yOrigin, maxCursorSpeed); + } +#ifdef ANALOG_JOYSTICK_CLICK_PIN + report.button = !readPin(ANALOG_JOYSTICK_CLICK_PIN); +#endif + return report; +} + +void analog_joystick_init(void) { +#ifdef ANALOG_JOYSTICK_CLICK_PIN + setPinInputHigh(ANALOG_JOYSTICK_CLICK_PIN); +#endif + // Account for drift + xOrigin = analogReadPin(ANALOG_JOYSTICK_X_AXIS_PIN); + yOrigin = analogReadPin(ANALOG_JOYSTICK_Y_AXIS_PIN); +} diff --git a/drivers/sensors/analog_joystick.h b/drivers/sensors/analog_joystick.h new file mode 100644 index 0000000000..6892a08817 --- /dev/null +++ b/drivers/sensors/analog_joystick.h @@ -0,0 +1,51 @@ +/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#ifndef ANALOG_JOYSTICK_X_AXIS_PIN +# error No pin specified for X Axis +#endif +#ifndef ANALOG_JOYSTICK_Y_AXIS_PIN +# error No pin specified for Y Axis +#endif + +#ifndef ANALOG_JOYSTICK_AXIS_MIN +# define ANALOG_JOYSTICK_AXIS_MIN 0 +#endif +#ifndef ANALOG_JOYSTICK_AXIS_MAX +# define ANALOG_JOYSTICK_AXIS_MAX 1023 +#endif +#ifndef ANALOG_JOYSTICK_SPEED_REGULATOR +# define ANALOG_JOYSTICK_SPEED_REGULATOR 20 +#endif +#ifndef ANALOG_JOYSTICK_READ_INTERVAL +# define ANALOG_JOYSTICK_READ_INTERVAL 10 +#endif +#ifndef ANALOG_JOYSTICK_SPEED_MAX +# define ANALOG_JOYSTICK_SPEED_MAX 2 +#endif + +typedef struct { + int8_t x; + int8_t y; + bool button; +} report_analog_joystick_t; +report_analog_joystick_t analog_joystick_read(void); +void analog_joystick_init(void); diff --git a/drivers/sensors/cirque_pinnacle.c b/drivers/sensors/cirque_pinnacle.c new file mode 100644 index 0000000000..b807c4f076 --- /dev/null +++ b/drivers/sensors/cirque_pinnacle.c @@ -0,0 +1,232 @@ +// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license +#include "cirque_pinnacle.h" +#include "print.h" +#include "debug.h" +#include "wait.h" + +// Registers for RAP +// clang-format off +#define FIRMWARE_ID 0x00 +#define FIRMWARE_VERSION_C 0x01 +#define STATUS_1 0x02 +#define SYSCONFIG_1 0x03 +#define FEEDCONFIG_1 0x04 +#define FEEDCONFIG_2 0x05 +#define CALIBRATION_CONFIG_1 0x07 +#define PS2_AU_CONTROL 0x08 +#define SAMPLE_RATE 0x09 +#define Z_IDLE_COUNT 0x0A +#define Z_SCALER 0x0B +#define SLEEP_INTERVAL 0x0C +#define SLEEP_TIMER 0x0D +#define PACKET_BYTE_0 0x12 +#define PACKET_BYTE_1 0x13 +#define PACKET_BYTE_2 0x14 +#define PACKET_BYTE_3 0x15 +#define PACKET_BYTE_4 0x16 +#define PACKET_BYTE_5 0x17 + +#define ERA_VALUE 0x1B +#define ERA_HIGH_BYTE 0x1C +#define ERA_LOW_BYTE 0x1D +#define ERA_CONTROL 0x1E + +// ADC-attenuation settings (held in BIT_7 and BIT_6) +// 1X = most sensitive, 4X = least sensitive +#define ADC_ATTENUATE_1X 0x00 +#define ADC_ATTENUATE_2X 0x40 +#define ADC_ATTENUATE_3X 0x80 +#define ADC_ATTENUATE_4X 0xC0 + +// Register config values for this demo +#define SYSCONFIG_1_VALUE 0x00 +#define FEEDCONFIG_1_VALUE 0x03 // 0x03 for absolute mode 0x01 for relative mode +#define FEEDCONFIG_2_VALUE 0x1C // 0x1F for normal functionality 0x1E for intellimouse disabled +#define Z_IDLE_COUNT_VALUE 0x05 +// clang-format on + +bool touchpad_init; +uint16_t scale_data = 1024; + +void cirque_pinnacle_clear_flags(void); +void cirque_pinnacle_enable_feed(bool feedEnable); +void RAP_ReadBytes(uint8_t address, uint8_t* data, uint8_t count); +void RAP_Write(uint8_t address, uint8_t data); + +#ifdef CONSOLE_ENABLE +void print_byte(uint8_t byte) { xprintf("%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 + +/* Logical Scaling Functions */ +// Clips raw coordinates to "reachable" window of sensor +// NOTE: values outside this window can only appear as a result of noise +void ClipCoordinates(pinnacle_data_t* coordinates) { + if (coordinates->xValue < CIRQUE_PINNACLE_X_LOWER) { + coordinates->xValue = CIRQUE_PINNACLE_X_LOWER; + } else if (coordinates->xValue > CIRQUE_PINNACLE_X_UPPER) { + coordinates->xValue = CIRQUE_PINNACLE_X_UPPER; + } + if (coordinates->yValue < CIRQUE_PINNACLE_Y_LOWER) { + coordinates->yValue = CIRQUE_PINNACLE_Y_LOWER; + } else if (coordinates->yValue > CIRQUE_PINNACLE_Y_UPPER) { + coordinates->yValue = CIRQUE_PINNACLE_Y_UPPER; + } +} + +uint16_t cirque_pinnacle_get_scale(void) { return scale_data; } +void cirque_pinnacle_set_scale(uint16_t scale) { scale_data = scale; } + +// Scales data to desired X & Y resolution +void cirque_pinnacle_scale_data(pinnacle_data_t* coordinates, uint16_t xResolution, uint16_t yResolution) { + uint32_t xTemp = 0; + uint32_t yTemp = 0; + + ClipCoordinates(coordinates); + + xTemp = coordinates->xValue; + yTemp = coordinates->yValue; + + // translate coordinates to (0, 0) reference by subtracting edge-offset + xTemp -= CIRQUE_PINNACLE_X_LOWER; + yTemp -= CIRQUE_PINNACLE_Y_LOWER; + + // scale coordinates to (xResolution, yResolution) range + coordinates->xValue = (uint16_t)(xTemp * xResolution / CIRQUE_PINNACLE_X_RANGE); + coordinates->yValue = (uint16_t)(yTemp * yResolution / CIRQUE_PINNACLE_Y_RANGE); +} + +// Clears Status1 register flags (SW_CC and SW_DR) +void cirque_pinnacle_clear_flags() { + RAP_Write(STATUS_1, 0x00); + wait_us(50); +} + +// Enables/Disables the feed +void cirque_pinnacle_enable_feed(bool feedEnable) { + uint8_t temp; + + RAP_ReadBytes(FEEDCONFIG_1, &temp, 1); // Store contents of FeedConfig1 register + + if (feedEnable) { + temp |= 0x01; // Set Feed Enable bit + RAP_Write(0x04, temp); + } else { + temp &= ~0x01; // Clear Feed Enable bit + RAP_Write(0x04, temp); + } +} + +/* ERA (Extended Register Access) Functions */ +// Reads bytes from an extended register at
(16-bit address), +// stores values in <*data> +void ERA_ReadBytes(uint16_t address, uint8_t* data, uint16_t count) { + uint8_t ERAControlValue = 0xFF; + + cirque_pinnacle_enable_feed(false); // Disable feed + + RAP_Write(ERA_HIGH_BYTE, (uint8_t)(address >> 8)); // Send upper byte of ERA address + RAP_Write(ERA_LOW_BYTE, (uint8_t)(address & 0x00FF)); // Send lower byte of ERA address + + for (uint16_t i = 0; i < count; i++) { + RAP_Write(ERA_CONTROL, 0x05); // Signal ERA-read (auto-increment) to Pinnacle + + // Wait for status register 0x1E to clear + do { + RAP_ReadBytes(ERA_CONTROL, &ERAControlValue, 1); + } while (ERAControlValue != 0x00); + + RAP_ReadBytes(ERA_VALUE, data + i, 1); + + cirque_pinnacle_clear_flags(); + } +} + +// Writes a byte, , to an extended register at
(16-bit address) +void ERA_WriteByte(uint16_t address, uint8_t data) { + uint8_t ERAControlValue = 0xFF; + + cirque_pinnacle_enable_feed(false); // Disable feed + + RAP_Write(ERA_VALUE, data); // Send data byte to be written + + RAP_Write(ERA_HIGH_BYTE, (uint8_t)(address >> 8)); // Upper byte of ERA address + RAP_Write(ERA_LOW_BYTE, (uint8_t)(address & 0x00FF)); // Lower byte of ERA address + + RAP_Write(ERA_CONTROL, 0x02); // Signal an ERA-write to Pinnacle + + // Wait for status register 0x1E to clear + do { + RAP_ReadBytes(ERA_CONTROL, &ERAControlValue, 1); + } while (ERAControlValue != 0x00); + + cirque_pinnacle_clear_flags(); +} + +void cirque_pinnacle_set_adc_attenuation(uint8_t adcGain) { + uint8_t temp = 0x00; + + ERA_ReadBytes(0x0187, &temp, 1); + temp &= 0x3F; // clear top two bits + temp |= adcGain; + ERA_WriteByte(0x0187, temp); + ERA_ReadBytes(0x0187, &temp, 1); +} + +// Changes thresholds to improve detection of fingers +void cirque_pinnacle_tune_edge_sensitivity(void) { + uint8_t temp = 0x00; + + ERA_ReadBytes(0x0149, &temp, 1); + ERA_WriteByte(0x0149, 0x04); + ERA_ReadBytes(0x0149, &temp, 1); + + ERA_ReadBytes(0x0168, &temp, 1); + ERA_WriteByte(0x0168, 0x03); + ERA_ReadBytes(0x0168, &temp, 1); +} + +/* Pinnacle-based TM040040 Functions */ +void cirque_pinnacle_init(void) { +#if defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) + spi_init(); +#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) + i2c_init(); +#endif + + touchpad_init = true; + // Host clears SW_CC flag + cirque_pinnacle_clear_flags(); + + // Host configures bits of registers 0x03 and 0x05 + RAP_Write(SYSCONFIG_1, SYSCONFIG_1_VALUE); + RAP_Write(FEEDCONFIG_2, FEEDCONFIG_2_VALUE); + + // Host enables preferred output mode (absolute) + RAP_Write(FEEDCONFIG_1, FEEDCONFIG_1_VALUE); + + // Host sets z-idle packet count to 5 (default is 30) + RAP_Write(Z_IDLE_COUNT, Z_IDLE_COUNT_VALUE); + + cirque_pinnacle_set_adc_attenuation(0xFF); + cirque_pinnacle_tune_edge_sensitivity(); + cirque_pinnacle_enable_feed(true); +} + +// Reads XYZ data from Pinnacle registers 0x14 through 0x17 +// Stores result in pinnacle_data_t struct with xValue, yValue, and zValue members +pinnacle_data_t cirque_pinnacle_read_data(void) { + uint8_t data[6] = {0}; + pinnacle_data_t result = {0}; + RAP_ReadBytes(PACKET_BYTE_0, data, 6); + + cirque_pinnacle_clear_flags(); + + result.buttonFlags = data[0] & 0x3F; + result.xValue = data[2] | ((data[4] & 0x0F) << 8); + result.yValue = data[3] | ((data[4] & 0xF0) << 4); + result.zValue = data[5] & 0x3F; + + result.touchDown = (result.xValue != 0 || result.yValue != 0); + + return result; +} diff --git a/drivers/sensors/cirque_pinnacle.h b/drivers/sensors/cirque_pinnacle.h new file mode 100644 index 0000000000..db891122a6 --- /dev/null +++ b/drivers/sensors/cirque_pinnacle.h @@ -0,0 +1,74 @@ +// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license + +#pragma once + +#include +#include + +// Convenient way to store and access measurements +typedef struct { + uint16_t xValue; + uint16_t yValue; + uint16_t zValue; + uint8_t buttonFlags; + bool touchDown; +} pinnacle_data_t; + +void cirque_pinnacle_init(void); +pinnacle_data_t cirque_pinnacle_read_data(void); +void cirque_pinnacle_scale_data(pinnacle_data_t* coordinates, uint16_t xResolution, uint16_t yResolution); +uint16_t cirque_pinnacle_get_scale(void); +void cirque_pinnacle_set_scale(uint16_t scale); + +#ifndef CIRQUE_PINNACLE_TIMEOUT +# define CIRQUE_PINNACLE_TIMEOUT 20 +#endif + +// Coordinate scaling values +#ifndef CIRQUE_PINNACLE_X_LOWER +# define CIRQUE_PINNACLE_X_LOWER 127 // min "reachable" X value +#endif +#ifndef CIRQUE_PINNACLE_X_UPPER +# define CIRQUE_PINNACLE_X_UPPER 1919 // max "reachable" X value +#endif +#ifndef CIRQUE_PINNACLE_Y_LOWER +# define CIRQUE_PINNACLE_Y_LOWER 63 // min "reachable" Y value +#endif +#ifndef CIRQUE_PINNACLE_Y_UPPER +# define CIRQUE_PINNACLE_Y_UPPER 1471 // max "reachable" Y value +#endif +#ifndef CIRQUE_PINNACLE_X_RANGE +# define CIRQUE_PINNACLE_X_RANGE (CIRQUE_PINNACLE_X_UPPER - CIRQUE_PINNACLE_X_LOWER) +#endif +#ifndef CIRQUE_PINNACLE_Y_RANGE +# define CIRQUE_PINNACLE_Y_RANGE (CIRQUE_PINNACLE_Y_UPPER - CIRQUE_PINNACLE_Y_LOWER) +#endif + +#if defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) +# include "i2c_master.h" +// Cirque's 7-bit I2C Slave Address +# ifndef CIRQUE_PINNACLE_ADDR +# define CIRQUE_PINNACLE_ADDR 0x2A +# endif +#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) +# include "spi_master.h" +# ifndef CIRQUE_PINNACLE_CLOCK_SPEED +# define CIRQUE_PINNACLE_CLOCK_SPEED 10000000 +# endif +# ifndef CIRQUE_PINNACLE_SPI_LSBFIRST +# define CIRQUE_PINNACLE_SPI_LSBFIRST false +# endif +# ifndef CIRQUE_PINNACLE_SPI_MODE +# define CIRQUE_PINNACLE_SPI_MODE 1 +# endif +# ifndef CIRQUE_PINNACLE_SPI_DIVISOR +# ifdef __AVR__ +# define CIRQUE_PINNACLE_SPI_DIVISOR (F_CPU / CIRQUE_PINNACLE_CLOCK_SPEED) +# else +# define CIRQUE_PINNACLE_SPI_DIVISOR 64 +# endif +# ifndef CIRQUE_PINNACLE_SPI_CS_PIN +# error "No Chip Select pin has been defined -- missing CIRQUE_PINNACLE_SPI_CS_PIN define" +# endif +# endif +#endif diff --git a/drivers/sensors/cirque_pinnacle_i2c.c b/drivers/sensors/cirque_pinnacle_i2c.c new file mode 100644 index 0000000000..81dd982b0c --- /dev/null +++ b/drivers/sensors/cirque_pinnacle_i2c.c @@ -0,0 +1,43 @@ +// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license +#include "cirque_pinnacle.h" +#include "i2c_master.h" +#include "print.h" +#include "debug.h" +#include "stdio.h" + +// Masks for Cirque Register Access Protocol (RAP) +#define WRITE_MASK 0x80 +#define READ_MASK 0xA0 + +extern bool touchpad_init; + +/* RAP Functions */ +// Reads Pinnacle registers starting at
+void RAP_ReadBytes(uint8_t address, uint8_t* data, uint8_t count) { + uint8_t cmdByte = READ_MASK | address; // Form the READ command byte + if (touchpad_init) { + i2c_writeReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, NULL, 0, CIRQUE_PINNACLE_TIMEOUT); + if (i2c_readReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, data, count, CIRQUE_PINNACLE_TIMEOUT) != I2C_STATUS_SUCCESS) { +#ifdef CONSOLE_ENABLE + dprintf("error right touchpad\n"); +#endif + touchpad_init = false; + } + i2c_stop(); + } +} + +// Writes single-byte to
+void RAP_Write(uint8_t address, uint8_t data) { + uint8_t cmdByte = WRITE_MASK | address; // Form the WRITE command byte + + if (touchpad_init) { + if (i2c_writeReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, &data, sizeof(data), CIRQUE_PINNACLE_TIMEOUT) != I2C_STATUS_SUCCESS) { +#ifdef CONSOLE_ENABLE + dprintf("error right touchpad\n"); +#endif + touchpad_init = false; + } + i2c_stop(); + } +} diff --git a/drivers/sensors/cirque_pinnacle_spi.c b/drivers/sensors/cirque_pinnacle_spi.c new file mode 100644 index 0000000000..f3eee88758 --- /dev/null +++ b/drivers/sensors/cirque_pinnacle_spi.c @@ -0,0 +1,52 @@ +// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license +#include "cirque_pinnacle.h" +#include "spi_master.h" +#include "print.h" +#include "debug.h" + +// Masks for Cirque Register Access Protocol (RAP) +#define WRITE_MASK 0x80 +#define READ_MASK 0xA0 + +extern bool touchpad_init; + +/* RAP Functions */ +// Reads Pinnacle registers starting at