Index: firmware/App/Controllers/ConductivitySensors.c =================================================================== diff -u -r5adaa0ae1236d34fca1fc8def7fa107ec470115e -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Controllers/ConductivitySensors.c (.../ConductivitySensors.c) (revision 5adaa0ae1236d34fca1fc8def7fa107ec470115e) +++ firmware/App/Controllers/ConductivitySensors.c (.../ConductivitySensors.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -14,6 +14,7 @@ * @date (original) 13-Jul-2020 * ***************************************************************************/ +#include // Used for calculating the polynomial calibration equation. #include "ConductivitySensors.h" #include "FPGA.h" @@ -106,17 +107,20 @@ static BOOL packageStarted = FALSE; ///< Flag to indicate the start of a package measurement data. static U08 packageIndex = 0U; ///< Current package measurement data bytes index. static U08 package[ 50 ]; ///< Storage of package bytes until ready to process. +static DG_COND_SENSORS_CAL_RECORD_T condSensorsCalRecord; ///< Conductivity sensors' calibration record. // ********** private function prototypes ********** -static F32 calcCompensatedConductivity( F32 conductivity, F32 temperature); +static F32 calcCompensatedConductivity( F32 conductivity, F32 temperature ); static void calcRORejectionRatio( void ); static void processCPiCPoSensorRead( U32 sensorId, U32 fgpaRead, U08 fpgaReadCount, U08 fpgaErrorCount, U08 fpgaSensorFault ); static U32 prefixStrToSIFactor( U08 prefix ); static void processMeasurementDataPackage( U32 sensorId ); static void processCD1CD2SensorRead( U16 fpgaReadCount, U08 fpgaErrorCount ); static U32 getConductivityDataPublishInterval( void ); +static BOOL processCalibrationData( void ); +static F32 getCalibrationAppliedConductivityValue( U32 sensorId, F32 compensatedValue ); /*********************************************************************//** * @brief @@ -155,11 +159,19 @@ * The execConductivitySensors function gets conductivity sensors' latest * readings from FPGA and advertises them over CAN. * @details Inputs: none - * @details Outputs: Conductivity sensors' latest reading is updated and advertised. + * @details Outputs: Conductivity sensors' latest reading is updated and + * advertised. * @return none *************************************************************************/ void execConductivitySensors( void ) { + // Check if a new calibration is available + if ( TRUE == isNewCalibrationRecordAvailable() ) + { + // Get the new calibration data and check its validity + processCalibrationData(); + } + processCPiCPoSensorRead( CONDUCTIVITYSENSORS_CPI_SENSOR, getFPGACPi(), getFPGACPiReadCount(), getFPGACPiErrorCount(), getFPGACPiFault() ); processCPiCPoSensorRead( CONDUCTIVITYSENSORS_CPO_SENSOR, getFPGACPo(), getFPGACPoReadCount(), getFPGACPoErrorCount(), getFPGACPoFault() ); processCD1CD2SensorRead( getFPGAEmstatRxFifoCount(), getFPGAEmstatRxErrCount() ); @@ -179,7 +191,32 @@ broadcastConductivityData( &data ); } +} +/*********************************************************************//** + * @brief + * The execConductivitySensorsSelfTest function executes the conductivity + * sensors' self-test. + * @details Inputs: none + * @details Outputs: none + * @return PressuresSelfTestResult (SELF_TEST_STATUS_T) + *************************************************************************/ +SELF_TEST_STATUS_T execConductivitySensorsSelfTest( void ) +{ + SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; + + BOOL calStatus = processCalibrationData(); + + if ( TRUE == calStatus ) + { + result = SELF_TEST_STATUS_PASSED; + } + else + { + result = SELF_TEST_STATUS_FAILED; + } + + return result; } /*********************************************************************//** @@ -264,7 +301,8 @@ BOOL isDialysateConductivityInRange( void ) { F32 const bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); - BOOL const isConductivityInRange = isAcidConductivityInRange() && ( MIN_DIALYSATE_CONDUCTIVITY <= bicarbConductivity ) && ( bicarbConductivity <= MAX_DIALYSATE_CONDUCTIVITY ); + BOOL const isConductivityInRange = isAcidConductivityInRange() && ( MIN_DIALYSATE_CONDUCTIVITY <= bicarbConductivity ) && + ( bicarbConductivity <= MAX_DIALYSATE_CONDUCTIVITY ); return isConductivityInRange; } @@ -357,15 +395,17 @@ *************************************************************************/ static void processCPiCPoSensorRead( U32 sensorId, U32 fgpaRead, U08 fpgaReadCount, U08 fpgaErrorCount, U08 fpgaSensorFault ) { - if ( ( fpgaErrorCount == 0 ) && ( fpgaSensorFault == 0 ) ) + if ( ( 0 == fpgaErrorCount ) && ( 0 == fpgaSensorFault ) ) { if ( ( readCount[ sensorId ] != fpgaReadCount ) ) { F32 const temperature = getTemperatureValue( associateTempSensor[ sensorId ] ); F32 const conductivity = ( (F32)( fgpaRead ) / COND_SENSOR_DECIMAL_CONVERSION ); + F32 const compensatedCond = calcCompensatedConductivity( conductivity, temperature ); + readCount[ sensorId ] = fpgaReadCount; internalErrorCount[ sensorId ] = 0; - compensatedConductivityValues[ sensorId ].data = calcCompensatedConductivity( conductivity, temperature ); + compensatedConductivityValues[ sensorId ].data = getCalibrationAppliedConductivityValue( sensorId, compensatedCond ); } else { @@ -439,9 +479,10 @@ F32 const resistance = ( ( F32 )( hexStrToDec( measurementPtr->value, sizeof( measurementPtr->value ) ) - EMSTAT_PICO_MEASUREMENT_OFFSET ) / prefix ); F32 const temperature = getTemperatureValue( associateTempSensor[ sensorId ] ); F32 const conductivity = ( 1 / resistance * SIEMENS_TO_MICROSIEMENS_CONVERSION ); + F32 const compensatedCond = calcCompensatedConductivity( conductivity, temperature ); internalErrorCount[ sensorId ] = 0; - compensatedConductivityValues[ sensorId ].data = calcCompensatedConductivity( conductivity, temperature ); + compensatedConductivityValues[ sensorId ].data = getCalibrationAppliedConductivityValue( sensorId, compensatedCond ); } else { @@ -537,7 +578,69 @@ return result; } +/*********************************************************************//** + * @brief + * The processCalibrationData function gets the calibration data and makes + * sure it is valid by checking the calibration date. The calibration date + * should not be 0. + * @details Inputs: none + * @details Outputs: condSensorsCalRecord + * @return TRUE if the calibration record is valid, otherwise FALSE + *************************************************************************/ +static BOOL processCalibrationData( void ) +{ + BOOL status = TRUE; + U32 sensor; + // Get the calibration record from NVDataMgmt + DG_COND_SENSORS_CAL_RECORD_T calData = getDGConducitivitySensorsCalibrationRecord(); + + for ( sensor = 0; sensor < NUM_OF_CAL_DATA_COND_SENSORS; sensor++ ) + { +#ifndef SKIP_CAL_CHECK + // Check if the calibration data that was received from NVDataMgmt is legitimate + // The calibration date item should not be zero. If the calibration date is 0, + // then the data is not stored in the NV memory or it was corrupted. + if ( 0 == calData.condSensors[ sensor ].calibrationTime ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_COND_SENSORS_INVALID_CAL_RECORD, (U32)sensor ); + status = FALSE; + } +#endif + + // The calibration data was valid, update the local copy + condSensorsCalRecord.condSensors[ sensor ].fourthOrderCoeff = calData.condSensors[ sensor ].fourthOrderCoeff; + condSensorsCalRecord.condSensors[ sensor ].thirdOrderCoeff = calData.condSensors[ sensor ].thirdOrderCoeff; + condSensorsCalRecord.condSensors[ sensor ].secondOrderCoeff = calData.condSensors[ sensor ].secondOrderCoeff; + condSensorsCalRecord.condSensors[ sensor ].gain = calData.condSensors[ sensor ].gain; + condSensorsCalRecord.condSensors[ sensor ].offset = calData.condSensors[ sensor ].offset; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The getCalibrationAppliedConductivityValue function gets the temperature + * compensated conductivity value and applies calibration to it. + * @details Inputs: condSensorsCalRecord + * @details Outputs: none + * @param sensorId which is the conductivity sensor ID + * @param compensatedValue which is the temperature compensated conductivity + * value of the conductivity sensor + * @return the calibration applied conductivity value + *************************************************************************/ +static F32 getCalibrationAppliedConductivityValue( U32 sensorId, F32 compensatedValue ) +{ + F32 conductivity = pow(compensatedValue, 4) * condSensorsCalRecord.condSensors[ (CAL_DATA_DG_COND_SENSORS_T)sensorId ].fourthOrderCoeff + + pow(compensatedValue, 3) * condSensorsCalRecord.condSensors[ (CAL_DATA_DG_COND_SENSORS_T)sensorId ].thirdOrderCoeff + + pow(compensatedValue, 2) * condSensorsCalRecord.condSensors[ (CAL_DATA_DG_COND_SENSORS_T)sensorId ].secondOrderCoeff + + compensatedValue * condSensorsCalRecord.condSensors[ (CAL_DATA_DG_COND_SENSORS_T)sensorId ].gain + + condSensorsCalRecord.condSensors[ (CAL_DATA_DG_COND_SENSORS_T)sensorId ].offset; + return conductivity; +} + + /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ Index: firmware/App/Controllers/Fans.c =================================================================== diff -u -ra8bb1da29825b5d666333629fda871652d16229a -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Controllers/Fans.c (.../Fans.c) (revision a8bb1da29825b5d666333629fda871652d16229a) +++ firmware/App/Controllers/Fans.c (.../Fans.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -32,9 +32,11 @@ #define FANS_SELF_TEST_WAIT_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Fans self test wait time for the fans to get to RPM. #define FANS_SELF_TEST_TARGET_PWM 0.5 ///< Fans self test target PWM for testing the fans are running. #define FANS_MAX_ALLOWED_RPM_OUT_OF_RANGE_INTERVAL ( 3 * MS_PER_SECOND ) ///< Fans max allowed RPM out of range time interval. -#define FANS_MAX_ALLOWED_RPM 5000 ///< Fans max allowed RPM value. +#define FANS_MAX_ALLOWED_RPM 5500 ///< Fans max allowed RPM value. #define FANS_MIN_ALLOWED_RPM 150 ///< Fans max allowed RPM value. #define FANS_MONITOR_INTERVAL_COUNT ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Fans monitor time interval in counts. +#define FANS_MIN_RPM_OUT_OF_RANGE_TOL 0.25 ///< Fans min RPM out of range tolerance. +#define FANS_MAX_RPM_OUT_OF_RANGE_TOL 0.5 ///< Fans max RPM out of range tolerance. /// Fans self test states typedef enum fans_Self_Test @@ -61,12 +63,11 @@ } FAN_STATUS_T; static FAN_STATUS_T fansStatus; ///< Fans status. -static SELF_TEST_STATUS_T fansSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; ///< Fans self test result. -static FANS_SELF_TEST_STATES_T fansSelfTestState = FANS_SELF_TEST_START_STATE; ///< Fans self test state. static FANS_EXEC_STATES_T fansExecState = FANS_EXEC_STATE_WAIT_FOR_POST_STATE; ///< Fans exec state. static U32 fansControlCounter = 0; ///< Fans control interval counter. static U32 fansPublishCounter = 0; ///< Fans data publish interval counter. static U32 fansMonitorCounter = 0; ///< Fans monitor interval counter. +static BOOL isPOSTComplete = FALSE; ///< Flag that indicates whether POST is complete or not. /// Temperature to duty cycle conversion slope (duty cycle not in percent) static const F32 SLOPE = ( FANS_MAX_DUTY_CYCLE - FANS_MIN_DUTY_CYCLE ) / ( MAX_ALLOWED_AMBINET_TEMPERATURE - MIN_ALLOWED_AMBIENT_TEMPERATURE ); @@ -76,9 +77,6 @@ static OVERRIDE_U32_T fansPublishInterval = { FANS_DATA_PUBLISH_INTERVAL, FANS_DATA_PUBLISH_INTERVAL, 0, 0 }; ///< Fans publish time interval override -static FANS_SELF_TEST_STATES_T handleSelfTestStart( void ); -static FANS_SELF_TEST_STATES_T handleSelfTestCheckRPM( void ); - static FANS_EXEC_STATES_T handleExecStateWaitForPOST( void ); static FANS_EXEC_STATES_T handleExecStateRun( void ); @@ -93,22 +91,20 @@ /*********************************************************************//** * @brief * The initFans function initializes the fans module. - * @details Inputs: fansExecState, fansSelfTestReslt, fansSelfTestState, - * fansStatus, fansControlCounter, fansPublishCounter - * @details Outputs: fansExecState, fansSelfTestReslt, fansSelfTestState, - * fansStatus, fansControlCounter, fansPublishCounter + * @details Inputs: fansExecState, fansStatus, fansControlCounter, + * fansPublishCounter + * @details Outputs: fansExecState, fansStatus, fansControlCounter, + * fansPublishCounter * @return none *************************************************************************/ void initFans( void ) { // Initialize the variables fansExecState = FANS_EXEC_STATE_WAIT_FOR_POST_STATE; - fansSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; - fansSelfTestState = FANS_SELF_TEST_START_STATE; - fansExecState = FANS_EXEC_STATE_WAIT_FOR_POST_STATE; fansControlCounter = 0; fansPublishCounter = 0; fansMonitorCounter = 0; + isPOSTComplete = FALSE; // Initialize a persistent alarm for fans RPM out of range initPersistentAlarm( ALARM_ID_DG_FAN_RPM_OUT_OF_RANGE, FANS_MAX_ALLOWED_RPM_OUT_OF_RANGE_INTERVAL, FANS_MAX_ALLOWED_RPM_OUT_OF_RANGE_INTERVAL ); @@ -117,34 +113,21 @@ /*********************************************************************//** * @brief * The execFansSelfTest function executes the fans self test. - * @details Inputs: fansSelfTestState, fansSelfTestReslt - * @details Outputs: fansSelfTestState, fansSelfTestReslt + * @details Inputs: none + * @details Outputs: isPOSTComplete * @return Status of self test *************************************************************************/ SELF_TEST_STATUS_T execFansSelfTest( void ) { - switch ( fansSelfTestState ) - { - case FANS_SELF_TEST_START_STATE: - fansSelfTestState = handleSelfTestStart(); - break; + SELF_TEST_STATUS_T status = SELF_TEST_STATUS_IN_PROGRESS; - case FANS_SELF_TEST_CHECK_RPM_STATE: - fansSelfTestState = handleSelfTestCheckRPM(); - break; + // TODO implement the calibration processing function. + // It returns a pass for now - case FAN_SELF_TEST_COMPLETE_STATE: - // Done with POST. Do nothing - break; + isPOSTComplete = TRUE; + status = SELF_TEST_STATUS_PASSED; - default: - // Wrong state called - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_FAN_INVALID_SELF_TEST_STATE, fansSelfTestState ); - fansSelfTestState = FAN_SELF_TEST_COMPLETE_STATE; - break; - } - - return fansSelfTestResult; + return status; } /*********************************************************************//** @@ -157,7 +140,7 @@ void execFans( void ) { // Monitor the fans - monitorFans(); + //monitorFans(); TODO uncomment. this is to investigate why the fans RPM are out of range switch ( fansExecState ) { @@ -205,69 +188,6 @@ /*********************************************************************//** * @brief - * The handleSelfTestStart function handles the start state of the fans' - * self test. - * @details Inputs: none - * @details Outputs: none - * @return next state of self test - *************************************************************************/ -static FANS_SELF_TEST_STATES_T handleSelfTestStart( void ) -{ - FANS_SELF_TEST_STATES_T state = FANS_SELF_TEST_CHECK_RPM_STATE; - - // Set the fans to the target PWM for the next stage - setInletFansDutyCycle( FANS_SELF_TEST_TARGET_PWM ); - setOutletFansDutyCycle( FANS_SELF_TEST_TARGET_PWM ); - state = FANS_SELF_TEST_CHECK_RPM_STATE; - - return state; -} - -/*********************************************************************//** - * @brief - * The handleSelfTestCheckRPM function handles the check RPM state of the - * fans' self test. - * @details Inputs: fansStatus - * @details Outputs: none - * @return next state of self test - *************************************************************************/ -static FANS_SELF_TEST_STATES_T handleSelfTestCheckRPM( void ) -{ - FANS_SELF_TEST_STATES_T state = FANS_SELF_TEST_CHECK_RPM_STATE; - - FAN_NAMES_T fan; - - // Wait for the fans to run for a certain period of time before checking their RPM - if ( ++fansControlCounter > FANS_SELF_TEST_WAIT_INTERVAL ) - { - // Assuming the test will pass - fansSelfTestResult = SELF_TEST_STATUS_PASSED; - - convertTogglePeriod2RPM(); - - // Loop through all the fans to check their RPM. They should be above the specified RPM - for( fan = FAN_INLET_1; fan < NUM_OF_FANS_NAMES; fan++ ) - { - if ( fansStatus.rpm[ fan ] < MIN_TARGET_RPM_IN_SELF_TEST ) - { - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_FAN_RPM_OUT_OF_RANGE, fan, fansStatus.rpm[ fan ] ); - - fansSelfTestResult = SELF_TEST_STATUS_FAILED; - } - } - - // Turn off the fans, done with self test - setInletFansDutyCycle( 0.0 ); - setOutletFansDutyCycle( 0.0 ); - - state = FAN_SELF_TEST_COMPLETE_STATE; - } - - return state; -} - -/*********************************************************************//** - * @brief * The handleFansExecStateStart function handles the start state of the * fans exec state machine. * @details Inputs: none @@ -279,7 +199,7 @@ FANS_EXEC_STATES_T state = FANS_EXEC_STATE_WAIT_FOR_POST_STATE; // Wait for the self test to finish before starting the fans - if ( fansSelfTestState == FAN_SELF_TEST_COMPLETE_STATE ) + if ( TRUE == isPOSTComplete ) { // Start the fans with minimum PWM. The control will decide the next PWM automatically. setInletFansDutyCycle( FANS_MIN_DUTY_CYCLE ); @@ -313,10 +233,6 @@ // Get the maximum temperature among all the thermistors and temperature sensors to run fan from the hottest temperature F32 temperature = getMaximumTemperature(); - //TODO REMOVE FOR TESTING - temperature = getThermistorTemperatureValue( THERMISTOR_ONBOARD_NTC ); - //TODO REMOVE FOR TESTING only - // Solve the linear equation to calculate the duty cycle from temperature F32 dutyCycle = ( SLOPE * ( temperature - MIN_ALLOWED_AMBIENT_TEMPERATURE ) ) + FANS_MIN_DUTY_CYCLE; @@ -477,11 +393,18 @@ if ( ++fansMonitorCounter >= FANS_MONITOR_INTERVAL_COUNT ) { + // The RPM is expected to be 5500 @ 100% duty cycle + // The nominal RPM = duty cycle * 5500 / 1.0 + // The RPM tolerance is -25% to +50% of the nominal RPM + F32 fansNominalRPM = fansStatus.targetDutyCycle * FANS_MAX_ALLOWED_RPM; + F32 fansMinAllowedRPM = fansNominalRPM - ( fansNominalRPM * FANS_MIN_RPM_OUT_OF_RANGE_TOL ); + F32 fansMaxAllowedRPM = fansNominalRPM - ( fansNominalRPM * FANS_MAX_RPM_OUT_OF_RANGE_TOL ); + convertTogglePeriod2RPM(); for ( fan = FAN_INLET_1; fan < NUM_OF_FANS_NAMES; fan++ ) { - BOOL const fanRpmOutOfRange = ( fansStatus.rpm[ fan ] >= FANS_MAX_ALLOWED_RPM ) || ( fansStatus.rpm[ fan ] <= FANS_MIN_ALLOWED_RPM ); + BOOL const fanRpmOutOfRange = ( fansStatus.rpm[ fan ] < fansMinAllowedRPM ) || ( fansStatus.rpm[ fan ] > fansMaxAllowedRPM ); checkPersistentAlarm( ALARM_ID_DG_FAN_RPM_OUT_OF_RANGE, fanRpmOutOfRange, fansStatus.rpm[ fan ], FANS_MAX_ALLOWED_RPM ); } Index: firmware/App/Controllers/Heaters.c =================================================================== diff -u -r8b0c842f94e3c94cecf62f0f913e429def8e5efa -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Controllers/Heaters.c (.../Heaters.c) (revision 8b0c842f94e3c94cecf62f0f913e429def8e5efa) +++ firmware/App/Controllers/Heaters.c (.../Heaters.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -23,6 +23,7 @@ #include "AlarmMgmt.h" #include "DGDefs.h" #include "Heaters.h" +#include "InternalADC.h" #include "OperationModes.h" #include "PIControllers.h" #include "ROPump.h" @@ -40,38 +41,42 @@ // ********** private definitions ********** #ifndef V_2_SYSTEM -#define MAIN_PRIMARY_HEATER_MAX_DUTY_CYCLE 1.00 ///< Main primary heater (heater A) max duty cycle (100%). -#define SMALL_PRIMAY_HEATER_MAX_DUTY_CYCLE 1.00 ///< Small Primary heater (heater B) max duty cycle (100%). -#define TRIMMER_HEATER_MAX_DUTY_CYCLE 1.00 ///< Trimmer heater max duty cycle (100%). +#define MAIN_PRIMARY_HEATER_MAX_DUTY_CYCLE 1.00 ///< Main primary heater (heater A) max duty cycle (100%). +#define SMALL_PRIMAY_HEATER_MAX_DUTY_CYCLE 1.00 ///< Small Primary heater (heater B) max duty cycle (100%). +#define TRIMMER_HEATER_MAX_DUTY_CYCLE 1.00 ///< Trimmer heater max duty cycle (100%). #else -#define MAIN_PRIMARY_HEATER_MAX_DUTY_CYCLE 0.89 ///< Main primary heater (heater A) max duty cycle (89%). -#define SMALL_PRIMAY_HEATER_MAX_DUTY_CYCLE 0.50 ///< Small Primary heater (heater B) max duty cycle (50%). -#define TRIMMER_HEATER_MAX_DUTY_CYCLE 0.50 ///< Trimmer heater max duty cycle (50%). +#define MAIN_PRIMARY_HEATER_MAX_DUTY_CYCLE 0.89 ///< Main primary heater (heater A) max duty cycle (89%). +#define SMALL_PRIMAY_HEATER_MAX_DUTY_CYCLE 0.50 ///< Small Primary heater (heater B) max duty cycle (50%). +#define TRIMMER_HEATER_MAX_DUTY_CYCLE 0.50 ///< Trimmer heater max duty cycle (50%). #endif -#define HEATERS_MIN_DUTY_CYCLE 0.00 ///< Primary and trimmer heaters minimum duty cycle (0.00%). +#define HEATERS_MIN_DUTY_CYCLE 0.00 ///< Primary and trimmer heaters minimum duty cycle (0.00%). #define PRIMARY_HEATERS_CUMULATIVE_DUTY_CYCLE ( MAIN_PRIMARY_HEATER_MAX_DUTY_CYCLE + \ - SMALL_PRIMAY_HEATER_MAX_DUTY_CYCLE ) ///< Primary heaters cumulative duty cycle. -#define PRIMARY_HEATER_INITIAL_DUTY_CYCLE_ESTIMATE_DIVISOR 2.0 ///< Primary heaters initial duty cycle estimation divisor. -#define MAIN_AND_SMALL_PRIMARY_HEATER_DUTY_CYCLE_DIVISOR 2.0 ///< Main and small primary heater duty cycle divisor + SMALL_PRIMAY_HEATER_MAX_DUTY_CYCLE ) ///< Primary heaters cumulative duty cycle. +#define PRIMARY_HEATER_INITIAL_DUTY_CYCLE_ESTIMATE_DIVISOR 2.0 ///< Primary heaters initial duty cycle estimation divisor. +#define MAIN_AND_SMALL_PRIMARY_HEATER_DUTY_CYCLE_DIVISOR 2.0 ///< Main and small primary heater duty cycle divisor -#define PRIMARY_HEATERS_P_COEFFICIENT 0.15 ///< Primary heaters proportional coefficient. -#define PRIMARY_HEATERS_I_COEFFICIENT 0.001 ///< Primary heaters integral coefficient. +#define PRIMARY_HEATERS_P_COEFFICIENT 0.15 ///< Primary heaters proportional coefficient. +#define PRIMARY_HEATERS_I_COEFFICIENT 0.001 ///< Primary heaters integral coefficient. -#define TRIMMER_HEATER_P_COEFFICIENT 0.02 ///< Trimmer heater proportional coefficient. -#define TRIMMER_HEATER_I_COEFFICIENT 0.001 ///< Trimmer heater integral coefficient. +#define TRIMMER_HEATER_P_COEFFICIENT 0.02 ///< Trimmer heater proportional coefficient. +#define TRIMMER_HEATER_I_COEFFICIENT 0.001 ///< Trimmer heater integral coefficient. -#define CONTROLLER_CHECK_INTERVAL_COUNT 10U ///< Time interval count to check the PI controller. -#define TEMP_SENSORS_INTERVAL_COUNT 10U ///< Temperature sensors interval count. +#define CONTROLLER_CHECK_INTERVAL_COUNT 10U ///< Time interval count to check the PI controller. +#define TEMP_SENSORS_INTERVAL_COUNT 10U ///< Temperature sensors interval count. -#define HEATERS_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Heaters data publish interval. +#define HEATERS_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Heaters data publish interval. -#define MINIMUM_TARGET_TEMPERATURE 10.0 ///< Minimum allowed target temperature for the heaters. -#define MAXIMUM_TARGET_TEMPERATURE 90.0 ///< Maximum allowed target temperature for the heaters. +#define MINIMUM_TARGET_TEMPERATURE 10.0 ///< Minimum allowed target temperature for the heaters. +#define MAXIMUM_TARGET_TEMPERATURE 90.0 ///< Maximum allowed target temperature for the heaters. -#define HEATERS_ON_WITH_NO_FLOW_TIMEOUT_COUNT ( ( 3 * MS_PER_SECOND ) / TASK_PRIORITY_INTERVAL ) ///< Heaters are on but there is no sufficient flow timeout in counts. -#define HEATERS_MAX_ALLOWED_INTERNAL_TEMPERATURE_C 190.0 ///< Heaters max allowed internal temperature in degrees C. TODO figure out the max temperature value -#define HEATERS_MAX_ALLOWED_INTERNAL_TEMPERATURE_TIMEOUT_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Heaters max allowed internal temperature timeout in milliseconds. -#define HEATERS_ON_NO_FLOW_TIMEOUT_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Heaters on with no flow time out in milliseconds. +#define HEATERS_ON_WITH_NO_FLOW_TIMEOUT_COUNT ( ( 3 * MS_PER_SECOND ) / TASK_PRIORITY_INTERVAL ) ///< Heaters are on but there is no sufficient flow timeout in counts. +#define HEATERS_MAX_ALLOWED_INTERNAL_TEMPERATURE_C 170.0 ///< Heaters max allowed internal temperature in C. +#define HEATERS_MAX_ALLOWED_COLD_JUNCTION_TEMPERATURE_C 80.0 ///< Heaters max allowed cold junction temperature in C. +#define HEATERS_MAX_ALLOWED_INTERNAL_TEMPERATURE_TIMEOUT_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Heaters max allowed internal temperature timeout in milliseconds. +#define HEATERS_ON_NO_FLOW_TIMEOUT_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Heaters on with no flow time out in milliseconds. +#define HEATERS_MAX_OPERATING_VOLTAGE_V 24.0 ///< Heaters max operating voltage in volts. +#define HEATERS_VOLTAGE_MONITOR_TIME_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Heaters voltage monitor timer interval. +#define HEATERS_MAX_VOLTAGE_OUT_OF_RANGE_TOL 0.2 ///< Heaters max voltage out of range tolerance. /// Primary heaters exec states typedef enum primary_heaters_exec_states @@ -121,11 +126,12 @@ static U32 heatersOnWithNoFlowTimer; ///< Heaters are on but there is no sufficient flow. static TEMPERATURE_SENSORS_T primaryHeatersFeedbackTempSensor = TEMPSENSORS_OUTLET_PRIMARY_HEATER; ///< Primary heaters feedback temperature sensors. static TEMPERATURE_SENSORS_T trimmerHeaterFeedbackTempSensor = TEMPSENSORS_INLET_DIALYSATE; ///< Trimmer heater feedback temperature sensors. -static U32 primaryHeatersInternalTempOutTimer = 0; ///< Primary heaters internal temperature out of range timer. -static U32 trimmerHeaterInternalTempOutTimer = 0; ///< Trimmer heater internal temperature out of range timer. -static BOOL isPrimaryHeatersTempOutOfRange = FALSE; ///< Boolean flag to indicate if the primary heaters internal temperature out of range. +static U32 primaryHeaterTempOutTimer = 0; ///< Primary heaters temperature out of range start timer. +static U32 trimmerHeaterTempOutTimer = 0; ///< Trimmer heater internal temperature out of range timer. +static BOOL isPrimaryHeaterTempOutOfRange = FALSE; ///< Boolean flag to indicate if the primary heaters internal temperature out of range. static BOOL isTrimmerHeaterTempOutOfRange = FALSE; ///< Boolean flag to indicate if the trimmer heater internal temperature out of range. static BOOL isFlowBelowMin = FALSE; ///< Boolean flag to indicate if the flow is below the minimum. +static U32 heatersVoltageMonitorTimeCounter = 0; ///< Heaters voltage monitor counter. // ********** private function prototypes ********** @@ -139,8 +145,11 @@ static void setSmallPrimaryHeaterPWM( F32 pwm ); static void setTrimmerHeaterPWM( F32 pwm ); static void resetHeaterState( NAME_OF_HEATER_T heater ); -static void publishHeatersData( void ); static U32 getPublishHeatersDataInterval( void ); +static void publishHeatersData( void ); +static void checkPrimaryHeaterTempSensors( void ); +static void checkTrimmerHeaterTempSensors( void ); +static void monitorHeatersVoltage( void ); /*********************************************************************//** * @brief @@ -152,22 +161,23 @@ *************************************************************************/ void initHeaters( void ) { - primaryHeatersExecState = PRIMARY_HEATERS_EXEC_STATE_OFF; - trimmerHeaterExecState = TRIMMER_HEATER_EXEC_STATE_OFF; - primaryHeaterTargetTemperature = 0.0; - trimmerHeaterTargetTemperature = 0.0; - primaryHeaterTimerCounter = 0; - trimmerHeaterTimerCounter = 5; - dataPublicationTimerCounter = 0; - isPrimaryHeaterOn = FALSE; - isTrimmerHeaterOn = FALSE; - primaryHeatersFeedbackTempSensor = TEMPSENSORS_OUTLET_PRIMARY_HEATER; - trimmerHeaterFeedbackTempSensor = TEMPSENSORS_INLET_DIALYSATE; - primaryHeatersInternalTempOutTimer = 0; - trimmerHeaterInternalTempOutTimer = 0; - isPrimaryHeatersTempOutOfRange = FALSE; - isTrimmerHeaterTempOutOfRange = FALSE; - isFlowBelowMin = FALSE; + primaryHeatersExecState = PRIMARY_HEATERS_EXEC_STATE_OFF; + trimmerHeaterExecState = TRIMMER_HEATER_EXEC_STATE_OFF; + primaryHeaterTargetTemperature = 0.0; + trimmerHeaterTargetTemperature = 0.0; + primaryHeaterTimerCounter = 0; + trimmerHeaterTimerCounter = 5; + dataPublicationTimerCounter = 0; + isPrimaryHeaterOn = FALSE; + isTrimmerHeaterOn = FALSE; + primaryHeatersFeedbackTempSensor = TEMPSENSORS_OUTLET_PRIMARY_HEATER; + trimmerHeaterFeedbackTempSensor = TEMPSENSORS_INLET_DIALYSATE; + primaryHeaterTempOutTimer = 0; + trimmerHeaterTempOutTimer = 0; + isPrimaryHeaterTempOutOfRange = FALSE; + isTrimmerHeaterTempOutOfRange = FALSE; + isFlowBelowMin = FALSE; + heatersVoltageMonitorTimeCounter = 0; // Initialize the PI controller for the primary heaters initializePIController( PI_CONTROLLER_ID_PRIMARY_HEATER, HEATERS_MIN_DUTY_CYCLE, PRIMARY_HEATERS_P_COEFFICIENT, PRIMARY_HEATERS_I_COEFFICIENT, @@ -318,59 +328,26 @@ /*********************************************************************//** * @brief - * The execHeatersMonitor function turns off the heaters when RO pump is - * not on. - * @details Inputs: none - * @details Outputs: Turns off the heaters when RO pump is not on + * The execHeatersMonitor function monitors the status of the heaters. + * The internal temperature sensors and the voltages of the heaters are + * monitored. The flow is continuously checked and if there is no flow + * for a period of time, the heaters are turned off. + * @details Inputs: isTrimmerHeaterOn, mainPrimaryHeaterDutyCycle, + * smallPrimaryHeaterDutyCycle, trimmerHeaterDutyCycle, + * heatersOnWithNoFlowTimer, isFlowBelowMin + * @details Outputs: heatersOnWithNoFlowTimer, isFlowBelowMin * @return none *************************************************************************/ void execHeatersMonitor( void ) { #ifndef IGNORE_HEATERS_MONITOR - F32 primaryHeatersInternalTemp = getTemperatureValue( TEMPSENSORS_PRIMARY_HEATER_INTERNAL ); - F32 trimmerHeaterInternalTemp = getTemperatureValue( TEMPSENSORS_TRIMMER_HEATER_INTERNAL ); - - // Check if the primary heaters' internal temperature is above the limit - if ( primaryHeatersInternalTemp > HEATERS_MAX_ALLOWED_INTERNAL_TEMPERATURE_C ) - { - SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_PRIMARY_HEATERS_INTERNAL_TEMP_OUT_OF_RANGE, primaryHeatersInternalTemp ); - - // If it is above the range for the first time, stop the primary heaters - // and set the variables - if ( FALSE == isPrimaryHeatersTempOutOfRange ) - { - stopPrimaryHeater(); - isPrimaryHeatersTempOutOfRange = TRUE; - primaryHeatersInternalTempOutTimer = getMSTimerCount(); - } - // If the primary heaters internal temperature was out for more than the define period, activate the safety shutdown - else if ( TRUE == didTimeout( primaryHeatersInternalTempOutTimer, HEATERS_MAX_ALLOWED_INTERNAL_TEMPERATURE_TIMEOUT_MS ) ) - { - activateSafetyShutdown(); - } - } - - // Check if the trimmer heater internal temperature is above the limit - if ( trimmerHeaterInternalTemp > HEATERS_MAX_ALLOWED_INTERNAL_TEMPERATURE_C ) - { - SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_TRIMMER_HEATER_INTERNAL_TEMP_OUT_OF_RANGE, trimmerHeaterInternalTemp ); - - // If it is above the range for the first time, stop the trimmer heater - // and set the variables - if ( FALSE == isTrimmerHeaterTempOutOfRange ) - { - stopTrimmerHeater(); - isTrimmerHeaterTempOutOfRange = TRUE; - trimmerHeaterInternalTempOutTimer = getMSTimerCount(); - } - // If the trimmer heater internal temperature was out for more than the define period, activate the safety shutdown - else if ( TRUE == didTimeout( trimmerHeaterInternalTempOutTimer, HEATERS_MAX_ALLOWED_INTERNAL_TEMPERATURE_TIMEOUT_MS ) ) - { - activateSafetyShutdown(); - } - } + checkPrimaryHeaterTempSensors(); + checkTrimmerHeaterTempSensors(); #endif + // Monitor the heaters voltage + //monitorHeatersVoltage(); + /* * If any of the heaters are on or any of the heaters' PWMs are not zero, check if the flow is below than the minimum value * If the flow is below minimum for the first time, set the variables @@ -403,7 +380,7 @@ stopPrimaryHeater(); stopTrimmerHeater(); - SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DG_HEATERS_ON_WITH_NO_FLOW_TIMEOUT, measuredFlow ); + activateAlarmNoData( ALARM_ID_DG_HEATERS_ON_WITH_NO_FLOW_TIMEOUT ); } } else @@ -545,7 +522,7 @@ // Once the primary heaters duty cycle is set, it is divided into 2 so both heaters will start and both elements are heated up smallPrimaryHeaterDutyCycle = mainPrimaryHeaterDutyCycle / MAIN_AND_SMALL_PRIMARY_HEATER_DUTY_CYCLE_DIVISOR; - mainPrimaryHeaterDutyCycle = mainPrimaryHeaterDutyCycle / MAIN_AND_SMALL_PRIMARY_HEATER_DUTY_CYCLE_DIVISOR; + mainPrimaryHeaterDutyCycle = mainPrimaryHeaterDutyCycle / MAIN_AND_SMALL_PRIMARY_HEATER_DUTY_CYCLE_DIVISOR; } setMainPrimaryHeaterPWM( mainPrimaryHeaterDutyCycle ); @@ -767,6 +744,135 @@ } } +/*********************************************************************//** + * @brief + * The checkPrimaryHeaterTempSensors function checks the primary heater's + * thermocouple and cold junction temperature sensors. + * @details Inputs: isPrimaryHeaterTempOutOfRange + * @details Outputs: isPrimaryHeaterTempOutOfRange + * @return none + *************************************************************************/ +static void checkPrimaryHeaterTempSensors( void ) +{ + F32 primaryHeaterInternalTemp = getTemperatureValue( TEMPSENSORS_PRIMARY_HEATER_INTERNAL ); + F32 primaryHeaterColdJunctionTemp = getTemperatureValue( TEMPSENSORS_PRIMARY_HEATER_COLD_JUNCTION ); + BOOL isTempOut = FALSE; + + // Check if the primary heater's internal temperature or the cold junction temperature is above the allowed limit + if ( primaryHeaterInternalTemp > HEATERS_MAX_ALLOWED_INTERNAL_TEMPERATURE_C ) + { + isTempOut = TRUE; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_PRIMARY_HEATER_INTERNAL_TEMP_OUT_OF_RANGE, primaryHeaterInternalTemp ); + } + else if ( primaryHeaterColdJunctionTemp > HEATERS_MAX_ALLOWED_COLD_JUNCTION_TEMPERATURE_C ) + { + isTempOut = TRUE; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_PRIMARY_HEATER_CJ_TEMP_OUT_OF_RANGE, primaryHeaterInternalTemp ); + } + + // If any of the temperatures are above the range + if ( ( FALSE == isPrimaryHeaterTempOutOfRange ) && ( TRUE == isTempOut ) ) + { + stopPrimaryHeater(); + isPrimaryHeaterTempOutOfRange = TRUE; + primaryHeaterTempOutTimer = getMSTimerCount(); + } + // If the primary heaters internal temperature was out for more than the define period, activate the safety shutdown + else if ( ( TRUE == isPrimaryHeaterTempOutOfRange ) && + ( TRUE == didTimeout( primaryHeaterTempOutTimer, HEATERS_MAX_ALLOWED_INTERNAL_TEMPERATURE_TIMEOUT_MS ) ) ) + { + isPrimaryHeaterTempOutOfRange = FALSE; + activateSafetyShutdown(); + } +} + +/*********************************************************************//** + * @brief + * The checkTrimmerHeaterTempSensors function checks the trimmer heater's + * thermocouple and cold junction temperature sensors. + * @details Inputs: isTrimmerHeaterTempOutOfRange + * @details Outputs: isTrimmerHeaterTempOutOfRange + * @return none + *************************************************************************/ +static void checkTrimmerHeaterTempSensors( void ) +{ + F32 trimmerHeaterInternalTemp = getTemperatureValue( TEMPSENSORS_TRIMMER_HEATER_INTERNAL ); + F32 trimmerHeaterColdJunctionTemp = getTemperatureValue( TEMPSENSORS_TRIMMER_HEATER_COLD_JUNCTION ); + BOOL isTempOut = FALSE; + + // Check if the primary heater's internal temperature or the cold junction temperature is above the allowed limit + if ( trimmerHeaterInternalTemp > HEATERS_MAX_ALLOWED_INTERNAL_TEMPERATURE_C ) + { + isTempOut = TRUE; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_TRIMMER_HEATER_INTERNAL_TEMP_OUT_OF_RANGE, trimmerHeaterInternalTemp ); + } + else if ( trimmerHeaterColdJunctionTemp > HEATERS_MAX_ALLOWED_COLD_JUNCTION_TEMPERATURE_C ) + { + isTempOut = TRUE; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_TRIMMER_HEATER_CJ_TEMP_OUT_OF_RANGE, trimmerHeaterColdJunctionTemp ); + } + + // If it is above the range for the first time, stop the trimmer heater + // and set the variables + if ( ( FALSE == isTrimmerHeaterTempOutOfRange ) && ( TRUE == isTempOut ) ) + { + stopTrimmerHeater(); + isTrimmerHeaterTempOutOfRange = TRUE; + trimmerHeaterTempOutTimer = getMSTimerCount(); + } + // If the trimmer heater internal temperature was out for more than the define period, activate the safety shutdown + else if ( ( TRUE == isTrimmerHeaterTempOutOfRange ) && + ( TRUE == didTimeout( trimmerHeaterTempOutTimer, HEATERS_MAX_ALLOWED_INTERNAL_TEMPERATURE_TIMEOUT_MS ) ) ) + { + activateSafetyShutdown(); + } +} + +/*********************************************************************//** + * @brief + * The monitorHeatersVoltage function monitors the heaters' voltages + * @details Inputs: heatersVoltageMonitorTimeCounter + * @details Outputs: heatersVoltageMonitorTimeCounter + * @return none + *************************************************************************/ +static void monitorHeatersVoltage( void ) +{ + if ( ++heatersVoltageMonitorTimeCounter >= HEATERS_VOLTAGE_MONITOR_TIME_INTERVAL ) + { + F32 mainPriVoltage = getIntADCVoltageConverted( INT_ADC_PRIMARY_HEATER_24_VOLTS ); + // TODO it is assumed that the main and small primary heaters have equal voltage since the PWMs are divided into 2 + // before applying the PWMs to the heaters. Right now, there is no ADC channel available for the small primary + // heater so the main primary heater's ADC channel is used for the small primary heater as well. + F32 smallPriVoltage = getIntADCVoltageConverted( INT_ADC_PRIMARY_HEATER_24_VOLTS ); + F32 trimmerVoltage = getIntADCVoltageConverted( INT_ADC_TRIMMER_HEATER_24_VOLTS ); + + // Voltage to PWM is reverse. If PWM = 0 -> V = 24V + F32 mainPri = 1.0 - mainPrimaryHeaterDutyCycle; + F32 smallPri = 1.0 - smallPrimaryHeaterDutyCycle; + F32 trimmer = 1.0 - trimmerHeaterDutyCycle; + + // Check main primary heater's voltage + // The corresponding voltage of the current PWM must be close to the sensed voltage + if ( fabs( ( HEATERS_MAX_OPERATING_VOLTAGE_V * mainPri ) - mainPriVoltage ) > HEATERS_MAX_VOLTAGE_OUT_OF_RANGE_TOL * mainPriVoltage ) + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DG_MAIN_PRIMARY_HEATER_VOLTAGE_OUT_OF_RANGE, mainPriVoltage ); + } + // Check small primary heater's voltage + if ( fabs( ( HEATERS_MAX_OPERATING_VOLTAGE_V * smallPri ) - smallPriVoltage ) > HEATERS_MAX_VOLTAGE_OUT_OF_RANGE_TOL * smallPriVoltage ) + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DG_SMALL_PRIMARY_HEATER_VOLTAGE_OUT_OF_RANGE, smallPriVoltage ); + } + // Check trimmer heater's voltage + if ( fabs( ( HEATERS_MAX_OPERATING_VOLTAGE_V * trimmer ) - trimmerVoltage ) > HEATERS_MAX_VOLTAGE_OUT_OF_RANGE_TOL * trimmerVoltage ) + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DG_TRIMMER_HEATER_VOLTAGE_OUT_OF_RANGE, trimmerVoltage ); + } + + heatersVoltageMonitorTimeCounter = 0; + } +} + + /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ Index: firmware/App/Controllers/Pressures.c =================================================================== diff -u -r5a36a768d11cc597a36b894c1fb3a5e5590130f1 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Controllers/Pressures.c (.../Pressures.c) (revision 5a36a768d11cc597a36b894c1fb3a5e5590130f1) +++ firmware/App/Controllers/Pressures.c (.../Pressures.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -14,10 +14,12 @@ * @date (original) 04-Apr-2020 * ***************************************************************************/ +#include #include "AlarmMgmt.h" #include "FPGA.h" -#include "InternalADC.h" +#include "InternalADC.h" +#include "NVDataMgmt.h" #include "OperationModes.h" #include "PersistentAlarm.h" #include "Pressures.h" @@ -74,14 +76,16 @@ static U32 pressureFilterCounter = 0; ///< Used to schedule pressure sensor filtering. static PRESSURE_SELF_TEST_STATE_T pressuresSelfTestState; ///< Current pressure self-test state. -static SELF_TEST_STATUS_T pressuresSelfTestResult; ///< Self-test result of the Pressures module. +static SELF_TEST_STATUS_T pressuresSelfTestResult; ///< Self-test result of the Pressures module. +static DG_PRES_SENSORS_CAL_RECORD_T pressuresCalRecord; ///< Pressures calibration record. // ********** private function prototypes ********** static PRESSURE_STATE_T handlePressuresInitState( void ); static PRESSURE_STATE_T handlePressuresContReadState( void ); static void publishPressuresData( void ); -static U32 getPublishPressuresDataInterval( void ); +static U32 getPublishPressuresDataInterval( void ); +static BOOL processCalibrationData( void ); static SELF_TEST_STATUS_T handleSelfTestADCCheck( void ); @@ -94,7 +98,7 @@ *************************************************************************/ void initPressures( void ) { - U32 i; + U32 i; for ( i = 0; i < NUM_OF_PRESSURE_SENSORS; i++ ) { @@ -111,8 +115,73 @@ pressuresDataPublicationTimerCounter = 0; initPersistentAlarm( ALARM_ID_INLET_WATER_LOW_PRESSURE, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); - initPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_FAULT, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); + initPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_FAULT, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); } + +/*********************************************************************//** + * @brief + * The checkInletPressure function checks inlet water pressure value + * and triggers an alarm when pressure value is out of allowed range. + * @details Inputs: RO pump inlet pressure sensor value + * @details Outputs: Triggers low pressure persistent alarm + * @return none + *************************************************************************/ +void checkInletPressure( void ) +{ +#ifndef DISABLE_WATER_QUALITY_CHECK + F32 const pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); + BOOL const isPressureTooLow = ( pressure < MIN_INLET_WATER_PRESSURE ); + + checkPersistentAlarm( ALARM_ID_INLET_WATER_LOW_PRESSURE, isPressureTooLow, pressure, MIN_INLET_WATER_PRESSURE ); +#endif +} + +/*********************************************************************//** + * @brief + * The checkInletPressureFault function checks inlet water pressure value + * and triggers a machine fault when pressure value is out of allowed range. + * @details Inputs: RO pump inlet pressure sensor value + * @details Outputs: Triggers pressure fault persistent alarm + * @return none + *************************************************************************/ +void checkInletPressureFault( void ) +{ + F32 const pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); + BOOL const isPressureTooLow = ( pressure < MIN_INLET_WATER_PRESSURE ); + + checkPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_FAULT, isPressureTooLow, pressure, MIN_INLET_WATER_PRESSURE ); +} + +/*********************************************************************//** + * @brief + * The getMeasuredArterialPressure function gets the current arterial pressure. + * @details Inputs: arterialPressure + * @details Outputs: none + * @param pressureID pressure sensor ID + * @return the current arterial pressure (in mmHg). + *************************************************************************/ +F32 getMeasuredDGPressure( U32 pressureID ) +{ + F32 result = 0.0; + + if ( pressureID < NUM_OF_PRESSURE_SENSORS ) + { + if ( OVERRIDE_KEY == pressures[ pressureID ].override ) + { + result = pressures[ pressureID ].ovData; + } + else + { + result = pressures[ pressureID ].data; + } + } + else + { + activateAlarmNoData( ALARM_ID_DG_SOFTWARE_FAULT ); + } + + return result; +} /*********************************************************************//** * @brief @@ -143,6 +212,41 @@ // publish pressure/occlusion data on interval publishPressuresData(); +} + +/*********************************************************************//** + * @brief + * The execPressureSelfTest function executes the pressures self-test's + * state machine. + * @details Inputs: pressuresSelfTestState + * @details Outputs: pressuresSelfTestState + * @return PressuresSelfTestResult (SELF_TEST_STATUS_T) + *************************************************************************/ +SELF_TEST_STATUS_T execPressureSelfTest( void ) +{ + switch ( pressuresSelfTestState ) + { + case PRESSURE_SELF_TEST_STATE_START: + processCalibrationData(); + pressuresSelfTestState = PRESSURE_TEST_STATE_IN_PROGRESS; + pressuresSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; + break; + + case PRESSURE_TEST_STATE_IN_PROGRESS: + pressuresSelfTestResult = handleSelfTestADCCheck(); + pressuresSelfTestState = PRESSURE_TEST_STATE_COMPLETE; + break; + + case PRESSURE_TEST_STATE_COMPLETE: + // Done with self-test + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_DG_PRESSURES_INVALID_SELF_TEST_STATE, pressuresSelfTestState ); + break; + } + + return pressuresSelfTestResult; } /*********************************************************************//** @@ -182,40 +286,62 @@ measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_INLET ] += measuredPressureReadingsRaw[ PRESSURE_SENSOR_RO_PUMP_INLET ]; measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_OUTLET ] += measuredPressureReadingsRaw[ PRESSURE_SENSOR_RO_PUMP_OUTLET ]; measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ] += measuredPressureReadingsRaw[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ]; - measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] += measuredPressureReadingsRaw[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ]; + measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] += measuredPressureReadingsRaw[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ]; + + // Check if a new calibration is available + if ( TRUE == isNewCalibrationRecordAvailable() ) + { + // Get the new calibration data and check its validity + processCalibrationData(); + } // filter every 200ms if ( ++pressureFilterCounter >= PRESSURE_SAMPLES_TO_AVERAGE ) - { // calculate average pressures + { + U32 sensor; + F32 pressuresBeforeCal[ NUM_OF_PRESSURE_SENSORS ]; + + // Calculate average pressures F32 avgRoIn = (F32)measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_INLET ] * PRESSURE_AVERAGE_MULTIPLIER; F32 avgRoOut = (F32)measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_OUTLET ] * PRESSURE_AVERAGE_MULTIPLIER; F32 avgDrnIn = (F32)measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ] * PRESSURE_AVERAGE_MULTIPLIER; - F32 avgDrnOut = (F32)measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] * PRESSURE_AVERAGE_MULTIPLIER; + F32 avgDrnOut = (F32)measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] * PRESSURE_AVERAGE_MULTIPLIER; + + // Convert average pressure readings to PSI + pressuresBeforeCal[ PRESSURE_SENSOR_RO_PUMP_INLET ] = avgRoIn * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; + pressuresBeforeCal[ PRESSURE_SENSOR_RO_PUMP_OUTLET ] = avgRoOut * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; + pressuresBeforeCal[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ] = avgDrnIn * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; + pressuresBeforeCal[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] = avgDrnOut * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; - // reset average counter - pressureFilterCounter = 0; + // Reset average counter + pressureFilterCounter = 0; + + // Apply calibration to the pressure values prior to storing them + for ( sensor = 0; sensor < NUM_OF_PRESSURE_SENSORS; sensor++ ) + { + pressures[ sensor ].data = pow(pressuresBeforeCal[ sensor ], 4) * pressuresCalRecord.pressureSensors[ sensor ].fourthOrderCoeff + + pow(pressuresBeforeCal[ sensor ], 3) * pressuresCalRecord.pressureSensors[ sensor ].thirdOrderCoeff + + pow(pressuresBeforeCal[ sensor ], 2) * pressuresCalRecord.pressureSensors[ sensor ].secondOrderCoeff + + pressuresBeforeCal[ sensor ] * pressuresCalRecord.pressureSensors[ sensor ].gain + + pressuresCalRecord.pressureSensors[ sensor ].offset; + } - // convert average pressure readings to PSI - pressures[ PRESSURE_SENSOR_RO_PUMP_INLET ].data = avgRoIn * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; - pressures[ PRESSURE_SENSOR_RO_PUMP_OUTLET ].data = avgRoOut * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; - pressures[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ].data = avgDrnIn * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; - pressures[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ].data = avgDrnOut * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; - // reset sums for next averaging measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_INLET ] = 0; measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_OUTLET ] = 0; measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ] = 0; measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] = 0; - } - + } + // TODO - any other checks return result; } /*********************************************************************//** * @brief - * The getPublishPressuresDataInterval function gets the pressure data publish internval. + * The getPublishPressuresDataInterval function gets the pressure data + * publish interval. * @details Inputs: pressuresDataPublishInterval * @details Outputs: none * @return the current pressures data publication interval (in task intervals). @@ -230,75 +356,52 @@ } return result; -} +} /*********************************************************************//** * @brief - * The checkInletPressure function checks inlet water pressure value - * and triggers an alarm when pressure value is out of allowed range. - * @details Inputs: RO pump inlet pressure sensor value - * @details Outputs: Triggers low pressure persistent alarm - * @return none + * The processCalibrationData function gets the calibration data and makes + * sure it is valid by checking the calibration date. The calibration date + * should not be 0. + * @details Inputs: none + * @details Outputs: pressuresCalRecord + * @return TRUE if the calibration record is valid, otherwise FALSE *************************************************************************/ -void checkInletPressure( void ) +static BOOL processCalibrationData( void ) { -#ifndef DISABLE_WATER_QUALITY_CHECK - F32 const pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); - BOOL const isPressureTooLow = ( pressure < MIN_INLET_WATER_PRESSURE ); + BOOL status = TRUE; + U32 sensor; - checkPersistentAlarm( ALARM_ID_INLET_WATER_LOW_PRESSURE, isPressureTooLow, pressure, MIN_INLET_WATER_PRESSURE ); + // Get the calibration record from NVDataMgmt + DG_PRES_SENSORS_CAL_RECORD_T calData = getDGPressureSensorsCalibrationRecord(); + + for ( sensor = 0; sensor < NUM_OF_CAL_DATA_PRES_SENSORS; sensor++ ) + { +#ifndef SKIP_CAL_CHECK + // Check if the calibration data that was received from NVDataMgmt is legitimate + // The calibration date item should not be zero. If the calibration date is 0, + // then the data is not stored in the NV memory or it was corrupted. + if ( 0 == calData.pressureSensors[ sensor ].calibrationTime ) + { + + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_PRESSURE_SENSORS_INVALID_CAL_RECORD, (U32)sensor ); + status = FALSE; + } #endif -} -/*********************************************************************//** - * @brief - * The checkInletPressureFault function checks inlet water pressure value - * and triggers a machine fault when pressure value is out of allowed range. - * @details Inputs: RO pump inlet pressure sensor value - * @details Outputs: Triggers pressure fault persistent alarm - * @return none - *************************************************************************/ -void checkInletPressureFault( void ) -{ - F32 const pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); - BOOL const isPressureTooLow = ( pressure < MIN_INLET_WATER_PRESSURE ); + // The calibration data was valid, update the local copy + pressuresCalRecord.pressureSensors[ sensor ].fourthOrderCoeff = calData.pressureSensors[ sensor ].fourthOrderCoeff; + pressuresCalRecord.pressureSensors[ sensor ].thirdOrderCoeff = calData.pressureSensors[ sensor ].thirdOrderCoeff; + pressuresCalRecord.pressureSensors[ sensor ].secondOrderCoeff = calData.pressureSensors[ sensor ].secondOrderCoeff; + pressuresCalRecord.pressureSensors[ sensor ].gain = calData.pressureSensors[ sensor ].gain; + pressuresCalRecord.pressureSensors[ sensor ].offset = calData.pressureSensors[ sensor ].offset; + } - checkPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_FAULT, isPressureTooLow, pressure, MIN_INLET_WATER_PRESSURE ); + return status; } /*********************************************************************//** * @brief - * The getMeasuredArterialPressure function gets the current arterial pressure. - * @details Inputs: arterialPressure - * @details Outputs: none - * @param pressureID pressure sensor ID - * @return the current arterial pressure (in mmHg). - *************************************************************************/ -F32 getMeasuredDGPressure( U32 pressureID ) -{ - F32 result = 0.0; - - if ( pressureID < NUM_OF_PRESSURE_SENSORS ) - { - if ( OVERRIDE_KEY == pressures[ pressureID ].override ) - { - result = pressures[ pressureID ].ovData; - } - else - { - result = pressures[ pressureID ].data; - } - } - else - { - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_PRESSURE_SENSOR_ID, pressureID ); - } - - return result; -} - -/*********************************************************************//** - * @brief * The publishPressuresData function publishes DG pressures data at a set interval. * @details Inputs: pressuresDataPublicationTimerCounter * @details Outputs: Pressures data are published to CAN bus @@ -319,39 +422,6 @@ } } -/*********************************************************************//** - * @brief - * The execPressureSelfTest function executes the pressures self-test's state machine. - * @details Inputs: pressuresSelfTestState - * @details Outputs: pressuresSelfTestState - * @return PressuresSelfTestResult (SELF_TEST_STATUS_T) - *************************************************************************/ -SELF_TEST_STATUS_T execPressureSelfTest( void ) -{ - switch ( pressuresSelfTestState ) - { - case PRESSURE_SELF_TEST_STATE_START: - pressuresSelfTestState = PRESSURE_TEST_STATE_IN_PROGRESS; - pressuresSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; - break; - - case PRESSURE_TEST_STATE_IN_PROGRESS: - pressuresSelfTestResult = handleSelfTestADCCheck(); - pressuresSelfTestState = PRESSURE_TEST_STATE_COMPLETE; - break; - - case PRESSURE_TEST_STATE_COMPLETE: - // Done with self-test - break; - - default: - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_TEMPERATURE_SENSORS_INVALID_SELF_TEST_STATE, pressuresSelfTestState ); - break; - } - - return pressuresSelfTestResult; -} - /*********************************************************************//** * @brief * The handleSelfTestADCCheck function checks whether the ADC reads and @@ -366,7 +436,7 @@ SELF_TEST_STATUS_T result = SELF_TEST_STATUS_PASSED; U16 const inletPressureADCReading = getIntADCReading( INT_ADC_RO_PUMP_INLET_PRESSURE ); - if ( ( inletPressureADCReading == 0 ) || ( inletPressureADCReading >= INT_ADC_FULL_SCALE_BITS ) ) + if ( ( 0 == inletPressureADCReading ) || ( inletPressureADCReading >= INT_ADC_FULL_SCALE_BITS ) ) { result = SELF_TEST_STATUS_FAILED; SET_ALARM_WITH_1_U32_DATA( ALARM_ID_PRESSURE_SENSOR_FAULT, inletPressureADCReading ); Index: firmware/App/Controllers/ROPump.c =================================================================== diff -u -r5a36a768d11cc597a36b894c1fb3a5e5590130f1 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Controllers/ROPump.c (.../ROPump.c) (revision 5a36a768d11cc597a36b894c1fb3a5e5590130f1) +++ firmware/App/Controllers/ROPump.c (.../ROPump.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -21,9 +21,10 @@ #include "mibspi.h" #include "FPGA.h" +#include "NVDataMgmt.h" #include "OperationModes.h" -#include "PIControllers.h" #include "PersistentAlarm.h" +#include "PIControllers.h" #include "Pressures.h" #include "ROPump.h" #include "SafetyShutdown.h" @@ -131,6 +132,7 @@ static S32 measuredFlowReadingsSum = 0; ///< Raw flow reading sums for averaging. static U32 flowFilterCounter = 0; ///< Flow filtering counter. +static DG_FLOW_SENSORS_CAL_RECORD_T flowSensorsCalRecord; ///< Flow sensors calibration record. // ********** private function prototypes ********** @@ -145,6 +147,7 @@ static void stopROPump( void ); static void publishROPumpData( void ); static U32 getPublishROPumpDataInterval( void ); +static BOOL processCalibrationData( void ); /*********************************************************************//** * @brief @@ -331,20 +334,38 @@ BOOL isPressureMax = actualPressure >= MAX_ALLOWED_MEASURED_PRESSURE_PSI; checkPersistentAlarm( ALARM_ID_RO_PUMP_PRESSURE_OUT_OF_RANGE, isPressureMax, actualPressure, MAX_ALLOWED_MEASURED_PRESSURE_PSI ); + // Check if a new calibration is available + if ( TRUE == isNewCalibrationRecordAvailable() ) + { + // Get the new calibration data and check its validity + processCalibrationData(); + } + // Read flow at the control set if ( ++flowFilterCounter >= FLOW_SAMPLES_TO_AVERAGE ) { - F32 avgROFlow = (F32)measuredFlowReadingsSum * FLOW_AVERAGE_MULTIPLIER; + U32 sensor; + F32 flow = RO_FLOW_ADC_TO_LPM_FACTOR / ( (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 ) ) + if ( ( FLOW_SENSOR_ZERO_READING == roFlowReading ) || ( 0 == roFlowReading ) ) { measuredROFlowRateLPM.data = 0.0; } else { - measuredROFlowRateLPM.data = RO_FLOW_ADC_TO_LPM_FACTOR / avgROFlow; + // Right now there is only one flow sensor but a for loop is used here to be able to automatically accommodate + // the future flow sensors + for( sensor = 0; sensor < NUM_OF_CAL_DATA_FLOW_SENSORS; sensor++ ) + { + measuredROFlowRateLPM.data = pow(flow, 4) * flowSensorsCalRecord.flowSensors[ sensor ].fourthOrderCoeff + + pow(flow, 3) * flowSensorsCalRecord.flowSensors[ sensor ].thirdOrderCoeff + + pow(flow, 2) * flowSensorsCalRecord.flowSensors[ sensor ].secondOrderCoeff + + flow * flowSensorsCalRecord.flowSensors[ sensor ].gain + + flowSensorsCalRecord.flowSensors[ sensor ].offset; + } } measuredFlowReadingsSum = 0; @@ -368,7 +389,6 @@ checkPersistentAlarm( ALARM_ID_FLOW_RATE_OUT_OF_UPPER_RANGE, isFlowOutOfUpperRange, currentFlow, targetFlow ); checkPersistentAlarm( ALARM_ID_FLOW_RATE_OUT_OF_LOWER_RANGE, isFlowOutOfLowerRange, currentFlow, targetFlow ); } -#endif // If the pump is off and PPi + 5psi < PPo for a certain period of time, activate safety shutdown if ( FALSE == isROPumpOn ) @@ -384,6 +404,7 @@ activateSafetyShutdown(); } } +#endif // Publish RO pump data on interval publishROPumpData(); @@ -449,6 +470,31 @@ /*********************************************************************//** * @brief + * The execROPumpSelfTest function executes the RO pump's self-test. + * @details Inputs: none + * @details Outputs: none + * @return PressuresSelfTestResult (SELF_TEST_STATUS_T) + *************************************************************************/ +SELF_TEST_STATUS_T execROPumpSelfTest( void ) +{ + SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; + + BOOL calStatus = processCalibrationData(); + + if ( TRUE == calStatus ) + { + result = SELF_TEST_STATUS_PASSED; + } + else + { + result = SELF_TEST_STATUS_FAILED; + } + + return result; +} + +/*********************************************************************//** + * @brief * The isROPumpRunning function returns the on/off status of RO pump. * @details Inputs: isROPumpOn * @details Outputs: none @@ -462,6 +508,51 @@ /*********************************************************************//** * @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 targetROPumpMaxPressure; +} + +/*********************************************************************//** + * @brief * The handleROPumpOffState function handles the RO pump off state of the * controller state machine. * @details Inputs: roPumpControlMode, roPumpPWMDutyCyclePctSet, @@ -717,51 +808,48 @@ /*********************************************************************//** * @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). + * The processCalibrationData function gets the calibration data and makes + * sure it is valid by checking the calibration date. The calibration date + * should not be 0. + * @details Inputs: none + * @details Outputs: flowSensorsCalRecord + * @return TRUE if the calibration record is valid, otherwise FALSE *************************************************************************/ -F32 getTargetROPumpFlowRate( void ) +static BOOL processCalibrationData( void ) { - return targetROPumpFlowRate; -} + BOOL status = TRUE; + U32 sensor; -/*********************************************************************//** - * @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; + // Get the calibration record from NVDataMgmt + DG_FLOW_SENSORS_CAL_RECORD_T calData = getDGFlowSensorsCalibrationRecord(); - if ( OVERRIDE_KEY == measuredROFlowRateLPM.override ) + for ( sensor = 0; sensor < NUM_OF_CAL_DATA_FLOW_SENSORS; sensor++ ) { - result = measuredROFlowRateLPM.ovData; +#ifndef SKIP_CAL_CHECK + // Check if the calibration data that was received from NVDataMgmt is legitimate + // The calibration date item should not be zero. If the calibration date is 0, + // then the flow sensors data is not stored in the NV memory or it was corrupted. + if ( 0 == calData.flowSensors[ sensor ].calibrationTime ) + { + + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_FLOW_SENSORS_INVALID_CAL_RECORD, (U32)sensor ); + status = FALSE; + } +#endif + + // The calibration data was valid, update the local copy + flowSensorsCalRecord.flowSensors[ sensor ].fourthOrderCoeff = calData.flowSensors[ sensor ].fourthOrderCoeff; + flowSensorsCalRecord.flowSensors[ sensor ].thirdOrderCoeff = calData.flowSensors[ sensor ].thirdOrderCoeff; + flowSensorsCalRecord.flowSensors[ sensor ].secondOrderCoeff = calData.flowSensors[ sensor ].secondOrderCoeff; + flowSensorsCalRecord.flowSensors[ sensor ].gain = calData.flowSensors[ sensor ].gain; + flowSensorsCalRecord.flowSensors[ sensor ].offset = calData.flowSensors[ sensor ].offset; } - return result; + return status; } /*********************************************************************//** * @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 targetROPumpMaxPressure; -} - -/*********************************************************************//** - * @brief * The publishROPumpData function publishes RO pump data at the set interval. * @details Inputs: roPumpDataPublicationTimerCounter * @details Outputs: roPumpDataPublicationTimerCounter @@ -785,6 +873,7 @@ } } + /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ Index: firmware/App/Controllers/UVReactors.c =================================================================== diff -u -r2fff37fa585181917705645494549b5fd4a4d522 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Controllers/UVReactors.c (.../UVReactors.c) (revision 2fff37fa585181917705645494549b5fd4a4d522) +++ firmware/App/Controllers/UVReactors.c (.../UVReactors.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -48,7 +48,7 @@ { UV_REACTOR_STATE_T execState; ///< UV reactor executive state PIN_SIGNAL_STATE_T pinSignalState; ///< UV reactor pin signal state - SWITCH_STATES_T switchState; ///< UV reactor turn on/turn off state + UV_REACTOR_STATES_T switchState; ///< UV reactor turn on/turn off state U32 reactorEnablePin; ///< UV reactor enable pin of GIO port A U32 reactorHealthStatusPin; ///< UV reactor status pin of N2HET1 OVERRIDE_U32_T healthStatus; ///< UV reactor current health status Index: firmware/App/DGCommon.h =================================================================== diff -u -r2fff37fa585181917705645494549b5fd4a4d522 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/DGCommon.h (.../DGCommon.h) (revision 2fff37fa585181917705645494549b5fd4a4d522) +++ firmware/App/DGCommon.h (.../DGCommon.h) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -23,7 +23,7 @@ // ********** version ********** #define DG_VERSION_MAJOR 0 -#define DG_VERSION_MINOR 5 +#define DG_VERSION_MINOR 6 #define DG_VERSION_MICRO 0 #define DG_VERSION_BUILD 15 @@ -33,20 +33,19 @@ #ifndef _VECTORCAST_ // TODO: Removed debug build flags when release build is ready // #define BOARD_WITH_NO_HARDWARE 1 - #define DONT_SKIP_NV_POST 1 // #define SIMULATE_UI 1 // #define TASK_TIMING_OUTPUT_ENABLED 1 // re-purposes drain pump enable pin for task timing // #define DISABLE_HEATERS_AND_TEMPS 1 // #define DISABLE_ACCELS 1 // #define SKIP_POST 1 - #define DISABLE_CAL_CHECK 1 + #define SKIP_CAL_CHECK 1 // #define ENABLE_DIP_SWITCHES 1 // #define EMC_TEST_BUILD 1 #define ALARMS_DEBUG 1 // #define HEATERS_DEBUG 1 // #define PRESSURES_DEBUG 1 #define DISABLE_DIALYSATE_CHECK 1 -// #define IGNORE_DRAIN_PUMP_MONITOR 1 + #define IGNORE_DRAIN_PUMP_MONITOR 1 // #define IGNORE_HEATERS_MONITOR 1 #define IGNORE_RO_PUMP_MONITOR 1 // #define DISABLE_RO_RATIO_CHECK 1 @@ -55,7 +54,6 @@ // #define DISABLE_WATER_QUALITY_CHECK 1 #define DISABLE_RTC_CONFIG 1 //#define V_2_SYSTEM 1 - //#define SKIP_RECIRC 1 #define THD_USING_TRO_CONNECTOR 1 #define IGNORE_CONC_PUMP_IN_HEAT_DISINFECT 1 #include Index: firmware/App/Modes/ModeFlush.c =================================================================== diff -u -r2fff37fa585181917705645494549b5fd4a4d522 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Modes/ModeFlush.c (.../ModeFlush.c) (revision 2fff37fa585181917705645494549b5fd4a4d522) +++ firmware/App/Modes/ModeFlush.c (.../ModeFlush.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -15,6 +15,7 @@ * ***************************************************************************/ +#include "ConcentratePumps.h" #include "DrainPump.h" #include "Heaters.h" #include "LoadCell.h" @@ -39,33 +40,36 @@ // General defines #define FLUSH_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Mode flush data publish interval in counts. -#define RO_PUMP_TARGET_FLOW_RATE_LPM 0.8 ///< RO pump target flow rate during flush/fill in L/min. TODO original flow was 0.8 -#define RO_PUMP_MAX_PRESSURE_PSI 130 ///< Maximum RO pump pressure during flush/fill states in psi. -#define DRAIN_PUMP_TARGET_RPM 2200 ///< Drain pump target RPM during drain. +#define RO_PUMP_TARGET_FLOW_RATE_LPM 0.8 ///< RO pump target flow rate during flush/fill in L/min. +#define RO_PUMP_MAX_PRESSURE_PSI 130 ///< Maximum RO pump pressure during flush/fill states in psi. +#define DRAIN_PUMP_TARGET_RPM 2200 ///< Drain pump target RPM during drain. // Drain R1 & R2 states defines -#define DRAIN_WEIGHT_UNCHANGE_TIMEOUT ( 6 * MS_PER_SECOND ) ///< Time period of unchanged weight during draining before timeout. +#define DRAIN_WEIGHT_UNCHANGE_TIMEOUT ( 6 * MS_PER_SECOND ) ///< Time period of unchanged weight during draining before timeout. // Flush drain path state defines -#define FLUSH_DRAIN_WAIT_TIME_MS ( 1 * 60 * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. TODo original time is 2 minutes +#define FLUSH_DRAIN_WAIT_TIME_MS ( 2 * 60 * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. // Flush dialysate state defines #define FLUSH_DIALYSATE_WAIT_TIME_MS ( 0.5 * 60 * MS_PER_SECOND ) ///< Flush dialysate wait time in milliseconds. // Flush concentrate straws state defines -#define FLUSH_CONCENTRATE_STRAWS_TIME_MS ( 0.5 * 60 * MS_PER_SECOND ) ///< Flush concentrate straws wait time in milliseconds. TODO original time is 3 minutes +#define FLUSH_CONCENTRATE_STRAWS_TIME_MS ( 3 * 60 * MS_PER_SECOND ) ///< Flush concentrate straws wait time in milliseconds. +#define ACID_PUMP_SPEED_ML_PER_MIN -30.0 ///< Acid pump speed in mL/min. +// The bicarb pump is 2% faster than the acid pump to create a flow from acid to bicarb line during flush +#define BICARB_PUMP_SPEED_ML_PER_MIN 30.6 ///< Bicarb pump speed in mL/min. // Flush and drain R1 and R2 state defines -#define RSRVRS_FULL_VOL_ML 1650.0 ///< Reservoirs 1 & 2 full volume in mL. TODo original value was 1900 -#define RSRVRS_PARTIAL_FILL_VOL_ML 500.0 ///< Reservoirs 1 & 2 partial volume in mL. +#define RSRVRS_FULL_VOL_ML 1800.0 ///< Reservoirs 1 & 2 full volume in mL. TODo original value was 1900 +#define RSRVRS_PARTIAL_FILL_VOL_ML 500.0 ///< Reservoirs 1 & 2 partial volume in mL. #define RSRVRS_FULL_STABLE_TIME_COUNT ( ( 4 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) ///< Reservoirs 1 & 2 full stable time in counts. -#define RSRVRS_FILL_UP_TIMEOUT_MS ( 5 * 60 * MS_PER_SECOND ) ///< Reservoirs 1 & 2 full fill up timeout in ms. TODO original value was 5 minutes +#define RSRVRS_FILL_UP_TIMEOUT_MS ( 5 * 60 * MS_PER_SECOND ) ///< Reservoirs 1 & 2 full fill up timeout in ms. #define RSRVRS_PARTIAL_FILL_TIMEOUT_MS ( 2 * 60 * MS_PER_SECOND ) ///< Reservoirs 1 & 2 partial fill up timeout in ms. -#define RSRVRS_DRAIN_TIMEOUT_MS ( 3 * 60 * MS_PER_SECOND ) ///< Reservoirs 1 & 2 drain timeout in ms. TODO original value was 2 minutes -#define FINAL_DRAIN_RO_PUMP_FLOW_LPM 0.6 ///< Final drain RO pump flow rate in L/min. This is used to flush the drain line during drain. +#define RSRVRS_DRAIN_TIMEOUT_MS ( 3 * 60 * MS_PER_SECOND ) ///< Reservoirs 1 & 2 drain timeout in ms. +#define FINAL_DRAIN_RO_PUMP_FLOW_LPM 0.6 ///< Final drain RO pump flow rate in L/min. This is used to flush the drain line during drain. // Flush drain line state defines -#define FLUSH_DRAIN_LINE_VOLUME_L 0.1 ///< Water volume to flush in liters. +#define FLUSH_DRAIN_LINE_VOLUME_L 0.1 ///< Water volume to flush in liters. #define FLUSH_DRAIN_LINE_TIMEOUT_MS ( 1 * 60 * MS_PER_SECOND ) ///< Flush drain lines timeout in milliseconds. // Flush circulation state defines @@ -78,6 +82,7 @@ static DG_FLUSH_STATE_T flushState = DG_FLUSH_STATE_START; ///< Current active flush state. static DG_FLUSH_STATE_T prevFlushState = DG_FLUSH_STATE_START; ///< Previous flush state. +static DG_FLUSH_UI_STATE_T flushUIState = FLUSH_UI_STATE_NOT_RUNNING; ///< Current UI flush state. static U32 rsrvrFillStableTimeCounter = 0; ///< Reservoirs fill stable time counter. static U32 overallFlushElapsedTimeStart = 0; ///< Overall flush mode elapsed time start. static U32 stateTimerStart = 0; ///< State timer start. @@ -126,6 +131,7 @@ // Initialize the variables flushState = DG_FLUSH_STATE_START; prevFlushState = DG_FLUSH_STATE_START; + flushUIState = FLUSH_UI_STATE_NOT_RUNNING; rsrvrFillStableTimeCounter = 0; overallFlushElapsedTimeStart = 0; isThisInitialDrain = TRUE; @@ -308,6 +314,7 @@ setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + flushUIState = FLUSH_UI_STATE_DRAIN_DEVICE; stateTimerStart = getMSTimerCount(); rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; state = DG_FLUSH_STATE_DRAIN_R1; @@ -409,10 +416,12 @@ #else setValveState( VPD, VALVE_STATE_OPEN_C_TO_NO ); #endif + setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); setValveState( VRC, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setROPumpTargetFlowRate( FINAL_DRAIN_RO_PUMP_FLOW_LPM, RO_PUMP_MAX_PRESSURE_PSI ); + flushUIState = FLUSH_UI_STATE_FLUSH_RECIRCULATION_PATH; state = DG_FLUSH_STATE_FLUSH_CIRCULATION_DRAIN_LINE; } @@ -474,7 +483,14 @@ if ( TRUE == didTimeout( stateTimerStart, FLUSH_DIALYSATE_WAIT_TIME_MS ) ) { - // TODO turn on the concentrate pumps + // Turn the pumps on in reverse + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, ACID_PUMP_SPEED_ML_PER_MIN ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, BICARB_PUMP_SPEED_ML_PER_MIN ); + + // Turn on the concentrate pumps + requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpsOn( CONCENTRATEPUMPS_CP2_BICARB ); + stateTimerStart = getMSTimerCount(); state = DG_FLUSH_STATE_FLUSH_CONCENTRATE_STRAWS; @@ -498,14 +514,18 @@ if ( TRUE == didTimeout( stateTimerStart, FLUSH_CONCENTRATE_STRAWS_TIME_MS ) ) { - // TODO turn off the concentrate pumps + // Done with flushing the concentrate pumps line + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP2_BICARB ); + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); stateTimerStart = getMSTimerCount(); rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; + flushUIState = FLUSH_UI_STATE_FLUSH_RESERVOIRS; state = DG_FLUSH_STATE_FLUSH_R1_TO_R2; } @@ -660,6 +680,7 @@ stateTimerStart = getMSTimerCount(); isThisInitialDrain = FALSE; rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + flushUIState = FLUSH_UI_STATE_DRAIN_RESERVOIRS; state = DG_FLUSH_STATE_DRAIN_R1; } // Check if reservoir 1 fill timed out @@ -710,7 +731,6 @@ #endif setValveState( VRC, VALVE_STATE_RECIRC_C_TO_NC ); setValveState( VDR, VALVE_STATE_RECIRC_C_TO_NC ); - setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); setROPumpTargetFlowRate( RO_PUMP_TARGET_FLOW_RATE_LPM, RO_PUMP_MAX_PRESSURE_PSI ); stateTimerStart = getMSTimerCount(); @@ -774,7 +794,7 @@ if ( TRUE == didTimeout( stateTimerStart, FLUSH_WITH_FRESH_WATER_WAIT_TIME_MS ) ) { deenergizeActuators(); - + flushUIState = FLUSH_UI_STATE_COMPLETE; state = DG_FLUSH_STATE_COMPLETE; } @@ -798,6 +818,8 @@ // deenergize all the actuators failFlushMode(); + flushUIState = FLUSH_UI_STATE_CANCEL_FLUSH; + return state; } @@ -841,6 +863,8 @@ // Start the drain pump setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + flushUIState = FLUSH_UI_STATE_CANCEL_FLUSH; + // Start the timer for drain timeout stateTimerStart = getMSTimerCount(); } @@ -1026,6 +1050,7 @@ data.overallElapsedTime = calcTimeSince( overallFlushElapsedTimeStart ); data.stateElapsedTime = calcTimeSince( stateTimerStart ); data.drainLineVolume = flushLinesVolumeL; + data.flushUIState = (U32)flushUIState; broadcastFlushData( &data ); Index: firmware/App/Modes/ModeHeatDisinfect.c =================================================================== diff -u -r2fff37fa585181917705645494549b5fd4a4d522 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision 2fff37fa585181917705645494549b5fd4a4d522) +++ firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -64,7 +64,9 @@ #define FLUSH_CICRCULATION_WAIT_TIME_MS ( 30 * MS_PER_SECOND ) ///< Flush/rinse circulation path wait time in milliseconds. TODO original time was 30 seconds #define MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C 50.0 ///< Maximum flush circulation temperature difference tolerance in C. TODO original difference was 3.0 degrees #define NUM_OF_TEMP_SENSORS_TO_AVG 4.0 ///< Number of temperature sensors to average to check the difference. -#define CONC_PUMPS_REVERSE_SPEED_ML_PER_MIN -30.0 ///< Concentrate pumps reverse speed in mL/min. +#define ACID_PUMP_SPEED_ML_PER_MIN -30.0 ///< Acid concentrate pump speed in mL/min. +// The bicarb pump is 2% faster than the acid pump to create a flow from acid to bicarb line during heat disinfect +#define BICARB_PUMP_SPEED_ML_PER_MIN 30.6 ///< Bicarb concentrate pump speed in mL/min. // Flush and drain R1 and R2 #define RSRVRS_FULL_VOL_ML 1750.0 ///< Reservoirs 1 & 2 full volume in mL. TODo original value was 1900 @@ -118,7 +120,8 @@ /// Heat disinfect status typedef enum Heat_disinfect_status { - HEAT_DISINFECT_IN_PROGRESS = 0, ///< Heat disinfect in progress. + HEAT_DISINFECT_HEAT_UP_IN_PROGRESS = 0, ///< Heat disinfect in progress. + HEAT_DISINFECT_DISINFECT_IN_PROGRESS, ///< Heat disinfect disinfect in progress. HEAT_DISINFECT_RSRVRS_LEAK_TIMEOUT, ///< Heat disinfect reservoirs leak timeout. HEAT_DISINFECT_HEAT_UP_TIMEOUT, ///< Heat disinfect heat up timeout. HEAT_DISINFECT_COMPLETE, ///< Heat disinfect complete. @@ -153,6 +156,7 @@ static U32 ROFCirculationTimer = 0; ///< RO filter circulation timer. static U32 ROFCoolingTimer = 0; ///< RO filter cooling timer. static BOOL hasROFCirculationBeenStarted = FALSE; ///< Flag to indicate the water in RO filter has been recirculated. +static U32 targetDisinfectTime = 0; ///< Target disinfect time. // ********** private function prototypes ********** @@ -196,8 +200,8 @@ * R1HeatDisinfectVol, R2HeatDisinfectVol, overallHeatDisinfectTimer, * cancellationMode, rsrvrFillStableTimeCounter, prevHeatDisinfectState * isPartialDisinfectInProgress, isDrainPumpOnInMixDrain, - * hasROFCirculationBeenStarted, ROFCirculationTimer, - * ROFCirculationCoolingCounter, concentratePumpsPrimeTimer + * hasROFCirculationBeenStarted, ROFCirculationTimer, targetDisinfectTime + * ROFCirculationCoolingCounter, concentratePumpsPrimeTimer, * @return none *************************************************************************/ void initHeatDisinfectMode( void ) @@ -222,6 +226,7 @@ ROFCoolingTimer = 0; hasROFCirculationBeenStarted = FALSE; concentratePumpsPrimeTimer = 0; + targetDisinfectTime = 0; } /*********************************************************************//** @@ -700,8 +705,8 @@ areTempSensorsInRange = TRUE; #ifndef IGNORE_CONC_PUMP_IN_HEAT_DISINFECT // Turn the pumps on in reverse - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, CONC_PUMPS_REVERSE_SPEED_ML_PER_MIN ); - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, CONC_PUMPS_REVERSE_SPEED_ML_PER_MIN ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, ACID_PUMP_SPEED_ML_PER_MIN ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, ACID_PUMP_SPEED_ML_PER_MIN ); // Turn on the concentrate pumps requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1_ACID ); @@ -719,7 +724,7 @@ rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; - // Done with priming the concentrate pumps line + // Done with flushing the concentrate pumps line requestConcentratePumpsOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpsOff( CONCENTRATEPUMPS_CP2_BICARB ); @@ -1122,6 +1127,10 @@ state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; break; + case HEAT_DISINFECT_DISINFECT_IN_PROGRESS: + heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_DISINFECT_RESERVOIR_1; + break; + case HEAT_DISINFECT_COMPLETE: requestConcentratePumpsOff( CONCENTRATEPUMPS_CP1_ACID ); @@ -1145,9 +1154,9 @@ state = DG_HEAT_DISINFECT_STATE_FILL_R2_WITH_HOT_WATER; break; - case HEAT_DISINFECT_IN_PROGRESS: + case HEAT_DISINFECT_HEAT_UP_IN_PROGRESS: default: - // Do nothing, heat disinfect is in progress + heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_HEAT_UP_WATER; break; } @@ -1221,6 +1230,7 @@ { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DISINFECT_R2_TO_R1; HEAT_DISINFECT_STATUS_T status = getHeatDisinfectStatus(); + heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_DISINFECT_RESERVOIR_2; switch ( status ) { @@ -1237,7 +1247,7 @@ state = DG_HEAT_DISINFECT_STATE_COOL_DOWN_HEATERS; break; - case HEAT_DISINFECT_IN_PROGRESS: + case HEAT_DISINFECT_HEAT_UP_IN_PROGRESS: default: // Do nothing heat disinfect is in progress. break; @@ -1628,8 +1638,8 @@ setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); #ifndef IGNORE_CONC_PUMP_IN_HEAT_DISINFECT // Turn the pumps on in reverse - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, CONC_PUMPS_REVERSE_SPEED_ML_PER_MIN ); - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, CONC_PUMPS_REVERSE_SPEED_ML_PER_MIN ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, ACID_PUMP_SPEED_ML_PER_MIN ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, ACID_PUMP_SPEED_ML_PER_MIN ); // Turn on the concentrate pumps requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1_ACID ); @@ -1964,7 +1974,7 @@ *************************************************************************/ static HEAT_DISINFECT_STATUS_T getHeatDisinfectStatus( void ) { - HEAT_DISINFECT_STATUS_T status = HEAT_DISINFECT_IN_PROGRESS; + HEAT_DISINFECT_STATUS_T status = HEAT_DISINFECT_HEAT_UP_IN_PROGRESS; F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); F32 ThdTemp = getTemperatureValue( TEMPSENSORS_HEAT_DISINFECT ); @@ -1984,7 +1994,7 @@ // If the volume is out of range and it has timed out, exit else if ( TRUE == didTimeout( rsrvrsVolMonitorTimer, RSRVRS_TARGET_VOL_OUT_TIMEOUT_MS ) ) { - areRsrvrsLeaking = FALSE; + areRsrvrsLeaking = FALSE; alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_LEAK_TIMEOUT; status = HEAT_DISINFECT_RSRVRS_LEAK_TIMEOUT; } @@ -2002,7 +2012,7 @@ // Keep reseting the disinfect timer so the elapsed time is always 0 until disinfect truly starts heatDisinfectTimer = getMSTimerCount(); isPartialDisinfectInProgress = FALSE; - heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_HEAT_UP_WATER; + targetDisinfectTime = 0; if ( TRUE == didTimeout( stateTimer, HEAT_DISINFECT_START_TEMP_TIMOUT_MS ) ) { @@ -2016,32 +2026,37 @@ // The temperature of the coldest spot is in range to start the disinfect timer heatDisinfectTimer = getMSTimerCount(); isPartialDisinfectInProgress = TRUE; + targetDisinfectTime = HEAT_DISINFECT_TIME_MS; + status = HEAT_DISINFECT_DISINFECT_IN_PROGRESS; - // Set the heat disinfect UI state + // In disinfect R1 to R2, concentrate pumps are also run if ( DG_HEAT_DISINFECT_STATE_DISINFECT_R1_TO_R2 == heatDisinfectState ) { - heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_DISINFECT_RESERVOIR_1; #ifndef IGNORE_CONC_PUMP_IN_HEAT_DISINFECT // Turn the pumps on in reverse - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, CONC_PUMPS_REVERSE_SPEED_ML_PER_MIN ); - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, CONC_PUMPS_REVERSE_SPEED_ML_PER_MIN ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, ACID_PUMP_SPEED_ML_PER_MIN ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, BICARB_PUMP_SPEED_ML_PER_MIN ); // During R1 to R2 disinfect, the concentrate pumps turn on requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpsOn( CONCENTRATEPUMPS_CP2_BICARB ); #endif } - else - { - heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_DISINFECT_RESERVOIR_2; - } } + else if ( ( TRUE == isPartialDisinfectInProgress ) && ( ThdTemp > HEAT_DISINFECT_START_TEMPERATURE_C ) ) + { + status = HEAT_DISINFECT_DISINFECT_IN_PROGRESS; + } // If heat disinfect temperature has been reached, check if this stage of heat disinfect is done if ( ( TRUE == isPartialDisinfectInProgress ) && ( TRUE == didTimeout( heatDisinfectTimer, HEAT_DISINFECT_TIME_MS ) ) ) { // Done with this stage of heat disnfect. Reset the variables - status = HEAT_DISINFECT_COMPLETE; + // Target disinfect time is the time that is published to the UI and when there is + // no disinfect count down, this variable is set to 0. When the target time is 0, the UI + // hides the timer on the UI disinfect screen + targetDisinfectTime = 0; + status = HEAT_DISINFECT_COMPLETE; isPartialDisinfectInProgress = FALSE; } @@ -2073,8 +2088,12 @@ if ( ( DG_HEAT_DISINFECT_STATE_DISINFECT_R1_TO_R2 == heatDisinfectState ) || ( DG_HEAT_DISINFECT_STATE_DISINFECT_R2_TO_R1 == heatDisinfectState ) ) { - uiData.heatDisinfectTargetTime = HEAT_DISINFECT_TIME_MS; - uiData.heatDisinfectCountdownTime = HEAT_DISINFECT_TIME_MS - calcTimeSince( heatDisinfectTimer ); + // If the disinfect target time is 0, meaning the actual disinfect has not started, set the count down to 0, otherwise, publish + // the actual value + U32 countDown = ( HEAT_DISINFECT_TIME_MS == targetDisinfectTime ? ( targetDisinfectTime - calcTimeSince( heatDisinfectTimer) ) : 0 ); + + uiData.heatDisinfectTargetTime = targetDisinfectTime; + uiData.heatDisinfectCountdownTime = countDown / 1000; // The count down is converted into seconds since the UI does not work with milliseconds data.R1FillLevel = R1HeatDisinfectVol; data.R2FillLevel = R2HeatDisinfectVol; } Index: firmware/App/Modes/ModeInitPOST.c =================================================================== diff -u -r2fff37fa585181917705645494549b5fd4a4d522 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Modes/ModeInitPOST.c (.../ModeInitPOST.c) (revision 2fff37fa585181917705645494549b5fd4a4d522) +++ firmware/App/Modes/ModeInitPOST.c (.../ModeInitPOST.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -16,6 +16,8 @@ ***************************************************************************/ #include "Accel.h" +#include "ConcentratePumps.h" +#include "ConductivitySensors.h" #include "CPLD.h" #include "DrainPump.h" #include "Fans.h" @@ -26,6 +28,8 @@ #include "NVDataMgmt.h" #include "OperationModes.h" #include "Pressures.h" +#include "Reservoirs.h" +#include "ROPump.h" #include "RTC.h" #include "SystemCommMessages.h" #include "TemperatureSensors.h" @@ -98,10 +102,6 @@ #ifdef SKIP_POST postState = DG_POST_STATE_COMPLETED; #endif -#ifdef DONT_SKIP_NV_POST - // Only run NVDataMgmt POST - postState = DG_POST_STATE_NVDATAMGMT; -#endif break; case DG_POST_STATE_FW_COMPATIBILITY: @@ -129,14 +129,12 @@ postState = handlePOSTStatus( testStatus ); break; + // NOTE: all the actuators and sensors must execute their POST after NVDataMgmt + // NVDataMgmt must load all the calibration data into RAM so the actuators + // can query their corresponding calibration values successfully case DG_POST_STATE_TEMPERATURE_SENSORS: -#ifdef DONT_SKIP_NV_POST - // Skip the rest of the POSTs - postState = DG_POST_STATE_COMPLETED; -#else testStatus = execTemperatureSensorsSelfTest(); postState = handlePOSTStatus( testStatus ); -#endif break; case DG_POST_STATE_ACCELEROMETER: @@ -153,29 +151,46 @@ postState = handlePOSTStatus( testStatus ); break; -#ifdef _VECTORCAST_ - case DG_POST_DRAIN_PUMP: + case DG_POST_STATE_RO_PUMP: + testStatus = execROPumpSelfTest(); + postState = handlePOSTStatus( testStatus ); + break; + + case DG_POST_STATE_DRAIN_PUMP: testStatus = execDrainPumpSelfTest(); postState = handlePOSTStatus( testStatus ); break; -// To be able to run integration test in VectorCAST. -// Not all the fans and thermistors have been implemented so POST might fail - case DG_POST_STATE_THERMISTORS: - testStatus = execThermistorsSelfTest(); + + case DG_POST_STATE_CONCENTRATE_PUMPS: + testStatus = execConcenratePumpsSelfTest(); postState = handlePOSTStatus( testStatus ); break; - case DG_POST_STATE_FANS: - testStatus = execFansSelfTest(); + case DG_POST_STATE_CONDUCTIVITY_SENSORS: + testStatus = execConductivitySensorsSelfTest(); postState = handlePOSTStatus( testStatus ); break; + case DG_POST_STATE_RESERVOIRS: + testStatus = execReservoirsSelfTest(); + postState = handlePOSTStatus( testStatus ); + break; + case DG_POST_STATE_UV_REACTORS: testStatus = execUVReactorsSelfTest(); postState = handlePOSTStatus( testStatus ); break; -#endif + case DG_POST_STATE_THERMISTORS: + testStatus = execThermistorsSelfTest(); + postState = handlePOSTStatus( testStatus ); + break; + + case DG_POST_STATE_FANS: + testStatus = execFansSelfTest(); + postState = handlePOSTStatus( testStatus ); + break; + case DG_POST_STATE_WATCHDOG: testStatus = execWatchdogTest(); postState = handlePOSTStatus( testStatus ); @@ -184,7 +199,8 @@ // Should be last POST (and last POST test must be a test that completes in a single call) case DG_POST_STATE_LOAD_CELL: testStatus = execLoadCellsSelfTest(); - handlePOSTStatus( testStatus ); // ignoring return value because last test + handlePOSTStatus( testStatus ); + // Ignoring return value because last test if ( TRUE == tempPOSTPassed ) { postState = DG_POST_STATE_COMPLETED; Index: firmware/App/Modes/ModeRecirculate.c =================================================================== diff -u -r2fff37fa585181917705645494549b5fd4a4d522 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Modes/ModeRecirculate.c (.../ModeRecirculate.c) (revision 2fff37fa585181917705645494549b5fd4a4d522) +++ firmware/App/Modes/ModeRecirculate.c (.../ModeRecirculate.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -24,6 +24,7 @@ #include "FPGA.h" #include "Heaters.h" #include "ModeRecirculate.h" +#include "NVDataMgmt.h" #include "OperationModes.h" #include "Pressures.h" #include "ROPump.h" @@ -44,12 +45,11 @@ #define TARGET_RO_FLOW_RATE_L 0.3 ///< Target flow rate for RO pump. #define TARGET_FLUSH_LINES_RO_FLOW_RATE_L 0.6 ///< Target flow rate for RO pump. -#define FLUSH_LINES_VOLUME_L 0.01 ///< Water volume (in Liters) to flush when starting re-circulate mode. // ********** private data ********** -static DG_RECIRCULATE_MODE_STATE_T recircState; ///< Currently active re-circulation state. -static F32 flushLinesVolumeL = 0.0; ///< Volume of water pumped by RO pump during flush lines state. +static DG_RECIRCULATE_MODE_STATE_T recircState; ///< Currently active re-circulation state. +static F32 flushLinesVolumeL = 0.0; ///< Volume of water pumped by RO pump during flush lines state. // ********** private function prototypes ********** @@ -66,7 +66,7 @@ *************************************************************************/ void initRecirculateMode( void ) { - recircState = DG_RECIRCULATE_MODE_STATE_START; + recircState = DG_RECIRCULATE_MODE_STATE_START; flushLinesVolumeL = 0.0; } @@ -87,6 +87,8 @@ setValveState( VPI, VALVE_STATE_OPEN ); #ifndef V_2_SYSTEM setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); + setValveState( VRD1, VALVE_STATE_CLOSED ); + setValveState( VRD2, VALVE_STATE_CLOSED ); #endif setValveState( VRC, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); @@ -117,9 +119,11 @@ /*********************************************************************//** * @brief - * The execRecirculateMode function executes the re-circulate mode state machine. + * The execRecirculateMode function executes the re-circulate mode state + * machine. * @details Inputs: recircState - * @details Outputs: Check water quality, re-circulate mode state machine executed + * @details Outputs: Check water quality, re-circulate mode state machine + * executed * @return current state *************************************************************************/ U32 execRecirculateMode( void ) @@ -160,6 +164,35 @@ /*********************************************************************//** * @brief + * The requestDGStop function handles an HD request to stop (return to standby mode). + * @details Inputs: none + * @details Outputs: DG standby mode requested + * @return TRUE if request accepted, FALSE if not. + *************************************************************************/ +BOOL requestDGStop( void ) +{ + BOOL result = TRUE; + + requestNewOperationMode( DG_MODE_STAN ); + + return result; +} + +/*********************************************************************//** + * @brief + * The getCurrentRecirculateState function returns the current state of the + * re-circulate mode. + * @details Inputs: recircState + * @details Outputs: none + * @return the current state of re-circulate mode + *************************************************************************/ +DG_RECIRCULATE_MODE_STATE_T getCurrentRecirculateState( void ) +{ + return recircState; +} + +/*********************************************************************//** + * @brief * The handleFlushLinesState function executes the flush lines state of the * re-circulate mode state machine. * @details Inputs: none @@ -172,15 +205,13 @@ F32 waterFlowRate = getMeasuredROFlowRate(); F32 waterVolume = ( ( waterFlowRate / SEC_PER_MIN ) / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); - // integrate volume of water moved through line + // Integrate volume of water moved through line flushLinesVolumeL += waterVolume; - // when enough water volume has flowed to flush the lines, transition to re-circ state - if ( flushLinesVolumeL >= FLUSH_LINES_VOLUME_L ) + // When enough water volume has flowed to flush the lines, transition to re-circ state + if ( flushLinesVolumeL >= getRecirculationDrainVol() ) { -#ifndef SKIP_RECIRC setValveState( VDR, VALVE_STATE_RECIRC_C_TO_NC ); -#endif setROPumpTargetFlowRate( TARGET_RO_FLOW_RATE_L, TARGET_RO_PRESSURE_PSI ); result = DG_RECIRCULATE_MODE_STATE_RECIRC_WATER; } @@ -218,33 +249,4 @@ return result; } -/*********************************************************************//** - * @brief - * The requestDGStop function handles an HD request to stop (return to standby mode). - * @details Inputs: none - * @details Outputs: DG standby mode requested - * @return TRUE if request accepted, FALSE if not. - *************************************************************************/ -BOOL requestDGStop( void ) -{ - BOOL result = TRUE; - - requestNewOperationMode( DG_MODE_STAN ); - - return result; -} - -/*********************************************************************//** - * @brief - * The getCurrentRecirculateState function returns the current state of the - * re-circulate mode. - * @details Inputs: recircState - * @details Outputs: none - * @return the current state of re-circulate mode - *************************************************************************/ -DG_RECIRCULATE_MODE_STATE_T getCurrentRecirculateState( void ) -{ - return recircState; -} - /**@}*/ Index: firmware/App/Services/AlarmMgmt.h =================================================================== diff -u -r0d46714bc22f87f7027c6e82ad59cef1a01ff69b -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Services/AlarmMgmt.h (.../AlarmMgmt.h) (revision 0d46714bc22f87f7027c6e82ad59cef1a01ff69b) +++ firmware/App/Services/AlarmMgmt.h (.../AlarmMgmt.h) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -75,7 +75,7 @@ ALARM_DATAS_T data; ///< The alarm data of specified type } ALARM_DATA_T; #pragma pack(pop) - + // ********** public function prototypes ********** void initAlarmMgmt( void ); Index: firmware/App/Services/AlarmMgmtSWFaults.h =================================================================== diff -u -r8b0c842f94e3c94cecf62f0f913e429def8e5efa -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision 8b0c842f94e3c94cecf62f0f913e429def8e5efa) +++ firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -125,6 +125,8 @@ SW_FAULT_ID_INVALID_MONITORED_VOLTAGE_ID, SW_FAULT_ID_INVALID_LOAD_CELL_ID, // 95 SW_FAULT_ID_DG_CHEM_DISINFECT_INVALID_EXEC_STATE, + SW_FAULT_ID_DG_INVALID_SWITCH_ID, + SW_FAULT_ID_DG_PRESSURES_INVALID_SELF_TEST_STATE, NUM_OF_SW_FAULT_IDS } SW_FAULT_ID_T; Index: firmware/App/Services/FPGA.c =================================================================== diff -u -r5a36a768d11cc597a36b894c1fb3a5e5590130f1 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Services/FPGA.c (.../FPGA.c) (revision 5a36a768d11cc597a36b894c1fb3a5e5590130f1) +++ firmware/App/Services/FPGA.c (.../FPGA.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -18,12 +18,14 @@ #include // for memset(), memcpy() +#include "gio.h" #include "sci.h" #include "sys_dma.h" -#include "FPGA.h" #include "Comm.h" #include "Compatible.h" +#include "FPGA.h" +#include "PersistentAlarm.h" #include "SystemCommMessages.h" #include "Utilities.h" @@ -79,6 +81,12 @@ #define FPGA_FLUIDLEAK_STATE_MASK 0x0004 ///< Bit mask for fluid leak detector. +#define FLUID_DOOR_SWITCH_MASK 0x08 ///< Fluid door switch bit mask. +#define DIALYSATE_CAP_SWITCH_MASK 0x10 ///< Dialysate cap switch bit mask. +#define CONCENTRATE_CAP_SWITCH_MASK 0x1A ///< Concentrate cap switch bit mask. +#define FPGA_POWER_OUT_TIMEOUT_MS ( 2 * MS_PER_SECOND ) ///< FPGA power out timeout in milliseconds. +#define FPGA_GPIO_POWER_STATUS_PIN 7 ///< FPGA GPIO power status pin + // FPGA header struct. #pragma pack(push,1) typedef struct @@ -245,6 +253,7 @@ static void startDMAReceiptOfReadResp( void ); static void consumeUnexpectedData( void ); +static void monitorFPGAPowerStatus( void ); /*********************************************************************//** * @brief @@ -358,6 +367,9 @@ // there shouldn't be any data pending yet consumeUnexpectedData(); + + // Initialize the persistent alarm for FPGA power out + initPersistentAlarm( ALARM_ID_DG_FPGA_POWER_OUT_TIMEOUT, FPGA_POWER_OUT_TIMEOUT_MS, FPGA_POWER_OUT_TIMEOUT_MS ); } /*********************************************************************//** @@ -480,6 +492,9 @@ // reset comm flags after processing incoming responses resetFPGACommFlags(); + + // Check the FPGA power status + monitorFPGAPowerStatus(); } /*********************************************************************//** @@ -924,6 +939,22 @@ /*********************************************************************//** * @brief + * The monitorFPGAPowerStatus function monitors the status of the FPGA power source. + * @details Inputs: none + * @details Outputs: none + * @return none + *************************************************************************/ +static void monitorFPGAPowerStatus( void ) +{ + // If the GIO bit returned a 0 it mean the power is out, otherwise the power is not out + BOOL isPowerOut = ( TRUE == (BOOL)gioGetBit( gioPORTA, FPGA_GPIO_POWER_STATUS_PIN ) ? FALSE : TRUE ); + + // TODO check to make sure alarm is not raised when the power is good + checkPersistentAlarm( ALARM_ID_DG_FPGA_POWER_OUT_TIMEOUT, isPowerOut, 0, FPGA_POWER_OUT_TIMEOUT_MS ); +} + +/*********************************************************************//** + * @brief * The setFPGAValveStates function sets the DG valve states with a 16-bit * set of states - one bit per valve, with a 1 meaning "energized" and a 0 * meaning "de-energized". The bit positions for these bit states are as follows: @@ -1843,4 +1874,43 @@ return ( 0 == noFluidLeakDetected ? FALSE : TRUE ); } +/*********************************************************************//** + * @brief + * The getFPGAGFluidDoorStatus function gets the FPGA fluid door status + * bit. + * @details Inputs: fpgaSensorReadings + * @details Outputs: none + * @return fluid door status bit + *************************************************************************/ +U08 getFPGAGFluidDoorStatus( void ) +{ + return ( fpgaSensorReadings.fpgaGPIO & FLUID_DOOR_SWITCH_MASK ); +} + +/*********************************************************************//** + * @brief + * The getFPGADialysateCapStatus function gets the FPGA dialysate cap status + * bit. + * @details Inputs: fpgaSensorReadings + * @details Outputs: none + * @return dialysate cap status bit + *************************************************************************/ +U08 getFPGADialysateCapStatus( void ) +{ + return ( fpgaSensorReadings.fpgaGPIO & DIALYSATE_CAP_SWITCH_MASK ); +} + +/*********************************************************************//** + * @brief + * The getFPGAConcentrateCapStatus function gets the FPGA concentrate cap + * status bit. + * @details Inputs: fpgaSensorReadings + * @details Outputs: none + * @return concentrate cap status bit + *************************************************************************/ +U08 getFPGAConcentrateCapStatus( void ) +{ + return ( fpgaSensorReadings.fpgaGPIO & CONCENTRATE_CAP_SWITCH_MASK ); +} + /**@}*/ Index: firmware/App/Services/FPGA.h =================================================================== diff -u -r5a36a768d11cc597a36b894c1fb3a5e5590130f1 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Services/FPGA.h (.../FPGA.h) (revision 5a36a768d11cc597a36b894c1fb3a5e5590130f1) +++ firmware/App/Services/FPGA.h (.../FPGA.h) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -132,9 +132,13 @@ U08 getFPGAADC2ReadCount( void ); U08 getFPGAADC2ErrorCount( void ); +BOOL noFPGAFluidLeakDetected( void ); + U16 getFPGATimerCount( void ); -BOOL noFPGAFluidLeakDetected( void); +U08 getFPGAGFluidDoorStatus( void ); +U08 getFPGADialysateCapStatus( void ); +U08 getFPGAConcentrateCapStatus( void ); /**@}*/ Index: firmware/App/Services/Reservoirs.c =================================================================== diff -u -r71a23ba316740818ac4e1f47b13858e570184652 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision 71a23ba316740818ac4e1f47b13858e570184652) +++ firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -36,7 +36,6 @@ #define MIN_RESERVOIR_VOLUME_ML 0 ///< Minimum reservoir volume in mL. #define MAX_RESERVOIR_VOLUME_ML 2000 ///< Maximum reservoir volume in mL. #define DEFAULT_FILL_VOLUME_ML 1700 ///< Default fill volume for treatment in mL. -#define DISINFECT_FILL_VOLUME_ML 2400 ///< Fill volume for disinfection in mL. #define MAX_FILL_VOLUME_ML MAX_RESERVOIR_VOLUME_ML ///< Maximum fill volume in mL. #define DEFAULT_DRAIN_VOLUME_ML 0 ///< Default drain volume in mL. #define MAX_DRAIN_VOLUME_ML MAX_RESERVOIR_VOLUME_ML ///< Maximum drain volume in mL. @@ -63,13 +62,15 @@ static F32 reservoirLowestWeight[ NUM_OF_DG_RESERVOIRS ] = { MAX_RESERVOIR_WEIGHT, MAX_RESERVOIR_WEIGHT }; static U32 reservoirWeightUnchangeStartTime[ NUM_OF_DG_RESERVOIRS ] = { 0, 0 }; ///< The reservoirs' weight start time when weight stop decreasing. static BOOL tareLoadCellRequest; ///< Flag indicates if load cell tare has been requested by HD. +static DG_RESERVOIR_VOLUME_RECORD_T reservoirsCalRecord; ///< DG reservoirs non-volatile record. // ********** private function prototypes ********** static DG_RESERVOIR_ID_T getActiveReservoir( void ); static U32 getReservoirFillVolumeTargetMl( void ); -static U32 getReservoirDrainVolumeTargetMl( void ); +static U32 getReservoirDrainVolumeTargetMl( void ); +static BOOL processCalibrationData( void ); /*********************************************************************//** * @brief @@ -93,7 +94,14 @@ * @return none *************************************************************************/ void execReservoirs( void ) -{ +{ + // Check if a new calibration is available + if ( TRUE == isNewCalibrationRecordAvailable() ) + { + // Get the new calibration data and check its validity + processCalibrationData(); + } + // publish active reservoir, fill/drain volume targets at 1 Hz. if ( ++reservoirDataPublicationTimerCounter >= RESERVOIR_DATA_PUB_INTERVAL ) { @@ -103,6 +111,31 @@ broadcastReservoirData( actRes, filVol, drnVol ); reservoirDataPublicationTimerCounter = 0; } +} + +/*********************************************************************//** + * @brief + * The execDrainPumpSelfTest function executes the drain pump's self-test. + * @details Inputs: none + * @details Outputs: none + * @return PressuresSelfTestResult (SELF_TEST_STATUS_T) + *************************************************************************/ +SELF_TEST_STATUS_T execReservoirsSelfTest( void ) +{ + SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; + + BOOL calStatus = processCalibrationData(); + + if ( TRUE == calStatus ) + { + result = SELF_TEST_STATUS_PASSED; + } + else + { + result = SELF_TEST_STATUS_FAILED; + } + + return result; } /*********************************************************************//** @@ -232,8 +265,7 @@ cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_NONE; // fill command only valid in re-circulate mode - if ( ( DG_MODE_CIRC == getCurrentOperationMode() ) && - ( DG_RECIRCULATE_MODE_STATE_RECIRC_WATER == getCurrentRecirculateState() ) ) + if ( ( DG_MODE_CIRC == getCurrentOperationMode() ) && ( DG_RECIRCULATE_MODE_STATE_RECIRC_WATER == getCurrentRecirculateState() ) ) { // validate parameters if ( fillToVolMl < MAX_FILL_VOLUME_ML ) @@ -416,6 +448,19 @@ /*********************************************************************//** * @brief + * The getReservoirsCalRecord function returns the reservoirs' calibration + * record. + * @details Inputs: reservoirsCalRecord + * @details Outputs: none + * @return reservoirs' calibration record + *************************************************************************/ +DG_RESERVOIR_VOLUME_RECORD_T getReservoirsCalRecord( void ) +{ + return reservoirsCalRecord; +} + +/*********************************************************************//** + * @brief * The hasTargetFillVolumeReached function checks if the target fill volume * for specific reservoir has been reached. * @details Inputs: fillVolumeTargetMl @@ -566,7 +611,47 @@ return result; } +/*********************************************************************//** + * @brief + * The processCalibrationData function gets the calibration data and makes + * sure it is valid by checking the calibration date. The calibration date + * should not be 0. + * @details Inputs: none + * @details Outputs: reservoirsCalRecord + * @return TRUE if the calibration record is valid, otherwise FALSE + *************************************************************************/ +static BOOL processCalibrationData( void ) +{ + BOOL status = TRUE; + U32 reservoir; + // Get the calibration record from NVDataMgmt + DG_RESERVOIR_VOLUME_RECORD_T calData = getDGReservoirsVolumeRecord(); + + for ( reservoir = 0; reservoir < NUM_OF_CAL_DATA_RSRVRS; reservoir++ ) + { +#ifndef SKIP_CAL_CHECK + // Check if the calibration data that was received from NVDataMgmt is legitimate + // The calibration date item should not be zero. If the calibration date is 0, + // then the data is not stored in the NV memory or it was corrupted. + if ( 0 == calData.reservoir[ reservoir ].calibrationTime ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_RESERVOIRS_INVALID_CAL_RECORD, (U32)reservoir ); + status = FALSE; + } +#endif + + // The calibration data was valid, update the local copy + reservoirsCalRecord.reservoir[ reservoir ].maxResidualFluid = calData.reservoir[ reservoir ].maxResidualFluid; + reservoirsCalRecord.reservoir[ reservoir ].normalFillVolume = calData.reservoir[ reservoir ].normalFillVolume; + reservoirsCalRecord.reservoir[ reservoir ].rsrvrUnfilledWeight = calData.reservoir[ reservoir ].rsrvrUnfilledWeight; + reservoirsCalRecord.reservoir[ reservoir ].rsrvrVolume = calData.reservoir[ reservoir ].rsrvrVolume; + } + + return status; +} + + /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ Index: firmware/App/Services/SystemComm.c =================================================================== diff -u -r2fff37fa585181917705645494549b5fd4a4d522 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Services/SystemComm.c (.../SystemComm.c) (revision 2fff37fa585181917705645494549b5fd4a4d522) +++ firmware/App/Services/SystemComm.c (.../SystemComm.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -1007,6 +1007,7 @@ case MSG_ID_REQUEST_FW_VERSIONS: handleFWVersionCmd( message ); + handleDGSerialNumberRequest( message ); break; case MSG_ID_DG_SWITCH_RESERVOIR_CMD: @@ -1053,6 +1054,10 @@ handleUIClockSyncRequest( message ); break; + case MSG_ID_UI_REQUEST_SERVICE_INFO: + handleDGServiceScheduleRequest( message ); + break; + // NOTE: This case must be last case MSG_ID_DG_TESTER_LOGIN_REQUEST: handleTesterLogInRequest( message ); @@ -1172,6 +1177,14 @@ handleSetDrainPumpDeltaPressureOverrideRequest( message ); break; + case MSG_ID_DG_SWITCHES_STATUS_OVERRIDE: + handleSetSwitchesStatusOverrideRequest( message ); + break; + + case MSG_ID_DG_SWITCHES_PUBLISH_INTERVAL_OVERRIDE: + handleTestSwitchesPublishIntervalOverrideRequest( message ); + break; + case MSG_ID_DG_SOFTWARE_RESET_REQUEST: handleDGSoftwareResetRequest( message ); break; Index: firmware/App/Services/SystemCommMessages.c =================================================================== diff -u -r2fff37fa585181917705645494549b5fd4a4d522 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Services/SystemCommMessages.c (.../SystemCommMessages.c) (revision 2fff37fa585181917705645494549b5fd4a4d522) +++ firmware/App/Services/SystemCommMessages.c (.../SystemCommMessages.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -22,10 +22,8 @@ #include "Accel.h" #include "ConcentratePumps.h" #include "ConductivitySensors.h" -#include "DrainPump.h" #include "FPGA.h" #include "Heaters.h" -#include "LoadCell.h" #include "ModeFlush.h" #include "ModeStandby.h" #include "ModeRecirculate.h" @@ -34,8 +32,8 @@ #include "OperationModes.h" #include "Pressures.h" #include "Reservoirs.h" -#include "ROPump.h" #include "RTC.h" +#include "Switches.h" #include "SystemComm.h" #include "SafetyShutdown.h" #include "SystemCommMessages.h" @@ -385,31 +383,22 @@ * The broadcastLoadCellData function sends out load cell data. * @details Inputs: none * @details Outputs: load cell data msg constructed and queued - * @param loadCellA1 load cell A 1 data in grams - * @param loadCellA2 load cell A 2 data in grams - * @param loadCellB1 load cell B 1 data in grams - * @param loadCellB2 load cell B 2 data in grams + * @param loadCell which is the loadcells data structure pointer * @return TRUE if msg successfully queued for transmit, FALSE if not *************************************************************************/ -BOOL broadcastLoadCellData( F32 loadCellA1, F32 loadCellA2, F32 loadCellB1, F32 loadCellB2 ) +BOOL broadcastLoadCellData( LOAD_CELL_DATA_T *loadCell ) { BOOL result; MESSAGE_T msg; U08 *payloadPtr = msg.payload; - LOAD_CELL_DATA_T payload; // create a message record blankMessage( &msg ); msg.hdr.msgID = MSG_ID_LOAD_CELL_READINGS; msg.hdr.payloadLen = sizeof( LOAD_CELL_DATA_T ); - payload.loadCellA1inGram = loadCellA1; - payload.loadCellA2inGram = loadCellA2; - payload.loadCellB1inGram = loadCellB1; - payload.loadCellB2inGram = loadCellB2; + memcpy( payloadPtr, loadCell, sizeof( LOAD_CELL_DATA_T ) ); - memcpy( payloadPtr, &payload, sizeof( LOAD_CELL_DATA_T ) ); - // serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer result = serializeMessage( msg, COMM_BUFFER_OUT_CAN_DG_BROADCAST, ACK_NOT_REQUIRED ); @@ -845,6 +834,67 @@ /*********************************************************************//** * @brief + * The handleDGSerialNumberRequest function handles a request for DG serial + * number request. + * @details Inputs: none + * @details Outputs: message handled, response constructed and queued for transmit. + * @param message a pointer to the message to handle. + * @return none + *************************************************************************/ +void handleDGSerialNumberRequest( MESSAGE_T *message ) +{ + MESSAGE_T msg; + DG_SYSTEM_RECORD_T system = getDGSystemRecord(); + U08 *payloadPtr = msg.payload; + + // Create a message record + blankMessage( &msg ); + msg.hdr.msgID = MSG_ID_DG_SERIAL_NUMBER; + + // Add 1 byte for null terminator + msg.hdr.payloadLen = MAX_TOP_LEVEL_SN_CHARS + 1; + + // Fill message payload + memcpy( payloadPtr, &system.topLevelSN, MAX_TOP_LEVEL_SN_CHARS ); + payloadPtr += MAX_TOP_LEVEL_SN_CHARS; + *payloadPtr = 0; + + // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer + serializeMessage( msg, COMM_BUFFER_OUT_CAN_DG_2_UI, ACK_REQUIRED ); +} + +/*********************************************************************//** + * @brief + * The handleDGServiceScheduleRequest function handles a request for DG + * service information. + * @details Inputs: none + * @details Outputs: message handled, response constructed and queued for + * transmit. + * @param message a pointer to the message to handle. + * @return none + *************************************************************************/ +void handleDGServiceScheduleRequest( MESSAGE_T *message ) +{ + MESSAGE_T msg; + DG_SERVICE_RECORD_T payload = getDGServiceRecord(); + U08 *payloadPtr = msg.payload; + + // Create a message record + blankMessage( &msg ); + msg.hdr.msgID = MSG_ID_DG_SERVICE_SCHEDULE_DATA; + msg.hdr.payloadLen = sizeof( U32 ) + sizeof( U32 ); + + // Fill message payload + memcpy( payloadPtr, &payload.lastServiceEpochDate, sizeof( U32 ) ); + payloadPtr += sizeof( U32 ); + memcpy( payloadPtr, &payload.serviceIntervalSeconds, sizeof( U32 ) ); + + // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer + serializeMessage( msg, COMM_BUFFER_OUT_CAN_DG_2_UI, ACK_REQUIRED ); +} + +/*********************************************************************//** + * @brief * The broadcastConductivityData function sends out conductivity data. * @details Inputs: none * @details Outputs: conductivity data message constructed and queued @@ -1164,6 +1214,33 @@ /*********************************************************************//** * @brief + * The broadcastSwitchesData function sends out switches data. + * @details Inputs: none + * @details Outputs: switches data msg constructed and queued + * @param switchesData which is switches msg constructed and queued + * @return TRUE if msg successfully queued for transmit, FALSE if not + *************************************************************************/ +BOOL broadcastSwitchesData( SWITCHES_DATA_T *switchesData ) +{ + BOOL result; + MESSAGE_T msg; + U08 *payloadPtr = msg.payload; + + // create a message record + blankMessage( &msg ); + msg.hdr.msgID = MSG_ID_DG_SWITCHES_DATA; + msg.hdr.payloadLen = sizeof( SWITCHES_DATA_T ); + + memcpy( payloadPtr, switchesData, sizeof( SWITCHES_DATA_T ) ); + + // serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer + result = serializeMessage( msg, COMM_BUFFER_OUT_CAN_DG_BROADCAST, ACK_NOT_REQUIRED ); + + return result; +} + +/*********************************************************************//** + * @brief * The sendDGSystemRecord function sends out the DG system record. * @details Inputs: none * @details Outputs: DG system record msg constructed and queued @@ -2274,6 +2351,70 @@ sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); } +/************************************************************************* + * @brief + * The handleSetSwitchesStatusOverrideRequest function handles a + * request to override the status of a switch in DG. + * @details Inputs: none + * @details Outputs: message handled + * @param message a pointer to the message to handle + * @return none + *************************************************************************/ +void handleSetSwitchesStatusOverrideRequest( MESSAGE_T *message ) +{ + TEST_OVERRIDE_ARRAY_PAYLOAD_T payload; + BOOL result = FALSE; + + // verify payload length + if ( sizeof( TEST_OVERRIDE_ARRAY_PAYLOAD_T ) == message->hdr.payloadLen ) + { + memcpy( &payload, message->payload, sizeof( TEST_OVERRIDE_ARRAY_PAYLOAD_T ) ); + if ( FALSE == payload.reset ) + { + result = testSetSwitchesStatusOverride( payload.index, payload.state.u32 ); + } + else + { + result = testResetSwitchesStatusOverride( payload.index ); + } + } + + // respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); +} + +/************************************************************************* + * @brief + * The handleTestSwitchesPublishIntervalOverrideRequest function handles a + * request to override the the switches data publish interval. + * @details Inputs: none + * @details Outputs: message handled + * @param message a pointer to the message to handle + * @return none + *************************************************************************/ +void handleTestSwitchesPublishIntervalOverrideRequest( MESSAGE_T *message ) +{ + TEST_OVERRIDE_PAYLOAD_T payload; + BOOL result = FALSE; + + // verify payload length + if ( sizeof( TEST_OVERRIDE_PAYLOAD_T ) == message->hdr.payloadLen ) + { + memcpy( &payload, message->payload, sizeof( TEST_OVERRIDE_PAYLOAD_T ) ); + if ( FALSE == payload.reset ) + { + result = testSetSwitchesDataPublishIntervalOverride( payload.state.u32 ); + } + else + { + result = testResetSwitchesDataPublishIntervalOverrid(); + } + } + + // respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); +} + /*********************************************************************//** * @brief * The handleTestDGAccelOverrideRequest function handles a request to Index: firmware/App/Services/SystemCommMessages.h =================================================================== diff -u -r2fff37fa585181917705645494549b5fd4a4d522 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Services/SystemCommMessages.h (.../SystemCommMessages.h) (revision 2fff37fa585181917705645494549b5fd4a4d522) +++ firmware/App/Services/SystemCommMessages.h (.../SystemCommMessages.h) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -23,13 +23,15 @@ #include "Fans.h" #include "FluidLeak.h" #include "Heaters.h" +#include "LoadCell.h" #include "ModeChemicalDisinfect.h" #include "ModeFlush.h" #include "ModeHeatDisinfect.h" #include "MsgQueues.h" #include "NVDataMgmt.h" #include "Reservoirs.h" #include "ROPump.h" +#include "Switches.h" #include "TemperatureSensors.h" #include "Thermistors.h" #include "UVReactors.h" @@ -69,7 +71,7 @@ BOOL broadcastDGOperationMode( U32 mode, U32 subMode ); // MSG_ID_LOAD_CELL_READINGS -BOOL broadcastLoadCellData( F32 loadCellA1, F32 loadCellA2, F32 loadCellB1, F32 loadCellB2 ); +BOOL broadcastLoadCellData( LOAD_CELL_DATA_T *loadCell ); // MSG_ID_DG_VALVES_STATES BOOL broadcastValvesStates( U16 valvesStates ); @@ -131,6 +133,9 @@ // MSG_ID_DG_HEAT_DISINFECT_TO_UI_DATA_PUBLISH BOOL broadcastHeatDisinfectData2UI( MODE_HEAT_DISINFECT_UI_DATA_T *heatDisinfectUIData ); +// MSG_ID_DG_SWITCHES_DATA +BOOL broadcastSwitchesData( SWITCHES_DATA_T *switchesData ); + // MSG_ID_DG_COMMAND_RESPONSE void sendCommandResponseMsg( DG_CMD_RESPONSE_T *cmdResponsePtr ); @@ -146,6 +151,12 @@ // MSG_ID_REQUEST_FW_VERSIONS void handleFWVersionCmd( MESSAGE_T *message ); +// MSG_ID_REQUEST_FW_VERSIONS +void handleDGSerialNumberRequest( MESSAGE_T *message ); + +// MSG_ID_UI_REQUEST_SERVICE_INFO +void handleDGServiceScheduleRequest( MESSAGE_T *message ); + // MSG_ID_DG_SWITCH_RESERVOIR_CMD void handleSwitchReservoirCmd( MESSAGE_T *message ); @@ -256,6 +267,12 @@ // MSG_ID_DRAIN_PUMP_SET_DELTA_PRESSURE_OVERRIDE void handleSetDrainPumpDeltaPressureOverrideRequest( MESSAGE_T *message ); +// MSG_ID_DG_SWITCHES_STATUS_OVERRIDE +void handleSetSwitchesStatusOverrideRequest( MESSAGE_T *message ); + +// MSG_ID_DG_SWITCHES_PUBLISH_INTERVAL_OVERRIDE +void handleTestSwitchesPublishIntervalOverrideRequest( MESSAGE_T *message ); + // MSG_ID_CONDUCTIVITY_OVERRIDE void handleTestSetConductivityOverrideRequest( MESSAGE_T *message ); Index: firmware/App/Services/WatchdogMgmt.c =================================================================== diff -u -r5adaa0ae1236d34fca1fc8def7fa107ec470115e -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/App/Services/WatchdogMgmt.c (.../WatchdogMgmt.c) (revision 5adaa0ae1236d34fca1fc8def7fa107ec470115e) +++ firmware/App/Services/WatchdogMgmt.c (.../WatchdogMgmt.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -108,7 +108,7 @@ } // check to see if watchdog has expired - if ( getCPLDWatchdogExpired() == PIN_SIGNAL_HIGH ) + if ( PIN_SIGNAL_LOW == getCPLDWatchdogExpired() ) { // ignore expired watchdog until after watchdog POST if ( WATCHDOG_SELF_TEST_STATE_COMPLETE == watchdogSelfTestState ) @@ -161,7 +161,7 @@ { // waiting here for w.d. test period to prevent this task from checking in - watchdog should expire } - if ( getCPLDWatchdogExpired() == PIN_SIGNAL_HIGH ) + if ( PIN_SIGNAL_LOW == getCPLDWatchdogExpired() ) { watchdogSelfTestStatus = SELF_TEST_STATUS_PASSED; } Index: firmware/source/sys_main.c =================================================================== diff -u -r2fff37fa585181917705645494549b5fd4a4d522 -r67021fbc633259e8e1bce76749dbef7d0cb51998 --- firmware/source/sys_main.c (.../sys_main.c) (revision 2fff37fa585181917705645494549b5fd4a4d522) +++ firmware/source/sys_main.c (.../sys_main.c) (revision 67021fbc633259e8e1bce76749dbef7d0cb51998) @@ -85,6 +85,7 @@ #include "ROPump.h" #include "RTC.h" #include "SafetyShutdown.h" +#include "Switches.h" #include "SystemComm.h" #include "TaskBG.h" #include "TemperatureSensors.h" @@ -192,6 +193,7 @@ initUVReactors(); initAccel(); initRTC(); + initSwitches(); initCommBuffers(); initMsgQueues(); initSystemComm();