paw3222.c (4890B)
1 /* Copyright 2024 Colin Lam (Ploopy Corporation) 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 "paw3222.h" 18 #include "wait.h" 19 #include "gpio.h" 20 #include "spi_master.h" 21 #include "pointing_device_internal.h" 22 23 #define MSB1 0x80 24 #define MSB0 0x7F 25 26 const pointing_device_driver_t paw3222_pointing_device_driver = { 27 .init = paw3222_init, 28 .get_report = paw3222_get_report, 29 .set_cpi = paw3222_set_cpi, 30 .get_cpi = paw3222_get_cpi, 31 }; 32 33 // Convert a 12-bit twos complement binary-represented number into a 34 // signed 12-bit integer. 35 static int16_t convert_twoscomp_12(uint16_t data) { 36 if ((data & 0x800) == 0x800) 37 return -2048 + (data & 0x7FF); 38 else 39 return data; 40 } 41 42 void paw3222_write(uint8_t reg_addr, uint8_t data) { 43 spi_start(PAW3222_CS_PIN, false, 3, PAW3222_SPI_DIVISOR); 44 wait_us(1); // Tncs_lead 45 spi_write(reg_addr | MSB1); 46 spi_write(data); 47 wait_us(1); // Tncs_lag 48 spi_stop(); 49 } 50 51 uint8_t paw3222_read(uint8_t reg_addr) { 52 spi_start(PAW3222_CS_PIN, false, 3, PAW3222_SPI_DIVISOR); 53 wait_us(1); // Tncs_lead 54 spi_write(reg_addr & MSB0); 55 wait_us(10); // Tprep_rd 56 uint8_t data = spi_read(); 57 wait_us(1); // Tncs_lag 58 spi_stop(); 59 60 return data; 61 } 62 63 bool paw3222_init(void) { 64 gpio_set_pin_output(PAW3222_CS_PIN); 65 66 // CS must be kept low at power-up stage for at least 1ms 67 gpio_write_pin_low(PAW3222_CS_PIN); 68 wait_ms(10); 69 gpio_write_pin_high(PAW3222_CS_PIN); 70 71 spi_init(); 72 73 // reboot 74 paw3222_write(0x06, 0x80); 75 wait_ms(50); 76 77 if (!paw3222_check_signature()) { 78 return false; 79 } 80 81 // initialize 82 paw3222_write(0x09, 0x5A); 83 paw3222_write(0x0D, 0x23); 84 paw3222_write(0x0E, 0x24); 85 paw3222_write(0x19, 0x1C); 86 paw3222_write(0x05, 0xA1); 87 paw3222_write(0x2B, 0x6D); 88 paw3222_write(0x30, 0x2E); 89 paw3222_write(0x5C, 0xDF); 90 paw3222_write(0x7F, 0x01); 91 paw3222_write(0x06, 0x14); 92 paw3222_write(0x31, 0x25); 93 paw3222_write(0x34, 0xC4); 94 paw3222_write(0x36, 0xCC); 95 paw3222_write(0x37, 0x42); 96 paw3222_write(0x38, 0x01); 97 paw3222_write(0x3A, 0x76); 98 paw3222_write(0x3B, 0x34); 99 paw3222_write(0x42, 0x39); 100 paw3222_write(0x43, 0xF2); 101 paw3222_write(0x44, 0x39); 102 paw3222_write(0x45, 0xF0); 103 paw3222_write(0x46, 0x12); 104 paw3222_write(0x47, 0x39); 105 paw3222_write(0x48, 0xE3); 106 paw3222_write(0x49, 0x48); 107 paw3222_write(0x4A, 0xD3); 108 paw3222_write(0x4B, 0x98); 109 paw3222_write(0x64, 0x46); 110 paw3222_write(0x71, 0x28); 111 paw3222_write(0x72, 0x28); 112 paw3222_write(0x7F, 0x00); 113 paw3222_write(0x09, 0x00); 114 115 // read a burst, then discard 116 paw3222_read_burst(); 117 118 return true; 119 } 120 121 report_paw3222_t paw3222_read_burst(void) { 122 report_paw3222_t report = {0}; 123 124 uint8_t motion = paw3222_read(0x02); 125 if ((motion & MSB1) == MSB1) { 126 // Motion detected 127 uint16_t dx = (uint16_t)paw3222_read(0x03); 128 uint16_t dy = (uint16_t)paw3222_read(0x04); 129 uint16_t dxy_hi = (uint16_t)paw3222_read(0x12); 130 131 dx = dx | ((dxy_hi & 0xF0) << 4); 132 dy = dy | ((dxy_hi & 0x0F) << 8); 133 134 report.dx = convert_twoscomp_12(dx); 135 report.dy = convert_twoscomp_12(dy); 136 } 137 138 return report; 139 } 140 141 void paw3222_set_cpi(uint16_t cpi) { 142 uint16_t cpival = (cpi) < (480) ? (480) : ((cpi) > (4020) ? (4020) : (cpi)); 143 uint8_t cpival_x = (cpival + (30 / 2)) / 30; 144 uint8_t cpival_y = (cpival + (29 / 2)) / 29; 145 146 paw3222_write(0x09, 0x5A); 147 paw3222_write(0x0D, cpival_x); 148 paw3222_write(0x0E, cpival_y); 149 paw3222_write(0x09, 0x00); 150 } 151 152 uint16_t paw3222_get_cpi(void) { 153 uint8_t cpival = paw3222_read(0x0D); 154 return (uint16_t)(cpival * 30); 155 } 156 157 report_mouse_t paw3222_get_report(report_mouse_t mouse_report) { 158 report_paw3222_t data = paw3222_read_burst(); 159 160 if (data.dx != 0 || data.dy != 0) { 161 pd_dprintf("Raw ] X: %d, Y: %d\n", data.dx, data.dy); 162 mouse_report.x = CONSTRAIN_HID_XY(data.dx); 163 mouse_report.y = CONSTRAIN_HID_XY(data.dy); 164 } 165 166 return mouse_report; 167 } 168 169 bool paw3222_check_signature(void) { 170 uint8_t checkval_1 = paw3222_read(0x00); 171 uint8_t checkval_2 = paw3222_read(0x01); 172 173 return (checkval_1 == 0x30 && checkval_2 == 0x02); 174 }