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 }