// Port State processing loop inline uint8_t Port_scan() { // Latency measurement start Latency_start_time( portLatencyResource ); // TODO Add in interconnect line cross #define USBPortSwapDelay_ms 1000 // Wait 1000 ms before checking // Only check for swapping after delay uint32_t wait_ms = systick_millis_count - Port_lastcheck_ms; if ( wait_ms > USBPortSwapDelay_ms ) { // Update timeout Port_lastcheck_ms = systick_millis_count; // USB not initialized, attempt to swap if ( !usb_configured() ) { Port_usb_swap(); } } // Latency measurement end Latency_end_time( portLatencyResource ); return 0; }
// Scan for updates in the master/slave // - Interrupts will deal with most input functions // - Used to send queries // - SyncEvent is sent immediately once the current command is sent // - SyncEvent is also blocking until sent void Connect_scan() { // Latency measurement start Latency_start_time( connectLatencyResource ); // Check if initially configured as a slave and usb comes up // Then reconfigure as a master if ( !Connect_master && Output_Available && !Connect_override ) { Connect_setup( Output_Available, 0 ); } // Limit how often we do cable checks //uint32_t time_compare = 0x007; // XXX Used for debugging cables -HaaTa uint32_t time_compare = 0x7FF; // Must be all 1's, 0x3FF is valid, 0x4FF is not uint32_t current_time = systick_millis_count; if ( Connect_lastCheck != current_time && ( current_time & time_compare ) == time_compare ) { // Make sure we don't double check if the clock speed is too high Connect_lastCheck = current_time; // Send a cable check command of 2 bytes Connect_send_CableCheck( UARTConnectCableCheckLength_define ); // If this is a slave, and we don't have an id yeth // Don't bother sending if there are cable issues if ( !Connect_master && Connect_id == DEFAULT_SLAVE_ID && Connect_cableOkMaster ) { Connect_send_IdRequest(); } } // Only process commands if uarts have been configured if ( uarts_configured ) { // Check if Tx Buffers are empty and the Tx Ring buffers have data to send // This happens if there was previously nothing to send #if defined(_kinetis_) if ( uart_tx_buf[ 0 ].items > 0 && UART0_TCFIFO == 0 ) uart_fillTxFifo( 0 ); if ( uart_tx_buf[ 1 ].items > 0 && UART1_TCFIFO == 0 ) uart_fillTxFifo( 1 ); #elif defined(_sam_) //SAM TODO #endif // Process Rx Buffers Connect_rx_process( 0 ); Connect_rx_process( 1 ); } // Latency measurement end Latency_end_time( connectLatencyResource ); }
// USB Data Poll inline void Output_poll() { // Start latency measurement Latency_start_time( outputPollLatencyResource ); #if enableRawIO_define == 1 // HID-IO Process HIDIO_process(); #endif // End latency measurement Latency_end_time( outputPollLatencyResource ); }
// USB Data Periodic inline void Output_periodic() { // Start latency measurement Latency_start_time( outputPeriodicLatencyResource ); #if enableMouse_define == 1 // Process mouse actions while ( USBMouse_Changed ) Output_callback( "mouse_send", "" ); #endif #if enableKeyboard_define == 1 // Boot Mode Only, unset stale keys if ( USBKeys_Protocol == 0 ) { for ( uint8_t c = USBKeys_Sent; c < USB_BOOT_MAX_KEYS; c++ ) { USBKeys_primary.keys[c] = 0; } } // Send keypresses while there are pending changes while ( USBKeys_primary.changed ) Output_callback( "keyboard_send", "" ); // Clear keys sent USBKeys_Sent = 0; // Signal Scan Module we are finished switch ( USBKeys_Protocol ) { case 0: // Boot Mode // Clear modifiers only in boot mode USBKeys_primary.modifiers = 0; Scan_finishedWithOutput( USBKeys_Sent <= USB_BOOT_MAX_KEYS ? USBKeys_Sent : USB_BOOT_MAX_KEYS ); break; case 1: // NKRO Mode Scan_finishedWithOutput( USBKeys_Sent ); break; } #endif // End latency measurement Latency_end_time( outputPeriodicLatencyResource ); }
inline void LED_scan() { // Latency measurement start Latency_start_time( ledLatencyResource ); // Check for current change event if ( LED_currentEvent ) { // Turn LEDs off in low power mode if ( LED_currentEvent < 150 ) { LED_enable_current = 0; // Pause animations and clear display Pixel_setAnimationControl( AnimationControl_WipePause ); } else { LED_enable_current = 1; // Start animations Pixel_setAnimationControl( AnimationControl_Forward ); } LED_currentEvent = 0; } // Check if an LED_pause is set // Some ISSI operations need a clear buffer, but still have the chip running if ( LED_pause ) goto led_finish_scan; // Check enable state if ( LED_enable && LED_enable_current ) { // Disable Hardware shutdown of ISSI chips (pull high) GPIO_Ctrl( hardware_shutdown_pin, GPIO_Type_DriveHigh, GPIO_Config_Pullup ); } // Only write pages to I2C if chip is enabled (i.e. Hardware shutdown is disabled) else { // Enable hardware shutdown GPIO_Ctrl( hardware_shutdown_pin, GPIO_Type_DriveLow, GPIO_Config_Pullup ); goto led_finish_scan; } // Check if any I2C buses have errored // Reset the buses and restart the Frame State if ( i2c_error() ) { i2c_reset(); Pixel_FrameState = FrameState_Update; } // Only start if we haven't already // And if we've finished updating the buffers if ( Pixel_FrameState == FrameState_Sending ) goto led_finish_scan; // Only send frame to ISSI chip if buffers are ready if ( Pixel_FrameState != FrameState_Ready ) goto led_finish_scan; // Adjust frame rate (i.e. delay and do something else for a bit) Time duration = Time_duration( LED_timePrev ); if ( duration.ms < LED_framerate ) goto led_finish_scan; // FPS Display if ( LED_displayFPS ) { // Show frame calculation dbug_msg("1frame/"); printInt32( Time_ms( duration ) ); print("ms + "); printInt32( duration.ticks ); print(" ticks"); // Check if we're not meeting frame rate if ( duration.ms > LED_framerate ) { print(" - Could not meet framerate: "); printInt32( LED_framerate ); } print( NL ); } // Emulated brightness control // Lower brightness by LED_brightness #if ISSI_Chip_31FL3731_define == 1 for ( uint8_t chip = 0; chip < ISSI_Chips_define; chip++ ) { for ( uint8_t ch = 0; ch < LED_EnableBufferLength; ch++ ) { LED_pageBuffer_brightness[ chip ].ledctrl[ ch ] = LED_pageBuffer[ chip ].ledctrl[ ch ]; } for ( uint8_t ch = 0; ch < LED_BufferLength; ch++ ) { // Don't modify is 0 if ( LED_pageBuffer[ chip ].buffer[ ch ] == 0 || LED_brightness == 0 ) { LED_pageBuffer_brightness[ chip ].buffer[ ch ] = 0; continue; } // XXX (HaaTa) Yes, this is a bit slow, but it's pretty accurate LED_pageBuffer_brightness[ chip ].buffer[ ch ] = (LED_pageBuffer[ chip ].buffer[ ch ] * LED_brightness) / 0xFF; } } #endif // Update frame start time LED_timePrev = Time_now(); // Set the page of all the ISSI chips // This way we can easily link the buffers to send the brightnesses in the background for ( uint8_t ch = 0; ch < ISSI_Chips_define; ch++ ) { uint8_t bus = LED_ChannelMapping[ ch ].bus; // Page Setup LED_setupPage( bus, LED_ChannelMapping[ ch ].addr, ISSI_LEDPwmPage ); } // Send current set of buffers // Uses interrupts to send to all the ISSI chips // Pixel_FrameState will be updated when complete LED_chipSend = 0; // Start with chip 0 LED_linkedSend(); led_finish_scan: // Latency measurement end Latency_end_time( ledLatencyResource ); }
// 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 }
// 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 ); } } }
inline void LED_scan() { // Latency measurement start Latency_start_time( ledLatencyResource ); // Check for current change event if ( LED_currentEvent ) { // Turn LEDs off in low power mode if ( LED_currentEvent < 150 ) { LED_enable = 0; // Pause animations and clear display Pixel_setAnimationControl( AnimationControl_WipePause ); } else { LED_enable = 1; // Start animations Pixel_setAnimationControl( AnimationControl_Forward ); } LED_currentEvent = 0; } // Check if an LED_pause is set // Some ISSI operations need a clear buffer, but still have the chip running if ( LED_pause ) goto led_finish_scan; // Check enable state if ( LED_enable ) { // Disable Hardware shutdown of ISSI chips (pull high) GPIOB_PSOR |= (1<<16); } // Only write pages to I2C if chip is enabled (i.e. Hardware shutdown is disabled) else { // Enable hardware shutdown GPIOB_PCOR |= (1<<16); goto led_finish_scan; } // Only start if we haven't already // And if we've finished updating the buffers if ( Pixel_FrameState == FrameState_Sending ) goto led_finish_scan; // Only send frame to ISSI chip if buffers are ready if ( Pixel_FrameState != FrameState_Ready ) goto led_finish_scan; // Adjust frame rate (i.e. delay and do something else for a bit) Time duration = Time_duration( LED_timePrev ); if ( duration.ms < LED_framerate ) goto led_finish_scan; // FPS Display if ( LED_displayFPS ) { // Show frame calculation dbug_msg("1frame/"); printInt32( Time_ms( duration ) ); print("ms + "); printInt32( duration.ticks ); print(" ticks"); // Check if we're not meeting frame rate if ( duration.ms > LED_framerate ) { print(" - Could not meet framerate: "); printInt32( LED_framerate ); } print( NL ); } // Emulated brightness control // Lower brightness by LED_brightness #if ISSI_Chip_31FL3731_define == 1 uint8_t inverse_brightness = 0xFF - LED_brightness; for ( uint8_t chip = 0; chip < ISSI_Chips_define; chip++ ) { for ( uint8_t ch = 0; ch < LED_BufferLength; ch++ ) { // Don't modify is 0 if ( LED_pageBuffer[ chip ].buffer[ ch ] == 0 ) { LED_pageBuffer_brightness[ chip ].buffer[ ch ] = 0; continue; } LED_pageBuffer_brightness[ chip ].buffer[ ch ] = LED_pageBuffer[ chip ].buffer[ ch ] - inverse_brightness < 0 ? 0x0 : LED_pageBuffer[ chip ].buffer[ ch ] - inverse_brightness; } } #endif // Update frame start time LED_timePrev = Time_now(); // Set the page of all the ISSI chips // This way we can easily link the buffers to send the brightnesses in the background for ( uint8_t ch = 0; ch < ISSI_Chips_define; ch++ ) { uint8_t bus = LED_ChannelMapping[ ch ].bus; // Page Setup LED_setupPage( bus, LED_ChannelMapping[ ch ].addr, ISSI_LEDPwmPage ); #if ISSI_Chip_31FL3731_define == 1 || ISSI_Chip_31FL3732_define == 1 // Reset LED enable mask // XXX At high speeds, the IS31FL3732 seems to have random bit flips // To get around this, just re-set the enable mask before each send // XXX Might be sufficient to do this every N frames though while ( i2c_send( bus, (uint16_t*)&LED_ledEnableMask[ ch ], sizeof( LED_EnableBuffer ) / 2 ) == -1 ) delay_us( ISSI_SendDelay ); #endif } // Send current set of buffers // Uses interrupts to send to all the ISSI chips // Pixel_FrameState will be updated when complete LED_chipSend = 0; // Start with chip 0 LED_linkedSend(); led_finish_scan: // Latency measurement end Latency_end_time( ledLatencyResource ); }