Prechádzať zdrojové kódy

Merge remote-tracking branch 'origin/develop' into xap

QMK Bot 3 rokov pred
rodič
commit
de2bd30a31

+ 1 - 1
builddefs/common_features.mk

@@ -138,7 +138,7 @@ ifeq ($(strip $(MOUSEKEY_ENABLE)), yes)
     SRC += $(QUANTUM_DIR)/mousekey.c
     SRC += $(QUANTUM_DIR)/mousekey.c
 endif
 endif
 
 
-VALID_POINTING_DEVICE_DRIVER_TYPES := adns5050 adns9800 analog_joystick cirque_pinnacle_i2c cirque_pinnacle_spi paw3204 pmw3360 pmw3389 pimoroni_trackball custom
+VALID_POINTING_DEVICE_DRIVER_TYPES := adns5050 adns9800 analog_joystick cirque_pinnacle_i2c cirque_pinnacle_spi paw3204 pmw3320 pmw3360 pmw3389 pimoroni_trackball custom
 ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
 ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
     ifeq ($(filter $(POINTING_DEVICE_DRIVER),$(VALID_POINTING_DEVICE_DRIVER_TYPES)),)
     ifeq ($(filter $(POINTING_DEVICE_DRIVER),$(VALID_POINTING_DEVICE_DRIVER_TYPES)),)
         $(call CATASTROPHIC_ERROR,Invalid POINTING_DEVICE_DRIVER,POINTING_DEVICE_DRIVER="$(POINTING_DEVICE_DRIVER)" is not a valid pointing device type)
         $(call CATASTROPHIC_ERROR,Invalid POINTING_DEVICE_DRIVER,POINTING_DEVICE_DRIVER="$(POINTING_DEVICE_DRIVER)" is not a valid pointing device type)

+ 18 - 0
docs/feature_pointing_device.md

@@ -197,6 +197,24 @@ The Pimoroni Trackball module is a I2C based breakout board with an RGB enable t
 | `PIMORONI_TRACKBALL_DEBOUNCE_CYCLES` | (Optional) The number of scan cycles used for debouncing on the ball press.        | `20`    |
 | `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`    |
 | `PIMORONI_TRACKBALL_ERROR_COUNT`     | (Optional) Specifies the number of read/write errors until the sensor is disabled. | `10`    |
 
 
+### PMW3320 Sensor
+
+To use the PMW3320 sensor, add this to your `rules.mk`
+
+```make
+POINTING_DEVICE_DRIVER = pmw3320
+```
+
+The PMW3320 sensor uses a serial type protocol for communication, and requires an additional light source (it could work without one, but expect it to be out of service early).
+
+| Setting             | Description                                                         | Default                    |
+| ------------------- | ------------------------------------------------------------------- | -------------------------- |
+| `PMW3320_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor.        | `POINTING_DEVICE_SCLK_PIN` |
+| `PMW3320_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor.         | `POINTING_DEVICE_SDIO_PIN` |
+| `PMW3320_CS_PIN`   | (Required) The pin connected to the cable select pin of the sensor. | `POINTING_DEVICE_CS_PIN`   |
+
+The CPI range is 500-3500, in increments of 250. Defaults to 1000 CPI.
+
 ### PMW 3360 and PMW 3389 Sensor
 ### PMW 3360 and PMW 3389 Sensor
 
 
 This drivers supports both the PMW 3360 and PMW 3389 sensor as well as multiple sensors of the same type _per_ controller, so 2 can be attached at the same side for split keyboards (or unsplit keyboards).
 This drivers supports both the PMW 3360 and PMW 3389 sensor as well as multiple sensors of the same type _per_ controller, so 2 can be attached at the same side for split keyboards (or unsplit keyboards).

+ 192 - 0
drivers/sensors/pmw3320.c

