diff --git a/common_features.mk b/common_features.mk index 7abdce302c1..896fbb0129d 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 badeadc12be..031ee52c1c9 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 -* `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 +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. -Keep in mind that a report_mouse_t (here "mouseReport") has the following properties: +### 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 +``` + + +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 254ef2ee87e..c23d24d5af5 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 +// 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 -// Definitions for the ADNS serial line. -#ifndef ADNS_SCLK_PIN -# define ADNS_SCLK_PIN B7 -#endif +void adns5050_init(void) { + // Initialize the ADNS serial pins. + setPinOutput(ADNS5050_SCLK_PIN); + setPinOutput(ADNS5050_SDIO_PIN); + setPinOutput(ADNS5050_CS_PIN); -#ifndef ADNS_SDIO_PIN -# define ADNS_SDIO_PIN C6 -#endif + // reboot the adns. + // if the adns hasn't initialized yet, this is harmless. + adns5050_write_reg(REG_CHIP_RESET, 0x5a); -#ifndef ADNS_CS_PIN -# define ADNS_CS_PIN B4 -#endif + // wait maximum time before adns is ready. + // this ensures that the adns is actuall ready after reset. + wait_ms(55); -#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); + // 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 -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); + 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 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 5e9edc29624..e45a2501964 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 -#ifdef CONSOLE_ENABLE -void print_byte(uint8_t byte); +#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 + +#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 b4f683452cd..c52f9918045 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 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 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 d19ded4012f..e75a869c03d 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 e698a401b94..d86ecbbd9eb 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 00000000000..0f4d1d7a4c2 --- /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 00000000000..6892a08817f --- /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 00000000000..b807c4f0766 --- /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 00000000000..db891122a66 --- /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 00000000000..81dd982b0cc --- /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 00000000000..f3eee887584 --- /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
+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) { + if (spi_start(CIRQUE_PINNACLE_SPI_CS_PIN, CIRQUE_TRACKPAD_SPI_LSBFIRST, CIRQUE_PINNACLE_SPI_MODE, CIRQUE_PINNACLE_SPI_DIVISOR)) { + spi_write(cmdByte); + spi_read(); // filler + spi_read(); // filler + for (uint8_t i = 0; i < count; i++) { + data[i] = spi_read(); // each sepsequent read gets another register's contents + } + } else { +#ifdef CONSOLE_ENABLE + dprintf("error right touchpad\n"); +#endif + touchpad_init = false; + j + } + spi_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 (spi_start(CIRQUE_PINNACLE_SPI_CS_PIN, CIRQUE_TRACKPAD_SPI_LSBFIRST, CIRQUE_PINNACLE_SPI_MODE, CIRQUE_PINNACLE_SPI_DIVISOR)) { + spi_write(cmdByte); + spi_write(data); + } else { +#ifdef CONSOLE_ENABLE + dprintf("error right touchpad\n"); +#endif + touchpad_init = false; + } + spi_stop(); + } +} diff --git a/drivers/sensors/pimoroni_trackball.c b/drivers/sensors/pimoroni_trackball.c index 48098ff0cca..7d390056ead 100644 --- a/drivers/sensors/pimoroni_trackball.c +++ b/drivers/sensors/pimoroni_trackball.c @@ -17,73 +17,55 @@ #include "pimoroni_trackball.h" #include "i2c_master.h" #include "print.h" +#include "debug.h" +#include "timer.h" -#ifndef PIMORONI_TRACKBALL_ADDRESS -# define PIMORONI_TRACKBALL_ADDRESS 0x0A -#endif -#ifndef PIMORONI_TRACKBALL_INTERVAL_MS -# define PIMORONI_TRACKBALL_INTERVAL_MS 8 -#endif -#ifndef PIMORONI_TRACKBALL_MOUSE_SCALE -# define PIMORONI_TRACKBALL_MOUSE_SCALE 5 -#endif -#ifndef PIMORONI_TRACKBALL_SCROLL_SCALE -# define PIMORONI_TRACKBALL_SCROLL_SCALE 1 -#endif -#ifndef PIMORONI_TRACKBALL_DEBOUNCE_CYCLES -# define PIMORONI_TRACKBALL_DEBOUNCE_CYCLES 20 -#endif -#ifndef PIMORONI_TRACKBALL_ERROR_COUNT -# define PIMORONI_TRACKBALL_ERROR_COUNT 10 -#endif +// clang-format off +#define PIMORONI_TRACKBALL_REG_LED_RED 0x00 +#define PIMORONI_TRACKBALL_REG_LED_GRN 0x01 +#define PIMORONI_TRACKBALL_REG_LED_BLU 0x02 +#define PIMORONI_TRACKBALL_REG_LED_WHT 0x03 +#define PIMORONI_TRACKBALL_REG_LEFT 0x04 +#define PIMORONI_TRACKBALL_REG_RIGHT 0x05 +#define PIMORONI_TRACKBALL_REG_UP 0x06 +#define PIMORONI_TRACKBALL_REG_DOWN 0x07 +// clang-format on -#define TRACKBALL_TIMEOUT 100 -#define TRACKBALL_REG_LED_RED 0x00 -#define TRACKBALL_REG_LED_GRN 0x01 -#define TRACKBALL_REG_LED_BLU 0x02 -#define TRACKBALL_REG_LED_WHT 0x03 -#define TRACKBALL_REG_LEFT 0x04 -#define TRACKBALL_REG_RIGHT 0x05 -#define TRACKBALL_REG_UP 0x06 -#define TRACKBALL_REG_DOWN 0x07 +static uint16_t precision = 128; -static pimoroni_data current_pimoroni_data; -static report_mouse_t mouse_report; -static bool scrolling = false; -static int16_t x_offset = 0; -static int16_t y_offset = 0; -static int16_t h_offset = 0; -static int16_t v_offset = 0; -static uint16_t precision = 128; -static uint8_t error_count = 0; +float pimoroni_trackball_get_precision(void) { return ((float)precision / 128); } +void pimoroni_trackball_set_precision(float floatprecision) { precision = (floatprecision * 128); } -float trackball_get_precision(void) { return ((float)precision / 128); } -void trackball_set_precision(float floatprecision) { precision = (floatprecision * 128); } -bool trackball_is_scrolling(void) { return scrolling; } -void trackball_set_scrolling(bool scroll) { scrolling = scroll; } - -void trackball_set_rgbw(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { +void pimoroni_trackball_set_rgbw(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { uint8_t data[4] = {r, g, b, w}; - __attribute__((unused)) i2c_status_t status = i2c_writeReg(PIMORONI_TRACKBALL_ADDRESS << 1, TRACKBALL_REG_LED_RED, data, sizeof(data), TRACKBALL_TIMEOUT); -#ifdef TRACKBALL_DEBUG - dprintf("Trackball RGBW i2c_status_t: %d\n", status); + __attribute__((unused)) i2c_status_t status = i2c_writeReg(PIMORONI_TRACKBALL_ADDRESS << 1, PIMORONI_TRACKBALL_REG_LED_RED, data, sizeof(data), PIMORONI_TRACKBALL_TIMEOUT); + +#ifdef CONSOLE_ENABLE + if (debug_mouse) dprintf("Trackball RGBW i2c_status_t: %d\n", status); #endif } -i2c_status_t read_pimoroni_trackball(pimoroni_data* data) { - i2c_status_t status = i2c_readReg(PIMORONI_TRACKBALL_ADDRESS << 1, TRACKBALL_REG_LEFT, (uint8_t*)data, sizeof(*data), TRACKBALL_TIMEOUT); -#ifdef TRACKBALL_DEBUG - dprintf("Trackball READ i2c_status_t: %d\nLeft: %d\nRight: %d\nUp: %d\nDown: %d\nSwtich: %d\n", status, data->left, data->right, data->up, data->down, data->click); +i2c_status_t read_pimoroni_trackball(pimoroni_data_t* data) { + i2c_status_t status = i2c_readReg(PIMORONI_TRACKBALL_ADDRESS << 1, PIMORONI_TRACKBALL_REG_LEFT, (uint8_t*)data, sizeof(*data), PIMORONI_TRACKBALL_TIMEOUT); +#ifdef CONSOLE_ENABLE + if (debug_mouse) { + static uint16_t d_timer; + if (timer_elapsed(d_timer) > PIMORONI_TRACKBALL_DEBUG_INTERVAL) { + dprintf("Trackball READ i2c_status_t: %d L: %d R: %d Up: %d D: %d SW: %d\n", status, data->left, data->right, data->up, data->down, data->click); + d_timer = timer_read(); + } + } #endif + return status; } -__attribute__((weak)) void pointing_device_init(void) { +__attribute__((weak)) void pimironi_trackball_device_init(void) { i2c_init(); - trackball_set_rgbw(0x00, 0x00, 0x00, 0x00); + pimoroni_trackball_set_rgbw(0x00, 0x00, 0x00, 0x00); } -int16_t trackball_get_offsets(uint8_t negative_dir, uint8_t positive_dir, uint8_t scale) { +int16_t pimoroni_trackball_get_offsets(uint8_t negative_dir, uint8_t positive_dir, uint8_t scale) { uint8_t offset = 0; bool isnegative = false; if (negative_dir > positive_dir) { @@ -96,7 +78,7 @@ int16_t trackball_get_offsets(uint8_t negative_dir, uint8_t positive_dir, uint8_ return isnegative ? -(int16_t)(magnitude) : (int16_t)(magnitude); } -void trackball_adapt_values(int8_t* mouse, int16_t* offset) { +void pimoroni_trackball_adapt_values(int8_t* mouse, int16_t* offset) { if (*offset > 127) { *mouse = 127; *offset -= 127; @@ -108,94 +90,3 @@ void trackball_adapt_values(int8_t* mouse, int16_t* offset) { *offset = 0; } } - -__attribute__((weak)) void trackball_click(bool pressed, report_mouse_t* mouse) { -#ifdef PIMORONI_TRACKBALL_CLICK - if (pressed) { - mouse->buttons |= MOUSE_BTN1; - } else { - mouse->buttons &= ~MOUSE_BTN1; - } -#endif -} - -__attribute__((weak)) bool pointing_device_task_user(pimoroni_data* trackball_data) { return true; }; - -__attribute__((weak)) void pointing_device_task() { - static fast_timer_t throttle = 0; - static uint16_t debounce = 0; - - if (error_count < PIMORONI_TRACKBALL_ERROR_COUNT && timer_elapsed_fast(throttle) >= PIMORONI_TRACKBALL_INTERVAL_MS) { - i2c_status_t status = read_pimoroni_trackball(¤t_pimoroni_data); - - if (status == I2C_STATUS_SUCCESS) { - error_count = 0; - - if (pointing_device_task_user(¤t_pimoroni_data)) { - mouse_report = pointing_device_get_report(); - - if (!(current_pimoroni_data.click & 128)) { - trackball_click(false, &mouse_report); - if (!debounce) { - if (scrolling) { -#ifdef PIMORONI_TRACKBALL_INVERT_X - h_offset += trackball_get_offsets(current_pimoroni_data.right, current_pimoroni_data.left, PIMORONI_TRACKBALL_SCROLL_SCALE); -#else - h_offset -= trackball_get_offsets(current_pimoroni_data.right, current_pimoroni_data.left, PIMORONI_TRACKBALL_SCROLL_SCALE); -#endif -#ifdef PIMORONI_TRACKBALL_INVERT_Y - v_offset += trackball_get_offsets(current_pimoroni_data.down, current_pimoroni_data.up, PIMORONI_TRACKBALL_SCROLL_SCALE); -#else - v_offset -= trackball_get_offsets(current_pimoroni_data.down, current_pimoroni_data.up, PIMORONI_TRACKBALL_SCROLL_SCALE); -#endif - } else { -#ifdef PIMORONI_TRACKBALL_INVERT_X - x_offset -= trackball_get_offsets(current_pimoroni_data.right, current_pimoroni_data.left, PIMORONI_TRACKBALL_MOUSE_SCALE); -#else - x_offset += trackball_get_offsets(current_pimoroni_data.right, current_pimoroni_data.left, PIMORONI_TRACKBALL_MOUSE_SCALE); -#endif -#ifdef PIMORONI_TRACKBALL_INVERT_Y - y_offset -= trackball_get_offsets(current_pimoroni_data.down, current_pimoroni_data.up, PIMORONI_TRACKBALL_MOUSE_SCALE); -#else - y_offset += trackball_get_offsets(current_pimoroni_data.down, current_pimoroni_data.up, PIMORONI_TRACKBALL_MOUSE_SCALE); -#endif - } - if (scrolling) { -#ifndef PIMORONI_TRACKBALL_ROTATE - trackball_adapt_values(&mouse_report.h, &h_offset); - trackball_adapt_values(&mouse_report.v, &v_offset); -#else - trackball_adapt_values(&mouse_report.h, &v_offset); - trackball_adapt_values(&mouse_report.v, &h_offset); -#endif - mouse_report.x = 0; - mouse_report.y = 0; - } else { -#ifndef PIMORONI_TRACKBALL_ROTATE - trackball_adapt_values(&mouse_report.x, &x_offset); - trackball_adapt_values(&mouse_report.y, &y_offset); -#else - trackball_adapt_values(&mouse_report.x, &y_offset); - trackball_adapt_values(&mouse_report.y, &x_offset); -#endif - mouse_report.h = 0; - mouse_report.v = 0; - } - } else { - debounce--; - } - } else { - trackball_click(true, &mouse_report); - debounce = PIMORONI_TRACKBALL_DEBOUNCE_CYCLES; - } - } - } else { - error_count++; - } - - pointing_device_set_report(mouse_report); - pointing_device_send(); - - throttle = timer_read_fast(); - } -} diff --git a/drivers/sensors/pimoroni_trackball.h b/drivers/sensors/pimoroni_trackball.h index 6b2a41425d4..59ee8724ba6 100644 --- a/drivers/sensors/pimoroni_trackball.h +++ b/drivers/sensors/pimoroni_trackball.h @@ -16,22 +16,46 @@ */ #pragma once -#include "quantum.h" -#include "pointing_device.h" +#include +#include "report.h" +#include "i2c_master.h" -typedef struct pimoroni_data { +#ifndef PIMORONI_TRACKBALL_ADDRESS +# define PIMORONI_TRACKBALL_ADDRESS 0x0A +#endif +#ifndef PIMORONI_TRACKBALL_INTERVAL_MS +# define PIMORONI_TRACKBALL_INTERVAL_MS 8 +#endif +#ifndef PIMORONI_TRACKBALL_SCALE +# define PIMORONI_TRACKBALL_SCALE 5 +#endif +#ifndef PIMORONI_TRACKBALL_DEBOUNCE_CYCLES +# define PIMORONI_TRACKBALL_DEBOUNCE_CYCLES 20 +#endif +#ifndef PIMORONI_TRACKBALL_ERROR_COUNT +# define PIMORONI_TRACKBALL_ERROR_COUNT 10 +#endif + +#ifndef PIMORONI_TRACKBALL_TIMEOUT +# define PIMORONI_TRACKBALL_TIMEOUT 100 +#endif + +#ifndef PIMORONI_TRACKBALL_DEBUG_INTERVAL +# define PIMORONI_TRACKBALL_DEBUG_INTERVAL 100 +#endif + +typedef struct { uint8_t left; uint8_t right; uint8_t up; uint8_t down; uint8_t click; -} pimoroni_data; +} pimoroni_data_t; -void trackball_set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white); -void trackball_click(bool pressed, report_mouse_t* mouse); -int16_t trackball_get_offsets(uint8_t negative_dir, uint8_t positive_dir, uint8_t scale); -void trackball_adapt_values(int8_t* mouse, int16_t* offset); -float trackball_get_precision(void); -void trackball_set_precision(float precision); -bool trackball_is_scrolling(void); -void trackball_set_scrolling(bool scroll); +void pimironi_trackball_device_init(void); +void pimoroni_trackball_set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white); +int16_t pimoroni_trackball_get_offsets(uint8_t negative_dir, uint8_t positive_dir, uint8_t scale); +void pimoroni_trackball_adapt_values(int8_t* mouse, int16_t* offset); +float pimoroni_trackball_get_precision(void); +void pimoroni_trackball_set_precision(float precision); +i2c_status_t read_pimoroni_trackball(pimoroni_data_t* data); diff --git a/drivers/sensors/pmw3360.c b/drivers/sensors/pmw3360.c index 79b653e452a..2b27dccbb64 100644 --- a/drivers/sensors/pmw3360.c +++ b/drivers/sensors/pmw3360.c @@ -20,62 +20,71 @@ #include "wait.h" #include "debug.h" #include "print.h" -#include "pmw3360_firmware.h" +#include PMW3360_FIRMWARE_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_Raw_Data_Sum 0x08 -#define REG_Maximum_Raw_data 0x09 -#define REG_Minimum_Raw_data 0x0A -#define REG_Shutter_Lower 0x0B -#define REG_Shutter_Upper 0x0C -#define REG_Control 0x0D -#define REG_Config1 0x0F -#define REG_Config2 0x10 -#define REG_Angle_Tune 0x11 -#define REG_Frame_Capture 0x12 -#define REG_SROM_Enable 0x13 -#define REG_Run_Downshift 0x14 -#define REG_Rest1_Rate_Lower 0x15 -#define REG_Rest1_Rate_Upper 0x16 -#define REG_Rest1_Downshift 0x17 -#define REG_Rest2_Rate_Lower 0x18 -#define REG_Rest2_Rate_Upper 0x19 -#define REG_Rest2_Downshift 0x1A -#define REG_Rest3_Rate_Lower 0x1B -#define REG_Rest3_Rate_Upper 0x1C -#define REG_Observation 0x24 -#define REG_Data_Out_Lower 0x25 -#define REG_Data_Out_Upper 0x26 -#define REG_Raw_Data_Dump 0x29 -#define REG_SROM_ID 0x2A -#define REG_Min_SQ_Run 0x2B -#define REG_Raw_Data_Threshold 0x2C -#define REG_Config5 0x2F -#define REG_Power_Up_Reset 0x3A -#define REG_Shutdown 0x3B -#define REG_Inverse_Product_ID 0x3F -#define REG_LiftCutoff_Tune3 0x41 -#define REG_Angle_Snap 0x42 -#define REG_LiftCutoff_Tune1 0x4A -#define REG_Motion_Burst 0x50 -#define REG_LiftCutoff_Tune_Timeout 0x58 +// 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_Raw_Data_Sum 0x08 +#define REG_Maximum_Raw_data 0x09 +#define REG_Minimum_Raw_data 0x0A +#define REG_Shutter_Lower 0x0B +#define REG_Shutter_Upper 0x0C +#define REG_Control 0x0D +#define REG_Config1 0x0F +#define REG_Config2 0x10 +#define REG_Angle_Tune 0x11 +#define REG_Frame_Capture 0x12 +#define REG_SROM_Enable 0x13 +#define REG_Run_Downshift 0x14 +#define REG_Rest1_Rate_Lower 0x15 +#define REG_Rest1_Rate_Upper 0x16 +#define REG_Rest1_Downshift 0x17 +#define REG_Rest2_Rate_Lower 0x18 +#define REG_Rest2_Rate_Upper 0x19 +#define REG_Rest2_Downshift 0x1A +#define REG_Rest3_Rate_Lower 0x1B +#define REG_Rest3_Rate_Upper 0x1C +#define REG_Observation 0x24 +#define REG_Data_Out_Lower 0x25 +#define REG_Data_Out_Upper 0x26 +#define REG_Raw_Data_Dump 0x29 +#define REG_SROM_ID 0x2A +#define REG_Min_SQ_Run 0x2B +#define REG_Raw_Data_Threshold 0x2C +#define REG_Config5 0x2F +#define REG_Power_Up_Reset 0x3A +#define REG_Shutdown 0x3B +#define REG_Inverse_Product_ID 0x3F +#define REG_LiftCutoff_Tune3 0x41 +#define REG_Angle_Snap 0x42 +#define REG_LiftCutoff_Tune1 0x4A +#define REG_Motion_Burst 0x50 +#define REG_LiftCutoff_Tune_Timeout 0x58 #define REG_LiftCutoff_Tune_Min_Length 0x5A -#define REG_SROM_Load_Burst 0x62 -#define REG_Lift_Config 0x63 -#define REG_Raw_Data_Burst 0x64 -#define REG_LiftCutoff_Tune2 0x65 +#define REG_SROM_Load_Burst 0x62 +#define REG_Lift_Config 0x63 +#define REG_Raw_Data_Burst 0x64 +#define REG_LiftCutoff_Tune2 0x65 +// clang-format on + +#ifndef MAX_CPI +# define MAX_CPI 0x77 // limits to 0--119, should be max cpi/100 +#endif bool _inBurst = false; +#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 +#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt))) bool spi_start_adv(void) { bool status = spi_start(PMW3360_CS_PIN, PMW3360_SPI_LSBFIRST, PMW3360_SPI_MODE, PMW3360_SPI_DIVISOR); @@ -124,20 +133,20 @@ uint8_t spi_read_adv(uint8_t reg_addr) { return data; } -void pmw_set_cpi(uint16_t cpi) { - uint8_t cpival = constrain((cpi / 100) - 1, 0, 0x77); // limits to 0--119 +void pmw3360_set_cpi(uint16_t cpi) { + uint8_t cpival = constrain((cpi / 100) - 1, 0, MAX_CPI); spi_start_adv(); spi_write_adv(REG_Config1, cpival); spi_stop(); } -uint16_t pmw_get_cpi(void) { +uint16_t pmw3360_get_cpi(void) { uint8_t cpival = spi_read_adv(REG_Config1); return (uint16_t)(cpival & 0xFF) * 100; } -bool pmw_spi_init(void) { +bool pmw3360_init(void) { setPinOutput(PMW3360_CS_PIN); spi_init(); @@ -164,12 +173,12 @@ bool pmw_spi_init(void) { spi_read_adv(REG_Delta_Y_L); spi_read_adv(REG_Delta_Y_H); - pmw_upload_firmware(); + pmw3360_upload_firmware(); spi_stop_adv(); wait_ms(10); - pmw_set_cpi(PMW3360_CPI); + pmw3360_set_cpi(PMW3360_CPI); wait_ms(1); @@ -177,14 +186,14 @@ bool pmw_spi_init(void) { spi_write_adv(REG_Angle_Tune, constrain(ROTATIONAL_TRANSFORM_ANGLE, -30, 30)); - bool init_success = pmw_check_signature(); + bool init_success = pmw3360_check_signature(); writePinLow(PMW3360_CS_PIN); return init_success; } -void pmw_upload_firmware(void) { +void pmw3360_upload_firmware(void) { spi_write_adv(REG_SROM_Enable, 0x1d); wait_ms(10); @@ -211,16 +220,18 @@ void pmw_upload_firmware(void) { wait_ms(10); } -bool pmw_check_signature(void) { +bool pmw3360_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 + return (pid == firmware_signature[0] && iv_pid == firmware_signature[1] && SROM_ver == firmware_signature[2]); // signature for SROM 0x04 } -report_pmw_t pmw_read_burst(void) { +report_pmw3360_t pmw3360_read_burst(void) { if (!_inBurst) { +#ifdef CONSOLE_ENABLE dprintf("burst on"); +#endif spi_write_adv(REG_Motion_Burst, 0x00); _inBurst = true; } @@ -229,12 +240,7 @@ report_pmw_t pmw_read_burst(void) { spi_write(REG_Motion_Burst); wait_us(35); // waits for tSRAD - report_pmw_t data; - data.motion = 0; - data.dx = 0; - data.mdx = 0; - data.dy = 0; - data.mdx = 0; + report_pmw3360_t data = {0}; data.motion = spi_read(); spi_write(0x00); // skip Observation @@ -245,6 +251,7 @@ report_pmw_t pmw_read_burst(void) { spi_stop(); +#ifdef CONSOLE_ENABLE if (debug_mouse) { print_byte(data.motion); print_byte(data.dx); @@ -253,6 +260,7 @@ report_pmw_t pmw_read_burst(void) { print_byte(data.mdy); dprintf("\n"); } +#endif data.isMotion = (data.motion & 0x80) != 0; data.isOnSurface = (data.motion & 0x08) == 0; diff --git a/drivers/sensors/pmw3360.h b/drivers/sensors/pmw3360.h index 7429a6ba09e..7b2bef5b80b 100644 --- a/drivers/sensors/pmw3360.h +++ b/drivers/sensors/pmw3360.h @@ -18,6 +18,8 @@ #pragma once +#include +#include "report.h" #include "spi_master.h" #ifndef PMW3360_CPI @@ -25,7 +27,7 @@ #endif #ifndef PMW3360_CLOCK_SPEED -# define PMW3360_CLOCK_SPEED 70000000 +# define PMW3360_CLOCK_SPEED 2000000 #endif #ifndef PMW3360_SPI_LSBFIRST @@ -52,6 +54,17 @@ # error "No chip select pin defined -- missing PMW3360_CS_PIN" #endif +/* +The pmw33660 and pmw3389 use the same registers and timing and such. +The only differences between the two is the firmware used, and the +range for the DPI. So add a semi-secret hack to allow use of the +pmw3389's firmware blob. Also, can set the max cpi range too. +This should work for the 3390 and 3391 too, in theory. +*/ +#ifndef PMW3360_FIRMWARE_H +# define PMW3360_FIRMWARE_H "pmw3360_firmware.h" +#endif + #ifdef CONSOLE_ENABLE void print_byte(uint8_t byte); #endif @@ -64,19 +77,18 @@ typedef struct { int8_t mdx; int16_t dy; // displacement on y directions. int8_t mdy; -} report_pmw_t; +} report_pmw3360_t; -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); -uint16_t pmw_get_cpi(void); -void pmw_upload_firmware(void); -bool pmw_check_signature(void); -report_pmw_t pmw_read_burst(void); +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 pmw3360_init(void); +void pmw3360_set_cpi(uint16_t cpi); +uint16_t pmw3360_get_cpi(void); +void pmw3360_upload_firmware(void); +bool pmw3360_check_signature(void); +report_pmw3360_t pmw3360_read_burst(void); #define degToRad(angleInDegrees) ((angleInDegrees)*M_PI / 180.0) #define radToDeg(angleInRadians) ((angleInRadians)*180.0 / M_PI) -#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt))) diff --git a/drivers/sensors/pmw3360_firmware.h b/drivers/sensors/pmw3360_firmware.h index 4d5fe7a0b97..ed9fda5a751 100644 --- a/drivers/sensors/pmw3360_firmware.h +++ b/drivers/sensors/pmw3360_firmware.h @@ -20,8 +20,13 @@ #include "progmem.h" +// PID, Inverse PID, SROM version +const uint8_t firmware_signature[] PROGMEM = {0x42, 0xBD, 0x04}; + #define FIRMWARE_LENGTH 4094 +// Firmware Blob foor PMW3360 + // clang-format off const uint8_t firmware_data[FIRMWARE_LENGTH] PROGMEM = { 0x01, 0x04, 0x8E, 0x96, 0x6E, 0x77, 0x3E, 0xFE, 0x7E, 0x5F, 0x1D, 0xB8, 0xF2, 0x66, 0x4E, 0xFF, diff --git a/drivers/sensors/pmw3389_firmware.h b/drivers/sensors/pmw3389_firmware.h new file mode 100644 index 00000000000..0564dab73af --- /dev/null +++ b/drivers/sensors/pmw3389_firmware.h @@ -0,0 +1,303 @@ +/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) + * Copyright 2019 Sunjun Kim + * Copyright 2020 Ploopy Corporation + * + * 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 + +// PID, Inverse PID, SROM version +const uint8_t firmware_signature[] PROGMEM = {0x42, 0xBD, 0x04}; + +// clang-format off +// Firmware Blob foor PMW3389 +const uint16_t firmware_length = 4094; +// clang-format off +const uint8_t firmware_data[] PROGMEM = { // SROM 0x04 +0x01, 0xe8, 0xba, 0x26, 0x0b, 0xb2, 0xbe, 0xfe, 0x7e, 0x5f, 0x3c, 0xdb, 0x15, 0xa8, 0xb3, +0xe4, 0x2b, 0xb5, 0xe8, 0x53, 0x07, 0x6d, 0x3b, 0xd1, 0x20, 0xc2, 0x06, 0x6f, 0x3d, 0xd9, +0x11, 0xa0, 0xc2, 0xe7, 0x2d, 0xb9, 0xd1, 0x20, 0xa3, 0xa5, 0xc8, 0xf3, 0x64, 0x4a, 0xf7, +0x4d, 0x18, 0x93, 0xa4, 0xca, 0xf7, 0x6c, 0x5a, 0x36, 0xee, 0x5e, 0x3e, 0xfe, 0x7e, 0x7e, +0x5f, 0x1d, 0x99, 0xb0, 0xc3, 0xe5, 0x29, 0xd3, 0x03, 0x65, 0x48, 0x12, 0x87, 0x6d, 0x58, +0x32, 0xe6, 0x2f, 0xdc, 0x3a, 0xf2, 0x4f, 0xfd, 0x59, 0x11, 0x81, 0x61, 0x21, 0xc0, 0x02, +0x86, 0x8e, 0x7f, 0x5d, 0x38, 0xf2, 0x47, 0x0c, 0x7b, 0x55, 0x28, 0xb3, 0xe4, 0x4a, 0x16, +0xab, 0xbf, 0xdd, 0x38, 0xf2, 0x66, 0x4e, 0xff, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xa5, 0xc8, +0x12, 0xa6, 0xaf, 0xdc, 0x3a, 0xd1, 0x41, 0x60, 0x75, 0x58, 0x24, 0x92, 0xd4, 0x72, 0x6c, +0xe0, 0x2f, 0xfd, 0x23, 0x8d, 0x1c, 0x5b, 0xb2, 0x97, 0x36, 0x3d, 0x0b, 0xa2, 0x49, 0xb1, +0x58, 0xf2, 0x1f, 0xc0, 0xcb, 0xf8, 0x41, 0x4f, 0xcd, 0x1e, 0x6b, 0x39, 0xa7, 0x2b, 0xe9, +0x30, 0x16, 0x83, 0xd2, 0x0e, 0x47, 0x8f, 0xe3, 0xb1, 0xdf, 0xa2, 0x15, 0xdb, 0x5d, 0x30, +0xc5, 0x1a, 0xab, 0x31, 0x99, 0xf3, 0xfa, 0xb2, 0x86, 0x69, 0xad, 0x7a, 0xe8, 0xa7, 0x18, +0x6a, 0xcc, 0xc8, 0x65, 0x23, 0x87, 0xa8, 0x5f, 0xf5, 0x21, 0x59, 0x75, 0x09, 0x71, 0x45, +0x55, 0x25, 0x4b, 0xda, 0xa1, 0xc3, 0xf7, 0x41, 0xab, 0x59, 0xd9, 0x74, 0x12, 0x55, 0x5f, +0xbc, 0xaf, 0xd9, 0xfd, 0xb0, 0x1e, 0xa3, 0x0f, 0xff, 0xde, 0x11, 0x16, 0x6a, 0xae, 0x0e, +0xe1, 0x5d, 0x3c, 0x10, 0x43, 0x9a, 0xa1, 0x0b, 0x24, 0x8f, 0x0d, 0x7f, 0x0b, 0x5e, 0x4c, +0x42, 0xa4, 0x84, 0x2c, 0x40, 0xd0, 0x55, 0x39, 0xe6, 0x4b, 0xf8, 0x9b, 0x2f, 0xdc, 0x28, +0xff, 0xfa, 0xb5, 0x85, 0x19, 0xe5, 0x28, 0xa1, 0x77, 0xaa, 0x73, 0xf3, 0x03, 0xc7, 0x62, +0xa6, 0x91, 0x18, 0xc9, 0xb0, 0xcd, 0x05, 0xdc, 0xca, 0x81, 0x26, 0x1a, 0x47, 0x40, 0xda, +0x36, 0x7d, 0x6a, 0x53, 0xc8, 0x5a, 0x77, 0x5d, 0x19, 0xa4, 0x1b, 0x23, 0x83, 0xd0, 0xb2, +0xaa, 0x0e, 0xbf, 0x77, 0x4e, 0x3a, 0x3b, 0x59, 0x00, 0x31, 0x0d, 0x02, 0x1b, 0x88, 0x7a, +0xd4, 0xbd, 0x9d, 0xcc, 0x58, 0x04, 0x69, 0xf6, 0x3b, 0xca, 0x42, 0xe2, 0xfd, 0xc3, 0x3d, +0x39, 0xc5, 0xd0, 0x71, 0xe4, 0xc8, 0xb7, 0x3e, 0x3f, 0xc8, 0xe9, 0xca, 0xc9, 0x3f, 0x04, +0x4e, 0x1b, 0x79, 0xca, 0xa5, 0x61, 0xc2, 0xed, 0x1d, 0xa6, 0xda, 0x5a, 0xe9, 0x7f, 0x65, +0x8c, 0xbe, 0x12, 0x6e, 0xa4, 0x5b, 0x33, 0x2f, 0x84, 0x28, 0x9c, 0x1c, 0x88, 0x2d, 0xff, +0x07, 0xbf, 0xa6, 0xd7, 0x5a, 0x88, 0x86, 0xb0, 0x3f, 0xf6, 0x31, 0x5b, 0x11, 0x6d, 0xf5, +0x58, 0xeb, 0x58, 0x02, 0x9e, 0xb5, 0x9a, 0xb1, 0xff, 0x25, 0x9d, 0x8b, 0x4f, 0xb6, 0x0a, +0xf9, 0xea, 0x3e, 0x3f, 0x21, 0x09, 0x65, 0x21, 0x22, 0xfe, 0x3d, 0x4e, 0x11, 0x5b, 0x9e, +0x5a, 0x59, 0x8b, 0xdd, 0xd8, 0xce, 0xd6, 0xd9, 0x59, 0xd2, 0x1e, 0xfd, 0xef, 0x0d, 0x1b, +0xd9, 0x61, 0x7f, 0xd7, 0x2d, 0xad, 0x62, 0x09, 0xe5, 0x22, 0x63, 0xea, 0xc7, 0x31, 0xd9, +0xa1, 0x38, 0x80, 0x5c, 0xa7, 0x32, 0x82, 0xec, 0x1b, 0xa2, 0x49, 0x5a, 0x06, 0xd2, 0x7c, +0xc9, 0x96, 0x57, 0xbb, 0x17, 0x75, 0xfc, 0x7a, 0x8f, 0x0d, 0x77, 0xb5, 0x7a, 0x8e, 0x3e, +0xf4, 0xba, 0x2f, 0x69, 0x13, 0x26, 0xd6, 0xd9, 0x21, 0x60, 0x2f, 0x21, 0x3e, 0x87, 0xee, +0xfd, 0x87, 0x16, 0x0d, 0xc8, 0x08, 0x00, 0x25, 0x71, 0xac, 0x2c, 0x03, 0x2a, 0x37, 0x2d, +0xb3, 0x34, 0x09, 0x91, 0xe3, 0x06, 0x2c, 0x38, 0x37, 0x95, 0x3b, 0x17, 0x7a, 0xaf, 0xac, +0x99, 0x55, 0xab, 0x41, 0x39, 0x5f, 0x8e, 0xa6, 0x43, 0x80, 0x03, 0x88, 0x6f, 0x7d, 0xbd, +0x5a, 0xb4, 0x2b, 0x32, 0x23, 0x5a, 0xa9, 0x31, 0x32, 0x39, 0x4c, 0x5b, 0xf4, 0x6b, 0xaf, +0x66, 0x6f, 0x3c, 0x8e, 0x2d, 0x82, 0x97, 0x9f, 0x4a, 0x01, 0xdc, 0x99, 0x98, 0x00, 0xec, +0x38, 0x7a, 0x79, 0x70, 0xa6, 0x85, 0xd6, 0x21, 0x63, 0x0d, 0x45, 0x9a, 0x2e, 0x5e, 0xa7, +0xb1, 0xea, 0x66, 0x6a, 0xbc, 0x62, 0x2d, 0x7b, 0x7d, 0x85, 0xea, 0x95, 0x2f, 0xc0, 0xe8, +0x6f, 0x35, 0xa0, 0x3a, 0x02, 0x25, 0xbc, 0xb2, 0x5f, 0x5c, 0x43, 0x96, 0xcc, 0x26, 0xd2, +0x16, 0xb4, 0x96, 0x73, 0xd7, 0x13, 0xc7, 0xae, 0x53, 0x15, 0x31, 0x89, 0x68, 0x66, 0x6d, +0x2c, 0x92, 0x1f, 0xcc, 0x5b, 0xa7, 0x8f, 0x5d, 0xbb, 0xc9, 0xdb, 0xe8, 0x3b, 0x9d, 0x61, +0x74, 0x8b, 0x05, 0xa1, 0x58, 0x52, 0x68, 0xee, 0x3d, 0x39, 0x79, 0xa0, 0x9b, 0xdd, 0xe1, +0x55, 0xc9, 0x60, 0xeb, 0xad, 0xb8, 0x5b, 0xc2, 0x5a, 0xb5, 0x2c, 0x18, 0x55, 0xa9, 0x50, +0xc3, 0xf6, 0x72, 0x5f, 0xcc, 0xe2, 0xf4, 0x55, 0xb5, 0xd6, 0xb5, 0x4a, 0x99, 0xa5, 0x28, +0x74, 0x97, 0x18, 0xe8, 0xc0, 0x84, 0x89, 0x50, 0x03, 0x86, 0x4d, 0x1a, 0xb7, 0x09, 0x90, +0xa2, 0x01, 0x04, 0xbb, 0x73, 0x62, 0xcb, 0x97, 0x22, 0x70, 0x5d, 0x52, 0x41, 0x8e, 0xd9, +0x90, 0x15, 0xaa, 0xab, 0x0a, 0x31, 0x65, 0xb4, 0xda, 0xd0, 0xee, 0x24, 0xc9, 0x41, 0x91, +0x1e, 0xbc, 0x46, 0x70, 0x40, 0x9d, 0xda, 0x0e, 0x2a, 0xe4, 0xb2, 0x4c, 0x9f, 0xf2, 0xfc, +0xf3, 0x84, 0x17, 0x44, 0x1e, 0xd7, 0xca, 0x23, 0x1f, 0x3f, 0x5a, 0x22, 0x3d, 0xaf, 0x9b, +0x2d, 0xfc, 0x41, 0xad, 0x26, 0xb4, 0x45, 0x67, 0x0b, 0x80, 0x0e, 0xf9, 0x61, 0x37, 0xec, +0x3b, 0xf4, 0x4b, 0x14, 0xdf, 0x5a, 0x0c, 0x3a, 0x50, 0x0b, 0x14, 0x0c, 0x72, 0xae, 0xc6, +0xc5, 0xec, 0x35, 0x53, 0x2d, 0x59, 0xed, 0x91, 0x74, 0xe2, 0xc4, 0xc8, 0xf2, 0x25, 0x6b, +0x97, 0x6f, 0xc9, 0x76, 0xce, 0xa9, 0xb1, 0x99, 0x8f, 0x5a, 0x92, 0x3b, 0xc4, 0x8d, 0x54, +0x50, 0x40, 0x72, 0xd6, 0x90, 0x83, 0xfc, 0xe5, 0x49, 0x8b, 0x17, 0xf5, 0xfd, 0x6b, 0x8d, +0x32, 0x02, 0xe9, 0x0a, 0xfe, 0xbf, 0x00, 0x6b, 0xa3, 0xad, 0x5f, 0x09, 0x4b, 0x97, 0x2b, +0x00, 0x58, 0x65, 0x2e, 0x07, 0x49, 0x0a, 0x3b, 0x6b, 0x2e, 0x50, 0x6c, 0x1d, 0xac, 0xb7, +0x6a, 0x26, 0xd8, 0x13, 0xa4, 0xca, 0x16, 0xae, 0xab, 0x93, 0xb9, 0x1c, 0x1c, 0xb4, 0x47, +0x6a, 0x38, 0x36, 0x17, 0x27, 0xc9, 0x7f, 0xc7, 0x64, 0xcb, 0x89, 0x58, 0xc5, 0x61, 0xc2, +0xc6, 0xea, 0x15, 0x0b, 0x34, 0x0c, 0x5d, 0x61, 0x76, 0x6e, 0x2b, 0x62, 0x40, 0x92, 0xa3, +0x6c, 0xef, 0xf4, 0xe4, 0xc3, 0xa1, 0xa8, 0xf5, 0x94, 0x79, 0x0d, 0xd1, 0x3d, 0xcb, 0x3d, +0x40, 0xb6, 0xd0, 0xf0, 0x10, 0x54, 0xd8, 0x47, 0x25, 0x51, 0xc5, 0x41, 0x79, 0x00, 0xe5, +0xa0, 0x72, 0xde, 0xbb, 0x3b, 0x62, 0x17, 0xf6, 0xbc, 0x5d, 0x00, 0x76, 0x2e, 0xa7, 0x3b, +0xb6, 0xf1, 0x98, 0x72, 0x59, 0x2a, 0x73, 0xb0, 0x21, 0xd6, 0x49, 0xe0, 0xc0, 0xd5, 0xeb, +0x02, 0x7d, 0x4b, 0x41, 0x28, 0x70, 0x2d, 0xec, 0x2b, 0x71, 0x1f, 0x0b, 0xb9, 0x71, 0x63, +0x06, 0xe6, 0xbc, 0x60, 0xbb, 0xf4, 0x9a, 0x62, 0x43, 0x09, 0x18, 0x4e, 0x93, 0x06, 0x4d, +0x76, 0xfa, 0x7f, 0xbd, 0x02, 0xe4, 0x50, 0x91, 0x12, 0xe5, 0x86, 0xff, 0x64, 0x1e, 0xaf, +0x7e, 0xb3, 0xb2, 0xde, 0x89, 0xc1, 0xa2, 0x6f, 0x40, 0x7b, 0x41, 0x51, 0x63, 0xea, 0x25, +0xd1, 0x97, 0x57, 0x92, 0xa8, 0x45, 0xa1, 0xa5, 0x45, 0x21, 0x43, 0x7f, 0x83, 0x15, 0x29, +0xd0, 0x30, 0x53, 0x32, 0xb4, 0x5a, 0x17, 0x96, 0xbc, 0xc2, 0x68, 0xa9, 0xb7, 0xaf, 0xac, +0xdf, 0xf1, 0xe3, 0x89, 0xba, 0x24, 0x79, 0x54, 0xc6, 0x14, 0x07, 0x1c, 0x1e, 0x0d, 0x3a, +0x6b, 0xe5, 0x3d, 0x4e, 0x10, 0x60, 0x96, 0xec, 0x6c, 0xda, 0x47, 0xae, 0x03, 0x25, 0x39, +0x1d, 0x74, 0xc8, 0xac, 0x6a, 0xf2, 0x6b, 0x05, 0x2a, 0x9a, 0xe7, 0xe8, 0x92, 0xd6, 0xc2, +0x6d, 0xfa, 0xe8, 0xa7, 0x9d, 0x5f, 0x48, 0xc9, 0x75, 0xf1, 0x66, 0x6a, 0xdb, 0x5d, 0x9a, +0xcd, 0x27, 0xdd, 0xb9, 0x24, 0x04, 0x9c, 0x18, 0xc2, 0x6d, 0x0c, 0x91, 0x34, 0x48, 0x42, +0x6f, 0xe9, 0x59, 0x70, 0xc4, 0x7e, 0x81, 0x0e, 0x32, 0x0a, 0x93, 0x48, 0xb0, 0xc0, 0x15, +0x9e, 0x05, 0xac, 0x36, 0x16, 0xcb, 0x59, 0x65, 0xa0, 0x83, 0xdf, 0x3e, 0xda, 0xfb, 0x1d, +0x1a, 0xdb, 0x65, 0xec, 0x9a, 0xc6, 0xc3, 0x8e, 0x3c, 0x45, 0xfd, 0xc8, 0xf5, 0x1c, 0x6a, +0x67, 0x0d, 0x8f, 0x99, 0x7d, 0x30, 0x21, 0x8c, 0xea, 0x22, 0x87, 0x65, 0xc9, 0xb2, 0x4c, +0xe4, 0x1b, 0x46, 0xba, 0x54, 0xbd, 0x7c, 0xca, 0xd5, 0x8f, 0x5b, 0xa5, 0x01, 0x04, 0xd8, +0x0a, 0x16, 0xbf, 0xb9, 0x50, 0x2e, 0x37, 0x2f, 0x64, 0xf3, 0x70, 0x11, 0x02, 0x05, 0x31, +0x9b, 0xa0, 0xb2, 0x01, 0x5e, 0x4f, 0x19, 0xc9, 0xd4, 0xea, 0xa1, 0x79, 0x54, 0x53, 0xa7, +0xde, 0x2f, 0x49, 0xd3, 0xd1, 0x63, 0xb5, 0x03, 0x15, 0x4e, 0xbf, 0x04, 0xb3, 0x26, 0x8b, +0x20, 0xb2, 0x45, 0xcf, 0xcd, 0x5b, 0x82, 0x32, 0x88, 0x61, 0xa7, 0xa8, 0xb2, 0xa0, 0x72, +0x96, 0xc0, 0xdb, 0x2b, 0xe2, 0x5f, 0xba, 0xe3, 0xf5, 0x8a, 0xde, 0xf1, 0x18, 0x01, 0x16, +0x40, 0xd9, 0x86, 0x12, 0x09, 0x18, 0x1b, 0x05, 0x0c, 0xb1, 0xb5, 0x47, 0xe2, 0x43, 0xab, +0xfe, 0x92, 0x63, 0x7e, 0x95, 0x2b, 0xf0, 0xaf, 0xe1, 0xf1, 0xc3, 0x4a, 0xff, 0x2b, 0x09, +0xbb, 0x4a, 0x0e, 0x9a, 0xc4, 0xd8, 0x64, 0x7d, 0x83, 0xa0, 0x4f, 0x44, 0xdb, 0xc4, 0xa8, +0x58, 0xef, 0xfc, 0x9e, 0x77, 0xf9, 0xa6, 0x8f, 0x58, 0x8b, 0x12, 0xf4, 0xe9, 0x81, 0x12, +0x47, 0x51, 0x41, 0x83, 0xef, 0xf6, 0x73, 0xbc, 0x8e, 0x0f, 0x4c, 0x8f, 0x4e, 0x69, 0x90, +0x77, 0x29, 0x5d, 0x92, 0xb0, 0x6d, 0x06, 0x67, 0x29, 0x60, 0xbd, 0x4b, 0x17, 0xc8, 0x89, +0x69, 0x28, 0x29, 0xd6, 0x78, 0xcb, 0x11, 0x4c, 0xba, 0x8b, 0x68, 0xae, 0x7e, 0x9f, 0xef, +0x95, 0xda, 0xe2, 0x9e, 0x7f, 0xe9, 0x55, 0xe5, 0xe1, 0xe2, 0xb7, 0xe6, 0x5f, 0xbb, 0x2c, +0xa2, 0xe6, 0xee, 0xc7, 0x0a, 0x60, 0xa9, 0xd1, 0x80, 0xdf, 0x7f, 0xd6, 0x97, 0xab, 0x1d, +0x22, 0x25, 0xfc, 0x79, 0x23, 0xe0, 0xae, 0xc5, 0xef, 0x16, 0xa4, 0xa1, 0x0f, 0x92, 0xa9, +0xc7, 0xe3, 0x3a, 0x55, 0xdf, 0x62, 0x49, 0xd9, 0xf5, 0x84, 0x49, 0xc5, 0x90, 0x34, 0xd3, +0xe1, 0xac, 0x99, 0x21, 0xb1, 0x02, 0x76, 0x4a, 0xfa, 0xd4, 0xbb, 0xa4, 0x9c, 0xa2, 0xe2, +0xcb, 0x3d, 0x3b, 0x14, 0x75, 0x60, 0xd1, 0x02, 0xb4, 0xa3, 0xb4, 0x72, 0x06, 0xf9, 0x19, +0x9c, 0xe2, 0xe4, 0xa7, 0x0f, 0x25, 0x88, 0xc6, 0x86, 0xd6, 0x8c, 0x74, 0x4e, 0x6e, 0xfc, +0xa8, 0x48, 0x9e, 0xa7, 0x9d, 0x1a, 0x4b, 0x37, 0x09, 0xc8, 0xb0, 0x10, 0xbe, 0x6f, 0xfe, +0xa3, 0xc4, 0x7a, 0xb5, 0x3d, 0xe8, 0x30, 0xf1, 0x0d, 0xa0, 0xb2, 0x44, 0xfc, 0x9b, 0x8c, +0xf8, 0x61, 0xed, 0x81, 0xd1, 0x62, 0x11, 0xb4, 0xe1, 0xd5, 0x39, 0x52, 0x89, 0xd3, 0xa8, +0x49, 0x31, 0xdf, 0xb6, 0xf9, 0x91, 0xf4, 0x1c, 0x9d, 0x09, 0x95, 0x40, 0x56, 0xe7, 0xe3, +0xcd, 0x5c, 0x92, 0xc1, 0x1d, 0x6b, 0xe9, 0x78, 0x6f, 0x8e, 0x94, 0x42, 0x66, 0xa2, 0xaa, +0xd3, 0xc8, 0x2e, 0xe3, 0xf6, 0x07, 0x72, 0x0b, 0x6b, 0x1e, 0x7b, 0xb9, 0x7c, 0xe0, 0xa0, +0xbc, 0xd9, 0x25, 0xdf, 0x87, 0xa8, 0x5f, 0x9c, 0xcc, 0xf0, 0xdb, 0x42, 0x8e, 0x07, 0x31, +0x13, 0x01, 0x66, 0x32, 0xd1, 0xb8, 0xd6, 0xe3, 0x5e, 0x12, 0x76, 0x61, 0xd3, 0x38, 0x89, +0xe6, 0x17, 0x6f, 0xa5, 0xf2, 0x71, 0x0e, 0xa5, 0xe2, 0x88, 0x30, 0xbb, 0xbe, 0x8a, 0xea, +0xc7, 0x62, 0xc4, 0xcf, 0xb8, 0xcd, 0x33, 0x8d, 0x3d, 0x3e, 0xb5, 0x60, 0x3a, 0x03, 0x92, +0xe4, 0x6d, 0x1b, 0xe0, 0xb4, 0x84, 0x08, 0x55, 0x88, 0xa7, 0x3a, 0xb9, 0x3d, 0x43, 0xc3, +0xc0, 0xfa, 0x07, 0x6a, 0xca, 0x94, 0xad, 0x99, 0x55, 0xf1, 0xf1, 0xc0, 0x23, 0x87, 0x1d, +0x3d, 0x1c, 0xd1, 0x66, 0xa0, 0x57, 0x10, 0x52, 0xa2, 0x7f, 0xbe, 0xf9, 0x88, 0xb6, 0x02, +0xbf, 0x08, 0x23, 0xa9, 0x0c, 0x63, 0x17, 0x2a, 0xae, 0xf5, 0xf7, 0xb7, 0x21, 0x83, 0x92, +0x31, 0x23, 0x0d, 0x20, 0xc3, 0xc2, 0x05, 0x21, 0x62, 0x8e, 0x45, 0xe8, 0x14, 0xc1, 0xda, +0x75, 0xb8, 0xf8, 0x92, 0x01, 0xd0, 0x5d, 0x18, 0x9f, 0x99, 0x11, 0x19, 0xf5, 0x35, 0xe8, +0x7f, 0x20, 0x88, 0x8c, 0x05, 0x75, 0xf5, 0xd7, 0x40, 0x17, 0xbb, 0x1e, 0x36, 0x52, 0xd9, +0xa4, 0x9c, 0xc2, 0x9d, 0x42, 0x81, 0xd8, 0xc7, 0x8a, 0xe7, 0x4c, 0x81, 0xe0, 0xb7, 0x57, +0xed, 0x48, 0x8b, 0xf0, 0x97, 0x15, 0x61, 0xd9, 0x2c, 0x7c, 0x45, 0xaf, 0xc2, 0xcd, 0xfc, +0xaa, 0x13, 0xad, 0x59, 0xcc, 0xb2, 0xb2, 0x6e, 0xdd, 0x63, 0x9c, 0x32, 0x0f, 0xec, 0x83, +0xbe, 0x78, 0xac, 0x91, 0x44, 0x1a, 0x1f, 0xea, 0xfd, 0x5d, 0x8e, 0xb4, 0xc0, 0x84, 0xd4, +0xac, 0xb4, 0x87, 0x5f, 0xac, 0xef, 0xdf, 0xcd, 0x12, 0x56, 0xc8, 0xcd, 0xfe, 0xc5, 0xda, +0xd3, 0xc1, 0x69, 0xf3, 0x61, 0x05, 0xea, 0x25, 0xe2, 0x12, 0x05, 0x8f, 0x39, 0x08, 0x08, +0x7c, 0x37, 0xb6, 0x7e, 0x5b, 0xd8, 0xb1, 0x0e, 0xf2, 0xdb, 0x4b, 0xf1, 0xad, 0x90, 0x01, +0x57, 0xcd, 0xa0, 0xb4, 0x52, 0xe8, 0xf3, 0xd7, 0x8a, 0xbd, 0x4f, 0x9f, 0x21, 0x40, 0x72, +0xa4, 0xfc, 0x0b, 0x01, 0x2b, 0x2f, 0xb6, 0x4c, 0x95, 0x2d, 0x35, 0x33, 0x41, 0x6b, 0xa0, +0x93, 0xe7, 0x2c, 0xf2, 0xd3, 0x72, 0x8b, 0xf4, 0x4f, 0x15, 0x3c, 0xaf, 0xd6, 0x12, 0xde, +0x3f, 0x83, 0x3f, 0xff, 0xf8, 0x7f, 0xf6, 0xcc, 0xa6, 0x7f, 0xc9, 0x9a, 0x6e, 0x1f, 0xc1, +0x0c, 0xfb, 0xee, 0x9c, 0xe7, 0xaf, 0xc9, 0x26, 0x54, 0xef, 0xb0, 0x39, 0xef, 0xb2, 0xe9, +0x23, 0xc4, 0xef, 0xd1, 0xa1, 0xa4, 0x25, 0x24, 0x6f, 0x8d, 0x6a, 0xe5, 0x8a, 0x32, 0x3a, +0xaf, 0xfc, 0xda, 0xce, 0x18, 0x25, 0x42, 0x07, 0x4d, 0x45, 0x8b, 0xdf, 0x85, 0xcf, 0x55, +0xb2, 0x24, 0xfe, 0x9c, 0x69, 0x74, 0xa7, 0x6e, 0xa0, 0xce, 0xc0, 0x39, 0xf4, 0x86, 0xc6, +0x8d, 0xae, 0xb9, 0x48, 0x64, 0x13, 0x0b, 0x40, 0x81, 0xa2, 0xc9, 0xa8, 0x85, 0x51, 0xee, +0x9f, 0xcf, 0xa2, 0x8c, 0x19, 0x52, 0x48, 0xe2, 0xc1, 0xa8, 0x58, 0xb4, 0x10, 0x24, 0x06, +0x58, 0x51, 0xfc, 0xb9, 0x12, 0xec, 0xfd, 0x73, 0xb4, 0x6d, 0x84, 0xfa, 0x06, 0x8b, 0x05, +0x0b, 0x2d, 0xd6, 0xd6, 0x1f, 0x29, 0x82, 0x9f, 0x19, 0x12, 0x1e, 0xb2, 0x04, 0x8f, 0x7f, +0x4d, 0xbd, 0x30, 0x2e, 0xe3, 0xe0, 0x88, 0x29, 0xc5, 0x93, 0xd6, 0x6c, 0x1f, 0x29, 0x45, +0x91, 0xa7, 0x58, 0xcd, 0x05, 0x17, 0xd6, 0x6d, 0xb3, 0xca, 0x66, 0xcc, 0x3c, 0x4a, 0x74, +0xfd, 0x08, 0x10, 0xa6, 0x99, 0x92, 0x10, 0xd2, 0x85, 0xab, 0x6e, 0x1d, 0x0e, 0x8b, 0x26, +0x46, 0xd1, 0x6c, 0x84, 0xc0, 0x26, 0x43, 0x59, 0x68, 0xf0, 0x13, 0x1d, 0xfb, 0xe3, 0xd1, +0xd2, 0xb4, 0x71, 0x9e, 0xf2, 0x59, 0x6a, 0x33, 0x29, 0x79, 0xd2, 0xd7, 0x26, 0xf1, 0xae, +0x78, 0x9e, 0x1f, 0x0f, 0x3f, 0xe3, 0xe8, 0xd0, 0x27, 0x78, 0x77, 0xf6, 0xac, 0x9c, 0x56, +0x39, 0x73, 0x8a, 0x6b, 0x2f, 0x34, 0x78, 0xb1, 0x11, 0xdb, 0xa4, 0x5c, 0x80, 0x01, 0x71, +0x6a, 0xc2, 0xd1, 0x2e, 0x5e, 0x76, 0x28, 0x70, 0x93, 0xae, 0x3e, 0x78, 0xb0, 0x1f, 0x0f, +0xda, 0xbf, 0xfb, 0x8a, 0x67, 0x65, 0x4f, 0x91, 0xed, 0x49, 0x75, 0x78, 0x62, 0xa2, 0x93, +0xb5, 0x70, 0x7f, 0x4d, 0x08, 0x4e, 0x79, 0x61, 0xa8, 0x5f, 0x7f, 0xb4, 0x65, 0x9f, 0x91, +0x54, 0x3a, 0xe8, 0x50, 0x33, 0xd3, 0xd5, 0x8a, 0x7c, 0xf3, 0x9e, 0x8b, 0x77, 0x7b, 0xc6, +0xc6, 0x0c, 0x45, 0x95, 0x1f, 0xb0, 0xd0, 0x0b, 0x27, 0x4a, 0xfd, 0xc7, 0xf7, 0x0d, 0x5a, +0x43, 0xc9, 0x7d, 0x35, 0xb0, 0x7d, 0xc4, 0x9c, 0x57, 0x1e, 0x76, 0x0d, 0xf1, 0x95, 0x30, +0x71, 0xcc, 0xb3, 0x66, 0x3b, 0x63, 0xa8, 0x6c, 0xa3, 0x43, 0xa0, 0x24, 0xcc, 0xb7, 0x53, +0xfe, 0xfe, 0xbc, 0x6e, 0x60, 0x89, 0xaf, 0x16, 0x21, 0xc8, 0x91, 0x6a, 0x89, 0xce, 0x80, +0x2c, 0xf1, 0x59, 0xce, 0xc3, 0x60, 0x61, 0x3b, 0x0b, 0x19, 0xfe, 0x99, 0xac, 0x65, 0x90, +0x15, 0x12, 0x05, 0xac, 0x7e, 0xff, 0x98, 0x7b, 0x66, 0x64, 0x0e, 0x4b, 0x5b, 0xaa, 0x8d, +0x3b, 0xd2, 0x56, 0xcf, 0x99, 0x39, 0xee, 0x22, 0x81, 0xd0, 0x60, 0x06, 0x66, 0x20, 0x81, +0x48, 0x3c, 0x6f, 0x3a, 0x77, 0xba, 0xcb, 0x52, 0xac, 0x79, 0x56, 0xaf, 0xe9, 0x16, 0x17, +0x0a, 0xa3, 0x82, 0x08, 0xd5, 0x3c, 0x97, 0xcb, 0x09, 0xff, 0x7f, 0xf9, 0x4f, 0x60, 0x05, +0xb9, 0x53, 0x26, 0xaa, 0xb8, 0x50, 0xaa, 0x19, 0x25, 0xae, 0x5f, 0xea, 0x8a, 0xd0, 0x89, +0x12, 0x80, 0x43, 0x50, 0x24, 0x12, 0x21, 0x14, 0xcd, 0x77, 0xeb, 0x21, 0xcc, 0x5c, 0x09, +0x64, 0xf3, 0xc7, 0xcb, 0xc5, 0x4b, 0xc3, 0xe7, 0xed, 0xe7, 0x86, 0x2c, 0x1d, 0x8e, 0x19, +0x52, 0x9b, 0x2a, 0x0c, 0x18, 0x72, 0x0b, 0x1e, 0x1b, 0xb0, 0x0f, 0x42, 0x99, 0x04, 0xae, +0xd5, 0xb7, 0x89, 0x1a, 0xb9, 0x4f, 0xd6, 0xaf, 0xf3, 0xc9, 0x93, 0x6f, 0xb0, 0x60, 0x83, +0x6e, 0x6b, 0xd1, 0x5f, 0x3f, 0x1a, 0x83, 0x1e, 0x24, 0x00, 0x87, 0xb5, 0x3e, 0xdb, 0xf9, +0x4d, 0xa7, 0x16, 0x2e, 0x19, 0x5b, 0x8f, 0x1b, 0x0d, 0x47, 0x72, 0x42, 0xe9, 0x0a, 0x11, +0x08, 0x2d, 0x88, 0x1c, 0xbc, 0xc7, 0xb4, 0xbe, 0x29, 0x4d, 0x03, 0x5e, 0xec, 0xdf, 0xf3, +0x3d, 0x2f, 0xe8, 0x1d, 0x9a, 0xd2, 0xd1, 0xab, 0x41, 0x3d, 0x87, 0x11, 0x45, 0xb0, 0x0d, +0x46, 0xf5, 0xe8, 0x95, 0x62, 0x1c, 0x68, 0xf7, 0xa6, 0x5b, 0x39, 0x4e, 0xbf, 0x47, 0xba, +0x5d, 0x7f, 0xb7, 0x6a, 0xf4, 0xba, 0x1d, 0x69, 0xf6, 0xa4, 0xe7, 0xe4, 0x6b, 0x3b, 0x0d, +0x23, 0x16, 0x4a, 0xb2, 0x68, 0xf0, 0xb2, 0x0d, 0x09, 0x17, 0x6a, 0x63, 0x8c, 0x83, 0xd3, +0xbd, 0x05, 0xc9, 0xf6, 0xf0, 0xa1, 0x31, 0x0b, 0x2c, 0xac, 0x83, 0xac, 0x80, 0x34, 0x32, +0xb4, 0xec, 0xd0, 0xbc, 0x54, 0x82, 0x9a, 0xc8, 0xf6, 0xa0, 0x7d, 0xc6, 0x79, 0x73, 0xf4, +0x20, 0x99, 0xf3, 0xb4, 0x01, 0xde, 0x91, 0x27, 0xf2, 0xc0, 0xdc, 0x81, 0x00, 0x4e, 0x7e, +0x07, 0x99, 0xc8, 0x3a, 0x51, 0xbc, 0x38, 0xd6, 0x8a, 0xa2, 0xde, 0x3b, 0x6a, 0x8c, 0x1a, +0x7c, 0x81, 0x0f, 0x3a, 0x1f, 0xe4, 0x05, 0x7b, 0x20, 0x35, 0x6b, 0xa5, 0x6a, 0xa7, 0xe7, +0xbc, 0x9c, 0x20, 0xec, 0x00, 0x15, 0xe2, 0x51, 0xaf, 0x77, 0xeb, 0x29, 0x3c, 0x7d, 0x2e, +0x00, 0x5c, 0x81, 0x21, 0xfa, 0x35, 0x6f, 0x40, 0xef, 0xfb, 0xd1, 0x3f, 0xcc, 0x9d, 0x55, +0x53, 0xfb, 0x5a, 0xa5, 0x56, 0x89, 0x0b, 0x52, 0xeb, 0x57, 0x73, 0x4f, 0x1b, 0x67, 0x24, +0xcb, 0xb8, 0x6a, 0x10, 0x69, 0xd6, 0xfb, 0x52, 0x40, 0xff, 0x20, 0xa5, 0xf3, 0x72, 0xe1, +0x3d, 0xa4, 0x8c, 0x81, 0x66, 0x16, 0x0d, 0x5d, 0xad, 0xa8, 0x50, 0x25, 0x78, 0x31, 0x77, +0x0c, 0x57, 0xe4, 0xe9, 0x15, 0x2d, 0xdb, 0x07, 0x87, 0xc8, 0xb0, 0x43, 0xde, 0xfc, 0xfe, +0xa9, 0xeb, 0xf5, 0xb0, 0xd3, 0x7b, 0xe9, 0x1f, 0x6e, 0xca, 0xe4, 0x03, 0x95, 0xc5, 0xd1, +0x59, 0x72, 0x63, 0xf0, 0x86, 0x54, 0xe8, 0x16, 0x62, 0x0b, 0x35, 0x29, 0xc2, 0x68, 0xd0, +0xd6, 0x3e, 0x90, 0x60, 0x57, 0x1d, 0xc9, 0xed, 0x3f, 0xed, 0xb0, 0x2f, 0x7e, 0x97, 0x02, +0x51, 0xec, 0xee, 0x6f, 0x82, 0x74, 0x76, 0x7f, 0xfb, 0xd6, 0xc4, 0xc3, 0xdd, 0xe8, 0xb1, +0x60, 0xfc, 0xc6, 0xb9, 0x0d, 0x6a, 0x33, 0x78, 0xc6, 0xc1, 0xbf, 0x86, 0x2c, 0x50, 0xcc, +0x9a, 0x70, 0x8e, 0x7b, 0xec, 0xab, 0x95, 0xac, 0x53, 0xa0, 0x4b, 0x07, 0x88, 0xaf, 0x42, +0xed, 0x19, 0x8d, 0xf6, 0x32, 0x17, 0x48, 0x47, 0x1d, 0x41, 0x6f, 0xfe, 0x2e, 0xa7, 0x8f, +0x4b, 0xa0, 0x51, 0xf3, 0xbf, 0x02, 0x0a, 0x48, 0x58, 0xf7, 0xa1, 0x6d, 0xea, 0xa5, 0x13, +0x5a, 0x5b, 0xea, 0x0c, 0x9e, 0x52, 0x4f, 0x9e, 0xb9, 0x71, 0x7f, 0x23, 0x83, 0xda, 0x1b, +0x86, 0x9a, 0x41, 0x29, 0xda, 0x70, 0xe7, 0x64, 0xa1, 0x7b, 0xd5, 0x0a, 0x22, 0x0d, 0x5c, +0x40, 0xc4, 0x81, 0x07, 0x25, 0x35, 0x4a, 0x1c, 0x10, 0xdb, 0x45, 0x0a, 0xff, 0x36, 0xd4, +0xe0, 0xeb, 0x5f, 0x68, 0xd6, 0x67, 0xc6, 0xd0, 0x8b, 0x76, 0x1a, 0x7d, 0x59, 0x42, 0xa1, +0xcb, 0x96, 0x4d, 0x84, 0x09, 0x9a, 0x3d, 0xe0, 0x52, 0x85, 0x6e, 0x48, 0x90, 0x85, 0x2a, +0x63, 0xb2, 0x69, 0xd2, 0x00, 0x43, 0x31, 0x37, 0xb3, 0x52, 0xaf, 0x62, 0xfa, 0xc1, 0xe0, +0x03, 0xfb, 0x62, 0xaa, 0x88, 0xc9, 0xb2, 0x2c, 0xd5, 0xa8, 0xf5, 0xa5, 0x4c, 0x12, 0x59, +0x4e, 0x06, 0x5e, 0x9b, 0x15, 0x66, 0x11, 0xb2, 0x27, 0x92, 0xdc, 0x98, 0x59, 0xde, 0xdf, +0xfa, 0x9a, 0x32, 0x2e, 0xc0, 0x5d, 0x3c, 0x33, 0x41, 0x6d, 0xaf, 0xb2, 0x25, 0x23, 0x14, +0xa5, 0x7b, 0xc7, 0x9b, 0x68, 0xf3, 0xda, 0xeb, 0xe3, 0xa9, 0xe2, 0x6f, 0x0e, 0x1d, 0x1c, +0xba, 0x55, 0xb6, 0x34, 0x6a, 0x93, 0x1f, 0x1f, 0xb8, 0x34, 0xc8, 0x84, 0x08, 0xb1, 0x6b, +0x6a, 0x28, 0x74, 0x74, 0xe5, 0xeb, 0x75, 0xe9, 0x7c, 0xd8, 0xba, 0xd8, 0x42, 0xa5, 0xee, +0x1f, 0x80, 0xd9, 0x96, 0xb2, 0x2e, 0xe7, 0xbf, 0xba, 0xeb, 0xd1, 0x69, 0xbb, 0x8f, 0xfd, +0x5a, 0x63, 0x8f, 0x39, 0x7f, 0xdf, 0x1d, 0x37, 0xd2, 0x18, 0x35, 0x9d, 0xb6, 0xcc, 0xe4, +0x27, 0x81, 0x89, 0x38, 0x38, 0x68, 0x33, 0xe7, 0x78, 0xd8, 0x76, 0xf5, 0xee, 0xd0, 0x4a, +0x07, 0x69, 0x19, 0x7a, 0xad, 0x18, 0xb1, 0x94, 0x61, 0x45, 0x53, 0xa2, 0x48, 0xda, 0x96, +0x4a, 0xf9, 0xee, 0x94, 0x2a, 0x1f, 0x6e, 0x18, 0x3c, 0x92, 0x46, 0xd1, 0x1a, 0x28, 0x18, +0x32, 0x1f, 0x3a, 0x45, 0xbe, 0x04, 0x35, 0x92, 0xe5, 0xa3, 0xcb, 0xb5, 0x2e, 0x32, 0x43, +0xac, 0x65, 0x17, 0x89, 0x99, 0x15, 0x03, 0x9e, 0xb1, 0x23, 0x2f, 0xed, 0x76, 0x4d, 0xd8, +0xac, 0x21, 0x40, 0xc4, 0x99, 0x4e, 0x65, 0x71, 0x2c, 0xb3, 0x45, 0xab, 0xfb, 0xe7, 0x72, +0x39, 0x56, 0x30, 0x6d, 0xfb, 0x74, 0xeb, 0x99, 0xf3, 0xcd, 0x57, 0x5c, 0x78, 0x75, 0xe9, +0x8d, 0xc3, 0xa2, 0xfb, 0x5d, 0xe0, 0x90, 0xc5, 0x55, 0xad, 0x91, 0x53, 0x4e, 0x9e, 0xbd, +0x8c, 0x49, 0xa4, 0xa4, 0x69, 0x10, 0x0c, 0xc5, 0x76, 0xe9, 0x25, 0x86, 0x8d, 0x66, 0x23, +0xa8, 0xdb, 0x5c, 0xe8, 0xd9, 0x30, 0xe1, 0x15, 0x7b, 0xc0, 0x99, 0x0f, 0x03, 0xec, 0xaa, +0x12, 0xef, 0xce, 0xd4, 0xea, 0x55, 0x5c, 0x08, 0x86, 0xf4, 0xf4, 0xb0, 0x83, 0x42, 0x95, +0x37, 0xb6, 0x38, 0xe0, 0x2b, 0x54, 0x89, 0xbd, 0x4e, 0x20, 0x9d, 0x3f, 0xc3, 0x4b, 0xb7, +0xec, 0xfa, 0x5a, 0x14, 0x03, 0xcb, 0x64, 0xc8, 0x34, 0x4a, 0x4b, 0x6e, 0xf8, 0x6e, 0x56, +0xf6, 0xdd, 0x5f, 0xa1, 0x24, 0xe2, 0xd4, 0xd0, 0x82, 0x64, 0x1f, 0x8e, 0x9b, 0xfa, 0xb4, +0xcb, 0xdb, 0x0a, 0xe8, 0x15, 0xfc, 0x15, 0xab, 0x4b, 0x18, 0xbf, 0xd4, 0x42, 0x14, 0x48, +0x82, 0x85, 0xdd, 0xeb, 0x49, 0x1b, 0x0b, 0x0b, 0x05, 0xe9, 0xb4, 0xa1, 0x33, 0x0a, 0x5d, +0x0e, 0x6c, 0x4b, 0xc0, 0xd6, 0x6c, 0x7c, 0xfb, 0x69, 0x0b, 0x53, 0x19, 0xe4, 0xf3, 0x35, +0xfc, 0xbe, 0xa1, 0x34, 0x02, 0x09, 0x4f, 0x74, 0x86, 0x92, 0xcd, 0x5d, 0x1a, 0xc1, 0x27, +0x0c, 0xf2, 0xc5, 0xcf, 0xdd, 0x23, 0x93, 0x02, 0xbd, 0x41, 0x5e, 0x42, 0xf0, 0xa0, 0x9d, +0x0c, 0x72, 0xc8, 0xec, 0x32, 0x0a, 0x8a, 0xfd, 0x3d, 0x5a, 0x41, 0x27, 0x0c, 0x88, 0x59, +0xad, 0x94, 0x2e, 0xef, 0x5d, 0x8f, 0xc7, 0xdf, 0x66, 0xe4, 0xdd, 0x56, 0x6c, 0x7b, 0xca, +0x55, 0x81, 0xae, 0xae, 0x5c, 0x1b, 0x1a, 0xab, 0xae, 0x99, 0x8d, 0xcc, 0x42, 0x97, 0x59, +0xf4, 0x14, 0x3f, 0x75, 0xc6, 0xd1, 0x88, 0xba, 0xaa, 0x84, 0x4a, 0xd0, 0x34, 0x08, 0x3b, +0x7d, 0xdb, 0x15, 0x06, 0xb0, 0x5c, 0xbd, 0x40, 0xf5, 0xa8, 0xec, 0xae, 0x36, 0x40, 0xdd, +0x90, 0x1c, 0x3e, 0x0d, 0x7e, 0x73, 0xc7, 0xc2, 0xc5, 0x6a, 0xff, 0x52, 0x05, 0x7f, 0xbe, +0xd0, 0x92, 0xfd, 0xb3, 0x6f, 0xff, 0x5d, 0xb7, 0x97, 0x64, 0x73, 0x7b, 0xca, 0xd1, 0x98, +0x24, 0x6b, 0x0b, 0x01, 0x68, 0xdd, 0x27, 0x85, 0x85, 0xb5, 0x83, 0xc1, 0xe0, 0x50, 0x64, +0xc7, 0xaf, 0xf1, 0xc6, 0x4d, 0xb1, 0xef, 0xc9, 0xb4, 0x0a, 0x6d, 0x65, 0xf3, 0x47, 0xcc, +0xa3, 0x02, 0x21, 0x0c, 0xbe, 0x22, 0x29, 0x05, 0xcf, 0x5f, 0xe8, 0x94, 0x6c, 0xe5, 0xdc, +0xc4, 0xdf, 0xbe, 0x3e, 0xa8, 0xb4, 0x18, 0xb0, 0x99, 0xb8, 0x6f, 0xff, 0x5d, 0xb9, 0xfd, +0x3b, 0x5d, 0x16, 0xbf, 0x3e, 0xd8, 0xb3, 0xd8, 0x08, 0x34, 0xf6, 0x47, 0x35, 0x5b, 0x72, +0x1a, 0x33, 0xad, 0x52, 0x5d, 0xb8, 0xd0, 0x77, 0xc6, 0xab, 0xba, 0x55, 0x09, 0x5f, 0x02, +0xf8, 0xd4, 0x5f, 0x53, 0x06, 0x91, 0xcd, 0x74, 0x42, 0xae, 0x54, 0x91, 0x81, 0x62, 0x13, +0x6f, 0xd8, 0xa9, 0x77, 0xc3, 0x6c, 0xcb, 0xf1, 0x29, 0x5a, 0xcc, 0xda, 0x35, 0xbd, 0x52, +0x23, 0xbe, 0x59, 0xeb, 0x12, 0x6d, 0xb7, 0x53, 0xee, 0xfc, 0xb4, 0x1b, 0x13, 0x5e, 0xba, +0x16, 0x7c, 0xc5, 0xf3, 0xe3, 0x6d, 0x07, 0x78, 0xf5, 0x2b, 0x21, 0x05, 0x88, 0x4c, 0xc0, +0xa1, 0xe3, 0x36, 0x10, 0xf8, 0x1b, 0xd8, 0x17, 0xfb, 0x6a, 0x4e, 0xd8, 0xb3, 0x47, 0x2d, +0x99, 0xbd, 0xbb, 0x5d, 0x37, 0x7d, 0xba, 0xf1, 0xe1, 0x7c, 0xc0, 0xc5, 0x54, 0x62, 0x7f, +0xcf, 0x5a, 0x4a, 0x93, 0xcc, 0xf1, 0x1b, 0x34, 0xc8, 0xa6, 0x05, 0x4c, 0x55, 0x8b, 0x54, +0x84, 0xd5, 0x77, 0xeb, 0xc0, 0x6d, 0x3a, 0x29, 0xbd, 0x75, 0x61, 0x09, 0x9a, 0x2c, 0xbb, +0xf7, 0x18, 0x79, 0x34, 0x90, 0x24, 0xa5, 0x81, 0x70, 0x87, 0xc5, 0x02, 0x7c, 0xba, 0xd4, +0x5e, 0x14, 0x8e, 0xe4, 0xed, 0xa2, 0x61, 0x6a, 0xb9, 0x6e, 0xb5, 0x4a, 0xb9, 0x01, 0x46, +0xf4, 0xcf, 0xbc, 0x09, 0x2f, 0x27, 0x4b, 0xbd, 0x86, 0x7a, 0x10, 0xe1, 0xd4, 0xc8, 0xd9, +0x20, 0x8d, 0x8a, 0x63, 0x00, 0x63, 0x44, 0xeb, 0x54, 0x0b, 0x75, 0x49, 0x10, 0xa2, 0xa7, +0xad, 0xb9, 0xd1, 0x01, 0x80, 0x63, 0x25, 0xc8, 0x12, 0xa6, 0xce, 0x1e, 0xbe, 0xfe, 0x7e, +0x5f, 0x3c, 0xdb, 0x34, 0xea, 0x37, 0xec, 0x3b, 0xd5, 0x28, 0xd2, 0x07, 0x8c, 0x9a, 0xb6, +0xee, 0x5e, 0x3e, 0xdf, 0x1d, 0x99, 0xb0, 0xe2, 0x46, 0xef, 0x5c, 0x1b, 0xb4, 0xea, 0x56, +0x2e, 0xde, 0x1f, 0x9d, 0xb8, 0xd3, 0x24, 0xab, 0xd4, 0x2a, 0xd6, 0x2e, 0xde, 0x1f, 0x9d, +0xb8, 0xf2, 0x66, 0x2f, 0xbd, 0xf8, 0x72, 0x66, 0x4e, 0x1e, 0x9f, 0x9d, 0xb8, 0xf2, 0x47, +0x0c, 0x9a, 0xb6, 0xee, 0x3f, 0xfc, 0x7a, 0x57, 0x0d, 0x79, 0x70, 0x62, 0x27, 0xad, 0xb9, +0xd1, 0x01, 0x61, 0x40, 0x02, 0x67, 0x2d, 0xd8, 0x32, 0xe6, 0x2f, 0xdc, 0x3a, 0xd7, 0x2c, +0xbb, 0xf4, 0x4b, 0xf5, 0x49, 0xf1, 0x60, 0x23, 0xc4, 0x0a, 0x77, 0x4d, 0xf9, 0x51, 0x01, +0x80, 0x63, 0x25, 0xa9, 0xb1, 0xe0, 0x42, 0xe7, 0x4c, 0x1a, 0x97, 0xac, 0xbb, 0xf4, 0x6a, +0x37, 0xcd, 0x18, 0xb2, 0xe6, 0x2f, 0xdc, 0x1b, 0x95, 0xa8, 0xd2, 0x07, 0x6d, 0x58, 0x32, +0xe6, 0x4e, 0x1e, 0x9f, 0xbc, 0xfa, 0x57, 0x0d, 0x79, 0x51, 0x20, 0xc2, 0x06, 0x6f, 0x5c, +0x1b, 0x95, 0xa8, 0xb3, 0xc5, 0xe9, 0x31, 0xe0, 0x23, 0xc4, 0x0a, 0x77, 0x4d, 0x18, 0x93, +0x85, 0x69, 0x31, 0xc1, 0xe1, 0x21, 0xc0, 0xe3, 0x44, 0x0a, 0x77, 0x6c, 0x5a, 0x17, 0x8d, +0x98, 0x93, 0xa4, 0xab, 0xd4, 0x2a, 0xb7, 0xec, 0x5a, 0x17, 0xac, 0xbb, 0xf4, 0x4b, 0x14, +0xaa, 0xb7, 0xec, 0x3b, 0xd5, 0x28, 0xb3, 0xc5, 0xe9, 0x31, 0xc1, 0x00, 0x82, 0x67, 0x4c, +0xfb, 0x55, 0x28, 0xd2, 0x26, 0xaf, 0xbd, 0xd9, 0x11, 0x81, 0x61, 0x21, 0xa1, 0xa1, 0xc0, +0x02, 0x86, 0x6f, 0x5c, 0x1b, 0xb4, 0xcb, 0x14, 0x8b, 0x94, 0xaa, 0xd6, 0x2e, 0xbf, 0xdd, +0x19, 0xb0, 0xe2, 0x46, 0x0e, 0x7f, 0x7c, 0x5b, 0x15, 0x89, 0x90, 0x83, 0x84, 0x6b, 0x54, +0x0b, 0x75, 0x68, 0x52, 0x07, 0x6d, 0x58, 0x32, 0xc7, 0xed, 0x58, 0x32, 0xc7, 0xed, 0x58, +0x32, 0xe6, 0x4e, 0xff, 0x7c, 0x7a, 0x76, 0x6e, 0x3f, 0xdd, 0x38, 0xd3, 0x05, 0x88, 0x92, +0xa6, 0xaf, 0xdc, 0x1b, 0xb4, 0xcb, 0xf5, 0x68, 0x52, 0x07, 0x8c, 0x7b, 0x55, 0x09, 0x90, +0x83, 0x84, 0x6b, 0x54, 0x2a, 0xb7, 0xec, 0x3b, 0xd5, 0x09, 0x90, 0xa2, 0xc6, 0x0e, 0x7f, +0x7c, 0x7a, 0x57, 0x0d, 0x98, 0xb2, 0xc7, 0xed, 0x58, 0x32, 0xc7, 0x0c, 0x7b, 0x74, 0x4b, +0x14, 0x8b, 0x94, 0xaa, 0xb7, 0xcd, 0x18, 0x93, 0xa4, 0xca, 0x16, 0xae, 0xbf, 0xdd, 0x19, +0xb0, 0xe2, 0x46, 0x0e, 0x7f, 0x5d, 0x19, 0x91, 0x81, 0x80, 0x63, 0x44, 0xeb, 0x35, 0xc9, +0x10, 0x83, 0x65, 0x48, 0x12, 0xa6, 0xce, 0x1e, 0x9f, 0xbc, 0xdb, 0x15, 0x89, 0x71, 0x60, +0x23, 0xc4, 0xeb, 0x54, 0x2a, 0xb7, 0xec, 0x5a, 0x36, 0xcf, 0x81, 0x10, 0xac, 0x74 }; + +// clang-format off diff --git a/keyboards/oddball/pmw/pmw.h b/keyboards/40percentclub/nano/keymaps/drashna/config.h similarity index 62% rename from keyboards/oddball/pmw/pmw.h rename to keyboards/40percentclub/nano/keymaps/drashna/config.h index cc3c9ec5eb0..411ee8a81bb 100644 --- a/keyboards/oddball/pmw/pmw.h +++ b/keyboards/40percentclub/nano/keymaps/drashna/config.h @@ -1,4 +1,4 @@ -/* Copyright 2020 Alexander Tulloh +/* 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 @@ -16,20 +16,7 @@ #pragma once -#include +#define ANALOG_JOYSTICK_X_AXIS_PIN B4 +#define ANALOG_JOYSTICK_Y_AXIS_PIN B5 -typedef struct { - /* 100 - 12000 CPI supported */ - uint16_t cpi; -} config_pmw_t; - -typedef struct { - int16_t x; - int16_t y; -} report_pmw_t; - -void pmw_init(void); -config_pmw_t pmw_get_config(void); -void pmw_set_config(config_pmw_t); -/* Reads and clears the current delta values on the PMW sensor */ -report_pmw_t pmw_get_report(void); +#define ANALOG_JOYSTICK_CLICK_PIN E6 diff --git a/keyboards/40percentclub/nano/keymaps/drashna/keymap.c b/keyboards/40percentclub/nano/keymaps/drashna/keymap.c index 13f89dd53d7..cc5991d7a26 100644 --- a/keyboards/40percentclub/nano/keymaps/drashna/keymap.c +++ b/keyboards/40percentclub/nano/keymaps/drashna/keymap.c @@ -29,98 +29,3 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { }; // clang-format on - -// Joystick -// Set Pins -// uint8_t xPin = 8; // VRx / /B4 -// uint8_t yPin = 7; // VRy // B5 -uint8_t swPin = E6; // SW - -// Set Parameters -uint16_t minAxisValue = 0; -uint16_t maxAxisValue = 1023; - -uint8_t maxCursorSpeed = 2; -uint8_t precisionSpeed = 1; -uint8_t speedRegulator = 20; // Lower Values Create Faster Movement - -int8_t xPolarity = 1; -int8_t yPolarity = 1; - -uint8_t cursorTimeout = 10; - -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, int8_t polarity) { - int coordinate = axisCoordinate(pin, origin); - if (coordinate != 0) { - float percent = (float)coordinate / 100; - if (get_mods() & MOD_BIT(KC_LSFT)) { - return percent * precisionSpeed * polarity * (abs(coordinate) / speedRegulator); - } else { - return percent * maxCursorSpeed * polarity * (abs(coordinate) / speedRegulator); - } - } else { - return 0; - } -} - -void pointing_device_task(void) { - report_mouse_t report = pointing_device_get_report(); - - // todo read as one vector - if (timer_elapsed(lastCursor) > cursorTimeout) { - lastCursor = timer_read(); - report.x = axisToMouseComponent(B4, xOrigin, maxCursorSpeed, xPolarity); - report.y = axisToMouseComponent(B5, yOrigin, maxCursorSpeed, yPolarity); - } - // - if (!readPin(E6)) { - report.buttons |= MOUSE_BTN1; - } else { - report.buttons &= ~MOUSE_BTN1; - } - - pointing_device_set_report(report); - pointing_device_send(); -} - -void matrix_init_keymap(void) { - // init pin? Is needed? - setPinInputHigh(E6); - // Account for drift - xOrigin = analogReadPin(B4); - yOrigin = analogReadPin(B5); -} diff --git a/keyboards/40percentclub/nano/keymaps/drashna/rules.mk b/keyboards/40percentclub/nano/keymaps/drashna/rules.mk index 2b72a112b0d..aa7966a8b58 100644 --- a/keyboards/40percentclub/nano/keymaps/drashna/rules.mk +++ b/keyboards/40percentclub/nano/keymaps/drashna/rules.mk @@ -1,7 +1,6 @@ -POINTING_DEVICE_ENABLE = yes -RGBLIGHT_ENABLE = no -CONSOLE_ENABLE = no +POINTING_DEVICE_ENABLE = yes +POINTING_DEVICE_DRIVER = analog_joystick +RGBLIGHT_ENABLE = no +CONSOLE_ENABLE = no -BOOTLOADER = qmk-dfu - -SRC += analog.c +BOOTLOADER = qmk-dfu diff --git a/keyboards/handwired/tractyl_manuform/5x6_right/config.h b/keyboards/handwired/tractyl_manuform/5x6_right/config.h index ce35197f71c..6f702bd59e6 100644 --- a/keyboards/handwired/tractyl_manuform/5x6_right/config.h +++ b/keyboards/handwired/tractyl_manuform/5x6_right/config.h @@ -31,6 +31,7 @@ along with this program. If not, see . #define DIODE_DIRECTION COL2ROW #define ROTATIONAL_TRANSFORM_ANGLE -25 +#define POINTING_DEVICE_INVERT_X /* Bootmagic Lite key configuration */ #define BOOTMAGIC_LITE_ROW 0 diff --git a/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/keymap.c b/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/keymap.c index 5bfc21fd141..1275950766b 100644 --- a/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/keymap.c +++ b/keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/keymap.c @@ -174,7 +174,11 @@ bool tap_toggling = false; # define TAP_CHECK TAPPING_TERM # endif -void process_mouse_user(report_mouse_t* mouse_report, int8_t x, int8_t y) { +report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { + int8_t x = mouse_report.x, y = mouse_report.y; + mouse_report.x = 0; + mouse_report.y = 0; + if (x != 0 && y != 0) { mouse_timer = timer_read(); # ifdef OLED_ENABLE @@ -185,13 +189,14 @@ void process_mouse_user(report_mouse_t* mouse_report, int8_t x, int8_t y) { x = (x > 0 ? x * x / 16 + x : -x * x / 16 + x); y = (y > 0 ? y * y / 16 + y : -y * y / 16 + y); } - mouse_report->x = x; - mouse_report->y = y; + mouse_report.x = x; + mouse_report.y = y; if (!layer_state_is(_MOUSE)) { layer_on(_MOUSE); } } } + return mouse_report; } void matrix_scan_keymap(void) { diff --git a/keyboards/handwired/tractyl_manuform/5x6_right/rules.mk b/keyboards/handwired/tractyl_manuform/5x6_right/rules.mk index c9bc2dbb999..699e826485c 100644 --- a/keyboards/handwired/tractyl_manuform/5x6_right/rules.mk +++ b/keyboards/handwired/tractyl_manuform/5x6_right/rules.mk @@ -16,11 +16,11 @@ AUDIO_ENABLE = no # Audio output SWAP_HANDS_ENABLE = yes POINTING_DEVICE_ENABLE = yes +POINTING_DEVICE_DRIVER = pmw3360 MOUSE_SHARED_EP = no SPLIT_KEYBOARD = yes -SRC += drivers/sensors/pmw3360.c -QUANTUM_LIB_SRC += spi_master.c tm_sync.c +QUANTUM_LIB_SRC += tm_sync.c DEFAULT_FOLDER = handwired/tractyl_manuform/5x6_right/teensy2pp diff --git a/keyboards/handwired/tractyl_manuform/tm_sync.c b/keyboards/handwired/tractyl_manuform/tm_sync.c index 1cb3549469c..4739af1e76f 100644 --- a/keyboards/handwired/tractyl_manuform/tm_sync.c +++ b/keyboards/handwired/tractyl_manuform/tm_sync.c @@ -17,7 +17,6 @@ #include "tractyl_manuform.h" #include "transactions.h" #include -#include "drivers/sensors/pmw3360.h" kb_config_data_t kb_config; kb_mouse_report_t sync_mouse_report; @@ -82,6 +81,6 @@ void housekeeping_task_sync(void) { void trackball_set_cpi(uint16_t cpi) { kb_config.device_cpi = cpi; if (!is_keyboard_left()) { - pmw_set_cpi(cpi); + pointing_device_set_cpi(cpi); } } diff --git a/keyboards/handwired/tractyl_manuform/tractyl_manuform.c b/keyboards/handwired/tractyl_manuform/tractyl_manuform.c index d851528ffb6..5f476fcc98b 100644 --- a/keyboards/handwired/tractyl_manuform/tractyl_manuform.c +++ b/keyboards/handwired/tractyl_manuform/tractyl_manuform.c @@ -34,55 +34,7 @@ keyboard_config_t keyboard_config; uint16_t dpi_array[] = TRACKBALL_DPI_OPTIONS; #define DPI_OPTION_SIZE (sizeof(dpi_array) / sizeof(uint16_t)) -bool BurstState = false; // init burst state for Trackball module -uint16_t MotionStart = 0; // Timer for accel, 0 is resting state -__attribute__((weak)) void process_mouse_user(report_mouse_t* mouse_report, int8_t x, int8_t y) { - mouse_report->x = x; - mouse_report->y = y; -} - -__attribute__((weak)) void process_mouse(void) { - report_pmw_t data = pmw_read_burst(); - // Reset timer if stopped moving - if (!data.isMotion) { - if (MotionStart != 0) MotionStart = 0; - return; - } - - if (data.isOnSurface) { - // Set timer if new motion - if (MotionStart == 0) { - if (debug_mouse) dprintf("Starting motion.\n"); - MotionStart = timer_read(); - } - - if (debug_mouse) { - dprintf("Delt] d: %d t: %u\n", abs(data.dx) + abs(data.dy), MotionStart); - } - if (debug_mouse) { - dprintf("Pre ] X: %d, Y: %d\n", data.dx, data.dy); - } -#if defined(PROFILE_LINEAR) - float scale = float(timer_elaspsed(MotionStart)) / 1000.0; - data.dx *= scale; - data.dy *= scale; -#elif defined(PROFILE_INVERSE) - // TODO -#else - // no post processing -#endif - - // Wrap to HID size - data.dx = constrain(data.dx, -127, 127); - data.dy = constrain(data.dy, -127, 127); - if (debug_mouse) dprintf("Cons] X: %d, Y: %d\n", data.dx, data.dy); - // dprintf("Elapsed:%u, X: %f Y: %\n", i, pgm_read_byte(firmware_data+i)); - - sync_mouse_report.x = -data.dx; - sync_mouse_report.y = data.dy; - } -} bool process_record_kb(uint16_t keycode, keyrecord_t* record) { if (!process_record_user(keycode, record)) { @@ -109,11 +61,7 @@ bool process_record_kb(uint16_t keycode, keyrecord_t* record) { #ifndef MOUSEKEY_ENABLE if (IS_MOUSEKEY_BUTTON(keycode)) { report_mouse_t currentReport = pointing_device_get_report(); - if (record->event.pressed) { - currentReport.buttons |= 1 << (keycode - KC_MS_BTN1); - } else { - currentReport.buttons &= ~(1 << (keycode - KC_MS_BTN1)); - } + currentReport.buttons = pointing_device_handle_buttons(currentReport.buttons, record->event.pressed, keycode - KC_MS_BTN1); pointing_device_set_report(currentReport); pointing_device_send(); } @@ -145,33 +93,28 @@ void keyboard_post_init_kb(void) { } #ifdef POINTING_DEVICE_ENABLE -void pointing_device_init(void) { - if (!is_keyboard_left()) { - // initialize ball sensor - pmw_spi_init(); - } +void pointing_device_init_kb(void) { trackball_set_cpi(dpi_array[keyboard_config.dpi_config]); + pointing_device_init_user(); } -void pointing_device_task(void) { - report_mouse_t mouse_report = pointing_device_get_report(); - +report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { if (is_keyboard_left()) { if (is_keyboard_master()) { transaction_rpc_recv(RPC_ID_POINTER_STATE_SYNC, sizeof(sync_mouse_report), &sync_mouse_report); - process_mouse_user(&mouse_report, sync_mouse_report.x, sync_mouse_report.y); + mouse_report.x = sync_mouse_report.x; + mouse_report.y = sync_mouse_report.y; + pointing_device_task_user(mouse_report); } } else { - process_mouse(); if (is_keyboard_master()) { - process_mouse_user(&mouse_report, sync_mouse_report.x, sync_mouse_report.y); - sync_mouse_report.x = 0; - sync_mouse_report.y = 0; + pointing_device_task_user(mouse_report); + } else { + sync_mouse_report.x = mouse_report.x; + sync_mouse_report.y = mouse_report.y; } } - - pointing_device_set_report(mouse_report); - pointing_device_send(); + return mouse_report; } #endif diff --git a/keyboards/oddball/config.h b/keyboards/oddball/config.h index 050084d2d39..4bc9c7997c2 100644 --- a/keyboards/oddball/config.h +++ b/keyboards/oddball/config.h @@ -58,3 +58,6 @@ /* Bootmagic Lite key configuration */ // #define BOOTMAGIC_LITE_ROW 0 // #define BOOTMAGIC_LITE_COLUMN 0 + +#define ADNS9800_CS_PIN SPI_SS_PIN +#define PMW3360_CS_PIN SPI_SS_PIN diff --git a/keyboards/oddball/keymaps/default/config.h b/keyboards/oddball/keymaps/default/config.h deleted file mode 100644 index 7eddc070dcd..00000000000 --- a/keyboards/oddball/keymaps/default/config.h +++ /dev/null @@ -1 +0,0 @@ -#define ADNS_9800 diff --git a/keyboards/oddball/keymaps/default/rules.mk b/keyboards/oddball/keymaps/default/rules.mk new file mode 100644 index 00000000000..84de35aeb14 --- /dev/null +++ b/keyboards/oddball/keymaps/default/rules.mk @@ -0,0 +1 @@ +POINTING_DEVICE_DRIVER = adns9800 diff --git a/keyboards/oddball/keymaps/pmw3360/config.h b/keyboards/oddball/keymaps/pmw3360/config.h deleted file mode 100644 index f9af5f39113..00000000000 --- a/keyboards/oddball/keymaps/pmw3360/config.h +++ /dev/null @@ -1 +0,0 @@ -#define PMW_3360 diff --git a/keyboards/oddball/keymaps/pmw3360/rules.mk b/keyboards/oddball/keymaps/pmw3360/rules.mk new file mode 100644 index 00000000000..fab9162dc64 --- /dev/null +++ b/keyboards/oddball/keymaps/pmw3360/rules.mk @@ -0,0 +1 @@ +POINTING_DEVICE_DRIVER = pmw3360 diff --git a/keyboards/oddball/oddball.c b/keyboards/oddball/oddball.c index 7ac6d99016b..bbc3b3f2e4d 100644 --- a/keyboards/oddball/oddball.c +++ b/keyboards/oddball/oddball.c @@ -16,21 +16,17 @@ #include "oddball.h" #include "pointing_device.h" -#include "optical_sensor/optical_sensor.h" - -#define CLAMP_HID(value) value < -127 ? -127 : value > 127 ? 127 : value +extern const pointing_device_driver_t pointing_device_driver; static bool scroll_pressed; static bool mouse_buttons_dirty; static int8_t scroll_h; static int8_t scroll_v; -void pointing_device_init(void){ +void pointing_device_init_kb(void){ if(!is_keyboard_master()) return; - optical_sensor_init(); - // read config from EEPROM and update if needed config_oddball_t kb_config; @@ -41,21 +37,17 @@ void pointing_device_init(void){ eeconfig_update_kb(kb_config.raw); } - optical_sensor_set_config((config_optical_sensor_t){ kb_config.cpi }); + pointing_device_set_cpi(kb_config.cpi); } -void pointing_device_task(void){ - if(!is_keyboard_master()) - return; +report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { + if (!is_keyboard_master()) return mouse_report; - report_mouse_t mouse_report = pointing_device_get_report(); - report_optical_sensor_t sensor_report = optical_sensor_get_report(); - - int8_t clamped_x = CLAMP_HID(sensor_report.x); - int8_t clamped_y = CLAMP_HID(sensor_report.y); - - if(scroll_pressed) { + int8_t clamped_x = mouse_report.x, clamped_y = mouse_report.y; + mouse_report.x = 0; + mouse_report.y = 0; + if (scroll_pressed) { // accumulate scroll scroll_h += clamped_x; scroll_v += clamped_y; @@ -65,33 +57,21 @@ void pointing_device_task(void){ // clear accumulated scroll on assignment - if(scaled_scroll_h != 0){ + if (scaled_scroll_h != 0) { mouse_report.h = -scaled_scroll_h; - scroll_h = 0; + scroll_h = 0; } - if(scaled_scroll_v != 0){ + if (scaled_scroll_v != 0) { mouse_report.v = -scaled_scroll_v; - scroll_v = 0; + scroll_v = 0; } - } - else { + } else { mouse_report.x = -clamped_x; mouse_report.y = clamped_y; } - pointing_device_set_report(mouse_report); - - // only send report on change as even sending report with no change is treated as movement - if(mouse_buttons_dirty || - mouse_report.x != 0 || - mouse_report.y != 0 || - mouse_report.h != 0 || - mouse_report.v != 0){ - - mouse_buttons_dirty = false; - pointing_device_send(); - } + return mouse_report; } static void on_cpi_button(uint16_t cpi, keyrecord_t *record) { @@ -99,7 +79,7 @@ static void on_cpi_button(uint16_t cpi, keyrecord_t *record) { if(!record->event.pressed) return; - optical_sensor_set_config((config_optical_sensor_t){ cpi }); + pointing_device_set_cpi(cpi); config_oddball_t kb_config; kb_config.cpi = cpi; @@ -165,5 +145,5 @@ bool process_record_kb(uint16_t keycode, keyrecord_t *record) { default: return true; - } + } } diff --git a/keyboards/oddball/optical_sensor/optical_sensor.h b/keyboards/oddball/optical_sensor/optical_sensor.h deleted file mode 100644 index 00955209e28..00000000000 --- a/keyboards/oddball/optical_sensor/optical_sensor.h +++ /dev/null @@ -1,53 +0,0 @@ -/* Copyright 2020 Alexander Tulloh - * - * 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 . - */ - -/* common interface for opitcal sensors */ - -#if defined ADNS_9800 - #include "drivers/sensors/adns9800.h" - #define config_optical_sensor_t config_adns_t - #define report_optical_sensor_t report_adns_t - #define optical_sensor_init adns_init - #define optical_sensor_get_config adns_get_config - #define optical_sensor_set_config adns_set_config - #define optical_sensor_get_report adns_get_report -#elif defined PMW_3360 - #include "../pmw/pmw.h" - #define config_optical_sensor_t config_pmw_t - #define report_optical_sensor_t report_pmw_t - #define optical_sensor_init pmw_init - #define optical_sensor_get_config pmw_get_config - #define optical_sensor_set_config pmw_set_config - #define optical_sensor_get_report pmw_get_report -#else - /* fallback stub */ - - #include - - typedef struct { - uint16_t cpi; - } config_optical_sensor_t; - - typedef struct { - int16_t x; - int16_t y; - } report_optical_sensor_t; - - #define optical_sensor_init(){ } - #define optical_sensor_get_config() (config_optical_sensor_t){ } - #define optical_sensor_set_config(config_optical_sensor_t){ } - #define optical_sensor_get_report() (report_optical_sensor_t){ } -#endif diff --git a/keyboards/oddball/pmw/pmw.c b/keyboards/oddball/pmw/pmw.c deleted file mode 100644 index 51d69270240..00000000000 --- a/keyboards/oddball/pmw/pmw.c +++ /dev/null @@ -1,226 +0,0 @@ -/* Copyright 2020 Alexander Tulloh - * - * 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 "spi_master.h" -#include "quantum.h" -#include "pmw3360_srom_0x04.h" -#include "pmw.h" - -// registers -#define Product_ID 0x00 -#define Revision_ID 0x01 -#define Motion 0x02 -#define Delta_X_L 0x03 -#define Delta_X_H 0x04 -#define Delta_Y_L 0x05 -#define Delta_Y_H 0x06 -#define SQUAL 0x07 -#define Raw_Data_Sum 0x08 -#define Maximum_Raw_data 0x09 -#define Minimum_Raw_data 0x0A -#define Shutter_Lower 0x0B -#define Shutter_Upper 0x0C -#define Control 0x0D -#define Config1 0x0F -#define Config2 0x10 -#define Angle_Tune 0x11 -#define Frame_Capture 0x12 -#define SROM_Enable 0x13 -#define Run_Downshift 0x14 -#define Rest1_Rate_Lower 0x15 -#define Rest1_Rate_Upper 0x16 -#define Rest1_Downshift 0x17 -#define Rest2_Rate_Lower 0x18 -#define Rest2_Rate_Upper 0x19 -#define Rest2_Downshift 0x1A -#define Rest3_Rate_Lower 0x1B -#define Rest3_Rate_Upper 0x1C -#define Observation 0x24 -#define Data_Out_Lower 0x25 -#define Data_Out_Upper 0x26 -#define Raw_Data_Dump 0x29 -#define SROM_ID 0x2A -#define Min_SQ_Run 0x2B -#define Raw_Data_Threshold 0x2C -#define Config5 0x2F -#define Power_Up_Reset 0x3A -#define Shutdown 0x3B -#define Inverse_Product_ID 0x3F -#define LiftCutoff_Tune3 0x41 -#define Angle_Snap 0x42 -#define LiftCutoff_Tune1 0x4A -#define Motion_Burst 0x50 -#define LiftCutoff_Tune_Timeout 0x58 -#define LiftCutoff_Tune_Min_Length 0x5A -#define SROM_Load_Burst 0x62 -#define Lift_Config 0x63 -#define Raw_Data_Burst 0x64 -#define LiftCutoff_Tune2 0x65 - -#define PMW_CLOCK_SPEED 70000000 -#define MIN_CPI 100 -#define MAX_CPI 12000 -#define CPI_STEP 100 -#define CLAMP_CPI(value) value < MIN_CPI ? MIN_CPI : value > MAX_CPI ? MAX_CPI : value -#define SPI_MODE 3 -#define SPI_DIVISOR (F_CPU / PMW_CLOCK_SPEED) -#define US_BETWEEN_WRITES 180 -#define US_BETWEEN_READS 20 -#define US_BEFORE_MOTION 35 - -#define MSB1 0x80 - -extern const uint16_t pmw_firmware_length; -extern const uint8_t pmw_firmware_data[]; - -void pmw_spi_start(void){ - spi_start(SPI_SS_PIN, false, SPI_MODE, SPI_DIVISOR); -} - -void pmw_write(uint8_t reg_addr, uint8_t data){ - - pmw_spi_start(); - spi_write(reg_addr | MSB1 ); - spi_write(data); - spi_stop(); - wait_us(US_BETWEEN_WRITES); -} - -uint8_t pmw_read(uint8_t reg_addr){ - - pmw_spi_start(); - spi_write(reg_addr & 0x7f ); - uint8_t data = spi_read(); - spi_stop(); - wait_us(US_BETWEEN_READS); - - return data; -} - -void pmw_init() { - - setPinOutput(SPI_SS_PIN); - - spi_init(); - - // reboot - pmw_write(Power_Up_Reset, 0x5a); - wait_ms(50); - - // read registers and discard - pmw_read(Motion); - pmw_read(Delta_X_L); - pmw_read(Delta_X_H); - pmw_read(Delta_Y_L); - pmw_read(Delta_Y_H); - - // upload firmware - - // disable rest mode - pmw_write(Config2, 0x20); - - // enable initialisation - pmw_write(SROM_Enable, 0x1d); - - // wait a frame - wait_ms(10); - - // start SROM download - pmw_write(SROM_Enable, 0x18); - - // write the SROM file - - pmw_spi_start(); - - spi_write(SROM_Load_Burst | 0x80); - wait_us(15); - - // send all bytes of the firmware - unsigned char c; - for(int i = 0; i < pmw_firmware_length; i++){ - c = (unsigned char)pgm_read_byte(pmw_firmware_data + i); - spi_write(c); - wait_us(15); - } - - spi_stop(); - wait_us(US_BETWEEN_WRITES); - - // read id - pmw_read(SROM_ID); - - // wired mouse - pmw_write(Config2, 0x00); - - // first motion burst; write anything - pmw_write(Motion_Burst, 0xFF); - writePinLow(SPI_SS_PIN); -} - -config_pmw_t pmw_get_config(void) { - uint8_t config_1 = pmw_read(Config1); - return (config_pmw_t){ (config_1 & 0xFF) * CPI_STEP }; -} - -void pmw_set_config(config_pmw_t config) { - uint8_t config_1 = (CLAMP_CPI(config.cpi) / CPI_STEP) & 0xFF; - pmw_write(Config1, config_1); -} - -static int16_t convertDeltaToInt(uint8_t high, uint8_t low){ - - // join bytes into twos compliment - uint16_t twos_comp = (high << 8) | low; - - // convert twos comp to int - if (twos_comp & 0x8000) - return -1 * (~twos_comp + 1); - - return twos_comp; -} - -report_pmw_t pmw_get_report(void) { - - report_pmw_t report = {0, 0}; - - pmw_spi_start(); - - // start burst mode - spi_write(Motion_Burst & 0x7f); - - wait_us(US_BEFORE_MOTION); - - uint8_t motion = spi_read(); - - if(motion & 0x80) { - - // clear observation register - spi_read(); - - // delta registers - uint8_t delta_x_l = spi_read(); - uint8_t delta_x_h = spi_read(); - uint8_t delta_y_l = spi_read(); - uint8_t delta_y_h = spi_read(); - - report.x = convertDeltaToInt(delta_x_h, delta_x_l); - report.y = convertDeltaToInt(delta_y_h, delta_y_l); - } - - spi_stop(); - - return report; -} diff --git a/keyboards/oddball/pmw/pmw3360_srom_0x04.h b/keyboards/oddball/pmw/pmw3360_srom_0x04.h deleted file mode 100644 index 0dda4f1ab4e..00000000000 --- a/keyboards/oddball/pmw/pmw3360_srom_0x04.h +++ /dev/null @@ -1,280 +0,0 @@ -#pragma once - -#include "progmem.h" - -const uint16_t pmw_firmware_length = 4094; - -const uint8_t pmw_firmware_data[] PROGMEM = { -0x01, 0x04, 0x8e, 0x96, 0x6e, 0x77, 0x3e, 0xfe, 0x7e, 0x5f, 0x1d, 0xb8, 0xf2, 0x66, 0x4e, -0xff, 0x5d, 0x19, 0xb0, 0xc2, 0x04, 0x69, 0x54, 0x2a, 0xd6, 0x2e, 0xbf, 0xdd, 0x19, 0xb0, -0xc3, 0xe5, 0x29, 0xb1, 0xe0, 0x23, 0xa5, 0xa9, 0xb1, 0xc1, 0x00, 0x82, 0x67, 0x4c, 0x1a, -0x97, 0x8d, 0x79, 0x51, 0x20, 0xc7, 0x06, 0x8e, 0x7c, 0x7c, 0x7a, 0x76, 0x4f, 0xfd, 0x59, -0x30, 0xe2, 0x46, 0x0e, 0x9e, 0xbe, 0xdf, 0x1d, 0x99, 0x91, 0xa0, 0xa5, 0xa1, 0xa9, 0xd0, -0x22, 0xc6, 0xef, 0x5c, 0x1b, 0x95, 0x89, 0x90, 0xa2, 0xa7, 0xcc, 0xfb, 0x55, 0x28, 0xb3, -0xe4, 0x4a, 0xf7, 0x6c, 0x3b, 0xf4, 0x6a, 0x56, 0x2e, 0xde, 0x1f, 0x9d, 0xb8, 0xd3, 0x05, -0x88, 0x92, 0xa6, 0xce, 0x1e, 0xbe, 0xdf, 0x1d, 0x99, 0xb0, 0xe2, 0x46, 0xef, 0x5c, 0x07, -0x11, 0x5d, 0x98, 0x0b, 0x9d, 0x94, 0x97, 0xee, 0x4e, 0x45, 0x33, 0x6b, 0x44, 0xc7, 0x29, -0x56, 0x27, 0x30, 0xc6, 0xa7, 0xd5, 0xf2, 0x56, 0xdf, 0xb4, 0x38, 0x62, 0xcb, 0xa0, 0xb6, -0xe3, 0x0f, 0x84, 0x06, 0x24, 0x05, 0x65, 0x6f, 0x76, 0x89, 0xb5, 0x77, 0x41, 0x27, 0x82, -0x66, 0x65, 0x82, 0xcc, 0xd5, 0xe6, 0x20, 0xd5, 0x27, 0x17, 0xc5, 0xf8, 0x03, 0x23, 0x7c, -0x5f, 0x64, 0xa5, 0x1d, 0xc1, 0xd6, 0x36, 0xcb, 0x4c, 0xd4, 0xdb, 0x66, 0xd7, 0x8b, 0xb1, -0x99, 0x7e, 0x6f, 0x4c, 0x36, 0x40, 0x06, 0xd6, 0xeb, 0xd7, 0xa2, 0xe4, 0xf4, 0x95, 0x51, -0x5a, 0x54, 0x96, 0xd5, 0x53, 0x44, 0xd7, 0x8c, 0xe0, 0xb9, 0x40, 0x68, 0xd2, 0x18, 0xe9, -0xdd, 0x9a, 0x23, 0x92, 0x48, 0xee, 0x7f, 0x43, 0xaf, 0xea, 0x77, 0x38, 0x84, 0x8c, 0x0a, -0x72, 0xaf, 0x69, 0xf8, 0xdd, 0xf1, 0x24, 0x83, 0xa3, 0xf8, 0x4a, 0xbf, 0xf5, 0x94, 0x13, -0xdb, 0xbb, 0xd8, 0xb4, 0xb3, 0xa0, 0xfb, 0x45, 0x50, 0x60, 0x30, 0x59, 0x12, 0x31, 0x71, -0xa2, 0xd3, 0x13, 0xe7, 0xfa, 0xe7, 0xce, 0x0f, 0x63, 0x15, 0x0b, 0x6b, 0x94, 0xbb, 0x37, -0x83, 0x26, 0x05, 0x9d, 0xfb, 0x46, 0x92, 0xfc, 0x0a, 0x15, 0xd1, 0x0d, 0x73, 0x92, 0xd6, -0x8c, 0x1b, 0x8c, 0xb8, 0x55, 0x8a, 0xce, 0xbd, 0xfe, 0x8e, 0xfc, 0xed, 0x09, 0x12, 0x83, -0x91, 0x82, 0x51, 0x31, 0x23, 0xfb, 0xb4, 0x0c, 0x76, 0xad, 0x7c, 0xd9, 0xb4, 0x4b, 0xb2, -0x67, 0x14, 0x09, 0x9c, 0x7f, 0x0c, 0x18, 0xba, 0x3b, 0xd6, 0x8e, 0x14, 0x2a, 0xe4, 0x1b, -0x52, 0x9f, 0x2b, 0x7d, 0xe1, 0xfb, 0x6a, 0x33, 0x02, 0xfa, 0xac, 0x5a, 0xf2, 0x3e, 0x88, -0x7e, 0xae, 0xd1, 0xf3, 0x78, 0xe8, 0x05, 0xd1, 0xe3, 0xdc, 0x21, 0xf6, 0xe1, 0x9a, 0xbd, -0x17, 0x0e, 0xd9, 0x46, 0x9b, 0x88, 0x03, 0xea, 0xf6, 0x66, 0xbe, 0x0e, 0x1b, 0x50, 0x49, -0x96, 0x40, 0x97, 0xf1, 0xf1, 0xe4, 0x80, 0xa6, 0x6e, 0xe8, 0x77, 0x34, 0xbf, 0x29, 0x40, -0x44, 0xc2, 0xff, 0x4e, 0x98, 0xd3, 0x9c, 0xa3, 0x32, 0x2b, 0x76, 0x51, 0x04, 0x09, 0xe7, -0xa9, 0xd1, 0xa6, 0x32, 0xb1, 0x23, 0x53, 0xe2, 0x47, 0xab, 0xd6, 0xf5, 0x69, 0x5c, 0x3e, -0x5f, 0xfa, 0xae, 0x45, 0x20, 0xe5, 0xd2, 0x44, 0xff, 0x39, 0x32, 0x6d, 0xfd, 0x27, 0x57, -0x5c, 0xfd, 0xf0, 0xde, 0xc1, 0xb5, 0x99, 0xe5, 0xf5, 0x1c, 0x77, 0x01, 0x75, 0xc5, 0x6d, -0x58, 0x92, 0xf2, 0xb2, 0x47, 0x00, 0x01, 0x26, 0x96, 0x7a, 0x30, 0xff, 0xb7, 0xf0, 0xef, -0x77, 0xc1, 0x8a, 0x5d, 0xdc, 0xc0, 0xd1, 0x29, 0x30, 0x1e, 0x77, 0x38, 0x7a, 0x94, 0xf1, -0xb8, 0x7a, 0x7e, 0xef, 0xa4, 0xd1, 0xac, 0x31, 0x4a, 0xf2, 0x5d, 0x64, 0x3d, 0xb2, 0xe2, -0xf0, 0x08, 0x99, 0xfc, 0x70, 0xee, 0x24, 0xa7, 0x7e, 0xee, 0x1e, 0x20, 0x69, 0x7d, 0x44, -0xbf, 0x87, 0x42, 0xdf, 0x88, 0x3b, 0x0c, 0xda, 0x42, 0xc9, 0x04, 0xf9, 0x45, 0x50, 0xfc, -0x83, 0x8f, 0x11, 0x6a, 0x72, 0xbc, 0x99, 0x95, 0xf0, 0xac, 0x3d, 0xa7, 0x3b, 0xcd, 0x1c, -0xe2, 0x88, 0x79, 0x37, 0x11, 0x5f, 0x39, 0x89, 0x95, 0x0a, 0x16, 0x84, 0x7a, 0xf6, 0x8a, -0xa4, 0x28, 0xe4, 0xed, 0x83, 0x80, 0x3b, 0xb1, 0x23, 0xa5, 0x03, 0x10, 0xf4, 0x66, 0xea, -0xbb, 0x0c, 0x0f, 0xc5, 0xec, 0x6c, 0x69, 0xc5, 0xd3, 0x24, 0xab, 0xd4, 0x2a, 0xb7, 0x99, -0x88, 0x76, 0x08, 0xa0, 0xa8, 0x95, 0x7c, 0xd8, 0x38, 0x6d, 0xcd, 0x59, 0x02, 0x51, 0x4b, -0xf1, 0xb5, 0x2b, 0x50, 0xe3, 0xb6, 0xbd, 0xd0, 0x72, 0xcf, 0x9e, 0xfd, 0x6e, 0xbb, 0x44, -0xc8, 0x24, 0x8a, 0x77, 0x18, 0x8a, 0x13, 0x06, 0xef, 0x97, 0x7d, 0xfa, 0x81, 0xf0, 0x31, -0xe6, 0xfa, 0x77, 0xed, 0x31, 0x06, 0x31, 0x5b, 0x54, 0x8a, 0x9f, 0x30, 0x68, 0xdb, 0xe2, -0x40, 0xf8, 0x4e, 0x73, 0xfa, 0xab, 0x74, 0x8b, 0x10, 0x58, 0x13, 0xdc, 0xd2, 0xe6, 0x78, -0xd1, 0x32, 0x2e, 0x8a, 0x9f, 0x2c, 0x58, 0x06, 0x48, 0x27, 0xc5, 0xa9, 0x5e, 0x81, 0x47, -0x89, 0x46, 0x21, 0x91, 0x03, 0x70, 0xa4, 0x3e, 0x88, 0x9c, 0xda, 0x33, 0x0a, 0xce, 0xbc, -0x8b, 0x8e, 0xcf, 0x9f, 0xd3, 0x71, 0x80, 0x43, 0xcf, 0x6b, 0xa9, 0x51, 0x83, 0x76, 0x30, -0x82, 0xc5, 0x6a, 0x85, 0x39, 0x11, 0x50, 0x1a, 0x82, 0xdc, 0x1e, 0x1c, 0xd5, 0x7d, 0xa9, -0x71, 0x99, 0x33, 0x47, 0x19, 0x97, 0xb3, 0x5a, 0xb1, 0xdf, 0xed, 0xa4, 0xf2, 0xe6, 0x26, -0x84, 0xa2, 0x28, 0x9a, 0x9e, 0xdf, 0xa6, 0x6a, 0xf4, 0xd6, 0xfc, 0x2e, 0x5b, 0x9d, 0x1a, -0x2a, 0x27, 0x68, 0xfb, 0xc1, 0x83, 0x21, 0x4b, 0x90, 0xe0, 0x36, 0xdd, 0x5b, 0x31, 0x42, -0x55, 0xa0, 0x13, 0xf7, 0xd0, 0x89, 0x53, 0x71, 0x99, 0x57, 0x09, 0x29, 0xc5, 0xf3, 0x21, -0xf8, 0x37, 0x2f, 0x40, 0xf3, 0xd4, 0xaf, 0x16, 0x08, 0x36, 0x02, 0xfc, 0x77, 0xc5, 0x8b, -0x04, 0x90, 0x56, 0xb9, 0xc9, 0x67, 0x9a, 0x99, 0xe8, 0x00, 0xd3, 0x86, 0xff, 0x97, 0x2d, -0x08, 0xe9, 0xb7, 0xb3, 0x91, 0xbc, 0xdf, 0x45, 0xc6, 0xed, 0x0f, 0x8c, 0x4c, 0x1e, 0xe6, -0x5b, 0x6e, 0x38, 0x30, 0xe4, 0xaa, 0xe3, 0x95, 0xde, 0xb9, 0xe4, 0x9a, 0xf5, 0xb2, 0x55, -0x9a, 0x87, 0x9b, 0xf6, 0x6a, 0xb2, 0xf2, 0x77, 0x9a, 0x31, 0xf4, 0x7a, 0x31, 0xd1, 0x1d, -0x04, 0xc0, 0x7c, 0x32, 0xa2, 0x9e, 0x9a, 0xf5, 0x62, 0xf8, 0x27, 0x8d, 0xbf, 0x51, 0xff, -0xd3, 0xdf, 0x64, 0x37, 0x3f, 0x2a, 0x6f, 0x76, 0x3a, 0x7d, 0x77, 0x06, 0x9e, 0x77, 0x7f, -0x5e, 0xeb, 0x32, 0x51, 0xf9, 0x16, 0x66, 0x9a, 0x09, 0xf3, 0xb0, 0x08, 0xa4, 0x70, 0x96, -0x46, 0x30, 0xff, 0xda, 0x4f, 0xe9, 0x1b, 0xed, 0x8d, 0xf8, 0x74, 0x1f, 0x31, 0x92, 0xb3, -0x73, 0x17, 0x36, 0xdb, 0x91, 0x30, 0xd6, 0x88, 0x55, 0x6b, 0x34, 0x77, 0x87, 0x7a, 0xe7, -0xee, 0x06, 0xc6, 0x1c, 0x8c, 0x19, 0x0c, 0x48, 0x46, 0x23, 0x5e, 0x9c, 0x07, 0x5c, 0xbf, -0xb4, 0x7e, 0xd6, 0x4f, 0x74, 0x9c, 0xe2, 0xc5, 0x50, 0x8b, 0xc5, 0x8b, 0x15, 0x90, 0x60, -0x62, 0x57, 0x29, 0xd0, 0x13, 0x43, 0xa1, 0x80, 0x88, 0x91, 0x00, 0x44, 0xc7, 0x4d, 0x19, -0x86, 0xcc, 0x2f, 0x2a, 0x75, 0x5a, 0xfc, 0xeb, 0x97, 0x2a, 0x70, 0xe3, 0x78, 0xd8, 0x91, -0xb0, 0x4f, 0x99, 0x07, 0xa3, 0x95, 0xea, 0x24, 0x21, 0xd5, 0xde, 0x51, 0x20, 0x93, 0x27, -0x0a, 0x30, 0x73, 0xa8, 0xff, 0x8a, 0x97, 0xe9, 0xa7, 0x6a, 0x8e, 0x0d, 0xe8, 0xf0, 0xdf, -0xec, 0xea, 0xb4, 0x6c, 0x1d, 0x39, 0x2a, 0x62, 0x2d, 0x3d, 0x5a, 0x8b, 0x65, 0xf8, 0x90, -0x05, 0x2e, 0x7e, 0x91, 0x2c, 0x78, 0xef, 0x8e, 0x7a, 0xc1, 0x2f, 0xac, 0x78, 0xee, 0xaf, -0x28, 0x45, 0x06, 0x4c, 0x26, 0xaf, 0x3b, 0xa2, 0xdb, 0xa3, 0x93, 0x06, 0xb5, 0x3c, 0xa5, -0xd8, 0xee, 0x8f, 0xaf, 0x25, 0xcc, 0x3f, 0x85, 0x68, 0x48, 0xa9, 0x62, 0xcc, 0x97, 0x8f, -0x7f, 0x2a, 0xea, 0xe0, 0x15, 0x0a, 0xad, 0x62, 0x07, 0xbd, 0x45, 0xf8, 0x41, 0xd8, 0x36, -0xcb, 0x4c, 0xdb, 0x6e, 0xe6, 0x3a, 0xe7, 0xda, 0x15, 0xe9, 0x29, 0x1e, 0x12, 0x10, 0xa0, -0x14, 0x2c, 0x0e, 0x3d, 0xf4, 0xbf, 0x39, 0x41, 0x92, 0x75, 0x0b, 0x25, 0x7b, 0xa3, 0xce, -0x39, 0x9c, 0x15, 0x64, 0xc8, 0xfa, 0x3d, 0xef, 0x73, 0x27, 0xfe, 0x26, 0x2e, 0xce, 0xda, -0x6e, 0xfd, 0x71, 0x8e, 0xdd, 0xfe, 0x76, 0xee, 0xdc, 0x12, 0x5c, 0x02, 0xc5, 0x3a, 0x4e, -0x4e, 0x4f, 0xbf, 0xca, 0x40, 0x15, 0xc7, 0x6e, 0x8d, 0x41, 0xf1, 0x10, 0xe0, 0x4f, 0x7e, -0x97, 0x7f, 0x1c, 0xae, 0x47, 0x8e, 0x6b, 0xb1, 0x25, 0x31, 0xb0, 0x73, 0xc7, 0x1b, 0x97, -0x79, 0xf9, 0x80, 0xd3, 0x66, 0x22, 0x30, 0x07, 0x74, 0x1e, 0xe4, 0xd0, 0x80, 0x21, 0xd6, -0xee, 0x6b, 0x6c, 0x4f, 0xbf, 0xf5, 0xb7, 0xd9, 0x09, 0x87, 0x2f, 0xa9, 0x14, 0xbe, 0x27, -0xd9, 0x72, 0x50, 0x01, 0xd4, 0x13, 0x73, 0xa6, 0xa7, 0x51, 0x02, 0x75, 0x25, 0xe1, 0xb3, -0x45, 0x34, 0x7d, 0xa8, 0x8e, 0xeb, 0xf3, 0x16, 0x49, 0xcb, 0x4f, 0x8c, 0xa1, 0xb9, 0x36, -0x85, 0x39, 0x75, 0x5d, 0x08, 0x00, 0xae, 0xeb, 0xf6, 0xea, 0xd7, 0x13, 0x3a, 0x21, 0x5a, -0x5f, 0x30, 0x84, 0x52, 0x26, 0x95, 0xc9, 0x14, 0xf2, 0x57, 0x55, 0x6b, 0xb1, 0x10, 0xc2, -0xe1, 0xbd, 0x3b, 0x51, 0xc0, 0xb7, 0x55, 0x4c, 0x71, 0x12, 0x26, 0xc7, 0x0d, 0xf9, 0x51, -0xa4, 0x38, 0x02, 0x05, 0x7f, 0xb8, 0xf1, 0x72, 0x4b, 0xbf, 0x71, 0x89, 0x14, 0xf3, 0x77, -0x38, 0xd9, 0x71, 0x24, 0xf3, 0x00, 0x11, 0xa1, 0xd8, 0xd4, 0x69, 0x27, 0x08, 0x37, 0x35, -0xc9, 0x11, 0x9d, 0x90, 0x1c, 0x0e, 0xe7, 0x1c, 0xff, 0x2d, 0x1e, 0xe8, 0x92, 0xe1, 0x18, -0x10, 0x95, 0x7c, 0xe0, 0x80, 0xf4, 0x96, 0x43, 0x21, 0xf9, 0x75, 0x21, 0x64, 0x38, 0xdd, -0x9f, 0x1e, 0x95, 0x16, 0xda, 0x56, 0x1d, 0x4f, 0x9a, 0x53, 0xb2, 0xe2, 0xe4, 0x18, 0xcb, -0x6b, 0x1a, 0x65, 0xeb, 0x56, 0xc6, 0x3b, 0xe5, 0xfe, 0xd8, 0x26, 0x3f, 0x3a, 0x84, 0x59, -0x72, 0x66, 0xa2, 0xf3, 0x75, 0xff, 0xfb, 0x60, 0xb3, 0x22, 0xad, 0x3f, 0x2d, 0x6b, 0xf9, -0xeb, 0xea, 0x05, 0x7c, 0xd8, 0x8f, 0x6d, 0x2c, 0x98, 0x9e, 0x2b, 0x93, 0xf1, 0x5e, 0x46, -0xf0, 0x87, 0x49, 0x29, 0x73, 0x68, 0xd7, 0x7f, 0xf9, 0xf0, 0xe5, 0x7d, 0xdb, 0x1d, 0x75, -0x19, 0xf3, 0xc4, 0x58, 0x9b, 0x17, 0x88, 0xa8, 0x92, 0xe0, 0xbe, 0xbd, 0x8b, 0x1d, 0x8d, -0x9f, 0x56, 0x76, 0xad, 0xaf, 0x29, 0xe2, 0xd9, 0xd5, 0x52, 0xf6, 0xb5, 0x56, 0x35, 0x57, -0x3a, 0xc8, 0xe1, 0x56, 0x43, 0x19, 0x94, 0xd3, 0x04, 0x9b, 0x6d, 0x35, 0xd8, 0x0b, 0x5f, -0x4d, 0x19, 0x8e, 0xec, 0xfa, 0x64, 0x91, 0x0a, 0x72, 0x20, 0x2b, 0xbc, 0x1a, 0x4a, 0xfe, -0x8b, 0xfd, 0xbb, 0xed, 0x1b, 0x23, 0xea, 0xad, 0x72, 0x82, 0xa1, 0x29, 0x99, 0x71, 0xbd, -0xf0, 0x95, 0xc1, 0x03, 0xdd, 0x7b, 0xc2, 0xb2, 0x3c, 0x28, 0x54, 0xd3, 0x68, 0xa4, 0x72, -0xc8, 0x66, 0x96, 0xe0, 0xd1, 0xd8, 0x7f, 0xf8, 0xd1, 0x26, 0x2b, 0xf7, 0xad, 0xba, 0x55, -0xca, 0x15, 0xb9, 0x32, 0xc3, 0xe5, 0x88, 0x97, 0x8e, 0x5c, 0xfb, 0x92, 0x25, 0x8b, 0xbf, -0xa2, 0x45, 0x55, 0x7a, 0xa7, 0x6f, 0x8b, 0x57, 0x5b, 0xcf, 0x0e, 0xcb, 0x1d, 0xfb, 0x20, -0x82, 0x77, 0xa8, 0x8c, 0xcc, 0x16, 0xce, 0x1d, 0xfa, 0xde, 0xcc, 0x0b, 0x62, 0xfe, 0xcc, -0xe1, 0xb7, 0xf0, 0xc3, 0x81, 0x64, 0x73, 0x40, 0xa0, 0xc2, 0x4d, 0x89, 0x11, 0x75, 0x33, -0x55, 0x33, 0x8d, 0xe8, 0x4a, 0xfd, 0xea, 0x6e, 0x30, 0x0b, 0xd7, 0x31, 0x2c, 0xde, 0x47, -0xe3, 0xbf, 0xf8, 0x55, 0x42, 0xe2, 0x7f, 0x59, 0xe5, 0x17, 0xef, 0x99, 0x34, 0x69, 0x91, -0xb1, 0x23, 0x8e, 0x20, 0x87, 0x2d, 0xa8, 0xfe, 0xd5, 0x8a, 0xf3, 0x84, 0x3a, 0xf0, 0x37, -0xe4, 0x09, 0x00, 0x54, 0xee, 0x67, 0x49, 0x93, 0xe4, 0x81, 0x70, 0xe3, 0x90, 0x4d, 0xef, -0xfe, 0x41, 0xb7, 0x99, 0x7b, 0xc1, 0x83, 0xba, 0x62, 0x12, 0x6f, 0x7d, 0xde, 0x6b, 0xaf, -0xda, 0x16, 0xf9, 0x55, 0x51, 0xee, 0xa6, 0x0c, 0x2b, 0x02, 0xa3, 0xfd, 0x8d, 0xfb, 0x30, -0x17, 0xe4, 0x6f, 0xdf, 0x36, 0x71, 0xc4, 0xca, 0x87, 0x25, 0x48, 0xb0, 0x47, 0xec, 0xea, -0xb4, 0xbf, 0xa5, 0x4d, 0x9b, 0x9f, 0x02, 0x93, 0xc4, 0xe3, 0xe4, 0xe8, 0x42, 0x2d, 0x68, -0x81, 0x15, 0x0a, 0xeb, 0x84, 0x5b, 0xd6, 0xa8, 0x74, 0xfb, 0x7d, 0x1d, 0xcb, 0x2c, 0xda, -0x46, 0x2a, 0x76, 0x62, 0xce, 0xbc, 0x5c, 0x9e, 0x8b, 0xe7, 0xcf, 0xbe, 0x78, 0xf5, 0x7c, -0xeb, 0xb3, 0x3a, 0x9c, 0xaa, 0x6f, 0xcc, 0x72, 0xd1, 0x59, 0xf2, 0x11, 0x23, 0xd6, 0x3f, -0x48, 0xd1, 0xb7, 0xce, 0xb0, 0xbf, 0xcb, 0xea, 0x80, 0xde, 0x57, 0xd4, 0x5e, 0x97, 0x2f, -0x75, 0xd1, 0x50, 0x8e, 0x80, 0x2c, 0x66, 0x79, 0xbf, 0x72, 0x4b, 0xbd, 0x8a, 0x81, 0x6c, -0xd3, 0xe1, 0x01, 0xdc, 0xd2, 0x15, 0x26, 0xc5, 0x36, 0xda, 0x2c, 0x1a, 0xc0, 0x27, 0x94, -0xed, 0xb7, 0x9b, 0x85, 0x0b, 0x5e, 0x80, 0x97, 0xc5, 0xec, 0x4f, 0xec, 0x88, 0x5d, 0x50, -0x07, 0x35, 0x47, 0xdc, 0x0b, 0x3b, 0x3d, 0xdd, 0x60, 0xaf, 0xa8, 0x5d, 0x81, 0x38, 0x24, -0x25, 0x5d, 0x5c, 0x15, 0xd1, 0xde, 0xb3, 0xab, 0xec, 0x05, 0x69, 0xef, 0x83, 0xed, 0x57, -0x54, 0xb8, 0x64, 0x64, 0x11, 0x16, 0x32, 0x69, 0xda, 0x9f, 0x2d, 0x7f, 0x36, 0xbb, 0x44, -0x5a, 0x34, 0xe8, 0x7f, 0xbf, 0x03, 0xeb, 0x00, 0x7f, 0x59, 0x68, 0x22, 0x79, 0xcf, 0x73, -0x6c, 0x2c, 0x29, 0xa7, 0xa1, 0x5f, 0x38, 0xa1, 0x1d, 0xf0, 0x20, 0x53, 0xe0, 0x1a, 0x63, -0x14, 0x58, 0x71, 0x10, 0xaa, 0x08, 0x0c, 0x3e, 0x16, 0x1a, 0x60, 0x22, 0x82, 0x7f, 0xba, -0xa4, 0x43, 0xa0, 0xd0, 0xac, 0x1b, 0xd5, 0x6b, 0x64, 0xb5, 0x14, 0x93, 0x31, 0x9e, 0x53, -0x50, 0xd0, 0x57, 0x66, 0xee, 0x5a, 0x4f, 0xfb, 0x03, 0x2a, 0x69, 0x58, 0x76, 0xf1, 0x83, -0xf7, 0x4e, 0xba, 0x8c, 0x42, 0x06, 0x60, 0x5d, 0x6d, 0xce, 0x60, 0x88, 0xae, 0xa4, 0xc3, -0xf1, 0x03, 0xa5, 0x4b, 0x98, 0xa1, 0xff, 0x67, 0xe1, 0xac, 0xa2, 0xb8, 0x62, 0xd7, 0x6f, -0xa0, 0x31, 0xb4, 0xd2, 0x77, 0xaf, 0x21, 0x10, 0x06, 0xc6, 0x9a, 0xff, 0x1d, 0x09, 0x17, -0x0e, 0x5f, 0xf1, 0xaa, 0x54, 0x34, 0x4b, 0x45, 0x8a, 0x87, 0x63, 0xa6, 0xdc, 0xf9, 0x24, -0x30, 0x67, 0xc6, 0xb2, 0xd6, 0x61, 0x33, 0x69, 0xee, 0x50, 0x61, 0x57, 0x28, 0xe7, 0x7e, -0xee, 0xec, 0x3a, 0x5a, 0x73, 0x4e, 0xa8, 0x8d, 0xe4, 0x18, 0xea, 0xec, 0x41, 0x64, 0xc8, -0xe2, 0xe8, 0x66, 0xb6, 0x2d, 0xb6, 0xfb, 0x6a, 0x6c, 0x16, 0xb3, 0xdd, 0x46, 0x43, 0xb9, -0x73, 0x00, 0x6a, 0x71, 0xed, 0x4e, 0x9d, 0x25, 0x1a, 0xc3, 0x3c, 0x4a, 0x95, 0x15, 0x99, -0x35, 0x81, 0x14, 0x02, 0xd6, 0x98, 0x9b, 0xec, 0xd8, 0x23, 0x3b, 0x84, 0x29, 0xaf, 0x0c, -0x99, 0x83, 0xa6, 0x9a, 0x34, 0x4f, 0xfa, 0xe8, 0xd0, 0x3c, 0x4b, 0xd0, 0xfb, 0xb6, 0x68, -0xb8, 0x9e, 0x8f, 0xcd, 0xf7, 0x60, 0x2d, 0x7a, 0x22, 0xe5, 0x7d, 0xab, 0x65, 0x1b, 0x95, -0xa7, 0xa8, 0x7f, 0xb6, 0x77, 0x47, 0x7b, 0x5f, 0x8b, 0x12, 0x72, 0xd0, 0xd4, 0x91, 0xef, -0xde, 0x19, 0x50, 0x3c, 0xa7, 0x8b, 0xc4, 0xa9, 0xb3, 0x23, 0xcb, 0x76, 0xe6, 0x81, 0xf0, -0xc1, 0x04, 0x8f, 0xa3, 0xb8, 0x54, 0x5b, 0x97, 0xac, 0x19, 0xff, 0x3f, 0x55, 0x27, 0x2f, -0xe0, 0x1d, 0x42, 0x9b, 0x57, 0xfc, 0x4b, 0x4e, 0x0f, 0xce, 0x98, 0xa9, 0x43, 0x57, 0x03, -0xbd, 0xe7, 0xc8, 0x94, 0xdf, 0x6e, 0x36, 0x73, 0x32, 0xb4, 0xef, 0x2e, 0x85, 0x7a, 0x6e, -0xfc, 0x6c, 0x18, 0x82, 0x75, 0x35, 0x90, 0x07, 0xf3, 0xe4, 0x9f, 0x3e, 0xdc, 0x68, 0xf3, -0xb5, 0xf3, 0x19, 0x80, 0x92, 0x06, 0x99, 0xa2, 0xe8, 0x6f, 0xff, 0x2e, 0x7f, 0xae, 0x42, -0xa4, 0x5f, 0xfb, 0xd4, 0x0e, 0x81, 0x2b, 0xc3, 0x04, 0xff, 0x2b, 0xb3, 0x74, 0x4e, 0x36, -0x5b, 0x9c, 0x15, 0x00, 0xc6, 0x47, 0x2b, 0xe8, 0x8b, 0x3d, 0xf1, 0x9c, 0x03, 0x9a, 0x58, -0x7f, 0x9b, 0x9c, 0xbf, 0x85, 0x49, 0x79, 0x35, 0x2e, 0x56, 0x7b, 0x41, 0x14, 0x39, 0x47, -0x83, 0x26, 0xaa, 0x07, 0x89, 0x98, 0x11, 0x1b, 0x86, 0xe7, 0x73, 0x7a, 0xd8, 0x7d, 0x78, -0x61, 0x53, 0xe9, 0x79, 0xf5, 0x36, 0x8d, 0x44, 0x92, 0x84, 0xf9, 0x13, 0x50, 0x58, 0x3b, -0xa4, 0x6a, 0x36, 0x65, 0x49, 0x8e, 0x3c, 0x0e, 0xf1, 0x6f, 0xd2, 0x84, 0xc4, 0x7e, 0x8e, -0x3f, 0x39, 0xae, 0x7c, 0x84, 0xf1, 0x63, 0x37, 0x8e, 0x3c, 0xcc, 0x3e, 0x44, 0x81, 0x45, -0xf1, 0x4b, 0xb9, 0xed, 0x6b, 0x36, 0x5d, 0xbb, 0x20, 0x60, 0x1a, 0x0f, 0xa3, 0xaa, 0x55, -0x77, 0x3a, 0xa9, 0xae, 0x37, 0x4d, 0xba, 0xb8, 0x86, 0x6b, 0xbc, 0x08, 0x50, 0xf6, 0xcc, -0xa4, 0xbd, 0x1d, 0x40, 0x72, 0xa5, 0x86, 0xfa, 0xe2, 0x10, 0xae, 0x3d, 0x58, 0x4b, 0x97, -0xf3, 0x43, 0x74, 0xa9, 0x9e, 0xeb, 0x21, 0xb7, 0x01, 0xa4, 0x86, 0x93, 0x97, 0xee, 0x2f, -0x4f, 0x3b, 0x86, 0xa1, 0x41, 0x6f, 0x41, 0x26, 0x90, 0x78, 0x5c, 0x7f, 0x30, 0x38, 0x4b, -0x3f, 0xaa, 0xec, 0xed, 0x5c, 0x6f, 0x0e, 0xad, 0x43, 0x87, 0xfd, 0x93, 0x35, 0xe6, 0x01, -0xef, 0x41, 0x26, 0x90, 0x99, 0x9e, 0xfb, 0x19, 0x5b, 0xad, 0xd2, 0x91, 0x8a, 0xe0, 0x46, -0xaf, 0x65, 0xfa, 0x4f, 0x84, 0xc1, 0xa1, 0x2d, 0xcf, 0x45, 0x8b, 0xd3, 0x85, 0x50, 0x55, -0x7c, 0xf9, 0x67, 0x88, 0xd4, 0x4e, 0xe9, 0xd7, 0x6b, 0x61, 0x54, 0xa1, 0xa4, 0xa6, 0xa2, -0xc2, 0xbf, 0x30, 0x9c, 0x40, 0x9f, 0x5f, 0xd7, 0x69, 0x2b, 0x24, 0x82, 0x5e, 0xd9, 0xd6, -0xa7, 0x12, 0x54, 0x1a, 0xf7, 0x55, 0x9f, 0x76, 0x50, 0xa9, 0x95, 0x84, 0xe6, 0x6b, 0x6d, -0xb5, 0x96, 0x54, 0xd6, 0xcd, 0xb3, 0xa1, 0x9b, 0x46, 0xa7, 0x94, 0x4d, 0xc4, 0x94, 0xb4, -0x98, 0xe3, 0xe1, 0xe2, 0x34, 0xd5, 0x33, 0x16, 0x07, 0x54, 0xcd, 0xb7, 0x77, 0x53, 0xdb, -0x4f, 0x4d, 0x46, 0x9d, 0xe9, 0xd4, 0x9c, 0x8a, 0x36, 0xb6, 0xb8, 0x38, 0x26, 0x6c, 0x0e, -0xff, 0x9c, 0x1b, 0x43, 0x8b, 0x80, 0xcc, 0xb9, 0x3d, 0xda, 0xc7, 0xf1, 0x8a, 0xf2, 0x6d, -0xb8, 0xd7, 0x74, 0x2f, 0x7e, 0x1e, 0xb7, 0xd3, 0x4a, 0xb4, 0xac, 0xfc, 0x79, 0x48, 0x6c, -0xbc, 0x96, 0xb6, 0x94, 0x46, 0x57, 0x2d, 0xb0, 0xa3, 0xfc, 0x1e, 0xb9, 0x52, 0x60, 0x85, -0x2d, 0x41, 0xd0, 0x43, 0x01, 0x1e, 0x1c, 0xd5, 0x7d, 0xfc, 0xf3, 0x96, 0x0d, 0xc7, 0xcb, -0x2a, 0x29, 0x9a, 0x93, 0xdd, 0x88, 0x2d, 0x37, 0x5d, 0xaa, 0xfb, 0x49, 0x68, 0xa0, 0x9c, -0x50, 0x86, 0x7f, 0x68, 0x56, 0x57, 0xf9, 0x79, 0x18, 0x39, 0xd4, 0xe0, 0x01, 0x84, 0x33, -0x61, 0xca, 0xa5, 0xd2, 0xd6, 0xe4, 0xc9, 0x8a, 0x4a, 0x23, 0x44, 0x4e, 0xbc, 0xf0, 0xdc, -0x24, 0xa1, 0xa0, 0xc4, 0xe2, 0x07, 0x3c, 0x10, 0xc4, 0xb5, 0x25, 0x4b, 0x65, 0x63, 0xf4, -0x80, 0xe7, 0xcf, 0x61, 0xb1, 0x71, 0x82, 0x21, 0x87, 0x2c, 0xf5, 0x91, 0x00, 0x32, 0x0c, -0xec, 0xa9, 0xb5, 0x9a, 0x74, 0x85, 0xe3, 0x36, 0x8f, 0x76, 0x4f, 0x9c, 0x6d, 0xce, 0xbc, -0xad, 0x0a, 0x4b, 0xed, 0x76, 0x04, 0xcb, 0xc3, 0xb9, 0x33, 0x9e, 0x01, 0x93, 0x96, 0x69, -0x7d, 0xc5, 0xa2, 0x45, 0x79, 0x9b, 0x04, 0x5c, 0x84, 0x09, 0xed, 0x88, 0x43, 0xc7, 0xab, -0x93, 0x14, 0x26, 0xa1, 0x40, 0xb5, 0xce, 0x4e, 0xbf, 0x2a, 0x42, 0x85, 0x3e, 0x2c, 0x3b, -0x54, 0xe8, 0x12, 0x1f, 0x0e, 0x97, 0x59, 0xb2, 0x27, 0x89, 0xfa, 0xf2, 0xdf, 0x8e, 0x68, -0x59, 0xdc, 0x06, 0xbc, 0xb6, 0x85, 0x0d, 0x06, 0x22, 0xec, 0xb1, 0xcb, 0xe5, 0x04, 0xe6, -0x3d, 0xb3, 0xb0, 0x41, 0x73, 0x08, 0x3f, 0x3c, 0x58, 0x86, 0x63, 0xeb, 0x50, 0xee, 0x1d, -0x2c, 0x37, 0x74, 0xa9, 0xd3, 0x18, 0xa3, 0x47, 0x6e, 0x93, 0x54, 0xad, 0x0a, 0x5d, 0xb8, -0x2a, 0x55, 0x5d, 0x78, 0xf6, 0xee, 0xbe, 0x8e, 0x3c, 0x76, 0x69, 0xb9, 0x40, 0xc2, 0x34, -0xec, 0x2a, 0xb9, 0xed, 0x7e, 0x20, 0xe4, 0x8d, 0x00, 0x38, 0xc7, 0xe6, 0x8f, 0x44, 0xa8, -0x86, 0xce, 0xeb, 0x2a, 0xe9, 0x90, 0xf1, 0x4c, 0xdf, 0x32, 0xfb, 0x73, 0x1b, 0x6d, 0x92, -0x1e, 0x95, 0xfe, 0xb4, 0xdb, 0x65, 0xdf, 0x4d, 0x23, 0x54, 0x89, 0x48, 0xbf, 0x4a, 0x2e, -0x70, 0xd6, 0xd7, 0x62, 0xb4, 0x33, 0x29, 0xb1, 0x3a, 0x33, 0x4c, 0x23, 0x6d, 0xa6, 0x76, -0xa5, 0x21, 0x63, 0x48, 0xe6, 0x90, 0x5d, 0xed, 0x90, 0x95, 0x0b, 0x7a, 0x84, 0xbe, 0xb8, -0x0d, 0x5e, 0x63, 0x0c, 0x62, 0x26, 0x4c, 0x14, 0x5a, 0xb3, 0xac, 0x23, 0xa4, 0x74, 0xa7, -0x6f, 0x33, 0x30, 0x05, 0x60, 0x01, 0x42, 0xa0, 0x28, 0xb7, 0xee, 0x19, 0x38, 0xf1, 0x64, -0x80, 0x82, 0x43, 0xe1, 0x41, 0x27, 0x1f, 0x1f, 0x90, 0x54, 0x7a, 0xd5, 0x23, 0x2e, 0xd1, -0x3d, 0xcb, 0x28, 0xba, 0x58, 0x7f, 0xdc, 0x7c, 0x91, 0x24, 0xe9, 0x28, 0x51, 0x83, 0x6e, -0xc5, 0x56, 0x21, 0x42, 0xed, 0xa0, 0x56, 0x22, 0xa1, 0x40, 0x80, 0x6b, 0xa8, 0xf7, 0x94, -0xca, 0x13, 0x6b, 0x0c, 0x39, 0xd9, 0xfd, 0xe9, 0xf3, 0x6f, 0xa6, 0x9e, 0xfc, 0x70, 0x8a, -0xb3, 0xbc, 0x59, 0x3c, 0x1e, 0x1d, 0x6c, 0xf9, 0x7c, 0xaf, 0xf9, 0x88, 0x71, 0x95, 0xeb, -0x57, 0x00, 0xbd, 0x9f, 0x8c, 0x4f, 0xe1, 0x24, 0x83, 0xc5, 0x22, 0xea, 0xfd, 0xd3, 0x0c, -0xe2, 0x17, 0x18, 0x7c, 0x6a, 0x4c, 0xde, 0x77, 0xb4, 0x53, 0x9b, 0x4c, 0x81, 0xcd, 0x23, -0x60, 0xaa, 0x0e, 0x25, 0x73, 0x9c, 0x02, 0x79, 0x32, 0x30, 0xdf, 0x74, 0xdf, 0x75, 0x19, -0xf4, 0xa5, 0x14, 0x5c, 0xf7, 0x7a, 0xa8, 0xa5, 0x91, 0x84, 0x7c, 0x60, 0x03, 0x06, 0x3b, -0xcd, 0x50, 0xb6, 0x27, 0x9c, 0xfe, 0xb1, 0xdd, 0xcc, 0xd3, 0xb0, 0x59, 0x24, 0xb2, 0xca, -0xe2, 0x1c, 0x81, 0x22, 0x9d, 0x07, 0x8f, 0x8e, 0xb9, 0xbe, 0x4e, 0xfa, 0xfc, 0x39, 0x65, -0xba, 0xbf, 0x9d, 0x12, 0x37, 0x5e, 0x97, 0x7e, 0xf3, 0x89, 0xf5, 0x5d, 0xf5, 0xe3, 0x09, -0x8c, 0x62, 0xb5, 0x20, 0x9d, 0x0c, 0x53, 0x8a, 0x68, 0x1b, 0xd2, 0x8f, 0x75, 0x17, 0x5d, -0xd4, 0xe5, 0xda, 0x75, 0x62, 0x19, 0x14, 0x6a, 0x26, 0x2d, 0xeb, 0xf8, 0xaf, 0x37, 0xf0, -0x6c, 0xa4, 0x55, 0xb1, 0xbc, 0xe2, 0x33, 0xc0, 0x9a, 0xca, 0xb0, 0x11, 0x49, 0x4f, 0x68, -0x9b, 0x3b, 0x6b, 0x3c, 0xcc, 0x13, 0xf6, 0xc7, 0x85, 0x61, 0x68, 0x42, 0xae, 0xbb, 0xdd, -0xcd, 0x45, 0x16, 0x29, 0x1d, 0xea, 0xdb, 0xc8, 0x03, 0x94, 0x3c, 0xee, 0x4f, 0x82, 0x11, -0xc3, 0xec, 0x28, 0xbd, 0x97, 0x05, 0x99, 0xde, 0xd7, 0xbb, 0x5e, 0x22, 0x1f, 0xd4, 0xeb, -0x64, 0xd9, 0x92, 0xd9, 0x85, 0xb7, 0x6a, 0x05, 0x6a, 0xe4, 0x24, 0x41, 0xf1, 0xcd, 0xf0, -0xd8, 0x3f, 0xf8, 0x9e, 0x0e, 0xcd, 0x0b, 0x7a, 0x70, 0x6b, 0x5a, 0x75, 0x0a, 0x6a, 0x33, -0x88, 0xec, 0x17, 0x75, 0x08, 0x70, 0x10, 0x2f, 0x24, 0xcf, 0xc4, 0xe9, 0x42, 0x00, 0x61, -0x94, 0xca, 0x1f, 0x3a, 0x76, 0x06, 0xfa, 0xd2, 0x48, 0x81, 0xf0, 0x77, 0x60, 0x03, 0x45, -0xd9, 0x61, 0xf4, 0xa4, 0x6f, 0x3d, 0xd9, 0x30, 0xc3, 0x04, 0x6b, 0x54, 0x2a, 0xb7, 0xec, -0x3b, 0xf4, 0x4b, 0xf5, 0x68, 0x52, 0x26, 0xce, 0xff, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xa5, -0xa9, 0xb1, 0xe0, 0x23, 0xc4, 0x0a, 0x77, 0x4d, 0xf9, 0x51, 0x20, 0xa3, 0xa5, 0xa9, 0xb1, -0xc1, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xc4, 0xeb, 0x54, 0x0b, -0x75, 0x68, 0x52, 0x07, 0x8c, 0x9a, 0x97, 0x8d, 0x79, 0x70, 0x62, 0x46, 0xef, 0x5c, 0x1b, -0x95, 0x89, 0x71, 0x41, 0xe1, 0x21, 0xa1, 0xa1, 0xa1, 0xc0, 0x02, 0x67, 0x4c, 0x1a, 0xb6, -0xcf, 0xfd, 0x78, 0x53, 0x24, 0xab, 0xb5, 0xc9, 0xf1, 0x60, 0x23, 0xa5, 0xc8, 0x12, 0x87, -0x6d, 0x58, 0x13, 0x85, 0x88, 0x92, 0x87, 0x6d, 0x58, 0x32, 0xc7, 0x0c, 0x9a, 0x97, 0xac, -0xda, 0x36, 0xee, 0x5e, 0x3e, 0xdf, 0x1d, 0xb8, 0xf2, 0x66, 0x2f, 0xbd, 0xf8, 0x72, 0x47, -0xed, 0x58, 0x13, 0x85, 0x88, 0x92, 0x87, 0x8c, 0x7b, 0x55, 0x09, 0x90, 0xa2, 0xc6, 0xef, -0x3d, 0xf8, 0x53, 0x24, 0xab, 0xd4, 0x2a, 0xb7, 0xec, 0x5a, 0x36, 0xee, 0x5e, 0x3e, 0xdf, -0x3c, 0xfa, 0x76, 0x4f, 0xfd, 0x59, 0x30, 0xe2, 0x46, 0xef, 0x3d, 0xf8, 0x53, 0x05, 0x69, -0x31, 0xc1, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, 0x19, 0xb0, 0xe2, 0x27, 0xcc, 0xfb, 0x74, -0x4b, 0x14, 0x8b, 0x94, 0x8b, 0x75, 0x68, 0x33, 0xc5, 0x08, 0x92, 0x87, 0x8c, 0x9a, 0xb6, -0xcf, 0x1c, 0xba, 0xd7, 0x0d, 0x98, 0xb2, 0xe6, 0x2f, 0xdc, 0x1b, 0x95, 0x89, 0x71, 0x60, -0x23, 0xc4, 0x0a, 0x96, 0x8f, 0x9c, 0xba, 0xf6, 0x6e, 0x3f, 0xfc, 0x5b, 0x15, 0xa8, 0xd2, -0x26, 0xaf, 0xbd, 0xf8, 0x72, 0x66, 0x2f, 0xdc, 0x1b, 0xb4, 0xcb, 0x14, 0x8b, 0x94, 0xaa, -0xb7, 0xcd, 0xf9, 0x51, 0x01, 0x80, 0x82, 0x86, 0x6f, 0x3d, 0xd9, 0x30, 0xe2, 0x27, 0xcc, -0xfb, 0x74, 0x4b, 0x14, 0xaa, 0xb7, 0xcd, 0xf9, 0x70, 0x43, 0x04, 0x6b, 0x35, 0xc9, 0xf1, -0x60, 0x23, 0xa5, 0xc8, 0xf3, 0x45, 0x08, 0x92, 0x87, 0x6d, 0x58, 0x32, 0xe6, 0x2f, 0xbd, -0xf8, 0x72, 0x66, 0x4e, 0x1e, 0xbe, 0xfe, 0x7e, 0x7e, 0x7e, 0x5f, 0x1d, 0x99, 0x91, 0xa0, -0xa3, 0xc4, 0x0a, 0x77, 0x4d, 0x18, 0x93, 0xa4, 0xab, 0xd4, 0x0b, 0x75, 0x49, 0x10, 0xa2, -0xc6, 0xef, 0x3d, 0xf8, 0x53, 0x24, 0xab, 0xb5, 0xe8, 0x33, 0xe4, 0x4a, 0x16, 0xae, 0xde, -0x1f, 0xbc, 0xdb, 0x15, 0xa8, 0xb3, 0xc5, 0x08, 0x73, 0x45, 0xe9, 0x31, 0xc1, 0xe1, 0x21, -0xa1, 0xa1, 0xa1, 0xc0, 0x02, 0x86, 0x6f, 0x5c, 0x3a, 0xd7, 0x0d, 0x98, 0x93, 0xa4, 0xca, -0x16, 0xae, 0xde, 0x1f, 0x9d, 0x99, 0xb0, 0xe2, 0x46, 0xef, 0x3d, 0xf8, 0x72, 0x47, 0x0c, -0x9a, 0xb6, 0xcf, 0xfd, 0x59, 0x11, 0xa0, 0xa3, 0xa5, 0xc8, 0xf3, 0x45, 0x08, 0x92, 0x87, -0x6d, 0x39, 0xf0, 0x43, 0x04, 0x8a, 0x96, 0xae, 0xde, 0x3e, 0xdf, 0x1d, 0x99, 0x91, 0xa0, -0xc2, 0x06, 0x6f, 0x3d, 0xf8, 0x72, 0x47, 0x0c, 0x9a, 0x97, 0x8d, 0x98, 0x93, 0x85, 0x88, -0x73, 0x45, 0xe9, 0x31, 0xe0, 0x23, 0xa5, 0xa9, 0xd0, 0x03, 0x84, 0x8a, 0x96, 0xae, 0xde, -0x1f, 0xbc, 0xdb, 0x15, 0xa8, 0xd2, 0x26, 0xce, 0xff, 0x5d, 0x19, 0x91, 0x81, 0x80, 0x82, -0x67, 0x2d, 0xd8, 0x13, 0xa4, 0xab, 0xd4, 0x0b, 0x94, 0xaa, 0xb7, 0xcd, 0xf9, 0x51, 0x20, -0xa3, 0xa5, 0xc8, 0xf3, 0x45, 0xe9, 0x50, 0x22, 0xc6, 0xef, 0x5c, 0x3a, 0xd7, 0x0d, 0x98, -0x93, 0x85, 0x88, 0x73, 0x64, 0x4a, 0xf7, 0x4d, 0xf9, 0x51, 0x20, 0xa3, 0xc4, 0x0a, 0x96, -0xae, 0xde, 0x3e, 0xfe, 0x7e, 0x7e, 0x7e, 0x5f, 0x3c, 0xfa, 0x76, 0x4f, 0xfd, 0x78, 0x72, -0x66, 0x2f, 0xbd, 0xd9, 0x30, 0xc3, 0xe5, 0x48, 0x12, 0x87, 0x8c, 0x7b, 0x55, 0x28, 0xd2, -0x07, 0x8c, 0x9a, 0x97, 0xac, 0xda, 0x17, 0x8d, 0x79, 0x51, 0x20, 0xa3, 0xc4, 0xeb, 0x54, -0x0b, 0x94, 0x8b, 0x94, 0xaa, 0xd6, 0x2e, 0xbf, 0xfc, 0x5b, 0x15, 0xa8, 0xd2, 0x26, 0xaf, -0xdc, 0x1b, 0xb4, 0xea, 0x37, 0xec, 0x3b, 0xf4, 0x6a, 0x37, 0xcd, 0x18, 0x93, 0x85, 0x69, -0x31, 0xc1, 0xe1, 0x40, 0xe3, 0x25, 0xc8, 0x12, 0x87, 0x8c, 0x9a, 0xb6, 0xcf, 0xfd, 0x59, -0x11, 0xa0, 0xc2, 0x06, 0x8e, 0x7f, 0x5d, 0x38, 0xf2, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x37, -0xec, 0x5a, 0x36, 0xee, 0x3f, 0xfc, 0x7a, 0x76, 0x4f, 0x1c, 0x9b, 0x95, 0x89, 0x71, 0x41, -0x00, 0x63, 0x44, 0xeb, 0x54, 0x2a, 0xd6, 0x0f, 0x9c, 0xba, 0xd7, 0x0d, 0x98, 0x93, 0x85, -0x69, 0x31, 0xc1, 0x00, 0x82, 0x86, 0x8e, 0x9e, 0xbe, 0xdf, 0x3c, 0xfa, 0x57, 0x2c, 0xda, -0x36, 0xee, 0x3f, 0xfc, 0x5b, 0x15, 0x89, 0x71, 0x41, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, -0x38, 0xf2, 0x47, 0xed, 0x58, 0x13, 0xa4, 0xca, 0xf7, 0x4d, 0xf9, 0x51, 0x01, 0x80, 0x63, -0x44, 0xeb, 0x54, 0x2a, 0xd6, 0x2e, 0xbf, 0xdd, 0x19, 0x91, 0xa0, 0xa3, 0xa5, 0xa9, 0xb1, -0xe0, 0x42, 0x06, 0x8e, 0x7f, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xc4, 0x0a, 0x96, 0x8f, 0x7d, -0x78, 0x72, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x56, 0x2e, 0xde, 0x1f, 0xbc, 0xfa, 0x57, 0x0d, -0x79, 0x51, 0x01, 0x61, 0x21, 0xa1, 0xc0, 0xe3, 0x25, 0xa9, 0xb1, 0xc1, 0xe1, 0x40, 0x02, -0x67, 0x4c, 0x1a, 0x97, 0x8d, 0x98, 0x93, 0xa4, 0xab, 0xd4, 0x2a, 0xd6, 0x0f, 0x9c, 0x9b, -0xb4, 0xcb, 0x14, 0xaa, 0xb7, 0xcd, 0xf9, 0x51, 0x20, 0xa3, 0xc4, 0xeb, 0x35, 0xc9, 0xf1, -0x60, 0x42, 0x06, 0x8e, 0x7f, 0x7c, 0x7a, 0x76, 0x6e, 0x3f, 0xfc, 0x7a, 0x76, 0x6e, 0x5e, -0x3e, 0xfe, 0x7e, 0x5f, 0x3c, 0xdb, 0x15, 0x89, 0x71, 0x41, 0xe1, 0x21, 0xc0, 0xe3, 0x44, -0xeb, 0x54, 0x2a, 0xb7, 0xcd, 0xf9, 0x70, 0x62, 0x27, 0xad, 0xd8, 0x32, 0xc7, 0x0c, 0x7b, -0x74, 0x4b, 0x14, 0xaa, 0xb7, 0xec, 0x3b, 0xd5, 0x28, 0xd2, 0x07, 0x6d, 0x39, 0xd1, 0x20, -0xc2, 0xe7, 0x4c, 0x1a, 0x97, 0x8d, 0x98, 0xb2, 0xc7, 0x0c, 0x59, 0x28, 0xf3, 0x9b }; diff --git a/keyboards/oddball/rules.mk b/keyboards/oddball/rules.mk index f1e7d489409..d675ebcf2be 100644 --- a/keyboards/oddball/rules.mk +++ b/keyboards/oddball/rules.mk @@ -21,7 +21,3 @@ SPLIT_KEYBOARD = yes POINTING_DEVICE_ENABLE = yes DEFAULT_FOLDER = oddball/v1 - -SRC += spi_master.c -SRC += drivers/sensors/adns9800.c -SRC += pmw/pmw.c diff --git a/keyboards/ploopyco/mouse/mouse.c b/keyboards/ploopyco/mouse/mouse.c index 0bf96a20f77..1b00ef3b71e 100644 --- a/keyboards/ploopyco/mouse/mouse.c +++ b/keyboards/ploopyco/mouse/mouse.c @@ -31,7 +31,8 @@ # define OPT_SCALE 1 // Multiplier for wheel #endif #ifndef PLOOPY_DPI_OPTIONS -# define PLOOPY_DPI_OPTIONS { 1200, 1600, 2400 } +# define PLOOPY_DPI_OPTIONS \ + { 1200, 1600, 2400 } # ifndef PLOOPY_DPI_DEFAULT # define PLOOPY_DPI_DEFAULT 1 # endif @@ -40,10 +41,10 @@ # define PLOOPY_DPI_DEFAULT 0 #endif #ifndef PLOOPY_DRAGSCROLL_DPI -# define PLOOPY_DRAGSCROLL_DPI 100 // Fixed-DPI Drag Scroll +# define PLOOPY_DRAGSCROLL_DPI 100 // Fixed-DPI Drag Scroll #endif #ifndef PLOOPY_DRAGSCROLL_MULTIPLIER -# define PLOOPY_DRAGSCROLL_MULTIPLIER 0.75 // Variable-DPI Drag Scroll +# define PLOOPY_DRAGSCROLL_MULTIPLIER 0.75 // Variable-DPI Drag Scroll #endif keyboard_config_t keyboard_config; @@ -65,13 +66,7 @@ uint8_t OptLowPin = OPT_ENC1; bool debug_encoder = false; bool is_drag_scroll = false; -__attribute__((weak)) void process_wheel_user(report_mouse_t* mouse_report, int16_t h, int16_t v) { - mouse_report->h = h; - mouse_report->v = v; -} - -__attribute__((weak)) void process_wheel(report_mouse_t* mouse_report) { - // TODO: Replace this with interrupt driven code, polling is S L O W +void process_wheel(report_mouse_t* mouse_report) { // Lovingly ripped from the Ploopy Source // If the mouse wheel was just released, do not scroll. @@ -99,56 +94,25 @@ __attribute__((weak)) void process_wheel(report_mouse_t* mouse_report) { int dir = opt_encoder_handler(p1, p2); if (dir == 0) return; - process_wheel_user(mouse_report, mouse_report->h, (int)(mouse_report->v + (dir * OPT_SCALE))); + mouse_report->v = (int8_t)(dir * OPT_SCALE); } -__attribute__((weak)) void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y) { - mouse_report->x = x; - mouse_report->y = y; -} +__attribute__((weak)) report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { + process_wheel(&mouse_report); -__attribute__((weak)) void process_mouse(report_mouse_t* mouse_report) { - report_pmw_t data = pmw_read_burst(); - if (data.isOnSurface && data.isMotion) { - // Reset timer if stopped moving - if (!data.isMotion) { - if (MotionStart != 0) MotionStart = 0; - return; - } - - // Set timer if new motion - if ((MotionStart == 0) && data.isMotion) { - if (debug_mouse) dprintf("Starting motion.\n"); - MotionStart = timer_read(); - } - - if (debug_mouse) { - dprintf("Delt] d: %d t: %u\n", abs(data.dx) + abs(data.dy), MotionStart); - } - if (debug_mouse) { - dprintf("Pre ] X: %d, Y: %d\n", data.dx, data.dy); - } -#if defined(PROFILE_LINEAR) - float scale = float(timer_elaspsed(MotionStart)) / 1000.0; - data.dx *= scale; - data.dy *= scale; -#elif defined(PROFILE_INVERSE) - // TODO + if (is_drag_scroll) { + mouse_report.h = mouse_report.x; +#ifdef PLOOPY_DRAGSCROLL_INVERT + // Invert vertical scroll direction + mouse_report.v = -mouse_report.y; #else - // no post processing + mouse_report.v = mouse_report.y; #endif - // apply multiplier - // data.dx *= mouse_multiplier; - // data.dy *= mouse_multiplier; - - // Wrap to HID size - data.dx = constrain(data.dx, -127, 127); - data.dy = constrain(data.dy, -127, 127); - if (debug_mouse) dprintf("Cons] X: %d, Y: %d\n", data.dx, data.dy); - // dprintf("Elapsed:%u, X: %f Y: %\n", i, pgm_read_byte(firmware_data+i)); - - process_mouse_user(mouse_report, data.dx, data.dy); + mouse_report.x = 0; + mouse_report.y = 0; } + + return pointing_device_task_user(mouse_report); } bool process_record_kb(uint16_t keycode, keyrecord_t* record) { @@ -169,7 +133,7 @@ bool process_record_kb(uint16_t keycode, keyrecord_t* record) { if (keycode == DPI_CONFIG && record->event.pressed) { keyboard_config.dpi_config = (keyboard_config.dpi_config + 1) % DPI_OPTION_SIZE; eeconfig_update_kb(keyboard_config.raw); - pmw_set_cpi(dpi_array[keyboard_config.dpi_config]); + pointing_device_set_cpi(dpi_array[keyboard_config.dpi_config]); } if (keycode == DRAG_SCROLL) { @@ -180,9 +144,9 @@ bool process_record_kb(uint16_t keycode, keyrecord_t* record) { is_drag_scroll ^= 1; } #ifdef PLOOPY_DRAGSCROLL_FIXED - pmw_set_cpi(is_drag_scroll ? PLOOPY_DRAGSCROLL_DPI : dpi_array[keyboard_config.dpi_config]); + pointing_device_set_cpi(is_drag_scroll ? PLOOPY_DRAGSCROLL_DPI : dpi_array[keyboard_config.dpi_config]); #else - pmw_set_cpi(is_drag_scroll ? (dpi_array[keyboard_config.dpi_config] * PLOOPY_DRAGSCROLL_MULTIPLIER) : dpi_array[keyboard_config.dpi_config]); + pointing_device_set_cpi(is_drag_scroll ? (dpi_array[keyboard_config.dpi_config] * PLOOPY_DRAGSCROLL_MULTIPLIER) : dpi_array[keyboard_config.dpi_config]); #endif } @@ -194,11 +158,7 @@ bool process_record_kb(uint16_t keycode, keyrecord_t* record) { #ifndef MOUSEKEY_ENABLE if (IS_MOUSEKEY_BUTTON(keycode)) { report_mouse_t currentReport = pointing_device_get_report(); - if (record->event.pressed) { - currentReport.buttons |= 1 << (keycode - KC_MS_BTN1); - } else { - currentReport.buttons &= ~(1 << (keycode - KC_MS_BTN1)); - } + currentReport.buttons = pointing_device_handle_buttons(currentReport.buttons, record->event.pressed, keycode - KC_MS_BTN1); pointing_device_set_report(currentReport); pointing_device_send(); } @@ -240,35 +200,12 @@ void keyboard_pre_init_kb(void) { keyboard_pre_init_user(); } -void pointing_device_init(void) { - // initialize ball sensor - pmw_spi_init(); +void pointing_device_init_kb(void) { + pointing_device_set_cpi(dpi_array[keyboard_config.dpi_config]); // initialize the scroll wheel's optical encoder opt_encoder_init(); } - -void pointing_device_task(void) { - report_mouse_t mouse_report = pointing_device_get_report(); - process_wheel(&mouse_report); - process_mouse(&mouse_report); - - if (is_drag_scroll) { - mouse_report.h = mouse_report.x; -#ifdef PLOOPY_DRAGSCROLL_INVERT - // Invert vertical scroll direction - mouse_report.v = -mouse_report.y; -#else - mouse_report.v = mouse_report.y; -#endif - mouse_report.x = 0; - mouse_report.y = 0; - } - - pointing_device_set_report(mouse_report); - pointing_device_send(); -} - void eeconfig_init_kb(void) { keyboard_config.dpi_config = PLOOPY_DPI_DEFAULT; eeconfig_update_kb(keyboard_config.raw); @@ -284,9 +221,3 @@ void matrix_init_kb(void) { } matrix_init_user(); } - -void keyboard_post_init_kb(void) { - pmw_set_cpi(dpi_array[keyboard_config.dpi_config]); - - keyboard_post_init_user(); -} diff --git a/keyboards/ploopyco/mouse/mouse.h b/keyboards/ploopyco/mouse/mouse.h index d11aa5e9a46..ee598271395 100644 --- a/keyboards/ploopyco/mouse/mouse.h +++ b/keyboards/ploopyco/mouse/mouse.h @@ -19,11 +19,8 @@ #pragma once #include "quantum.h" -#include "spi_master.h" -#include "drivers/sensors/pmw3360.h" #include "analog.h" #include "opt_encoder.h" -#include "pointing_device.h" // Sensor defs #define OPT_ENC1 F0 @@ -31,10 +28,7 @@ #define OPT_ENC1_MUX 0 #define OPT_ENC2_MUX 4 -void process_mouse(report_mouse_t* mouse_report); -void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y); void process_wheel(report_mouse_t* mouse_report); -void process_wheel_user(report_mouse_t* mouse_report, int16_t h, int16_t v); #define LAYOUT(BLL, BL, BM, BR, BRR, BF, BB, BDPI) \ { {BL, BM, BR, BF, BB, BRR, BLL, BDPI}, } diff --git a/keyboards/ploopyco/mouse/rules.mk b/keyboards/ploopyco/mouse/rules.mk index c02afa00c3d..45cb38901a0 100644 --- a/keyboards/ploopyco/mouse/rules.mk +++ b/keyboards/ploopyco/mouse/rules.mk @@ -22,7 +22,8 @@ BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow AUDIO_ENABLE = no # Audio output POINTING_DEVICE_ENABLE = yes +POINTING_DEVICE_DRIVER = pmw3360 MOUSEKEY_ENABLE = yes # Mouse keys -QUANTUM_LIB_SRC += analog.c spi_master.c -SRC += drivers/sensors/pmw3360.c opt_encoder.c +QUANTUM_LIB_SRC += analog.c +SRC += opt_encoder.c diff --git a/keyboards/ploopyco/trackball/rules.mk b/keyboards/ploopyco/trackball/rules.mk index af9bc7d4ce0..d118581523c 100644 --- a/keyboards/ploopyco/trackball/rules.mk +++ b/keyboards/ploopyco/trackball/rules.mk @@ -19,9 +19,10 @@ BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow AUDIO_ENABLE = no # Audio output POINTING_DEVICE_ENABLE = yes +POINTING_DEVICE_DRIVER = pmw3360 MOUSEKEY_ENABLE = yes # Mouse keys -QUANTUM_LIB_SRC += analog.c spi_master.c -SRC += drivers/sensors/pmw3360.c opt_encoder.c +QUANTUM_LIB_SRC += analog.c +SRC += opt_encoder.c DEFAULT_FOLDER = ploopyco/trackball/rev1_005 diff --git a/keyboards/ploopyco/trackball/trackball.c b/keyboards/ploopyco/trackball/trackball.c index 71902099796..e2a6e4ed5c4 100644 --- a/keyboards/ploopyco/trackball/trackball.c +++ b/keyboards/ploopyco/trackball/trackball.c @@ -65,12 +65,7 @@ uint8_t OptLowPin = OPT_ENC1; bool debug_encoder = false; bool is_drag_scroll = false; -__attribute__((weak)) void process_wheel_user(report_mouse_t* mouse_report, int16_t h, int16_t v) { - mouse_report->h = h; - mouse_report->v = v; -} - -__attribute__((weak)) void process_wheel(report_mouse_t* mouse_report) { +void process_wheel(report_mouse_t* mouse_report) { // TODO: Replace this with interrupt driven code, polling is S L O W // Lovingly ripped from the Ploopy Source @@ -99,56 +94,25 @@ __attribute__((weak)) void process_wheel(report_mouse_t* mouse_report) { int dir = opt_encoder_handler(p1, p2); if (dir == 0) return; - process_wheel_user(mouse_report, mouse_report->h, (int)(mouse_report->v + (dir * OPT_SCALE))); + mouse_report->v = (int8_t)(dir * OPT_SCALE); } -__attribute__((weak)) void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y) { - mouse_report->x = x; - mouse_report->y = y; -} +report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { + process_wheel(&mouse_report); -__attribute__((weak)) void process_mouse(report_mouse_t* mouse_report) { - report_pmw_t data = pmw_read_burst(); - if (data.isOnSurface && data.isMotion) { - // Reset timer if stopped moving - if (!data.isMotion) { - if (MotionStart != 0) MotionStart = 0; - return; - } - - // Set timer if new motion - if ((MotionStart == 0) && data.isMotion) { - if (debug_mouse) dprintf("Starting motion.\n"); - MotionStart = timer_read(); - } - - if (debug_mouse) { - dprintf("Delt] d: %d t: %u\n", abs(data.dx) + abs(data.dy), MotionStart); - } - if (debug_mouse) { - dprintf("Pre ] X: %d, Y: %d\n", data.dx, data.dy); - } -#if defined(PROFILE_LINEAR) - float scale = float(timer_elaspsed(MotionStart)) / 1000.0; - data.dx *= scale; - data.dy *= scale; -#elif defined(PROFILE_INVERSE) - // TODO + if (is_drag_scroll) { + mouse_report.h = mouse_report.x; +#ifdef PLOOPY_DRAGSCROLL_INVERT + // Invert vertical scroll direction + mouse_report.v = -mouse_report.y; #else - // no post processing + mouse_report.v = mouse_report.y; #endif - // apply multiplier - // data.dx *= mouse_multiplier; - // data.dy *= mouse_multiplier; - - // Wrap to HID size - data.dx = constrain(data.dx, -127, 127); - data.dy = constrain(data.dy, -127, 127); - if (debug_mouse) dprintf("Cons] X: %d, Y: %d\n", data.dx, data.dy); - // dprintf("Elapsed:%u, X: %f Y: %\n", i, pgm_read_byte(firmware_data+i)); - - process_mouse_user(mouse_report, data.dx, -data.dy); + mouse_report.x = 0; + mouse_report.y = 0; } + + return pointing_device_task_user(mouse_report); } bool process_record_kb(uint16_t keycode, keyrecord_t* record) { @@ -169,7 +133,7 @@ bool process_record_kb(uint16_t keycode, keyrecord_t* record) { if (keycode == DPI_CONFIG && record->event.pressed) { keyboard_config.dpi_config = (keyboard_config.dpi_config + 1) % DPI_OPTION_SIZE; eeconfig_update_kb(keyboard_config.raw); - pmw_set_cpi(dpi_array[keyboard_config.dpi_config]); + pointing_device_set_cpi(dpi_array[keyboard_config.dpi_config]); } if (keycode == DRAG_SCROLL) { @@ -180,9 +144,9 @@ bool process_record_kb(uint16_t keycode, keyrecord_t* record) { is_drag_scroll ^= 1; } #ifdef PLOOPY_DRAGSCROLL_FIXED - pmw_set_cpi(is_drag_scroll ? PLOOPY_DRAGSCROLL_DPI : dpi_array[keyboard_config.dpi_config]); + pointing_device_set_cpi(is_drag_scroll ? PLOOPY_DRAGSCROLL_DPI : dpi_array[keyboard_config.dpi_config]); #else - pmw_set_cpi(is_drag_scroll ? (dpi_array[keyboard_config.dpi_config] * PLOOPY_DRAGSCROLL_MULTIPLIER) : dpi_array[keyboard_config.dpi_config]); + pointing_device_set_cpi(is_drag_scroll ? (dpi_array[keyboard_config.dpi_config] * PLOOPY_DRAGSCROLL_MULTIPLIER) : dpi_array[keyboard_config.dpi_config]); #endif } @@ -194,11 +158,7 @@ bool process_record_kb(uint16_t keycode, keyrecord_t* record) { #ifndef MOUSEKEY_ENABLE if (IS_MOUSEKEY_BUTTON(keycode)) { report_mouse_t currentReport = pointing_device_get_report(); - if (record->event.pressed) { - currentReport.buttons |= 1 << (keycode - KC_MS_BTN1); - } else { - currentReport.buttons &= ~(1 << (keycode - KC_MS_BTN1)); - } + currentReport.buttons = pointing_device_handle_buttons(record->event.pressed, keycode - KC_MS_BTN1); pointing_device_set_report(currentReport); pointing_device_send(); } @@ -239,35 +199,12 @@ void keyboard_pre_init_kb(void) { keyboard_pre_init_user(); } -void pointing_device_init(void) { - // initialize ball sensor - pmw_spi_init(); +void pointing_device_init_kb(void) { + pmw3360_set_cpi(dpi_array[keyboard_config.dpi_config]); // initialize the scroll wheel's optical encoder opt_encoder_init(); } - -void pointing_device_task(void) { - report_mouse_t mouse_report = pointing_device_get_report(); - process_wheel(&mouse_report); - process_mouse(&mouse_report); - - if (is_drag_scroll) { - mouse_report.h = mouse_report.x; -#ifdef PLOOPY_DRAGSCROLL_INVERT - // Invert vertical scroll direction - mouse_report.v = -mouse_report.y; -#else - mouse_report.v = mouse_report.y; -#endif - mouse_report.x = 0; - mouse_report.y = 0; - } - - pointing_device_set_report(mouse_report); - pointing_device_send(); -} - void eeconfig_init_kb(void) { keyboard_config.dpi_config = PLOOPY_DPI_DEFAULT; eeconfig_update_kb(keyboard_config.raw); @@ -285,7 +222,7 @@ void matrix_init_kb(void) { } void keyboard_post_init_kb(void) { - pmw_set_cpi(dpi_array[keyboard_config.dpi_config]); + pointing_device_set_cpi(dpi_array[keyboard_config.dpi_config]); keyboard_post_init_user(); } diff --git a/keyboards/ploopyco/trackball/trackball.h b/keyboards/ploopyco/trackball/trackball.h index 70f5d83b11f..52d955325b2 100644 --- a/keyboards/ploopyco/trackball/trackball.h +++ b/keyboards/ploopyco/trackball/trackball.h @@ -19,11 +19,8 @@ #pragma once #include "quantum.h" -#include "spi_master.h" -#include "drivers/sensors/pmw3360.h" #include "analog.h" #include "opt_encoder.h" -#include "pointing_device.h" #if defined(KEYBOARD_ploopyco_trackball_rev1) # include "rev1.h" #elif defined(KEYBOARD_ploopyco_trackball_rev1_005) @@ -36,10 +33,7 @@ #define OPT_ENC1_MUX 0 #define OPT_ENC2_MUX 4 -void process_mouse(report_mouse_t* mouse_report); -void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y); void process_wheel(report_mouse_t* mouse_report); -void process_wheel_user(report_mouse_t* mouse_report, int16_t h, int16_t v); #define LAYOUT(BL, BM, BR, BF, BB) \ { {BL, BM, BR, BF, BB}, } diff --git a/keyboards/ploopyco/trackball_mini/config.h b/keyboards/ploopyco/trackball_mini/config.h index f76a6eb2c1c..52d7390ca25 100644 --- a/keyboards/ploopyco/trackball_mini/config.h +++ b/keyboards/ploopyco/trackball_mini/config.h @@ -56,3 +56,9 @@ // If board has a debug LED, you can enable it by defining this // #define DEBUG_LED_PIN F7 + +#define ADNS5050_SCLK_PIN B7 +#define ADNS5050_SDIO_PIN C6 +#define ADNS5050_CS_PIN B4 + +#define POINTING_DEVICE_ROTATION_90 diff --git a/keyboards/ploopyco/trackball_mini/keymaps/drag_scroll/keymap.c b/keyboards/ploopyco/trackball_mini/keymaps/drag_scroll/keymap.c index 7784bc85538..7eb973216b9 100644 --- a/keyboards/ploopyco/trackball_mini/keymaps/drag_scroll/keymap.c +++ b/keyboards/ploopyco/trackball_mini/keymaps/drag_scroll/keymap.c @@ -44,7 +44,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { // to only scroll in one direction, if you wanted, as well. In fact, // there is no reason that you need to send this to the mouse report. // You could have it register a key, instead. -void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y) { +void process_mouse_user(report_mouse_t* mouse_report, int8_t x, int8_t y) { if (is_drag_scroll) { mouse_report->h = x; mouse_report->v = y; diff --git a/keyboards/ploopyco/trackball_mini/rules.mk b/keyboards/ploopyco/trackball_mini/rules.mk index 07a6889cbec..8d76c9b3dcb 100644 --- a/keyboards/ploopyco/trackball_mini/rules.mk +++ b/keyboards/ploopyco/trackball_mini/rules.mk @@ -1,9 +1,6 @@ # MCU name MCU = atmega32u4 -# Processor frequency -F_CPU = 16000000 - # Bootloader selection BOOTLOADER = atmel-dfu @@ -22,9 +19,10 @@ BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow AUDIO_ENABLE = no # Audio output POINTING_DEVICE_ENABLE = yes +POINTING_DEVICE_DRIVER = adns5050 MOUSEKEY_ENABLE = no # Mouse keys QUANTUM_LIB_SRC += analog.c -SRC += drivers/sensors/adns5050.c opt_encoder.c +SRC += opt_encoder.c DEFAULT_FOLDER = ploopyco/trackball_mini/rev1_001 diff --git a/keyboards/ploopyco/trackball_mini/trackball_mini.c b/keyboards/ploopyco/trackball_mini/trackball_mini.c index b50850b5491..2158a8f4889 100644 --- a/keyboards/ploopyco/trackball_mini/trackball_mini.c +++ b/keyboards/ploopyco/trackball_mini/trackball_mini.c @@ -37,20 +37,17 @@ # define OPT_SCALE 1 // Multiplier for wheel #endif -#define PLOOPY_DPI_OPTIONS { CPI375, CPI750, CPI1375 } +#define PLOOPY_DPI_OPTIONS \ + { 375, 750, 1375 } #define PLOOPY_DPI_DEFAULT 2 #ifndef PLOOPY_DRAGSCROLL_DPI -# define PLOOPY_DRAGSCROLL_DPI CPI375 // Fixed-DPI Drag Scroll +# define PLOOPY_DRAGSCROLL_DPI 375 // Fixed-DPI Drag Scroll #endif #ifndef PLOOPY_DRAGSCROLL_MULTIPLIER # define PLOOPY_DRAGSCROLL_MULTIPLIER 0.75 // Variable-DPI Drag Scroll #endif -// Transformation constants for delta-X and delta-Y -const static float ADNS_X_TRANSFORM = -1.0; -const static float ADNS_Y_TRANSFORM = 1.0; - keyboard_config_t keyboard_config; uint16_t dpi_array[] = PLOOPY_DPI_OPTIONS; #define DPI_OPTION_SIZE (sizeof(dpi_array) / sizeof(uint16_t)) @@ -70,12 +67,7 @@ uint8_t OptLowPin = OPT_ENC1; bool debug_encoder = false; bool is_drag_scroll = false; -__attribute__((weak)) void process_wheel_user(report_mouse_t* mouse_report, int16_t h, int16_t v) { - mouse_report->h = h; - mouse_report->v = v; -} - -__attribute__((weak)) void process_wheel(report_mouse_t* mouse_report) { +void process_wheel(report_mouse_t* mouse_report) { // If the mouse wheel was just released, do not scroll. if (timer_elapsed(lastMidClick) < SCROLL_BUTT_DEBOUNCE) return; @@ -103,33 +95,31 @@ __attribute__((weak)) void process_wheel(report_mouse_t* mouse_report) { if (dir == 0) return; - process_wheel_user(mouse_report, mouse_report->h, (int)(mouse_report->v + (dir * OPT_SCALE))); + mouse_report->v = (int8_t)(dir * OPT_SCALE); } -__attribute__((weak)) void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y) { - mouse_report->x = x; - mouse_report->y = y; +void pointing_device_init_kb(void) { + opt_encoder_init(); + + // set the DPI. + pointing_device_set_cpi(dpi_array[keyboard_config.dpi_config]); } -__attribute__((weak)) void process_mouse(report_mouse_t* mouse_report) { - report_adns_t data = adns_read_burst(); +report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { - if (data.dx != 0 || data.dy != 0) { - if (debug_mouse) - dprintf("Raw ] X: %d, Y: %d\n", data.dx, data.dy); - - // Apply delta-X and delta-Y transformations. - // x and y are swapped - // the sensor is rotated - // by 90 degrees - float xt = (float) data.dy * ADNS_X_TRANSFORM; - float yt = (float) data.dx * ADNS_Y_TRANSFORM; - - int16_t xti = (int16_t)xt; - int16_t yti = (int16_t)yt; - - process_mouse_user(mouse_report, xti, yti); + if (is_drag_scroll) { + mouse_report.h = mouse_report.x; +#ifdef PLOOPY_DRAGSCROLL_INVERT + // Invert vertical scroll direction + mouse_report.v = -mouse_report.y; +#else + mouse_report.v = mouse_report.y; +#endif + mouse_report.x = 0; + mouse_report.y = 0; } + + return pointing_device_task_user(mouse_report); } bool process_record_kb(uint16_t keycode, keyrecord_t* record) { @@ -147,7 +137,7 @@ bool process_record_kb(uint16_t keycode, keyrecord_t* record) { if (keycode == DPI_CONFIG && record->event.pressed) { keyboard_config.dpi_config = (keyboard_config.dpi_config + 1) % DPI_OPTION_SIZE; eeconfig_update_kb(keyboard_config.raw); - adns_set_cpi(dpi_array[keyboard_config.dpi_config]); + pointing_device_set_cpi(dpi_array[keyboard_config.dpi_config]); } if (keycode == DRAG_SCROLL) { @@ -157,7 +147,7 @@ bool process_record_kb(uint16_t keycode, keyrecord_t* record) { { is_drag_scroll ^= 1; } - adns_set_cpi(is_drag_scroll ? PLOOPY_DRAGSCROLL_DPI : dpi_array[keyboard_config.dpi_config]); + pointing_device_set_cpi(is_drag_scroll ? PLOOPY_DRAGSCROLL_DPI : dpi_array[keyboard_config.dpi_config]); } /* If Mousekeys is disabled, then use handle the mouse button @@ -168,11 +158,7 @@ bool process_record_kb(uint16_t keycode, keyrecord_t* record) { #ifndef MOUSEKEY_ENABLE if (IS_MOUSEKEY_BUTTON(keycode)) { report_mouse_t currentReport = pointing_device_get_report(); - if (record->event.pressed) { - currentReport.buttons |= 1 << (keycode - KC_MS_BTN1); - } else { - currentReport.buttons &= ~(1 << (keycode - KC_MS_BTN1)); - } + currentReport.buttons = pointing_device_handle_buttons(currentReport.buttons, record->event.pressed, keycode - KC_MS_BTN1); pointing_device_set_report(currentReport); pointing_device_send(); } @@ -207,48 +193,6 @@ void keyboard_pre_init_kb(void) { keyboard_pre_init_user(); } -void pointing_device_init(void) { - adns_init(); - opt_encoder_init(); - - // reboot the adns. - // if the adns hasn't initialized yet, this is harmless. - adns_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). - adns_read_burst(); - - // set the DPI. - adns_set_cpi(dpi_array[keyboard_config.dpi_config]); -} - -void pointing_device_task(void) { - report_mouse_t mouse_report = pointing_device_get_report(); - process_wheel(&mouse_report); - process_mouse(&mouse_report); - - if (is_drag_scroll) { - mouse_report.h = mouse_report.x; -#ifdef PLOOPY_DRAGSCROLL_INVERT - // Invert vertical scroll direction - mouse_report.v = -mouse_report.y; -#else - mouse_report.v = mouse_report.y; -#endif - mouse_report.x = 0; - mouse_report.y = 0; - } - - pointing_device_set_report(mouse_report); - pointing_device_send(); -} - void eeconfig_init_kb(void) { keyboard_config.dpi_config = PLOOPY_DPI_DEFAULT; eeconfig_update_kb(keyboard_config.raw); diff --git a/keyboards/ploopyco/trackball_mini/trackball_mini.h b/keyboards/ploopyco/trackball_mini/trackball_mini.h index 7bcb02a940c..fc86fe776e7 100644 --- a/keyboards/ploopyco/trackball_mini/trackball_mini.h +++ b/keyboards/ploopyco/trackball_mini/trackball_mini.h @@ -20,10 +20,8 @@ #pragma once #include "quantum.h" -#include "drivers/sensors/adns5050.h" #include "analog.h" #include "opt_encoder.h" -#include "pointing_device.h" // Sensor defs #define OPT_ENC1 F0 @@ -31,10 +29,7 @@ #define OPT_ENC1_MUX 0 #define OPT_ENC2_MUX 4 -void process_mouse(report_mouse_t* mouse_report); -void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y); void process_wheel(report_mouse_t* mouse_report); -void process_wheel_user(report_mouse_t* mouse_report, int16_t h, int16_t v); #define LAYOUT(BL, BM, BR, BF, BB) \ { {BL, BM, BR, BF, BB}, } diff --git a/keyboards/ploopyco/trackball_nano/config.h b/keyboards/ploopyco/trackball_nano/config.h index 7450f5574cb..7d109db99ed 100644 --- a/keyboards/ploopyco/trackball_nano/config.h +++ b/keyboards/ploopyco/trackball_nano/config.h @@ -45,3 +45,9 @@ a polling rate as possible. */ #define USB_POLLING_INTERVAL_MS 1 #define USB_MAX_POWER_CONSUMPTION 100 + +#define ADNS5050_SCLK_PIN B7 +#define ADNS5050_SDIO_PIN C6 +#define ADNS5050_CS_PIN B4 + +#define POINTING_DEVICE_ROTATION_90 diff --git a/keyboards/ploopyco/trackball_nano/keymaps/maddie/keymap.c b/keyboards/ploopyco/trackball_nano/keymaps/maddie/keymap.c index 6f568ecf511..9e33db3783a 100644 --- a/keyboards/ploopyco/trackball_nano/keymaps/maddie/keymap.c +++ b/keyboards/ploopyco/trackball_nano/keymaps/maddie/keymap.c @@ -21,14 +21,14 @@ // safe range starts at `PLOOPY_SAFE_RANGE` instead. uint8_t scroll_enabled = 0; uint8_t lock_state = 0; -int16_t delta_x = 0; -int16_t delta_y = 0; +int8_t delta_x = 0; +int8_t delta_y = 0; -void process_mouse_user(report_mouse_t *mouse_report, int16_t x, int16_t y) { - if (scroll_enabled) { - delta_x += x; +void process_mouse_user(report_mouse_t *mouse_report, int8_t x, int8_t y) { + if (scroll_enabled) { + delta_x += x; delta_y += y; - + if (delta_x > 60) { mouse_report->h = 1; delta_x = 0; @@ -44,10 +44,10 @@ void process_mouse_user(report_mouse_t *mouse_report, int16_t x, int16_t y) { mouse_report->v = 1; delta_y = 0; } - } else { - mouse_report->x = x; + } else { + mouse_report->x = x; mouse_report->y = y; - } + } } void keyboard_post_init_user(void) { @@ -62,7 +62,7 @@ bool led_update_user(led_t led_state) { scroll_timer = timer_read(); lock_count = 0; } - + if (led_state.num_lock != lock_state) { lock_count++; @@ -73,7 +73,7 @@ bool led_update_user(led_t led_state) { delta_y = 0; } } - + lock_state = led_state.num_lock; return true; } diff --git a/keyboards/ploopyco/trackball_nano/rules.mk b/keyboards/ploopyco/trackball_nano/rules.mk index a26ad93c89d..1a29a561e74 100644 --- a/keyboards/ploopyco/trackball_nano/rules.mk +++ b/keyboards/ploopyco/trackball_nano/rules.mk @@ -1,9 +1,6 @@ # MCU name MCU = atmega32u4 -# Processor frequency -F_CPU = 16000000 - # Bootloader selection BOOTLOADER = atmel-dfu @@ -22,9 +19,7 @@ BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow AUDIO_ENABLE = no # Audio output POINTING_DEVICE_ENABLE = yes +POINTING_DEVICE_DRIVER = adns5050 MOUSEKEY_ENABLE = no # Mouse keys -QUANTUM_LIB_SRC += analog.c -SRC += drivers/sensors/adns5050.c opt_encoder.c - DEFAULT_FOLDER = ploopyco/trackball_nano/rev1_001 diff --git a/keyboards/ploopyco/trackball_nano/trackball_nano.c b/keyboards/ploopyco/trackball_nano/trackball_nano.c index 9bcfa59ef8e..2702f605572 100644 --- a/keyboards/ploopyco/trackball_nano/trackball_nano.c +++ b/keyboards/ploopyco/trackball_nano/trackball_nano.c @@ -37,7 +37,8 @@ #endif #ifndef PLOOPY_DPI_OPTIONS -# define PLOOPY_DPI_OPTIONS { CPI375, CPI750, CPI1375 } +# define PLOOPY_DPI_OPTIONS \ + { 375, 750, 1375 } # ifndef PLOOPY_DPI_DEFAULT # define PLOOPY_DPI_DEFAULT 2 # endif @@ -49,12 +50,8 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { }; -// Transformation constants for delta-X and delta-Y -const static float ADNS_X_TRANSFORM = -1.0; -const static float ADNS_Y_TRANSFORM = 1.0; - keyboard_config_t keyboard_config; -uint16_t dpi_array[] = PLOOPY_DPI_OPTIONS; +uint16_t dpi_array[] = PLOOPY_DPI_OPTIONS; #define DPI_OPTION_SIZE (sizeof(dpi_array) / sizeof(uint16_t)) // TODO: Implement libinput profiles @@ -63,77 +60,13 @@ uint16_t dpi_array[] = PLOOPY_DPI_OPTIONS; // Valid options are ACC_NONE, ACC_LINEAR, ACC_CUSTOM, ACC_QUADRATIC // Trackball State -bool is_scroll_clicked = false; -bool BurstState = false; // init burst state for Trackball module -uint16_t MotionStart = 0; // Timer for accel, 0 is resting state -uint16_t lastScroll = 0; // Previous confirmed wheel event -uint16_t lastMidClick = 0; // Stops scrollwheel from being read if it was pressed -uint8_t OptLowPin = OPT_ENC1; -bool debug_encoder = false; +bool is_scroll_clicked = false; +bool BurstState = false; // init burst state for Trackball module +uint16_t MotionStart = 0; // Timer for accel, 0 is resting state -__attribute__((weak)) void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y) { - mouse_report->x = x; - mouse_report->y = y; -} - -__attribute__((weak)) void process_mouse(report_mouse_t* mouse_report) { - report_adns_t data = adns_read_burst(); - - if (data.dx != 0 || data.dy != 0) { - if (debug_mouse) - dprintf("Raw ] X: %d, Y: %d\n", data.dx, data.dy); - - // Apply delta-X and delta-Y transformations. - // x and y are swapped - // the sensor is rotated - // by 90 degrees - float xt = (float) data.dy * ADNS_X_TRANSFORM; - float yt = (float) data.dx * ADNS_Y_TRANSFORM; - - int16_t xti = (int16_t)xt; - int16_t yti = (int16_t)yt; - - process_mouse_user(mouse_report, xti, yti); - } -} - -bool process_record_kb(uint16_t keycode, keyrecord_t* record) { - xprintf("KL: kc: %u, col: %u, row: %u, pressed: %u\n", keycode, record->event.key.col, record->event.key.row, record->event.pressed); - - // Update Timer to prevent accidental scrolls - if ((record->event.key.col == 1) && (record->event.key.row == 0)) { - lastMidClick = timer_read(); - is_scroll_clicked = record->event.pressed; - } - - if (!process_record_user(keycode, record)) - return false; - - if (keycode == DPI_CONFIG && record->event.pressed) { - keyboard_config.dpi_config = (keyboard_config.dpi_config + 1) % DPI_OPTION_SIZE; - eeconfig_update_kb(keyboard_config.raw); - adns_set_cpi(dpi_array[keyboard_config.dpi_config]); - } - -/* If Mousekeys is disabled, then use handle the mouse button - * keycodes. This makes things simpler, and allows usage of - * the keycodes in a consistent manner. But only do this if - * Mousekeys is not enable, so it's not handled twice. - */ -#ifndef MOUSEKEY_ENABLE - if (IS_MOUSEKEY_BUTTON(keycode)) { - report_mouse_t currentReport = pointing_device_get_report(); - if (record->event.pressed) { - currentReport.buttons |= 1 << (keycode - KC_MS_BTN1); - } else { - currentReport.buttons &= ~(1 << (keycode - KC_MS_BTN1)); - } - pointing_device_set_report(currentReport); - pointing_device_send(); - } -#endif - - return true; +void pointing_device_init_kb(void) { + // set the DPI. + pointing_device_set_cpi(dpi_array[keyboard_config.dpi_config]); } // Hardware Setup @@ -143,9 +76,6 @@ void keyboard_pre_init_kb(void) { // debug_mouse = true; // debug_encoder = true; - setPinInput(OPT_ENC1); - setPinInput(OPT_ENC2); - /* Ground all output pins connected to ground. This provides additional * pathways to ground. If you're messing with this, know this: driving ANY * of these pins high will cause a short. On the MCU. Ka-blooey. @@ -162,34 +92,6 @@ void keyboard_pre_init_kb(void) { keyboard_pre_init_user(); } -void pointing_device_init(void) { - adns_init(); - opt_encoder_init(); - - // reboot the adns. - // if the adns hasn't initialized yet, this is harmless. - adns_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). - adns_read_burst(); - - // set the DPI. - adns_set_cpi(dpi_array[keyboard_config.dpi_config]); -} - -void pointing_device_task(void) { - report_mouse_t mouse_report = pointing_device_get_report(); - process_mouse(&mouse_report); - pointing_device_set_report(mouse_report); - pointing_device_send(); -} - void eeconfig_init_kb(void) { keyboard_config.dpi_config = PLOOPY_DPI_DEFAULT; eeconfig_update_kb(keyboard_config.raw); diff --git a/keyboards/ploopyco/trackball_nano/trackball_nano.h b/keyboards/ploopyco/trackball_nano/trackball_nano.h index 6c8ecace7d0..88725eab686 100644 --- a/keyboards/ploopyco/trackball_nano/trackball_nano.h +++ b/keyboards/ploopyco/trackball_nano/trackball_nano.h @@ -20,19 +20,6 @@ #pragma once #include "quantum.h" -#include "drivers/sensors/adns5050.h" -#include "analog.h" -#include "opt_encoder.h" -#include "pointing_device.h" - -// Sensor defs -#define OPT_ENC1 F0 -#define OPT_ENC2 F4 -#define OPT_ENC1_MUX 0 -#define OPT_ENC2_MUX 4 - -void process_mouse(report_mouse_t* mouse_report); -void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y); #define LAYOUT(k00) {{ KC_NO }} diff --git a/keyboards/tkw/grandiceps/rev2/rules.mk b/keyboards/tkw/grandiceps/rev2/rules.mk index c8f3f05bdd7..3e82c4507ef 100644 --- a/keyboards/tkw/grandiceps/rev2/rules.mk +++ b/keyboards/tkw/grandiceps/rev2/rules.mk @@ -1,5 +1,4 @@ EEPROM_DRIVER = i2c POINTING_DEVICE_ENABLE = yes -SRC += drivers/sensors/pimoroni_trackball.c -QUANTUM_LIB_SRC += i2c_master.c +POINTING_DEVICE_DRIVER = pimoroni_trackball diff --git a/layouts/community/ergodox/drashna/keymap.c b/layouts/community/ergodox/drashna/keymap.c index dd47899d7ad..3c9bcfeaa26 100644 --- a/layouts/community/ergodox/drashna/keymap.c +++ b/layouts/community/ergodox/drashna/keymap.c @@ -186,20 +186,20 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { #ifdef PIMORONI_TRACKBALL_ENABLE void run_trackball_cleanup(void) { - if (trackball_is_scrolling()) { - trackball_set_rgbw(RGB_CYAN, 0x00); - } else if (trackball_get_precision() != 1.0) { - trackball_set_rgbw(RGB_GREEN, 0x00); - } else { - trackball_set_rgbw(RGB_MAGENTA, 0x00); - } + // if (trackball_is_scrolling()) { + // trackball_set_rgbw(RGB_CYAN, 0x00); + // } else if (trackball_get_precision() != 1.0) { + // trackball_set_rgbw(RGB_GREEN, 0x00); + // } else { + // trackball_set_rgbw(RGB_MAGENTA, 0x00); + // } } void keyboard_post_init_keymap(void) { // trackball_set_precision(1.5); - trackball_set_rgbw(RGB_MAGENTA, 0x00); + // trackball_set_rgbw(RGB_MAGENTA, 0x00); } -void shutdown_keymap(void) { trackball_set_rgbw(RGB_RED, 0x00); } +// void shutdown_keymap(void) { trackball_set_rgbw(RGB_RED, 0x00); } static bool mouse_button_one, trackball_button_one; @@ -244,16 +244,16 @@ bool process_record_keymap(uint16_t keycode, keyrecord_t *record) { break; #ifdef PIMORONI_TRACKBALL_ENABLE case PM_SCROLL: - trackball_set_scrolling(record->event.pressed); + // trackball_set_scrolling(record->event.pressed); run_trackball_cleanup(); break; case PM_PRECISION: - if (record->event.pressed) { - trackball_set_precision(1.5); - } else { - trackball_set_precision(1); - } - run_trackball_cleanup(); + // if (record->event.pressed) { + // trackball_set_precision(1.5); + // } else { + // trackball_set_precision(1); + // } + // run_trackball_cleanup(); break; # if !defined(MOUSEKEY_ENABLE) case KC_MS_BTN1: diff --git a/quantum/pointing_device.c b/quantum/pointing_device.c index 09d889f697c..feeb2b3160d 100644 --- a/quantum/pointing_device.c +++ b/quantum/pointing_device.c @@ -1,34 +1,56 @@ -/* -Copyright 2017 Joshua Broekhuijsen +/* Copyright 2017 Joshua Broekhuijsen + * Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) + * Copyright 2021 Dasky (@daskygit) + * + * 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 . + */ -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 -#include "report.h" -#include "host.h" -#include "timer.h" -#include "print.h" -#include "debug.h" #include "pointing_device.h" +#ifdef MOUSEKEY_ENABLE +# include "mousekey.h" +#endif +#if (defined(POINTING_DEVICE_ROTATION_90) + defined(POINTING_DEVICE_ROTATION_180) + defined(POINTING_DEVICE_ROTATION_270)) > 1 +# error More than one rotation selected. This is not supported. +#endif static report_mouse_t mouseReport = {}; -__attribute__((weak)) bool has_mouse_report_changed(report_mouse_t new, report_mouse_t old) { return (new.buttons != old.buttons) || (new.x&& new.x != old.x) || (new.y&& new.y != old.y) || (new.h&& new.h != old.h) || (new.v&& new.v != old.v); } +extern const pointing_device_driver_t pointing_device_driver; + +__attribute__((weak)) bool has_mouse_report_changed(report_mouse_t new, report_mouse_t old) { return memcmp(&new, &old, sizeof(new)); } + +__attribute__((weak)) void pointing_device_init_kb(void) {} +__attribute__((weak)) void pointing_device_init_user(void) {} +__attribute__((weak)) report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { return pointing_device_task_user(mouse_report); } +__attribute__((weak)) report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { return mouse_report; } + +__attribute__((weak)) uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button) { + if (pressed) { + buttons |= 1 << (button); + } else { + buttons &= ~(1 << (button)); + } + return buttons; +} __attribute__((weak)) void pointing_device_init(void) { - // initialize device, if that needs to be done. + pointing_device_driver.init(); +#ifdef POINTING_DEVICE_MOTION_PIN + setPinInputHigh(POINTING_DEVICE_MOTION_PIN); +#endif + pointing_device_init_kb(); + pointing_device_init_user(); } __attribute__((weak)) void pointing_device_send(void) { @@ -43,20 +65,55 @@ __attribute__((weak)) void pointing_device_send(void) { mouseReport.y = 0; mouseReport.v = 0; mouseReport.h = 0; - old_report = mouseReport; + + memcpy(&old_report, &mouseReport, sizeof(mouseReport)); } __attribute__((weak)) void pointing_device_task(void) { - // gather info and put it in: - // mouseReport.x = 127 max -127 min - // mouseReport.y = 127 max -127 min - // mouseReport.v = 127 max -127 min (scroll vertical) - // mouseReport.h = 127 max -127 min (scroll horizontal) - // mouseReport.buttons = 0x1F (decimal 31, binary 00011111) max (bitmask for mouse buttons 1-5, 1 is rightmost, 5 is leftmost) 0x00 min - // send the report + // Gather report info +#ifdef POINTING_DEVICE_MOTION_PIN + if (!readPin(POINTING_DEVICE_MOTION_PIN)) +#endif + mouseReport = pointing_device_driver.get_report(mouseReport); + + // Support rotation of the sensor data +#if defined(POINTING_DEVICE_ROTATION_90) || defined(POINTING_DEVICE_ROTATION_180) || defined(POINTING_DEVICE_ROTATION_270) + int8_t x = mouseReport.x, y = mouseReport.y; +# if defined(POINTING_DEVICE_ROTATION_90) + mouseReport.x = y; + mouseReport.y = -x; +# elif defined(POINTING_DEVICE_ROTATION_180) + mouseReport.x = -x; + mouseReport.y = -y; +# elif defined(POINTING_DEVICE_ROTATION_270) + mouseReport.x = -y; + mouseReport.y = x; +# else +# error "How the heck did you get here?!" +# endif +#endif + // Support Inverting the X and Y Axises +#if defined(POINTING_DEVICE_INVERT_X) + mouseReport.x = -mouseReport.x; +#endif +#if defined(POINTING_DEVICE_INVERT_Y) + mouseReport.y = -mouseReport.y; +#endif + + // allow kb to intercept and modify report + mouseReport = pointing_device_task_kb(mouseReport); + // combine with mouse report to ensure that the combined is sent correctly +#ifdef MOUSEKEY_ENABLE + report_mouse_t mousekey_report = mousekey_get_report(); + mouseReport.buttons = mouseReport.buttons | mousekey_report.buttons; +#endif pointing_device_send(); } report_mouse_t pointing_device_get_report(void) { return mouseReport; } void pointing_device_set_report(report_mouse_t newMouseReport) { mouseReport = newMouseReport; } + +uint16_t pointing_device_get_cpi(void) { return pointing_device_driver.get_cpi(); } + +void pointing_device_set_cpi(uint16_t cpi) { pointing_device_driver.set_cpi(cpi); } diff --git a/quantum/pointing_device.h b/quantum/pointing_device.h index 56a542d5458..5106c26660e 100644 --- a/quantum/pointing_device.h +++ b/quantum/pointing_device.h @@ -21,9 +21,68 @@ along with this program. If not, see . #include "host.h" #include "report.h" +#if defined(POINTING_DEVICE_DRIVER_adns5050) +# include "drivers/sensors/adns5050.h" +#elif defined(POINTING_DEVICE_DRIVER_adns9800) +# include "spi_master.h" +# include "drivers/sensors/adns9800.h" +#elif defined(POINTING_DEVICE_DRIVER_analog_joystick) +# include "analog.h" +# include "drivers/sensors/analog_joystick.h" +#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) +# include "drivers/sensors/cirque_pinnacle.h" +#elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball) +# include "i2c_master.h" +# include "drivers/sensors/pimoroni_trackball.h" +// support for legacy pimoroni defines +# ifdef PIMORONI_TRACKBALL_INVERT_X +# define POINTING_DEVICE_INVERT_X +# endif +# ifdef PIMORONI_TRACKBALL_INVERT_Y +# define POINTING_DEVICE_INVERT_Y +# endif +# ifdef PIMORONI_TRACKBALL_ROTATE +# define POINTING_DEVICE_ROTATION_90 +# endif +#elif defined(POINTING_DEVICE_DRIVER_pmw3360) +# include "spi_master.h" +# include "drivers/sensors/pmw3360.h" +#else +void pointing_device_driver_init(void); +report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report); +uint16_t pointing_device_driver_get_cpi(void); +void pointing_device_driver_set_cpi(uint16_t cpi); +#endif + +typedef struct { + void (*init)(void); + report_mouse_t (*get_report)(report_mouse_t mouse_report); + void (*set_cpi)(uint16_t); + uint16_t (*get_cpi)(void); +} pointing_device_driver_t; + +typedef enum { + POINTING_DEVICE_BUTTON1, + POINTING_DEVICE_BUTTON2, + POINTING_DEVICE_BUTTON3, + POINTING_DEVICE_BUTTON4, + POINTING_DEVICE_BUTTON5, + POINTING_DEVICE_BUTTON6, + POINTING_DEVICE_BUTTON7, + POINTING_DEVICE_BUTTON8, +} pointing_device_buttons_t; + void pointing_device_init(void); void pointing_device_task(void); void pointing_device_send(void); report_mouse_t pointing_device_get_report(void); void pointing_device_set_report(report_mouse_t newMouseReport); bool has_mouse_report_changed(report_mouse_t new, report_mouse_t old); +uint16_t pointing_device_get_cpi(void); +void pointing_device_set_cpi(uint16_t cpi); + +void pointing_device_init_kb(void); +void pointing_device_init_user(void); +report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report); +report_mouse_t pointing_device_task_user(report_mouse_t mouse_report); +uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button); diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c new file mode 100644 index 00000000000..9ad5e76ba63 --- /dev/null +++ b/quantum/pointing_device_drivers.c @@ -0,0 +1,262 @@ +/* Copyright 2017 Joshua Broekhuijsen + * Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) + * Copyright 2021 Dasky (@daskygit) + * + * 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 "pointing_device.h" +#include "debug.h" +#include "wait.h" +#include "timer.h" +#include + +// hid mouse reports cannot exceed -127 to 127, so constrain to that value +#define constrain_hid(amt) ((amt) < -127 ? -127 : ((amt) > 127 ? 127 : (amt))) + +// get_report functions should probably be moved to their respective drivers. +#if defined(POINTING_DEVICE_DRIVER_adns5050) +report_mouse_t adns5050_get_report(report_mouse_t mouse_report) { + report_adns5050_t data = adns5050_read_burst(); + + if (data.dx != 0 || data.dy != 0) { +# ifdef CONSOLE_ENABLE + if (debug_mouse) dprintf("Raw ] X: %d, Y: %d\n", data.dx, data.dy); +# endif + + mouse_report.x = data.dx; + mouse_report.y = data.dy; + } + + return mouse_report; +} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = adns5050_init, + .get_report = adns5050_get_report, + .set_cpi = adns5050_set_cpi, + .get_cpi = adns5050_get_cpi, +}; +// clang-format on +#elif defined(POINTING_DEVICE_DRIVER_adns9800) + +report_mouse_t adns9800_get_report_driver(report_mouse_t mouse_report) { + report_adns9800_t sensor_report = adns9800_get_report(); + + int8_t clamped_x = constrain_hid(sensor_report.x); + int8_t clamped_y = constrain_hid(sensor_report.y); + + mouse_report.x = clamped_x; + mouse_report.y = clamped_y; + + return mouse_report; +} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = adns9800_init, + .get_report = adns9800_get_report_driver, + .set_cpi = adns9800_set_cpi, + .get_cpi = adns9800_get_cpi +}; +// clang-format on +#elif defined(POINTING_DEVICE_DRIVER_analog_joystick) +report_mouse_t analog_joystick_get_report(report_mouse_t mouse_report) { + report_analog_joystick_t data = analog_joystick_read(); + +# ifdef CONSOLE_ENABLE + if (debug_mouse) dprintf("Raw ] X: %d, Y: %d\n", data.x, data.y); +# endif + + mouse_report.x = data.x; + mouse_report.y = data.y; + + mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, data.button, POINTING_DEVICE_BUTTON1); + + return mouse_report; +} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = analog_joystick_init, + .get_report = analog_joystick_get_report, + .set_cpi = NULL, + .get_cpi = NULL +}; +// clang-format on +#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi) +# ifndef CIRQUE_PINNACLE_TAPPING_TERM +# ifdef TAPPING_TERM_PER_KEY +# include "action.h" +# include "action_tapping.h" +# define CIRQUE_PINNACLE_TAPPING_TERM get_tapping_term(KC_BTN1, NULL) +# else +# ifdef TAPPING_TERM +# define CIRQUE_PINNACLE_TAPPING_TERM TAPPING_TERM +# else +# define CIRQUE_PINNACLE_TAPPING_TERM 200 +# endif +# endif +# endif +# ifndef CIRQUE_PINNACLE_TOUCH_DEBOUNCE +# define CIRQUE_PINNACLE_TOUCH_DEBOUNCE (CIRQUE_PINNACLE_TAPPING_TERM * 8) +# endif + +report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) { + pinnacle_data_t touchData = cirque_pinnacle_read_data(); + static uint16_t x = 0, y = 0, mouse_timer = 0; + int8_t report_x = 0, report_y = 0; + static bool is_z_down = false; + + cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale()); // Scale coordinates to arbitrary X, Y resolution + + if (x && y && touchData.xValue && touchData.yValue) { + report_x = (int8_t)(touchData.xValue - x); + report_y = (int8_t)(touchData.yValue - y); + } + x = touchData.xValue; + y = touchData.yValue; + + if ((bool)touchData.zValue != is_z_down) { + is_z_down = (bool)touchData.zValue; + if (!touchData.zValue) { + if (timer_elapsed(mouse_timer) < CIRQUE_PINNACLE_TAPPING_TERM && mouse_timer != 0) { + mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1); + pointing_device_set_report(mouse_report); + pointing_device_send(); +# if TAP_CODE_DELAY > 0 + wait_ms(TAP_CODE_DELAY); +# endif + mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1); + pointing_device_set_report(mouse_report); + pointing_device_send(); + } + } + mouse_timer = timer_read(); + } + if (timer_elapsed(mouse_timer) > (CIRQUE_PINNACLE_TOUCH_DEBOUNCE)) { + mouse_timer = 0; + } + mouse_report.x = report_x; + mouse_report.y = report_y; + + return mouse_report; +} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = cirque_pinnacle_init, + .get_report = cirque_pinnacle_get_report, + .set_cpi = cirque_pinnacle_set_scale, + .get_cpi = cirque_pinnacle_get_scale +}; +// clang-format on + +#elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball) +report_mouse_t pimorono_trackball_get_report(report_mouse_t mouse_report) { + static fast_timer_t throttle = 0; + static uint16_t debounce = 0; + static uint8_t error_count = 0; + pimoroni_data_t pimoroni_data = {0}; + static int16_t x_offset = 0, y_offset = 0; + + if (error_count < PIMORONI_TRACKBALL_ERROR_COUNT && timer_elapsed_fast(throttle) >= PIMORONI_TRACKBALL_INTERVAL_MS) { + i2c_status_t status = read_pimoroni_trackball(&pimoroni_data); + + if (status == I2C_STATUS_SUCCESS) { + error_count = 0; + + if (!(pimoroni_data.click & 128)) { + mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1); + if (!debounce) { + x_offset += pimoroni_trackball_get_offsets(pimoroni_data.right, pimoroni_data.left, PIMORONI_TRACKBALL_SCALE); + y_offset += pimoroni_trackball_get_offsets(pimoroni_data.down, pimoroni_data.up, PIMORONI_TRACKBALL_SCALE); + pimoroni_trackball_adapt_values(&mouse_report.x, &x_offset); + pimoroni_trackball_adapt_values(&mouse_report.y, &y_offset); + } else { + debounce--; + } + } else { + mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1); + debounce = PIMORONI_TRACKBALL_DEBOUNCE_CYCLES; + } + } else { + error_count++; + } + throttle = timer_read_fast(); + } + return mouse_report; +} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = pimironi_trackball_device_init, + .get_report = pimorono_trackball_get_report, + .set_cpi = NULL, + .get_cpi = NULL +}; +// clang-format on +#elif defined(POINTING_DEVICE_DRIVER_pmw3360) + +static void init(void) { pmw3360_init(); } + +report_mouse_t pmw3360_get_report(report_mouse_t mouse_report) { + report_pmw3360_t data = pmw3360_read_burst(); + static uint16_t MotionStart = 0; // Timer for accel, 0 is resting state + + if (data.isOnSurface && data.isMotion) { + // Reset timer if stopped moving + if (!data.isMotion) { + if (MotionStart != 0) MotionStart = 0; + return mouse_report; + } + + // Set timer if new motion + if ((MotionStart == 0) && data.isMotion) { +# ifdef CONSOLE_ENABLE + if (debug_mouse) dprintf("Starting motion.\n"); +# endif + MotionStart = timer_read(); + } + mouse_report.x = constrain_hid(data.dx); + mouse_report.y = constrain_hid(data.dy); + } + + return mouse_report; +} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = init, + .get_report = pmw3360_get_report, + .set_cpi = pmw3360_set_cpi, + .get_cpi = pmw3360_get_cpi +}; +// 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) { return mouse_report; } +__attribute__((weak)) uint16_t pointing_device_driver_get_cpi(void) { return 0; } +__attribute__((weak)) void pointing_device_driver_set_cpi(uint16_t cpi) {} + +// clang-format off +const pointing_device_driver_t pointing_device_driver = { + .init = pointing_device_driver_init, + .get_report = pointing_device_driver_get_report, + .get_cpi = pointing_device_driver_get_cpi, + .set_cpi = pointing_device_driver_set_cpi +}; +// clang-format on +#endif diff --git a/quantum/quantum.c b/quantum/quantum.c index 0eca329f08b..c7a3bb19707 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -531,6 +531,10 @@ void suspend_power_down_quantum(void) { # ifdef ST7565_ENABLE st7565_off(); # endif +# if defined(POINTING_DEVICE_ENABLE) + // run to ensure scanning occurs while suspended + pointing_device_task(); +# endif #endif } diff --git a/quantum/quantum.h b/quantum/quantum.h index 9250f5acce7..e6015adbe9f 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -204,6 +204,10 @@ extern layer_state_t layer_state; # include "encoder.h" #endif +#ifdef POINTING_DEVICE_ENABLE +# include "pointing_device.h" +#endif + // For tri-layer void update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3); layer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_t layer2, uint8_t layer3);