qmk_firmware

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

adns9800.c (7793B)


      1 /* Copyright 2020 Alexander Tulloh
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 2 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     15  */
     16 
     17 #include "spi_master.h"
     18 #include "adns9800.h"
     19 #include "wait.h"
     20 
     21 // registers
     22 // clang-format off
     23 #define REG_Product_ID                   0x00
     24 #define REG_Revision_ID                  0x01
     25 #define REG_Motion                       0x02
     26 #define REG_Delta_X_L                    0x03
     27 #define REG_Delta_X_H                    0x04
     28 #define REG_Delta_Y_L                    0x05
     29 #define REG_Delta_Y_H                    0x06
     30 #define REG_SQUAL                        0x07
     31 #define REG_Pixel_Sum                    0x08
     32 #define REG_Maximum_Pixel                0x09
     33 #define REG_Minimum_Pixel                0x0a
     34 #define REG_Shutter_Lower                0x0b
     35 #define REG_Shutter_Upper                0x0c
     36 #define REG_Frame_Period_Lower           0x0d
     37 #define REG_Frame_Period_Upper           0x0e
     38 #define REG_Configuration_I              0x0f
     39 #define REG_Configuration_II             0x10
     40 #define REG_Frame_Capture                0x12
     41 #define REG_SROM_Enable                  0x13
     42 #define REG_Run_Downshift                0x14
     43 #define REG_Rest1_Rate                   0x15
     44 #define REG_Rest1_Downshift              0x16
     45 #define REG_Rest2_Rate                   0x17
     46 #define REG_Rest2_Downshift              0x18
     47 #define REG_Rest3_Rate                   0x19
     48 #define REG_Frame_Period_Max_Bound_Lower 0x1a
     49 #define REG_Frame_Period_Max_Bound_Upper 0x1b
     50 #define REG_Frame_Period_Min_Bound_Lower 0x1c
     51 #define REG_Frame_Period_Min_Bound_Upper 0x1d
     52 #define REG_Shutter_Max_Bound_Lower      0x1e
     53 #define REG_Shutter_Max_Bound_Upper      0x1f
     54 #define REG_LASER_CTRL0                  0x20
     55 #define REG_Observation                  0x24
     56 #define REG_Data_Out_Lower               0x25
     57 #define REG_Data_Out_Upper               0x26
     58 #define REG_SROM_ID                      0x2a
     59 #define REG_Lift_Detection_Thr           0x2e
     60 #define REG_Configuration_V              0x2f
     61 #define REG_Configuration_IV             0x39
     62 #define REG_Power_Up_Reset               0x3a
     63 #define REG_Shutdown                     0x3b
     64 #define REG_Inverse_Product_ID           0x3f
     65 #define REG_Motion_Burst                 0x50
     66 #define REG_SROM_Load_Burst              0x62
     67 #define REG_Pixel_Burst                  0x64
     68 
     69 #define MIN_CPI             200
     70 #define MAX_CPI             8200
     71 #define CPI_STEP            200
     72 #define CLAMP_CPI(value)    value<MIN_CPI ? MIN_CPI : value> MAX_CPI ? MAX_CPI : value
     73 #define US_BETWEEN_WRITES   120
     74 #define US_BETWEEN_READS    20
     75 #define US_DELAY_AFTER_ADDR 100
     76 #define US_BEFORE_MOTION    100
     77 #define MSB1                0x80
     78 // clang-format on
     79 
     80 const pointing_device_driver_t adns9800_pointing_device_driver = {
     81     .init       = adns9800_init,
     82     .get_report = adns9800_get_report_driver,
     83     .set_cpi    = adns9800_set_cpi,
     84     .get_cpi    = adns9800_get_cpi,
     85 };
     86 
     87 uint16_t __attribute__((weak)) adns9800_srom_get_length(void) {
     88     return 0;
     89 }
     90 
     91 uint8_t __attribute__((weak)) adns9800_srom_get_byte(uint16_t position) {
     92     return 0;
     93 }
     94 
     95 void adns9800_spi_start(void) {
     96     spi_start(ADNS9800_CS_PIN, false, ADNS9800_SPI_MODE, ADNS9800_SPI_DIVISOR);
     97 }
     98 
     99 void adns9800_write(uint8_t reg_addr, uint8_t data) {
    100     adns9800_spi_start();
    101     spi_write(reg_addr | MSB1);
    102     spi_write(data);
    103     spi_stop();
    104     wait_us(US_BETWEEN_WRITES);
    105 }
    106 
    107 uint8_t adns9800_read(uint8_t reg_addr) {
    108     adns9800_spi_start();
    109     spi_write(reg_addr & 0x7f);
    110     wait_us(US_DELAY_AFTER_ADDR);
    111     uint8_t data = spi_read();
    112     spi_stop();
    113     wait_us(US_BETWEEN_READS);
    114 
    115     return data;
    116 }
    117 
    118 bool __attribute__((weak)) adns9800_check_signature(void) {
    119     if (adns9800_read(REG_Product_ID) != 0x33) {
    120         return false;
    121     }
    122     return true;
    123 }
    124 
    125 bool adns9800_init(void) {
    126     gpio_set_pin_output(ADNS9800_CS_PIN);
    127 
    128     spi_init();
    129 
    130     // reboot
    131     adns9800_write(REG_Power_Up_Reset, 0x5a);
    132     wait_ms(50);
    133 
    134     // read registers and discard
    135     adns9800_read(REG_Motion);
    136     adns9800_read(REG_Delta_X_L);
    137     adns9800_read(REG_Delta_X_H);
    138     adns9800_read(REG_Delta_Y_L);
    139     adns9800_read(REG_Delta_Y_H);
    140 
    141     if (adns9800_srom_get_length() != 0) {
    142         // upload firmware
    143 
    144         // 3k firmware mode
    145         adns9800_write(REG_Configuration_IV, 0x02);
    146 
    147         // enable initialisation
    148         adns9800_write(REG_SROM_Enable, 0x1d);
    149 
    150         // wait a frame
    151         wait_ms(10);
    152 
    153         // start SROM download
    154         adns9800_write(REG_SROM_Enable, 0x18);
    155 
    156         // write the SROM file
    157 
    158         adns9800_spi_start();
    159 
    160         spi_write(REG_SROM_Load_Burst | 0x80);
    161         wait_us(15);
    162 
    163         // send all bytes of the firmware
    164         for (uint16_t i = 0; i < adns9800_srom_get_length(); i++) {
    165             spi_write(adns9800_srom_get_byte(i));
    166             wait_us(15);
    167         }
    168 
    169         spi_stop();
    170 
    171         wait_ms(10);
    172     } else {
    173         // write reset value to REG_Configuration_IV
    174         adns9800_write(REG_Configuration_IV, 0x0);
    175 
    176         // write reset value to REG_SROM_Enable
    177         adns9800_write(REG_SROM_Enable, 0x0);
    178 
    179         // wait a frame
    180         wait_ms(10);
    181     }
    182 
    183     // enable laser
    184     uint8_t laser_ctrl0 = adns9800_read(REG_LASER_CTRL0);
    185     adns9800_write(REG_LASER_CTRL0, laser_ctrl0 & 0xf0);
    186 
    187     adns9800_set_cpi(ADNS9800_CPI);
    188 
    189     return adns9800_check_signature();
    190 }
    191 
    192 config_adns9800_t adns9800_get_config(void) {
    193     uint8_t cpival = adns9800_read(REG_Configuration_I);
    194     return (config_adns9800_t){(cpival & 0xFF) * CPI_STEP};
    195 }
    196 
    197 void adns9800_set_config(config_adns9800_t config) {
    198     uint8_t config_1 = (CLAMP_CPI(config.cpi) / CPI_STEP) & 0xFF;
    199     adns9800_write(REG_Configuration_I, config_1);
    200 }
    201 
    202 uint16_t adns9800_get_cpi(void) {
    203     uint8_t cpival = adns9800_read(REG_Configuration_I);
    204     return (uint16_t)(cpival & 0xFF) * CPI_STEP;
    205 }
    206 
    207 void adns9800_set_cpi(uint16_t cpi) {
    208     uint8_t config_1 = (CLAMP_CPI(cpi) / CPI_STEP) & 0xFF;
    209     adns9800_write(REG_Configuration_I, config_1);
    210 }
    211 
    212 static int16_t convertDeltaToInt(uint8_t high, uint8_t low) {
    213     // join bytes into twos compliment
    214     uint16_t twos_comp = (high << 8) | low;
    215 
    216     // convert twos comp to int
    217     if (twos_comp & 0x8000) return -1 * (~twos_comp + 1);
    218 
    219     return twos_comp;
    220 }
    221 
    222 report_adns9800_t adns9800_get_report(void) {
    223     report_adns9800_t report = {0};
    224 
    225     adns9800_spi_start();
    226 
    227     // start burst mode
    228     spi_write(REG_Motion_Burst & 0x7f);
    229 
    230     wait_us(US_BEFORE_MOTION);
    231 
    232     uint8_t motion = spi_read();
    233 
    234     if (motion & 0x80) {
    235         // clear observation register
    236         spi_read();
    237 
    238         // delta registers
    239         uint8_t delta_x_l = spi_read();
    240         uint8_t delta_x_h = spi_read();
    241         uint8_t delta_y_l = spi_read();
    242         uint8_t delta_y_h = spi_read();
    243 
    244         report.x = convertDeltaToInt(delta_x_h, delta_x_l);
    245         report.y = convertDeltaToInt(delta_y_h, delta_y_l);
    246     }
    247 
    248     // clear residual motion
    249     spi_write(REG_Motion & 0x7f);
    250 
    251     spi_stop();
    252 
    253     return report;
    254 }
    255 
    256 report_mouse_t adns9800_get_report_driver(report_mouse_t mouse_report) {
    257     report_adns9800_t sensor_report = adns9800_get_report();
    258 
    259     mouse_report.x = CONSTRAIN_HID_XY(sensor_report.x);
    260     mouse_report.y = CONSTRAIN_HID_XY(sensor_report.y);
    261 
    262     return mouse_report;
    263 }