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 | ||
61cf2ea0 | 144 | void shift_data(uint32_t data, int data_idle, int shcp_idle, int stcp_idle) |
773b7610 PA |
145 | { |
146 | int i; | |
61cf2ea0 PA |
147 | writePin(CAPSENSE_SHIFT_SHCP, 0); |
148 | writePin(CAPSENSE_SHIFT_STCP, 0); | |
773b7610 PA |
149 | for (i=SHIFT_BITS-1; i>=0; i--) |
150 | { | |
151 | writePin(CAPSENSE_SHIFT_DIN, (data >> (SHIFT_BITS - 1)) & 1); | |
152 | writePin(CAPSENSE_SHIFT_SHCP, 1); | |
61cf2ea0 PA |
153 | if (!((i == 0) && (shcp_idle))) { |
154 | writePin(CAPSENSE_SHIFT_SHCP, 0); | |
155 | } | |
773b7610 PA |
156 | data <<= 1; |
157 | } | |
158 | writePin(CAPSENSE_SHIFT_STCP, 1); | |
61cf2ea0 PA |
159 | if (!stcp_idle) { |
160 | writePin(CAPSENSE_SHIFT_STCP, 0); | |
161 | } | |
162 | writePin(CAPSENSE_SHIFT_DIN, !!data_idle); | |
773b7610 PA |
163 | } |
164 | ||
31b3f2de | 165 | void shift_select_col_no_strobe(uint8_t col) |
405cc8cf PA |
166 | { |
167 | int i; | |
f4270f7d | 168 | for (i=SHIFT_BITS-1; i>=0; i--) |
405cc8cf | 169 | { |
3e1298e4 PA |
170 | writePin(CAPSENSE_SHIFT_DIN, !!(col == i)); |
171 | writePin(CAPSENSE_SHIFT_SHCP, 1); | |
172 | writePin(CAPSENSE_SHIFT_SHCP, 0); | |
405cc8cf | 173 | } |
405cc8cf PA |
174 | } |
175 | ||
31b3f2de PA |
176 | static inline void shift_select_col(uint8_t col) |
177 | { | |
178 | shift_select_col_no_strobe(col); | |
3e1298e4 PA |
179 | writePin(CAPSENSE_SHIFT_STCP, 1); |
180 | writePin(CAPSENSE_SHIFT_STCP, 0); | |
31b3f2de PA |
181 | } |
182 | ||
405cc8cf PA |
183 | void shift_init(void) |
184 | { | |
3e1298e4 PA |
185 | setPinOutput(CAPSENSE_SHIFT_DIN); |
186 | setPinOutput(CAPSENSE_SHIFT_OE); | |
187 | setPinOutput(CAPSENSE_SHIFT_STCP); | |
188 | setPinOutput(CAPSENSE_SHIFT_SHCP); | |
e999475d | 189 | writePin(CAPSENSE_SHIFT_OE, 0); |
3e1298e4 PA |
190 | writePin(CAPSENSE_SHIFT_STCP, 0); |
191 | writePin(CAPSENSE_SHIFT_SHCP, 0); | |
405cc8cf | 192 | shift_select_nothing(); |
3e1298e4 | 193 | wait_us(CAPSENSE_KEYBOARD_SETTLE_TIME_US); |
405cc8cf PA |
194 | } |
195 | ||
f3d8013a PA |
196 | // Timing: |
197 | // IN instructions (1 * CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE) | |
198 | // Store to array instructions (2 * number of bytes) | |
199 | // adiw: 1 cycle | |
200 | // cp: 1 cycle | |
201 | // cpc: 1 cycle | |
202 | // brlo: 2 cycles (when jumping) | |
203 | // --- Total loop length: | |
204 | // 3 * CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE + 5 cycles | |
205 | // first sample elements will be taken after [1..CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE-1] cycles | |
206 | // second sample elements will be taken after | |
207 | // [3 * CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE + 5 + 1.. | |
208 | // 3 * CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE + 5 + CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE-1] cycles | |
209 | ||
210 | // the following function requires storage for CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE * (time + 1) bytes | |
31b3f2de PA |
211 | // but returns valid data only in the first (time + 1) bytes |
212 | void test_multiple(uint8_t col, uint16_t time, uint8_t *array) | |
213 | { | |
214 | shift_select_col_no_strobe(col); | |
215 | uint16_t index; | |
3e1298e4 | 216 | CAPSENSE_READ_ROWS_LOCAL_VARS; |
31b3f2de PA |
217 | uint8_t *arrayp = array; |
218 | asm volatile ( | |
219 | "ldi %A[index], 0" "\n\t" | |
220 | "ldi %B[index], 0" "\n\t" | |
221 | "cli" "\n\t" | |
222 | "sbi %[stcp_regaddr], %[stcp_bit]" "\n\t" | |
f3d8013a PA |
223 | "1:" CAPSENSE_READ_ROWS_ASM_INSTRUCTIONS "\n\t" |
224 | CAPSENSE_READ_ROWS_STORE_TO_ARRAY_INSTRUCTIONS "\n\t" | |
31b3f2de PA |
225 | "adiw %A[index], 0x01" "\n\t" |
226 | "cp %A[index], %A[time]" "\n\t" | |
227 | "cpc %B[index], %B[time]" "\n\t" | |
228 | "brlo 1b" "\n\t" | |
229 | "sei" "\n\t" | |
230 | "cbi %[stcp_regaddr], %[stcp_bit]" "\n\t" | |
231 | : [arr] "=e" (arrayp), | |
232 | [index] "=&w" (index), | |
3e1298e4 | 233 | CAPSENSE_READ_ROWS_OUTPUT_CONSTRAINTS |
31b3f2de | 234 | : [time] "r" (time + 1), |
3e1298e4 PA |
235 | [stcp_regaddr] "I" (CAPSENSE_SHIFT_STCP_IO), |
236 | [stcp_bit] "I" (CAPSENSE_SHIFT_STCP_BIT), | |
237 | CAPSENSE_READ_ROWS_INPUT_CONSTRAINTS, | |
31b3f2de PA |
238 | "0" (arrayp) |
239 | : "memory" ); | |
240 | uint16_t i, p0, p1; | |
241 | p0 = p1 = 0; | |
242 | for (i=0; i<=time; i++) | |
243 | { | |
f3d8013a | 244 | CAPSENSE_READ_ROWS_EXTRACT_FROM_ARRAY; |
3e1298e4 | 245 | array[p1++] = CAPSENSE_READ_ROWS_VALUE; |
31b3f2de | 246 | } |
f2a03868 | 247 | shift_select_nothing(); |
3e1298e4 | 248 | wait_us(CAPSENSE_KEYBOARD_SETTLE_TIME_US); |
31b3f2de PA |
249 | } |
250 | ||
23d3309b | 251 | uint8_t test_single(uint8_t col, uint16_t time, uint8_t *interference_ptr) |
31b3f2de PA |
252 | { |
253 | shift_select_col_no_strobe(col); | |
254 | uint16_t index; | |
3e1298e4 | 255 | CAPSENSE_READ_ROWS_LOCAL_VARS; |
23d3309b PA |
256 | uint8_t array[CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE + 1]; // one sample before triggering, and one dummy byte |
257 | uint8_t *arrayp = array; | |
31b3f2de PA |
258 | asm volatile ( |
259 | "ldi %A[index], 0" "\n\t" | |
260 | "ldi %B[index], 0" "\n\t" | |
261 | "cli" "\n\t" | |
23d3309b PA |
262 | CAPSENSE_READ_ROWS_ASM_INSTRUCTIONS "\n\t" |
263 | CAPSENSE_READ_ROWS_STORE_TO_ARRAY_INSTRUCTIONS "\n\t" | |
31b3f2de | 264 | "sbi %[stcp_regaddr], %[stcp_bit]" "\n\t" |
f3d8013a PA |
265 | "1:" CAPSENSE_READ_ROWS_ASM_INSTRUCTIONS "\n\t" |
266 | CAPSENSE_READ_ROWS_STORE_TO_ARRAY_INSTRUCTIONS_FAKE "\n\t" | |
31b3f2de PA |
267 | "adiw %A[index], 0x01" "\n\t" |
268 | "cp %A[index], %A[time]" "\n\t" | |
269 | "cpc %B[index], %B[time]" "\n\t" | |
270 | "brlo 1b" "\n\t" | |
271 | "sei" "\n\t" | |
272 | "cbi %[stcp_regaddr], %[stcp_bit]" "\n\t" | |
273 | : [arr] "=e" (arrayp), | |
274 | [index] "=&w" (index), | |
3e1298e4 | 275 | CAPSENSE_READ_ROWS_OUTPUT_CONSTRAINTS |
31b3f2de | 276 | : [time] "r" (time + 1), |
3e1298e4 PA |
277 | [stcp_regaddr] "I" (CAPSENSE_SHIFT_STCP_IO), |
278 | [stcp_bit] "I" (CAPSENSE_SHIFT_STCP_BIT), | |
279 | CAPSENSE_READ_ROWS_INPUT_CONSTRAINTS, | |
31b3f2de PA |
280 | "0" (arrayp) |
281 | : "memory" ); | |
f2a03868 | 282 | shift_select_nothing(); |
3e1298e4 | 283 | wait_us(CAPSENSE_KEYBOARD_SETTLE_TIME_US); |
23d3309b PA |
284 | uint8_t value_at_time = CAPSENSE_READ_ROWS_VALUE; |
285 | if (interference_ptr) | |
286 | { | |
287 | uint16_t p0 = 0; | |
288 | CAPSENSE_READ_ROWS_EXTRACT_FROM_ARRAY; | |
289 | uint8_t interference = CAPSENSE_READ_ROWS_VALUE; | |
290 | *interference_ptr = interference; | |
291 | } | |
292 | return value_at_time; | |
31b3f2de | 293 | } |
405cc8cf | 294 | |
38e6feca | 295 | #ifndef NO_PRINT |
ced94ac4 | 296 | #define NRTIMES 64 |
f2a03868 PA |
297 | #define TESTATONCE 8 |
298 | #define REPS_V2 15 | |
299 | void test_col_print_data_v2(uint8_t col) | |
300 | { | |
301 | uprintf("%d: ", col); | |
f3d8013a | 302 | static uint8_t data[NRTIMES*CAPSENSE_READ_ROWS_NUMBER_OF_BYTES_PER_SAMPLE]; |
4addcae8 | 303 | static uint8_t sums[(TESTATONCE+1) * MATRIX_ROWS]; |
f2a03868 PA |
304 | uint8_t to_time = NRTIMES-1; |
305 | uint8_t from_time = 0; | |
306 | while (from_time<NRTIMES-1) | |
307 | { | |
308 | if (to_time - from_time + 1 > TESTATONCE) | |
309 | { | |
310 | to_time = from_time + TESTATONCE - 1; | |
311 | } | |
312 | uint8_t curr_TESTATONCE = to_time - from_time + 1; | |
313 | uint8_t i; | |
314 | for (i=0;i<(sizeof(sums)/sizeof(sums[0]));i++) | |
315 | { | |
316 | sums[i] = 0; | |
317 | } | |
318 | for (i=0;i<REPS_V2;i++) | |
319 | { | |
320 | uint8_t st = read_rows(); | |
321 | test_multiple(col, to_time, data); | |
322 | uint8_t j; | |
323 | uint8_t ii = 0; | |
324 | uint8_t k; | |
325 | for (j=0;j<curr_TESTATONCE;j++) | |
326 | { | |
327 | uint8_t dataj = data[j + from_time]; | |
4addcae8 | 328 | for (k=0; k<MATRIX_ROWS;k++) |
f2a03868 PA |
329 | { |
330 | sums[ii] += (dataj & 1); | |
331 | dataj >>= 1; | |
332 | ii += 1; | |
333 | } | |
334 | } | |
335 | if (from_time == 0) { | |
4addcae8 PA |
336 | ii = TESTATONCE * MATRIX_ROWS; |
337 | for (k=0; k<MATRIX_ROWS;k++) | |
f2a03868 PA |
338 | { |
339 | sums[ii] += (st & 1); | |
340 | st >>= 1; | |
341 | ii += 1; | |
342 | } | |
343 | } | |
344 | } | |
345 | if (from_time == 0) { | |
4addcae8 | 346 | for (i=TESTATONCE*MATRIX_ROWS;i<(TESTATONCE+1)*MATRIX_ROWS;i++) { |
f2a03868 PA |
347 | if (sums[i] > 0xf) { |
348 | print("?"); | |
349 | } else { | |
350 | uprintf("%X", sums[i]); | |
351 | } | |
352 | } | |
353 | print(":"); | |
354 | } | |
4addcae8 | 355 | for (i=0;i<curr_TESTATONCE*MATRIX_ROWS;i++) |
f2a03868 PA |
356 | { |
357 | if (sums[i] > 0xf) { | |
358 | print("?"); | |
359 | } else { | |
360 | uprintf("%X", sums[i]); | |
361 | } | |
362 | } | |
363 | from_time = to_time + 1; | |
364 | to_time = NRTIMES - 1; | |
365 | } | |
366 | print("\n"); | |
367 | } | |
38e6feca | 368 | #endif |
f2a03868 | 369 | |
38e6feca | 370 | #ifndef NO_PRINT |
f2a03868 PA |
371 | void test_v2(void) { |
372 | int i; | |
373 | for (i=7;i>0;i--) { | |
374 | uprintf("Starting test in %d\n", i); | |
375 | wait_ms(1000); | |
376 | } | |
377 | uprintf("shift_init()"); | |
378 | shift_init(); | |
379 | uprintf(" DONE\n"); | |
380 | uprintf("dac_init()"); | |
381 | dac_init(); | |
382 | uprintf(" DONE\n"); | |
383 | int d; | |
384 | for (d=90;d<=260;d++) | |
385 | { | |
386 | uprintf("Testing threshold: %d\n", d); | |
387 | dac_write_threshold(d); | |
388 | #if 1 | |
389 | int c; | |
4addcae8 | 390 | for (c=0; c<MATRIX_COLS;c++) |
f2a03868 | 391 | { |
4addcae8 | 392 | test_col_print_data_v2(CAPSENSE_KEYMAP_COL_TO_PHYSICAL_COL(c)); |
f2a03868 | 393 | } |
f2a03868 PA |
394 | #else |
395 | test_col_print_data_v2(0); | |
396 | test_col_print_data_v2(2); | |
397 | test_col_print_data_v2(6); | |
398 | test_col_print_data_v2(7); | |
399 | test_col_print_data_v2(15); | |
400 | #endif | |
401 | } | |
402 | uprintf("TEST DONE\n"); | |
403 | while(1); | |
404 | } | |
38e6feca | 405 | #endif |
f2a03868 | 406 | |
1e57a998 PA |
407 | #define TRACKING_TEST_TIME 4 |
408 | // Key 1 is the always non-pressed key under the space bar to the right. | |
409 | #define TRACKING_KEY_1_COL 6 | |
410 | #define TRACKING_KEY_1_ROW 4 | |
411 | // Key 2 is the always-pressed calibration pad to the far right-bottom of the keyboard. (both on F62 and F77) | |
412 | #define TRACKING_KEY_2_COL 15 | |
413 | #define TRACKING_KEY_2_ROW 6 | |
414 | // Key 3 is the F key | |
415 | #define TRACKING_KEY_3_COL 2 | |
416 | #define TRACKING_KEY_3_ROW 5 | |
417 | // Key 4 is the half of the split backspace that is unused if the user has a normal backspace. | |
418 | #define TRACKING_KEY_4_COL 7 | |
419 | #define TRACKING_KEY_4_ROW 3 | |
420 | // Key 5 is hidden key next to the left shift, which is only used in ISO layouts. | |
421 | #define TRACKING_KEY_5_COL 0 | |
422 | #define TRACKING_KEY_5_ROW 7 | |
423 | ||
424 | #define TRACKING_REPS 16 | |
425 | ||
38e6feca | 426 | uint16_t measure_middle(uint8_t col, uint8_t row, uint8_t time, uint8_t reps) |
1e57a998 | 427 | { |
f3f721b9 | 428 | uint8_t reps_div2 = reps / 2; |
05ffaaf5 | 429 | uint16_t min = 0, max = CAPSENSE_DAC_MAX; |
1e57a998 PA |
430 | while (min < max) |
431 | { | |
432 | uint16_t mid = (min + max) / 2; | |
433 | dac_write_threshold(mid); | |
434 | uint8_t sum = 0; | |
435 | uint8_t i; | |
f3f721b9 | 436 | for (i=0;i<reps;i++) |
1e57a998 | 437 | { |
23d3309b | 438 | sum += (test_single(col, time, NULL) >> row) & 1; |
1e57a998 | 439 | } |
f3f721b9 | 440 | if (sum < reps_div2) |
1e57a998 PA |
441 | { |
442 | max = mid - 1; | |
f3f721b9 | 443 | } else if (sum > reps_div2) { |
1e57a998 PA |
444 | min = mid + 1; |
445 | } else return mid; | |
446 | } | |
447 | return min; | |
448 | } | |
449 | ||
e8583e26 PA |
450 | uint16_t measure_middle_keymap_coords(uint8_t col, uint8_t row, uint8_t time, uint8_t reps) |
451 | { | |
452 | return measure_middle(CAPSENSE_KEYMAP_COL_TO_PHYSICAL_COL(col), CAPSENSE_KEYMAP_ROW_TO_PHYSICAL_ROW(row), time, reps); | |
453 | } | |
454 | ||
38e6feca | 455 | uint16_t measure_middle_settled(uint8_t col, uint8_t row, uint8_t reps) |
123c07cf PA |
456 | { |
457 | uint8_t reps_div2 = reps / 2; | |
05ffaaf5 | 458 | uint16_t min = 0, max = CAPSENSE_DAC_MAX; |
123c07cf PA |
459 | while (min < max) |
460 | { | |
461 | uint16_t mid = (min + max) / 2; | |
462 | dac_write_threshold(mid); | |
463 | uint8_t sum = 0; | |
464 | uint8_t i; | |
465 | for (i=0;i<reps;i++) | |
466 | { | |
467 | sum += (read_rows() >> row) & 1; | |
468 | } | |
469 | if (sum < reps_div2) | |
470 | { | |
471 | max = mid - 1; | |
472 | } else if (sum > reps_div2) { | |
473 | min = mid + 1; | |
474 | } else return mid; | |
475 | } | |
476 | return min; | |
477 | } | |
478 | ||
38e6feca | 479 | #ifndef NO_PRINT |
1e57a998 PA |
480 | void tracking_test(void) |
481 | { | |
482 | int i; | |
483 | for (i=7;i>0;i--) { | |
484 | uprintf("Starting test in %d\n", i); | |
485 | wait_ms(1000); | |
486 | } | |
487 | uprintf("shift_init()"); | |
488 | shift_init(); | |
489 | uprintf(" DONE\n"); | |
490 | uprintf("dac_init()"); | |
491 | dac_init(); | |
492 | uprintf(" DONE\n"); | |
493 | while (1) { | |
1e527cc7 | 494 | uint32_t tt = timer_read32(); |
f3f721b9 PA |
495 | uint16_t key1 = measure_middle(TRACKING_KEY_1_COL, TRACKING_KEY_1_ROW, TRACKING_TEST_TIME, TRACKING_REPS); |
496 | uint16_t key2 = measure_middle(TRACKING_KEY_2_COL, TRACKING_KEY_2_ROW, TRACKING_TEST_TIME, TRACKING_REPS); | |
497 | uint16_t key3 = measure_middle(TRACKING_KEY_3_COL, TRACKING_KEY_3_ROW, TRACKING_TEST_TIME, TRACKING_REPS); | |
498 | uint16_t key4 = measure_middle(TRACKING_KEY_4_COL, TRACKING_KEY_4_ROW, TRACKING_TEST_TIME, TRACKING_REPS); | |
499 | uint16_t key5 = measure_middle(TRACKING_KEY_5_COL, TRACKING_KEY_5_ROW, TRACKING_TEST_TIME, TRACKING_REPS); | |
123c07cf PA |
500 | uint16_t sett = measure_middle_settled(TRACKING_KEY_2_COL, TRACKING_KEY_2_ROW, TRACKING_REPS); |
501 | uint16_t key1l = measure_middle(TRACKING_KEY_1_COL, TRACKING_KEY_1_ROW, TRACKING_TEST_TIME*2, TRACKING_REPS); | |
502 | uint16_t key2l = measure_middle(TRACKING_KEY_2_COL, TRACKING_KEY_2_ROW, TRACKING_TEST_TIME*2, TRACKING_REPS); | |
503 | 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 |
504 | } |
505 | } | |
38e6feca | 506 | #endif |
f2a03868 | 507 | |
f3f721b9 PA |
508 | uint16_t calibration_measure_all_valid_keys(uint8_t time, uint8_t reps, bool looking_for_all_zero) |
509 | { | |
05ffaaf5 | 510 | uint16_t min = 0, max = CAPSENSE_DAC_MAX; |
f3f721b9 PA |
511 | while (min < max) |
512 | { | |
513 | uint16_t mid = (min + max) / 2; | |
514 | if (!looking_for_all_zero) { | |
515 | mid = (min + max + 1) / 2; | |
516 | } | |
517 | dac_write_threshold(mid); | |
518 | uint8_t col; | |
519 | for (col = 0; col < MATRIX_COLS; col++) | |
520 | { | |
521 | uint8_t valid_physical_rows = 0; | |
522 | uint8_t row; | |
523 | for (row=0; row < MATRIX_ROWS; row++) | |
524 | { | |
fe505403 | 525 | if (pgm_read_word(&keymaps[0][row][col]) != KC_NO) |
f3f721b9 | 526 | { |
3ee273a4 | 527 | valid_physical_rows |= (((matrix_row_t)1) << CAPSENSE_KEYMAP_ROW_TO_PHYSICAL_ROW(row)); // convert keymap row to physical row |
f3f721b9 PA |
528 | } |
529 | } | |
f4270f7d | 530 | uint8_t physical_col = CAPSENSE_KEYMAP_COL_TO_PHYSICAL_COL(col); |
f3f721b9 PA |
531 | uint8_t i; |
532 | for (i=0;i<reps;i++) { | |
533 | if (looking_for_all_zero) | |
534 | { | |
23d3309b | 535 | uint8_t all_zero = (test_single(physical_col, time, NULL) & valid_physical_rows) == 0; |
f3f721b9 PA |
536 | if (!all_zero) { |
537 | min = mid + 1; | |
538 | goto next_binary_search; | |
539 | } | |
540 | } else { | |
23d3309b | 541 | uint8_t all_ones = (test_single(physical_col, time, NULL) & valid_physical_rows) == valid_physical_rows; |
f3f721b9 PA |
542 | if (!all_ones) { |
543 | max = mid - 1; | |
544 | goto next_binary_search; | |
545 | } | |
546 | } | |
547 | } | |
548 | } | |
549 | if (looking_for_all_zero) { | |
550 | max = mid; | |
551 | } else { | |
552 | min = mid; | |
553 | } | |
554 | next_binary_search:; | |
555 | } | |
556 | return min; | |
557 | } | |
558 | ||
f4270f7d | 559 | #if CAPSENSE_CAL_ENABLED |
c737e37a PA |
560 | #if defined(BOOTMAGIC_ENABLE) || defined(BOOTMAGIC_LITE) |
561 | #error "Calibration is not supported in conjunction with BOOTMAGIC, because calibration requires that no keys are pressed while the keyboard is plugged in" | |
562 | #endif | |
563 | #endif | |
564 | ||
3e1298e4 PA |
565 | uint16_t cal_thresholds[CAPSENSE_CAL_BINS]; |
566 | matrix_row_t assigned_to_threshold[CAPSENSE_CAL_BINS][MATRIX_ROWS]; | |
f3f721b9 PA |
567 | uint16_t cal_tr_allzero; |
568 | uint16_t cal_tr_allone; | |
f3f721b9 PA |
569 | void calibration(void) |
570 | { | |
94b5327f PA |
571 | uint16_t cal_thresholds_max[CAPSENSE_CAL_BINS]; |
572 | uint16_t cal_thresholds_min[CAPSENSE_CAL_BINS]; | |
573 | memset(cal_thresholds_max, 0xff, sizeof(cal_thresholds_max)); | |
574 | memset(cal_thresholds_min, 0xff, sizeof(cal_thresholds_min)); | |
3e1298e4 PA |
575 | cal_tr_allzero = calibration_measure_all_valid_keys(CAPSENSE_HARDCODED_SAMPLE_TIME, CAPSENSE_CAL_INIT_REPS, true); |
576 | cal_tr_allone = calibration_measure_all_valid_keys(CAPSENSE_HARDCODED_SAMPLE_TIME, CAPSENSE_CAL_INIT_REPS, false); | |
f3f721b9 PA |
577 | uint16_t max = (cal_tr_allzero == 0) ? 0 : (cal_tr_allzero - 1); |
578 | uint16_t min = cal_tr_allone + 1; | |
579 | if (max < min) max = min; | |
580 | uint16_t d = max - min; | |
581 | uint8_t i; | |
3e1298e4 PA |
582 | for (i=0;i<CAPSENSE_CAL_BINS;i++) { |
583 | cal_thresholds[i] = min + (d * (2 * i + 1)) / 2 / CAPSENSE_CAL_BINS; | |
f3f721b9 PA |
584 | } |
585 | uint8_t col; | |
586 | for (col = 0; col < MATRIX_COLS; col++) { | |
f4270f7d | 587 | uint8_t physical_col = CAPSENSE_KEYMAP_COL_TO_PHYSICAL_COL(col); |
f3f721b9 PA |
588 | uint8_t row; |
589 | for (row = 0; row < MATRIX_ROWS; row++) { | |
fe505403 | 590 | if (pgm_read_word(&keymaps[0][row][col]) != KC_NO) { |
f4270f7d | 591 | uint16_t threshold = measure_middle(physical_col, CAPSENSE_KEYMAP_ROW_TO_PHYSICAL_ROW(row), CAPSENSE_HARDCODED_SAMPLE_TIME, CAPSENSE_CAL_EACHKEY_REPS); |
f3f721b9 PA |
592 | uint8_t besti = 0; |
593 | uint16_t best_diff = (uint16_t)abs(threshold - cal_thresholds[besti]); | |
3e1298e4 | 594 | for (i=1;i<CAPSENSE_CAL_BINS;i++) { |
f3f721b9 PA |
595 | uint16_t this_diff = (uint16_t)abs(threshold - cal_thresholds[i]); |
596 | if (this_diff < best_diff) | |
597 | { | |
598 | best_diff = this_diff; | |
599 | besti = i; | |
600 | } | |
601 | } | |
3ee273a4 | 602 | assigned_to_threshold[besti][row] |= (((matrix_row_t)1) << col); |
94b5327f PA |
603 | if ((cal_thresholds_max[besti] == 0xFFFFU) || (cal_thresholds_max[besti] < threshold)) cal_thresholds_max[besti] = threshold; |
604 | if ((cal_thresholds_min[besti] == 0xFFFFU) || (cal_thresholds_min[besti] > threshold)) cal_thresholds_min[besti] = threshold; | |
f3f721b9 PA |
605 | } |
606 | } | |
607 | } | |
3e1298e4 | 608 | for (i=0;i<CAPSENSE_CAL_BINS;i++) { |
2ec3972f | 609 | uint16_t bin_signal_level; |
3f49a80b | 610 | if ((cal_thresholds_max[i] == 0xFFFFU) || (cal_thresholds_min[i] == 0xFFFFU)) { |
2ec3972f | 611 | bin_signal_level = cal_thresholds[i]; |
3f49a80b | 612 | } else { |
2ec3972f | 613 | bin_signal_level = (cal_thresholds_max[i] + cal_thresholds_min[i]) / 2; |
3f49a80b | 614 | } |
2ec3972f PA |
615 | #ifdef CAPSENSE_CONDUCTIVE_PLASTIC_IS_PUSHED_DOWN_ON_KEYPRESS |
616 | if ((bin_signal_level + CAPSENSE_CAL_THRESHOLD_OFFSET) > CAPSENSE_DAC_MAX) | |
617 | { | |
618 | cal_thresholds[i] = CAPSENSE_DAC_MAX; | |
619 | } else { | |
620 | cal_thresholds[i] = bin_signal_level + CAPSENSE_CAL_THRESHOLD_OFFSET; | |
621 | } | |
622 | #else | |
623 | if (bin_signal_level < CAPSENSE_CAL_THRESHOLD_OFFSET) | |
624 | { | |
625 | cal_thresholds[i] = 0; | |
626 | } else { | |
627 | cal_thresholds[i] = bin_signal_level - CAPSENSE_CAL_THRESHOLD_OFFSET; | |
628 | } | |
629 | #endif | |
f3f721b9 PA |
630 | } |
631 | } | |
632 | ||
03a454e1 PA |
633 | void set_leds(int num_lock, int caps_lock, int scroll_lock) |
634 | { | |
635 | #if defined(LED_NUM_LOCK_PIN) | |
636 | #if defined(LED_NUM_LOCK_ACTIVE_LOW) | |
637 | writePin(LED_NUM_LOCK_PIN, !num_lock); | |
638 | #else | |
639 | writePin(LED_NUM_LOCK_PIN, num_lock); | |
640 | #endif | |
641 | #endif | |
642 | #if defined(LED_CAPS_LOCK_PIN) | |
643 | #if defined(LED_CAPS_LOCK_ACTIVE_LOW) | |
644 | writePin(LED_CAPS_LOCK_PIN, !caps_lock); | |
645 | #else | |
646 | writePin(LED_CAPS_LOCK_PIN, caps_lock); | |
647 | #endif | |
648 | #endif | |
649 | #if defined(LED_SCROLL_LOCK_PIN) | |
650 | #if defined(LED_SCROLL_LOCK_ACTIVE_LOW) | |
651 | writePin(LED_SCROLL_LOCK_PIN, !scroll_lock); | |
652 | #else | |
653 | writePin(LED_SCROLL_LOCK_PIN, scroll_lock); | |
654 | #endif | |
655 | #endif | |
656 | } | |
657 | ||
09c2b24e PA |
658 | void real_keyboard_init_basic(void) |
659 | { | |
12d658b8 | 660 | SETUP_UNUSED_PINS(); |
03a454e1 PA |
661 | |
662 | #if defined(LED_NUM_LOCK_PIN) | |
663 | setPinOutput(LED_NUM_LOCK_PIN); | |
664 | #endif | |
665 | #if defined(LED_CAPS_LOCK_PIN) | |
666 | setPinOutput(LED_CAPS_LOCK_PIN); | |
667 | #endif | |
668 | #if defined(LED_SCROLL_LOCK_PIN) | |
669 | setPinOutput(LED_SCROLL_LOCK_PIN); | |
670 | #endif | |
671 | set_leds(0, 0, 0); | |
672 | ||
38e6feca | 673 | #ifndef NO_PRINT |
09c2b24e | 674 | uprintf("shift_init()"); |
38e6feca | 675 | #endif |
09c2b24e | 676 | shift_init(); |
38e6feca | 677 | #ifndef NO_PRINT |
09c2b24e PA |
678 | uprintf(" DONE\n"); |
679 | uprintf("dac_init()"); | |
38e6feca | 680 | #endif |
09c2b24e | 681 | dac_init(); |
38e6feca | 682 | #ifndef NO_PRINT |
09c2b24e | 683 | uprintf(" DONE\n"); |
38e6feca | 684 | #endif |
c5657b7d | 685 | SETUP_ROW_GPIOS(); |
f4270f7d | 686 | #if CAPSENSE_CAL_ENABLED |
f3f721b9 PA |
687 | calibration(); |
688 | #else | |
38e6feca PA |
689 | dac_write_threshold(CAPSENSE_HARDCODED_THRESHOLD); |
690 | dac_write_threshold(CAPSENSE_HARDCODED_THRESHOLD); | |
691 | dac_write_threshold(CAPSENSE_HARDCODED_THRESHOLD); | |
f3f721b9 | 692 | #endif |
330e4867 | 693 | #if defined(CONTROLLER_IS_THROUGH_HOLE_BEAMSPRING) || defined(CONTROLLER_IS_THROUGH_HOLE_MODEL_F) |
b6bb4370 PA |
694 | // Disable on-board leds. |
695 | setPinOutput(D5); | |
696 | writePin(D5, 1); | |
697 | setPinOutput(B0); | |
698 | writePin(B0, 1); | |
699 | #endif | |
a03d56f8 PA |
700 | #ifdef USING_SOLENOID_ENABLE_PIN |
701 | // ^^ this must be defined in config.h if you are using and xwhatsit type solenoid | |
702 | setPinOutput(USING_SOLENOID_ENABLE_PIN); | |
703 | writePin(USING_SOLENOID_ENABLE_PIN, 1); | |
704 | #endif | |
09c2b24e PA |
705 | } |
706 | ||
03a454e1 PA |
707 | |
708 | bool led_update_kb(led_t led_state) { | |
709 | bool res = led_update_user(led_state); | |
710 | if(res) { | |
711 | set_leds(led_state.num_lock, led_state.caps_lock, led_state.scroll_lock); | |
712 | } | |
713 | return res; | |
714 | } | |
715 | ||
06b963f3 | 716 | void matrix_init_custom(void) { |
03a454e1 PA |
717 | |
718 | ||
9d9305db | 719 | //test_v2(); |
1e57a998 | 720 | //tracking_test(); |
09c2b24e | 721 | real_keyboard_init_basic(); |
06b963f3 PA |
722 | } |
723 | ||
f3f721b9 | 724 | matrix_row_t previous_matrix[MATRIX_ROWS]; |
38e6feca PA |
725 | |
726 | bool matrix_has_it_changed(const matrix_row_t current_matrix[]) { | |
727 | uint8_t row; | |
728 | bool changed = false; | |
729 | for (row=0;row<MATRIX_ROWS;row++) | |
730 | { | |
731 | if (previous_matrix[row] != current_matrix[row]) changed = true; | |
732 | previous_matrix[row] = current_matrix[row]; | |
733 | } | |
734 | return changed; | |
735 | } | |
736 | ||
f4270f7d | 737 | #if CAPSENSE_CAL_ENABLED && CAPSENSE_CAL_DEBUG |
f3f721b9 PA |
738 | bool cal_stats_printed = false; |
739 | #endif | |
dabf024c | 740 | |
38e6feca PA |
741 | bool keyboard_scan_enabled = 1; |
742 | ||
743 | #ifndef NO_PRINT | |
744 | void matrix_print_stats(void) | |
745 | { | |
746 | uint8_t row, cal; | |
f4270f7d | 747 | #if CAPSENSE_CAL_ENABLED && CAPSENSE_CAL_DEBUG |
f3f721b9 PA |
748 | if (!cal_stats_printed) |
749 | { | |
750 | uint32_t time = timer_read32(); | |
751 | if (time >= 10 * 1000UL) { // after 10 seconds | |
752 | uprintf("Cal All Zero = %u, Cal All Ones = %u\n", cal_tr_allzero, cal_tr_allone); | |
3e1298e4 | 753 | for (cal=0;cal<CAPSENSE_CAL_BINS;cal++) |
f3f721b9 PA |
754 | { |
755 | uprintf("Cal bin %u, Threshold=%u Assignments:\n", cal, cal_thresholds[cal]); | |
756 | for (row=0;row<MATRIX_ROWS;row++) | |
757 | { | |
758 | uprintf("0x%02X\n", assigned_to_threshold[cal][row]); | |
759 | } | |
760 | } | |
761 | cal_stats_printed = true; | |
762 | } | |
763 | } | |
764 | #endif | |
38e6feca PA |
765 | } |
766 | #endif | |
767 | ||
768 | void matrix_scan_raw(matrix_row_t current_matrix[]) { | |
02743d4c | 769 | uint8_t col, row; |
38e6feca | 770 | memset(current_matrix, 0, sizeof(matrix_row_t) * MATRIX_ROWS); |
f4270f7d | 771 | #if CAPSENSE_CAL_ENABLED |
02743d4c | 772 | uint8_t cal; |
3e1298e4 | 773 | for (cal=0;cal<CAPSENSE_CAL_BINS;cal++) { |
f3f721b9 PA |
774 | dac_write_threshold(cal_thresholds[cal]); |
775 | for (col=0;col<MATRIX_COLS;col++) { | |
f4270f7d | 776 | uint8_t real_col = CAPSENSE_KEYMAP_COL_TO_PHYSICAL_COL(col); |
23d3309b | 777 | uint8_t d, interference; |
f3f721b9 PA |
778 | uint8_t d_tested = 0; |
779 | for (row=0;row<MATRIX_ROWS;row++) { | |
3ee273a4 | 780 | if (assigned_to_threshold[cal][row] & (((matrix_row_t)1) << col)) |
f3f721b9 PA |
781 | { |
782 | if (!d_tested) | |
783 | { | |
23d3309b | 784 | d = test_single(real_col, CAPSENSE_HARDCODED_SAMPLE_TIME, &interference); |
f4270f7d PA |
785 | #ifdef CAPSENSE_CONDUCTIVE_PLASTIC_IS_PULLED_UP_ON_KEYPRESS |
786 | d = ~d; | |
787 | #endif | |
f3f721b9 PA |
788 | d_tested = 1; |
789 | } | |
f4270f7d | 790 | uint8_t physical_row = CAPSENSE_KEYMAP_ROW_TO_PHYSICAL_ROW(row); |
23d3309b PA |
791 | if (!((interference >> physical_row) & 1)) |
792 | { | |
793 | current_matrix[row] |= ((matrix_row_t)((d >> physical_row) & 1)) << col; | |
794 | } | |
f3f721b9 PA |
795 | } |
796 | } | |
797 | } | |
798 | } | |
799 | #else | |
800 | for (col=0;col<MATRIX_COLS;col++) | |
09c2b24e | 801 | { |
f4270f7d | 802 | uint8_t real_col = CAPSENSE_KEYMAP_COL_TO_PHYSICAL_COL(col); |
23d3309b PA |
803 | uint8_t interference; |
804 | uint8_t d = test_single(real_col, CAPSENSE_HARDCODED_SAMPLE_TIME, &interference); | |
f4270f7d PA |
805 | #ifdef CAPSENSE_CONDUCTIVE_PLASTIC_IS_PULLED_UP_ON_KEYPRESS |
806 | d = ~d; | |
807 | #endif | |
f3f721b9 | 808 | for (row=0;row<MATRIX_ROWS;row++) |
09c2b24e | 809 | { |
a6b3e4e5 | 810 | current_matrix[CAPSENSE_PHYSICAL_ROW_TO_KEYMAP_ROW(row)] |= (((matrix_row_t)(d & 1)) << col); |
09c2b24e PA |
811 | d >>= 1; |
812 | } | |
813 | } | |
f3f721b9 | 814 | #endif |
38e6feca PA |
815 | } |
816 | ||
817 | bool matrix_scan_custom(matrix_row_t current_matrix[]) { | |
818 | #ifndef NO_PRINT | |
819 | matrix_print_stats(); | |
820 | #endif | |
821 | if (keyboard_scan_enabled) { | |
822 | matrix_scan_raw(current_matrix); | |
823 | } else { | |
824 | memset(current_matrix, 0, sizeof(matrix_row_t) * MATRIX_ROWS); | |
f3f721b9 | 825 | } |
38e6feca | 826 | return matrix_has_it_changed(current_matrix); |
332fa7c4 | 827 | } |