aboutsummaryrefslogtreecommitdiff
path: root/keyboards/ploopyco/pmw3600.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyboards/ploopyco/pmw3600.c')
-rw-r--r--keyboards/ploopyco/pmw3600.c222
1 files changed, 222 insertions, 0 deletions
diff --git a/keyboards/ploopyco/pmw3600.c b/keyboards/ploopyco/pmw3600.c
new file mode 100644
index 000000000..93b47078a
--- /dev/null
+++ b/keyboards/ploopyco/pmw3600.c
@@ -0,0 +1,222 @@
1/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
2 * Copyright 2019 Sunjun Kim
3 * Copyright 2020 Ploopy Corporation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20#include "pmw3600.h"
21#include "pmw3600_firmware.h"
22#ifdef CONSOLE_ENABLE
23# include "print.h"
24#endif
25bool _inBurst = false;
26
27#ifndef PMW_CPI
28# define PMW_CPI 1600
29#endif
30#ifndef SPI_DIVISOR
31# define SPI_DIVISOR 2
32#endif
33
34static const int8_t ROTATIONAL_TRANSFORM_ANGLE = 20;
35
36#ifdef CONSOLE_ENABLE
37void print_byte(uint8_t byte) { dprintf("%c%c%c%c%c%c%c%c|", (byte & 0x80 ? '1' : '0'), (byte & 0x40 ? '1' : '0'), (byte & 0x20 ? '1' : '0'), (byte & 0x10 ? '1' : '0'), (byte & 0x08 ? '1' : '0'), (byte & 0x04 ? '1' : '0'), (byte & 0x02 ? '1' : '0'), (byte & 0x01 ? '1' : '0')); }
38#endif
39
40
41bool spi_start_adv(void) {
42 bool status = spi_start(SPI_SS_PIN, false, 3, SPI_DIVISOR);
43 wait_us(1);
44 return status;
45}
46
47void spi_stop_adv(void) {
48 wait_us(1);
49 spi_stop();
50}
51
52spi_status_t spi_write_adv(uint8_t reg_addr, uint8_t data) {
53 if (reg_addr != REG_Motion_Burst) {
54 _inBurst = false;
55 }
56
57 spi_start_adv();
58 // send address of the register, with MSBit = 1 to indicate it's a write
59 spi_status_t status = spi_write(reg_addr | 0x80);
60 status = spi_write(data);
61
62 // tSCLK-NCS for write operation
63 wait_us(20);
64
65 // tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound
66 wait_us(100);
67 spi_stop();
68 return status;
69}
70
71uint8_t spi_read_adv(uint8_t reg_addr) {
72 spi_start_adv();
73 // send adress of the register, with MSBit = 0 to indicate it's a read
74 spi_write(reg_addr & 0x7f);
75
76 uint8_t data = spi_read();
77
78 // tSCLK-NCS for read operation is 120ns
79 wait_us(1);
80
81 // tSRW/tSRR (=20us) minus tSCLK-NCS
82 wait_us(19);
83
84 spi_stop();
85 return data;
86}
87
88void pmw_set_cpi(uint16_t cpi) {
89 int cpival = constrain((cpi / 100) - 1, 0, 0x77); // limits to 0--119
90
91 spi_start_adv();
92 spi_write_adv(REG_Config1, cpival);
93 spi_stop();
94}
95
96bool pmw_spi_init(void) {
97 spi_init();
98 _inBurst = false;
99
100 spi_stop();
101 spi_start_adv();
102 spi_stop();
103
104 spi_write_adv(REG_Shutdown, 0xb6); // Shutdown first
105 wait_ms(300);
106
107 spi_start_adv();
108 wait_us(40);
109 spi_stop_adv();
110 wait_us(40);
111
112 spi_write_adv(REG_Power_Up_Reset, 0x5a);
113 wait_ms(50);
114
115 spi_read_adv(REG_Motion);
116 spi_read_adv(REG_Delta_X_L);
117 spi_read_adv(REG_Delta_X_H);
118 spi_read_adv(REG_Delta_Y_L);
119 spi_read_adv(REG_Delta_Y_H);
120
121 pmw_upload_firmware();
122
123 spi_write_adv(REG_Angle_Tune, constrain(ROTATIONAL_TRANSFORM_ANGLE, -30, 30));
124 spi_stop_adv();
125
126 wait_ms(10);
127 pmw_set_cpi(PMW_CPI);
128
129 wait_ms(1);
130
131 return pmw_check_signature();
132}
133
134void pmw_upload_firmware(void) {
135 spi_write_adv(REG_Config2, 0x00);
136
137 spi_write_adv(REG_SROM_Enable, 0x1d);
138
139 wait_ms(10);
140
141 spi_write_adv(REG_SROM_Enable, 0x18);
142
143 spi_start_adv();
144 spi_write(REG_SROM_Load_Burst | 0x80);
145 wait_us(15);
146
147 unsigned char c;
148 for (int i = 0; i < firmware_length; i++) {
149 c = (unsigned char)pgm_read_byte(firmware_data + i);
150 spi_write(c);
151 wait_us(15);
152 }
153 wait_us(200);
154
155 spi_read_adv(REG_SROM_ID);
156
157 spi_write_adv(REG_Config2, 0x00);
158
159 spi_stop();
160 wait_ms(10);
161}
162
163bool pmw_check_signature(void) {
164 uint8_t pid = spi_read_adv(REG_Product_ID);
165 uint8_t iv_pid = spi_read_adv(REG_Inverse_Product_ID);
166 uint8_t SROM_ver = spi_read_adv(REG_SROM_ID);
167 return (pid == 0x42 && iv_pid == 0xBD && SROM_ver == 0x04); // signature for SROM 0x04
168}
169
170report_pmw_t pmw_read_burst(void) {
171 if (!_inBurst) {
172#ifdef CONSOLE_ENABLE
173 dprintf("burst on");
174#endif
175 spi_write_adv(REG_Motion_Burst, 0x00);
176 _inBurst = true;
177 }
178
179 spi_start_adv();
180 spi_write(REG_Motion_Burst);
181 wait_us(35); // waits for tSRAD
182
183 report_pmw_t data;
184 data.motion = 0;
185 data.dx = 0;
186 data.mdx = 0;
187 data.dy = 0;
188 data.mdx = 0;
189
190 data.motion = spi_read();
191 spi_write(0x00); // skip Observation
192 data.dx = spi_read();
193 data.mdx = spi_read();
194 data.dy = spi_read();
195 data.mdy = spi_read();
196
197 spi_stop();
198
199#ifdef CONSOLE_ENABLE
200 print_byte(data.motion);
201 print_byte(data.dx);
202 print_byte(data.mdx);
203 print_byte(data.dy);
204 print_byte(data.mdy);
205 dprintf("\n");
206#endif
207
208 data.isMotion = (data.motion & 0x80) != 0;
209 data.isOnSurface = (data.motion & 0x08) == 0;
210 data.dx |= (data.mdx << 8);
211 data.dx = data.dx * -1;
212 data.dy |= (data.mdy << 8);
213 // data.dy = data.dy * -1;
214
215 spi_stop();
216
217 if (data.motion & 0b111) { // panic recovery, sometimes burst mode works weird.
218 _inBurst = false;
219 }
220
221 return data;
222}