/************************************************************************** * * Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file Buttons.c * * @author (last) Sean Nash * @date (last) 24-Sep-2020 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 * ***************************************************************************/ #include "Buttons.h" #include "CPLD.h" #include "NVDataMgmt.h" #include "OperationModes.h" #include "SystemCommMessages.h" #include "TaskPriority.h" #include "Timers.h" /** * @addtogroup Buttons * @{ */ // ********** private definitions ********** /// Enumeration of button self-test states. typedef enum Button_Self_Test_States { BUTTON_SELF_TEST_STATE_START = 0, ///< Button self-test start state BUTTON_SELF_TEST_STATE_IN_PROGRESS, ///< Button self-test in progress state BUTTON_SELF_TEST_STATE_COMPLETE, ///< Button self-test completed state NUM_OF_BUTTON_SELF_TEST_STATES ///< Number of button self-test states } BUTTON_SELF_TEST_STATE_T; /// Enumeration of hardware buttons. typedef enum Buttons { BUTTON_OFF = 0, ///< Off button BUTTON_STOP, ///< Stop button NUM_OF_BUTTONS ///< Number of hardware buttons } BUTTON_T; /// Enumeration of off button commands to UI. typedef enum OffButtonCmdsToUI { OFF_BUTTON_CMD_PROMPT_USER_TO_CONFIRM = 0, ///< Prompt user to confirm power off command OFF_BUTTON_CMD_CANCEL_USER_CONFIRM_PROMPT, ///< Cancel user confirm prompt command OFF_BUTTON_CMD_REJECT_USER_OFF_REQUEST, ///< Reject user off request command NUM_OF_OFF_BUTTON_CMDS ///< Number of off button commands to UI } OFF_BUTTON_CMD_T; /// Enumeration of off button responses from UI. typedef enum OffButtonRspsFromUI { OFF_BUTTON_RSP_USER_REQUESTS_POWER_OFF = 0, ///< User requests power off response OFF_BUTTON_RSP_USER_CONFIRMS_POWER_OFF, ///< User confirms power off response OFF_BUTTON_RSP_USER_REJECTS_POWER_OFF, ///< User rejects power off response NUM_OF_OFF_BUTTON_RSPS ///< Number of off button responses from UI } OFF_BUTTON_RSP_T; #define OFF_REQUEST_PULSE_COUNT 4 ///< Number of edges for power off sequence on power off output signal. #define OFF_REQUEST_PULSE_INTVL_MS 50 ///< Duration (in ms) of power off sequence steps. #define OFF_REQUEST_DELAY_TIME_MS 2000 ///< Duration (in ms) of delay before power off sequence is initiated to provide sub-systems time to wrap things up. #define STOP_BUTTON_PENDING_TIMEOUT_MS 500 ///< Timeout period (in ms) for stop button press to be consumed. #define STUCK_BUTTON_TIMEOUT_MS 1000 ///< Duration (in ms) that a button must be in pressed state before considering it stuck at power up. #define OFF_REQUEST_EXPIRATION_TIME_MS (1000 * 60) ///< Time (in ms) that the user is given to confirm a power off request before it expires. #define USER_CONFIRMED 1 ///< User response code to power off confirmation prompt that indicates confirmation. #define USER_REJECTED 0 ///< User response code to power off confirmation prompt that indicates rejection. // ********** private data ********** /// Current off button state (overrideable). static OVERRIDE_U32_T dataOffButtonState = { BUTTON_STATE_RELEASED, BUTTON_STATE_PRESSED, BUTTON_STATE_PRESSED, 0 }; static BUTTON_STATE_T prevOffButtonState = BUTTON_STATE_RELEASED; ///< Previous state of off button. static BOOL offRequestAwaitingUserConfirmation = FALSE; ///< Flag indicates whether a power off request is pending user confirmation. static U32 offRequestPendingTimer = 0; ///< Timer counter for pending power off request. static BOOL offButtonPressPending = FALSE; ///< Flag indicates whether a confirmed power off request is pending execution. static U32 offRequestPulseCount = 0; ///< Power off sequence step counter. static U32 offRequestPulseTimer = 0; ///< Power off sequence step timer counter. static U32 offRequestDelayTimer = 0; ///< Power off sequence delay timer counter. /// Current stop button state (overrideable). static OVERRIDE_U32_T dataStopButtonState = { BUTTON_STATE_RELEASED, BUTTON_STATE_PRESSED, BUTTON_STATE_PRESSED, 0 }; static BUTTON_STATE_T prevStopButtonState = BUTTON_STATE_RELEASED; ///< Previous state of stop button. static BOOL stopButtonPressPending = FALSE; ///< Flag indicates a stop button press is pending consumption. static U32 stopButtonPendingTimer = 0; ///< Timer counter for pending stop button press. static BUTTON_SELF_TEST_STATE_T buttonSelfTestState = BUTTON_SELF_TEST_STATE_START; ///< Current state of button self-test. static U32 buttonSelfTestTimerCount = 0; ///< Timer counter for button self-test states. // ********** private function prototypes ********** static void handleOffButtonProcessing( void ); static void handleStopButtonProcessing( void ); static BOOL isCurrentOpModeOkToTurnOff( void ); /*********************************************************************//** * @brief * The initButtons function initializes the Buttons module. * @details Inputs: none * @details Outputs: Buttons module initialized. * @return none *************************************************************************/ void initButtons( void ) { prevOffButtonState = BUTTON_STATE_RELEASED; offRequestAwaitingUserConfirmation = FALSE; offRequestPendingTimer = 0; offButtonPressPending = FALSE; offRequestPulseCount = 0; offRequestPulseTimer = 0; prevStopButtonState = BUTTON_STATE_RELEASED; stopButtonPressPending = FALSE; stopButtonPendingTimer = 0; buttonSelfTestState = BUTTON_SELF_TEST_STATE_START; buttonSelfTestTimerCount = 0; } /*********************************************************************//** * @brief * The execButtons function executes the Buttons monitor. * @details Inputs: none * @details Outputs: offButtonState, stopButtonState, prevOffButtonState, prevStopButtonState * @return none *************************************************************************/ void execButtons( void ) { PIN_SIGNAL_STATE_T off = getCPLDOffButton(); PIN_SIGNAL_STATE_T stop = getCPLDStopButton(); // Set current button states read from CPLD dataOffButtonState.data = ( off == PIN_SIGNAL_HIGH ? BUTTON_STATE_PRESSED : BUTTON_STATE_RELEASED ); dataStopButtonState.data = ( stop == PIN_SIGNAL_HIGH ? BUTTON_STATE_PRESSED : BUTTON_STATE_RELEASED ); // Handle button state transitions for stop button handleStopButtonProcessing(); // Handle button state transitions for off button handleOffButtonProcessing(); } /*********************************************************************//** * @brief * The isStopButtonPressed function determines whether the stop button has been * pressed. Once the stop button has transitioned from released to pressed, a * press for the stop button will be pending until this function is called. * @details Inputs: stopButtonPressPending * @details Outputs: stopButtonPressPending * @return true if the stop button is pressed, false if not *************************************************************************/ BOOL isStopButtonPressed( void ) { BOOL result = stopButtonPressPending; stopButtonPressPending = FALSE; return result; } /*********************************************************************//** * @brief * The getOffButtonState function determines whether the off button is * currently pressed. * @details Inputs: dataOffButtonState, prevOffButtonState * @details Outputs: none * @return BUTTON_STATE_PRESSED if off button pressed, BUTTON_STATE_RELEASED if not *************************************************************************/ BUTTON_STATE_T getOffButtonState( void ) { BUTTON_STATE_T result = (BUTTON_STATE_T)dataOffButtonState.data; if ( OVERRIDE_KEY == dataOffButtonState.override ) { result = (BUTTON_STATE_T)dataOffButtonState.ovData; } return result; } /*********************************************************************//** * @brief * The getStopButtonState function determines whether the stop button is * currently pressed. * @details Inputs: dataStopButtonState, prevStopButtonState * @details Outputs: none * @return BUTTON_STATE_PRESSED if stop button pressed, BUTTON_STATE_RELEASED if not *************************************************************************/ BUTTON_STATE_T getStopButtonState( void ) { BUTTON_STATE_T result = (BUTTON_STATE_T)dataStopButtonState.data; if ( OVERRIDE_KEY == dataStopButtonState.override ) { result = (BUTTON_STATE_T)dataStopButtonState.ovData; } return result; } /*********************************************************************//** * @brief * The execStuckButtonTest function executes the stuck button test. * This function should be called periodically until a pass or fail * result is returned. * @details Inputs: dataOffButtonState * @details Outputs: buttonSelfTestState, buttonSelfTestTimerCount * @return in progress, passed, or failed *************************************************************************/ SELF_TEST_STATUS_T execStuckButtonTest( void ) { SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; switch ( buttonSelfTestState ) { case BUTTON_SELF_TEST_STATE_START: buttonSelfTestState = BUTTON_SELF_TEST_STATE_IN_PROGRESS; buttonSelfTestTimerCount = getMSTimerCount(); // No break here so we pass through directly to in progress processing case BUTTON_SELF_TEST_STATE_IN_PROGRESS: if ( ( dataOffButtonState.data == BUTTON_STATE_RELEASED ) && ( dataStopButtonState.data == BUTTON_STATE_RELEASED ) ) { buttonSelfTestState = BUTTON_SELF_TEST_STATE_COMPLETE; result = SELF_TEST_STATUS_PASSED; } else if ( TRUE == didTimeout( buttonSelfTestTimerCount, STUCK_BUTTON_TIMEOUT_MS ) ) { U32 almData = ( dataStopButtonState.data == BUTTON_STATE_PRESSED ? BUTTON_STOP : BUTTON_OFF ); SET_ALARM_WITH_1_U32_DATA( ALARM_ID_STUCK_BUTTON_TEST_FAILED, almData ) buttonSelfTestState = BUTTON_SELF_TEST_STATE_COMPLETE; result = SELF_TEST_STATUS_FAILED; } // Else just stay in progress and wait for next call break; case BUTTON_SELF_TEST_STATE_COMPLETE: // If we get called in this state, assume we are doing self-test again buttonSelfTestState = BUTTON_SELF_TEST_STATE_START; break; default: result = SELF_TEST_STATUS_FAILED; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_BUTTONS_INVALID_SELF_TEST_STATE, buttonSelfTestState ) break; } return result; } /*********************************************************************//** * @brief * The userConfirmOffButton function handles user confirmation of the off * button. The off request will be initiated here if confirmed or cancelled * if rejected by user. * @details Inputs: current operation mode * @details Outputs: stopButtonPressPending * @param response 1 = confirmed, 0 = rejected * @return none *************************************************************************/ void userConfirmOffButton( U08 response ) { switch ( response ) { case OFF_BUTTON_RSP_USER_REQUESTS_POWER_OFF: // If we are in a mode that allows power off, set off pending flag and request user confirmation if ( TRUE == isCurrentOpModeOkToTurnOff() ) { offRequestAwaitingUserConfirmation = TRUE; offRequestPendingTimer = 0; #ifndef SIMULATE_UI sendOffButtonMsgToUI( OFF_BUTTON_CMD_PROMPT_USER_TO_CONFIRM ); #endif } else { // Send rejection response to power off request sendOffButtonMsgToUI( OFF_BUTTON_CMD_REJECT_USER_OFF_REQUEST ); } break; case OFF_BUTTON_RSP_USER_CONFIRMS_POWER_OFF: // Is an off request pending user confirmation? if ( TRUE == offRequestAwaitingUserConfirmation ) { // Reset off request pending flag offRequestAwaitingUserConfirmation = FALSE; // If we are in a mode that allows power off, initiate power off sequence if ( TRUE == isCurrentOpModeOkToTurnOff() ) { broadcastData( MSG_ID_POWER_OFF_WARNING, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)0, 0 ); signalPowerOffWarning(); offButtonPressPending = TRUE; offRequestPulseCount = OFF_REQUEST_PULSE_COUNT; offRequestPulseTimer = 0; } else { sendOffButtonMsgToUI( OFF_BUTTON_CMD_REJECT_USER_OFF_REQUEST ); } } else { sendOffButtonMsgToUI( OFF_BUTTON_CMD_REJECT_USER_OFF_REQUEST ); } break; case OFF_BUTTON_RSP_USER_REJECTS_POWER_OFF: // Is an off request pending user confirmation? if ( TRUE == offRequestAwaitingUserConfirmation ) { // Reset off request pending flag offRequestAwaitingUserConfirmation = FALSE; } break; default: // Ok - do nothing break; } // End switch } /*********************************************************************//** * @brief * The isCurrentOpModeOkToTurnOff function determines whether the system can * be turned off in current operation mode. * @details Inputs: Current operation mode. * @details Outputs: none * @return TRUE if can turn system off in current mode, FALSE if not *************************************************************************/ static BOOL isCurrentOpModeOkToTurnOff( void ) { HD_OP_MODE_T opMode = getCurrentOperationMode(); BOOL result = FALSE; if ( ( opMode == MODE_STAN ) || ( opMode == MODE_SERV ) || ( opMode == MODE_FAUL ) ) { result = TRUE; } return result; } /*********************************************************************//** * @brief * The initiatePowerOff function initiates a power off sequence. * @details Inputs: none * @details Outputs: offRequestDelayTimer, offRequestPulseTimer, offRequestPulseCount, offButtonPressPending * @return none *************************************************************************/ void initiatePowerOff( void ) { // Warn NV Data Mgr that power will be lost shortly signalPowerOffWarning(); // Mimic confirmed power off button state to initiate power off cycle offRequestDelayTimer = 0; offRequestPulseTimer = 0; offRequestPulseCount = OFF_REQUEST_PULSE_COUNT; offButtonPressPending = TRUE; } /*********************************************************************//** * @brief * The handleOffButtonProcessing function checks for and processes off button * activity. * @details Inputs: offButtonState, prevOffButtonState * @details Outputs: offButtonPressPending, offRequestPulseCount, offRequestPulseTimer * @return none *************************************************************************/ static void handleOffButtonProcessing( void ) { // Handle button state transitions for off button if ( getOffButtonState() != prevOffButtonState ) { if ( getOffButtonState() == BUTTON_STATE_PRESSED ) { // If off request in a valid mode, send to UI for user confirmation userConfirmOffButton( OFF_BUTTON_RSP_USER_REQUESTS_POWER_OFF ); } prevOffButtonState = getOffButtonState(); } // If off request has not been confirmed by user before it expires, cancel it if ( TRUE == offRequestAwaitingUserConfirmation ) { offRequestPendingTimer += TASK_PRIORITY_INTERVAL; if ( offRequestPendingTimer >= OFF_REQUEST_EXPIRATION_TIME_MS ) { offRequestAwaitingUserConfirmation = FALSE; sendOffButtonMsgToUI( OFF_BUTTON_CMD_CANCEL_USER_CONFIRM_PROMPT ); } } // If user confirmed off button press, manage off request sequence if ( TRUE == offButtonPressPending ) { // Delay power off to provide sub-systems time to prepare for shutdown offRequestDelayTimer += TASK_PRIORITY_INTERVAL; if ( offRequestDelayTimer >= OFF_REQUEST_DELAY_TIME_MS ) { // Power off sequence is 4 50 ms toggles of the off request output signal offRequestPulseTimer += TASK_PRIORITY_INTERVAL; if ( offRequestPulseTimer >= OFF_REQUEST_PULSE_INTVL_MS ) { offRequestPulseTimer = 0; offRequestPulseCount--; if ( offRequestPulseCount == 0 ) { offButtonPressPending = false; } toggleCPLDOffRequest(); } } } } /*********************************************************************//** * @brief * The handleStopButtonProcessing function checks for and processes stop button * activity. * @details Inputs: stopButtonState, prevStopButtonState * @details Outputs: stopButtonPressPending * @return none *************************************************************************/ static void handleStopButtonProcessing( void ) { // Handle button state transitions for stop button if ( getStopButtonState() != prevStopButtonState ) { if ( getStopButtonState() == BUTTON_STATE_PRESSED ) { stopButtonPressPending = TRUE; stopButtonPendingTimer = getMSTimerCount(); } prevStopButtonState = getStopButtonState(); } // Handle when a stop button press is pending if ( TRUE == stopButtonPressPending ) { // If stop button not consumed within a reasonable time, s/w fault if ( TRUE == didTimeout( stopButtonPendingTimer, STOP_BUTTON_PENDING_TIMEOUT_MS ) ) { stopButtonPressPending = FALSE; SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_BUTTONS_STOP_BUTTON_NOT_CONSUMED ) } } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetOffButtonStateOverride function overrides the state of then * off button with a given state.n * @details Inputs: none * @details Outputs: dataOffButtonState * @param value override state for the off button * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetOffButtonStateOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; dataOffButtonState.ovData = value; dataOffButtonState.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetOffButtonStateOverride function resets the override of then * state of the off button. * @details Inputs: none * @details Outputs: dataOffButtonState * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetOffButtonStateOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; dataOffButtonState.override = OVERRIDE_RESET; dataOffButtonState.ovData = dataOffButtonState.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetStopButtonStateOverride function overrides the state of then * stop button with a given state. * @details Inputs: none * @details Outputs: dataStopButtonState * @param value override state for the stop button * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetStopButtonStateOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; dataStopButtonState.ovData = value; dataStopButtonState.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetStopButtonStateOverride function resets the override of then * state of the stop button. * @details Inputs: none * @details Outputs: dataStopButtonState * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetStopButtonStateOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; dataStopButtonState.override = OVERRIDE_RESET; dataStopButtonState.ovData = dataStopButtonState.ovInitData; } return result; } /**@}*/