]> git.donarmstrong.com Git - kiibohd-controller.git/blob - Scan/MatrixARM/matrix_scan.c
Minor styling fixes.
[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 // Ghost Arrays
74 #ifdef GHOSTING_MATRIX
75 KeyGhost Matrix_ghostArray[ Matrix_colsNum * Matrix_rowsNum ];
76
77 uint8_t col_use[Matrix_colsNum], row_use[Matrix_rowsNum];  // used count
78 uint8_t col_ghost[Matrix_colsNum], row_ghost[Matrix_rowsNum];  // marked as having ghost if 1
79 #endif
80
81
82 // Matrix debug flag - If set to 1, for each keypress the scan code is displayed in hex
83 //                     If set to 2, for each key state change, the scan code is displayed along with the state
84 uint8_t matrixDebugMode = 0;
85
86 // Matrix State Table Debug Counter - If non-zero display state table after every matrix scan
87 uint16_t matrixDebugStateCounter = 0;
88
89 // Matrix Scan Counters
90 uint16_t matrixMaxScans  = 0;
91 uint16_t matrixCurScans  = 0;
92 uint16_t matrixPrevScans = 0;
93
94 // System Timer used for delaying debounce decisions
95 extern volatile uint32_t systick_millis_count;
96
97
98
99 // ----- Functions -----
100
101 // Pin action (Strobe, Sense, Strobe Setup, Sense Setup)
102 // NOTE: This function is highly dependent upon the organization of the register map
103 //       Only guaranteed to work with Freescale MK20 series uCs
104 uint8_t Matrix_pin( GPIO_Pin gpio, Type type )
105 {
106         // Register width is defined as size of a pointer
107         unsigned int gpio_offset = gpio.port * 0x40   / sizeof(unsigned int*);
108         unsigned int port_offset = gpio.port * 0x1000 / sizeof(unsigned int*) + gpio.pin;
109
110         // Assumes 0x40 between GPIO Port registers and 0x1000 between PORT pin registers
111         // See Lib/mk20dx.h
112         volatile unsigned int *GPIO_PDDR = (unsigned int*)(&GPIOA_PDDR) + gpio_offset;
113         volatile unsigned int *GPIO_PSOR = (unsigned int*)(&GPIOA_PSOR) + gpio_offset;
114         volatile unsigned int *GPIO_PCOR = (unsigned int*)(&GPIOA_PCOR) + gpio_offset;
115         volatile unsigned int *GPIO_PDIR = (unsigned int*)(&GPIOA_PDIR) + gpio_offset;
116         volatile unsigned int *PORT_PCR  = (unsigned int*)(&PORTA_PCR0) + port_offset;
117
118         // Operation depends on Type
119         switch ( type )
120         {
121         case Type_StrobeOn:
122                 *GPIO_PSOR |= (1 << gpio.pin);
123                 #ifdef GHOSTING_MATRIX
124                 *GPIO_PDDR |= (1 << gpio.pin);  // output
125                 #endif
126                 break;
127
128         case Type_StrobeOff:
129                 *GPIO_PCOR |= (1 << gpio.pin);
130                 #ifdef GHOSTING_MATRIX
131                 // Ghosting martix needs to put not used (off) strobes in high impedance state
132                 *GPIO_PDDR &= ~(1 << gpio.pin);  // input, high Z state
133                 #endif
134                 break;
135
136         case Type_StrobeSetup:
137                 // Set as output pin
138                 *GPIO_PDDR |= (1 << gpio.pin);
139
140                 // Configure pin with slow slew, high drive strength and GPIO mux
141                 *PORT_PCR = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
142
143                 // Enabling open-drain if specified
144                 switch ( Matrix_type )
145                 {
146                 case Config_Opendrain:
147                         *PORT_PCR |= PORT_PCR_ODE;
148                         break;
149
150                 // Do nothing otherwise
151                 default:
152                         break;
153                 }
154                 break;
155
156         case Type_Sense:
157                 return *GPIO_PDIR & (1 << gpio.pin) ? 1 : 0;
158
159         case Type_SenseSetup:
160                 // Set as input pin
161                 *GPIO_PDDR &= ~(1 << gpio.pin);
162
163                 // Configure pin with passive filter and GPIO mux
164                 *PORT_PCR = PORT_PCR_PFE | PORT_PCR_MUX(1);
165
166                 // Pull resistor config
167                 switch ( Matrix_type )
168                 {
169                 case Config_Pullup:
170                         *PORT_PCR |= PORT_PCR_PE | PORT_PCR_PS;
171                         break;
172
173                 case Config_Pulldown:
174                         *PORT_PCR |= PORT_PCR_PE;
175                         break;
176
177                 // Do nothing otherwise
178                 default:
179                         break;
180                 }
181                 break;
182         }
183
184         return 0;
185 }
186
187 // Setup GPIO pins for matrix scanning
188 void Matrix_setup()
189 {
190         // Register Matrix CLI dictionary
191         CLI_registerDictionary( matrixCLIDict, matrixCLIDictName );
192
193         info_msg("Columns:  ");
194         printHex( Matrix_colsNum );
195
196         // Setup Strobe Pins
197         for ( uint8_t pin = 0; pin < Matrix_colsNum; pin++ )
198         {
199                 Matrix_pin( Matrix_cols[ pin ], Type_StrobeSetup );
200         }
201
202         print( NL );
203         info_msg("Rows:     ");
204         printHex( Matrix_rowsNum );
205
206         // Setup Sense Pins
207         for ( uint8_t pin = 0; pin < Matrix_rowsNum; pin++ )
208         {
209                 Matrix_pin( Matrix_rows[ pin ], Type_SenseSetup );
210         }
211
212         print( NL );
213         info_msg("Max Keys: ");
214         printHex( Matrix_maxKeys );
215         print( NL );
216
217         // Clear out Debounce Array
218         for ( uint8_t item = 0; item < Matrix_maxKeys; item++ )
219         {
220                 Matrix_scanArray[ item ].prevState        = KeyState_Off;
221                 Matrix_scanArray[ item ].curState         = KeyState_Off;
222                 Matrix_scanArray[ item ].activeCount      = 0;
223                 Matrix_scanArray[ item ].inactiveCount    = DebounceDivThreshold_define; // Start at 'off' steady state
224                 Matrix_scanArray[ item ].prevDecisionTime = 0;
225                 #ifdef GHOSTING_MATRIX
226                 Matrix_ghostArray[ item ].prev            = KeyState_Off;
227                 Matrix_ghostArray[ item ].cur             = KeyState_Off;
228                 Matrix_ghostArray[ item ].saved           = KeyState_Off;
229                 #endif
230         }
231
232         // Clear scan stats counters
233         matrixMaxScans  = 0;
234         matrixPrevScans = 0;
235 }
236
237 void Matrix_keyPositionDebug( KeyPosition pos )
238 {
239         // Depending on the state, use a different flag + color
240         switch ( pos )
241         {
242         case KeyState_Off:
243                 print("\033[1mO\033[0m");
244                 break;
245
246         case KeyState_Press:
247                 print("\033[1;33mP\033[0m");
248                 break;
249
250         case KeyState_Hold:
251                 print("\033[1;32mH\033[0m");
252                 break;
253
254         case KeyState_Release:
255                 print("\033[1;35mR\033[0m");
256                 break;
257
258         case KeyState_Invalid:
259         default:
260                 print("\033[1;31mI\033[0m");
261                 break;
262         }
263 }
264
265
266 // Scan the matrix for keypresses
267 // NOTE: scanNum should be reset to 0 after a USB send (to reset all the counters)
268 void Matrix_scan( uint16_t scanNum )
269 {
270 #if ( DebounceThrottleDiv_define > 0 )
271         // Scan-rate throttling
272         // By scanning using a divider, the scan rate slowed down
273         // DebounceThrottleDiv_define == 1 means -> /2 or half scan rate
274         // This helps with bouncy switches on fast uCs
275         if ( !( Matrix_divCounter++ & (1 << ( DebounceThrottleDiv_define - 1 )) ) )
276                 return;
277 #endif
278
279         // Increment stats counters
280         if ( scanNum > matrixMaxScans ) matrixMaxScans = scanNum;
281         if ( scanNum == 0 )
282         {
283                 matrixPrevScans = matrixCurScans;
284                 matrixCurScans = 0;
285         }
286         else
287         {
288                 matrixCurScans++;
289         }
290
291         // Read systick for event scheduling
292         uint8_t currentTime = (uint8_t)systick_millis_count;
293
294         // For each strobe, scan each of the sense pins
295         for ( uint8_t strobe = 0; strobe < Matrix_colsNum; strobe++ )
296         {
297                 // Strobe Pin
298                 Matrix_pin( Matrix_cols[ strobe ], Type_StrobeOn );
299
300                 // Scan each of the sense pins
301                 for ( uint8_t sense = 0; sense < Matrix_rowsNum; sense++ )
302                 {
303                         // Key position
304                         uint8_t key = Matrix_colsNum * sense + strobe;
305                         KeyState *state = &Matrix_scanArray[ key ];
306
307                         // If first scan, reset state
308                         if ( scanNum == 0 )
309                         {
310                                 // Set previous state, and reset current state
311                                 state->prevState = state->curState;
312                                 state->curState  = KeyState_Invalid;
313                         }
314
315                         // Signal Detected
316                         // Increment count and right shift opposing count
317                         // This means there is a maximum of scan 13 cycles on a perfect off to on transition
318                         //  (coming from a steady state 0xFFFF off scans)
319                         // Somewhat longer with switch bounciness
320                         // The advantage of this is that the count is ongoing and never needs to be reset
321                         // State still needs to be kept track of to deal with what to send to the Macro module
322                         if ( Matrix_pin( Matrix_rows[ sense ], Type_Sense ) )
323                         {
324                                 // Only update if not going to wrap around
325                                 if ( state->activeCount < DebounceDivThreshold_define ) state->activeCount += 1;
326                                 state->inactiveCount >>= 1;
327                         }
328                         // Signal Not Detected
329                         else
330                         {
331                                 // Only update if not going to wrap around
332                                 if ( state->inactiveCount < DebounceDivThreshold_define ) state->inactiveCount += 1;
333                                 state->activeCount >>= 1;
334                         }
335
336                         // Check for state change if it hasn't been set
337                         // But only if enough time has passed since last state change
338                         // Only check if the minimum number of scans has been met
339                         //   the current state is invalid
340                         //   and either active or inactive count is over the debounce threshold
341                         if ( state->curState == KeyState_Invalid )
342                         {
343                                 // Determine time since last decision
344                                 uint8_t lastTransition = currentTime - state->prevDecisionTime;
345
346                                 // Attempt state transition
347                                 switch ( state->prevState )
348                                 {
349                                 case KeyState_Press:
350                                 case KeyState_Hold:
351                                         if ( state->activeCount > state->inactiveCount )
352                                         {
353                                                 state->curState = KeyState_Hold;
354                                         }
355                                         else
356                                         {
357                                                 // If not enough time has passed since Hold
358                                                 // Keep previous state
359                                                 if ( lastTransition < MinDebounceTime_define )
360                                                 {
361                                                         //warn_print("FAST Release stopped");
362                                                         state->curState = state->prevState;
363                                                         continue;
364                                                 }
365
366                                                 state->curState = KeyState_Release;
367                                         }
368                                         break;
369
370                                 case KeyState_Release:
371                                 case KeyState_Off:
372                                         if ( state->activeCount > state->inactiveCount )
373                                         {
374                                                 // If not enough time has passed since Hold
375                                                 // Keep previous state
376                                                 if ( lastTransition < MinDebounceTime_define )
377                                                 {
378                                                         //warn_print("FAST Press stopped");
379                                                         state->curState = state->prevState;
380                                                         continue;
381                                                 }
382
383                                                 state->curState = KeyState_Press;
384                                         }
385                                         else
386                                         {
387                                                 state->curState = KeyState_Off;
388                                         }
389                                         break;
390
391                                 case KeyState_Invalid:
392                                 default:
393                                         erro_print("Matrix scan bug!! Report me!");
394                                         break;
395                                 }
396
397                                 // Update decision time
398                                 state->prevDecisionTime = currentTime;
399
400                                 // Send keystate to macro module
401                                 #ifndef GHOSTING_MATRIX
402                                 Macro_keyState( key, state->curState );
403                                 #endif
404
405                                 // Matrix Debug, only if there is a state change
406                                 if ( matrixDebugMode && state->curState != state->prevState )
407                                 {
408                                         // Basic debug output
409                                         if ( matrixDebugMode == 1 && state->curState == KeyState_Press )
410                                         {
411                                                 printHex( key );
412                                                 print(" ");
413                                         }
414                                         // State transition debug output
415                                         else if ( matrixDebugMode == 2 )
416                                         {
417                                                 printHex( key );
418                                                 Matrix_keyPositionDebug( state->curState );
419                                                 print(" ");
420                                         }
421                                 }
422                         }
423                 }
424
425                 // Unstrobe Pin
426                 Matrix_pin( Matrix_cols[ strobe ], Type_StrobeOff );
427         }
428
429
430         // Matrix ghosting check and elimination
431         // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
432 #ifdef GHOSTING_MATRIX
433         // strobe = column, sense = row
434
435         // Count (rows) use for columns
436         //print("C ");
437         for ( uint8_t col = 0; col < Matrix_colsNum; col++ )
438         {
439                 uint8_t used = 0;
440                 for ( uint8_t row = 0; row < Matrix_rowsNum; row++ )
441                 {
442                         uint8_t key = Matrix_colsNum * row + col;
443                         KeyState *state = &Matrix_scanArray[ key ];
444                         if ( keyOn(state->curState) )
445                                 used++;
446                 }
447                 //printInt8(used);
448                 col_use[col] = used;
449                 col_ghost[col] = 0;  // clear
450         }
451
452         // Count (columns) use for rows
453         //print("  R ");
454         for ( uint8_t row = 0; row < Matrix_rowsNum; row++ )
455         {
456                 uint8_t used = 0;
457                 for ( uint8_t col = 0; col < Matrix_colsNum; col++ )
458                 {
459                         uint8_t key = Matrix_colsNum * row + col;
460                         KeyState *state = &Matrix_scanArray[ key ];
461                         if ( keyOn(state->curState) )
462                                 used++;
463                 }
464                 //printInt8(used);
465                 row_use[row] = used;
466                 row_ghost[row] = 0;  // clear
467         }
468         
469         // Check if matrix has ghost
470         // Happens when key is pressed and some other key is pressed in same row and another in same column
471         //print("  G ");
472         for ( uint8_t col = 0; col < Matrix_colsNum; col++ )
473         {
474                 for ( uint8_t row = 0; row < Matrix_rowsNum; row++ )
475                 {
476                         uint8_t key = Matrix_colsNum * row + col;
477                         KeyState *state = &Matrix_scanArray[ key ];
478                         if ( keyOn(state->curState) && col_use[col] >= 2 && row_use[row] >= 2 )
479                         {
480                                 // mark col and row as having ghost
481                                 col_ghost[col] = 1;
482                                 row_ghost[row] = 1;
483                                 //print(" ");  printInt8(col);  print(",");  printInt8(row);
484                         }
485                 }
486         }
487         //print( NL );
488
489         // Send keys
490         for ( uint8_t col = 0; col < Matrix_colsNum; col++ )
491         {
492                 for ( uint8_t row = 0; row < Matrix_rowsNum; row++ )
493                 {
494                         uint8_t key = Matrix_colsNum * row + col;
495                         KeyState *state = &Matrix_scanArray[ key ];
496                         KeyGhost *st = &Matrix_ghostArray[ key ];
497                         
498                         // col or row is ghosting (crossed)
499                         uint8_t ghost = (col_ghost[col] > 0 || row_ghost[row] > 0) ? 1 : 0;
500                         
501                         st->prev = st->cur;  // previous
502                         // save state if no ghost or outside ghosted area
503                         if ( ghost == 0 )
504                                 st->saved = state->curState;  // save state if no ghost
505                         // final
506                         // use saved state if ghosting, or current if not
507                         st->cur = ghost > 0 ? st->saved : state->curState;
508                         
509                         //  Send keystate to macro module
510                         KeyPosition k = !st->cur 
511                                 ? (!st->prev ? KeyState_Off : KeyState_Release)
512                                 : ( st->prev ? KeyState_Hold : KeyState_Press);
513                         //if (!st->cur && !st->prev)  k = KeyState_Off; else
514                         //if ( st->cur &&  st->prev)  k = KeyState_Hold; else
515                         //if ( st->cur && !st->prev)  k = KeyState_Press; else
516                         //if (!st->cur &&  st->prev)  k = KeyState_Release;
517                         Macro_keyState( key, k );
518                 }
519         }
520 #endif
521         // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
522
523
524         // State Table Output Debug
525         if ( matrixDebugStateCounter > 0 )
526         {
527                 // Decrement counter
528                 matrixDebugStateCounter--;
529
530                 // Output stats on number of scans being done per USB send
531                 print( NL );
532                 info_msg("Max scans:      ");
533                 printHex( matrixMaxScans );
534                 print( NL );
535                 info_msg("Previous scans: ");
536                 printHex( matrixPrevScans );
537                 print( NL );
538
539                 // Output current scan number
540                 info_msg("Scan Number:    ");
541                 printHex( scanNum );
542                 print( NL );
543
544                 // Display the state info for each key
545                 print("<key>:<previous state><current state> <active count> <inactive count>");
546                 for ( uint8_t key = 0; key < Matrix_maxKeys; key++ )
547                 {
548                         // Every 4 keys, put a newline
549                         if ( key % 4 == 0 )
550                                 print( NL );
551
552                         print("\033[1m0x");
553                         printHex_op( key, 2 );
554                         print("\033[0m");
555                         print(":");
556                         Matrix_keyPositionDebug( Matrix_scanArray[ key ].prevState );
557                         Matrix_keyPositionDebug( Matrix_scanArray[ key ].curState );
558                         print(" 0x");
559                         printHex_op( Matrix_scanArray[ key ].activeCount, 4 );
560                         print(" 0x");
561                         printHex_op( Matrix_scanArray[ key ].inactiveCount, 4 );
562                         print(" ");
563                 }
564
565                 print( NL );
566         }
567 }
568
569
570 // ----- CLI Command Functions -----
571
572 void cliFunc_matrixDebug ( char* args )
573 {
574         // Parse number from argument
575         //  NOTE: Only first argument is used
576         char* arg1Ptr;
577         char* arg2Ptr;
578         CLI_argumentIsolation( args, &arg1Ptr, &arg2Ptr );
579
580         // Set the matrix debug flag depending on the argument
581         // If no argument, set to scan code only
582         // If set to T, set to state transition
583         switch ( arg1Ptr[0] )
584         {
585         // T as argument
586         case 'T':
587         case 't':
588                 matrixDebugMode = matrixDebugMode != 2 ? 2 : 0;
589                 break;
590
591         // No argument
592         case '\0':
593                 matrixDebugMode = matrixDebugMode != 1 ? 1 : 0;
594                 break;
595
596         // Invalid argument
597         default:
598                 return;
599         }
600
601         print( NL );
602         info_msg("Matrix Debug Mode: ");
603         printInt8( matrixDebugMode );
604 }
605
606 void cliFunc_matrixState ( char* args )
607 {
608         // Parse number from argument
609         //  NOTE: Only first argument is used
610         char* arg1Ptr;
611         char* arg2Ptr;
612         CLI_argumentIsolation( args, &arg1Ptr, &arg2Ptr );
613
614         // Default to 1 if no argument is given
615         matrixDebugStateCounter = 1;
616
617         if ( arg1Ptr[0] != '\0' )
618         {
619                 matrixDebugStateCounter = (uint16_t)numToInt( arg1Ptr );
620         }
621 }
622