]> git.donarmstrong.com Git - kiibohd-controller.git/blob - Scan/MatrixARM/matrix_scan.c
8463b26917761ce575d600a4827be08f24fd22e9
[kiibohd-controller.git] / Scan / MatrixARM / matrix_scan.c
1 /* Copyright (C) 2014-2015 by Jacob Alexander
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to deal
5  * in the Software without restriction, including without limitation the rights
6  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7  * copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19  * THE SOFTWARE.
20  */
21
22 // ----- Includes -----
23
24 // Compiler Includes
25 #include <Lib/ScanLib.h>
26
27 // Project Includes
28 #include <cli.h>
29 #include <kll_defs.h>
30 #include <led.h>
31 #include <print.h>
32 #include <macro.h>
33
34 // Local Includes
35 #include "matrix_scan.h"
36
37 // Matrix Configuration
38 #include <matrix.h>
39
40
41
42 // ----- Defines -----
43
44 #if ( DebounceThrottleDiv_define > 0 )
45 nat_ptr_t Matrix_divCounter = 0;
46 #endif
47
48
49
50 // ----- Function Declarations -----
51
52 // CLI Functions
53 void cliFunc_matrixDebug( char* args );
54 void cliFunc_matrixState( char* args );
55
56
57
58 // ----- Variables -----
59
60 // Scan Module command dictionary
61 CLIDict_Entry( matrixDebug,  "Enables matrix debug mode, prints out each scan code." NL "\t\tIf argument \033[35mT\033[0m is given, prints out each scan code state transition." );
62 CLIDict_Entry( matrixState,  "Prints out the current scan table N times." NL "\t\t \033[1mO\033[0m - Off, \033[1;33mP\033[0m - Press, \033[1;32mH\033[0m - Hold, \033[1;35mR\033[0m - Release, \033[1;31mI\033[0m - Invalid" );
63
64 CLIDict_Def( matrixCLIDict, "Matrix Module Commands" ) = {
65         CLIDict_Item( matrixDebug ),
66         CLIDict_Item( matrixState ),
67         { 0, 0, 0 } // Null entry for dictionary end
68 };
69
70 // Debounce Array
71 KeyState Matrix_scanArray[ Matrix_colsNum * Matrix_rowsNum ];
72
73 // Matrix debug flag - If set to 1, for each keypress the scan code is displayed in hex
74 //                     If set to 2, for each key state change, the scan code is displayed along with the state
75 uint8_t matrixDebugMode = 0;
76
77 // Matrix State Table Debug Counter - If non-zero display state table after every matrix scan
78 uint16_t matrixDebugStateCounter = 0;
79
80 // Matrix Scan Counters
81 uint16_t matrixMaxScans  = 0;
82 uint16_t matrixCurScans  = 0;
83 uint16_t matrixPrevScans = 0;
84
85 // System Timer used for delaying debounce decisions
86 extern volatile uint32_t systick_millis_count;
87
88
89
90 // ----- Functions -----
91
92 // Pin action (Strobe, Sense, Strobe Setup, Sense Setup)
93 // NOTE: This function is highly dependent upon the organization of the register map
94 //       Only guaranteed to work with Freescale MK20 series uCs
95 uint8_t Matrix_pin( GPIO_Pin gpio, Type type )
96 {
97         // Register width is defined as size of a pointer
98         unsigned int gpio_offset = gpio.port * 0x40   / sizeof(unsigned int*);
99         unsigned int port_offset = gpio.port * 0x1000 / sizeof(unsigned int*) + gpio.pin;
100
101         // Assumes 0x40 between GPIO Port registers and 0x1000 between PORT pin registers
102         // See Lib/mk20dx.h
103         volatile unsigned int *GPIO_PDDR = (unsigned int*)(&GPIOA_PDDR) + gpio_offset;
104         volatile unsigned int *GPIO_PSOR = (unsigned int*)(&GPIOA_PSOR) + gpio_offset;
105         volatile unsigned int *GPIO_PCOR = (unsigned int*)(&GPIOA_PCOR) + gpio_offset;
106         volatile unsigned int *GPIO_PDIR = (unsigned int*)(&GPIOA_PDIR) + gpio_offset;
107         volatile unsigned int *PORT_PCR  = (unsigned int*)(&PORTA_PCR0) + port_offset;
108
109         // Operation depends on Type
110         switch ( type )
111         {
112         case Type_StrobeOn:
113                 *GPIO_PSOR |= (1 << gpio.pin);
114                 break;
115
116         case Type_StrobeOff:
117                 *GPIO_PCOR |= (1 << gpio.pin);
118                 break;
119
120         case Type_StrobeSetup:
121                 // Set as output pin
122                 *GPIO_PDDR |= (1 << gpio.pin);
123
124                 // Configure pin with slow slew, high drive strength and GPIO mux
125                 *PORT_PCR = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
126
127                 // Enabling open-drain if specified
128                 switch ( Matrix_type )
129                 {
130                 case Config_Opendrain:
131                         *PORT_PCR |= PORT_PCR_ODE;
132                         break;
133
134                 // Do nothing otherwise
135                 default:
136                         break;
137                 }
138                 break;
139
140         case Type_Sense:
141                 return *GPIO_PDIR & (1 << gpio.pin) ? 1 : 0;
142
143         case Type_SenseSetup:
144                 // Set as input pin
145                 *GPIO_PDDR &= ~(1 << gpio.pin);
146
147                 // Configure pin with passive filter and GPIO mux
148                 *PORT_PCR = PORT_PCR_PFE | PORT_PCR_MUX(1);
149
150                 // Pull resistor config
151                 switch ( Matrix_type )
152                 {
153                 case Config_Pullup:
154                         *PORT_PCR |= PORT_PCR_PE | PORT_PCR_PS;
155                         break;
156
157                 case Config_Pulldown:
158                         *PORT_PCR |= PORT_PCR_PE;
159                         break;
160
161                 // Do nothing otherwise
162                 default:
163                         break;
164                 }
165                 break;
166         }
167
168         return 0;
169 }
170
171 // Setup GPIO pins for matrix scanning
172 void Matrix_setup()
173 {
174         // Register Matrix CLI dictionary
175         CLI_registerDictionary( matrixCLIDict, matrixCLIDictName );
176
177         info_msg("Columns:  ");
178         printHex( Matrix_colsNum );
179
180         // Setup Strobe Pins
181         for ( uint8_t pin = 0; pin < Matrix_colsNum; pin++ )
182         {
183                 Matrix_pin( Matrix_cols[ pin ], Type_StrobeSetup );
184         }
185
186         print( NL );
187         info_msg("Rows:     ");
188         printHex( Matrix_rowsNum );
189
190         // Setup Sense Pins
191         for ( uint8_t pin = 0; pin < Matrix_rowsNum; pin++ )
192         {
193                 Matrix_pin( Matrix_rows[ pin ], Type_SenseSetup );
194         }
195
196         print( NL );
197         info_msg("Max Keys: ");
198         printHex( Matrix_maxKeys );
199
200         // Clear out Debounce Array
201         for ( uint8_t item = 0; item < Matrix_maxKeys; item++ )
202         {
203                 Matrix_scanArray[ item ].prevState        = KeyState_Off;
204                 Matrix_scanArray[ item ].curState         = KeyState_Off;
205                 Matrix_scanArray[ item ].activeCount      = 0;
206                 Matrix_scanArray[ item ].inactiveCount    = DebounceDivThreshold_define; // Start at 'off' steady state
207                 Matrix_scanArray[ item ].prevDecisionTime = 0;
208         }
209
210         // Clear scan stats counters
211         matrixMaxScans  = 0;
212         matrixPrevScans = 0;
213 }
214
215 void Matrix_keyPositionDebug( KeyPosition pos )
216 {
217         // Depending on the state, use a different flag + color
218         switch ( pos )
219         {
220         case KeyState_Off:
221                 print("\033[1mO\033[0m");
222                 break;
223
224         case KeyState_Press:
225                 print("\033[1;33mP\033[0m");
226                 break;
227
228         case KeyState_Hold:
229                 print("\033[1;32mH\033[0m");
230                 break;
231
232         case KeyState_Release:
233                 print("\033[1;35mR\033[0m");
234                 break;
235
236         case KeyState_Invalid:
237         default:
238                 print("\033[1;31mI\033[0m");
239                 break;
240         }
241 }
242
243
244 // Scan the matrix for keypresses
245 // NOTE: scanNum should be reset to 0 after a USB send (to reset all the counters)
246 void Matrix_scan( uint16_t scanNum )
247 {
248 #if ( DebounceThrottleDiv_define > 0 )
249         // Scan-rate throttling
250         // By scanning using a divider, the scan rate slowed down
251         // DebounceThrottleDiv_define == 1 means -> /2 or half scan rate
252         // This helps with bouncy switches on fast uCs
253         if ( !( Matrix_divCounter++ & (1 << ( DebounceThrottleDiv_define - 1 )) ) )
254                 return;
255 #endif
256
257         // Increment stats counters
258         if ( scanNum > matrixMaxScans ) matrixMaxScans = scanNum;
259         if ( scanNum == 0 )
260         {
261                 matrixPrevScans = matrixCurScans;
262                 matrixCurScans = 0;
263         }
264         else
265         {
266                 matrixCurScans++;
267         }
268
269         // Read systick for event scheduling
270         uint8_t currentTime = (uint8_t)systick_millis_count;
271
272         // For each strobe, scan each of the sense pins
273         for ( uint8_t strobe = 0; strobe < Matrix_colsNum; strobe++ )
274         {
275                 // Strobe Pin
276                 Matrix_pin( Matrix_cols[ strobe ], Type_StrobeOn );
277
278                 // Scan each of the sense pins
279                 for ( uint8_t sense = 0; sense < Matrix_rowsNum; sense++ )
280                 {
281                         // Key position
282                         uint8_t key = Matrix_colsNum * sense + strobe;
283                         KeyState *state = &Matrix_scanArray[ key ];
284
285                         // If first scan, reset state
286                         if ( scanNum == 0 )
287                         {
288                                 // Set previous state, and reset current state
289                                 state->prevState = state->curState;
290                                 state->curState  = KeyState_Invalid;
291                         }
292
293                         // Signal Detected
294                         // Increment count and right shift opposing count
295                         // This means there is a maximum of scan 13 cycles on a perfect off to on transition
296                         //  (coming from a steady state 0xFFFF off scans)
297                         // Somewhat longer with switch bounciness
298                         // The advantage of this is that the count is ongoing and never needs to be reset
299                         // State still needs to be kept track of to deal with what to send to the Macro module
300                         if ( Matrix_pin( Matrix_rows[ sense ], Type_Sense ) )
301                         {
302                                 // Only update if not going to wrap around
303                                 if ( state->activeCount < DebounceDivThreshold_define ) state->activeCount += 1;
304                                 state->inactiveCount >>= 1;
305                         }
306                         // Signal Not Detected
307                         else
308                         {
309                                 // Only update if not going to wrap around
310                                 if ( state->inactiveCount < DebounceDivThreshold_define ) state->inactiveCount += 1;
311                                 state->activeCount >>= 1;
312                         }
313
314                         // Check for state change if it hasn't been set
315                         // But only if enough time has passed since last state change
316                         // Only check if the minimum number of scans has been met
317                         //   the current state is invalid
318                         //   and either active or inactive count is over the debounce threshold
319                         if ( state->curState == KeyState_Invalid )
320                         {
321                                 // Determine time since last decision
322                                 uint8_t lastTransition = currentTime - state->prevDecisionTime;
323
324                                 // Attempt state transition
325                                 switch ( state->prevState )
326                                 {
327                                 case KeyState_Press:
328                                 case KeyState_Hold:
329                                         if ( state->activeCount > state->inactiveCount )
330                                         {
331                                                 state->curState = KeyState_Hold;
332                                         }
333                                         else
334                                         {
335                                                 // If not enough time has passed since Hold
336                                                 // Keep previous state
337                                                 if ( lastTransition < MinDebounceTime_define )
338                                                 {
339                                                         //warn_print("FAST Release stopped");
340                                                         state->curState = state->prevState;
341                                                         continue;
342                                                 }
343
344                                                 state->curState = KeyState_Release;
345                                         }
346                                         break;
347
348                                 case KeyState_Release:
349                                 case KeyState_Off:
350                                         if ( state->activeCount > state->inactiveCount )
351                                         {
352                                                 // If not enough time has passed since Hold
353                                                 // Keep previous state
354                                                 if ( lastTransition < MinDebounceTime_define )
355                                                 {
356                                                         //warn_print("FAST Press stopped");
357                                                         state->curState = state->prevState;
358                                                         continue;
359                                                 }
360
361                                                 state->curState = KeyState_Press;
362                                         }
363                                         else
364                                         {
365                                                 state->curState = KeyState_Off;
366                                         }
367                                         break;
368
369                                 case KeyState_Invalid:
370                                 default:
371                                         erro_print("Matrix scan bug!! Report me!");
372                                         break;
373                                 }
374
375                                 // Update decision time
376                                 state->prevDecisionTime = currentTime;
377
378                                 // Send keystate to macro module
379                                 Macro_keyState( key, state->curState );
380
381                                 // Matrix Debug, only if there is a state change
382                                 if ( matrixDebugMode && state->curState != state->prevState )
383                                 {
384                                         // Basic debug output
385                                         if ( matrixDebugMode == 1 && state->curState == KeyState_Press )
386                                         {
387                                                 printHex( key );
388                                                 print(" ");
389                                         }
390                                         // State transition debug output
391                                         else if ( matrixDebugMode == 2 )
392                                         {
393                                                 printHex( key );
394                                                 Matrix_keyPositionDebug( state->curState );
395                                                 print(" ");
396                                         }
397                                 }
398                         }
399                 }
400
401                 // Unstrobe Pin
402                 Matrix_pin( Matrix_cols[ strobe ], Type_StrobeOff );
403         }
404
405         // State Table Output Debug
406         if ( matrixDebugStateCounter > 0 )
407         {
408                 // Decrement counter
409                 matrixDebugStateCounter--;
410
411                 // Output stats on number of scans being done per USB send
412                 print( NL );
413                 info_msg("Max scans:      ");
414                 printHex( matrixMaxScans );
415                 print( NL );
416                 info_msg("Previous scans: ");
417                 printHex( matrixPrevScans );
418                 print( NL );
419
420                 // Output current scan number
421                 info_msg("Scan Number:    ");
422                 printHex( scanNum );
423                 print( NL );
424
425                 // Display the state info for each key
426                 print("<key>:<previous state><current state> <active count> <inactive count>");
427                 for ( uint8_t key = 0; key < Matrix_maxKeys; key++ )
428                 {
429                         // Every 4 keys, put a newline
430                         if ( key % 4 == 0 )
431                                 print( NL );
432
433                         print("\033[1m0x");
434                         printHex_op( key, 2 );
435                         print("\033[0m");
436                         print(":");
437                         Matrix_keyPositionDebug( Matrix_scanArray[ key ].prevState );
438                         Matrix_keyPositionDebug( Matrix_scanArray[ key ].curState );
439                         print(" 0x");
440                         printHex_op( Matrix_scanArray[ key ].activeCount, 4 );
441                         print(" 0x");
442                         printHex_op( Matrix_scanArray[ key ].inactiveCount, 4 );
443                         print(" ");
444                 }
445
446                 print( NL );
447         }
448 }
449
450
451 // ----- CLI Command Functions -----
452
453 void cliFunc_matrixDebug ( char* args )
454 {
455         // Parse number from argument
456         //  NOTE: Only first argument is used
457         char* arg1Ptr;
458         char* arg2Ptr;
459         CLI_argumentIsolation( args, &arg1Ptr, &arg2Ptr );
460
461         // Set the matrix debug flag depending on the argument
462         // If no argument, set to scan code only
463         // If set to T, set to state transition
464         switch ( arg1Ptr[0] )
465         {
466         // T as argument
467         case 'T':
468         case 't':
469                 matrixDebugMode = matrixDebugMode != 2 ? 2 : 0;
470                 break;
471
472         // No argument
473         case '\0':
474                 matrixDebugMode = matrixDebugMode != 1 ? 1 : 0;
475                 break;
476
477         // Invalid argument
478         default:
479                 return;
480         }
481
482         print( NL );
483         info_msg("Matrix Debug Mode: ");
484         printInt8( matrixDebugMode );
485 }
486
487 void cliFunc_matrixState ( char* args )
488 {
489         // Parse number from argument
490         //  NOTE: Only first argument is used
491         char* arg1Ptr;
492         char* arg2Ptr;
493         CLI_argumentIsolation( args, &arg1Ptr, &arg2Ptr );
494
495         // Default to 1 if no argument is given
496         matrixDebugStateCounter = 1;
497
498         if ( arg1Ptr[0] != '\0' )
499         {
500                 matrixDebugStateCounter = (uint16_t)numToInt( arg1Ptr );
501         }
502 }
503