Index: firmware/App/Controllers/Valves.c =================================================================== diff -u -r4a5f3c9dfe803562a40aeea3bbc9fc3be0d1e26d -re13152777df58456a873789fbfc3f889813109c9 --- firmware/App/Controllers/Valves.c (.../Valves.c) (revision 4a5f3c9dfe803562a40aeea3bbc9fc3be0d1e26d) +++ firmware/App/Controllers/Valves.c (.../Valves.c) (revision e13152777df58456a873789fbfc3f889813109c9) @@ -1,49 +1,1538 @@ -/* - * Valves.c - * - * Created on: Aug 7, 2020 - * Author: fw - */ +/************************************************************************** +* +* 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 Valves.c +* +* @author (last) Dara Navaei +* @date (last) 08-Oct-2020 +* +* @author (original) Dara Navaei +* @date (original) 07-Aug-2020 +* +***************************************************************************/ +// Includes +#include "reg_het.h" +#include "gio.h" + #include "Valves.h" +#include "FPGA.h" +#include "Timers.h" +#include "TaskPriority.h" +#include "SystemCommMessages.h" +/** + * @addtogroup Valves + * @{ + */ +// ********** private definitions ********** + +// Valve dialyzer inlet defines +#define VDI_SET_PID_BIT_MASK 0x0010 ///< VDI enable PID bit mask +#define VDI_SET_BYPASS_BIT_MASK 0x0020 ///< VDI enable bypass bit mask +#define VDI_RESET_CONTROL_BIT_MASK 0x03CF ///< VDI reset control bit mask + +#define VDI_INIT_STATUS_BIT_MASK 0x0040 ///< VDI init status bit mask +#define VDI_ENABLE_PID_STATUS_BIT_MASK 0x0080 ///< VDI PID status bit mask +#define VDI_ENABLE_BYPASS_STATUS_BIT_MASK 0x0100 ///< VDI bypass status bit mask + +// Valve dialyzer outlet defines +#define VDO_SET_PID_BIT_MASK 0x0040 ///< VDO enable PID interface +#define VDO_SET_BYPASS_BIT_MASK 0x0080 ///< VDO enable bypass bit mask +#define VDO_RESET_CONTROL_BIT_MASK 0x033F ///< VDO reset control bit mask + +#define VDO_INIT_STATUS_BIT_MASK 0x0200 ///< VDO init status bit mask +#define VDO_ENABLE_PID_STATUS_BIT_MASK 0x0400 ///< VDO enable PID status bit mask +#define VDO_ENABLE_BYPASS_STATUS_BIT_MASK 0x0800 ///< VDO enable bypass status bit mask + +// Valve blood arterial defines +#define VBA_SET_PID_BIT_MASK 0x0001 ///< VBA enable PID interface +#define VBA_SET_BYPASS_BIT_MASK 0x0002 ///< VBA enable bypass interface +#define VBA_RESET_CONTROL_BIT_MASK 0x03FC ///< VBA reset control bit mask + +#define VBA_INIT_STATUS_BIT_MASK 0x0001 ///< VBA init status bit mask +#define VBA_ENABLE_PID_STATUS_BIT_MASK 0x0002 ///< VBA PID status bit mask +#define VBA_ENABLE_BYPASS_STATUS_BIT_MASK 0x0004 ///< VBA bypass status bit mask + +// Valve blood venous defines +#define VBV_SET_PID_BIT_MASK 0x0004 ///< VBV enable PID interface bit mask +#define VBV_SET_BYPASS_BIT_MASK 0x0008 ///< VBV enable bypass interface bit mask +#define VBV_RESET_CONTROL_BIT_MASK 0x03F3 ///< VBV reset control bit mask + +#define VBV_INIT_STATUS_BIT_MASK 0x0008 ///< VBV init status bit mask +#define VBV_ENABLE_PID_STATUS_BIT_MASK 0x0010 ///< VBV PID status bit mask +#define VBV_ENABLE_BYPASS_STATUS_BIT_MASK 0x0020 ///< VBV bypass status bit mask + +#define ENABLE_VALVE_CURRENT_RELAXATION 0x8000 ///< Enable valves current relaxation +#define DISABLE_VALVE_CURRENT_RELAXATION 0x7FFF ///< Disable valves current relaxation + +#define ADC_TO_CURRENT_CONVERSION_CONSTANT 2048.0 ///< Valves ADC count to current conversion + +#define INITIAL_EDGE_OFFSET_READ_COUNT 100U ///< Offset in counts from energized and de-energized edges + +#define INITIAL_ENERGIZED_EDGE_UPPER_RANGE 13000U ///< Energized edge maximum count +#define INITIAL_ENERGIZED_EDGE_LOWER_RANGE 9000U ///< Energized edge minimum count + +#define AIR_TRAP_VALVE_GPIO_PIN 0x12 ///< Air trap valve GPIO Pin 18 of HET Port 1 + +#define HOMING_STEP_CHANGE_IN_COUNTS 1000 ///< Step change in counts during homing +#define TRANSITION_STEP_CHANGE_IN_COUNTS 15000 ///< Step change in counts during normal operations + +#define MAX_DEVIATION_FROM_TARGET_IN_COUNTS 150 ///< Maximum deviation from target in counts +#define MAX_DEVIATION_FROM_TRAGET_IN_POS_B 1000 ///< Maximum allowed deviation from position B in counts +#define MAX_ALLOWED_FAILED_HOMINGS 3U ///< Maximum allowed failed homings +#define HOMING_EDGE_DETECTION_TIME_INTERVAL ( MS_PER_SECOND / ( 2 * TASK_PRIORITY_INTERVAL ) ) ///< The time that the valve must be at the edge to be considered for edge detection +#define VALVES_CURRENT_THRESHOLD_AMPS 1.0 ///< Valves current threshold +#define MAX_OVER_CURRENT_TIME_INTERVAL_COUNTER ( MS_PER_SECOND / ( TASK_PRIORITY_INTERVAL ) ) ///< Valves over current time interval counter +#define MAX_POS_DEVIATION_TIME_INTERVAL_COUNTER ( MS_PER_SECOND / ( TASK_PRIORITY_INTERVAL ) ) ///< Valves out of range time interval counter +#define VALVE_TRANSITION_TIMEOUT_MS 3000U ///< Valves transition time out in ms + +#define VALVES_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Valve data publication time interval + +#define VALVE_MAX_ALLOWED_PWM_PERCENT 100U ///< Valve maximum allowed PWM in percent +#define VALVE_CW_PWM_TO_CNT_CONVERSION( pwm ) ( ( 20 * pwm ) + 2500 ) ///< Valve clockwise PWM to count conversion +#define VALVE_CW_CNT_TO_PWM_CONVERSION( cnt ) ( ( 0.05 * cnt ) - 125 ) ///< Valve clockwise count to PWM conversion +#define VALVE_CCW_PWM_TO_CNT_CONVERSION( pwm ) ( ( -20 * pwm ) + 2500 ) ///< Valve counter clockwise PWM to count conversion +#define VALVE_CCW_CNT_TO_PWM_CONVERSION( cnt ) ( ( -0.05 * cnt ) + 125 ) ///< Valve counter clockwise count to PWM conversion + +/// Exec valve self test states +typedef enum valves_Self_Test_States +{ + VALVE_SELF_TEST_ENABLE_VALVES = 0, ///< Valve self test enable valves + VALVE_SELF_TEST_CONFIRM_ENABLE, ///< Valve self test confirm enable + VALVE_SELF_TEST_COMPLETE, ///< Valve self test complete + NUM_OF_VALVE_SELF_TEST_STATES ///< Number of valve self test states +} VALVE_SELF_TEST_STATE_T; + +/// Exec valve states +typedef enum valve_Exec_States +{ + VALVE_STATE_WAIT_FOR_POST = 0, ///< Valve state wait for POST + VALVE_STATE_HOMING_NOT_STARTED, ///< Valve state homing not started + VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE, ///< Valve state homing find energized edge + VALVE_STATE_HOMING_FIND_DEENERGIZED_EDGE, ///< Valve state homing find de-energized edge + VALVE_STATE_IDLE, ///< Valve state idle + VALVE_STATE_IN_TRANSITION, ///< Valve state in transition + VALVE_STATE_IN_BYPASS_MODE, ///< Valve state in bypass mode (Fixed PWM control) + NUM_OF_VALVE_STATES, ///< Number of valve exec states +} VALVE_STATE_T; + +/// Valve modes +typedef enum valve_modes +{ + VALVE_CONTROL_MODE_ENABLE_PID = 0, ///< Valve control mode enable PID + VALVE_CONTORL_MODE_ENABLE_BYPASS, ///< Valve control mode enable bypass + VALVE_CONTROL_MODE_DISABLE_ALL, ///< Valve control mode disable all modes + NUM_OF_VALVE_CONTROL_MODES, ///< Number of valve control modes +} VALVE_MODE_T; + +/// Valve control status +enum valve_control_status +{ + VALVE_CONTROL_STATUS_INITIALIZED = 0, ///< Valve control status initialized (PID control is initialized) + VALVE_CONTROL_STATUS_PID_ENABLED, ///< Valve control status PID enabled + VALVE_CONTROL_STATUS_BYPASS_ENABLED, ///< Valve control status bypass enabled + VALVE_CONTROL_STATUS_DISABLED, ///< Valve control status all disabled + NUM_OF_VALVE_CONTROL_STATUS ///< Number of valve control status +}; + +/// Valves bypass mode direction +typedef enum valve_bypass_mode_direction +{ + VAVLE_DIR_CLOCKWISE = 0, ///< Clockwise direction in bypass mode to moves the valves towards energized edge + VALVE_DIR_COUNTERCLOCKWISE, ///< Counter clockwise direction in bypass mode to move the valves towards de-energized edge + NUM_OF_VALVE_DIRECTION ///< Number of valve directions +} VALVE_DIRECTION_T; + +/// Valve bypass (fixed PWM) structure +typedef struct +{ + BOOL hasBypassModeBeenRequeseted; ///< Valve bypass mode request flag + BOOL hasChangeBeenRequested; ///< Valve bypass mode change of direction or PWM flag + U16 currentPWMInPercent; ///< Valve current PWM in percent + U16 commandedPWMInPercent; ///< Valve commanded PWM in percent + VALVE_DIRECTION_T direction; ///< Valve move direction (CW or CCW) +} VALVE_BYPASS_T; + +/// Valve status structure +typedef struct +{ + VALVE_POSITION_T commandedPosition; ///< Valve commanded position enum + VALVE_POSITION_T currentPosition; ///< Valve current position enum + S16 currentPositionInCounts; ///< Valve current position in counts + S16 targetPositionInCounts; ///< Valve target position in counts + BOOL hasTransitionBeenRequested; ///< Valve transition request flag + VALVE_STATE_T execState; ///< Valve execution state + U32 transitionStartTime; ///< Valve transition start time + S16 positions[ NUM_OF_VALVE_POSITIONS ]; ///< Valve positions array (Pos A, Pos B, Pos C) + VALVE_MODE_T controlMode; ///< Valve control mode (PID, bypass, disable) + F32 current; ///< Valve current + U32 overCurrentCounter; ///< Valve over current counter + U32 positionOutOfRangeCounter; ///< Valve position out of range counter + U32 dataPublishCounter; ///< Valve data publish counter + // Homing variables + U32 numberOfFailedHomings; ///< Valve number of failed homing + U32 homingEdgeDetectionCounter; ///< Valve homing counter when there is not much travel + BOOL hasHomingBeenRequested; ///< Valve homing request flag + BOOL hasValveBeenHomed; ///< Valve homing completed flag + BOOL hasHomingFailed; ///< Valve homing failed flag + // Bypass (fixed PWM) mode + VALVE_BYPASS_T bypassModeStatus; ///< Valve bypass (fixed PWM) mode +} VALVE_STATUS_T; + +// ********** private data ********** + +static VALVE_SELF_TEST_STATE_T valveSelfTestState = VALVE_SELF_TEST_ENABLE_VALVES; ///< Valves self test state +static SELF_TEST_STATUS_T valvesSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; ///< Valves self test result + +/// Array of type VALVE_STATUS_T structure that holds the status of each valve +static VALVE_STATUS_T valvesStatus[ NUM_OF_VALVES ]; + +static OVERRIDE_U32_T valvesDataPublishInterval = { VALVES_DATA_PUB_INTERVAL, VALVES_DATA_PUB_INTERVAL, 0, 0 }; ///< Valves data publish interval + +static OVERRIDE_U32_T valvesPositionOverride[ NUM_OF_VALVES ] = { VALVE_POSITION_C_CLOSE, VALVE_POSITION_C_CLOSE, 0, 0 }; ///< Valves position override + +static const U16 valvesControlModesSetBits[ NUM_OF_VALVES ][ NUM_OF_VALVE_CONTROL_MODES ] = + { { VDI_SET_PID_BIT_MASK, VDI_SET_BYPASS_BIT_MASK, VDI_RESET_CONTROL_BIT_MASK }, + { VDO_SET_PID_BIT_MASK, VDO_SET_BYPASS_BIT_MASK, VDO_RESET_CONTROL_BIT_MASK }, + { VBA_SET_PID_BIT_MASK, VBA_SET_BYPASS_BIT_MASK, VBA_RESET_CONTROL_BIT_MASK }, + { VBV_SET_PID_BIT_MASK, VBV_SET_BYPASS_BIT_MASK, VBV_RESET_CONTROL_BIT_MASK } }; ///< Valves control mode set bits + +static const U16 valvesControlModesResetBits[ NUM_OF_VALVES ] = { VDI_RESET_CONTROL_BIT_MASK, VDO_RESET_CONTROL_BIT_MASK, + VBA_RESET_CONTROL_BIT_MASK, VBV_RESET_CONTROL_BIT_MASK }; ///< Valves control modes rest bits + +static const U16 valvesControlStatusBits[ NUM_OF_VALVES ][ NUM_OF_VALVE_CONTROL_STATUS ] = + { { VDI_INIT_STATUS_BIT_MASK, VDI_ENABLE_PID_STATUS_BIT_MASK, VDI_ENABLE_BYPASS_STATUS_BIT_MASK, VDI_RESET_CONTROL_BIT_MASK }, + { VDO_INIT_STATUS_BIT_MASK, VDO_ENABLE_PID_STATUS_BIT_MASK, VDO_ENABLE_BYPASS_STATUS_BIT_MASK, VDO_RESET_CONTROL_BIT_MASK }, + { VBA_INIT_STATUS_BIT_MASK, VBA_ENABLE_PID_STATUS_BIT_MASK, VBA_ENABLE_BYPASS_STATUS_BIT_MASK, VBA_RESET_CONTROL_BIT_MASK }, + { VBV_INIT_STATUS_BIT_MASK, VBV_ENABLE_PID_STATUS_BIT_MASK, VBV_ENABLE_BYPASS_STATUS_BIT_MASK, VBV_RESET_CONTROL_BIT_MASK } }; ///< Valves control status bits + +static U16 valvesControlSetBits = 0x0000; ///< Valves control set bit +static OPN_CLS_STATE_T valveAirTrapStatus; ///< Air trap valve status (open/close) + +// Self test function prototypes +static VALVE_SELF_TEST_STATE_T handleValveSelfTestEnableValves( void ); +static VALVE_SELF_TEST_STATE_T handleValveSelfTestConfirmEnable( void ); + +// Exec function prototypes +static VALVE_STATE_T handleValveStateWaitForPost( VALVE_T valve ); +static VALVE_STATE_T handleValveStateHomingNotStarted( VALVE_T valve ); +static VALVE_STATE_T handleValveStateHomingFindEnergizedEdge( VALVE_T valve ); +static VALVE_STATE_T handleValveStateHomingFindDeenergizedEdge( VALVE_T valve ); +static VALVE_STATE_T handleValveStateIdle( VALVE_T valve ); +static VALVE_STATE_T handleValveStateInTransition( VALVE_T valve ); +static VALVE_STATE_T handleValveStateInBypassMode( VALVE_T valve ); + +// Private function prototypes +static void setValveControlMode( VALVE_T valve, VALVE_MODE_T mode ); +static void execMonitorValves( void ); +static BOOL areValvesFunctional( void ); +static void setFPGAValveSetPoint( VALVE_T valve, S16 position, BOOL enableCurrentRelaxation ); +static void convertAndMonitorValvesCurrent( void ); +static void getAndMonitorValvesCurrentPosition( void ); +static DATA_GET_PROTOTYPE( U32, getPublishValvesDataInterval ); +static void publishValvesData( VALVE_T valve ); +static void setValveNextStep( VALVE_T valve, U32 stepChange ); + +// These functions will be used in debug only mode +#ifdef DEBUG_ENABLED +static void setFPGAValvePWM( VALVE_T valve ); +static void getValvesCurrentPWM( void ); +#endif + +/*********************************************************************//** + * @brief + * The initValves function initializes the valves driver. + * @details Inputs: valveSelfTestState, valvesSelfTestResult, valvesStatus, + * valvesControlSetBits + * @details Outputs: valveSelfTestState, valvesSelfTestResult, valvesStatus + * @return none + *************************************************************************/ void initValves( void ) { + VALVE_T valve; + valveSelfTestState = VALVE_SELF_TEST_ENABLE_VALVES; + valvesSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; + valveAirTrapStatus = STATE_CLOSED; + + // Set the valves bit control to 0 + valvesControlSetBits = 0x0000; + setFPGAValvesControlMode( valvesControlSetBits ); + // Set the air trap valve to be in closed state + setValveAirTrap( STATE_CLOSED ); + + // Initialize some of the variables + for ( valve = VDI; valve < NUM_OF_VALVES; valve++ ) + { + valvesStatus[ valve ].execState = VALVE_STATE_WAIT_FOR_POST; + } + + // close air trap valve + setValveAirTrap( STATE_CLOSED ); } +/*********************************************************************//** + * @brief + * The homeValve function sets the homing request flag of a valve to TRUE. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus (The flag that sets the homing request) + * @param valve that is set to be homed + * @return none + *************************************************************************/ +BOOL homeValve( VALVE_T valve ) +{ + BOOL result = FALSE; + + if ( valve < NUM_OF_VALVES ) + { + valvesStatus[ valve ].hasHomingBeenRequested = TRUE; + result = TRUE; + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_VALVE_SELECTED, (U32)valve ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The setValvePosition function sets the requested position of a valve. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus + * @param valve that its position is set + * @param position that the valve is set + * @return returns TRUE if the requested position was legal + *************************************************************************/ +BOOL setValvePosition( VALVE_T valve, VALVE_POSITION_T position ) +{ + BOOL result = FALSE; + + // Valve not in position cannot be requested + // All the other positions are valid + if ( position != VALVE_POSITION_NOT_IN_POSITION && valve < NUM_OF_VALVES ) + { + valvesStatus[ valve ].commandedPosition = position; + valvesStatus[ valve ].hasTransitionBeenRequested = TRUE; + + result = TRUE; + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_VALVE_SELECTED, (U32)valve ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getValvePosition function returns the current position of a valve. + * @details Inputs: none + * @details Outputs: valvesStatus + * @param valve that the position is requested + * @return returns the current position of the valve in enum + *************************************************************************/ +VALVE_POSITION_T getValvePosition( VALVE_T valve ) +{ + VALVE_POSITION_T position = VALVE_POSITION_NOT_IN_POSITION; + + if ( valve < NUM_OF_VALVES ) + { + position = valvesStatus[ valve ].currentPosition; + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_VALVE_SELECTED, (U32)valve ); + } + + return position; +} + +/*********************************************************************//** + * @brief + * The setValveBloodTrap function set the blood trap valve to open or close + * position. + * @details Inputs: valveAirTrapStatus + * @details Outputs: valveAirTrapStatus + * @param: state enumeration of open or close for valve + * @return: none + *************************************************************************/ +void setValveAirTrap( OPN_CLS_STATE_T state ) +{ + BOOL energized = FALSE; + + // Check if the proper air trap state was requested + if ( state < NUM_OF_OPN_CLS_STATES ) + { + if ( STATE_OPEN == state ) + { + energized = TRUE; + } + } + // Of course if invalid air trap state was requested, keep the valve de-energized (close) + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_AIR_TRAP_REQUEST, state ); + } + + valveAirTrapStatus = state; + gioSetBit( hetPORT1, AIR_TRAP_VALVE_GPIO_PIN, energized ); +} + +/*********************************************************************//** + * @brief + * The getValveAirTrapStatus function returns the status of air trap valve + * which is either open or close + * @details Inputs: valveAirTrapStatus + * @details Outputs: none + * @return: valveAirTrapStatus the status of air trap valve (open or close) + *************************************************************************/ +OPN_CLS_STATE_T getValveAirTrapStatus( void ) +{ + return valveAirTrapStatus; +} + +/*********************************************************************//** + * @brief + * The execValvesSelfTest function executes the valves self test. + * @details Inputs: valveSelfTestState + * @details Outputs: valveSelfTestState + * @return valvesSelfTestResult The status of the self test + *************************************************************************/ SELF_TEST_STATUS_T execValvesSelfTest( void ) { + switch( valveSelfTestState ) + { + case VALVE_SELF_TEST_ENABLE_VALVES: + valveSelfTestState = handleValveSelfTestEnableValves(); + break; + case VALVE_SELF_TEST_CONFIRM_ENABLE: + valveSelfTestState = handleValveSelfTestConfirmEnable(); + break; + + case VALVE_SELF_TEST_COMPLETE: + // Do nothing POST completed + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_SELF_TEST_STATE, + valveSelfTestState ); + valveSelfTestState = VALVE_SELF_TEST_COMPLETE; + } + + return valvesSelfTestResult; } +/*********************************************************************//** + * @brief + * The execValves function executes the valves exec state machine. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus + * @return none + *************************************************************************/ void execValves( void ) { + VALVE_T valve; + // Monitoring should be done once POST is complete and the valves are + // set correctly + if ( valveSelfTestState == VALVE_SELF_TEST_COMPLETE ) + { + execMonitorValves(); + } + + // Loop through all of the valves + for ( valve = VDI; valve < NUM_OF_VALVES; valve++ ) + { + switch ( valvesStatus[ valve ].execState ) + { + case VALVE_STATE_WAIT_FOR_POST: + valvesStatus[ valve ].execState = handleValveStateWaitForPost( valve ); + break; + + case VALVE_STATE_HOMING_NOT_STARTED: + valvesStatus[ valve ].execState = handleValveStateHomingNotStarted( valve ); + break; + + case VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE: + valvesStatus[ valve ].execState = handleValveStateHomingFindEnergizedEdge( valve ); + break; + + case VALVE_STATE_HOMING_FIND_DEENERGIZED_EDGE: + valvesStatus[ valve ].execState = handleValveStateHomingFindDeenergizedEdge( valve ); + break; + + case VALVE_STATE_IDLE: + valvesStatus[ valve ].execState = handleValveStateIdle( valve ); + break; + + case VALVE_STATE_IN_TRANSITION: + valvesStatus[ valve ].execState = handleValveStateInTransition( valve ); + break; +// PWM mode only used in debug mode +#ifdef DEBUG_ENABLED + case VALVE_STATE_IN_BYPASS_MODE: + valvesStatus[ valve ].execState = handleValveStateInBypassMode( valve ); + break; +#endif + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_EXEC_STATE, + valvesStatus[ valve ].execState ); + valvesStatus[ valve ].execState = VALVE_STATE_IDLE; + break; + } + + publishValvesData( valve ); + } } -BOOL homeDialyzerInletValve( void ) +/*********************************************************************//** + * @brief + * The handleValveSelfTestEnableValves function starts the valves self test. + * @details Inputs: valvesSelfTestResult, valvesSelfTestResult + * @details Outputs: valvesSelfTestResult, valvesSelfTestResult + * @return next state of the self test state machine + *************************************************************************/ +static VALVE_SELF_TEST_STATE_T handleValveSelfTestEnableValves( void ) { + VALVE_T valve; + VALVE_SELF_TEST_STATE_T state = VALVE_SELF_TEST_CONFIRM_ENABLE; + valvesSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; + + // Set all the valves to be in PID control mode + for ( valve = VDI; valve < NUM_OF_VALVES; valve++ ) + { + setValveControlMode( valve, VALVE_CONTROL_MODE_ENABLE_PID ); + + // Set the control mode of each valve to be PID mode + valvesStatus[ valve ].controlMode = VALVE_CONTROL_MODE_ENABLE_PID; + } + + return state; } -BOOL homeDialyzerOutletValve( void ) +/*********************************************************************//** + * @brief + * The handleValveSelfTestConfirmEnable function checks to make sure the + * valves are enabled properly. + * @details Inputs: valvesSelfTestResult + * @details Outputs: valvesSelfTestResult + * @return next state of the self test state machine + *************************************************************************/ +static VALVE_SELF_TEST_STATE_T handleValveSelfTestConfirmEnable( void ) { + VALVE_SELF_TEST_STATE_T state = VALVE_SELF_TEST_COMPLETE; + if ( areValvesFunctional() ) + { + valvesSelfTestResult = SELF_TEST_STATUS_PASSED; + } + else + { + valvesSelfTestResult = SELF_TEST_STATUS_FAILED; + } + + return state; } -BOOL homeBloodArterialValve( void ) +/*********************************************************************//** + * @brief + * The handleValveStateWaitForPost function handles the wait for POST state. + * @details Inputs: valveSelfTestState + * @details Outputs: none + * @param valve that is waiting for POST to complete + * @return next state of exec state + *************************************************************************/ +static VALVE_STATE_T handleValveStateWaitForPost( VALVE_T valve ) { + VALVE_STATE_T state = VALVE_STATE_WAIT_FOR_POST; + if ( valveSelfTestState == VALVE_SELF_TEST_COMPLETE ) + { + state = VALVE_STATE_HOMING_NOT_STARTED; + } + + return state; } -BOOL homeBloodVenousValve( void ) +/*********************************************************************//** + * @brief + * The handleValveStateHomingNotStarted function handles homing not started + * state of the state machine. + * @details Inputs: valvesStatus, valveSelfTestState, isDoorClosed + * @details Outputs: valvesStatus + * @param valve that is set to be homed + * @return next state of the exec state machine + *************************************************************************/ +static VALVE_STATE_T handleValveStateHomingNotStarted( VALVE_T valve ) { + VALVE_STATE_T state = VALVE_STATE_HOMING_NOT_STARTED; + // TODO connect this flag to the doors driver later + // For now it is assumed the door is closed + BOOL isDoorClosed = TRUE; + + // If homing has been requested or POST is completed and the door has been close for the specified + // period of time, start the homing + if ( valveSelfTestState == VALVE_SELF_TEST_COMPLETE && ( valvesStatus[ valve ].hasHomingBeenRequested || isDoorClosed ) + && ( ! valvesStatus[ valve ].hasHomingFailed ) ) + { + // Get ready for the energized state + valvesStatus[ valve ].homingEdgeDetectionCounter = 0; + valvesStatus[ valve ].targetPositionInCounts = valvesStatus[ valve ].currentPositionInCounts + HOMING_STEP_CHANGE_IN_COUNTS; + valvesStatus[ valve ].hasValveBeenHomed = FALSE; + valvesStatus[ valve ].currentPosition = VALVE_POSITION_NOT_IN_POSITION; + setFPGAValveSetPoint( valve, valvesStatus[ valve ].targetPositionInCounts, FALSE ); + state = VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE; + } + + return state; } -BOOL homeBloodTrapValve( void ) +/*********************************************************************//** + * @brief + * The handleValveStateHomingFindEnergizedEdge function handles find + * energized state of the state machine. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus + * @param valve that its energized edge is found + * @return next state of the exec state machine + *************************************************************************/ +static VALVE_STATE_T handleValveStateHomingFindEnergizedEdge( VALVE_T valve ) { + VALVE_STATE_T state = VALVE_STATE_HOMING_FIND_ENERGIZED_EDGE; + S16 currentPosition = valvesStatus[ valve ].currentPositionInCounts; + S16 targetPosition = valvesStatus[ valve ].targetPositionInCounts; + S16 deltaPosition = targetPosition - currentPosition; + + // If there has not been any major travel for a certain period of time + if ( deltaPosition > MAX_DEVIATION_FROM_TARGET_IN_COUNTS ) + { + if ( valvesStatus[ valve ].homingEdgeDetectionCounter >= HOMING_EDGE_DETECTION_TIME_INTERVAL ) + { + // Current position (positive or negative) will be stored in the Position B of the current valve + valvesStatus[ valve ].positions[ VALVE_POSITION_B_OPEN ] = currentPosition; + valvesStatus[ valve ].homingEdgeDetectionCounter = 0; + valvesStatus[ valve ].targetPositionInCounts = currentPosition - HOMING_STEP_CHANGE_IN_COUNTS; + setFPGAValveSetPoint( valve, valvesStatus[ valve ].targetPositionInCounts, FALSE ); + + state = VALVE_STATE_HOMING_FIND_DEENERGIZED_EDGE; + } + else + { + valvesStatus[ valve ].homingEdgeDetectionCounter++; + } + + } + // Reached to target, go for the next target + else + { + valvesStatus[ valve ].homingEdgeDetectionCounter = 0; + valvesStatus[ valve ].targetPositionInCounts = currentPosition + HOMING_STEP_CHANGE_IN_COUNTS; + setFPGAValveSetPoint( valve, valvesStatus[ valve ].targetPositionInCounts, FALSE ); + } + + return state; } + +/*********************************************************************//** + * @brief + * The handleValveStateHomingFindDeenergizedEdge function handles find + * de-energized state of the state machine. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus + * @param valve that its de-energized edge is found + * @return next state of the exec state machine + *************************************************************************/ +static VALVE_STATE_T handleValveStateHomingFindDeenergizedEdge( VALVE_T valve ) +{ + VALVE_STATE_T state = VALVE_STATE_HOMING_FIND_DEENERGIZED_EDGE; + + S16 currentPosition = valvesStatus[ valve ].currentPositionInCounts; + S16 targetPosition = valvesStatus[ valve ].targetPositionInCounts; + S16 deltaPosition = currentPosition - targetPosition; + + // If there has not been any major travel + if ( deltaPosition > MAX_DEVIATION_FROM_TARGET_IN_COUNTS ) + { + // Check if the specified time has elapsed + if ( valvesStatus[ valve ].homingEdgeDetectionCounter >= HOMING_EDGE_DETECTION_TIME_INTERVAL ) + { + S16 energizedEdge = valvesStatus[ valve ].positions[ VALVE_POSITION_B_OPEN ]; + S16 deltaEdges = energizedEdge - currentPosition; + + // The range from energized to de-energized should be in between 9000 to 13000 steps + if ( deltaEdges <= INITIAL_ENERGIZED_EDGE_UPPER_RANGE && deltaEdges >= INITIAL_ENERGIZED_EDGE_LOWER_RANGE ) + { + S16 positionB = valvesStatus[ valve ].positions[ VALVE_POSITION_B_OPEN ]; + + // Positions B and C will have an offset from the edge to make sure each time the valve will not hit the edge + valvesStatus[ valve ].positions[ VALVE_POSITION_B_OPEN ] = positionB - INITIAL_EDGE_OFFSET_READ_COUNT; + valvesStatus[ valve ].positions[ VALVE_POSITION_C_CLOSE ] = currentPosition + INITIAL_EDGE_OFFSET_READ_COUNT; + // Position A is the average of the Position B that was read last time and position C that was the target of this state + valvesStatus[ valve ].positions[ VALVE_POSITION_A_INSERT_EJECT ] = ( valvesStatus[ valve ].positions[ VALVE_POSITION_B_OPEN ] - + valvesStatus[ valve ].positions[ VALVE_POSITION_C_CLOSE ] ) / 2; + + // Set the current position to Position C and the commanded position to Position A + valvesStatus[ valve ].hasValveBeenHomed = TRUE; + valvesStatus[ valve ].currentPosition = VALVE_POSITION_C_CLOSE; + valvesStatus[ valve ].commandedPosition = VALVE_POSITION_A_INSERT_EJECT; + valvesStatus[ valve ].hasTransitionBeenRequested = TRUE; + valvesStatus[ valve ].hasHomingBeenRequested = FALSE; + valvesStatus[ valve ].numberOfFailedHomings = 0; + + // Idle will initiate a transition + state = VALVE_STATE_IDLE; + } + // Max number of failed homing. Fault and go back to homing not started + else if ( ++valvesStatus[ valve ].numberOfFailedHomings >= MAX_ALLOWED_FAILED_HOMINGS ) + { + valvesStatus[ valve ].hasValveBeenHomed = FALSE; + valvesStatus[ valve ].hasHomingFailed = TRUE; + state = VALVE_STATE_HOMING_NOT_STARTED; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_VALVE_HOMING_FAILED, (U32)valve ); + } + else + { + state = VALVE_STATE_HOMING_NOT_STARTED; + } + } + // The time has not elapsed, increment it + else + { + valvesStatus[ valve ].homingEdgeDetectionCounter++; + } + } + // Reached to target, go for the next target + else + { + valvesStatus[ valve ].homingEdgeDetectionCounter = 0; + valvesStatus[ valve ].targetPositionInCounts = currentPosition - HOMING_STEP_CHANGE_IN_COUNTS; + setFPGAValveSetPoint( valve, valvesStatus[ valve ].targetPositionInCounts, FALSE ); + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleValveStateIdle function handles the idle state of the valves + * state machine. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus + * @param valve that is in idle state + * @return next state of the exec state machine + *************************************************************************/ +static VALVE_STATE_T handleValveStateIdle( VALVE_T valve ) +{ + VALVE_STATE_T state = VALVE_STATE_IDLE; + +#ifdef TST_3WAY_VALVES_ALWAYS_OPEN + static BOOL valvesOpenedForSarina[NUM_OF_VALVES] = { FALSE, FALSE, FALSE, FALSE }; + + if ( FALSE == valvesOpenedForSarina[ valve ] ) + { + valvesOpenedForSarina[ valve ] = TRUE; + setValvePosition( valve, VALVE_POSITION_B_OPEN ); + } +#endif + + if ( valvesStatus[ valve ].hasHomingBeenRequested ) + { + state = VALVE_STATE_HOMING_NOT_STARTED; + } + else if ( valvesStatus[ valve ].hasValveBeenHomed && valvesStatus[ valve ].hasTransitionBeenRequested ) + { + if ( valvesStatus[ valve ].currentPosition != valvesStatus[ valve ].commandedPosition ) + { + // Just set the valves to transition so it will not be in a known position for a while + valvesStatus[ valve ].transitionStartTime = getMSTimerCount(); + setValveNextStep( valve, TRANSITION_STEP_CHANGE_IN_COUNTS ); + + state = VALVE_STATE_IN_TRANSITION; + } + else + { + valvesStatus[ valve ].hasTransitionBeenRequested = FALSE; + } + } +// This option is only available in a debug build +#ifdef DEBUG_ENABLED + // Check if the valves have been homed and a bypass mode has been requested + else if ( valvesStatus[ valve ].hasValveBeenHomed && + valvesStatus[ valve ].bypassModeStatus.hasBypassModeBeenRequeseted ) + { + // Set the valve control mode to bypass and set the PWM + setValveControlMode( valve, VALVE_CONTORL_MODE_ENABLE_BYPASS ); + valvesStatus[ valve ].controlMode = VALVE_CONTORL_MODE_ENABLE_BYPASS; + valvesStatus[ valve ].bypassModeStatus.hasChangeBeenRequested = FALSE; + setFPGAValvePWM( valve ); + + state = VALVE_STATE_IN_BYPASS_MODE; + } +#endif + + return state; +} + +/*********************************************************************//** + * @brief + * The handleValveStateInTransition function handles the in transition + * state of the state machine. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus + * @param valve that is in position transition + * @return next state of the exec state machine + *************************************************************************/ +static VALVE_STATE_T handleValveStateInTransition( VALVE_T valve ) +{ + VALVE_STATE_T state = VALVE_STATE_IN_TRANSITION; + + VALVE_POSITION_T commandedPositionEnum = valvesStatus[ valve ].commandedPosition; + + // Get the corresponding counts of the positions + S16 commandedPosition = valvesStatus[ valve ].positions[ commandedPositionEnum ]; + S16 currentPosition = valvesStatus[ valve ].currentPositionInCounts; + S16 targetPosition = valvesStatus[ valve ].targetPositionInCounts; + + // Check if the valve is within range of the final commanded position. + // NOTE: This is checked to the final position and not the temporary target en route to the final destination + if ( abs( currentPosition - commandedPosition ) < MAX_DEVIATION_FROM_TARGET_IN_COUNTS ) + { + // The valve is in range. Set the current position to the commanded position + valvesStatus[ valve ].currentPosition = commandedPositionEnum; + // Set the transition request to false as it has been serviced + valvesStatus[ valve ].hasTransitionBeenRequested = FALSE; + + // If we are in Pos B, enable current relaxation + if ( commandedPositionEnum == VALVE_POSITION_B_OPEN ) + { + setFPGAValveSetPoint( valve, currentPosition, TRUE ); + } + + // Go back to Idle state + state = VALVE_STATE_IDLE; + } + // Check if the valve's transition time has timed out + else if ( didTimeout( valvesStatus[ valve ].transitionStartTime, VALVE_TRANSITION_TIMEOUT_MS ) ) + { + valvesStatus[ valve ].hasTransitionBeenRequested = FALSE; + // Go back to Idle state + state = VALVE_STATE_IDLE; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_VALVE_TRANSITION_TIMEOUT, (U32)valve ); + } + // Check if the valve is close to the temporary target position and if it is, assign the next target position + else if ( abs( currentPosition - targetPosition ) < MAX_DEVIATION_FROM_TARGET_IN_COUNTS ) + { + setValveNextStep( valve, TRANSITION_STEP_CHANGE_IN_COUNTS ); + } + + return state; +} +#ifdef DEBUG_ENABLED +/*********************************************************************//** + * @brief + * The handleValveStateInBypassMode function handles the bypass mode + * state of the state machine. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus + * @param valve that is in position transition + * @return next state of the exec state machine + *************************************************************************/ +static VALVE_STATE_T handleValveStateInBypassMode( VALVE_T valve ) +{ + VALVE_STATE_T state = VALVE_STATE_IN_BYPASS_MODE; + + // If bypass mode has been turned off + if ( valvesStatus[ valve ].bypassModeStatus.hasBypassModeBeenRequeseted == FALSE ) + { + // Change the control mode to PID + setValveControlMode( valve, VALVE_CONTROL_MODE_ENABLE_PID ); + valvesStatus[ valve ].controlMode = VALVE_CONTROL_MODE_ENABLE_PID; + + // Request a change of position to get back to the last known position in PID mode + // Go back to Pos A + valvesStatus[ valve ].commandedPosition = VALVE_POSITION_A_INSERT_EJECT; + valvesStatus[ valve ].hasTransitionBeenRequested = TRUE; + + state = VALVE_STATE_IDLE; + } + // If a valve change either a change in CW or CCW or PWM has changed, set the new valve PWM + else if ( valvesStatus[ valve ].bypassModeStatus.hasChangeBeenRequested ) + { + setFPGAValvePWM( valve ); + } + + return state; +} +#endif +/*********************************************************************//** + * @brief + * The setValveControlMode function sets the valves control mode. + * @details Inputs: valvesControlSetBits, valvesControlModesSetBits, + * valvesControlModesResetBits + * @details Outputs: valvesControlSetBits + * @param valve that its control mode is set + * @param mode the control mode that the valve will be set to + * @return none + *************************************************************************/ +static void setValveControlMode( VALVE_T valve, VALVE_MODE_T mode ) +{ + // Get the set and reset bits for the particular valve + U16 control = valvesControlModesSetBits[ valve ][ mode ]; + U16 reset = valvesControlModesResetBits[ valve ]; + + // Reset the control bits of the particular valve + valvesControlSetBits &= reset; + + // Set the new control mode of the valve to the variable + valvesControlSetBits |= control; + + setFPGAValvesControlMode( valvesControlSetBits ); +} + +/*********************************************************************//** + * @brief + * The execMonitorValves function handles monitoring of the valves. The + * function calls other functions to monitor the values. + * @details Inputs: none + * @details Outputs: none + * @return none + *************************************************************************/ +static void execMonitorValves( void ) +{ +#ifndef DISABLE_3WAY_VALVES + // Check if the valves are still enabled + areValvesFunctional(); +#endif + + // Get the current position of the valves in counts and store them + getAndMonitorValvesCurrentPosition(); + +#ifndef DISABLE_3WAY_VALVES + // Get the current in ADC and convert them to amps + // Check whether any of the valves are over current + convertAndMonitorValvesCurrent(); + + // Get the current PWM values back. + // This function is only available if the valves are in bypass mode +#ifdef DEBUG_ENABLED + getValvesCurrentPWM(); +#endif +#endif +} + +/*********************************************************************//** + * @brief + * The areValvesEnabled function checks the current valves status from FPGA + * to the enable bit mask of each valve. If any of the valves is not enabled + * the function raises an alarm. + * @details Inputs: valvesStatus, valvesControlStatusBits + * @details Outputs: none + * @return Returns TRUE if all the valves are enabled properly + *************************************************************************/ +static BOOL areValvesFunctional( void ) +{ + VALVE_T valve; + VALVE_MODE_T mode; + BOOL result = TRUE; + + // Get the status of the valves from FPGA + U16 status = getFPGAValvesStatus(); + + for ( valve = VDI; valve < NUM_OF_VALVES; valve++ ) + { + mode = valvesStatus[ valve ].controlMode; + + // Depending on the control mode of the valve, different bit masks will be checked + if ( mode == VALVE_CONTROL_MODE_ENABLE_PID ) + { + if ( ! ( status & valvesControlStatusBits[ valve ][ VALVE_CONTROL_STATUS_INITIALIZED ] ) ) + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_VALVE_NOT_FUNCTIONAL, (U32)valve, (U32)mode ); + result = FALSE; + } + if ( ! ( status & valvesControlStatusBits[ valve ][ VALVE_CONTROL_STATUS_PID_ENABLED ] ) ) + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_VALVE_NOT_FUNCTIONAL, (U32)valve, (U32)mode ); + result = FALSE; + } + } + else if ( mode == VALVE_CONTORL_MODE_ENABLE_BYPASS ) + { + if ( ! ( status & valvesControlStatusBits[ valve ][ VALVE_CONTROL_STATUS_BYPASS_ENABLED ] ) ) + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_VALVE_NOT_FUNCTIONAL, (U32)valve, (U32)mode ); + result = FALSE; + } + } + else if ( mode == VALVE_CONTROL_MODE_DISABLE_ALL ) + { + if ( ! ( status & ( ~valvesControlStatusBits[ valve ][ VALVE_CONTROL_STATUS_DISABLED ] ) ) ) + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_VALVE_NOT_FUNCTIONAL, (U32)valve, (U32)mode ); + result = FALSE; + } + } + // Invalid mode was selected + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_CONTROL_MODE_SELECTED, (U32)valve ); + result = FALSE; + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The setFPGAValue function sets the position of a valve to FPGA. + * @details Inputs: none + * @details Outputs: none + * @param valve that its FPGA set point is updated + * @param position that is set in counts + * @param enableCurrentRelaxation flag. Indicates whether current relaxation + * should be enabled for the valve + * @return none + *************************************************************************/ +static void setFPGAValveSetPoint( VALVE_T valve, S16 position, BOOL enableCurrentRelaxation ) +{ + // Always set the MSB of the S16 to 0 + position &= DISABLE_VALVE_CURRENT_RELAXATION; + + // If current relaxation is needed, set the MSB to 1 + if ( enableCurrentRelaxation ) + { + position |= ENABLE_VALVE_CURRENT_RELAXATION; + } + + switch ( valve ) + { + case VDI: + setFPGAValveDialyzerInletPosition( position ); + break; + + case VDO: + setFPGAValveDialyzerOutletPosition( position ); + break; + + case VBA: + setFPGAValveBloodArterialPosition( position ); + break; + + case VBV: + setFPGAValveBloodVenousPosition( position ); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_VALVE_SELECTED, (U32)valve ); + break; + } +} + +/*********************************************************************//** + * @brief + * The convertAndMonitorValvesCurrent function gets the current values + * in counts from FPGA and converts them to current. If any of the values + * is above the threshold, the function raises an alarm. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus + * @return none + *************************************************************************/ +static void convertAndMonitorValvesCurrent( void ) +{ + VALVE_T valve; + U16 currentInADC = 0; + + currentInADC = getFPGAValveDialyzerInletCurrentCounts(); + valvesStatus[ VDI ].current = ( (F32)currentInADC / ADC_TO_CURRENT_CONVERSION_CONSTANT ) - 1; + + currentInADC = getFPGAValveDialyzerOutletCurrentCounts(); + valvesStatus[ VDO ].current = ( (F32)currentInADC / ADC_TO_CURRENT_CONVERSION_CONSTANT ) - 1; + + currentInADC = getFPGAValveBloodArterialCurrentCounts(); + valvesStatus[ VBA ].current = ( (F32)currentInADC / ADC_TO_CURRENT_CONVERSION_CONSTANT ) - 1; + + currentInADC = getFPGAValveBloodVenousCurrentCounts(); + valvesStatus[ VBV ].current = ( (F32)currentInADC / ADC_TO_CURRENT_CONVERSION_CONSTANT ) - 1; + + // Check the current of all the valves + for ( valve = VDI; valve < NUM_OF_VALVES; valve++) + { + // Absolute value is used to cover both positive and negative values + F32 current = fabs( valvesStatus[ valve ].current ); + + if ( current > VALVES_CURRENT_THRESHOLD_AMPS ) + { + valvesStatus[ valve ].overCurrentCounter++; + } + // Check if the current is over the threshold for the defined amount of time + if ( valvesStatus[ valve ].overCurrentCounter > MAX_OVER_CURRENT_TIME_INTERVAL_COUNTER ) + { + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_VALVE_CURRENT_OUT_OF_RANGE, (F32)valve, current ); + } + // If the current is below the threshold again and the counter for the time is greater than + else if ( current < VALVES_CURRENT_THRESHOLD_AMPS && + valvesStatus[ valve ].overCurrentCounter > 0 ) + { + valvesStatus[ valve ].overCurrentCounter = 0; + } + } +} + +/*********************************************************************//** + * @brief + * The getAndMonitorValvesCurrentPosition function gets the current position \n + * of the valves and stores them into the structure of each valve. The function \n + * checks whether any of the valves have deviated from their current position \n + * if they are in idle state. If any of the valves have deviated, the function \n + * raises an alarm. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus + * @return none + *************************************************************************/ +static void getAndMonitorValvesCurrentPosition( void ) +{ + VALVE_T valve; + S16 currentPostion = 0; + S16 commandedPoistion = 0; + VALVE_POSITION_T commandedPositionEnum; + + // Get the position of the valves and update the structure of each valve + valvesStatus[ VDI ].currentPositionInCounts = getFPGAValveDialyzerInletPosition(); + valvesStatus[ VDO ].currentPositionInCounts = getFPGAValveDialyzerOutletPosition(); + valvesStatus[ VBA ].currentPositionInCounts = getFPGAValveBloodArterialPosition(); + valvesStatus[ VBV ].currentPositionInCounts = getFPGAValveBloodVenousPosition(); + +#ifndef DISABLE_3WAY_VALVES + // Check the position of each valve + for ( valve = VDI; valve < NUM_OF_VALVES; valve++ ) + { + // Check the valve is in Idle state, otherwise the position should not be checked + if ( valvesStatus[ valve ].execState == VALVE_STATE_IDLE ) + { + U32 maxDeviation = MAX_DEVIATION_FROM_TARGET_IN_COUNTS; + + currentPostion = valvesStatus[ valve ].currentPositionInCounts; + commandedPositionEnum = valvesStatus[ valve ].commandedPosition; + commandedPoistion = valvesStatus[ valve ].positions[ commandedPositionEnum ]; + + // If the current position is B, current relaxation requires looser range check + if ( VALVE_POSITION_B_OPEN == commandedPositionEnum ) + { + maxDeviation = MAX_DEVIATION_FROM_TRAGET_IN_POS_B; + } + + // Check if the current position has deviated from the position it is supposed to be in + // for more than a certain amount of time. If it has, raise an alarm + // Absolute value is used for comparison to cover +/- from the commanded position + if ( abs( currentPostion - commandedPoistion ) > maxDeviation ) + { + valvesStatus[ valve ].positionOutOfRangeCounter++; + } + + if ( valvesStatus[ valve ].positionOutOfRangeCounter > MAX_POS_DEVIATION_TIME_INTERVAL_COUNTER ) + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_VALVE_POSITION_OUT_OF_RANGE, (U32)valve, currentPostion ); + } + else if ( abs( currentPostion - commandedPoistion ) < MAX_DEVIATION_FROM_TARGET_IN_COUNTS && + valvesStatus[ valve ].positionOutOfRangeCounter > 0 ) + { + valvesStatus[ valve ].positionOutOfRangeCounter = 0; + } + } + } +#endif +} + +/*********************************************************************//** + * @brief + * The getPublishValvesDataInterval function gets the valves data publish + * interval. + * @details Inputs: valvesDataPublishInterval + * @details Outputs: none + * @return data publish time interval + *************************************************************************/ +U32 getPublishValvesDataInterval( void ) +{ + U32 result = valvesDataPublishInterval.data; + + if ( OVERRIDE_KEY == valvesDataPublishInterval.override ) + { + result = valvesDataPublishInterval.ovData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The publishValvesData function publishes the data of the valves at the + * specified time interval. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus + * @param valve that its data is published + * @return none + *************************************************************************/ +static void publishValvesData( VALVE_T valve ) +{ + // Check the counter + if ( ++valvesStatus[ valve ].dataPublishCounter > getPublishValvesDataInterval() ) + { + HD_VALVE_DATA_T valveData; + valveData.valveID = (U32)valve; + valveData.state = (U32)valvesStatus[ valve ].execState; + valveData.currentPosID = (U32)valvesStatus[ valve ].currentPosition; + valveData.currentPos = valvesStatus[ valve ].currentPositionInCounts; + valveData.nextPos = valvesStatus[ valve ].targetPositionInCounts; + valveData.current = valvesStatus[ valve ].current; + valveData.posC = valvesStatus[ valve ].positions[ VALVE_POSITION_C_CLOSE ]; + valveData.posA = valvesStatus[ valve ].positions[ VALVE_POSITION_A_INSERT_EJECT ]; + valveData.posB = valvesStatus[ valve ].positions[ VALVE_POSITION_B_OPEN ]; + valveData.pwm = valvesStatus[ valve ].bypassModeStatus.currentPWMInPercent; + valveData.airTrapValve = (U32)getValveAirTrapStatus(); + + broadcastHDValves( &valveData ); + + valvesStatus[ valve ].dataPublishCounter = 0; + } +} + +/*********************************************************************//** + * @brief + * The setValveNextStep function calculates the next step of a valve and + * calls another function to set the next step. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus + * @param valve that its next step is calculated and set + * @return none + *************************************************************************/ +static void setValveNextStep( VALVE_T valve, U32 stepChange ) +{ + S16 nextStep = 0; + VALVE_POSITION_T currentPositionEnum = valvesStatus[ valve ].currentPosition; + VALVE_POSITION_T commandedPositionEnum = valvesStatus[ valve ].commandedPosition; + S16 currentPosition = valvesStatus[ valve ].currentPositionInCounts; + S16 commandedPosition = valvesStatus[ valve ].positions[ commandedPositionEnum ]; + + // If the next step is less than the specified step change, set that + if ( abs( currentPosition - commandedPosition ) <= stepChange ) + { + nextStep = abs( currentPosition - commandedPosition ); + } + else + { + nextStep = stepChange; + } + + switch ( currentPositionEnum ) + { + case VALVE_POSITION_NOT_IN_POSITION: + // Do nothing. It will be faulted in the monitor function if it is out of position + break; + + case VALVE_POSITION_A_INSERT_EJECT: + // If the valve is currently in position A and its commanded position is position B, + // add the defined number of steps for the next transition + // If the valve is currently in position A and its commanded position is position C, + // subtract the defined number of steps for the next transition + if ( commandedPositionEnum == VALVE_POSITION_B_OPEN ) + { + valvesStatus[ valve ].targetPositionInCounts = valvesStatus[ valve ].currentPositionInCounts + nextStep; + } + if ( commandedPositionEnum == VALVE_POSITION_C_CLOSE ) + { + valvesStatus[ valve ].targetPositionInCounts = valvesStatus[ valve ].currentPositionInCounts - nextStep; + } + break; + + case VALVE_POSITION_B_OPEN: + // If the valve is currently in position B, subtract the defined number of steps for the next transition + valvesStatus[ valve ].targetPositionInCounts = valvesStatus[ valve ].currentPositionInCounts - nextStep; + break; + + case VALVE_POSITION_C_CLOSE: + // If the valve is currently in position C, add the defined number of steps for the next transition + valvesStatus[ valve ].targetPositionInCounts = valvesStatus[ valve ].currentPositionInCounts + nextStep; + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VAVLES_INVALID_POSITION_SELECTED, (U32)valve ); + break; + } + + // Call the function to send the set point to FPGA. Current relaxation is not + // enabled here, so it is always false + setFPGAValveSetPoint( valve, valvesStatus[ valve ].targetPositionInCounts, FALSE ); +} + +#ifdef DEBUG_ENABLED +/*********************************************************************//** + * @brief + * The setFPGAValvePWM function checks whether the valve should be moving + * CW or CCW and converts the PWM in percent to PWM in counts accordingly. + * The converted PWM in counts is set via the FPGA functions. + * @details Inputs: valvesStatus + * @details Outputs: none + * @param valve which its PWM is being set + * @return none + *************************************************************************/ +static void setFPGAValvePWM( VALVE_T valve ) +{ + U16 pwmInCounts = 0; + U16 commandedPWMPct = valvesStatus[ valve ].bypassModeStatus.commandedPWMInPercent; + + // Check the requested direction and convert PWM in percent to counts + if ( valvesStatus[ valve ].bypassModeStatus.direction == VAVLE_DIR_CLOCKWISE ) + { + pwmInCounts = VALVE_CW_PWM_TO_CNT_CONVERSION( commandedPWMPct ); + } + else + { + pwmInCounts = VALVE_CCW_PWM_TO_CNT_CONVERSION( commandedPWMPct ); + } + + switch ( valve ) + { + case VDI: + setFPGAValveDialyzerInletPWM( pwmInCounts ); + break; + + case VDO: + setFPGAValveDialyzerOutletPWM( pwmInCounts ); + break; + + case VBA: + setFPGAValveBloodArterialPWM( pwmInCounts ); + break; + + case VBV: + setFPGAValveBloodVenousPWM( pwmInCounts ); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_VALVES_INVALID_VALVE_SELECTED, (U32)valve ); + break; + } +} + +/*********************************************************************//** + * @brief + * The getValvesCurrentPWM function gets the PWM counts from the FPGA module + * and converts them in to duty cycle in percent. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus + * @return none + *************************************************************************/ +static void getValvesCurrentPWM( void ) +{ + VALVE_T valve; + U16 currentPWM = 0; + + // Get all the PWMs in count + U16 pwmInCounts[ NUM_OF_VALVES ] = { getFPGAValveDialyzerInletPWM(), getFPGAValveDialyzerOutletPWM(), + getFPGAValveBloodArterialPWM(), getFPGAValveBloodVenousPWM() }; + + for ( valve = VDI; valve < NUM_OF_VALVES; valve++ ) + { + // Get the count + currentPWM = pwmInCounts[ valve ]; + + // Check if the direction is clockwise or not to use the right conversion equation + if ( valvesStatus[ valve ].bypassModeStatus.direction == VAVLE_DIR_CLOCKWISE ) + { + currentPWM = VALVE_CW_CNT_TO_PWM_CONVERSION( currentPWM ); + } + else + { + currentPWM = VALVE_CCW_CNT_TO_PWM_CONVERSION( currentPWM ); + } + + valvesStatus[ valve ].bypassModeStatus.currentPWMInPercent = currentPWM; + } +} +#endif + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + +/*********************************************************************//** + * @brief + * The testSetValvesDataPublishInterval function overrides the valves data + * publish interval. + * @details Inputs: valvesDataPublishInterval + * @details Outputs: valvesDataPublishInterval + * @param value which is override value for the valve data publish interval + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetValvesDataPublishInterval( U32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + U32 intvl = value / TASK_PRIORITY_INTERVAL; + + result = TRUE; + valvesDataPublishInterval.ovData = intvl; + valvesDataPublishInterval.override = OVERRIDE_KEY; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetValvesDataPublishInterval function resets the override + * of the valves publish interval. + * @details Inputs: valvesDataPublishInterval + * @details Outputs: valvesDataPublishInterval + * @return TRUE if override reset successful, FALSE if not + *************************************************************************/ +BOOL testResetValvesDataPublishInterval( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + valvesDataPublishInterval.override = OVERRIDE_RESET; + valvesDataPublishInterval.ovData = valvesDataPublishInterval.ovInitData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetValvesPositionOverride function overrides the valves position. + * @details Inputs: valvesPositionOverride, valvesStatus + * @details Outputs: valvesPositionOverride + * @param valve to override its position + * @param position of the valve that will be overridden + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetValvesPositionOverride( U32 valve, U32 position ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() && valve < NUM_OF_VALVES ) + { + // Get the current position of the valve as the initial position + valvesPositionOverride[ valve ].ovInitData = (U32)valvesStatus[ valve ].currentPosition; + valvesPositionOverride[ valve ].ovData = position; + valvesPositionOverride[ valve ].override = OVERRIDE_KEY; + + result = setValvePosition( (VALVE_T)valve, (VALVE_POSITION_T)position ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetValvesDataPublishInterval function resets the override + * of the valves publish interval. + * @details Inputs: valvesDataPublishInterval + * @details Outputs: valvesDataPublishInterval + * @param valve that its data publish will be overridden + * @return TRUE if override reset successful, FALSE if not + *************************************************************************/ +BOOL testResetValvesPositionOverride( U32 valve ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() && valve < NUM_OF_VALVES ) + { + valvesPositionOverride[ valve ].override = OVERRIDE_RESET; + valvesPositionOverride[ valve ].ovData = valvesPositionOverride[ valve ].ovInitData; + + result = setValvePosition( (VALVE_T)valve, (VALVE_POSITION_T)valvesPositionOverride[ valve ].ovInitData ); + } + + return result; +} + +#ifdef DEBUG_ENABLED +/*********************************************************************//** + * @brief + * The testSetValvePWMOverride function overrides the valves PWM in bypass + * control mode. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus + * @param valve to override its PWM + * @param pwm in percent + * @param direction of the movement of the valve in clockwise or counter + * clockwise + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetValvePWMOverride( U32 valve, U32 pwm, U32 direction ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() && valve < NUM_OF_VALVES ) + { + BOOL homingStatus = valvesStatus[ valve ].hasValveBeenHomed; + + // The PWM should be in range and also the valve direction should be legal and the valve must have been homed + // prior to running this command + if ( pwm <= VALVE_MAX_ALLOWED_PWM_PERCENT && direction < NUM_OF_VALVE_DIRECTION && homingStatus ) + { + valvesStatus[ (VALVE_T)valve ].bypassModeStatus.commandedPWMInPercent = (U16)pwm; + valvesStatus[ (VALVE_T)valve ].bypassModeStatus.direction = (VALVE_DIRECTION_T)direction; + // This flag sets the valves to bypass mode in the idle state. After that the valve will be sitting in the + // bypass mode until this flag is set to FALSE in the reset function. + valvesStatus[ (VALVE_T)valve ].bypassModeStatus.hasBypassModeBeenRequeseted = TRUE; + // This flag is used to check if a change in PWM has been requested so the new PWM is processed and set + valvesStatus[ (VALVE_T)valve ].bypassModeStatus.hasChangeBeenRequested = TRUE; + + result = TRUE; + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetValvePWMOverride function resets the override PWM in bypass + * control mode. + * @details Inputs: valvesStatus + * @details Outputs: valvesStatus + * @param valve that its data publish will be overridden + * @return TRUE if override reset successful, FALSE if not + *************************************************************************/ +BOOL testResetValvePWMOverride( U32 valve ) +{ + BOOL result = FALSE; + + // Check if the valve is in the range of the valves available + if ( TRUE == isTestingActivated() && valve < NUM_OF_VALVES ) + { + // Turn off the bypass mode request and the change request + valvesStatus[ (VALVE_T)valve ].bypassModeStatus.hasBypassModeBeenRequeseted = FALSE; + valvesStatus[ (VALVE_T)valve ].bypassModeStatus.hasChangeBeenRequested = FALSE; + + result = TRUE; + } + + return result; +} +#endif + +/**@}*/