/************************************************************************** * * 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 BloodFlow.c * * @author (last) Sean Nash * @date (last) 24-Jun-2020 * * @author (original) Sean Nash * @date (original) 07-Nov-2019 * ***************************************************************************/ #include #include "can.h" #include "etpwm.h" // TODO - test includes - remove later #include "DialInFlow.h" #include "PresOccl.h" #include "FPGA.h" #include "InternalADC.h" #include "NVDataMgmt.h" #include "OperationModes.h" #include "PIControllers.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TaskPriority.h" #include "Timers.h" #include "BloodFlow.h" /** * @addtogroup BloodFlow * @{ */ // ********** private definitions ********** #define BLOOD_FLOW_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< interval (ms/task time) at which the blood flow data is published on the CAN bus #define MAX_BLOOD_PUMP_PWM_STEP_UP_CHANGE 0.008 ///< max duty cycle change when ramping up ~ 100 mL/min/s. #define MAX_BLOOD_PUMP_PWM_STEP_DN_CHANGE 0.016 ///< max duty cycle change when ramping down ~ 200 mL/min/s. #define MAX_BLOOD_PUMP_PWM_DUTY_CYCLE 0.88 ///< controller will error if PWM duty cycle > 90%, so set max to 88% #define MIN_BLOOD_PUMP_PWM_DUTY_CYCLE 0.12 ///< controller will error if PWM duty cycle < 10%, so set min to 12% #define BP_CONTROL_INTERVAL ( 10000 / TASK_GENERAL_INTERVAL ) ///< interval (ms/task time) at which the blood pump is controlled #define BP_P_COEFFICIENT 0.00035 ///< P term for blood pump control #define BP_I_COEFFICIENT 0.00035 ///< I term for blood pump control #define BP_HOME_RATE 100 ///< target pump speed (in estimate mL/min) for homing. #define BP_HOME_TIMEOUT_MS 10000 ///< maximum time allowed for homing to complete (in ms). #define BP_SPEED_CALC_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< interval (ms/task time) at which the blood pump speed is calculated. #define BP_HALL_EDGE_COUNTS_PER_REV 48 ///< number of hall sensor edge counts per motor revolution. #define BP_MAX_ROTOR_SPEED_RPM 100.0 ///< maximum rotor speed allowed for blood pump. #define BP_MAX_FLOW_VS_SPEED_DIFF_ML_MIN 50.0 ///< maximum difference between measured flow and flow implied by measured motor speed. #define BP_MAX_MOTOR_SPEED_WHILE_OFF_RPM 100.0 ///< maximum motor speed (RPM) while motor is commanded off. #define BP_MAX_ROTOR_VS_MOTOR_DIFF_RPM 5.0 ///< maximum difference in speed between motor and rotor (in rotor RPM). #define BP_MAX_MOTOR_SPEED_ERROR_RPM 300.0 ///< maximum difference in speed between measured and commanded RPM. #define BP_FLOW_VS_SPEED_PERSIST ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< persist time (task intervals) for flow vs. motor speed error condition. #define BP_OFF_ERROR_PERSIST ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< persist time (task intervals) for motor off error condition. #define BP_MOTOR_SPEED_ERROR_PERSIST ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< persist time (task intervals) motor speed error condition. #define BP_ROTOR_SPEED_ERROR_PERSIST ((12 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< persist time (task intervals) rotor speed error condition. #define BP_MAX_CURR_WHEN_STOPPED_MA 150.0 ///< motor controller current should not exceed this when pump should be stopped #define BP_MIN_CURR_WHEN_RUNNING_MA 150.0 ///< motor controller current should always exceed this when pump should be running #define BP_MAX_CURR_WHEN_RUNNING_MA 1000.0 ///< motor controller current should not exceed this when pump should be running #define BP_MAX_CURR_ERROR_DURATION_MS 2000 ///< motor controller current errors persisting beyond this duration will trigger an alarm #define BP_SPEED_ADC_TO_RPM_FACTOR 1.280938 ///< conversion factor from ADC counts to RPM for blood pump motor #define BP_CURRENT_ADC_TO_MA_FACTOR 3.002 ///< conversion factor from ADC counts to mA for blood pump motor #define BP_REV_PER_LITER 150.24 ///< rotor revolutions per liter #define BP_ML_PER_MIN_TO_PUMP_RPM_FACTOR ( BP_REV_PER_LITER / ML_PER_LITER ) ///< conversion factor from mL/min to motor RPM. #define BP_GEAR_RATIO 32.0 ///< blood pump motor to blood pump gear ratio #define BP_MOTOR_RPM_TO_PWM_DC_FACTOR 0.00028 ///< ~28 BP motor RPM = 1% PWM duty cycle #define BP_PWM_ZERO_OFFSET 0.1 ///< 10% PWM duty cycle = zero speed #define BP_PWM_FROM_ML_PER_MIN(rate) ( (rate) * BP_ML_PER_MIN_TO_PUMP_RPM_FACTOR * BP_GEAR_RATIO * BP_MOTOR_RPM_TO_PWM_DC_FACTOR + BP_PWM_ZERO_OFFSET ) ///< conversion factor from mL/min to estimated PWM duty cycle %. #define BLOODPUMP_ADC_FULL_SCALE_V 3.0 ///< BP analog signals are 0-3V (while int. ADC ref may be different) #define BLOODPUMP_ADC_ZERO 1998 ///< Blood pump ADC channel zero offset. #define SIGN_FROM_12_BIT_VALUE(v) ( (S16)(v) - (S16)BLOODPUMP_ADC_ZERO ) ///< macro converts 12 bit ADC value to signed 16-bit value. #define SIZE_OF_ROLLING_AVG ( ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) * 10 ) ///< measured blood flow is filtered w/ moving average /// Enumeration of blood pump controller states. typedef enum BloodPump_States { BLOOD_PUMP_OFF_STATE = 0, ///< Blood pump off state. BLOOD_PUMP_RAMPING_UP_STATE, ///< Blood pump ramping up state. BLOOD_PUMP_RAMPING_DOWN_STATE, ///< Blood pump ramping down state. BLOOD_PUMP_CONTROL_TO_TARGET_STATE, ///< Blood pump controlling to target state. NUM_OF_BLOOD_PUMP_STATES ///< Number of blood pump states. } BLOOD_PUMP_STATE_T; /// Enumeration of blood pump self test states. typedef enum BloodFlow_Self_Test_States { BLOOD_FLOW_SELF_TEST_STATE_START = 0, ///< Blood pump self test start state. BLOOD_FLOW_TEST_STATE_IN_PROGRESS, ///< Blood pump self test in progress state. BLOOD_FLOW_TEST_STATE_COMPLETE, ///< Blood pump self test completed state. NUM_OF_BLOOD_FLOW_SELF_TEST_STATES ///< Number of blood pump self test states. } BLOOD_FLOW_SELF_TEST_STATE_T; // pin assignments for pump stop and direction outputs #define STOP_CAN3_PORT_MASK 0x00000002 // (Tx - re-purposed as output GPIO for blood pump stop signal) #define DIR_CAN3_PORT_MASK 0x00000002 // (Rx - re-purposed as output GPIO for blood pump direction signal) // blood pump stop and direction macros #define SET_BP_DIR() {canREG3->RIOC |= DIR_CAN3_PORT_MASK;} // macro to set blood pump direction signal high. #define CLR_BP_DIR() {canREG3->RIOC &= ~DIR_CAN3_PORT_MASK;} // macro to set blood pump direction signal low. #ifndef BREADBOARD_TARGET #define SET_BP_STOP() {canREG3->TIOC &= ~STOP_CAN3_PORT_MASK;} // macro to set blood pump stop signal (active low). #define CLR_BP_STOP() {canREG3->TIOC |= STOP_CAN3_PORT_MASK;} // macro to clear blood pump stop signal (active low). #else #define SET_BP_STOP() {canREG3->TIOC |= STOP_CAN3_PORT_MASK;} #define CLR_BP_STOP() {canREG3->TIOC &= ~STOP_CAN3_PORT_MASK;} #endif // ********** private data ********** static BLOOD_PUMP_STATE_T bloodPumpState = BLOOD_PUMP_OFF_STATE; ///< current state of blood flow controller state machine static U32 bloodFlowDataPublicationTimerCounter = 0; ///< used to schedule blood flow data publication to CAN bus static BOOL isBloodPumpOn = FALSE; ///< blood pump is currently running static F32 bloodPumpPWMDutyCyclePct = 0.0; ///< initial blood pump PWM duty cycle static F32 bloodPumpPWMDutyCyclePctSet = 0.0; ///< currently set blood pump PWM duty cycle static MOTOR_DIR_T bloodPumpDirection = MOTOR_DIR_FORWARD; ///< requested blood flow direction static MOTOR_DIR_T bloodPumpDirectionSet = MOTOR_DIR_FORWARD; ///< currently set blood flow direction static PUMP_CONTROL_MODE_T bloodPumpControlMode = PUMP_CONTROL_MODE_CLOSED_LOOP; ///< requested blood pump control mode. static PUMP_CONTROL_MODE_T bloodPumpControlModeSet = PUMP_CONTROL_MODE_CLOSED_LOOP; ///< currently set blood pump control mode. static F32 bloodFlowCalGain = 1.0; ///< blood flow calibration gain. static F32 bloodFlowCalOffset = 0.0; ///< blood flow calibration offset. static OVERRIDE_U32_T bloodFlowDataPublishInterval = { BLOOD_FLOW_DATA_PUB_INTERVAL, BLOOD_FLOW_DATA_PUB_INTERVAL, BLOOD_FLOW_DATA_PUB_INTERVAL, 0 }; ///< Interval (in ms) at which to publish blood flow data to CAN bus static OVERRIDE_S32_T targetBloodFlowRate = { 0, 0, 0, 0 }; ///< requested blood flow rate static OVERRIDE_F32_T measuredBloodFlowRate = { 0.0, 0.0, 0.0, 0 }; ///< measured blood flow rate static OVERRIDE_F32_T bloodPumpRotorSpeedRPM = { 0.0, 0.0, 0.0, 0 }; ///< measured blood pump rotor speed static OVERRIDE_F32_T bloodPumpSpeedRPM = { 0.0, 0.0, 0.0, 0 }; ///< measured blood pump motor speed static OVERRIDE_F32_T adcBloodPumpMCSpeedRPM = { 0.0, 0.0, 0.0, 0 }; ///< measured blood pump motor controller speed static OVERRIDE_F32_T adcBloodPumpMCCurrentmA = { 0.0, 0.0, 0.0, 0 }; ///< measured blood pump motor controller current static U32 bpControlTimerCounter = 0; ///< determines when to perform control on blood flow static U32 bpRotorRevStartTime = 0; ///< blood pump rotor rotation start time (in ms) static BOOL bpStopAtHomePosition = FALSE; ///< stop blood pump at next home position static U32 bpHomeStartTime = 0; ///< when did blood pump home command begin? (in ms) static U16 bpLastMotorHallSensorCount = 0; ///< last hall sensor count for the blood pump motor static MOTOR_DIR_T bpMotorDirectionFromHallSensors = MOTOR_DIR_FORWARD; ///< pump direction according to hall sensor count static U32 bpMotorSpeedCalcTimerCtr = 0; ///< counter determines interval for calculating blood pump motor speed from hall sensor count. static U32 errorBloodFlowVsMotorSpeedPersistTimerCtr = 0; ///< persistence timer counter for flow vs. motor speed error condition. static U32 errorBloodMotorOffPersistTimerCtr = 0; ///< persistence timer counter for motor off check error condition. static U32 errorBloodMotorSpeedPersistTimerCtr = 0; ///< persistence timer counter for motor speed error condition. static U32 errorBloodRotorSpeedPersistTimerCtr = 0; ///< persistence timer counter for rotor speed error condition. static F32 flowReadings[ SIZE_OF_ROLLING_AVG ]; ///< holds flow samples for a rolling average static U32 flowReadingsIdx = 0; ///< index for next sample in rolling average array static F32 flowReadingsTotal = 0.0; ///< rolling total - used to calc average static U32 flowReadingsCount = 0; ///< number of samples in flow rolling average buffer static U32 bpCurrErrorDurationCtr = 0; ///< used for tracking persistence of bp current errors static BLOOD_FLOW_SELF_TEST_STATE_T bloodPumpSelfTestState = BLOOD_FLOW_SELF_TEST_STATE_START; ///< current blood pump self test state static U32 bloodPumpSelfTestTimerCount = 0; ///< timer counter for blood pump self test // ********** private function prototypes ********** static BLOOD_PUMP_STATE_T handleBloodPumpOffState( void ); static BLOOD_PUMP_STATE_T handleBloodPumpRampingUpState( void ); static BLOOD_PUMP_STATE_T handleBloodPumpRampingDownState( void ); static BLOOD_PUMP_STATE_T handleBloodPumpControlToTargetState( void ); static void setBloodPumpControlSignalPWM( F32 newPWM ); static void stopBloodPump( void ); static void releaseBloodPumpStop( void ); static void setBloodPumpDirection( MOTOR_DIR_T dir ); static void publishBloodFlowData( void ); static void resetBloodFlowMovingAverage( void ); static void filterBloodFlowReadings( F32 flow ); static void updateBloodPumpSpeedAndDirectionFromHallSensors( void ); static void checkBloodPumpRotor( void ); static void checkBloodPumpDirection( void ); static void checkBloodPumpSpeeds( void ); static void checkBloodPumpFlowAgainstSpeed( void ); static void checkBloodPumpMCCurrent( void ); static DATA_GET_PROTOTYPE( U32, getPublishBloodFlowDataInterval ); /*********************************************************************//** * @brief * The initBloodFlow function initializes the BloodFlow module. * @details * Inputs : none * Outputs : BloodFlow module initialized. * @return none *************************************************************************/ void initBloodFlow( void ) { bpLastMotorHallSensorCount = getFPGABloodPumpHallSensorCount(); stopBloodPump(); setBloodPumpDirection( MOTOR_DIR_FORWARD ); // zero rolling flow average buffer resetBloodFlowMovingAverage(); // initialize blood flow PI controller initializePIController( PI_CONTROLLER_ID_BLOOD_FLOW, MIN_BLOOD_PUMP_PWM_DUTY_CYCLE, BP_P_COEFFICIENT, BP_I_COEFFICIENT, MIN_BLOOD_PUMP_PWM_DUTY_CYCLE, MAX_BLOOD_PUMP_PWM_DUTY_CYCLE ); } /*********************************************************************//** * @brief * The setBloodPumpTargetFlowRate function sets a new target flow rate and * pump direction. * @details * Inputs : isBloodPumpOn, bloodPumpDirectionSet * Outputs : targetBloodFlowRate, bloodPumpdirection, bloodPumpPWMDutyCyclePct * @param flowRate new target blood flow rate * @param dir new blood flow direction * @param mode new control mode * @return TRUE if new flow rate & dir are set, FALSE if not *************************************************************************/ BOOL setBloodPumpTargetFlowRate( U32 flowRate, MOTOR_DIR_T dir, PUMP_CONTROL_MODE_T mode ) { BOOL result = FALSE; // direction change while pump is running is not allowed if ( ( FALSE == isBloodPumpOn ) || ( 0 == flowRate ) || ( dir == bloodPumpDirectionSet ) ) { // verify flow rate if ( flowRate <= MAX_BLOOD_FLOW_RATE ) { resetBloodFlowMovingAverage(); targetBloodFlowRate.data = ( dir == MOTOR_DIR_FORWARD ? (S32)flowRate : (S32)flowRate * -1 ); bloodPumpDirection = dir; bloodPumpControlMode = mode; // set PWM duty cycle target to an estimated initial target to ramp to based on target flow rate - then we'll control to flow when ramp completed bloodPumpPWMDutyCyclePct = BP_PWM_FROM_ML_PER_MIN((F32)flowRate); // ~ 8% per 100 mL/min with a 10% zero offset added in (e.g. 100 mL/min = 8+10 = 18%) switch ( bloodPumpState ) { case BLOOD_PUMP_RAMPING_UP_STATE: // see if we need to reverse direction of ramp if ( bloodPumpPWMDutyCyclePct < bloodPumpPWMDutyCyclePctSet ) { bloodPumpState = BLOOD_PUMP_RAMPING_DOWN_STATE; } break; case BLOOD_PUMP_RAMPING_DOWN_STATE: // see if we need to reverse direction of ramp if ( bloodPumpPWMDutyCyclePct > bloodPumpPWMDutyCyclePctSet ) { bloodPumpState = BLOOD_PUMP_RAMPING_UP_STATE; } break; case BLOOD_PUMP_CONTROL_TO_TARGET_STATE: // start ramp to new target in appropriate direction if ( bloodPumpPWMDutyCyclePctSet > bloodPumpPWMDutyCyclePct ) { bloodPumpState = BLOOD_PUMP_RAMPING_DOWN_STATE; } else { bloodPumpState = BLOOD_PUMP_RAMPING_UP_STATE; } break; default: // ok - not all states need to be handled here break; } result = TRUE; } else // requested flow rate too high { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_BLOOD_FLOW_SET_TOO_HIGH, flowRate ) } } return result; } /*********************************************************************//** * @brief * The signalBloodPumpHardStop function stops the blood pump immediately. * @details * Inputs : none * Outputs : Blood pump stopped, set point reset, state changed to off * @return none *************************************************************************/ void signalBloodPumpHardStop( void ) { targetBloodFlowRate.data = 0; stopBloodPump(); bloodPumpState = BLOOD_PUMP_OFF_STATE; bloodPumpPWMDutyCyclePct = 0.0; bpControlTimerCounter = 0; resetPIController( PI_CONTROLLER_ID_BLOOD_FLOW, MIN_BLOOD_PUMP_PWM_DUTY_CYCLE ); } /*********************************************************************//** * @brief * The signalBloodPumpRotorHallSensor function handles the blood pump rotor * hall sensor detection. Calculates rotor speed (in RPM). Stops pump if * there is a pending request to home the pump. * @details * Inputs : bpRotorRevStartTime, bpStopAtHomePosition * Outputs : bpRotorRevStartTime, bloodPumpRotorSpeedRPM * @return none *************************************************************************/ void signalBloodPumpRotorHallSensor( void ) { U32 rotTime = getMSTimerCount(); U32 deltaTime = calcTimeBetween( bpRotorRevStartTime, rotTime ); // calculate rotor speed (in RPM) bloodPumpRotorSpeedRPM.data = ( 1.0 / (F32)deltaTime ) * (F32)MS_PER_SECOND * (F32)SEC_PER_MIN; bpRotorRevStartTime = rotTime; // if we're supposed to stop pump at home position, stop pump now. if ( TRUE == bpStopAtHomePosition ) { signalBloodPumpHardStop(); bpStopAtHomePosition = FALSE; } } /*********************************************************************//** * @brief * The homeBloodPump function initiates a blood pump home operation. * @details * Inputs : bloodPumpState * Outputs : bpStopAtHomePosition, bpHomeStartTime, blood pump started (slow) * @return none *************************************************************************/ BOOL homeBloodPump( void ) { BOOL result = FALSE; if ( BLOOD_PUMP_OFF_STATE == bloodPumpState ) { bpStopAtHomePosition = TRUE; bpHomeStartTime = getMSTimerCount(); result = setBloodPumpTargetFlowRate( BP_HOME_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); } return result; } /*********************************************************************//** * @brief * The execBloodFlowMonitor function executes the blood flow monitor. * @details * Inputs : none * Outputs : measuredBloodFlowRate, adcBloodPumpMCSpeedRPM, adcBloodPumpMCCurrentmA * @return none *************************************************************************/ void execBloodFlowMonitor( void ) { U16 bpRPM = getIntADCReading( INT_ADC_BLOOD_PUMP_SPEED ); U16 bpmA = getIntADCReading( INT_ADC_BLOOD_PUMP_MOTOR_CURRENT ); F32 bpFlow = ( ( getFPGABloodFlow() * -1.0 ) * bloodFlowCalGain ) + bloodFlowCalOffset; // blood flow sensor installed backwards on HD adcBloodPumpMCSpeedRPM.data = (F32)(SIGN_FROM_12_BIT_VALUE(bpRPM)) * BP_SPEED_ADC_TO_RPM_FACTOR; adcBloodPumpMCCurrentmA.data = (F32)(SIGN_FROM_12_BIT_VALUE(bpmA)) * BP_CURRENT_ADC_TO_MA_FACTOR; filterBloodFlowReadings( bpFlow ); // calculate blood pump motor speed/direction from hall sensor count updateBloodPumpSpeedAndDirectionFromHallSensors(); // don't start enforcing checks until out of init/POST mode if ( getCurrentOperationMode() != MODE_INIT ) { // check pump direction checkBloodPumpDirection(); // check pump controller current checkBloodPumpMCCurrent(); // check pump speeds and flow checkBloodPumpSpeeds(); checkBloodPumpFlowAgainstSpeed(); // check for home position, zero/low speed checkBloodPumpRotor(); } // publish blood flow data on interval publishBloodFlowData(); } /*********************************************************************//** * @brief * The execBloodFlowController function executes the blood flow controller. * @details * Inputs : bloodPumpState * Outputs : bloodPumpState * @return none *************************************************************************/ void execBloodFlowController( void ) { switch ( bloodPumpState ) { case BLOOD_PUMP_OFF_STATE: bloodPumpState = handleBloodPumpOffState(); break; case BLOOD_PUMP_RAMPING_UP_STATE: bloodPumpState = handleBloodPumpRampingUpState(); break; case BLOOD_PUMP_RAMPING_DOWN_STATE: bloodPumpState = handleBloodPumpRampingDownState(); break; case BLOOD_PUMP_CONTROL_TO_TARGET_STATE: bloodPumpState = handleBloodPumpControlToTargetState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_BLOOD_FLOW_INVALID_BLOOD_PUMP_STATE, bloodPumpState ) break; } } /*********************************************************************//** * @brief * The handleBloodPumpOffState function handles the blood pump off state * of the blood pump controller state machine. * @details * Inputs : targetBloodFlowRate, bloodPumpDirection * Outputs : bloodPumpPWMDutyCyclePctSet, bloodPumpDirectionSet, isBloodPumpOn * @return next state *************************************************************************/ static BLOOD_PUMP_STATE_T handleBloodPumpOffState( void ) { BLOOD_PUMP_STATE_T result = BLOOD_PUMP_OFF_STATE; // if we've been given a flow rate, setup ramp up and transition to ramp up state if ( getTargetBloodFlowRate() != 0 ) { // set initial PWM duty cycle bloodPumpPWMDutyCyclePctSet = BP_PWM_ZERO_OFFSET + MAX_BLOOD_PUMP_PWM_STEP_UP_CHANGE; setBloodPumpControlSignalPWM( bloodPumpPWMDutyCyclePctSet ); // allow blood pump to run in requested direction setBloodPumpDirection( bloodPumpDirection ); releaseBloodPumpStop(); isBloodPumpOn = TRUE; result = BLOOD_PUMP_RAMPING_UP_STATE; } return result; } /*********************************************************************//** * @brief * The handleBloodPumpRampingUpState function handles the ramp up state * of the blood pump controller state machine. * @details * Inputs : bloodPumpPWMDutyCyclePctSet * Outputs : bloodPumpPWMDutyCyclePctSet * @return next state *************************************************************************/ static BLOOD_PUMP_STATE_T handleBloodPumpRampingUpState( void ) { BLOOD_PUMP_STATE_T result = BLOOD_PUMP_RAMPING_UP_STATE; // have we been asked to stop the blood pump? if ( 0 == getTargetBloodFlowRate() ) { // start ramp down to stop bloodPumpPWMDutyCyclePctSet -= MAX_BLOOD_PUMP_PWM_STEP_DN_CHANGE; setBloodPumpControlSignalPWM( bloodPumpPWMDutyCyclePctSet ); result = BLOOD_PUMP_RAMPING_DOWN_STATE; } // have we reached end of ramp up? else if ( bloodPumpPWMDutyCyclePctSet >= bloodPumpPWMDutyCyclePct ) { resetBloodFlowMovingAverage(); resetPIController( PI_CONTROLLER_ID_BLOOD_FLOW, bloodPumpPWMDutyCyclePctSet ); bloodPumpControlModeSet = bloodPumpControlMode; // if open loop mode, set PWM to requested duty cycle where it will stay during control state if ( bloodPumpControlModeSet == PUMP_CONTROL_MODE_OPEN_LOOP ) { bloodPumpPWMDutyCyclePctSet = bloodPumpPWMDutyCyclePct; setBloodPumpControlSignalPWM( bloodPumpPWMDutyCyclePct ); } result = BLOOD_PUMP_CONTROL_TO_TARGET_STATE; } // continue ramp up else { bloodPumpPWMDutyCyclePctSet += MAX_BLOOD_PUMP_PWM_STEP_UP_CHANGE; setBloodPumpControlSignalPWM( bloodPumpPWMDutyCyclePctSet ); } return result; } /*********************************************************************//** * @brief * The handleBloodPumpRampingDownState function handles the ramp down state * of the blood pump controller state machine. * @details * Inputs : bloodPumpPWMDutyCyclePctSet * Outputs : bloodPumpPWMDutyCyclePctSet * @return next state *************************************************************************/ static BLOOD_PUMP_STATE_T handleBloodPumpRampingDownState( void ) { BLOOD_PUMP_STATE_T result = BLOOD_PUMP_RAMPING_DOWN_STATE; // have we essentially reached zero speed if ( bloodPumpPWMDutyCyclePctSet < (MAX_BLOOD_PUMP_PWM_STEP_UP_CHANGE + BP_PWM_ZERO_OFFSET) ) { stopBloodPump(); result = BLOOD_PUMP_OFF_STATE; } // have we reached end of ramp down? else if ( bloodPumpPWMDutyCyclePctSet <= bloodPumpPWMDutyCyclePct ) { resetBloodFlowMovingAverage(); resetPIController( PI_CONTROLLER_ID_BLOOD_FLOW, bloodPumpPWMDutyCyclePctSet ); bloodPumpControlModeSet = bloodPumpControlMode; // if open loop mode, set PWM to requested duty cycle where it will stay during control state if ( bloodPumpControlModeSet == PUMP_CONTROL_MODE_OPEN_LOOP ) { bloodPumpPWMDutyCyclePctSet = bloodPumpPWMDutyCyclePct; setBloodPumpControlSignalPWM( bloodPumpPWMDutyCyclePct ); } result = BLOOD_PUMP_CONTROL_TO_TARGET_STATE; } // continue ramp down else { bloodPumpPWMDutyCyclePctSet -= MAX_BLOOD_PUMP_PWM_STEP_DN_CHANGE; setBloodPumpControlSignalPWM( bloodPumpPWMDutyCyclePctSet ); } return result; } /*********************************************************************//** * @brief * The handleBloodPumpControlToTargetState function handles the "control to * target" state of the blood pump controller state machine. * @details * Inputs : none * Outputs : bloodPumpState * @return next state *************************************************************************/ static BLOOD_PUMP_STATE_T handleBloodPumpControlToTargetState( void ) { BLOOD_PUMP_STATE_T result = BLOOD_PUMP_CONTROL_TO_TARGET_STATE; // control at set interval if ( ++bpControlTimerCounter >= BP_CONTROL_INTERVAL ) { if ( bloodPumpControlModeSet == PUMP_CONTROL_MODE_CLOSED_LOOP ) { F32 tgtFlow = (F32)getTargetBloodFlowRate(); F32 actFlow = getMeasuredBloodFlowRate(); F32 newPWM; newPWM = runPIController( PI_CONTROLLER_ID_BLOOD_FLOW, tgtFlow, actFlow ); bloodPumpPWMDutyCyclePctSet = newPWM; setBloodPumpControlSignalPWM( newPWM ); } bpControlTimerCounter = 0; } return result; } /*********************************************************************//** * @brief * The setBloodPumpControlSignalPWM function sets the PWM duty cycle for * the blood pump to a given %. * @details * Inputs : none * Outputs : blood pump stop signal activated, PWM duty cycle zeroed * @param newPWM new duty cycle % to apply to PWM * @return none *************************************************************************/ static void setBloodPumpControlSignalPWM( F32 newPWM ) { etpwmSetCmpA( etpwmREG1, (U32)( (S32)( ( newPWM * (F32)(etpwmREG1->TBPRD) ) + FLOAT_TO_INT_ROUNDUP_OFFSET ) ) ); } /*********************************************************************//** * @brief * The stopBloodPump function sets the blood pump stop signal. * @details * Inputs : none * Outputs : blood pump stop signal activated, PWM duty cycle zeroed * @return none *************************************************************************/ static void stopBloodPump( void ) { isBloodPumpOn = FALSE; bloodPumpPWMDutyCyclePctSet = 0.0; etpwmSetCmpA( etpwmREG1, 0 ); SET_BP_STOP(); } /*********************************************************************//** * @brief * The releaseBloodPumpStop function clears the blood pump stop signal. * @details * Inputs : none * Outputs : blood pump stop signal * @return none *************************************************************************/ static void releaseBloodPumpStop( void ) { CLR_BP_STOP(); } /*********************************************************************//** * @brief * The setBloodPumpDirection function sets the set blood pump direction to * the given direction. * @details * Inputs : bloodPumpState * Outputs : bloodPumpState * @param dir blood pump direction to set * @return none *************************************************************************/ static void setBloodPumpDirection( MOTOR_DIR_T dir ) { switch ( dir ) { case MOTOR_DIR_FORWARD: bloodPumpDirectionSet = dir; SET_BP_DIR(); break; case MOTOR_DIR_REVERSE: bloodPumpDirectionSet = dir; CLR_BP_DIR(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_BLOOD_FLOW_INVALID_BLOOD_PUMP_DIRECTION, dir ) break; } } /*********************************************************************//** * @brief * The getPublishBloodFlowDataInterval function gets the blood flow data * publication interval. * @details * Inputs : bloodFlowDataPublishInterval * Outputs : none * @return the current blood flow data publication interval (in task intervals). *************************************************************************/ U32 getPublishBloodFlowDataInterval( void ) { U32 result = bloodFlowDataPublishInterval.data; if ( OVERRIDE_KEY == bloodFlowDataPublishInterval.override ) { result = bloodFlowDataPublishInterval.ovData; } return result; } /*********************************************************************//** * @brief * The getTargetBloodFlowRate function gets the current target blood flow * rate. * @details * Inputs : targetBloodFlowRate * Outputs : none * @return the current target blood flow rate (in mL/min). *************************************************************************/ S32 getTargetBloodFlowRate( void ) { S32 result = targetBloodFlowRate.data; if ( OVERRIDE_KEY == targetBloodFlowRate.override ) { result = targetBloodFlowRate.ovData; } return result; } /*********************************************************************//** * @brief * The getMeasuredBloodFlowRate function gets the measured blood flow * rate. * @details * Inputs : measuredBloodFlowRate * Outputs : none * @return the current blood flow rate (in mL/min). *************************************************************************/ F32 getMeasuredBloodFlowRate( void ) { F32 result = measuredBloodFlowRate.data; if ( OVERRIDE_KEY == measuredBloodFlowRate.override ) { result = measuredBloodFlowRate.ovData; } return result; } /*********************************************************************//** * @brief * The getMeasuredBloodPumpRotorSpeed function gets the measured blood flow * rate. * @details * Inputs : bloodPumpRotorSpeedRPM * Outputs : none * @return the current blood flow rate (in mL/min). *************************************************************************/ F32 getMeasuredBloodPumpRotorSpeed( void ) { F32 result = bloodPumpRotorSpeedRPM.data; if ( OVERRIDE_KEY == bloodPumpRotorSpeedRPM.override ) { result = bloodPumpRotorSpeedRPM.ovData; } return result; } /*********************************************************************//** * @brief * The getMeasuredBloodPumpSpeed function gets the measured blood flow * rate. * @details * Inputs : bloodPumpSpeedRPM * Outputs : none * @return the current blood flow rate (in mL/min). *************************************************************************/ F32 getMeasuredBloodPumpSpeed( void ) { F32 result = bloodPumpSpeedRPM.data; if ( OVERRIDE_KEY == bloodPumpSpeedRPM.override ) { result = bloodPumpSpeedRPM.ovData; } return result; } /*********************************************************************//** * @brief * The getMeasuredBloodPumpMCSpeed function gets the measured blood pump * speed. * @details * Inputs : adcBloodPumpMCSpeedRPM * Outputs : none * @return the current blood pump speed (in RPM). *************************************************************************/ F32 getMeasuredBloodPumpMCSpeed( void ) { F32 result = adcBloodPumpMCSpeedRPM.data; if ( OVERRIDE_KEY == adcBloodPumpMCSpeedRPM.override ) { result = adcBloodPumpMCSpeedRPM.ovData; } return result; } /*********************************************************************//** * @brief * The getMeasuredBloodPumpMCCurrent function gets the measured blood pump * current. * @details * Inputs : adcBloodPumpMCCurrentmA * Outputs : none * @return the current blood pump current (in mA). *************************************************************************/ F32 getMeasuredBloodPumpMCCurrent( void ) { F32 result = adcBloodPumpMCCurrentmA.data; if ( OVERRIDE_KEY == adcBloodPumpMCCurrentmA.override ) { result = adcBloodPumpMCCurrentmA.ovData; } return result; } /*********************************************************************//** * @brief * The publishBloodFlowData function publishes blood flow data at the set * interval. * @details * Inputs : target flow rate, measured flow rate, measured MC speed, * measured MC current * Outputs : Blood flow data is published to CAN bus. * @return none *************************************************************************/ static void publishBloodFlowData( void ) { // publish blood flow data on interval #ifndef FLOW_DEBUG if ( ++bloodFlowDataPublicationTimerCounter >= getPublishBloodFlowDataInterval() ) #endif { S32 flowStPt = (S32)getTargetBloodFlowRate(); F32 measFlow = getMeasuredBloodFlowRate(); F32 measRotSpd = getMeasuredBloodPumpRotorSpeed(); F32 measSpd = getMeasuredBloodPumpSpeed(); F32 measMCSpd = getMeasuredBloodPumpMCSpeed(); F32 measMCCurr = getMeasuredBloodPumpMCCurrent(); F32 pumpPWMPctDutyCycle = bloodPumpPWMDutyCyclePctSet * FRACTION_TO_PERCENT_FACTOR; #ifdef FLOW_DEBUG // TODO - temporary debug code - remove later char debugFlowStr[ 256 ]; F32 measFlowSig = getFPGABloodFlowSignalStrength() * 100.0; F32 dialFlow = getMeasuredDialInFlowRate(); F32 dialFlowSig = getFPGADialysateFlowSignalStrength() * 100.0; F32 bldOccl = getMeasuredBloodPumpOcclusion(); F32 dpiOccl = getMeasuredDialInPumpOcclusion(); F32 dpoOccl = getMeasuredDialOutPumpOcclusion(); sprintf( debugFlowStr, "Blood: %5.1f, %6.1f, %6.1f Dial-I: %5.1f, %6.1f, %6.1f Dial-O: %6.1f\n", measFlowSig, measFlow, bldOccl, dialFlowSig, dialFlow, dpiOccl, dpoOccl ); sendDebugData( (U08*)debugFlowStr, strlen(debugFlowStr) ); #else broadcastBloodFlowData( flowStPt, measFlow, measRotSpd, measSpd, measMCSpd, measMCCurr, pumpPWMPctDutyCycle ); #endif bloodFlowDataPublicationTimerCounter = 0; } } /*********************************************************************//** * @brief * The resetBloodFlowMovingAverage function re-initializes the blood flow * moving average sample buffer. * @details * Inputs : none * Outputs : flowReadingsTotal, flowReadingsIdx, flowReadingsCount all set to zero. * @return none *************************************************************************/ static void resetBloodFlowMovingAverage( void ) { flowReadingsIdx = 0; flowReadingsCount = 0; flowReadingsTotal = 0.0; bpControlTimerCounter = 0; } /*********************************************************************//** * @brief * The filterBloodFlowReadings function adds a new flow sample to the filter. * @details * Inputs : none * Outputs : flowReadings[], flowReadingsIdx, flowReadingsCount, flowReadingsTotal * @param flow newest blood flow sample * @return none *************************************************************************/ static void filterBloodFlowReadings( F32 flow ) { #ifndef RAW_FLOW_SENSOR_DATA if ( flowReadingsCount >= SIZE_OF_ROLLING_AVG ) { flowReadingsTotal -= flowReadings[ flowReadingsIdx ]; } flowReadings[ flowReadingsIdx ] = flow; flowReadingsTotal += flow; flowReadingsIdx = INC_WRAP( flowReadingsIdx, 0, SIZE_OF_ROLLING_AVG - 1 ); flowReadingsCount = INC_CAP( flowReadingsCount, SIZE_OF_ROLLING_AVG ); measuredBloodFlowRate.data = flowReadingsTotal / (F32)flowReadingsCount; #else measuredBloodFlowRate.data = flow; #endif } /*********************************************************************//** * @brief * The updateBloodPumpSpeedAndDirectionFromHallSensors function calculates * the blood pump motor speed and direction from hall sensor counter on * a 1 second interval. * @details * Inputs : bpLastMotorHallSensorCount, bpMotorSpeedCalcTimerCtr, current count from FPGA * Outputs : bpMotorDirectionFromHallSensors, bloodPumpSpeedRPM * @return none *************************************************************************/ static void updateBloodPumpSpeedAndDirectionFromHallSensors( void ) { if ( ++bpMotorSpeedCalcTimerCtr >= BP_SPEED_CALC_INTERVAL ) { U16 bpMotorHallSensorCount = getFPGABloodPumpHallSensorCount(); U16 incDelta = ( bpMotorHallSensorCount >= bpLastMotorHallSensorCount ? bpMotorHallSensorCount - bpLastMotorHallSensorCount : ( HEX_64_K - bpLastMotorHallSensorCount ) + bpMotorHallSensorCount ); U16 decDelta = HEX_64_K - incDelta; U16 delta; // determine blood pump speed/direction from delta hall sensor count since last interval if ( incDelta < decDelta ) { bpMotorDirectionFromHallSensors = MOTOR_DIR_FORWARD; delta = incDelta; bloodPumpSpeedRPM.data = ( (F32)delta / (F32)BP_HALL_EDGE_COUNTS_PER_REV ) * (F32)SEC_PER_MIN; } else { bpMotorDirectionFromHallSensors = MOTOR_DIR_REVERSE; delta = decDelta; bloodPumpSpeedRPM.data = ( (F32)delta / (F32)BP_HALL_EDGE_COUNTS_PER_REV ) * (F32)SEC_PER_MIN * -1.0; } // update last count for next time bpLastMotorHallSensorCount = bpMotorHallSensorCount; bpMotorSpeedCalcTimerCtr = 0; } } /*********************************************************************//** * @brief * The checkBloodPumpRotor function checks the rotor for the blood * pump. If homing, this function will stop when hall sensor detected. If pump * is off or running very slowly, rotor speed will be set to zero. * @details * Inputs : bpStopAtHomePosition, bpHomeStartTime, bpRotorRevStartTime * Outputs : pump may be stopped if homing, bloodPumpRotorSpeedRPM may be set to zero. * @return none *************************************************************************/ static void checkBloodPumpRotor( void ) { F32 rotorSpeed = getMeasuredBloodPumpRotorSpeed(); // if homing, check timeout if ( ( TRUE == bpStopAtHomePosition ) && ( TRUE == didTimeout( bpHomeStartTime, BP_HOME_TIMEOUT_MS ) ) ) { signalBloodPumpHardStop(); bpStopAtHomePosition = FALSE; // TODO - alarm??? } // ensure rotor speed below maximum if ( rotorSpeed > BP_MAX_ROTOR_SPEED_RPM ) { SET_ALARM_WITH_1_F32_DATA( ALARM_ID_BLOOD_PUMP_ROTOR_SPEED_TOO_HIGH, rotorSpeed ) } // if pump is stopped or running very slowly, set rotor speed to zero if ( TRUE == didTimeout( bpRotorRevStartTime, BP_HOME_TIMEOUT_MS ) ) { bloodPumpRotorSpeedRPM.data = 0.0; } } /*********************************************************************//** * @brief * The checkBloodPumpDirection function checks the set direction vs. * the direction implied by the sign of the measured MC speed. * @details * Inputs : * Outputs : * @return none *************************************************************************/ static void checkBloodPumpDirection( void ) { MOTOR_DIR_T bpMCDir; if ( BLOOD_PUMP_CONTROL_TO_TARGET_STATE == bloodPumpState ) { // check set direction vs. direction from hall sensors or sign of motor controller speed bpMCDir = ( getMeasuredBloodPumpMCSpeed() >= 0.0 ? MOTOR_DIR_FORWARD : MOTOR_DIR_REVERSE ); if ( ( bloodPumpDirectionSet != bpMCDir ) || ( bloodPumpDirectionSet != bpMotorDirectionFromHallSensors ) ) { #ifndef DISABLE_PUMP_DIRECTION_CHECKS SET_ALARM_WITH_2_U32_DATA( ALARM_ID_BLOOD_PUMP_MC_DIRECTION_CHECK, (U32)bloodPumpDirectionSet, (U32)bpMotorDirectionFromHallSensors ) #endif } } } /*********************************************************************//** * @brief * The checkBloodPumpSpeeds function checks several aspects of the blood pump * speed. * 1. while pump is commanded off, measured motor speed should be < limit. * 2. while pump is controlling, measured motor speed should be within allowed range of commanded speed. * 3. measured motor speed should be within allowed range of measured rotor speed. * All 3 checks have a persistence time that must be met before an alarm is triggered. * @details * Inputs : targetBloodFlowRate, bloodPumpSpeedRPM, bloodPumpRotorSpeedRPM * Outputs : alarm(s) may be triggered * @return none *************************************************************************/ static void checkBloodPumpSpeeds( void ) { F32 measMotorSpeed = getMeasuredBloodPumpSpeed(); S32 cmdRate = getTargetBloodFlowRate(); // check for pump running while commanded off if ( 0 == cmdRate ) { if ( measMotorSpeed > BP_MAX_MOTOR_SPEED_WHILE_OFF_RPM ) { if ( ++errorBloodMotorOffPersistTimerCtr >= BP_OFF_ERROR_PERSIST ) { #ifndef DISABLE_PUMP_SPEED_CHECKS SET_ALARM_WITH_1_F32_DATA( ALARM_ID_BLOOD_PUMP_OFF_CHECK, measMotorSpeed ); #endif } } else { errorBloodMotorOffPersistTimerCtr = 0; } } else { errorBloodMotorOffPersistTimerCtr = 0; } if ( BLOOD_PUMP_CONTROL_TO_TARGET_STATE == bloodPumpState ) { F32 cmdMotorSpeed = ( (F32)cmdRate / (F32)ML_PER_LITER ) * BP_REV_PER_LITER * BP_GEAR_RATIO; F32 deltaMotorSpeed = fabs( measMotorSpeed - cmdMotorSpeed ); F32 measRotorSpeed = getMeasuredBloodPumpRotorSpeed(); F32 measMotorSpeedInRotorRPM = measMotorSpeed / BP_GEAR_RATIO; F32 deltaRotorSpeed = fabs( measRotorSpeed - measMotorSpeedInRotorRPM ); // check measured motor speed vs. commanded motor speed while controlling to target if ( deltaMotorSpeed > BP_MAX_MOTOR_SPEED_ERROR_RPM ) { if ( ++errorBloodMotorSpeedPersistTimerCtr >= BP_MOTOR_SPEED_ERROR_PERSIST ) { #ifndef DISABLE_PUMP_SPEED_CHECKS SET_ALARM_WITH_2_F32_DATA( ALARM_ID_BLOOD_PUMP_MOTOR_SPEED_CHECK, (F32)cmdRate, measMotorSpeed ); #endif } } else { errorBloodMotorSpeedPersistTimerCtr = 0; } // check measured rotor speed vs. measured motor speed while controlling to target if ( deltaRotorSpeed > BP_MAX_ROTOR_VS_MOTOR_DIFF_RPM ) { if ( ++errorBloodRotorSpeedPersistTimerCtr >= BP_ROTOR_SPEED_ERROR_PERSIST ) { #ifndef DISABLE_PUMP_SPEED_CHECKS SET_ALARM_WITH_2_F32_DATA( ALARM_ID_BLOOD_PUMP_ROTOR_SPEED_CHECK, measRotorSpeed, measMotorSpeed ); #endif } } else { errorBloodRotorSpeedPersistTimerCtr = 0; } } else { errorBloodMotorSpeedPersistTimerCtr = 0; errorBloodRotorSpeedPersistTimerCtr = 0; } } /*********************************************************************//** * @brief * The checkBloodPumpFlowAgainstSpeed function checks the measured blood flow * against the implied flow of the measured pump speed when in treatment mode * and controlling to target flow. If a sufficient difference persists, a * flow vs. motor speed check error is triggered. * @details * Inputs : measuredBloodFlowRate, bloodPumpSpeedRPM, errorBloodFlowVsMotorSpeedPersistTimerCtr * Outputs : alarm may be triggered * @return none *************************************************************************/ static void checkBloodPumpFlowAgainstSpeed( void ) { // check only performed while in treatment mode and while we're in control to target state if ( ( MODE_TREA == getCurrentOperationMode() ) && ( BLOOD_PUMP_CONTROL_TO_TARGET_STATE == bloodPumpState ) ) { F32 flow = getMeasuredBloodFlowRate(); F32 speed = getMeasuredBloodPumpSpeed(); F32 impliedFlow = ( ( speed / BP_GEAR_RATIO ) / BP_REV_PER_LITER ) * (F32)ML_PER_LITER; F32 delta = fabs( flow - impliedFlow ); if ( delta > BP_MAX_FLOW_VS_SPEED_DIFF_ML_MIN ) { if ( ++errorBloodFlowVsMotorSpeedPersistTimerCtr >= BP_FLOW_VS_SPEED_PERSIST ) { #ifndef DISABLE_PUMP_FLOW_CHECKS SET_ALARM_WITH_2_F32_DATA( ALARM_ID_BLOOD_PUMP_FLOW_VS_MOTOR_SPEED_CHECK, flow, impliedFlow ); #endif } } else { errorBloodFlowVsMotorSpeedPersistTimerCtr = 0; } } else { errorBloodFlowVsMotorSpeedPersistTimerCtr = 0; } } /*********************************************************************//** * @brief * The checkBloodPumpMCCurrent function checks the measured MC current vs. * the set state of the blood pump (stopped or running). * @details * Inputs : * Outputs : * @return none *************************************************************************/ static void checkBloodPumpMCCurrent( void ) { F32 bpCurr; // blood pump should be off if ( BLOOD_PUMP_OFF_STATE == bloodPumpState ) { bpCurr = fabs( getMeasuredBloodPumpMCCurrent() ); if ( bpCurr > BP_MAX_CURR_WHEN_STOPPED_MA ) { bpCurrErrorDurationCtr += TASK_PRIORITY_INTERVAL; if ( bpCurrErrorDurationCtr > BP_MAX_CURR_ERROR_DURATION_MS ) { #ifndef DISABLE_MOTOR_CURRENT_CHECKS SET_ALARM_WITH_1_F32_DATA( ALARM_ID_BLOOD_PUMP_MC_CURRENT_CHECK, getMeasuredBloodPumpMCCurrent() ); #endif } } else { bpCurrErrorDurationCtr = 0; } } // blood pump should be running else { bpCurr = fabs( getMeasuredBloodPumpMCCurrent() ); if ( ( bpCurr < BP_MIN_CURR_WHEN_RUNNING_MA ) || ( bpCurr > BP_MAX_CURR_WHEN_RUNNING_MA ) ) { bpCurrErrorDurationCtr += TASK_PRIORITY_INTERVAL; if ( bpCurrErrorDurationCtr > BP_MAX_CURR_ERROR_DURATION_MS ) { #ifndef DISABLE_MOTOR_CURRENT_CHECKS SET_ALARM_WITH_1_F32_DATA( ALARM_ID_BLOOD_PUMP_MC_CURRENT_CHECK, getMeasuredBloodPumpMCCurrent() ); #endif } } else { bpCurrErrorDurationCtr = 0; } } } /*********************************************************************//** * @brief * The execBloodFlowTest function executes the state machine for the * BloodFlow self test. * @details * Inputs : none * Outputs : none * @return the current state of the BloodFlow self test. *************************************************************************/ SELF_TEST_STATUS_T execBloodFlowTest( void ) { SELF_TEST_STATUS_T result = SELF_TEST_STATUS_FAILED; CALIBRATION_DATA_T cal; switch ( bloodPumpSelfTestState ) { case BLOOD_FLOW_SELF_TEST_STATE_START: // retrieve blood flow sensor calibration data if ( TRUE == getCalibrationData( &cal ) ) { bloodFlowCalGain = cal.bloodFlowGain; bloodFlowCalOffset = cal.bloodFlowOffset_mL_min; bloodPumpSelfTestState = BLOOD_FLOW_TEST_STATE_COMPLETE; // TODO - implement rest of self test(s) result = SELF_TEST_STATUS_PASSED; } break; case BLOOD_FLOW_TEST_STATE_IN_PROGRESS: break; case BLOOD_FLOW_TEST_STATE_COMPLETE: break; default: // TODO - s/w fault break; } return result; } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The setBloodFlowCalibration function sets the blood flow calibration * factors and has them stored in non-volatile memory. * @details * Inputs : none * Outputs : bloodFlowCalGain, bloodFlowCalOffset * @param gain gain calibration factor for blood flow sensor * @param offset offset calibration factor for blood flow sensor * @return TRUE if calibration factors successfully set/stored, FALSE if not *************************************************************************/ BOOL setBloodFlowCalibration( F32 gain, F32 offset ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { CALIBRATION_DATA_T cal; getCalibrationData( &cal ); // keep locally and apply immediately bloodFlowCalGain = gain; bloodFlowCalOffset = offset; // also update calibration record in non-volatile memory cal.bloodFlowGain = gain; cal.bloodFlowOffset_mL_min = offset; if ( TRUE == setCalibrationData( cal ) ) { result = TRUE; } } return result; } /*********************************************************************//** * @brief * The getBloodFlowCalibration function retrieves the current blood flow * calibration factors. * @details * Inputs : bloodFlowCalGain, bloodFlowCalOffset * Outputs : none * @param gain value to populate with gain calibration factor for blood flow sensor * @param offset value to populate with offset calibration factor for blood flow sensor * @return none *************************************************************************/ void getBloodFlowCalibration( F32 *gain, F32 *offset ) { *gain = bloodFlowCalGain; *offset = bloodFlowCalOffset; } /*********************************************************************//** * @brief * The testSetBloodFlowDataPublishIntervalOverride function overrides the * blood flow data publish interval. * @details * Inputs : none * Outputs : bloodFlowDataPublishInterval * @param value override blood flow data publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetBloodFlowDataPublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_PRIORITY_INTERVAL; result = TRUE; bloodFlowDataPublishInterval.ovData = intvl; bloodFlowDataPublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetBloodFlowDataPublishIntervalOverride function resets the override * of the blood flow data publish interval. * @details * Inputs : none * Outputs : bloodFlowDataPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetBloodFlowDataPublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; bloodFlowDataPublishInterval.override = OVERRIDE_RESET; bloodFlowDataPublishInterval.ovData = bloodFlowDataPublishInterval.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetTargetBloodFlowRateOverride function overrides the target * blood flow rate. * @details * Inputs : none * Outputs : targetBloodFlowRate * @param value override target blood flow rate (in mL/min) * @param ctrlMode override pump control mode to this mode (0 = closed loop, 1 = open loop) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetTargetBloodFlowRateOverride( S32 value, U32 ctrlMode ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { MOTOR_DIR_T dir; if ( value < 0 ) { dir = MOTOR_DIR_REVERSE; } else { dir = MOTOR_DIR_FORWARD; } if ( ctrlMode < NUM_OF_PUMP_CONTROL_MODES ) { targetBloodFlowRate.ovInitData = targetBloodFlowRate.data; // backup current target flow rate targetBloodFlowRate.ovData = value; targetBloodFlowRate.override = OVERRIDE_KEY; result = setBloodPumpTargetFlowRate( abs(value), dir, (PUMP_CONTROL_MODE_T)ctrlMode ); } } return result; } /*********************************************************************//** * @brief * The testResetTargetBloodFlowRateOverride function resets the override of the * target blood flow rate. * @details * Inputs : none * Outputs : targetBloodFlowRate * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetTargetBloodFlowRateOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { targetBloodFlowRate.data = targetBloodFlowRate.ovInitData; // restore pre-override target flow rate targetBloodFlowRate.override = OVERRIDE_RESET; targetBloodFlowRate.ovInitData = 0; targetBloodFlowRate.ovData = 0; result = setBloodPumpTargetFlowRate( targetBloodFlowRate.data, bloodPumpDirection, bloodPumpControlMode ); } return result; } /*********************************************************************//** * @brief * The testResetMeasuredBloodFlowRateOverride function overrides the measured * blood flow rate. * @details * Inputs : none * Outputs : measuredBloodFlowRate * @param value override measured blood flow rate (in mL/min) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredBloodFlowRateOverride( F32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; measuredBloodFlowRate.ovData = value; measuredBloodFlowRate.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetOffButtonStateOverride function resets the override of the * measured blood flow rate. * @details * Inputs : none * Outputs : measuredBloodFlowRate * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredBloodFlowRateOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; measuredBloodFlowRate.override = OVERRIDE_RESET; measuredBloodFlowRate.ovData = measuredBloodFlowRate.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetMeasuredBloodPumpRotorSpeedOverride function overrides the measured * blood pump rotor speed. * @details * Inputs : none * Outputs : bloodPumpRotorSpeedRPM * @param value override measured blood pump rotor speed (in RPM) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredBloodPumpRotorSpeedOverride( F32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; bloodPumpRotorSpeedRPM.ovData = value; bloodPumpRotorSpeedRPM.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetMeasuredBloodPumpRotorSpeedOverride function resets the override of the * measured blood pump rotor speed. * @details * Inputs : none * Outputs : bloodPumpRotorSpeedRPM * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredBloodPumpRotorSpeedOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; bloodPumpRotorSpeedRPM.override = OVERRIDE_RESET; bloodPumpRotorSpeedRPM.ovData = bloodPumpRotorSpeedRPM.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetMeasuredBloodPumpSpeedOverride function overrides the measured * blood pump motor speed. * @details * Inputs : none * Outputs : bloodPumpSpeedRPM * @param value override measured blood pump motor speed (in RPM) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredBloodPumpSpeedOverride( F32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; bloodPumpSpeedRPM.ovData = value; bloodPumpSpeedRPM.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetMeasuredBloodPumpSpeedOverride function resets the override of the * measured blood pump motor speed. * @details * Inputs : none * Outputs : bloodPumpSpeedRPM * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredBloodPumpSpeedOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; bloodPumpSpeedRPM.override = OVERRIDE_RESET; bloodPumpSpeedRPM.ovData = bloodPumpSpeedRPM.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetMeasuredBloodPumpMCSpeedOverride function overrides the measured * blood pump motor speed. * @details * Inputs : none * Outputs : adcBloodPumpMCSpeedRPM * @param value override measured blood pump speed (in RPM) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredBloodPumpMCSpeedOverride( F32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; adcBloodPumpMCSpeedRPM.ovData = value; adcBloodPumpMCSpeedRPM.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetMeasuredBloodPumpMCSpeedOverride function resets the override of the * measured blood pump motor speed. * @details * Inputs : none * Outputs : adcBloodPumpMCSpeedRPM * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredBloodPumpMCSpeedOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; adcBloodPumpMCSpeedRPM.override = OVERRIDE_RESET; adcBloodPumpMCSpeedRPM.ovData = adcBloodPumpMCSpeedRPM.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetMeasuredBloodPumpMCCurrentOverride function overrides the measured * blood pump motor current. * @details * Inputs : none * Outputs : adcBloodPumpMCCurrentmA * @param value override measured blood pump current (in mA) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredBloodPumpMCCurrentOverride( F32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; adcBloodPumpMCCurrentmA.ovData = value; adcBloodPumpMCCurrentmA.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetMeasuredBloodPumpMCCurrentOverride function resets the override of the * measured blood pump motor current. * @details * Inputs : none * Outputs : adcBloodPumpMCCurrentmA * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredBloodPumpMCCurrentOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; adcBloodPumpMCCurrentmA.override = OVERRIDE_RESET; adcBloodPumpMCCurrentmA.ovData = adcBloodPumpMCCurrentmA.ovInitData; } return result; } /**@}*/