/************************************************************************** * * 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 * * @date 20-Sep-2019 * @author S. Nash * * @brief Monitor/Controller for the off and stop buttons. * **************************************************************************/ #include #include "Buttons.h" #include "CPLD.h" #include "OperationModes.h" #include "SystemCommMessages.h" #include "TaskPriority.h" #include "Timers.h" // ********** private definitions ********** typedef enum Button_Self_Test_States { BUTTON_SELF_TEST_STATE_START = 0, BUTTON_SELF_TEST_STATE_IN_PROGRESS, BUTTON_SELF_TEST_STATE_COMPLETE, NUM_OF_BUTTON_SELF_TEST_STATES } BUTTON_SELF_TEST_STATE_T; typedef enum Buttons { BUTTON_OFF = 0, BUTTON_STOP, NUM_OF_BUTTONS } BUTTON_T; typedef enum OffButtonCmdsToUI { OFF_BUTTON_CMD_PROMPT_USER_TO_CONFIRM = 0, OFF_BUTTON_CMD_CANCEL_USER_CONFIRM_PROMPT, OFF_BUTTON_CMD_REJECT_USER_OFF_REQUEST, NUM_OF_OFF_BUTTON_CMDS } OFF_BUTTON_CMD_T; typedef enum OffButtonRspsFromUI { OFF_BUTTON_RSP_USER_REQUESTS_POWER_OFF = 0, OFF_BUTTON_RSP_USER_CONFIRMS_POWER_OFF, OFF_BUTTON_RSP_USER_REJECTS_POWER_OFF, NUM_OF_OFF_BUTTON_RSPS } OFF_BUTTON_RSP_T; #define OFF_REQUEST_PULSE_COUNT 4 #define OFF_REQUEST_PULSE_INTVL 50 // ms #define STOP_BUTTON_PENDING_TIMEOUT 500 // ms #define STUCK_BUTTON_TIMEOUT 1000 // ms #define OFF_REQUEST_EXPIRATION_TIME (1000 * 60) // ms (1 minute) #define USER_CONFIRMED 1 #define USER_REJECTED 0 // ********** private data ********** DATA_DECL( BUTTON_STATE_T, OffButtonState, dataOffButtonState, BUTTON_STATE_RELEASED, BUTTON_STATE_PRESSED ); static BUTTON_STATE_T prevOffButtonState = BUTTON_STATE_RELEASED; static BOOL offRequestAwaitingUserConfirmation = FALSE; static U32 offRequestPendingTimer = 0; static BOOL offButtonPressPending = FALSE; static U32 offRequestPulseCount = 0; static U32 offRequestPulseTimer = 0; DATA_DECL( BUTTON_STATE_T, StopButtonState, dataStopButtonState, BUTTON_STATE_RELEASED, BUTTON_STATE_PRESSED ); static BUTTON_STATE_T prevStopButtonState = BUTTON_STATE_RELEASED; static BOOL stopButtonPressPending = FALSE; static U32 stopButtonPendingTimer = 0; static BUTTON_SELF_TEST_STATE_T buttonSelfTestState = BUTTON_SELF_TEST_STATE_START; static U32 buttonSelfTestTimerCount = 0; // ********** private function prototypes ********** static void handleOffButtonProcessing( void ); static void handleStopButtonProcessing( void ); static BOOL isCurrentOpModeOkToTurnOff( void ); /************************************************************************* * @brief initButtons * The initButtons function initializes the Buttons module. * @details * Inputs : none * Outputs : Buttons module initialized. * @param none * @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 execButtons * The execButtons function executes the Buttons monitor. * @details * Inputs : none * Outputs : offButtonState, stopButtonState, prevOffButtonState, prevStopButtonState * @param none * @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 isStopButtonPressed * The isStopButtonPressed function determines whether the stop button has been \n * pressed. Once the stop button has transitioned from released to pressed, a \n * press for the stop button will be pending until this function is called. \n * @details * Inputs : stopButtonPressPending * Outputs : stopButtonPressPending * @param button * @return true if the stop button is pressed, false if not *************************************************************************/ BOOL isStopButtonPressed( void ) { BOOL result = stopButtonPressPending; stopButtonPressPending = FALSE; return result; } /************************************************************************* * @brief getOffButtonState * The getOffButtonState function determines whether the off button is \n * currently pressed. * @details * Inputs : dataOffButtonState, prevOffButtonState * Outputs : none * @param none * @return BUTTON_STATE_PRESSED if off button pressed, BUTTON_STATE_RELEASED if not *************************************************************************/ DATA_GET( BUTTON_STATE_T, getOffButtonState, dataOffButtonState ) /************************************************************************* * @brief getStopButtonState * The getStopButtonState function determines whether the stop button is \n * currently pressed. * @details * Inputs : dataStopButtonState, prevStopButtonState * Outputs : none * @param none * @return BUTTON_STATE_PRESSED if stop button pressed, BUTTON_STATE_RELEASED if not *************************************************************************/ DATA_GET( BUTTON_STATE_T, getStopButtonState, dataStopButtonState ) /************************************************************************* * @brief execStuckButtonTest * The execStuckButtonTest function executes the stuck button test. \n * This function should be called periodically until a pass or fail \n * result is returned. * @details * Inputs : * Outputs : * @param none * @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 ) ) { 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're 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_SOFTWARE_FAULT, SW_FAULT_ID_BUTTONS_INVALID_SELF_TEST_STATE, buttonSelfTestState ) break; } return result; } /************************************************************************* * @brief userConfirmOffButton * The userConfirmOffButton function handles user confirmation of the off \n * button. The off request will be initiated here if confirmed or cancelled \n * if rejected by user. * @details * Inputs : current operation mode * 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're in a mode that allows power off, set off pending flag and request user confirmation if ( TRUE == isCurrentOpModeOkToTurnOff() ) { offRequestAwaitingUserConfirmation = TRUE; offRequestPendingTimer = 0; sendOffButtonMsgToUI( OFF_BUTTON_CMD_PROMPT_USER_TO_CONFIRM ); } 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're in a mode that allows power off, initiate power off sequence if ( TRUE == isCurrentOpModeOkToTurnOff() ) { offButtonPressPending = TRUE; offRequestPulseCount = OFF_REQUEST_PULSE_COUNT; offRequestPulseTimer = 0; } } 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 isCurrentOpModeOkToTurnOff * The isCurrentOpModeOkToTurnOff function determines whether the system can \n * be turned off in current operation mode. * @details * Inputs : Current operation mode. * Outputs : none * @param none * @return TRUE if can turn system off in current mode, FALSE if not *************************************************************************/ static BOOL isCurrentOpModeOkToTurnOff( void ) { OP_MODE opMode = getCurrentOperationMode(); BOOL result = FALSE; if ( ( opMode == MODE_STAN ) || ( opMode == MODE_SERV ) || ( opMode == MODE_FAUL ) ) { result = TRUE; } return result; } /************************************************************************* * @brief handleOffButtonProcessing * The handleOffButtonProcessing function checks for and processes off button \n * activity. * @details * Inputs : offButtonState, prevOffButtonState * Outputs : offButtonPressPending, offRequestPulseCount, offRequestPulseTimer * @param none * @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 if ( TRUE == isCurrentOpModeOkToTurnOff() ) { // send off button to UI for user confirmation sendOffButtonMsgToUI( OFF_BUTTON_CMD_PROMPT_USER_TO_CONFIRM ); offRequestAwaitingUserConfirmation = TRUE; offRequestPendingTimer = 0; #ifdef SIMULATE_UI userConfirmOffButton( OFF_BUTTON_RSP_USER_REQUESTS_POWER_OFF ); #endif } } 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 ) { offRequestAwaitingUserConfirmation = FALSE; sendOffButtonMsgToUI( OFF_BUTTON_CMD_CANCEL_USER_CONFIRM_PROMPT ); } } // if user confirmed off button press, manage off request sequence if ( TRUE == offButtonPressPending ) { offRequestPulseTimer += TASK_PRIORITY_INTERVAL; if ( offRequestPulseTimer >= OFF_REQUEST_PULSE_INTVL ) { offRequestPulseTimer = 0; offRequestPulseCount--; if ( offRequestPulseCount == 0 ) { offButtonPressPending = false; } toggleCPLDOffRequest(); } } } /************************************************************************* * @brief handleStopButtonProcessing * The handleStopButtonProcessing function checks for and processes stop button \n * activity. * @details * Inputs : stopButtonState, prevStopButtonState * Outputs : stopButtonPressPending * @param none * @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 ) ) { stopButtonPressPending = FALSE; SET_ALARM_WITH_1_U32_DATA( ALARM_ID_SOFTWARE_FAULT, SW_FAULT_ID_BUTTONS_STOP_BUTTON_NOT_CONSUMED ) } } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /************************************************************************* * @brief testSetOffButtonStateOverride and testResetOffButtonStateOverride * The testSetOffButtonStateOverride function overrides the state of the \n * off button with a given state. \n * The testResetOffButtonStateOverride function resets the override of the \n * state of the off button. * @details * Inputs : none * Outputs : dataOffButtonState * @param value : override state for the off button * @return TRUE if override successful, FALSE if not *************************************************************************/ DATA_OVERRIDE_FUNC( BUTTON_STATE_T, testSetOffButtonStateOverride, testResetOffButtonStateOverride, dataOffButtonState ) /************************************************************************* * @brief testSetStopButtonStateOverride and testResetStopButtonStateOverride * The testSetStopButtonStateOverride function overrides the state of the \n * stop button with a given state. \n * The testResetStopButtonStateOverride function resets the override of the \n * state of the stop button. * @details * Inputs : none * Outputs : dataStopButtonState * @param value : override state for the stop button * @return TRUE if override successful, FALSE if not *************************************************************************/ DATA_OVERRIDE_FUNC( BUTTON_STATE_T, testSetStopButtonStateOverride, testResetStopButtonStateOverride, dataStopButtonState )