@@ -0,0 +1,192 @@
+/* Copyright 2021 Colin Lam (Ploopy Corporation)
+ * Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
+ * Copyright 2019 Sunjun Kim
+ * Copyright 2019 Hiroyuki Okada
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "pmw3320.h"
+#include "wait.h"
+#include "debug.h"
+#include "gpio.h"
+
+void pmw3320_init(void) {
+    // Initialize sensor serial pins.
+    setPinOutput(PMW3320_SCLK_PIN);
+    setPinOutput(PMW3320_SDIO_PIN);
+    setPinOutput(PMW3320_CS_PIN);
+
+    // reboot the sensor.
+    pmw3320_write_reg(REG_Power_Up_Reset, 0x5a);
+
+    // wait maximum time before sensor is ready.
+    // this ensures that the sensor is actually ready after reset.
+    wait_ms(55);
+
+    // read a burst from the sensor and then discard it.
+    // gets the sensor ready for write commands
+    // (for example, setting the dpi).
+    pmw3320_read_burst();
+
+    // Pretty sure that this shouldn't be in the driver.
+    // Probably device specific?
+    // Set rest mode to default
+    pmw3320_write_reg(REG_Rest_Mode_Status, 0x00);
+    // Set LED to be always on
+    pmw3320_write_reg(REG_Led_Control, 0x4);
+    // Disable rest mode
+    pmw3320_write_reg(REG_Performance, 0x80);
+}
+
+// Perform a synchronization with sensor.
+// Just as with the serial protocol, this is used by the slave to send a
+// synchronization signal to the master.
+void pmw3320_sync(void) {
+    writePinLow(PMW3320_CS_PIN);
+    wait_us(1);
+    writePinHigh(PMW3320_CS_PIN);
+}
+
+void pmw3320_cs_select(void) {
+    writePinLow(PMW3320_CS_PIN);
+}
+
+void pmw3320_cs_deselect(void) {
+    writePinHigh(PMW3320_CS_PIN);
+}
+
+uint8_t pmw3320_serial_read(void) {
+    setPinInput(PMW3320_SDIO_PIN);
+    uint8_t byte = 0;
+
+    for (uint8_t i = 0; i < 8; ++i) {
+        writePinLow(PMW3320_SCLK_PIN);
+        wait_us(1);
+
+        byte = (byte << 1) | readPin(PMW3320_SDIO_PIN);
+
+        writePinHigh(PMW3320_SCLK_PIN);
+        wait_us(1);
+    }
+
+    return byte;
+}
+
+void pmw3320_serial_write(uint8_t data) {
+    setPinOutput(PMW3320_SDIO_PIN);
+
+    for (int8_t b = 7; b >= 0; b--) {
+        writePinLow(PMW3320_SCLK_PIN);
+
+        if (data & (1 << b))
+            writePinHigh(PMW3320_SDIO_PIN);
+        else
+            writePinLow(PMW3320_SDIO_PIN);
+
+        wait_us(2);
+
+        writePinHigh(PMW3320_SCLK_PIN);
+    }
+
+    // This was taken from ADNS5050 driver.
+    // There's no any info in PMW3320 datasheet about this...
+    // tSWR. See page 15 of the ADNS5050 spec sheet.
+    // Technically, this is only necessary if the next operation is an SDIO
+    // read. This is not guaranteed to be the case, but we're being lazy.
+    wait_us(4);
+
+    // Note that tSWW is never necessary. All write operations require at
+    // least 32us, which exceeds tSWW, so there's never a need to wait for it.
+}
+
+// Read a byte of data from a register on the sensor.
+uint8_t pmw3320_read_reg(uint8_t reg_addr) {
+    pmw3320_cs_select();
+
+    pmw3320_serial_write(reg_addr);
+
+    uint8_t byte = pmw3320_serial_read();
+
+    // This was taken directly from ADNS5050 driver...
+    // tSRW & tSRR. See page 15 of the ADNS5050 spec sheet.
+    // Technically, this is only necessary if the next operation is an SDIO
+    // read or write. This is not guaranteed to be the case.
+    // Honestly, this wait could probably be removed.
+    wait_us(1);
+
+    pmw3320_cs_deselect();
+
+    return byte;
+}
+
+void pmw3320_write_reg(uint8_t reg_addr, uint8_t data) {
+    pmw3320_cs_select();
+    pmw3320_serial_write(0b10000000 | reg_addr);
+    pmw3320_serial_write(data);
+    pmw3320_cs_deselect();
+}
+
+report_pmw3320_t pmw3320_read_burst(void) {
+    pmw3320_cs_select();
+
+    report_pmw3320_t data;
+    data.dx = 0;
+    data.dy = 0;
+
+    pmw3320_serial_write(REG_Motion_Burst);
+
+    uint8_t x = pmw3320_serial_read();
+    uint8_t y = pmw3320_serial_read();
+
+    // Probably burst mode may include contents of delta_xy register,
+    // which contain HI parts of x/y deltas, but I had no luck finding it.
+    // Probably it's required to activate 12-bit mode to access this data.
+    // So we end burst mode early to not read unneeded information.
+    pmw3320_cs_deselect();
+
+    data.dx = convert_twoscomp(x);
+    data.dy = convert_twoscomp(y);
+
+    return data;
+}
+
+// Convert a two's complement byte from an unsigned data type into a signed
+// data type.
+int8_t convert_twoscomp(uint8_t data) {
+    if ((data & 0x80) == 0x80)
+        return -128 + (data & 0x7F);
+    else
+        return data;
+}
+
+uint16_t pmw3320_get_cpi(void) {
+    uint8_t cpival = pmw3320_read_reg(REG_Resolution);
+    // 0x1F is an inversion of 0x20 which is 0b100000
+    return (uint16_t)((cpival & 0x1F) * PMW3320_CPI_STEP);
+}
+
+void pmw3320_set_cpi(uint16_t cpi) {
+    uint8_t cpival = constrain((cpi / PMW3320_CPI_STEP) - 1U, 0, (PMW3320_CPI_MAX / PMW3320_CPI_STEP) - 1U);
+    // Fifth bit is probably a control bit.
+    // PMW3320 datasheet don't have any info on this, so this is a pure guess.
+    pmw3320_write_reg(REG_Resolution, 0x20 | cpival);
+}
+
+bool pmw3320_check_signature(void) {
+    uint8_t pid  = pmw3320_read_reg(REG_Product_ID);
+    uint8_t pid2 = pmw3320_read_reg(REG_Inverse_Product_ID);
+
+    return (pid == 0x3b && pid2 == 0xc4);
+}

+ 119 - 0
drivers/sensors/pmw3320.h

@@ -0,0 +1,119 @@
+/* Copyright 2021 Colin Lam (Ploopy Corporation)
+ * Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
+ * Copyright 2019 Sunjun Kim
+ * Copyright 2019 Hiroyuki Okada
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
+
+// Definitions for the PMW3320 serial line.
+#ifndef PMW3320_SCLK_PIN
+#    ifdef POINTING_DEVICE_SCLK_PIN
+#        define PMW3320_SCLK_PIN POINTING_DEVICE_SCLK_PIN
+#    else
+#        error "No clock pin defined -- missing POINTING_DEVICE_SCLK_PIN or PMW3320_SCLK_PIN"
+#    endif
+#endif
+
+#ifndef PMW3320_SDIO_PIN
+#    ifdef POINTING_DEVICE_SDIO_PIN
+#        define PMW3320_SDIO_PIN POINTING_DEVICE_SDIO_PIN
+#    else
+#        error "No data pin defined -- missing POINTING_DEVICE_SDIO_PIN or PMW3320_SDIO_PIN"
+#    endif
+#endif
+
+#ifndef PMW3320_CS_PIN
+#    ifdef POINTING_DEVICE_CS_PIN
+#        define PMW3320_CS_PIN POINTING_DEVICE_CS_PIN
+#    else
+#        error "No chip select pin defined -- missing POINTING_DEVICE_CS_PIN or PMW3320_CS_PIN define"
+#    endif
+#endif
+
+typedef struct {
+    int8_t dx;
+    int8_t dy;
+} report_pmw3320_t;
+
+// A bunch of functions to implement the PMW3320-specific serial protocol.
+// Mostly taken from ADNS5050 driver.
+// Note that the "serial.h" driver is insufficient, because it does not
+// manually manipulate a serial clock signal.
+void             pmw3320_init(void);
+void             pmw3320_sync(void);
+uint8_t          pmw3320_serial_read(void);
+void             pmw3320_serial_write(uint8_t data);
+uint8_t          pmw3320_read_reg(uint8_t reg_addr);
+void             pmw3320_write_reg(uint8_t reg_addr, uint8_t data);
+report_pmw3320_t pmw3320_read_burst(void);
+void             pmw3320_set_cpi(uint16_t cpi);
+uint16_t         pmw3320_get_cpi(void);
+int8_t           convert_twoscomp(uint8_t data);
+bool             pmw3320_check_signature(void);
+
+#if !defined(PMW3320_CPI)
+#    define PMW3320_CPI 1000
+#endif
+
+#define PMW3320_CPI_STEP 250
+#define PMW3320_CPI_MIN 250
+#define PMW3320_CPI_MAX 3500
+
+// PMW3320 register addresses
+// 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_Accum                0x09
+#define REG_Minimum_Pixel              0x0a
+#define REG_Pixel_Grab                 0x0b
+#define REG_Delta_XY                   0x0c
+#define REG_Resolution                 0x0d
+#define REG_Run_Downshift              0x0e
+#define REG_Rest1_Period               0x0f
+#define REG_Rest1_Downshift            0x10
+#define REG_Rest2_Preiod               0x11
+#define REG_Rest2_Downshift            0x12
+#define REG_Rest3_Period               0x13
+#define REG_Min_SQ_Run                 0x17
+#define REG_Axis_Control               0x1a
+#define REG_Performance                0x22
+#define REG_Low_Motion_Jitter          0x23
+#define REG_Shutter_Max_HI             0x36
+#define REG_Shutter_Max_LO             0x37
+#define REG_Frame_Rate                 0x39
+#define REG_Power_Up_Reset             0x3a
+#define REG_Shutdown                   0x3b
+#define REG_Inverse_Revision_ID        0x3f
+#define REG_Led_Control                0x40
+#define REG_Motion_Control             0x41
+#define REG_Burst_Read_First           0x42
+#define REG_Rest_Mode_Status           0x45
+#define REG_Inverse_Product_ID         0x4f
+#define REG_Motion_Burst               0x63
+// clang-format on

+ 3 - 0
quantum/pointing_device/pointing_device.h

@@ -28,6 +28,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #if defined(POINTING_DEVICE_DRIVER_adns5050)
 #if defined(POINTING_DEVICE_DRIVER_adns5050)
 #    include "drivers/sensors/adns5050.h"
 #    include "drivers/sensors/adns5050.h"
 #    define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW
 #    define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW
+#elif defined(POINTING_DEVICE_DRIVER_pmw3320)
+#    include "drivers/sensors/pmw3320.h"
+#    define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW
 #elif defined(POINTING_DEVICE_DRIVER_adns9800)
 #elif defined(POINTING_DEVICE_DRIVER_adns9800)
 #    include "spi_master.h"
 #    include "spi_master.h"
 #    include "drivers/sensors/adns9800.h"
 #    include "drivers/sensors/adns9800.h"

+ 22 - 0
quantum/pointing_device/pointing_device_drivers.c

@@ -50,6 +50,28 @@ const pointing_device_driver_t pointing_device_driver = {
 };
 };
 // clang-format on
 // clang-format on
 
 
+#elif defined(POINTING_DEVICE_DRIVER_pmw3320)
+report_mouse_t pmw3320_get_report(report_mouse_t mouse_report) {
+    report_pmw3320_t data = pmw3320_read_burst();
+
+    if (data.dx != 0 || data.dy != 0) {
+        pd_dprintf("Raw ] X: %d, Y: %d\n", data.dx, data.dy);
+        mouse_report.x = (mouse_xy_report_t)data.dx;
+        mouse_report.y = (mouse_xy_report_t)data.dy;
+    }
+
+    return mouse_report;
+}
+
+// clang-format off
+const pointing_device_driver_t pointing_device_driver = {
+    .init         = pmw3320_init,
+    .get_report   = pmw3320_get_report,
+    .set_cpi      = pmw3320_set_cpi,
+    .get_cpi      = pmw3320_get_cpi,
+};
+// clang-format on
+
 #elif defined(POINTING_DEVICE_DRIVER_adns9800)
 #elif defined(POINTING_DEVICE_DRIVER_adns9800)
 
 
 report_mouse_t adns9800_get_report_driver(report_mouse_t mouse_report) {
 report_mouse_t adns9800_get_report_driver(report_mouse_t mouse_report) {