Index: firmware/App/Controllers/DialInFlow.c =================================================================== diff -u -r30b1f6126c37f7b99cf699f63b4bcee9d1a1745f -rabbad386f4cc94f315300dffef321fe8c03fbd52 --- firmware/App/Controllers/DialInFlow.c (.../DialInFlow.c) (revision 30b1f6126c37f7b99cf699f63b4bcee9d1a1745f) +++ firmware/App/Controllers/DialInFlow.c (.../DialInFlow.c) (revision abbad386f4cc94f315300dffef321fe8c03fbd52) @@ -46,10 +46,11 @@ #define MAX_DIAL_IN_PUMP_PWM_STEP_UP_CHANGE 0.0133 ///< Max duty cycle change when ramping up ~ 200 mL/min/s. #define MAX_DIAL_IN_PUMP_PWM_STEP_DN_CHANGE 0.02 ///< Max duty cycle change when ramping down ~ 300 mL/min/s. -#define MAX_DIAL_IN_PUMP_PWM_DUTY_CYCLE 0.88 ///< Controller will error if PWM duty cycle > 90%, so set max to 88%. -#define MIN_DIAL_IN_PUMP_PWM_DUTY_CYCLE 0.12 ///< Controller will error if PWM duty cycle < 10%, so set min to 12%. - -#define DIP_CONTROL_INTERVAL ( 10000 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the dialIn pump is controlled +#define MAX_DIAL_IN_PUMP_PWM_DUTY_CYCLE 0.89 ///< Controller will error if PWM duty cycle > 90%, so set max to 89%. +#define MIN_DIAL_IN_PUMP_PWM_DUTY_CYCLE 0.10 ///< Controller will error if PWM duty cycle < 10%, so set min to 10%. + +/// Interval (ms/task time) at which the dialIn pump is controlled. +static const U32 DIP_CONTROL_INTERVAL = ( 10000 / TASK_GENERAL_INTERVAL ); #define DIP_P_COEFFICIENT 0.00035 ///< P term for dialIn pump control. #define DIP_I_COEFFICIENT 0.00035 ///< I term for dialIn pump control. @@ -64,27 +65,37 @@ #define DIP_MAX_FLOW_VS_SPEED_DIFF_RPM 200.0 ///< Maximum difference between measured motor speed and speed implied by measured flow. #define DIP_MAX_MOTOR_SPEED_WHILE_OFF_RPM 100.0 ///< Maximum motor speed (RPM) while motor is commanded off. #define DIP_MAX_ROTOR_VS_MOTOR_DIFF_RPM 5.0 ///< Maximum difference in speed between motor and rotor (in rotor RPM). -#define DIP_MAX_MOTOR_SPEED_ERROR_RPM 300.0 ///< Maximum difference in speed between measured and commanded RPM. -#define DIP_FLOW_VS_SPEED_PERSIST ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< Persist time (task intervals) for flow vs. motor speed error condition. -#define DIP_OFF_ERROR_PERSIST ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< Persist time (task intervals) for motor off error condition. -#define DIP_MOTOR_SPEED_ERROR_PERSIST ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< Persist time (task intervals) motor speed error condition. -#define DIP_ROTOR_SPEED_ERROR_PERSIST ((12 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL) ///< Persist time (task intervals) rotor speed error condition. -#define DIP_DIRECTION_ERROR_PERSIST (250 / TASK_PRIORITY_INTERVAL) ///< Persist time (task intervals) pump direction error condition. +#define DIP_MAX_MOTOR_SPEED_ERROR_RPM 300.0 ///< Maximum difference in speed between measured and commanded RPM. + +/// Persist time (task intervals) for flow vs. motor speed error condition. +static const U32 DIP_FLOW_VS_SPEED_PERSIST = ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL); +/// Persist time (task intervals) for motor off error condition. +static const U32 DIP_OFF_ERROR_PERSIST = ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL); +/// Persist time (task intervals) motor speed error condition. +static const U32 DIP_MOTOR_SPEED_ERROR_PERSIST = ((5 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL); +/// Persist time (task intervals) rotor speed error condition. +static const U32 DIP_ROTOR_SPEED_ERROR_PERSIST = ((12 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL); +/// Persist time (task intervals) pump direction error condition. +static const U32 DIP_DIRECTION_ERROR_PERSIST = (250 / TASK_PRIORITY_INTERVAL); #define DIP_MAX_CURR_WHEN_STOPPED_MA 150.0 ///< Motor controller current should not exceed this when pump should be stopped. -#define DIP_MIN_CURR_WHEN_RUNNING_MA 150.0 ///< Motor controller current should always exceed this when pump should be running. #define DIP_MAX_CURR_WHEN_RUNNING_MA 2000.0 ///< Motor controller current should not exceed this when pump should be running. #define DIP_MAX_CURR_ERROR_DURATION_MS 2000 ///< Motor controller current errors persisting beyond this duration will trigger an alarm. -#define DIP_SPEED_ADC_TO_RPM_FACTOR 1.280938 ///< Conversion factor from ADC counts to RPM for dialIn pump motor. +#ifndef V2_0_SYSTEM + #define DIP_SPEED_ADC_TO_RPM_FACTOR 1.751752 ///< Conversion factor from ADC counts to RPM for dialIn pump motor. + #define DIP_MOTOR_RPM_TO_PWM_DC_FACTOR 0.00025 ///< ~40 BP motor RPM = 1% PWM duty cycle +#else + #define DIP_SPEED_ADC_TO_RPM_FACTOR 1.280938 ///< Conversion factor from ADC counts to RPM for blood pump motor + #define DIP_MOTOR_RPM_TO_PWM_DC_FACTOR 0.0003125 ///< ~32 BP motor RPM = 1% PWM duty cycle +#endif #define DIP_CURRENT_ADC_TO_MA_FACTOR 3.002 ///< Conversion factor from ADC counts to mA for dialIn pump motor. #define DIP_REV_PER_LITER 150.0 ///< Rotor revolutions per liter. /// Macro converts flow rate to motor RPM. #define DIP_ML_PER_MIN_TO_PUMP_RPM_FACTOR ( DIP_REV_PER_LITER / ML_PER_LITER ) #define DIP_GEAR_RATIO 32.0 ///< DialIn pump motor to dialIn pump gear ratio. -#define DIP_MOTOR_RPM_TO_PWM_DC_FACTOR 0.00028 ///< ~28 BP motor RPM = 1% PWM duty cycle. #define DIP_PWM_ZERO_OFFSET 0.1 ///< 10% PWM duty cycle = zero speed. /// Macro converts flow rate to estimate PWM needed to achieve it. #define DIP_PWM_FROM_ML_PER_MIN(rate) ( (rate) * DIP_ML_PER_MIN_TO_PUMP_RPM_FACTOR * DIP_GEAR_RATIO * DIP_MOTOR_RPM_TO_PWM_DC_FACTOR + DIP_PWM_ZERO_OFFSET ) @@ -101,8 +112,18 @@ #define FLOW_SIG_STRGTH_ALARM_PERSIST ( 5 * MS_PER_SECOND ) #define MIN_FLOW_SIG_STRENGTH 0.9 ///< Minimum flow sensor signal strength (90%). -#define DFM_SENSOR_PARAM_CORRUPT_STATUS 0x7 ///< Dialysate flow meter NVM parameter corrupt status. +/// Dialysate flow fast read timeout alarm persistence. +#define DIALYSATE_FLOW_FAST_READ_TO_PERSIST 100 +/// Dialysate flow slow read timeout alarm persistence. +#define DIALYSATE_FLOW_SLOW_READ_TO_PERSIST ( MS_PER_SECOND * 3 ) +/// Blood flow comm error persistence. +#define DIALYSATE_FLOW_COMM_ERROR_PERSIST MS_PER_SECOND +#define DFM_SENSOR_CONNECTED_STATUS 0x00 ///< Dialysate flow meter connected status. +#define DFM_SENSOR_PARAM_CORRUPT_STATUS 0x07 ///< Dialysate flow meter NVM parameter corrupt status. + +#define PUMP_DIR_ERROR_COUNT_MASK 0x3F ///< Bit mask for pump direction error counter. + /// Enumeration of dialysate inlet pump states. typedef enum DialInPump_States { @@ -142,8 +163,6 @@ static MOTOR_DIR_T dialInPumpDirectionSet = MOTOR_DIR_FORWARD; ///< Currently set dialysate flow direction static PUMP_CONTROL_MODE_T dialInPumpControlMode = PUMP_CONTROL_MODE_CLOSED_LOOP; ///< Requested dialIn pump control mode. static PUMP_CONTROL_MODE_T dialInPumpControlModeSet = PUMP_CONTROL_MODE_CLOSED_LOOP;///< Currently set dialIn pump control mode. -static F32 dialInFlowCalGain = 1.0; ///< Dialysate flow calibration gain. -static F32 dialInFlowCalOffset = 0.0; ///< Dialysate flow calibration offset. /// Interval (in ms) at which to publish dialIn flow data to CAN bus static OVERRIDE_U32_T dialInFlowDataPublishInterval = { DIAL_IN_FLOW_DATA_PUB_INTERVAL, DIAL_IN_FLOW_DATA_PUB_INTERVAL, DIAL_IN_FLOW_DATA_PUB_INTERVAL, 0 }; @@ -171,13 +190,19 @@ static U32 errorDialInRotorSpeedPersistTimerCtr = 0; ///< Persistence timer counter for rotor speed error condition. static U32 errorDialInPumpDirectionPersistTimerCtr = 0; ///< Persistence timer counter for pump direction error condition. -static F32 flowReadings[ SIZE_OF_ROLLING_AVG ]; ///< Holds flow samples for a rolling average -static U32 flowReadingsIdx = 0; ///< Index for next sample in rolling average array -static F32 flowReadingsTotal = 0.0; ///< Rolling total - used to calc average -static U32 flowReadingsCount = 0; ///< Number of samples in flow rolling average buffer +static F32 flowReadings[ SIZE_OF_ROLLING_AVG ]; ///< Holds flow samples for a rolling average. +static U32 flowReadingsIdx = 0; ///< Index for next sample in rolling average array. +static F32 flowReadingsTotal = 0.0; ///< Rolling total - used to calc average. +static U32 flowReadingsCount = 0; ///< Number of samples in flow rolling average buffer. -static U32 dipCurrErrorDurationCtr = 0; ///< Used for tracking persistence of dip current errors +static U32 dipCurrErrorDurationCtr = 0; ///< Used for tracking persistence of dip current errors. +static U08 lastDialysateFlowFastPacketReadCtr = 0; ///< Previous read counter for the dialysate flow fast packets. +static U08 lastDialysateFlowSlowPacketReadCtr = 0; ///< Previous read counter for the dialysate flow slow packets. +static U08 lastDialInPumpDirectionCount = 0; ///< Previous pump direction error count reported by FPGA. +static U08 lastDialysateFlowCommErrorCount = 0; ///< Previous DPi flow sensor comm error count. +static HD_FLOW_SENSORS_CAL_RECORD_T dialysateFlowCalRecord; ///< Dialysate flow sensor calibration record. + // ********** private function prototypes ********** static DIAL_IN_PUMP_STATE_T handleDialInPumpOffState( void ); @@ -198,7 +223,8 @@ static void checkDialInPumpFlowAgainstSpeed( void ); static void checkDialInPumpMCCurrent( void ); static void checkDialInFlowSensorSignalStrength( void ); -static U32 getPublishDialInFlowDataInterval( void ); +static U32 getPublishDialInFlowDataInterval( void ); +static BOOL processCalibrationData( void ); /*********************************************************************//** * @brief @@ -231,6 +257,9 @@ // Initialize persistent alarm for flow sensor signal strength too low initPersistentAlarm( ALARM_ID_DIALYSATE_FLOW_SIGNAL_STRENGTH_TOO_LOW, FLOW_SIG_STRGTH_ALARM_PERSIST, FLOW_SIG_STRGTH_ALARM_PERSIST ); + initPersistentAlarm( ALARM_ID_HD_DP_FLOW_READ_TIMEOUT_ERROR, 0, DIALYSATE_FLOW_FAST_READ_TO_PERSIST ); + initPersistentAlarm( ALARM_ID_HD_DP_FLOW_SLOW_READ_TIMEOUT_ERROR, 0, DIALYSATE_FLOW_SLOW_READ_TO_PERSIST ); + initPersistentAlarm( ALARM_ID_HD_DP_FLOW_SENSOR_ERROR, 0, DIALYSATE_FLOW_COMM_ERROR_PERSIST ); } /*********************************************************************//** @@ -385,11 +414,56 @@ * @return none *************************************************************************/ void execDialInFlowMonitor( void ) -{ +{ + // Check if a new calibration is available + if ( TRUE == isNewCalibrationRecordAvailable() ) + { + // Get the new calibration data and check its validity + processCalibrationData(); + } + HD_OP_MODE_T opMode = getCurrentOperationMode(); U16 dipRPM = getIntADCReading( INT_ADC_DIAL_IN_PUMP_SPEED ); U16 dipmA = getIntADCReading( INT_ADC_DIAL_IN_PUMP_MOTOR_CURRENT ); - F32 dipFlow = ( getFPGADialysateFlow() * dialInFlowCalGain ) + dialInFlowCalOffset; + U08 fpReadCtr = getFPGADialysateFlowFastPacketReadCounter(); + U08 spReadCtr = getFPGADialysateFlowSlowPacketReadCounter(); + U08 flowErrorCtr = getFPGADialysateFlowErrorCounter(); + U08 flowStatus = getFPGADialysateFlowMeterStatus(); + + F32 fpgaDialysateFlow = getFPGADialysateFlow(); + F32 dipFlow = pow(fpgaDialysateFlow, 4) * dialysateFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].fourthOrderCoeff + + pow(fpgaDialysateFlow, 3) * dialysateFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].thirdOrderCoeff + + pow(fpgaDialysateFlow, 2) * dialysateFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].secondOrderCoeff + + fpgaDialysateFlow * dialysateFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].gain + + dialysateFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].offset; + +#ifndef DISABLE_PUMP_FLOW_CHECKS + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_DP_FLOW_SENSOR_ERROR, ( flowErrorCtr != lastDialysateFlowCommErrorCount ) ) ) + { + activateAlarmNoData( ALARM_ID_HD_DP_FLOW_SENSOR_ERROR ); + } + if ( flowStatus != DFM_SENSOR_CONNECTED_STATUS ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_DIALYSATE_FLOW_STATUS_SELF_TEST_FAILURE, (U32)flowStatus ); + } + lastDialysateFlowCommErrorCount = flowErrorCtr; +#endif + +#ifndef DISABLE_FPGA_COUNTER_CHECKS + // Check for stale flow reading + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_DP_FLOW_READ_TIMEOUT_ERROR, ( fpReadCtr == lastDialysateFlowFastPacketReadCtr ) ) ) + { + activateAlarmNoData( ALARM_ID_HD_DP_FLOW_READ_TIMEOUT_ERROR ); + } + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_DP_FLOW_SLOW_READ_TIMEOUT_ERROR, ( spReadCtr == lastDialysateFlowSlowPacketReadCtr ) ) ) + { + activateAlarmNoData( ALARM_ID_HD_DP_FLOW_SLOW_READ_TIMEOUT_ERROR ); + } +#endif + + // Record flow read counters for next time around + lastDialysateFlowFastPacketReadCtr = fpReadCtr; + lastDialysateFlowSlowPacketReadCtr = spReadCtr; adcDialInPumpMCSpeedRPM.data = (F32)(SIGN_FROM_12_BIT_VALUE(dipRPM)) * DIP_SPEED_ADC_TO_RPM_FACTOR; adcDialInPumpMCCurrentmA.data = (F32)(SIGN_FROM_12_BIT_VALUE(dipmA)) * DIP_CURRENT_ADC_TO_MA_FACTOR; @@ -687,6 +761,52 @@ 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: dialysateFlowCalRecord + * @return TRUE if the calibration record is valid, otherwise FALSE + *************************************************************************/ +static BOOL processCalibrationData( void ) +{ + BOOL status = TRUE; + + // Get the calibration record from NVDataMgmt + HD_FLOW_SENSORS_CAL_RECORD_T calData = getHDFlowSensorsCalibrationRecord(); + + // 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 dialysate flow sensors data is not stored in the NV memory or it was corrupted. + if ( 0 == calData.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].calibrationTime ) + { +#ifndef SKIP_CAL_CHECK + activateAlarmNoData( ALARM_ID_HD_DIALYSATE_FLOW_INVALID_CAL_RECORD ); + status = FALSE; +#endif + } + + // The calibration data was valid, update the local copy + dialysateFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].fourthOrderCoeff = + calData.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].fourthOrderCoeff; + + dialysateFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].thirdOrderCoeff = + calData.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].thirdOrderCoeff; + + dialysateFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].secondOrderCoeff = + calData.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].secondOrderCoeff; + + dialysateFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].gain = + calData.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].gain; + + dialysateFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].offset = + calData.hdFlowSensors[ CAL_DATA_HD_DIALYSATE_FLOW_SENSOR ].offset; + + return status; +} /*********************************************************************//** * @brief @@ -969,7 +1089,15 @@ if ( DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE == dialInPumpState ) { MOTOR_DIR_T dipMCDir, dipDir; + U08 dirErrorCnt = getFPGADialInPumpHallSensorStatus() & PUMP_DIR_ERROR_COUNT_MASK; + // Check pump direction error count + if ( lastDialInPumpDirectionCount != dirErrorCnt ) + { + lastDialInPumpDirectionCount = dirErrorCnt; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_PUMP_DIRECTION_STATUS_ERROR, (U32)HD_PUMP_DIALYSATE_INLET_PUMP ) + } + dipMCDir = ( getMeasuredDialInPumpMCSpeed() >= 0.0 ? MOTOR_DIR_FORWARD : MOTOR_DIR_REVERSE ); dipDir = ( getMeasuredDialInPumpSpeed() >= 0.0 ? MOTOR_DIR_FORWARD : MOTOR_DIR_REVERSE ); @@ -1165,7 +1293,7 @@ else { dipCurr = fabs( getMeasuredDialInPumpMCCurrent() ); - if ( ( dipCurr < DIP_MIN_CURR_WHEN_RUNNING_MA ) || ( dipCurr > DIP_MAX_CURR_WHEN_RUNNING_MA ) ) + if ( dipCurr > DIP_MAX_CURR_WHEN_RUNNING_MA ) { dipCurrErrorDurationCtr += TASK_PRIORITY_INTERVAL; if ( dipCurrErrorDurationCtr > DIP_MAX_CURR_ERROR_DURATION_MS ) @@ -1219,16 +1347,26 @@ *************************************************************************/ SELF_TEST_STATUS_T execDialInFlowTest( void ) { - SELF_TEST_STATUS_T result = SELF_TEST_STATUS_FAILED; - CALIBRATION_DATA_T cal; + SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; + U08 const dfmStatus = getFPGADialysateFlowMeterStatus(); - // Retrieve dialysate flow sensor calibration data - if ( ( TRUE == getCalibrationData( &cal ) ) && ( DFM_SENSOR_PARAM_CORRUPT_STATUS != getFPGADialysateFlowMeterStatus() ) ) + if ( DFM_SENSOR_PARAM_CORRUPT_STATUS != dfmStatus ) { - dialInFlowCalGain = cal.dialysateFlowGain; - dialInFlowCalOffset = cal.dialysateFlowOffset_mL_min; - result = SELF_TEST_STATUS_PASSED; + BOOL calStatus = processCalibrationData(); + + if ( TRUE == calStatus ) + { + result = SELF_TEST_STATUS_PASSED; + } + else + { + result = SELF_TEST_STATUS_FAILED; + } } + else + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_DIALYSATE_FLOW_STATUS_SELF_TEST_FAILURE, (U32)dfmStatus ); + } return result; } @@ -1237,58 +1375,8 @@ /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ - - -/*********************************************************************//** - * @brief - * The setDialInFlowCalibration function sets the dialysate flow calibration - * factors and has them stored in non-volatile memory. - * @details Inputs: none - * @details Outputs: dialInFlowCalGain, dialInFlowCalOffset - * @param gain gain calibration factor for dialysate flow sensor - * @param offset offset calibration factor for dialysate flow sensor - * @return TRUE if calibration factors successfully set/stored, FALSE if not - *************************************************************************/ -BOOL setDialInFlowCalibration( F32 gain, F32 offset ) -{ - BOOL result = FALSE; - if ( TRUE == isTestingActivated() ) - { - CALIBRATION_DATA_T cal; - - getCalibrationData( &cal ); - // Keep locally and apply immediately - dialInFlowCalGain = gain; - dialInFlowCalOffset = offset; - // Also update calibration record in non-volatile memory - cal.dialysateFlowGain = gain; - cal.dialysateFlowOffset_mL_min = offset; - if ( TRUE == setCalibrationData( cal ) ) - { - result = TRUE; - } - } - - return result; -} - -/*********************************************************************//** - * @brief - * The getDialInFlowCalibration function retrieves the current dialysate flow - * calibration factors. - * @details Inputs: dialInFlowCalGain, dialInFlowCalOffset - * @details Outputs: none - * @param gain value to populate with gain calibration factor for dialysate flow sensor - * @param offset value to populate with offset calibration factor for dialysate flow sensor - * @return none - *************************************************************************/ -void getDialInFlowCalibration( F32 *gain, F32 *offset ) -{ - *gain = dialInFlowCalGain; - *offset = dialInFlowCalOffset; -} - + /*********************************************************************//** * @brief * The testSetDialInFlowDataPublishIntervalOverride function overrides the