qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

pmw3320.c (6585B)


      1 /* Copyright 2021 Colin Lam (Ploopy Corporation)
      2  * Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
      3  * Copyright 2019 Sunjun Kim
      4  * Copyright 2019 Hiroyuki Okada
      5  *
      6  * This program is free software: you can redistribute it and/or modify
      7  * it under the terms of the GNU General Public License as published by
      8  * the Free Software Foundation, either version 2 of the License, or
      9  * (at your option) any later version.
     10  *
     11  * This program is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  * GNU General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU General Public License
     17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     18  */
     19 
     20 #include "pmw3320.h"
     21 #include "wait.h"
     22 #include "debug.h"
     23 #include "gpio.h"
     24 #include "pointing_device_internal.h"
     25 
     26 const pointing_device_driver_t pmw3320_pointing_device_drivera = {
     27     .init       = pmw3320_init,
     28     .get_report = pmw3320_get_report,
     29     .set_cpi    = pmw3320_set_cpi,
     30     .get_cpi    = pmw3320_get_cpi,
     31 };
     32 
     33 bool pmw3320_init(void) {
     34     // Initialize sensor serial pins.
     35     gpio_set_pin_output(PMW3320_SCLK_PIN);
     36     gpio_set_pin_output(PMW3320_SDIO_PIN);
     37     gpio_set_pin_output(PMW3320_CS_PIN);
     38 
     39     // reboot the sensor.
     40     pmw3320_write_reg(REG_Power_Up_Reset, 0x5a);
     41 
     42     // wait maximum time before sensor is ready.
     43     // this ensures that the sensor is actually ready after reset.
     44     wait_ms(55);
     45 
     46     // read a burst from the sensor and then discard it.
     47     // gets the sensor ready for write commands
     48     // (for example, setting the dpi).
     49     pmw3320_read_burst();
     50 
     51     // Pretty sure that this shouldn't be in the driver.
     52     // Probably device specific?
     53     // Set rest mode to default
     54     pmw3320_write_reg(REG_Rest_Mode_Status, 0x00);
     55     // Set LED to be always on
     56     pmw3320_write_reg(REG_Led_Control, 0x4);
     57     // Disable rest mode
     58     pmw3320_write_reg(REG_Performance, 0x80);
     59 
     60     return pmw3320_check_signature();
     61 }
     62 
     63 // Perform a synchronization with sensor.
     64 // Just as with the serial protocol, this is used by the slave to send a
     65 // synchronization signal to the master.
     66 void pmw3320_sync(void) {
     67     gpio_write_pin_low(PMW3320_CS_PIN);
     68     wait_us(1);
     69     gpio_write_pin_high(PMW3320_CS_PIN);
     70 }
     71 
     72 void pmw3320_cs_select(void) {
     73     gpio_write_pin_low(PMW3320_CS_PIN);
     74 }
     75 
     76 void pmw3320_cs_deselect(void) {
     77     gpio_write_pin_high(PMW3320_CS_PIN);
     78 }
     79 
     80 uint8_t pmw3320_serial_read(void) {
     81     gpio_set_pin_input(PMW3320_SDIO_PIN);
     82     uint8_t byte = 0;
     83 
     84     for (uint8_t i = 0; i < 8; ++i) {
     85         gpio_write_pin_low(PMW3320_SCLK_PIN);
     86         wait_us(1);
     87 
     88         byte = (byte << 1) | gpio_read_pin(PMW3320_SDIO_PIN);
     89 
     90         gpio_write_pin_high(PMW3320_SCLK_PIN);
     91         wait_us(1);
     92     }
     93 
     94     return byte;
     95 }
     96 
     97 void pmw3320_serial_write(uint8_t data) {
     98     gpio_set_pin_output(PMW3320_SDIO_PIN);
     99 
    100     for (int8_t b = 7; b >= 0; b--) {
    101         gpio_write_pin_low(PMW3320_SCLK_PIN);
    102 
    103         if (data & (1 << b))
    104             gpio_write_pin_high(PMW3320_SDIO_PIN);
    105         else
    106             gpio_write_pin_low(PMW3320_SDIO_PIN);
    107 
    108         wait_us(2);
    109 
    110         gpio_write_pin_high(PMW3320_SCLK_PIN);
    111     }
    112 
    113     // This was taken from ADNS5050 driver.
    114     // There's no any info in PMW3320 datasheet about this...
    115     // tSWR. See page 15 of the ADNS5050 spec sheet.
    116     // Technically, this is only necessary if the next operation is an SDIO
    117     // read. This is not guaranteed to be the case, but we're being lazy.
    118     wait_us(4);
    119 
    120     // Note that tSWW is never necessary. All write operations require at
    121     // least 32us, which exceeds tSWW, so there's never a need to wait for it.
    122 }
    123 
    124 // Read a byte of data from a register on the sensor.
    125 uint8_t pmw3320_read_reg(uint8_t reg_addr) {
    126     pmw3320_cs_select();
    127 
    128     pmw3320_serial_write(reg_addr);
    129 
    130     uint8_t byte = pmw3320_serial_read();
    131 
    132     // This was taken directly from ADNS5050 driver...
    133     // tSRW & tSRR. See page 15 of the ADNS5050 spec sheet.
    134     // Technically, this is only necessary if the next operation is an SDIO
    135     // read or write. This is not guaranteed to be the case.
    136     // Honestly, this wait could probably be removed.
    137     wait_us(1);
    138 
    139     pmw3320_cs_deselect();
    140 
    141     return byte;
    142 }
    143 
    144 void pmw3320_write_reg(uint8_t reg_addr, uint8_t data) {
    145     pmw3320_cs_select();
    146     pmw3320_serial_write(0b10000000 | reg_addr);
    147     pmw3320_serial_write(data);
    148     pmw3320_cs_deselect();
    149 }
    150 
    151 report_pmw3320_t pmw3320_read_burst(void) {
    152     pmw3320_cs_select();
    153 
    154     report_pmw3320_t data;
    155     data.dx = 0;
    156     data.dy = 0;
    157 
    158     pmw3320_serial_write(REG_Motion_Burst);
    159 
    160     uint8_t x = pmw3320_serial_read();
    161     uint8_t y = pmw3320_serial_read();
    162 
    163     // Probably burst mode may include contents of delta_xy register,
    164     // which contain HI parts of x/y deltas, but I had no luck finding it.
    165     // Probably it's required to activate 12-bit mode to access this data.
    166     // So we end burst mode early to not read unneeded information.
    167     pmw3320_cs_deselect();
    168 
    169     data.dx = convert_twoscomp(x);
    170     data.dy = convert_twoscomp(y);
    171 
    172     return data;
    173 }
    174 
    175 // Convert a two's complement byte from an unsigned data type into a signed
    176 // data type.
    177 int8_t convert_twoscomp(uint8_t data) {
    178     if ((data & 0x80) == 0x80)
    179         return -128 + (data & 0x7F);
    180     else
    181         return data;
    182 }
    183 
    184 uint16_t pmw3320_get_cpi(void) {
    185     uint8_t cpival = pmw3320_read_reg(REG_Resolution);
    186     // 0x1F is an inversion of 0x20 which is 0b100000
    187     return (uint16_t)((cpival & 0x1F) * PMW3320_CPI_STEP);
    188 }
    189 
    190 void pmw3320_set_cpi(uint16_t cpi) {
    191     uint8_t cpival = constrain((cpi / PMW3320_CPI_STEP), (PMW3320_CPI_MIN / PMW3320_CPI_STEP), (PMW3320_CPI_MAX / PMW3320_CPI_STEP)) - 1U;
    192     // Fifth bit is probably a control bit.
    193     // PMW3320 datasheet don't have any info on this, so this is a pure guess.
    194     pmw3320_write_reg(REG_Resolution, 0x20 | cpival);
    195 }
    196 
    197 bool __attribute__((weak)) pmw3320_check_signature(void) {
    198     uint8_t pid  = pmw3320_read_reg(REG_Product_ID);
    199     uint8_t pid2 = pmw3320_read_reg(REG_Inverse_Product_ID);
    200 
    201     return (pid == 0x3b && pid2 == 0xc4);
    202 }
    203 
    204 report_mouse_t pmw3320_get_report(report_mouse_t mouse_report) {
    205     report_pmw3320_t data = pmw3320_read_burst();
    206 
    207     if (data.dx != 0 || data.dy != 0) {
    208         pd_dprintf("Raw ] X: %d, Y: %d\n", data.dx, data.dy);
    209         mouse_report.x = (mouse_xy_report_t)data.dx;
    210         mouse_report.y = (mouse_xy_report_t)data.dy;
    211     }
    212 
    213     return mouse_report;
    214 }