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 }