Commit | Line | Data |
---|---|---|
332fa7c4 PA |
1 | /* Copyright 2020 Purdea Andrei |
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 "quantum.h" | |
38e6feca PA |
18 | #include "matrix_manipulate.h" |
19 | #include <string.h> | |
332fa7c4 | 20 | |
1e61ab07 PA |
21 | /* Notes on Expansion Header: |
22 | ||
23 | Pinout: | |
24 | --1--O O--2-- | |
25 | --3--O O--4-- | |
26 | --5--O O--6-- | |
27 | ||
28 | Pin 1 is always VCC | |
29 | Pin 6 in always GND | |
30 | ||
31 | When using xwhatsit's solenoid controller board, | |
32 | pin 2 is connected to the ENABLE input of the current | |
33 | limiter, and pin 4 drives the solenoid. | |
34 | ||
35 | On original xwhatsit controllers: | |
36 | pin 2 = PB7 | |
37 | pin 3 = PB4 | |
38 | pin 4 = PB6 | |
39 | pin 5 = PB5 | |
40 | ||
41 | On the TH xwhatsit controller: | |
42 | pin 2 = HEADER3 = TXO = PD3 | |
43 | pin 3 = HEADER1 = D(igital)6 = PD7 | |
44 | pin 4 = HEADER4 = RXI = PD2 | |
45 | pin 5 = HEADER2 = D(igital)7 = PE6 | |
46 | */ | |
47 | ||
38f3278e | 48 | // USING_SOLENOID_ENABLE_PIN must be defined in config.h if you are using and xwhatsit type solenoid |
1e61ab07 | 49 | |
0162369b | 50 | static inline uint8_t read_rows(void) |
405cc8cf | 51 | { |
3e1298e4 PA |
52 | CAPSENSE_READ_ROWS_LOCAL_VARS; |
53 | asm volatile (CAPSENSE_READ_ROWS_ASM_INSTRUCTIONS : CAPSENSE_READ_ROWS_OUTPUT_CONSTRAINTS : CAPSENSE_READ_ROWS_INPUT_CONSTRAINTS); | |
54 | return CAPSENSE_READ_ROWS_VALUE; | |
405cc8cf PA |
55 | } |
56 | ||
d22c0016 PA |
57 | #if defined(CAPSENSE_DAC_MCP4921) |
58 | ||
59 | void dac_init(void) | |
60 | { | |
61 | writePin(CAPSENSE_DAC_NCS, 1); | |
62 | setPinOutput(CAPSENSE_DAC_NCS); | |
63 | setPinOutput(CAPSENSE_DAC_SCK); | |
64 | setPinOutput(CAPSENSE_DAC_SDI); | |
65 | writePin(CAPSENSE_DAC_NCS, 1); | |
66 | writePin(CAPSENSE_DAC_SCK, 0); | |
67 | writePin(CAPSENSE_DAC_SDI, 0); | |
68 | } | |
69 | ||
70 | void dac_write_threshold(uint16_t value) | |
71 | { | |
72 | const uint16_t buffered = 0; | |
73 | #define nSHDN_BIT 12 | |
74 | value |= 1 << nSHDN_BIT; // nSHDN = 0 -- make sure output is not floating. | |
75 | #define MCP_DAC_GAIN_2X 0 | |
76 | #define MCP_DAC_GAIN_1X 1 | |
77 | #define nGA_BIT 13 | |
78 | value |= MCP_DAC_GAIN_1X << nGA_BIT; | |
79 | #define BUF_BIT 14; | |
80 | value |= buffered << BUF_BIT; | |
81 | ||
82 | writePin(CAPSENSE_DAC_NCS, 0); | |
83 | int i; | |
84 | for (i=0;i<16;i++) | |
85 | { | |
86 | writePin(CAPSENSE_DAC_SDI, (value >> 15) & 1); | |
87 | value <<= 1; | |
88 | writePin(CAPSENSE_DAC_SCK, 1); | |
89 | writePin(CAPSENSE_DAC_SCK, 0); | |
90 | } | |
91 | writePin(CAPSENSE_DAC_NCS, 1); | |
92 | wait_us(CAPSENSE_DAC_SETTLE_TIME_US); | |
93 | } | |
94 | ||
95 | #else | |
96 | ||
405cc8cf PA |
97 | void dac_init(void) |
98 | { | |
3e1298e4 PA |
99 | setPinOutput(CAPSENSE_DAC_SCLK); |
100 | setPinOutput(CAPSENSE_DAC_DIN); | |
101 | setPinOutput(CAPSENSE_DAC_SYNC_N); | |
102 | writePin(CAPSENSE_DAC_SYNC_N, 1); | |
103 | writePin(CAPSENSE_DAC_SCLK, 0); | |
104 | writePin(CAPSENSE_DAC_SCLK, 1); | |
105 | writePin(CAPSENSE_DAC_SCLK, 0); | |
405cc8cf PA |
106 | } |
107 | ||
108 | void dac_write_threshold(uint16_t value) | |
109 | { | |
c733dd82 | 110 | value <<= 2; // The two LSB bits of this DAC are don't care. |
3e1298e4 | 111 | writePin(CAPSENSE_DAC_SYNC_N, 0); |
405cc8cf PA |
112 | int i; |
113 | for (i=0;i<16;i++) | |
114 | { | |
3e1298e4 | 115 | writePin(CAPSENSE_DAC_DIN, (value >> 15) & 1); |
405cc8cf | 116 | value <<= 1; |
3e1298e4 PA |
117 | writePin(CAPSENSE_DAC_SCLK, 1); |
118 | writePin(CAPSENSE_DAC_SCLK, 0); | |
405cc8cf | 119 | } |
3e1298e4 PA |
120 | writePin(CAPSENSE_DAC_SYNC_N, 1); |
121 | writePin(CAPSENSE_DAC_SCLK, 1); | |
122 | writePin(CAPSENSE_DAC_SCLK, 0); | |
123 | wait_us(CAPSENSE_DAC_SETTLE_TIME_US); | |
405cc8cf PA |
124 | } |
125 | ||
d22c0016 PA |
126 | #endif |
127 | ||
aa63771d PA |
128 | #define SHIFT_BITS (((CAPSENSE_KEYMAP_COL_TO_PHYSICAL_COL(MATRIX_COLS - 1) >= 16) || \ |
129 | (CAPSENSE_KEYMAP_COL_TO_PHYSICAL_COL(0) >= 16)) ? 24 : 16) | |
f4270f7d | 130 | |
405cc8cf PA |
131 | void shift_select_nothing(void) |
132 | { | |
3e1298e4 | 133 | writePin(CAPSENSE_SHIFT_DIN, 0); |
405cc8cf | 134 | int i; |
f4270f7d | 135 | for (i=0;i<SHIFT_BITS;i++) |
405cc8cf | 136 | { |
3e1298e4 PA |
137 | writePin(CAPSENSE_SHIFT_SHCP, 1); |
138 | writePin(CAPSENSE_SHIFT_SHCP, 0); | |
405cc8cf | 139 | } |
3e1298e4 PA |
140 | writePin(CAPSENSE_SHIFT_STCP, 1); |
141 | writePin(CAPSENSE_SHIFT_STCP, 0); | |
405cc8cf PA |
142 | } |
143 | ||
773b7610 PA |
144 | void shift_data(uint32_t data) |
145 | { | |
146 | int i; | |
147 | for (i=SHIFT_BITS-1; i>=0; i--) | |
148 | { | |
149 | writePin(CAPSENSE_SHIFT_DIN, (data >> (SHIFT_BITS - 1)) & 1); | |
150 | writePin(CAPSENSE_SHIFT_SHCP, 1); | |
151 | writePin(CAPSENSE_SHIFT_SHCP, 0); | |
152 | data <<= 1; | |
153 | } | |
154 | writePin(CAPSENSE_SHIFT_STCP, 1); | |
155 | writePin(CAPSENSE_SHIFT_STCP, 0); | |
156 | } | |
157 | ||
31b3f2de | 158 | void shift_select_col_no_strobe(uint8_t col) |
405cc8cf PA |
159 | { |
160 | int i; | |
f4270f7d | 161 | for (i=SHIFT_BITS-1; i>=0; i--) |
405cc8cf | 162 | { |
3e1298e4 PA |
163 | writePin(CAPSENSE_SHIFT_DIN, !!(col == i)); |
164 | writePin(CAPSENSE_SHIFT_SHCP, 1); | |
165 | writePin(CAPSENSE_SHIFT_SHCP, 0); | |
405cc8cf | 166 | } |
405cc8cf PA |
167 | } |
168 | ||
31b3f2de PA |
169 | static inline void shift_select_col(uint8_t col) |
170 | { | |
171 | shift_select_col_no_strobe(col); | |
3e1298e4 PA |
172 | writePin(CAPSENSE_SHIFT_STCP, 1); |
173 | writePin(CAPSENSE_SHIFT_STCP, 0); | |
31b3f2de PA |
174 | } |
175 | ||
405cc8cf PA |
176 | void shift_init(void) |
177 | { | |
3e1298e4 PA |
178 | setPinOutput(CAPSENSE_SHIFT_DIN); |
179 | setPinOutput(CAPSENSE_SHIFT_OE); | |
180 | setPinOutput(CAPSENSE_SHIFT_STCP); | |
181 | setPinOutput(CAPSENSE_SHIFT_SHCP); | |
182 | writePin(CAPSENSE_SHIFT_STCP, 0); | |
183 | writePin(CAPSENSE_SHIFT_SHCP, 0); | |
405cc8cf | 184 | shift_select_nothing(); |
3e1298e4 | 185 | wait_us(CAPSENSE_KEYBOARD_SETTLE_TIME_US); |
405cc8cf PA |
186 | } |
187 | ||
f3d8013a PA |
188 | // Timing: |
189 | // IN instructions (1 * CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE) | |
190 | // Store to array instructions (2 * number of bytes) | |
191 | // adiw: 1 cycle | |
192 | // cp: 1 cycle | |
193 | // cpc: 1 cycle | |
194 | // brlo: 2 cycles (when jumping) | |
195 | // --- Total loop length: | |
196 | // 3 * CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE + 5 cycles | |
197 | // first sample elements will be taken after [1..CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE-1] cycles | |
198 | // second sample elements will be taken after | |
199 | // [3 * CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE + 5 + 1.. | |
200 | // 3 * CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE + 5 + CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE-1] cycles | |
201 | ||
202 | // the following function requires storage for CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE * (time + 1) bytes | |
31b3f2de PA |
203 | // but returns valid data only in the first (time + 1) bytes |
204 | void test_multiple(uint8_t col, uint16_t time, uint8_t *array) | |
205 | { | |
206 | shift_select_col_no_strobe(col); | |
207 | uint16_t index; | |
3e1298e4 | 208 | CAPSENSE_READ_ROWS_LOCAL_VARS; |
31b3f2de PA |
209 | uint8_t *arrayp = array; |
210 | asm volatile ( | |
211 | "ldi %A[index], 0" "\n\t" | |
212 | "ldi %B[index], 0" "\n\t" | |
213 | "cli" "\n\t" | |
214 | "sbi %[stcp_regaddr], %[stcp_bit]" "\n\t" | |
f3d8013a PA |
215 | "1:" CAPSENSE_READ_ROWS_ASM_INSTRUCTIONS "\n\t" |
216 | CAPSENSE_READ_ROWS_STORE_TO_ARRAY_INSTRUCTIONS "\n\t" | |
31b3f2de PA |
217 | "adiw %A[index], 0x01" "\n\t" |
218 | "cp %A[index], %A[time]" "\n\t" | |
219 | "cpc %B[index], %B[time]" "\n\t" | |
220 | "brlo 1b" "\n\t" | |
221 | "sei" "\n\t" | |
222 | "cbi %[stcp_regaddr], %[stcp_bit]" "\n\t" | |
223 | : [arr] "=e" (arrayp), | |
224 | [index] "=&w" (index), | |
3e1298e4 | 225 | CAPSENSE_READ_ROWS_OUTPUT_CONSTRAINTS |
31b3f2de | 226 | : [time] "r" (time + 1), |
3e1298e4 PA |
227 | [stcp_regaddr] "I" (CAPSENSE_SHIFT_STCP_IO), |
228 | [stcp_bit] "I" (CAPSENSE_SHIFT_STCP_BIT), | |
229 | CAPSENSE_READ_ROWS_INPUT_CONSTRAINTS, | |
31b3f2de PA |
230 | "0" (arrayp) |
231 | : "memory" ); | |
232 | uint16_t i, p0, p1; | |
233 | p0 = p1 = 0; | |
234 | for (i=0; i<=time; i++) | |
235 | { | |
f3d8013a | 236 | CAPSENSE_READ_ROWS_EXTRACT_FROM_ARRAY; |
3e1298e4 | 237 | array[p1++] = CAPSENSE_READ_ROWS_VALUE; |
31b3f2de | 238 | } |
f2a03868 | 239 | shift_select_nothing(); |
3e1298e4 | 240 | wait_us(CAPSENSE_KEYBOARD_SETTLE_TIME_US); |
31b3f2de PA |
241 | } |
242 | ||
23d3309b | 243 | uint8_t test_single(uint8_t col, uint16_t time, uint8_t *interference_ptr) |
31b3f2de PA |
244 | { |
245 | shift_select_col_no_strobe(col); | |
246 | uint16_t index; | |
3e1298e4 | 247 | CAPSENSE_READ_ROWS_LOCAL_VARS; |
23d3309b PA |
248 | uint8_t array[CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE + 1]; // one sample before triggering, and one dummy byte |
249 | uint8_t *arrayp = array; | |
31b3f2de PA |
250 | asm volatile ( |
251 | "ldi %A[index], 0" "\n\t" | |
252 | "ldi %B[index], 0" "\n\t" | |
253 | "cli" "\n\t" | |
23d3309b PA |
254 | CAPSENSE_READ_ROWS_ASM_INSTRUCTIONS "\n\t" |
255 | CAPSENSE_READ_ROWS_STORE_TO_ARRAY_INSTRUCTIONS "\n\t" | |
31b3f2de | 256 | "sbi %[stcp_regaddr], %[stcp_bit]" "\n\t" |
f3d8013a PA |
257 | "1:" CAPSENSE_READ_ROWS_ASM_INSTRUCTIONS "\n\t" |
258 | CAPSENSE_READ_ROWS_STORE_TO_ARRAY_INSTRUCTIONS_FAKE "\n\t" | |
31b3f2de PA |
259 | "adiw %A[index], 0x01" "\n\t" |
260 | "cp %A[index], %A[time]" "\n\t" | |
261 | "cpc %B[index], %B[time]" "\n\t" | |
262 | "brlo 1b" "\n\t" | |
263 | "sei" "\n\t" | |
264 | "cbi %[stcp_regaddr], %[stcp_bit]" "\n\t" | |
265 | : [arr] "=e" (arrayp), | |
266 | [index] "=&w" (index), | |
3e1298e4 | 267 | CAPSENSE_READ_ROWS_OUTPUT_CONSTRAINTS |
31b3f2de | 268 | : [time] "r" (time + 1), |
3e1298e4 PA |
269 | [stcp_regaddr] "I" (CAPSENSE_SHIFT_STCP_IO), |
270 | [stcp_bit] "I" (CAPSENSE_SHIFT_STCP_BIT), | |
271 | CAPSENSE_READ_ROWS_INPUT_CONSTRAINTS, | |
31b3f2de PA |
272 | "0" (arrayp) |
273 | : "memory" ); | |
f2a03868 | 274 | shift_select_nothing(); |
3e1298e4 | 275 | wait_us(CAPSENSE_KEYBOARD_SETTLE_TIME_US); |
23d3309b PA |
276 | uint8_t value_at_time = CAPSENSE_READ_ROWS_VALUE; |
277 | if (interference_ptr) | |
278 | { | |
279 | uint16_t p0 = 0; | |
280 | CAPSENSE_READ_ROWS_EXTRACT_FROM_ARRAY; | |
281 | uint8_t interference = CAPSENSE_READ_ROWS_VALUE; | |
282 | *interference_ptr = interference; | |
283 | } | |
284 | return value_at_time; | |
31b3f2de | 285 | } |
405cc8cf | 286 | |
38e6feca | 287 | #ifndef NO_PRINT |
ced94ac4 | 288 | #define NRTIMES 64 |
f2a03868 PA |
289 | #define TESTATONCE 8 |
290 | #define REPS_V2 15 | |
291 | void test_col_print_data_v2(uint8_t col) | |
292 | { | |
293 | uprintf("%d: ", col); | |
f3d8013a | 294 | static uint8_t data[NRTIMES*CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE]; |
4addcae8 | 295 | static uint8_t sums[(TESTATONCE+1) * MATRIX_ROWS]; |
f2a03868 PA |
296 | uint8_t to_time = NRTIMES-1; |
297 | uint8_t from_time = 0; | |
298 | while (from_time<NRTIMES-1) | |
299 | { | |
300 | if (to_time - from_time + 1 > TESTATONCE) | |
301 | { | |
302 | to_time = from_time + TESTATONCE - 1; | |
303 | } | |
304 | uint8_t curr_TESTATONCE = to_time - from_time + 1; | |
305 | uint8_t i; | |
306 | for (i=0;i<(sizeof(sums)/sizeof(sums[0]));i++) | |
307 | { | |
308 | sums[i] = 0; | |
309 | } | |
310 | for (i=0;i<REPS_V2;i++) | |
311 | { | |
312 | uint8_t st = read_rows(); | |
313 | test_multiple(col, to_time, data); | |
314 | uint8_t j; | |
315 | uint8_t ii = 0; | |
316 | uint8_t k; | |
317 | for (j=0;j<curr_TESTATONCE;j++) | |
318 | { | |
319 | uint8_t dataj = data[j + from_time]; | |
4addcae8 | 320 | for (k=0; k<MATRIX_ROWS;k++) |
f2a03868 PA |
321 | { |
322 | sums[ii] += (dataj & 1); | |
323 | dataj >>= 1; | |
324 | ii += 1; | |
325 | } | |
326 | } | |
327 | if (from_time == 0) { | |
4addcae8 PA |
328 | ii = TESTATONCE * MATRIX_ROWS; |
329 | for (k=0; k<MATRIX_ROWS;k++) | |
f2a03868 PA |
330 | { |
331 | sums[ii] += (st & 1); | |
332 | st >>= 1; | |
333 | ii += 1; | |
334 | } | |
335 | } | |
336 | } | |
337 | if (from_time == 0) { | |
4addcae8 | 338 | for (i=TESTATONCE*MATRIX_ROWS;i<(TESTATONCE+1)*MATRIX_ROWS;i++) { |
f2a03868 PA |
339 | if (sums[i] > 0xf) { |
340 | print("?"); | |
341 | } else { | |
342 | uprintf("%X", sums[i]); | |
343 | } | |
344 | } | |
345 | print(":"); | |
346 | } | |
4addcae8 | 347 | for (i=0;i<curr_TESTATONCE*MATRIX_ROWS;i++) |
f2a03868 PA |
348 | { |
349 | if (sums[i] > 0xf) { | |
350 | print("?"); | |
351 | } else { | |
352 | uprintf("%X", sums[i]); | |
353 | } | |
354 | } | |
355 | from_time = to_time + 1; | |
356 | to_time = NRTIMES - 1; | |
357 | } | |
358 | print("\n"); | |
359 | } | |
38e6feca | 360 | #endif |
f2a03868 | 361 | |
38e6feca | 362 | #ifndef NO_PRINT |
f2a03868 PA |
363 | void test_v2(void) { |
364 | int i; | |
365 | for (i=7;i>0;i--) { | |
366 | uprintf("Starting test in %d\n", i); | |
367 | wait_ms(1000); | |
368 | } | |
369 | uprintf("shift_init()"); | |
370 | shift_init(); | |
371 | uprintf(" DONE\n"); | |
372 | uprintf("dac_init()"); | |
373 | dac_init(); | |
374 | uprintf(" DONE\n"); | |
375 | int d; | |
376 | for (d=90;d<=260;d++) | |
377 | { | |
378 | uprintf("Testing threshold: %d\n", d); | |
379 | dac_write_threshold(d); | |
380 | #if 1 | |
381 | int c; | |
4addcae8 | 382 | for (c=0; c<MATRIX_COLS;c++) |
f2a03868 | 383 | { |
4addcae8 | 384 | test_col_print_data_v2(CAPSENSE_KEYMAP_COL_TO_PHYSICAL_COL(c)); |
f2a03868 | 385 | } |
f2a03868 PA |
386 | #else |
387 | test_col_print_data_v2(0); | |
388 | test_col_print_data_v2(2); | |
389 | test_col_print_data_v2(6); | |
390 | test_col_print_data_v2(7); | |
391 | test_col_print_data_v2(15); | |
392 | #endif | |
393 | } | |
394 | uprintf("TEST DONE\n"); | |
395 | while(1); | |
396 | } | |
38e6feca | 397 | #endif |
f2a03868 | 398 | |
1e57a998 PA |
399 | #define TRACKING_TEST_TIME 4 |
400 | // Key 1 is the always non-pressed key under the space bar to the right. | |
401 | #define TRACKING_KEY_1_COL 6 | |
402 | #define TRACKING_KEY_1_ROW 4 | |
403 | // Key 2 is the always-pressed calibration pad to the far right-bottom of the keyboard. (both on F62 and F77) | |
404 | #define TRACKING_KEY_2_COL 15 | |
405 | #define TRACKING_KEY_2_ROW 6 | |
406 | // Key 3 is the F key | |
407 | #define TRACKING_KEY_3_COL 2 | |
408 | #define TRACKING_KEY_3_ROW 5 | |
409 | // Key 4 is the half of the split backspace that is unused if the user has a normal backspace. | |
410 | #define TRACKING_KEY_4_COL 7 | |
411 | #define TRACKING_KEY_4_ROW 3 | |
412 | // Key 5 is hidden key next to the left shift, which is only used in ISO layouts. | |
413 | #define TRACKING_KEY_5_COL 0 | |
414 | #define TRACKING_KEY_5_ROW 7 | |
415 | ||
416 | #define TRACKING_REPS 16 | |
417 | ||
38e6feca | 418 | uint16_t measure_middle(uint8_t col, uint8_t row, uint8_t time, uint8_t reps) |
1e57a998 | 419 | { |
f3f721b9 | 420 | uint8_t reps_div2 = reps / 2; |
05ffaaf5 | 421 | uint16_t min = 0, max = CAPSENSE_DAC_MAX; |
1e57a998 PA |
422 | while (min < max) |
423 | { | |
424 | uint16_t mid = (min + max) / 2; | |
425 | dac_write_threshold(mid); | |
426 | uint8_t sum = 0; | |
427 | uint8_t i; | |
f3f721b9 | 428 | for (i=0;i<reps;i++) |
1e57a998 | 429 | { |
23d3309b | 430 | sum += (test_single(col, time, NULL) >> row) & 1; |
1e57a998 | 431 | } |
f3f721b9 | 432 | if (sum < reps_div2) |
1e57a998 PA |
433 | { |
434 | max = mid - 1; | |
f3f721b9 | 435 | } else if (sum > reps_div2) { |
1e57a998 PA |
436 | min = mid + 1; |
437 | } else return mid; | |
438 | } | |
439 | return min; | |
440 | } | |
441 | ||
e8583e26 PA |
442 | uint16_t measure_middle_keymap_coords(uint8_t col, uint8_t row, uint8_t time, uint8_t reps) |
443 | { | |
444 | return measure_middle(CAPSENSE_KEYMAP_COL_TO_PHYSICAL_COL(col), CAPSENSE_KEYMAP_ROW_TO_PHYSICAL_ROW(row), time, reps); | |
445 | } | |
446 | ||
38e6feca | 447 | uint16_t measure_middle_settled(uint8_t col, uint8_t row, uint8_t reps) |
123c07cf PA |
448 | { |
449 | uint8_t reps_div2 = reps / 2; | |
05ffaaf5 | 450 | uint16_t min = 0, max = CAPSENSE_DAC_MAX; |
123c07cf PA |
451 | while (min < max) |
452 | { | |
453 | uint16_t mid = (min + max) / 2; | |
454 | dac_write_threshold(mid); | |
455 | uint8_t sum = 0; | |
456 | uint8_t i; | |
457 | for (i=0;i<reps;i++) | |
458 | { | |
459 | sum += (read_rows() >> row) & 1; | |
460 | } | |
461 | if (sum < reps_div2) | |
462 | { | |
463 | max = mid - 1; | |
464 | } else if (sum > reps_div2) { | |
465 | min = mid + 1; | |
466 | } else return mid; | |
467 | } | |
468 | return min; | |
469 | } | |
470 | ||
38e6feca | 471 | #ifndef NO_PRINT |
1e57a998 PA |
472 | void tracking_test(void) |
473 | { | |
474 | int i; | |
475 | for (i=7;i>0;i--) { | |
476 | uprintf("Starting test in %d\n", i); | |
477 | wait_ms(1000); | |
478 | } | |
479 | uprintf("shift_init()"); | |
480 | shift_init(); | |
481 | uprintf(" DONE\n"); | |
482 | uprintf("dac_init()"); | |
483 | dac_init(); | |
484 | uprintf(" DONE\n"); | |
485 | while (1) { | |
1e527cc7 | 486 | uint32_t tt = timer_read32(); |
f3f721b9 PA |
487 | uint16_t key1 = measure_middle(TRACKING_KEY_1_COL, TRACKING_KEY_1_ROW, TRACKING_TEST_TIME, TRACKING_REPS); |
488 | uint16_t key2 = measure_middle(TRACKING_KEY_2_COL, TRACKING_KEY_2_ROW, TRACKING_TEST_TIME, TRACKING_REPS); | |
489 | uint16_t key3 = measure_middle(TRACKING_KEY_3_COL, TRACKING_KEY_3_ROW, TRACKING_TEST_TIME, TRACKING_REPS); | |
490 | uint16_t key4 = measure_middle(TRACKING_KEY_4_COL, TRACKING_KEY_4_ROW, TRACKING_TEST_TIME, TRACKING_REPS); | |
491 | uint16_t key5 = measure_middle(TRACKING_KEY_5_COL, TRACKING_KEY_5_ROW, TRACKING_TEST_TIME, TRACKING_REPS); | |
123c07cf PA |
492 | uint16_t sett = measure_middle_settled(TRACKING_KEY_2_COL, TRACKING_KEY_2_ROW, TRACKING_REPS); |
493 | uint16_t key1l = measure_middle(TRACKING_KEY_1_COL, TRACKING_KEY_1_ROW, TRACKING_TEST_TIME*2, TRACKING_REPS); | |
494 | uint16_t key2l = measure_middle(TRACKING_KEY_2_COL, TRACKING_KEY_2_ROW, TRACKING_TEST_TIME*2, TRACKING_REPS); | |
495 | uprintf("%5lu.%03u, %u, %u, %u, %u, %u, %u, %u, %u\n", tt/1000, (uint16_t)(tt%1000), key1, key2, key3, key4, key5, sett, key1l, key2l); | |
1e57a998 PA |
496 | } |
497 | } | |
38e6feca | 498 | #endif |
f2a03868 | 499 | |
f3f721b9 PA |
500 | uint16_t calibration_measure_all_valid_keys(uint8_t time, uint8_t reps, bool looking_for_all_zero) |
501 | { | |
05ffaaf5 | 502 | uint16_t min = 0, max = CAPSENSE_DAC_MAX; |
f3f721b9 PA |
503 | while (min < max) |
504 | { | |
505 | uint16_t mid = (min + max) / 2; | |
506 | if (!looking_for_all_zero) { | |
507 | mid = (min + max + 1) / 2; | |
508 | } | |
509 | dac_write_threshold(mid); | |
510 | uint8_t col; | |
511 | for (col = 0; col < MATRIX_COLS; col++) | |
512 | { | |
513 | uint8_t valid_physical_rows = 0; | |
514 | uint8_t row; | |
515 | for (row=0; row < MATRIX_ROWS; row++) | |
516 | { | |
fe505403 | 517 | if (pgm_read_word(&keymaps[0][row][col]) != KC_NO) |
f3f721b9 | 518 | { |
3ee273a4 | 519 | valid_physical_rows |= (((matrix_row_t)1) << CAPSENSE_KEYMAP_ROW_TO_PHYSICAL_ROW(row)); // convert keymap row to physical row |
f3f721b9 PA |
520 | } |
521 | } | |
f4270f7d | 522 | uint8_t physical_col = CAPSENSE_KEYMAP_COL_TO_PHYSICAL_COL(col); |
f3f721b9 PA |
523 | uint8_t i; |
524 | for (i=0;i<reps;i++) { | |
525 | if (looking_for_all_zero) | |
526 | { | |
23d3309b | 527 | uint8_t all_zero = (test_single(physical_col, time, NULL) & valid_physical_rows) == 0; |
f3f721b9 PA |
528 | if (!all_zero) { |
529 | min = mid + 1; | |
530 | goto next_binary_search; | |
531 | } | |
532 | } else { | |
23d3309b | 533 | uint8_t all_ones = (test_single(physical_col, time, NULL) & valid_physical_rows) == valid_physical_rows; |
f3f721b9 PA |
534 | if (!all_ones) { |
535 | max = mid - 1; | |
536 | goto next_binary_search; | |
537 | } | |
538 | } | |
539 | } | |
540 | } | |
541 | if (looking_for_all_zero) { | |
542 | max = mid; | |
543 | } else { | |
544 | min = mid; | |
545 | } | |
546 | next_binary_search:; | |
547 | } | |
548 | return min; | |
549 | } | |
550 | ||
f4270f7d | 551 | #if CAPSENSE_CAL_ENABLED |
c737e37a PA |
552 | #if defined(BOOTMAGIC_ENABLE) || defined(BOOTMAGIC_LITE) |
553 | #error "Calibration is not supported in conjunction with BOOTMAGIC, because calibration requires that no keys are pressed while the keyboard is plugged in" | |
554 | #endif | |
555 | #endif | |
556 | ||
3e1298e4 PA |
557 | uint16_t cal_thresholds[CAPSENSE_CAL_BINS]; |
558 | matrix_row_t assigned_to_threshold[CAPSENSE_CAL_BINS][MATRIX_ROWS]; | |
f3f721b9 PA |
559 | uint16_t cal_tr_allzero; |
560 | uint16_t cal_tr_allone; | |
f3f721b9 PA |
561 | void calibration(void) |
562 | { | |
94b5327f PA |
563 | uint16_t cal_thresholds_max[CAPSENSE_CAL_BINS]; |
564 | uint16_t cal_thresholds_min[CAPSENSE_CAL_BINS]; | |
565 | memset(cal_thresholds_max, 0xff, sizeof(cal_thresholds_max)); | |
566 | memset(cal_thresholds_min, 0xff, sizeof(cal_thresholds_min)); | |
3e1298e4 PA |
567 | cal_tr_allzero = calibration_measure_all_valid_keys(CAPSENSE_HARDCODED_SAMPLE_TIME, CAPSENSE_CAL_INIT_REPS, true); |
568 | cal_tr_allone = calibration_measure_all_valid_keys(CAPSENSE_HARDCODED_SAMPLE_TIME, CAPSENSE_CAL_INIT_REPS, false); | |
f3f721b9 PA |
569 | uint16_t max = (cal_tr_allzero == 0) ? 0 : (cal_tr_allzero - 1); |
570 | uint16_t min = cal_tr_allone + 1; | |
571 | if (max < min) max = min; | |
572 | uint16_t d = max - min; | |
573 | uint8_t i; | |
3e1298e4 PA |
574 | for (i=0;i<CAPSENSE_CAL_BINS;i++) { |
575 | cal_thresholds[i] = min + (d * (2 * i + 1)) / 2 / CAPSENSE_CAL_BINS; | |
f3f721b9 PA |
576 | } |
577 | uint8_t col; | |
578 | for (col = 0; col < MATRIX_COLS; col++) { | |
f4270f7d | 579 | uint8_t physical_col = CAPSENSE_KEYMAP_COL_TO_PHYSICAL_COL(col); |
f3f721b9 PA |
580 | uint8_t row; |
581 | for (row = 0; row < MATRIX_ROWS; row++) { | |
fe505403 | 582 | if (pgm_read_word(&keymaps[0][row][col]) != KC_NO) { |
f4270f7d | 583 | uint16_t threshold = measure_middle(physical_col, CAPSENSE_KEYMAP_ROW_TO_PHYSICAL_ROW(row), CAPSENSE_HARDCODED_SAMPLE_TIME, CAPSENSE_CAL_EACHKEY_REPS); |
f3f721b9 PA |
584 | uint8_t besti = 0; |
585 | uint16_t best_diff = (uint16_t)abs(threshold - cal_thresholds[besti]); | |
3e1298e4 | 586 | for (i=1;i<CAPSENSE_CAL_BINS;i++) { |
f3f721b9 PA |
587 | uint16_t this_diff = (uint16_t)abs(threshold - cal_thresholds[i]); |
588 | if (this_diff < best_diff) | |
589 | { | |
590 | best_diff = this_diff; | |
591 | besti = i; | |
592 | } | |
593 | } | |
3ee273a4 | 594 | assigned_to_threshold[besti][row] |= (((matrix_row_t)1) << col); |
94b5327f PA |
595 | if ((cal_thresholds_max[besti] == 0xFFFFU) || (cal_thresholds_max[besti] < threshold)) cal_thresholds_max[besti] = threshold; |
596 | if ((cal_thresholds_min[besti] == 0xFFFFU) || (cal_thresholds_min[besti] > threshold)) cal_thresholds_min[besti] = threshold; | |
f3f721b9 PA |
597 | } |
598 | } | |
599 | } | |
3e1298e4 | 600 | for (i=0;i<CAPSENSE_CAL_BINS;i++) { |
2ec3972f | 601 | uint16_t bin_signal_level; |
3f49a80b | 602 | if ((cal_thresholds_max[i] == 0xFFFFU) || (cal_thresholds_min[i] == 0xFFFFU)) { |
2ec3972f | 603 | bin_signal_level = cal_thresholds[i]; |
3f49a80b | 604 | } else { |
2ec3972f | 605 | bin_signal_level = (cal_thresholds_max[i] + cal_thresholds_min[i]) / 2; |
3f49a80b | 606 | } |
2ec3972f PA |
607 | #ifdef CAPSENSE_CONDUCTIVE_PLASTIC_IS_PUSHED_DOWN_ON_KEYPRESS |
608 | if ((bin_signal_level + CAPSENSE_CAL_THRESHOLD_OFFSET) > CAPSENSE_DAC_MAX) | |
609 | { | |
610 | cal_thresholds[i] = CAPSENSE_DAC_MAX; | |
611 | } else { | |
612 | cal_thresholds[i] = bin_signal_level + CAPSENSE_CAL_THRESHOLD_OFFSET; | |
613 | } | |
614 | #else | |
615 | if (bin_signal_level < CAPSENSE_CAL_THRESHOLD_OFFSET) | |
616 | { | |
617 | cal_thresholds[i] = 0; | |
618 | } else { | |
619 | cal_thresholds[i] = bin_signal_level - CAPSENSE_CAL_THRESHOLD_OFFSET; | |
620 | } | |
621 | #endif | |
f3f721b9 PA |
622 | } |
623 | } | |
624 | ||
09c2b24e PA |
625 | void real_keyboard_init_basic(void) |
626 | { | |
38e6feca | 627 | #ifndef NO_PRINT |
09c2b24e | 628 | uprintf("shift_init()"); |
38e6feca | 629 | #endif |
09c2b24e | 630 | shift_init(); |
38e6feca | 631 | #ifndef NO_PRINT |
09c2b24e PA |
632 | uprintf(" DONE\n"); |
633 | uprintf("dac_init()"); | |
38e6feca | 634 | #endif |
09c2b24e | 635 | dac_init(); |
38e6feca | 636 | #ifndef NO_PRINT |
09c2b24e | 637 | uprintf(" DONE\n"); |
38e6feca | 638 | #endif |
f4270f7d | 639 | #if CAPSENSE_CAL_ENABLED |
f3f721b9 PA |
640 | calibration(); |
641 | #else | |
38e6feca PA |
642 | dac_write_threshold(CAPSENSE_HARDCODED_THRESHOLD); |
643 | dac_write_threshold(CAPSENSE_HARDCODED_THRESHOLD); | |
644 | dac_write_threshold(CAPSENSE_HARDCODED_THRESHOLD); | |
f3f721b9 | 645 | #endif |
330e4867 | 646 | #if defined(CONTROLLER_IS_THROUGH_HOLE_BEAMSPRING) || defined(CONTROLLER_IS_THROUGH_HOLE_MODEL_F) |
b6bb4370 PA |
647 | // Disable on-board leds. |
648 | setPinOutput(D5); | |
649 | writePin(D5, 1); | |
650 | setPinOutput(B0); | |
651 | writePin(B0, 1); | |
652 | #endif | |
a03d56f8 PA |
653 | #ifdef USING_SOLENOID_ENABLE_PIN |
654 | // ^^ this must be defined in config.h if you are using and xwhatsit type solenoid | |
655 | setPinOutput(USING_SOLENOID_ENABLE_PIN); | |
656 | writePin(USING_SOLENOID_ENABLE_PIN, 1); | |
657 | #endif | |
09c2b24e PA |
658 | } |
659 | ||
06b963f3 | 660 | void matrix_init_custom(void) { |
9d9305db | 661 | //test_v2(); |
1e57a998 | 662 | //tracking_test(); |
09c2b24e | 663 | real_keyboard_init_basic(); |
06b963f3 PA |
664 | } |
665 | ||
f3f721b9 | 666 | matrix_row_t previous_matrix[MATRIX_ROWS]; |
38e6feca PA |
667 | |
668 | bool matrix_has_it_changed(const matrix_row_t current_matrix[]) { | |
669 | uint8_t row; | |
670 | bool changed = false; | |
671 | for (row=0;row<MATRIX_ROWS;row++) | |
672 | { | |
673 | if (previous_matrix[row] != current_matrix[row]) changed = true; | |
674 | previous_matrix[row] = current_matrix[row]; | |
675 | } | |
676 | return changed; | |
677 | } | |
678 | ||
f4270f7d | 679 | #if CAPSENSE_CAL_ENABLED && CAPSENSE_CAL_DEBUG |
f3f721b9 PA |
680 | bool cal_stats_printed = false; |
681 | #endif | |
dabf024c | 682 | |
38e6feca PA |
683 | bool keyboard_scan_enabled = 1; |
684 | ||
685 | #ifndef NO_PRINT | |
686 | void matrix_print_stats(void) | |
687 | { | |
688 | uint8_t row, cal; | |
f4270f7d | 689 | #if CAPSENSE_CAL_ENABLED && CAPSENSE_CAL_DEBUG |
f3f721b9 PA |
690 | if (!cal_stats_printed) |
691 | { | |
692 | uint32_t time = timer_read32(); | |
693 | if (time >= 10 * 1000UL) { // after 10 seconds | |
694 | uprintf("Cal All Zero = %u, Cal All Ones = %u\n", cal_tr_allzero, cal_tr_allone); | |
3e1298e4 | 695 | for (cal=0;cal<CAPSENSE_CAL_BINS;cal++) |
f3f721b9 PA |
696 | { |
697 | uprintf("Cal bin %u, Threshold=%u Assignments:\n", cal, cal_thresholds[cal]); | |
698 | for (row=0;row<MATRIX_ROWS;row++) | |
699 | { | |
700 | uprintf("0x%02X\n", assigned_to_threshold[cal][row]); | |
701 | } | |
702 | } | |
703 | cal_stats_printed = true; | |
704 | } | |
705 | } | |
706 | #endif | |
38e6feca PA |
707 | } |
708 | #endif | |
709 | ||
710 | void matrix_scan_raw(matrix_row_t current_matrix[]) { | |
02743d4c | 711 | uint8_t col, row; |
38e6feca | 712 | memset(current_matrix, 0, sizeof(matrix_row_t) * MATRIX_ROWS); |
f4270f7d | 713 | #if CAPSENSE_CAL_ENABLED |
02743d4c | 714 | uint8_t cal; |
3e1298e4 | 715 | for (cal=0;cal<CAPSENSE_CAL_BINS;cal++) { |
f3f721b9 PA |
716 | dac_write_threshold(cal_thresholds[cal]); |
717 | for (col=0;col<MATRIX_COLS;col++) { | |
f4270f7d | 718 | uint8_t real_col = CAPSENSE_KEYMAP_COL_TO_PHYSICAL_COL(col); |
23d3309b | 719 | uint8_t d, interference; |
f3f721b9 PA |
720 | uint8_t d_tested = 0; |
721 | for (row=0;row<MATRIX_ROWS;row++) { | |
3ee273a4 | 722 | if (assigned_to_threshold[cal][row] & (((matrix_row_t)1) << col)) |
f3f721b9 PA |
723 | { |
724 | if (!d_tested) | |
725 | { | |
23d3309b | 726 | d = test_single(real_col, CAPSENSE_HARDCODED_SAMPLE_TIME, &interference); |
f4270f7d PA |
727 | #ifdef CAPSENSE_CONDUCTIVE_PLASTIC_IS_PULLED_UP_ON_KEYPRESS |
728 | d = ~d; | |
729 | #endif | |
f3f721b9 PA |
730 | d_tested = 1; |
731 | } | |
f4270f7d | 732 | uint8_t physical_row = CAPSENSE_KEYMAP_ROW_TO_PHYSICAL_ROW(row); |
23d3309b PA |
733 | if (!((interference >> physical_row) & 1)) |
734 | { | |
735 | current_matrix[row] |= ((matrix_row_t)((d >> physical_row) & 1)) << col; | |
736 | } | |
f3f721b9 PA |
737 | } |
738 | } | |
739 | } | |
740 | } | |
741 | #else | |
742 | for (col=0;col<MATRIX_COLS;col++) | |
09c2b24e | 743 | { |
f4270f7d | 744 | uint8_t real_col = CAPSENSE_KEYMAP_COL_TO_PHYSICAL_COL(col); |
23d3309b PA |
745 | uint8_t interference; |
746 | uint8_t d = test_single(real_col, CAPSENSE_HARDCODED_SAMPLE_TIME, &interference); | |
f4270f7d PA |
747 | #ifdef CAPSENSE_CONDUCTIVE_PLASTIC_IS_PULLED_UP_ON_KEYPRESS |
748 | d = ~d; | |
749 | #endif | |
f3f721b9 | 750 | for (row=0;row<MATRIX_ROWS;row++) |
09c2b24e | 751 | { |
a6b3e4e5 | 752 | current_matrix[CAPSENSE_PHYSICAL_ROW_TO_KEYMAP_ROW(row)] |= (((matrix_row_t)(d & 1)) << col); |
09c2b24e PA |
753 | d >>= 1; |
754 | } | |
755 | } | |
f3f721b9 | 756 | #endif |
38e6feca PA |
757 | } |
758 | ||
759 | bool matrix_scan_custom(matrix_row_t current_matrix[]) { | |
760 | #ifndef NO_PRINT | |
761 | matrix_print_stats(); | |
762 | #endif | |
763 | if (keyboard_scan_enabled) { | |
764 | matrix_scan_raw(current_matrix); | |
765 | } else { | |
766 | memset(current_matrix, 0, sizeof(matrix_row_t) * MATRIX_ROWS); | |
f3f721b9 | 767 | } |
38e6feca | 768 | return matrix_has_it_changed(current_matrix); |
332fa7c4 | 769 | } |