/************************************************************************** * * 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 ROPump.c * * @author (last) Quang Nguyen * @date (last) 14-Sep-2020 * * @author (original) Sean * @date (original) 04-Apr-2020 * ***************************************************************************/ #include #include "etpwm.h" #include "mibspi.h" #include "FPGA.h" #include "OperationModes.h" #include "PIControllers.h" #include "PersistentAlarm.h" #include "Pressures.h" #include "ROPump.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TaskPriority.h" #include "Timers.h" #ifdef EMC_TEST_BUILD #include "Heaters.h" #endif #ifdef ENABLE_DIP_SWITCHES #include "Valves.h" #endif /** * @addtogroup ROPump * @{ */ // ********** private definitions ********** #define RO_PUMP_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Interval (ms/task time) at which the RO Pump data is published on the CAN bus. #define MAX_RO_PUMP_DUTY_CYCLE 0.99 ///< max duty cycle. #define MIN_RO_PUMP_DUTY_CYCLE 0.0 ///< min duty cycle. #define ROP_CONTROL_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< interval (ms/task time) at which the RO pump is controlled. #define ROP_P_COEFFICIENT 0.0020 ///< P term for RO pump pressure control. #define ROP_I_COEFFICIENT 0.0015 ///< I term for RO pump pressure control. #define ROP_RAMP_UP_P_COEFFICIENT 0.0 ///< P term for RO pump flow control. #define ROP_RAMP_UP_I_COEFFICIENT 0.1 ///< I term for RO pump flow control. #define ROP_FLOW_TARGET_TOLERANCE 0.05 ///< Tolerance in between the target flow rate and the actual flow rate in liter. #define FLOW_SENSOR_ZERO_READING 0xFFFF ///< Flow sensor reading indicates zero flow (or flow lower than can be detected by sensor). #define FLOW_SAMPLES_TO_AVERAGE ( 250 / TASK_PRIORITY_INTERVAL ) ///< Averaging flow data over 250 ms intervals. #define FLOW_AVERAGE_MULTIPLIER ( 1.0 / (F32)FLOW_SAMPLES_TO_AVERAGE ) ///< Optimization - multiplying is faster than dividing. #define FLOW_VERIFICATION_COUNTER_TARGET 40U ///< The time in counts to check the flow and make sure it is in range. #define RO_FLOW_ADC_TO_LPM_FACTOR 10909.0909 ///< conversion factor from ADC counts to LPM (liters/min) for RO flow rate (multiply this by inverse of FPGA reading). #define ROP_FLOW_TO_PWM_DC(flow) ( (F32)( flow / MAX_RO_FLOWRATE_LPM ) ) ///< Initial conversion factor from target flow rate to PWM duty cycle estimate. #define FLOW_SENSOR_ZERO_READING 0xFFFF ///< Flow sensor reading indicates zero flow (or flow lower than can be detected by sensor). #define FLOW_SAMPLES_TO_AVERAGE ( 250 / TASK_PRIORITY_INTERVAL ) ///< Averaging flow data over 250 ms intervals. #define FLOW_AVERAGE_MULTIPLIER ( 1.0 / (F32)FLOW_SAMPLES_TO_AVERAGE ) ///< Optimization - multiplying is faster than dividing. #define MAX_ALLOWED_FLOW_DEVIATION 0.1 ///< Max allowed deviation from target flow. #define FLOW_OUT_OF_RANGE_TIME_OUT ( 5000 / TASK_PRIORITY_INTERVAL ) ///< Flow out of range time out in counts. #define MAX_PRESSURE_TARGET_TOLERANCE 5 ///< Pressure tolerance from maximum set pressure by user in psi. #define MAX_ALLOWED_PRESSURE_PSI 130 ///< Maximum allowed pressure that the RO pump can go to. #define MIN_ALLOWED_PRESSURE_PSI 10 ///< Minimum allowed pressure that the RO pump can go to. #define MAX_ALLOWED_MEASURED_PRESSURE_PSI 135 ///< Maximum allowed pressure that the sensor measures. RO pump shut off pressure is 140psi. #define MAX_PRESSURE_OUT_OF_RANGE_TIME_OUT ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Maximum allowed time that the pressure can be very high. #define MAX_ALLOWED_RAMP_UP_TIME ( 30 * MS_PER_SECOND ) ///< Maximum allowed ramp up time to a flow rate in ms. #define RAMP_UP_TIME_OUT_COUNT ( MAX_ALLOWED_RAMP_UP_TIME / \ TASK_GENERAL_INTERVAL ) ///< Ramp up time out in counts. // TODO - this is a place holder for real conversion #define ROP_PSI_TO_PWM_DC(p) ( 0.2 + ( (F32)((p) - 100) * 0.01 ) ) ///< conversion factor from target PSI to PWM duty cycle estimate. TODO remove? /// Enumeration of RO pump states. typedef enum ROPump_States { RO_PUMP_OFF_STATE = 0, ///< RO pump off state RO_PUMP_RAMP_UP_STATE, ///< RO pump ramp up to target flow rate state RO_PUMP_VERIFY_FLOW_STATE, ///< RO pump maintain the flow rate for a set period of time RO_PUMP_CONTROL_TO_TARGET_STATE, ///< RO pump control to target pressure state RO_PUMP_OPEN_LOOP_STATE, ///< RO pump open loop state NUM_OF_RO_PUMP_STATES ///< Number of RO pump states } RO_PUMP_STATE_T; /// Enumeration of RO pump self-test states. typedef enum ROPump_Self_Test_States { RO_PUMP_SELF_TEST_STATE_START = 0, ///< RO pump self-test start state RO_PUMP_TEST_STATE_IN_PROGRESS, ///< RO pump self-tests in progress state RO_PUMP_TEST_STATE_COMPLETE, ///< RO pump self-tests completed state NUM_OF_RO_PUMP_SELF_TEST_STATES ///< Number of RO pump self-test states } RO_PUMP_SELF_TEST_STATE_T; // TODO - test code - remove later // pin assignments for pump test DIP switch 0 #define RO_PUMP_TEST_SPI5_PORT_MASK 0x00000100 // (ENA - re-purposed as input GPIO) // dialIn pump stop and direction macros #define GET_DIP_SW0_TEST() ( ( mibspiREG5->PC2 & RO_PUMP_TEST_SPI5_PORT_MASK ) != 0 ) // ********** private data ********** static RO_PUMP_STATE_T roPumpState = RO_PUMP_OFF_STATE; ///< current state of RO pump controller state machine static U32 roPumpDataPublicationTimerCounter = 0; ///< used to schedule RO pump data publication to CAN bus static BOOL isROPumpOn = FALSE; ///< RO pump is currently running static F32 roPumpPWMDutyCyclePct = 0.0; ///< initial RO pump PWM duty cycle static F32 roPumpDutyCyclePctSet = 0.0; ///< currently set RO pump PWM duty cycle static PUMP_CONTROL_MODE_T roPumpControlMode = PUMP_CONTROL_MODE_CLOSED_LOOP; ///< requested RO pump control mode static PUMP_CONTROL_MODE_T roPumpControlModeSet = PUMP_CONTROL_MODE_CLOSED_LOOP; ///< currently set RO pump control mode static F32 targetROPumpFlowRate = 0.0; ///< Target RO flow rate (in L/min) static U32 targetROPumpPressure = 0; ///< Target RO pressure (in PSI) static OVERRIDE_U32_T roPumpDataPublishInterval = { RO_PUMP_DATA_PUB_INTERVAL, RO_PUMP_DATA_PUB_INTERVAL, 0, 0 }; ///< Interval (in ms) at which to publish RO flow data to CAN bus static OVERRIDE_F32_T measuredROFlowRateLPM = { 0.0, 0.0, 0.0, 0 }; ///< measured RO flow rate (in L/min) static U32 flowVerificationCounter = 0; ///< Counter to verify the flow is in range static U32 roControlTimerCounter = 0; ///< determines when to perform control on RO pump static F32 roPumpOpenLoopTargetDutyCycle = 0; ///< Target RO pump open loop PWM static F32 roPumpFlowRateRunningSum = 0; ///< RO pump flow rate running sum static F32 roPumpPressureRunningSum = 0; ///< RO pump pressure running sum /* These variables are used for POST. POST has not been implemented yet static RO_PUMP_SELF_TEST_STATE_T roPumpSelfTestState = RO_PUMP_SELF_TEST_STATE_START; ///< Current ro pump self test state static U32 roPumpSelfTestTimerCount = 0; ///< Timer counter for ro pump self test */ static S32 measuredFlowReadingsSum = 0; ///< Raw flow reading sums for averaging static U32 flowFilterCounter = 0; ///< Flow filtering counter static U32 rampUp2FlowTimeoutCounter = 0; ///< Counter for ramping up to flow time // ********** private function prototypes ********** static RO_PUMP_STATE_T handleROPumpOffState( void ); static RO_PUMP_STATE_T handleROPumpRampUpState( void ); static RO_PUMP_STATE_T handleROPumpVerifyFlowState( void ); static RO_PUMP_STATE_T handleROPumpControlToTargetState( void ); static RO_PUMP_STATE_T handleROPumpOpenLoopState( void ); static void setROPumpTargetDutyCycle( F32 duty ); static void setROPumpControlSignalDutyCycle( F32 dutyCycle ); static void stopROPump( void ); static void publishROPumpData( void ); static U32 getPublishROPumpDataInterval( void ); /*********************************************************************//** * @brief * The initROPump function initializes the RO Pump module. * @details Inputs: rampUp2FlowTimeoutCounter * @details Outputs: rampUp2FlowTimeoutCounter * @return none *************************************************************************/ void initROPump( void ) { stopROPump(); // Initialize RO pump PI controller initializePIController( PI_CONTROLLER_ID_RO_PUMP, MIN_RO_PUMP_DUTY_CYCLE, ROP_P_COEFFICIENT, ROP_I_COEFFICIENT, MIN_RO_PUMP_DUTY_CYCLE, MAX_RO_PUMP_DUTY_CYCLE ); // Initialize the I controller during ramp up initializePIController( I_CONTROLLER_ID_RO_PUMP_RAMP_UP, MIN_RO_PUMP_DUTY_CYCLE, ROP_RAMP_UP_P_COEFFICIENT, ROP_RAMP_UP_I_COEFFICIENT, MIN_RO_PUMP_DUTY_CYCLE, MAX_RO_PUMP_DUTY_CYCLE ); // Initialize the persistent alarm for flow out of upper and lower range initPersistentAlarm( PERSISTENT_ALARM_RO_FLOW_RATE_OUT_OF_UPPER_RANGE, ALARM_ID_FLOW_RATE_OUT_OF_RANGE, TRUE, FLOW_OUT_OF_RANGE_TIME_OUT, FLOW_OUT_OF_RANGE_TIME_OUT ); initPersistentAlarm( PERSISTENT_ALARM_RO_FLOW_RATE_OUT_OF_LOWER_RANGE, ALARM_ID_FLOW_RATE_OUT_OF_RANGE, TRUE, FLOW_OUT_OF_RANGE_TIME_OUT, FLOW_OUT_OF_RANGE_TIME_OUT ); // Initialize the persistent alarm for max allowed pressure out of range initPersistentAlarm( PERSISTENT_ALARM_RO_PUMP_PRESSURE_OUT_OF_RANGE, ALARM_ID_RO_PUMP_PRESSURE_OUT_OF_RANGE, TRUE, MAX_PRESSURE_OUT_OF_RANGE_TIME_OUT, MAX_PRESSURE_OUT_OF_RANGE_TIME_OUT ); rampUp2FlowTimeoutCounter = 0; } /*********************************************************************//** * @brief * The setROPumpTargetDutyCycle function sets the duty cycle that the * pump should run. * @details Inputs: roPumpOpenLoopTargetDutyCycle, roPumpPWMDutyCyclePct, * roPumpPWMDutyCyclePctSet, roPumpControlMode * @details Outputs: roPumpOpenLoopTargetDutyCycle, roPumpPWMDutyCyclePct, * roPumpPWMDutyCyclePctSet, roPumpControlMode * @param: duty which is the duty cycle * @return none *************************************************************************/ static void setROPumpTargetDutyCycle( F32 duty ) { roPumpOpenLoopTargetDutyCycle = duty; roPumpPWMDutyCyclePct = roPumpOpenLoopTargetDutyCycle; roPumpDutyCyclePctSet = roPumpPWMDutyCyclePct; roPumpControlMode = PUMP_CONTROL_MODE_OPEN_LOOP; } /*********************************************************************//** * @brief * The setROPumpTargetFlowRate function sets a new target flow rate for the * RO pump. * @details Inputs: targetROPumpPressure, targetROPumpFlowRate, * roPumpControlMode, rampUp2FlowTimeoutCounter * @details Outputs: targetROPumpPressure, targetROPumpFlowRate, * roPumpControlMode, rampUp2FlowTimeoutCounter * @param roFlowRate which is target RO flow rate * @param maxPressure which is the maximum allowed pressure that the RO pump * can reach * @return TRUE if new target flow rate is set successfully, FALSE if not *************************************************************************/ BOOL setROPumpTargetFlowRate( F32 roFlowRate, U32 maxPressure ) { BOOL result = FALSE; // First of all, the flow rate must be in range if ( ( roFlowRate < MAX_RO_FLOWRATE_LPM ) && ( roFlowRate >= MIN_RO_FLOWRATE_LPM ) ) { // Then the max pressure that we are allowed to reach must be in range if ( ( maxPressure >= MIN_ALLOWED_PRESSURE_PSI ) && ( maxPressure <= MAX_ALLOWED_PRESSURE_PSI ) ) { // For now maximum allowed pressure is inserted into the target pressure override // if the target flow rate exceeded the max pressure, it will set the maximum pressure targetROPumpPressure = maxPressure; targetROPumpFlowRate = roFlowRate; roPumpControlMode = PUMP_CONTROL_MODE_CLOSED_LOOP; // Get the initial guess of the duty cycle roPumpPWMDutyCyclePct = ROP_FLOW_TO_PWM_DC( roFlowRate ); rampUp2FlowTimeoutCounter = 0; result = TRUE; } // Requested max pressure is out of range else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_RO_PUMP_INVALID_FLOW_RATE_SET, maxPressure ) } /*#ifdef EMC_TEST_BUILD roPumpPWMDutyCyclePct = 1.0; #else roPumpPWMDutyCyclePct = ROP_FLOW_TO_PWM_DC( roFlowRate ); #endif*/ } // Requested flow rate is out of range else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_RO_PUMP_INVALID_PRESSURE_SELECTED, roFlowRate ) } return result; } /*********************************************************************//** * @brief * The signalROPumpHardStop function stops the RO pump immediately and * resets all the variables associated with the RO pump run. * @details Inputs: targetROPumpFlowRate, roPumpState, roPumpPWMDutyCyclePct, * roPumpOpenLoopTargetDutyCycle, roControlTimerCounter, flowVerificationCounter, * flowVerificationCounter, isROPumpOn, rampUp2FlowTimeoutCounter * @details Outputs: targetROPumpFlowRate, roPumpState, roPumpPWMDutyCyclePct, * roPumpOpenLoopTargetDutyCycle, roControlTimerCounter, * flowVerificationCounter, isROPumpOn, rampUp2FlowTimeoutCounter * @return none *************************************************************************/ void signalROPumpHardStop( void ) { stopROPump(); targetROPumpFlowRate = 0; roPumpState = RO_PUMP_OFF_STATE; roPumpPWMDutyCyclePct = 0.0; roPumpOpenLoopTargetDutyCycle = 0.0; roControlTimerCounter = 0; flowVerificationCounter = 0; isROPumpOn = FALSE; rampUp2FlowTimeoutCounter = 0; resetPIController( PI_CONTROLLER_ID_RO_PUMP, MIN_RO_PUMP_DUTY_CYCLE ); } /*********************************************************************//** * @brief * The execROPumpMonitor function executes the RO pump monitor. * @details Inputs: measuredFlowReadingsSum, flowFilterCounter, * measuredROFlowRateLPM, measuredROFlowRateLPM, roPumpState, * flowOutOfRangeCounter, roPumpControlMode * @details Outputs: measuredFlowReadingsSum, flowFilterCounter, * measuredROFlowRateLPM, measuredROFlowRateLPM * @return none *************************************************************************/ void execROPumpMonitor( void ) { U16 roFlowReading = getFPGAROPumpFlowRate(); S32 roFlow = (S32)roFlowReading; // Update sum for flow average calculation measuredFlowReadingsSum += roFlow; // Read the pressure at the sensor. The pump cannot be more that the maximum allowed pressure // to make sure the hardware (especially the ROF) is not damaged. If it is the case, we need to // stop immediately F32 actualPressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_OUTLET ); BOOL isPressureMax = actualPressure >= MAX_ALLOWED_MEASURED_PRESSURE_PSI; checkPersistentAlarm( PERSISTENT_ALARM_RO_PUMP_PRESSURE_OUT_OF_RANGE, isPressureMax, actualPressure ); // Read flow at the control set if ( ++flowFilterCounter >= FLOW_SAMPLES_TO_AVERAGE ) { F32 avgROFlow = (F32)measuredFlowReadingsSum * FLOW_AVERAGE_MULTIPLIER; // If the flow is less than a certain value, FPGA will return 0xFFFF meaning that // the flow is 0. Otherwise, convert the count to flow rate in mL/min if ( ( roFlowReading == FLOW_SENSOR_ZERO_READING ) || ( roFlowReading == 0 ) ) { measuredROFlowRateLPM.data = 0.0; } else { measuredROFlowRateLPM.data = RO_FLOW_ADC_TO_LPM_FACTOR / avgROFlow; } measuredFlowReadingsSum = 0; flowFilterCounter = 0; } // To monitor the flow, the control mode must be in closed loop and the pump should be control to target state, // meaning, it is controlling to a certain pressure if ( roPumpControlMode == PUMP_CONTROL_MODE_CLOSED_LOOP && roPumpState == RO_PUMP_CONTROL_TO_TARGET_STATE ) { F32 currentFlow = getMeasuredROFlowRate(); F32 targetFlow = getTargetROPumpFlowRate(); F32 error = 1.0 - ( currentFlow / targetFlow ); BOOL isFlowOutOfRange = error > MAX_ALLOWED_FLOW_DEVIATION; // Figure out whether flow is out of range from which side if ( isFlowOutOfRange ) { BOOL isFlowOutOfUpperRange = currentFlow > targetFlow; BOOL isFlowOutOfLowerRange = currentFlow < targetFlow; // Check for flow out of range checkPersistentAlarm( PERSISTENT_ALARM_RO_FLOW_RATE_OUT_OF_UPPER_RANGE, isFlowOutOfUpperRange, currentFlow ); checkPersistentAlarm( PERSISTENT_ALARM_RO_FLOW_RATE_OUT_OF_LOWER_RANGE, isFlowOutOfLowerRange, currentFlow ); } } // Publish RO pump data on interval publishROPumpData(); } /*********************************************************************//** * @brief * The execROPumpController function executes the RO pump controller. * @details Inputs: roPumpState * @details Outputs: roPumpState * @return none *************************************************************************/ void execROPumpController( void ) { switch ( roPumpState ) { case RO_PUMP_OFF_STATE: roPumpState = handleROPumpOffState(); break; case RO_PUMP_RAMP_UP_STATE: roPumpState = handleROPumpRampUpState(); break; case RO_PUMP_VERIFY_FLOW_STATE: roPumpState = handleROPumpVerifyFlowState(); break; case RO_PUMP_CONTROL_TO_TARGET_STATE: roPumpState = handleROPumpControlToTargetState(); break; case RO_PUMP_OPEN_LOOP_STATE: roPumpState = handleROPumpOpenLoopState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_RO_PUMP_INVALID_EXEC_STATE, roPumpState ) roPumpState = RO_PUMP_OFF_STATE; break; } } /*********************************************************************//** * @brief * The isReverseOsmosisPumpOn function returns the on/off status of RO pump. * @details Inputs: isROPumpOn * @details Outputs: none * @return isROPumpOn the boolean flag that is TRUE if the pump is on and * FALSE if it is off *************************************************************************/ BOOL isReverseOsmosisPumpOn( void ) { return isROPumpOn; } /*********************************************************************//** * @brief * The execROPumpTest function executes the state machine for the RO pump * self-test. * @details * Inputs: TODO FILL UP * Outputs: TODO FILL UP * @return the current state of the ROPump self test. *************************************************************************/ SELF_TEST_STATUS_T execROPumpTest( void ) { SELF_TEST_STATUS_T result = SELF_TEST_STATUS_FAILED; // TODO - implement self-test(s) return result; } /*********************************************************************//** * @brief * The handleROPumpOffState function handles the RO pump off state of the * controller state machine. * @details Inputs: roPumpControlMode, roPumpControlModeSet, roPumpControlMode, * roPumpPWMDutyCyclePctSet, roPumpPWMDutyCyclePct, isROPumpOn, * roPumpOpenLoopTargetDutyCycle * @details Outputs: roPumpControlModeSet, roPumpPWMDutyCyclePctSet, * roPumpPWMDutyCyclePct, isROPumpOn * @return next state of the controller state machine *************************************************************************/ static RO_PUMP_STATE_T handleROPumpOffState( void ) { RO_PUMP_STATE_T result = RO_PUMP_OFF_STATE; #ifdef DEBUG_ENABLED #ifdef ENABLE_DIP_SWITCHES // TODO - test code - remove later if ( GET_DIP_SW0_TEST() ) { setValveState( VSP, VALVE_STATE_CLOSED ); setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); setValveState( VRC, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setROPumpTargetPressure( 120 ); #ifdef EMC_TEST_BUILD { F32 fanPWM = 0.25; etpwmSetCmpA( etpwmREG6, (U32)( (S32)( ( fanPWM * (F32)(etpwmREG6->TBPRD) ) + FLOAT_TO_INT_ROUNDUP_OFFSET ) ) ); etpwmSetCmpB( etpwmREG6, (U32)( (S32)( ( fanPWM * (F32)(etpwmREG6->TBPRD) ) + FLOAT_TO_INT_ROUNDUP_OFFSET ) ) ); setPrimaryHeaterTargetTemperature( 50.0 ); startPrimaryHeater(); } #endif } #endif #endif // If there is a flow, transition to the PI controller to get the corresponding pressure of that flow if ( getTargetROPumpFlowRate() > 0 && roPumpControlMode == PUMP_CONTROL_MODE_CLOSED_LOOP ) { roPumpControlModeSet = roPumpControlMode; // Set initial PWM duty cycle roPumpDutyCyclePctSet = roPumpPWMDutyCyclePct; setROPumpControlSignalDutyCycle( roPumpDutyCyclePctSet ); // Reset controller resetPIController( I_CONTROLLER_ID_RO_PUMP_RAMP_UP, roPumpDutyCyclePctSet ); // Set pump to on isROPumpOn = TRUE; result = RO_PUMP_RAMP_UP_STATE; } // If the target duty cycle is greater than zero (minimum is 10%) and the mode has been set to open // loop, set the duty cycle if ( roPumpOpenLoopTargetDutyCycle > 0 && roPumpControlMode == PUMP_CONTROL_MODE_OPEN_LOOP ) { setROPumpControlSignalDutyCycle( roPumpOpenLoopTargetDutyCycle ); isROPumpOn = TRUE; result = RO_PUMP_OPEN_LOOP_STATE; } return result; } /*********************************************************************//** * @brief * The handleROPumpRampUpState function handles the RO pump ramp up state * of the controller state machine. * @details Inputs: roControlTimerCounter, roPumpPWMDutyCyclePctSet, * rampUp2FlowTimeoutCounter * @details Outputs: roControlTimerCounter, roPumpPWMDutyCyclePctSet, * rampUp2FlowTimeoutCounter * @return next state of the controller state machine *************************************************************************/ static RO_PUMP_STATE_T handleROPumpRampUpState( void ) { RO_PUMP_STATE_T result = RO_PUMP_RAMP_UP_STATE; // Get the current pressure from the sensor F32 actualPressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_OUTLET ); // If we are still in the ramp up mode after the specified time, get the current pressure // and control from this if ( ++rampUp2FlowTimeoutCounter > RAMP_UP_TIME_OUT_COUNT ) { targetROPumpPressure = actualPressure; rampUp2FlowTimeoutCounter = 0; result = RO_PUMP_CONTROL_TO_TARGET_STATE; } // Control at set interval else if ( ++roControlTimerCounter >= ROP_CONTROL_INTERVAL ) { F32 targetFlowRate = getTargetROPumpFlowRate(); F32 actualFlowRate = (F32)getMeasuredROFlowRate(); // If the actual pressure is greater than the target pressure or it is within the tolerance of the maximum pressure, move to set // to target pressure straight. At the beginning the maximum pressure is set in the targetROPumpPressure override variable. // If the flow rate was reached without reaching to maximum pressure, the pressure that was set to targetROPumpPressure override will // be reset to the corresponding pressure of the target flow rate. if ( actualPressure > getTargetROPumpPressure() || ( getTargetROPumpPressure() - actualPressure ) > MAX_PRESSURE_TARGET_TOLERANCE ) { result = RO_PUMP_CONTROL_TO_TARGET_STATE; } // If the actual flow is still far from target flow, update the duty cycle using the I controller and stay in this state else if ( fabs( actualFlowRate - targetFlowRate ) > ROP_FLOW_TARGET_TOLERANCE ) { roPumpDutyCyclePctSet = runPIController( I_CONTROLLER_ID_RO_PUMP_RAMP_UP, targetFlowRate, actualFlowRate ); setROPumpControlSignalDutyCycle( roPumpDutyCyclePctSet ); } // Reached to the target flow go to the next state else { result = RO_PUMP_VERIFY_FLOW_STATE; } roControlTimerCounter = 0; } return result; } /*********************************************************************//** * @brief * The handleROPumpVerifyFlowState function handles the RO pump verify * flow state of the RO pump controller state machine. * @details Inputs: flowVerificationCounter, targetROPumpPressure, * roPumpFlowRateRunningSum, roPumpFlowRateRunningSum * @details Outputs: flowVerificationCounter, targetROPumpPressure, * roPumpFlowRateRunningSum, roPumpFlowRateRunningSum * @return next state of the controller state machine *************************************************************************/ static RO_PUMP_STATE_T handleROPumpVerifyFlowState( void ) { RO_PUMP_STATE_T result = RO_PUMP_VERIFY_FLOW_STATE; // Calculate the running sum of the flow rate and RO pump outlet pressure roPumpFlowRateRunningSum = roPumpFlowRateRunningSum + (F32)getMeasuredROFlowRate(); roPumpPressureRunningSum = roPumpPressureRunningSum + getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_OUTLET ); // Check if the time for flow verification has elapsed if ( ++flowVerificationCounter >= FLOW_VERIFICATION_COUNTER_TARGET ) { F32 actualPressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_OUTLET ); // If the flow has been achieved without reaching to the maximum pressure, set the new pressure // otherwise, stay with the maximum allowed pressure as target pressure if ( actualPressure < getTargetROPumpPressure() ) { // Calculate the average pressure and flow rate F32 avgPressure = roPumpPressureRunningSum / flowVerificationCounter; F32 avgFlowRate = roPumpFlowRateRunningSum / flowVerificationCounter; F32 targetFlowRate = getTargetROPumpFlowRate(); // Calculate the flow rate deviation from the target flow rate F32 flowRateDeviation = ( targetFlowRate - avgFlowRate ) / targetFlowRate; // Use the flow rate deviation to adjust the average calculated pressure. This // pressure is used as the target pressure avgPressure = avgPressure + ( avgPressure * flowRateDeviation ); // Save the target pressure targetROPumpPressure = avgPressure; } // Reset the I controller for the flow rate as it is no longer needed resetPIController( I_CONTROLLER_ID_RO_PUMP_RAMP_UP, MIN_RO_PUMP_DUTY_CYCLE ); // Set initial PWM duty cycle setROPumpControlSignalDutyCycle( roPumpDutyCyclePctSet ); // Reset controller resetPIController( PI_CONTROLLER_ID_RO_PUMP, roPumpDutyCyclePctSet ); // Reset all the variables before leaving flowVerificationCounter = 0; roPumpFlowRateRunningSum = 0; roPumpPressureRunningSum = 0; result = RO_PUMP_CONTROL_TO_TARGET_STATE; } return result; } /*********************************************************************//** * @brief * The handleROPumpControlToTargetState function handles the control to * target state of the RO pump controller state machine. * @details Inputs: roPumpPWMDutyCyclePctSet, roControlTimerCounter, * roPumpControlModeSet * @details Outputs: roPumpPWMDutyCyclePctSet, roControlTimerCounter * @return next state of the controller state machine *************************************************************************/ static RO_PUMP_STATE_T handleROPumpControlToTargetState( void ) { RO_PUMP_STATE_T result = RO_PUMP_CONTROL_TO_TARGET_STATE; // Control at set interval if ( ++roControlTimerCounter >= ROP_CONTROL_INTERVAL && roPumpControlModeSet == PUMP_CONTROL_MODE_CLOSED_LOOP ) { // Get the pressure to use it for setting the control F32 actualPressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_OUTLET ); roPumpDutyCyclePctSet = runPIController( PI_CONTROLLER_ID_RO_PUMP, getTargetROPumpPressure(), actualPressure ); setROPumpControlSignalDutyCycle( roPumpDutyCyclePctSet ); roControlTimerCounter = 0; } #ifdef DEBUG_ENABLED #ifdef ENABLE_DIP_SWITCHES // TODO - test code - remove later if ( !GET_DIP_SW0_TEST() ) { signalROPumpHardStop(); result = RO_PUMP_OFF_STATE; #ifdef EMC_TEST_BUILD etpwmSetCmpA( etpwmREG6, 0 ); etpwmSetCmpB( etpwmREG6, 0 ); stopPrimaryHeater(); #endif } #endif #endif return result; } /*********************************************************************//** * @brief * The handleROPumpOpenLoopState function handles the open loop state of * the RO pump controller state machine. * @details Inputs: none * @details Outputs: none * @return next state of the controller state machine *************************************************************************/ static RO_PUMP_STATE_T handleROPumpOpenLoopState( void ) { return RO_PUMP_OPEN_LOOP_STATE; } /*********************************************************************//** * @brief * The setROPumpControlSignalDutyCycle function sets the duty cycle for the * RO pump to a given duty cycle. * @details Inputs: none * @details Outputs: none * @param dutyCycle which is the duty cycle * @return none *************************************************************************/ static void setROPumpControlSignalDutyCycle( F32 dutyCycle ) { etpwmSetCmpB( etpwmREG2, (U32)( (S32)( ( dutyCycle * (F32)(etpwmREG2->TBPRD) ) + FLOAT_TO_INT_ROUNDUP_OFFSET ) ) ); } /*********************************************************************//** * @brief * The stopROPump function sets the RO pump duty cycle to zero. * @details Inputs: isROPumpOn, roPumpPWMDutyCyclePctSet * @details Outputs: isROPumpOn, roPumpPWMDutyCyclePctSet * @return none *************************************************************************/ static void stopROPump( void ) { isROPumpOn = FALSE; roPumpDutyCyclePctSet = 0.0; etpwmSetCmpB( etpwmREG2, 0 ); } /*********************************************************************//** * @brief * The getPublishROPumpDataInterval function gets the RO pump data publish * interval. * @details Inputs: roPumpDataPublishInterval * @details Outputs: roPumpDataPublishInterval * @return the current RO pump data publication interval (in ms). *************************************************************************/ static U32 getPublishROPumpDataInterval( void ) { U32 result = roPumpDataPublishInterval.data; if ( OVERRIDE_KEY == roPumpDataPublishInterval.override ) { result = roPumpDataPublishInterval.ovData; } return result; } /*********************************************************************//** * @brief * The getTargetROPumpFlowRate function gets the current target RO pump * flow rate. * @details Inputs: targetROPumpFlowRate * @details Outputs: targetROPumpFlowRate * @return the current target RO flow rate (in L/min). *************************************************************************/ F32 getTargetROPumpFlowRate( void ) { return targetROPumpFlowRate; } /*********************************************************************//** * @brief * The getMeasuredROFlowRate function gets the measured RO pump flow rate. * @details Inputs: measuredROFlowRateLPM * @details Outputs: measuredROFlowRateLPM * @return the current RO pump flow rate (in L/min). *************************************************************************/ F32 getMeasuredROFlowRate( void ) { F32 result = measuredROFlowRateLPM.data; if ( OVERRIDE_KEY == measuredROFlowRateLPM.override ) { result = measuredROFlowRateLPM.ovData; } return result; } /*********************************************************************//** * @brief * The getTargetROPumpPressure function gets the current target RO pump * pressure. * @details Inputs: targetROPumpPressure * @details Outputs: targetROPumpPressure * @return the current target RO targetROPumpPressure in psi. *************************************************************************/ F32 getTargetROPumpPressure( void ) { return targetROPumpPressure; } /*********************************************************************//** * @brief * The publishROPumpData function publishes RO pump data at the set interval. * @details Inputs: roPumpDataPublicationTimerCounter * @details Outputs: roPumpDataPublicationTimerCounter * @return none *************************************************************************/ static void publishROPumpData( void ) { // publish RO pump data on interval if ( ++roPumpDataPublicationTimerCounter >= getPublishROPumpDataInterval() ) { RO_PUMP_DATA_T pumpData; pumpData.roPumpTgtPressure = getTargetROPumpPressure(); pumpData.measROFlowRate = getMeasuredROFlowRate(); pumpData.roPumpDutyCycle = roPumpDutyCyclePctSet * FRACTION_TO_PERCENT_FACTOR; pumpData.roPumpState = (U32)roPumpState; broadcastROPumpData( &pumpData ); roPumpDataPublicationTimerCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetROPumpDataPublishIntervalOverride function overrides the * RO pump data publish interval. * @details Inputs: roPumpDataPublishInterval * @details Outputs: roPumpDataPublishInterval * @param: value : override RO pump data publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetROPumpDataPublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_PRIORITY_INTERVAL; roPumpDataPublishInterval.ovData = intvl; roPumpDataPublishInterval.override = OVERRIDE_KEY; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testResetROPumpDataPublishIntervalOverride function resets the * override of the RO pump data publish interval. * @details Inputs: roPumpDataPublishInterval * @details Outputs: roPumpDataPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetROPumpDataPublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { roPumpDataPublishInterval.override = OVERRIDE_RESET; roPumpDataPublishInterval.ovData = roPumpDataPublishInterval.ovInitData; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testSetTargetROPumpFlowRateOverride function overrides the target * flow rate. * @details Inputs: none * @details Outputs: none * @param value which is override target RO flow rate (in L/min) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetTargetROPumpFlowAndPressure( F32 flow, U32 pressure ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { // The flow rate and pressure must be in range if ( flow < MAX_RO_FLOWRATE_LPM && flow >= MIN_RO_FLOWRATE_LPM ) { if ( pressure >= MIN_ALLOWED_PRESSURE_PSI && pressure <= MAX_ALLOWED_PRESSURE_PSI ) { result = setROPumpTargetFlowRate( flow, pressure ); } } } return result; } /*********************************************************************//** * @brief * The testSetTargetROPumpPressureOverride function overrides the target * RO pressure. * @details Inputs: targetROPumpPressure * @details Outputs: targetROPumpPressure * @param value override target RO pressure (in psi) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetTargetROPumpPressure( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { // Make sure the requested pressure is in range if ( value >= MIN_ALLOWED_PRESSURE_PSI && value <= MAX_ALLOWED_PRESSURE_PSI ) { targetROPumpPressure = value; result = TRUE; } } return result; } /*********************************************************************//** * @brief * The testSetMeasuredROFlowRateOverride function overrides the measured * RO flow rate. * @details Inputs: measuredROFlowRateLPM * @details Outputs: measuredROFlowRateLPM * @param: value : override measured RO pump motor speed (in L/min) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredROFlowRateOverride( F32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { measuredROFlowRateLPM.ovInitData = measuredROFlowRateLPM.data; measuredROFlowRateLPM.ovData = value; measuredROFlowRateLPM.override = OVERRIDE_KEY; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testResetMeasuredROFlowRateOverride function resets the override * of the measured RO flow rate. * @details Inputs: measuredROFlowRateLPM * @details Outputs: measuredROFlowRateLPM * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredROFlowRateOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { measuredROFlowRateLPM.data = measuredROFlowRateLPM.ovInitData; measuredROFlowRateLPM.override = OVERRIDE_RESET; measuredROFlowRateLPM.ovInitData = 0.0; measuredROFlowRateLPM.ovData = 0.0; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testSetTargetDutyCycleOverride function overrides the target duty * cycle of the RO pump. * @details Inputs: none * @details Outputs: none * @param value which is the duty cycle to be set * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetTargetDutyCycle( F32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { // Check if duty cycle is within range if ( value >= MIN_RO_PUMP_DUTY_CYCLE && value <= MAX_RO_PUMP_DUTY_CYCLE ) { setROPumpTargetDutyCycle( value ); result = TRUE; } } return result; } /**@}*/