void cliFunc_current( char* args ) { print( NL ); info_msg("Current available: "); printInt16( Output_current_available() ); print(" mA"); }
void cliFunc_macroList( char* args ) { // Show pending key events print( NL ); info_msg("Pending Key Events: "); printInt16( (uint16_t)macroTriggerEventBufferSize ); print(" : "); for ( var_uint_t key = 0; key < macroTriggerEventBufferSize; key++ ) { printHex( macroTriggerEventBuffer[ key ].index ); print(" "); } // Show pending trigger macros print( NL ); info_msg("Pending Trigger Macros: "); printInt16( (uint16_t)macroTriggerMacroPendingListSize ); print(" : "); for ( var_uint_t macro = 0; macro < macroTriggerMacroPendingListSize; macro++ ) { printHex( macroTriggerMacroPendingList[ macro ] ); print(" "); } // Show pending result macros print( NL ); info_msg("Pending Result Macros: "); printInt16( (uint16_t)macroResultMacroPendingList.size ); print(" : "); for ( var_uint_t macro = 0; macro < macroResultMacroPendingList.size; macro++ ) { printHex( macroResultMacroPendingList.data[ macro ].index ); print(" "); } // Show available trigger macro indices print( NL ); info_msg("Trigger Macros Range: T0 -> T"); printInt16( (uint16_t)TriggerMacroNum - 1 ); // Hopefully large enough :P (can't assume 32-bit) // Show available result macro indices print( NL ); info_msg("Result Macros Range: R0 -> R"); printInt16( (uint16_t)ResultMacroNum - 1 ); // Hopefully large enough :P (can't assume 32-bit) // Show Trigger to Result Macro Links print( NL ); info_msg("Trigger : Result Macro Pairs"); for ( var_uint_t macro = 0; macro < TriggerMacroNum; macro++ ) { print( NL ); print("\tT"); printInt16( (uint16_t)macro ); // Hopefully large enough :P (can't assume 32-bit) print(" : R"); printInt16( (uint16_t)TriggerMacroList[ macro ].result ); // Hopefully large enough :P (can't assume 32-bit) } }
void cliFunc_usbInitTime( char* args ) { // Calculate overall USB initialization time // XXX A protocol analyzer will be more accurate, however, this is built-in and easier to collect data print(NL); info_msg("USB Init Time: "); printInt32( USBInit_TimeEnd - USBInit_TimeStart ); print(" ms - "); printInt16( USBInit_Ticks ); print(" ticks"); }
// Update layer state // States: // * 0x00 - Off // * 0x01 - Activate // * 0x02 - On // * 0x03 - Deactivate void Macro_layerState( uint16_t layerIndex, uint8_t state ) { // Lookup done based on size of layerIndex uint8_t index = 0; TriggerType type = TriggerType_Layer1; // Only add to macro trigger list if one of three states // Mask around Shift/Latch/Lock state switch ( state & ScheduleType_D ) { case ScheduleType_A: // Activate case ScheduleType_On: // On case ScheduleType_D: // Deactivate // Check if layer is out of range if ( layerIndex > LayerNum_KLL ) { warn_msg("LayerIndex is out of range/not defined: "); printInt16( layerIndex ); print( NL ); return; } // Determine which type if ( layerIndex < 256 ) { index = layerIndex; } else if ( layerIndex < 512 ) { index = layerIndex - 256; type = TriggerType_Layer2; } else if ( layerIndex < 768 ) { index = layerIndex - 512; type = TriggerType_Layer3; } else if ( layerIndex < 1024 ) { index = layerIndex - 768; type = TriggerType_Layer4; } macroTriggerEventBuffer[ macroTriggerEventBufferSize ].index = index; macroTriggerEventBuffer[ macroTriggerEventBufferSize ].state = state; macroTriggerEventBuffer[ macroTriggerEventBufferSize ].type = type; macroTriggerEventBufferSize++; break; } }
// Update animation state // States: // * 0x00 - Off // * 0x06 - Done // * 0x07 - Repeat void Macro_animationState( uint16_t animationIndex, uint8_t state ) { // Lookup done based on size of layerIndex uint8_t index = 0; TriggerType type = TriggerType_Animation1; // Only add to macro trigger list if one of three states switch ( state ) { case ScheduleType_Done: // Activate case ScheduleType_Repeat: // On // Check if animation index is out of range if ( animationIndex > AnimationNum_KLL ) { warn_msg("AnimationIndex is out of range/not defined: "); printInt16( animationIndex ); print( NL ); return; } // Determine which type if ( animationIndex < 256 ) { index = animationIndex; } else if ( animationIndex < 512 ) { index = animationIndex - 256; type = TriggerType_Animation2; } else if ( animationIndex < 768 ) { index = animationIndex - 512; type = TriggerType_Animation3; } else if ( animationIndex < 1024 ) { index = animationIndex - 768; type = TriggerType_Animation4; } macroTriggerEventBuffer[ macroTriggerEventBufferSize ].index = index; macroTriggerEventBuffer[ macroTriggerEventBufferSize ].state = state; macroTriggerEventBuffer[ macroTriggerEventBufferSize ].type = type; macroTriggerEventBufferSize++; break; } }
// Update Time State trigger // Only valid with Sleep/Resume and Inactive/Active triggers // States: // * 0x00 - Off // * 0x01 - Activate // * 0x02 - On // * 0x03 - Deactivate void Macro_timeState( uint8_t type, uint16_t cur_time, uint8_t state ) { // Make sure this is a valid trigger type switch ( type ) { case TriggerType_Sleep1: case TriggerType_Resume1: case TriggerType_Inactive1: case TriggerType_Active1: break; // Ignore if not the correct type default: warn_msg("Invalid time state trigger update: "); printHex( type ); print(NL); return; } // cur_time is controlled by the caller // When this function called the trigger is active if ( cur_time > 0xFF ) { warn_msg("Only 255 time instances are accepted for a time state trigger: "); printInt16( cur_time ); print(NL); return; } uint8_t index = cur_time; // Only add to macro trigger list if one of three states switch ( state ) { case ScheduleType_A: // Activate case ScheduleType_On: // On case ScheduleType_D: // Deactivate macroTriggerEventBuffer[ macroTriggerEventBufferSize ].index = index; macroTriggerEventBuffer[ macroTriggerEventBufferSize ].state = state; macroTriggerEventBuffer[ macroTriggerEventBufferSize ].type = type; macroTriggerEventBufferSize++; break; } }
void cliFunc_idle( char* args ) { print( NL ); // Parse number from argument // NOTE: Only first argument is used char* arg1Ptr; char* arg2Ptr; CLI_argumentIsolation( args, &arg1Ptr, &arg2Ptr ); // Set Idle count if ( arg1Ptr[0] != '\0' ) { uint8_t idle = (uint8_t)numToInt( arg1Ptr ); USBKeys_Idle_Config = idle; } // Show Idle count info_msg("USB Idle Config: "); printInt16( 4 * USBKeys_Idle_Config ); print(" ms - "); printInt8( USBKeys_Idle_Config ); }
// Macro Processing Loop, called from the periodic execution thread // Called once per USB buffer send void Macro_periodic() { // Latency measurement Latency_start_time( macroLatencyResource ); #if defined(ConnectEnabled_define) // Only compile in if a Connect node module is available // If this is a interconnect slave node, send all scancodes to master node if ( !Connect_master ) { if ( macroTriggerEventBufferSize > 0 ) { Connect_send_ScanCode( Connect_id, macroTriggerEventBuffer, macroTriggerEventBufferSize ); macroTriggerEventBufferSize = 0; } return; } #endif #if defined(ConnectEnabled_define) || defined(PressReleaseCache_define) #if defined(ConnectEnabled_define) // Check if there are any ScanCodes in the interconnect cache to process if ( Connect_master && macroInterconnectCacheSize > 0 ) #endif { // Iterate over all the cache ScanCodes uint8_t currentInterconnectCacheSize = macroInterconnectCacheSize; macroInterconnectCacheSize = 0; for ( uint8_t c = 0; c < currentInterconnectCacheSize; c++ ) { // Add to the trigger list macroTriggerEventBuffer[ macroTriggerEventBufferSize++ ] = macroInterconnectCache[ c ]; // TODO Handle other TriggerGuide types (e.g. analog) switch ( macroInterconnectCache[ c ].type ) { // Normal (Press/Hold/Release) case TriggerType_Switch1: case TriggerType_Switch2: case TriggerType_Switch3: case TriggerType_Switch4: case TriggerType_LED1: // Decide what to do based on the current state switch ( macroInterconnectCache[ c ].state ) { // Re-add to interconnect cache in hold state case ScheduleType_P: // Press //case ScheduleType_H: // Hold // XXX Why does this not work? -HaaTa macroInterconnectCache[ c ].state = ScheduleType_H; macroInterconnectCache[ macroInterconnectCacheSize++ ] = macroInterconnectCache[ c ]; break; case ScheduleType_R: // Release break; // Otherwise, do not re-add default: break; } break; // Not implemented default: erro_msg("Interconnect Trigger Event Type - Not Implemented "); printInt8( macroInterconnectCache[ c ].type ); print( NL ); break; } } } #endif // Macro incoming state debug switch ( macroDebugMode ) { case 1: case 2: // Iterate over incoming triggers for ( uint16_t trigger = 0; trigger < macroTriggerEventBufferSize; trigger++ ) { // Show debug info about incoming trigger Macro_showTriggerEvent( ¯oTriggerEventBuffer[trigger] ); print( NL ); } case 3: default: break; } // Check macroTriggerEventBufferSize to make sure no overflow if ( macroTriggerEventBufferSize >= MaxScanCode_KLL ) { // No scancodes defined if ( MaxScanCode_KLL == 0 ) { warn_print("No scancodes defined! Check your BaseMap!"); } // Bug! else { erro_msg("Macro Trigger Event Overflow! Serious Bug! "); printInt16( macroTriggerEventBufferSize ); print( NL ); macroTriggerEventBufferSize = 0; } } // If the pause flag is set, only process if the step counter is non-zero if ( macroPauseMode ) { if ( macroStepCounter == 0 ) return; // Proceed, decrementing the step counter macroStepCounter--; dbug_print("Macro Step"); } // Process Trigger Macros Trigger_process(); // Store events processed var_uint_t macroTriggerEventBufferSize_processed = macroTriggerEventBufferSize; // Reset TriggerList buffer macroTriggerEventBufferSize = 0; // Process result macros Result_process(); // Signal buffer that we've used it Scan_finishedWithMacro( macroTriggerEventBufferSize_processed ); #if defined(_host_) // Signal host to read layer state Output_callback( "layerState", "" ); #endif // Latency measurement Latency_end_time( macroLatencyResource ); // If Macro debug mode is set, clear the USB Buffer #if defined(Output_USBEnabled_define) if ( macroDebugMode == 1 || macroDebugMode == 3 ) { USBKeys_primary.changed = 0; } #endif }
// Update the scancode key state // States: // * 0x00 - Off // * 0x01 - Pressed // * 0x02 - Held // * 0x03 - Released // * 0x04 - Unpressed (this is currently ignored) void Macro_keyState( uint16_t scanCode, uint8_t state ) { #if defined(ConnectEnabled_define) // Only compile in if a Connect node module is available if ( !Connect_master ) { // ScanCodes are only added if there was a state change (on/off) switch ( state ) { case ScheduleType_O: // Off case ScheduleType_H: // Held return; } } #endif // Lookup done based on size of scanCode uint8_t index = 0; TriggerType type = TriggerType_Switch1; // Only add to macro trigger list if one of three states switch ( state ) { case ScheduleType_P: // Pressed case ScheduleType_H: // Held case ScheduleType_R: // Released // Check if ScanCode is out of range if ( scanCode > MaxScanCode_KLL ) { warn_msg("ScanCode is out of range/not defined: "); printInt16( scanCode ); print( NL ); return; } // Determine which type if ( scanCode < 256 ) { index = scanCode; } else if ( scanCode < 512 ) { index = scanCode - 256; type = TriggerType_Switch2; } else if ( scanCode < 768 ) { index = scanCode - 512; type = TriggerType_Switch3; } else if ( scanCode < 1024 ) { index = scanCode - 768; type = TriggerType_Switch4; } macroTriggerEventBuffer[ macroTriggerEventBufferSize ].index = index; macroTriggerEventBuffer[ macroTriggerEventBufferSize ].state = state; macroTriggerEventBuffer[ macroTriggerEventBufferSize ].type = type; macroTriggerEventBufferSize++; break; } }
void macroDebugShowResult( var_uint_t index ) { // Only proceed if the macro exists if ( index >= ResultMacroNum ) return; // Trigger Macro Show const ResultMacro *macro = &ResultMacroList[ index ]; print( NL ); info_msg("Result Macro Index: "); printInt16( (uint16_t)index ); // Hopefully large enough :P (can't assume 32-bit) print( NL ); // Read the comboLength for combo in the sequence (sequence of combos) var_uint_t pos = 0; uint8_t comboLength = macro->guide[ pos++ ]; // Iterate through and interpret the guide while ( comboLength != 0 ) { // Function Counter, used to keep track of the combos processed var_uint_t funcCount = 0; // Iterate through the combo while ( funcCount < comboLength ) { // Assign TriggerGuide element (key type, state and scancode) ResultGuide *guide = (ResultGuide*)(¯o->guide[ pos ]); // Display Function Index printHex( guide->index ); print("|"); // Display Function Ptr Address printHex( (nat_ptr_t)CapabilitiesList[ guide->index ].func ); print("|"); // Display/Lookup Capability Name (utilize debug mode of capability) void (*capability)(TriggerMacro*, uint8_t, uint8_t, uint8_t*) = \ (void(*)(TriggerMacro*, uint8_t, uint8_t, uint8_t*))(CapabilitiesList[ guide->index ].func); capability( 0, 0xFF, 0xFF, 0 ); // Display Argument(s) print("("); for ( var_uint_t arg = 0; arg < CapabilitiesList[ guide->index ].argCount; arg++ ) { // Arguments are only 8 bit values printHex( (&guide->args)[ arg ] ); // Only show arg separator if there are args left if ( arg + 1 < CapabilitiesList[ guide->index ].argCount ) print(","); } print(")"); // Increment position pos += ResultGuideSize( guide ); // Increment function count funcCount++; // Only show combo separator if there are combos left in the sequence element if ( funcCount < comboLength ) print("+"); } // Read the next comboLength comboLength = macro->guide[ pos++ ]; // Only show sequence separator if there is another combo to process if ( comboLength != 0 ) print(";"); } /* XXX (HaaTa) Fix for ring-buffer record list ResultMacroRecord *record = &ResultMacroRecordList[ index ]; // Display current position print( NL "Position: " ); printInt16( (uint16_t)record->pos ); // Hopefully large enough :P (can't assume 32-bit) print(" "); printInt16( (uint16_t)record->prevPos ); // Display final trigger state/type print( NL "Final Trigger State (State/Type): " ); printHex( record->state ); print("/"); printHex( record->stateType ); */ }
void macroDebugShowTrigger( var_uint_t index ) { // Only proceed if the macro exists if ( index >= TriggerMacroNum ) return; // Trigger Macro Show const TriggerMacro *macro = &TriggerMacroList[ index ]; TriggerMacroRecord *record = &TriggerMacroRecordList[ index ]; print( NL ); info_msg("Trigger Macro Index: "); printInt16( (uint16_t)index ); // Hopefully large enough :P (can't assume 32-bit) print( NL ); // Read the comboLength for combo in the sequence (sequence of combos) var_uint_t pos = 0; uint8_t comboLength = macro->guide[ pos ]; // Iterate through and interpret the guide while ( comboLength != 0 ) { // Initial position of the combo var_uint_t comboPos = ++pos; // Iterate through the combo while ( pos < comboLength * TriggerGuideSize + comboPos ) { // Assign TriggerGuide element (key type, state and scancode) TriggerGuide *guide = (TriggerGuide*)(¯o->guide[ pos ]); // Display guide information about trigger key printHex( guide->scanCode ); print("|"); printHex( guide->type ); print("|"); printHex( guide->state ); // Increment position pos += TriggerGuideSize; // Only show combo separator if there are combos left in the sequence element if ( pos < comboLength * TriggerGuideSize + comboPos ) print("+"); } // Read the next comboLength comboLength = macro->guide[ pos ]; // Only show sequence separator if there is another combo to process if ( comboLength != 0 ) print(";"); } // Display current position print( NL "Position: " ); printInt16( (uint16_t)record->pos ); // Hopefully large enough :P (can't assume 32-bit) print(" "); printInt16( (uint16_t)record->prevPos ); // Display result macro index print( NL "Result Macro Index: " ); printInt16( (uint16_t)macro->result ); // Hopefully large enough :P (can't assume 32-bit) // Display trigger macro state print( NL "Trigger Macro State: " ); switch ( record->state ) { case TriggerMacro_Press: print("Press"); break; case TriggerMacro_Release: print("Release"); break; case TriggerMacro_PressRelease: print("Press|Release"); break; case TriggerMacro_Waiting: print("Waiting"); break; } }
// Evaluate/Update TriggerMacro TriggerMacroEval Trigger_evalTriggerMacro( var_uint_t triggerMacroIndex ) { // Lookup TriggerMacro const TriggerMacro *macro = &TriggerMacroList[ triggerMacroIndex ]; TriggerMacroRecord *record = &TriggerMacroRecordList[ triggerMacroIndex ]; // Check if this is a long Trigger Macro uint8_t long_trigger_macro = Trigger_isLongTriggerMacro( macro ); // Long Macro if ( long_trigger_macro ) { // Check if macro has finished and should be incremented sequence elements if ( record->state != TriggerMacro_Waiting ) { record->prevPos = record->pos; record->pos = record->pos + macro->guide[ record->pos ] * TriggerGuideSize + 1; } // Current Macro position var_uint_t pos = record->pos; // Length of the combo being processed uint8_t comboLength = macro->guide[ pos ] * TriggerGuideSize; TriggerMacroVote overallVote = TriggerMacroVote_Invalid; // Iterate through the items in the combo, voting the on the key state // If any of the pressed keys do not match, fail the macro if ( comboLength != 0 ) { overallVote |= Trigger_overallVote( macro, record, long_trigger_macro, pos ); } // If this is a sequence, and have processed at least one vote already // then we need to keep track of releases if ( pos != 0 ) { overallVote |= Trigger_overallVote( macro, record, long_trigger_macro, record->prevPos ); } // If no pass vote was found after scanning the entire combo // And this is the first position in the combo, just remove it (nothing important happened) if ( overallVote & TriggerMacroVote_DoNothingRelease && pos == 0 ) { overallVote |= TriggerMacroVote_Fail; } // Vote Debug switch ( voteDebugMode ) { case 1: Trigger_showTriggerMacroVote( overallVote, long_trigger_macro ); print(" TriggerMacroList["); printInt16( triggerMacroIndex ); print("]"); break; } // Decide new state of macro after voting // Fail macro, remove from pending list if ( overallVote & TriggerMacroVote_Fail ) { return TriggerMacroEval_Remove; } // Do nothing, incorrect key is being held or released else if ( overallVote & TriggerMacroVote_DoNothing ) { record->state = TriggerMacro_Waiting; // Just doing nothing :) } // If ready for release state but we get both release and the next press at the same time // This is unlikely, but possible else if ( ( overallVote & TriggerMacroVote_PassRelease ) == TriggerMacroVote_PassRelease ) { record->state = TriggerMacro_PressRelease; // If this is the last combo in the sequence, trigger result if ( macro->guide[ pos + comboLength + 1 ] == 0 ) { return TriggerMacroEval_DoResultAndRemove; } } // If ready for transition and in Press state, increment combo position else if ( overallVote & TriggerMacroVote_Release && record->state & TriggerMacro_Press ) { record->state = TriggerMacro_Release; // If this is the last combo in the sequence, trigger result // Or, the final release of a sequence if ( comboLength == 0 || macro->guide[ pos + comboLength + 1 ] == 0 ) { return TriggerMacroEval_DoResultAndRemove; } } // If passing and in Waiting state, set macro state to Press else if ( overallVote & TriggerMacroVote_Pass && ( record->state == TriggerMacro_Waiting || record->state & TriggerMacro_Press ) ) { record->state = TriggerMacro_Press; // If this is the last combo in the sequence, trigger result if ( macro->guide[ pos + comboLength + 1 ] == 0 ) { return TriggerMacroEval_DoResultAndRemove; } } } // Short Macro else { // Current Macro position var_uint_t pos = record->pos; // Iterate through the items in the combo, voting the on the key state // If any of the pressed keys do not match, fail the macro TriggerMacroVote overallVote = Trigger_overallVote( macro, record, long_trigger_macro, pos ); // Vote Debug switch ( voteDebugMode ) { case 1: Trigger_showTriggerMacroVote( overallVote, long_trigger_macro ); print(" TriggerMacroList["); printInt16( triggerMacroIndex ); print("]"); break; } // Decide new state of macro after voting // Fail macro, remove from pending list if ( overallVote & TriggerMacroVote_Fail ) { return TriggerMacroEval_Remove; } // If passing and in Waiting state, set macro state to Press // And trigger result else if ( overallVote & TriggerMacroVote_Pass && ( record->state == TriggerMacro_Waiting || record->state == TriggerMacro_Press ) ) { record->state = TriggerMacro_Press; // Long result macro (more than 1 combo) if ( Trigger_isLongResultMacro( &ResultMacroList[ macro->result ] ) ) { // Only ever trigger result once, on press if ( overallVote == TriggerMacroVote_Pass ) { return TriggerMacroEval_DoResultAndRemove; } } // Short result macro else { // Trigger result continuously return TriggerMacroEval_DoResult; } } // Otherwise, just remove the macro on key release else if ( overallVote & TriggerMacroVote_Release ) { // Long result macro (more than 1 combo) are ignored (only on press) if ( !Trigger_isLongResultMacro( &ResultMacroList[ macro->result ] ) ) { record->state = TriggerMacro_Release; return TriggerMacroEval_DoResultAndRemove; } } // This is a short macro, just remove it // The state can be rebuilt on the next iteration return TriggerMacroEval_Remove; } return TriggerMacroEval_DoNothing; }
// Single strobe matrix scan // Only goes through a single strobe // This module keeps track of the next strobe to scan uint8_t Matrix_single_scan() { // Start latency measurement Latency_start_time( matrixLatencyResource ); // Read systick for event scheduling uint32_t currentTime = systick_millis_count; // Current strobe uint8_t strobe = matrixCurrentStrobe; // XXX (HaaTa) // Before strobing drain each sense line // This helps with faulty pull-up resistors (particularily with SAM4S) for ( uint8_t sense = 0; sense < Matrix_rowsNum; sense++ ) { GPIO_Ctrl( Matrix_rows[ sense ], GPIO_Type_DriveSetup, Matrix_type ); #if ScanCodeMatrixInvert_define == 2 // GPIO_Config_Pulldown GPIO_Ctrl( Matrix_rows[ sense ], GPIO_Type_DriveLow, Matrix_type ); #elif ScanCodeMatrixInvert_define == 1 // GPIO_Config_Pullup GPIO_Ctrl( Matrix_rows[ sense ], GPIO_Type_DriveHigh, Matrix_type ); #endif GPIO_Ctrl( Matrix_rows[ sense ], GPIO_Type_ReadSetup, Matrix_type ); } // Strobe Pin GPIO_Ctrl( Matrix_cols[ strobe ], GPIO_Type_DriveHigh, Matrix_type ); // Used to allow the strobe signal to propagate, generally not required if ( strobeDelayTime > 0 ) { delay_us( strobeDelayTime ); } // Scan each of the sense pins for ( uint8_t sense = 0; sense < Matrix_rowsNum; sense++ ) { // Key position uint16_t key = Matrix_colsNum * sense + strobe; #if ScanCodeRemapping_define == 1 uint16_t key_disp = matrixScanCodeRemappingMatrix[key]; #else uint16_t key_disp = key + 1; // 1-indexed for reporting purposes #endif // Check bounds, before attempting to scan // 1-indexed as ScanCode 0 is not used if ( key_disp > MaxScanCode_KLL || key_disp == 0 ) { continue; } volatile KeyState *state = &Matrix_scanArray[ key ]; // Signal Detected // Increment count and right shift opposing count // This means there is a maximum of scan 13 cycles on a perfect off to on transition // (coming from a steady state 0xFFFF off scans) // Somewhat longer with switch bounciness // The advantage of this is that the count is ongoing and never needs to be reset // State still needs to be kept track of to deal with what to send to the Macro module // Compared against the default state value (ScanCodeMatrixInvert_define), usually 0 if ( GPIO_Ctrl( Matrix_rows[ sense ], GPIO_Type_Read, Matrix_type ) != ScanCodeMatrixInvert_define ) { // Only update if not going to wrap around if ( state->activeCount < DebounceDivThreshold ) state->activeCount += 1; state->inactiveCount >>= 1; } // Signal Not Detected else { // Only update if not going to wrap around if ( state->inactiveCount < DebounceDivThreshold ) state->inactiveCount += 1; state->activeCount >>= 1; } // Check for state change // But only if: // 1) Enough time has passed since last state change // 2) Either active or inactive count is over the debounce threshold // Update previous state state->prevState = state->curState; // Determine time since last decision uint32_t lastTransition = currentTime - state->prevDecisionTime; // Attempt state transition switch ( state->prevState ) { case KeyState_Press: case KeyState_Hold: if ( state->activeCount > state->inactiveCount ) { state->curState = KeyState_Hold; } else { // If not enough time has passed since Hold // Keep previous state if ( lastTransition < debounceExpiryTime ) { state->curState = state->prevState; Macro_keyState( key_disp, state->curState ); continue; } state->curState = KeyState_Release; } break; case KeyState_Release: case KeyState_Off: if ( state->activeCount > state->inactiveCount ) { // If not enough time has passed since Hold // Keep previous state if ( lastTransition < debounceExpiryTime ) { state->curState = state->prevState; Macro_keyState( key_disp, state->curState ); continue; } state->curState = KeyState_Press; } else { state->curState = KeyState_Off; } break; case KeyState_Invalid: default: erro_msg("Matrix scan bug!! Report me! - "); printHex( state->prevState ); print(" Col: "); printHex( strobe ); print(" Row: "); printHex( sense ); print(" Key: "); printHex( key_disp ); print( NL ); break; } // Update decision time state->prevDecisionTime = currentTime; // Send keystate to macro module Macro_keyState( key_disp, state->curState ); // Check for activity and inactivity if ( state->curState != KeyState_Off ) { matrixStateActiveCount++; } switch ( state->curState ) { case KeyState_Press: matrixStatePressCount++; break; case KeyState_Release: matrixStateReleaseCount++; break; default: break; } // Matrix Debug, only if there is a state change if ( matrixDebugMode && state->curState != state->prevState ) { // Basic debug output if ( matrixDebugMode == 1 && state->curState == KeyState_Press ) { printInt16( key_disp ); print(":"); printHex( key_disp ); print(" "); } // State transition debug output else if ( matrixDebugMode == 2 ) { printInt16( key_disp ); Matrix_keyPositionDebug( state->curState ); print(" "); } else if ( matrixDebugMode == 3 ) { print("\033[1m"); printInt16( key_disp ); print("\033[0m"); print(":"); Matrix_keyPositionDebug( Matrix_scanArray[ key ].prevState ); Matrix_keyPositionDebug( Matrix_scanArray[ key ].curState ); print(" 0x"); printHex_op( state->activeCount, 2 ); print(" 0x"); printHex_op( state->inactiveCount, 2 ); print(" "); printInt32( lastTransition ); print( NL ); } } }