Index: firmware/App/Controllers/DialInFlow.c =================================================================== diff -u -r6419179374edcd65da462de84e8aeaefb7e20320 -rabbad386f4cc94f315300dffef321fe8c03fbd52 --- firmware/App/Controllers/DialInFlow.c (.../DialInFlow.c) (revision 6419179374edcd65da462de84e8aeaefb7e20320) +++ firmware/App/Controllers/DialInFlow.c (.../DialInFlow.c) (revision abbad386f4cc94f315300dffef321fe8c03fbd52) @@ -46,8 +46,8 @@ #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 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 ); @@ -83,14 +83,19 @@ #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 ) @@ -107,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 { @@ -148,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 }; @@ -177,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 ); @@ -204,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 @@ -237,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 ); } /*********************************************************************//** @@ -391,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; @@ -693,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 @@ -975,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 ); @@ -1225,20 +1347,24 @@ *************************************************************************/ SELF_TEST_STATUS_T execDialInFlowTest( void ) { - SELF_TEST_STATUS_T result = SELF_TEST_STATUS_FAILED; + SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; U08 const dfmStatus = getFPGADialysateFlowMeterStatus(); - // Get the flow sensors calibration record - HD_FLOW_SENSORS_CAL_RECORD_T cal = getHDFlowSensorsCalibrationRecord(); - if ( DFM_SENSOR_PARAM_CORRUPT_STATUS != getFPGADialysateFlowMeterStatus() ) + if ( DFM_SENSOR_PARAM_CORRUPT_STATUS != dfmStatus ) { - dialInFlowCalGain = cal.hdFlowSensors[ CAL_DATA_HD_DIALYZER_FLOW_SENSOR ].gain; - dialInFlowCalOffset = cal.hdFlowSensors[ CAL_DATA_HD_DIALYZER_FLOW_SENSOR ].offset; - result = SELF_TEST_STATUS_PASSED; + BOOL calStatus = processCalibrationData(); + + if ( TRUE == calStatus ) + { + result = SELF_TEST_STATUS_PASSED; + } + else + { + result = SELF_TEST_STATUS_FAILED; + } } else { - result = SELF_TEST_STATUS_FAILED; SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_DIALYSATE_FLOW_STATUS_SELF_TEST_FAILURE, (U32)dfmStatus ); } @@ -1249,6 +1375,7 @@ /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ + /*********************************************************************//** * @brief