qmk_firmware

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

pmw33xx_common.c (7242B)


      1 // Copyright 2022 Pablo Martinez (@elpekenin)
      2 // Copyright 2022 Daniel Kao (dkao)
      3 // Copyright 2022 Stefan Kerkmann (KarlK90)
      4 // Copyright 2022 Ulrich Spörlein (@uqs)
      5 // Copyright 2021 Alabastard (@Alabastard-64)
      6 // Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
      7 // Copyright 2019 Sunjun Kim
      8 // Copyright 2020 Ploopy Corporation
      9 // SPDX-License-Identifier: GPL-2.0-or-later
     10 
     11 #include "pointing_device_internal.h"
     12 #include "pmw33xx_common.h"
     13 #include "string.h"
     14 #include "wait.h"
     15 #include "spi_master.h"
     16 #include "progmem.h"
     17 
     18 extern const uint8_t pmw33xx_firmware_signature[2] PROGMEM;
     19 
     20 static const pin_t cs_pins_left[]  = PMW33XX_CS_PINS;
     21 static const pin_t cs_pins_right[] = PMW33XX_CS_PINS_RIGHT;
     22 
     23 static bool in_burst_left[ARRAY_SIZE(cs_pins_left)]   = {0};
     24 static bool in_burst_right[ARRAY_SIZE(cs_pins_right)] = {0};
     25 
     26 bool __attribute__((cold)) pmw33xx_upload_firmware(uint8_t sensor);
     27 bool __attribute__((cold)) pmw33xx_check_signature(uint8_t sensor);
     28 
     29 const pointing_device_driver_t pmw33xx_pointing_device_driver = {
     30     .init       = pmw33xx_init_wrapper,
     31     .get_report = pmw33xx_get_report,
     32     .set_cpi    = pmw33xx_set_cpi_wrapper,
     33     .get_cpi    = pmw33xx_get_cpi_wrapper,
     34 };
     35 
     36 uint16_t __attribute__((weak)) pmw33xx_srom_get_length(void) {
     37     return 0;
     38 }
     39 
     40 uint8_t __attribute__((weak)) pmw33xx_srom_get_byte(uint16_t position) {
     41     return 0;
     42 }
     43 
     44 void pmw33xx_set_cpi_all_sensors(uint16_t cpi) {
     45     for (uint8_t sensor = 0; sensor < pmw33xx_number_of_sensors; sensor++) {
     46         pmw33xx_set_cpi(sensor, cpi);
     47     }
     48 }
     49 
     50 bool pmw33xx_spi_start(uint8_t sensor) {
     51     if (!spi_start(cs_pins[sensor], false, 3, PMW33XX_SPI_DIVISOR)) {
     52         spi_stop();
     53         return false;
     54     }
     55     // tNCS-SCLK, 10ns
     56     wait_us(1);
     57     return true;
     58 }
     59 
     60 bool pmw33xx_write(uint8_t sensor, uint8_t reg_addr, uint8_t data) {
     61     if (!pmw33xx_spi_start(sensor)) {
     62         return false;
     63     }
     64 
     65     if (reg_addr != REG_Motion_Burst) {
     66         in_burst[sensor] = false;
     67     }
     68 
     69     // send address of the register, with MSBit = 1 to indicate it's a write
     70     uint8_t command[2] = {reg_addr | 0x80, data};
     71     if (spi_transmit(command, sizeof(command)) != SPI_STATUS_SUCCESS) {
     72         return false;
     73     }
     74 
     75     // tSCLK-NCS for write operation is 35us
     76     wait_us(35);
     77     spi_stop();
     78 
     79     // tSWW/tSWR (=18us) minus tSCLK-NCS. Could be shortened, but it looks like
     80     // a safe lower bound
     81     wait_us(145);
     82     return true;
     83 }
     84 
     85 uint8_t pmw33xx_read(uint8_t sensor, uint8_t reg_addr) {
     86     if (!pmw33xx_spi_start(sensor)) {
     87         return 0;
     88     }
     89 
     90     // send adress of the register, with MSBit = 0 to indicate it's a read
     91     spi_write(reg_addr & 0x7f);
     92     // tSRAD (=160us)
     93     wait_us(160);
     94     uint8_t data = spi_read();
     95 
     96     // tSCLK-NCS, 120ns
     97     wait_us(1);
     98     spi_stop();
     99 
    100     //  tSRW/tSRR (=20us) mins tSCLK-NCS
    101     wait_us(19);
    102     return data;
    103 }
    104 
    105 __attribute__((weak)) bool pmw33xx_check_signature(uint8_t sensor) {
    106     uint8_t signature_dump[2] = {
    107         pmw33xx_read(sensor, REG_Product_ID),
    108         pmw33xx_read(sensor, REG_Inverse_Product_ID),
    109     };
    110 
    111     return memcmp_P(signature_dump, pmw33xx_firmware_signature, sizeof(signature_dump)) == 0;
    112 }
    113 
    114 bool pmw33xx_upload_firmware(uint8_t sensor) {
    115     // Datasheet claims we need to disable REST mode first, but during startup
    116     // it's already disabled and we're not turning it on ...
    117     // pmw33xx_write(REG_Config2, 0x00);  // disable REST mode
    118     if (!pmw33xx_write(sensor, REG_SROM_Enable, 0x1d)) {
    119         return false;
    120     }
    121     wait_ms(10);
    122     pmw33xx_write(sensor, REG_SROM_Enable, 0x18);
    123 
    124     if (!pmw33xx_spi_start(sensor)) {
    125         return false;
    126     }
    127 
    128     spi_write(REG_SROM_Load_Burst | 0x80);
    129     wait_us(15);
    130 
    131     for (size_t i = 0; i < pmw33xx_srom_get_length(); i++) {
    132         spi_write(pmw33xx_srom_get_byte(i));
    133         wait_us(15);
    134     }
    135 
    136     spi_stop();
    137     wait_us(200);
    138 
    139     pmw33xx_read(sensor, REG_SROM_ID);
    140     pmw33xx_write(sensor, REG_Config2, 0x00);
    141 
    142     return true;
    143 }
    144 
    145 bool pmw33xx_init(uint8_t sensor) {
    146     if (sensor >= pmw33xx_number_of_sensors) {
    147         return false;
    148     }
    149     spi_init();
    150 
    151     // power up, need to first drive NCS high then low. the datasheet does not
    152     // say for how long, 40us works well in practice.
    153     if (!pmw33xx_spi_start(sensor)) {
    154         return false;
    155     }
    156     wait_us(40);
    157     spi_stop();
    158     wait_us(40);
    159 
    160     if (!pmw33xx_write(sensor, REG_Power_Up_Reset, 0x5a)) {
    161         return false;
    162     }
    163     wait_ms(50);
    164 
    165     // read registers and discard
    166     pmw33xx_read(sensor, REG_Motion);
    167     pmw33xx_read(sensor, REG_Delta_X_L);
    168     pmw33xx_read(sensor, REG_Delta_X_H);
    169     pmw33xx_read(sensor, REG_Delta_Y_L);
    170     pmw33xx_read(sensor, REG_Delta_Y_H);
    171 
    172     if (pmw33xx_srom_get_length() != 0) {
    173         if (!pmw33xx_upload_firmware(sensor)) {
    174             pd_dprintf("PMW33XX (%d): firmware upload failed!\n", sensor);
    175             return false;
    176         }
    177     } else {
    178         pd_dprintf("PMW33XX (%d): firmware upload skipped.\n", sensor);
    179     }
    180 
    181     spi_stop();
    182 
    183     wait_ms(10);
    184     pmw33xx_set_cpi(sensor, PMW33XX_CPI);
    185 
    186     wait_ms(1);
    187 
    188     pmw33xx_write(sensor, REG_Config2, 0x00);
    189     pmw33xx_write(sensor, REG_Angle_Tune, CONSTRAIN(ROTATIONAL_TRANSFORM_ANGLE, -127, 127));
    190     pmw33xx_write(sensor, REG_Lift_Config, PMW33XX_LIFTOFF_DISTANCE);
    191 
    192     if (!pmw33xx_check_signature(sensor)) {
    193         pd_dprintf("PMW33XX (%d): firmware signature verification failed!\n", sensor);
    194         return false;
    195     }
    196 
    197     return true;
    198 }
    199 
    200 pmw33xx_report_t pmw33xx_read_burst(uint8_t sensor) {
    201     pmw33xx_report_t report = {0};
    202 
    203     if (sensor >= pmw33xx_number_of_sensors) {
    204         return report;
    205     }
    206 
    207     if (!in_burst[sensor]) {
    208         pd_dprintf("PMW33XX (%d): burst\n", sensor);
    209         if (!pmw33xx_write(sensor, REG_Motion_Burst, 0x00)) {
    210             return report;
    211         }
    212         in_burst[sensor] = true;
    213     }
    214 
    215     if (!pmw33xx_spi_start(sensor)) {
    216         return report;
    217     }
    218 
    219     spi_write(REG_Motion_Burst);
    220     wait_us(35); // waits for tSRAD_MOTBR
    221 
    222     spi_receive((uint8_t *)&report, sizeof(report));
    223 
    224     // panic recovery, sometimes burst mode works weird.
    225     if (report.motion.w & 0b111) {
    226         in_burst[sensor] = false;
    227     }
    228 
    229     spi_stop();
    230 
    231     pd_dprintf("PMW33XX (%d): motion: 0x%x dx: %i dy: %i\n", sensor, report.motion.w, report.delta_x, report.delta_y);
    232 
    233     report.delta_x *= -1;
    234     report.delta_y *= -1;
    235 
    236     return report;
    237 }
    238 
    239 bool pmw33xx_init_wrapper(void) {
    240     return pmw33xx_init(0);
    241 }
    242 
    243 void pmw33xx_set_cpi_wrapper(uint16_t cpi) {
    244     pmw33xx_set_cpi(0, cpi);
    245 }
    246 
    247 uint16_t pmw33xx_get_cpi_wrapper(void) {
    248     return pmw33xx_get_cpi(0);
    249 }
    250 
    251 report_mouse_t pmw33xx_get_report(report_mouse_t mouse_report) {
    252     pmw33xx_report_t report    = pmw33xx_read_burst(0);
    253     static bool      in_motion = false;
    254 
    255     if (report.motion.b.is_lifted) {
    256         return mouse_report;
    257     }
    258 
    259     if (!report.motion.b.is_motion) {
    260         in_motion = false;
    261         return mouse_report;
    262     }
    263 
    264     if (!in_motion) {
    265         in_motion = true;
    266         pd_dprintf("PWM3360 (0): starting motion\n");
    267     }
    268 
    269     mouse_report.x = CONSTRAIN_HID_XY(report.delta_x);
    270     mouse_report.y = CONSTRAIN_HID_XY(report.delta_y);
    271     return mouse_report;
    272 }