logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

pmw33xx_common.c (7211B)


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