Index: firmware/App/Controllers/DialInFlow.c =================================================================== diff -u -r2bb447181c2519690441d81f83563d17e0882ef2 -rdc7d8d337cb5f3647e3cbd1bcd054d83fbe7bd2a --- firmware/App/Controllers/DialInFlow.c (.../DialInFlow.c) (revision 2bb447181c2519690441d81f83563d17e0882ef2) +++ firmware/App/Controllers/DialInFlow.c (.../DialInFlow.c) (revision dc7d8d337cb5f3647e3cbd1bcd054d83fbe7bd2a) @@ -1,299 +1,537 @@ -/************************************************************************** - * - * Copyright (c) 2019-2019 Diality Inc. - All Rights Reserved. - * - * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN - * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. - * - * @file DialInFlow.c - * - * @date 16-Dec-2019 - * @author S. Nash - * - * @brief Monitor/Controller for dialysate inlet pump and flow sensor. - * - **************************************************************************/ +/************************************************************************** +* +* Copyright (c) 2019-2022 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file DialInFlow.c +* +* @author (last) Dara Navaei +* @date (last) 18-Oct-2022 +* +* @author (original) Sean +* @date (original) 16-Dec-2019 +* +***************************************************************************/ -#ifndef _VECTORCAST_ - #include -#endif +#include #include "etpwm.h" #include "gio.h" #include "mibspi.h" -#include "Common.h" +#include "DialInFlow.h" #include "FPGA.h" #include "InternalADC.h" +#include "NVDataMgmt.h" #include "OperationModes.h" -#include "SystemCommMessages.h" +#include "PersistentAlarm.h" +#include "PIControllers.h" +#include "SafetyShutdown.h" +#include "SystemCommMessages.h" +#include "SystemComm.h" #include "TaskGeneral.h" #include "TaskPriority.h" #include "Timers.h" -#include "DialInFlow.h" +/** + * @addtogroup DialysateInletFlow + * @{ + */ + // ********** private definitions ********** -#define DIAL_IN_FLOW_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) // interval (ms/task time) at which the dialIn flow data is published on the CAN bus +/// Interval (ms/task time) at which the dialIn flow data is published on the CAN bus. +#define DIAL_IN_FLOW_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) -#define MAX_DIAL_IN_FLOW_RATE 500 // mL/min -#define MIN_DIAL_IN_FLOW_RATE 100 // mL/min +#define MAX_DIAL_IN_PUMP_PWM_STEP_UP_CHANGE 0.0133F ///< Max duty cycle change when ramping up ~ 200 mL/min/s. +#define MAX_DIAL_IN_PUMP_PWM_STEP_DN_CHANGE 0.02F ///< Max duty cycle change when ramping down ~ 300 mL/min/s. +#define MAX_DIAL_IN_PUMP_PWM_DUTY_CYCLE 0.89F ///< Controller will error if PWM duty cycle > 90%, so set max to 89%. +#define MIN_DIAL_IN_PUMP_PWM_DUTY_CYCLE 0.10F ///< Controller will error if PWM duty cycle < 10%, so set min to 10%. + +#define DIP_CONTROL_INTERVAL_SEC 4 ///< Dialysate inlet pump control interval (in seconds). +/// Interval (ms/task time) at which the dialIn pump is controlled. +static const U32 DIP_CONTROL_INTERVAL = ( DIP_CONTROL_INTERVAL_SEC * MS_PER_SECOND / TASK_GENERAL_INTERVAL ); +#define DIP_P_COEFFICIENT 0.0001F ///< P term for dialIn pump control. +#define DIP_I_COEFFICIENT 0.00075F ///< I term for dialIn pump control. -#define MAX_DIAL_IN_PUMP_PWM_STEP_CHANGE 0.02 // duty cycle TODO - fixed or parameterized or set in motor controller? -#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_HOME_RATE 100 ///< Target pump speed (in estimate mL/min) for homing. +#define DIP_HOME_TIMEOUT_MS 10000 ///< Maximum time allowed for homing to complete (in ms). +/// Interval (ms/task time) at which the blood pump speed is calculated (every 40 ms). +#define DIP_SPEED_CALC_INTERVAL ( 40 / TASK_PRIORITY_INTERVAL ) +/// Number of hall sensor counts kept in buffer to hold last 1 second of count data. +#define DIP_SPEED_CALC_BUFFER_LEN ( 1000 / DIP_SPEED_CALC_INTERVAL / TASK_PRIORITY_INTERVAL ) +#define DIP_HALL_EDGE_COUNTS_PER_REV 48 ///< Number of hall sensor edge counts per motor revolution. -#define DIP_CONTROL_INTERVAL ( 500 / TASK_GENERAL_INTERVAL ) // interval (ms/task time) at which the dialIn pump is controlled -#define DIP_P_COEFFICIENT 0.0002 // P term for dialIn pump control -#define DIP_I_COEFFICIENT 0.00002 // I term for dialIn pump control -#define DIP_MAX_ERROR_SUM 10.0 // for anti-wind-up in I term -#define DIP_MIN_ERROR_SUM -10.0 -#define DIP_MAX_PWM_DC_DELTA 0.01 // prevents large steps in PWM duty cycle -#define DIP_MIN_PWM_DC_DELTA -0.01 +#define DIP_MAX_FLOW_RATE 1320.0F ///< Maximum measured BP flow rate allowed. +#define DIP_MIN_FLOW_RATE -1320.0F ///< Minimum measured BP flow rate allowed. +#define DIP_MAX_FLOW_VS_SPEED_DIFF_RPM 350.0F ///< Maximum difference between measured motor speed and speed implied by measured flow. +#define DIP_MAX_MOTOR_SPEED_WHILE_OFF_RPM 100.0F ///< Maximum motor speed (RPM) while motor is commanded off. +#define DIP_MAX_ROTOR_VS_MOTOR_DIFF_RPM 5.0F ///< Maximum difference in speed between motor and rotor (in rotor RPM). +#define DIP_MAX_MOTOR_SPEED_ERROR_RPM 300.0F ///< 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); +/// Rotor speed persist time test needs a minimum number of rotations. +static const U32 DIP_ROTOR_ERROR_PERSIST_ROTATION_MIN = 3; +/// Persist time (task intervals) pump direction error condition. +static const U32 DIP_DIRECTION_ERROR_PERSIST = (250 / TASK_PRIORITY_INTERVAL); +/// Persist time (task intervals) dialysate flow rate out of range error condition. +static const U32 DIP_MAX_FLOW_RATE_OUT_OF_RANGE_PERSIST = (1 * MS_PER_SECOND); +/// Time threshold to trigger an alarm if Dialysate flow data has not arrived within 3 seconds +static const U32 DIP_DIALYSATE_FLOW_DATA_ALARM_THRESHOLD = ((3 * MS_PER_SECOND) / 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 1000.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_MAX_CURR_WHEN_STOPPED_MA 150.0F ///< Motor controller current should not exceed this when pump should be stopped. +#define DIP_MAX_CURR_WHEN_RUNNING_MA 2000.0F ///< 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.375 // conversion factor from ADC counts to RPM for dialIn pump motor -#define DIP_CURRENT_ADC_TO_MA_FACTOR 2.65 // conversion factor from ADC counts to mA for dialIn pump motor +#define DIAL_IN_PUMP_ADC_FULL_SCALE_V 3.0F ///< BP analog signals are 0-3V (while int. ADC ref V may be different). +#define DIAL_IN_PUMP_ADC_ZERO 1998 ///< Mid-point (zero) for ADC readings. + +///< Macro converts a 12-bit ADC reading to a signed 16-bit value. +#define SIGN_FROM_12_BIT_VALUE(v) ( (S16)(v) - (S16)DIAL_IN_PUMP_ADC_ZERO ) + +#define DIP_SPEED_ADC_TO_RPM_FACTOR 1.751752F ///< Conversion factor from ADC counts to RPM for dialIn pump motor (3500 RPM/1998 counts). +#define DIP_MOTOR_RPM_TO_PWM_DC_FACTOR 0.000238F ///< ~42 BP motor RPM = 1% PWM duty cycle +#define DIP_CURRENT_ADC_TO_MA_FACTOR 3.002F ///< Conversion factor from ADC counts to mA for dialIn pump motor. -#define DIP_REV_PER_LITER 124.0 // rotor revolutions per liter +#define DIP_REV_PER_LITER 146.84F ///< 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.0003717 // ~27 BP motor RPM = 1% PWM duty cycle -#define DIP_PWM_ZERO_OFFSET 0.1 // 10% PWM duty cycle = zero speed -#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 ) +#define DIP_GEAR_RATIO 32.0F ///< DialIn pump motor to dialIn pump gear ratio. +#define DIP_PWM_ZERO_OFFSET 0.1F ///< 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 ) +/// Conversion from PWM duty cycle % to commanded pump motor speed. PWM range is 10% to 90%. RPM range is 0 to 3200. 3200 / 0.8 = 4000. +#define DIP_PWM_TO_MOTOR_SPEED_RPM(pwm) ( ((pwm) - DIP_PWM_ZERO_OFFSET) * 4000.0F ) +// Macro converts PWM to estimate flow rate needed to achieve it. +#define DIP_ML_PER_MIN_FROM_PWM(pwm) ( (( pwm - DIP_PWM_ZERO_OFFSET) * 684.73F ) + 49.121F ) + -#define DIAL_INPUMP_ADC_FULL_SCALE_V 3.0 // BP analog signals are 0-3V (while int. ADC ref V is 3.3V) -#define DIAL_INPUMP_ADC_MID_PT_BITS ( (F32)( INT_ADC_FULL_SCALE_BITS >> 1 ) * ( DIAL_INPUMP_ADC_FULL_SCALE_V / INT_ADC_REF_V ) ) -#define SIGN_FROM_12_BIT_VALUE(v) ( (S16)(v) - (S16)DIAL_INPUMP_ADC_MID_PT_BITS ) - -#define DIAL_IN_FLOW_SAMPLE_FREQ ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) -#define SIZE_OF_ROLLING_AVG (U32)( (F32)DIAL_IN_FLOW_SAMPLE_FREQ * 0.8 ) // measured dialIn flow is filtered w/ moving average -#define MAX_FLOW_FILTER_INTERVAL 5 // slowest sample interval for filter is every 5th sample - +/// Measured dialIn flow is filtered w/ moving average. +#define SIZE_OF_ROLLING_AVG 4 + +#define PUMP_DIR_ERROR_COUNT_MASK 0x3F ///< Bit mask for pump direction error counter. +#define DIP_MIN_DIR_CHECK_SPEED_RPM 10.0F ///< Minimum motor speed before we check pump direction. +#define DIP_COMMUTATION_ERROR_MAX_CNT 3 ///< Maximum number of commutation errors within time window before alarm triggered. +#define DIP_COMMUTATION_ERROR_TIME_WIN_MS (15 * MS_PER_SECOND) ///< Time window for DPi commutation error. + +#define DATA_PUBLISH_COUNTER_START_COUNT 30 ///< Data publish counter start count. + +//Hybrid flow rate algorithm parameters +#define DIAL_IN_FLOW_A_ZERO 1.267F ///< Y intercept used for alpha flow coefficient calculation. +#define DIAL_IN_FLOW_WEAR_A_TERM 0.000000003551F ///< A term used for wear portion of alpha flow coefficient. +#define DIAL_IN_FLOW_WEAR_B_TERM 0.002244F ///< B term used for wear portion of alpha flow coefficient. +#define DIAL_IN_FLOW_QHIGHTRANSITION 400.0F ///< High flow rate transition for blended algorithm +#define DIAL_IN_FLOW_QLOWTRANSITION 300.0F ///< Low flow rate transition for blended algorithm +#define DIAL_IN_FLOW_PEST_A_TERM -0.000491F ///< a (2nd order) term in polynomial fit for pressure estimation +#define DIAL_IN_FLOW_PEST_B_TERM -0.04672F ///< b (first order) term in polynomial fit for pressure estimation +#define DIAL_IN_FLOW_PEST_C_TERM 18.648F ///< c (zero order) term in polynomial fit for pressure estimation +#define DIAL_IN_MAX_ROTOR_COUNT_FOR_WEAR 25000 ///< Maximum rotor count for determining wear of the cartridge (negligible affect beyond this threshold). + +/// Enumeration of dialysate inlet pump states. typedef enum DialInPump_States { - DIAL_IN_PUMP_OFF_STATE = 0, - DIAL_IN_PUMP_RAMPING_UP_STATE, - DIAL_IN_PUMP_RAMPING_DOWN_STATE, - DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE, - NUM_OF_DIAL_IN_PUMP_STATES + DIAL_IN_PUMP_OFF_STATE = 0, ///< Off state for the dialysate inlet pump. + DIAL_IN_PUMP_RAMPING_UP_STATE, ///< Ramping up state for the dialysate inlet pump. + DIAL_IN_PUMP_RAMPING_DOWN_STATE, ///< Ramping down state for the dialysate inlet pump. + DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE, ///< Control to target state for the dialysate inlet pump. + NUM_OF_DIAL_IN_PUMP_STATES ///< Number of dialysate inlet pump states. } DIAL_IN_PUMP_STATE_T; +/// Enumeration of dialysate inlet self-test states. typedef enum DialInFlow_Self_Test_States { - DIAL_IN_FLOW_SELF_TEST_STATE_START = 0, - DIAL_IN_FLOW_TEST_STATE_IN_PROGRESS, - DIAL_IN_FLOW_TEST_STATE_COMPLETE, - NUM_OF_DIAL_IN_FLOW_SELF_TEST_STATES + DIAL_IN_FLOW_SELF_TEST_STATE_START = 0, ///< Start state for the dialysate inlet pump self-test. + DIAL_IN_FLOW_TEST_STATE_IN_PROGRESS, ///< Test in progress state for the dialysate inlet pump self-test. + DIAL_IN_FLOW_TEST_STATE_COMPLETE, ///< Test completed state for the dialysate inlet pump self-test. + NUM_OF_DIAL_IN_FLOW_SELF_TEST_STATES ///< Number of dialysate inlet pump self-test states. } DIAL_IN_FLOW_SELF_TEST_STATE_T; -// pin assignments for pump stop and direction outputs -#define STOP_DI_PUMP_GIO_PORT_PIN 2U -#define DIR_DI_PUMP_SPI5_PORT_MASK 0x00000100 // (ENA - re-purposed as output GPIO) -// dialIn pump stop and direction macros -#define SET_DIP_DIR() {mibspiREG5->PC3 |= DIR_DI_PUMP_SPI5_PORT_MASK;} -#define SET_DIP_STOP() gioSetBit( gioPORTA, STOP_DI_PUMP_GIO_PORT_PIN, PIN_SIGNAL_HIGH ) -#define CLR_DIP_DIR() {mibspiREG5->PC3 &= ~DIR_DI_PUMP_SPI5_PORT_MASK;} -#define CLR_DIP_STOP() gioSetBit( gioPORTA, STOP_DI_PUMP_GIO_PORT_PIN, PIN_SIGNAL_LOW ) +// Pin assignments for pump stop and direction outputs +#define STOP_DI_PUMP_GIO_PORT_PIN 2U ///< Pin # on GIO A for stopping the dialysate inlet pump. +#define DIR_DI_PUMP_SPI5_PORT_MASK 0x00000100 ///< Pin on unused SPI5 peripheral (ENA) - re-purposed as output GPIO to set dialysate inlet pump direction. +// DialIn pump stop and direction macros +#define SET_DIP_DIR() {mibspiREG5->PC3 |= DIR_DI_PUMP_SPI5_PORT_MASK;} ///< Macro for setting the dialysate inlet pump direction pin high. +#define CLR_DIP_DIR() {mibspiREG5->PC3 &= ~DIR_DI_PUMP_SPI5_PORT_MASK;} ///< Macro for setting the dialysate inlet pump direction pin low. +#define SET_DIP_STOP() gioSetBit( gioPORTA, STOP_DI_PUMP_GIO_PORT_PIN, PIN_SIGNAL_LOW ) ///< Macro for setting the dialysate inlet pump stop pin low. +#define CLR_DIP_STOP() gioSetBit( gioPORTA, STOP_DI_PUMP_GIO_PORT_PIN, PIN_SIGNAL_HIGH ) ///< Macro for setting the dialysate inlet pump stop pin high. // ********** private data ********** -static DIAL_IN_PUMP_STATE_T dialInPumpState = DIAL_IN_PUMP_OFF_STATE; // current state of dialIn flow controller state machine -static U32 dialInFlowDataPublicationTimerCounter = 0; // used to schedule dialIn flow data publication to CAN bus -static BOOL isDialInPumpOn = FALSE; // dialIn pump is currently running -static F32 dialInPumpPWMDutyCyclePct = 0.0; // initial dialIn pump PWM duty cycle -static F32 dialInPumpPWMDutyCyclePctSet = 0.0; // currently set dialIn pump PWM duty cycle -static MOTOR_DIR_T dialInPumpDirection = MOTOR_DIR_FORWARD; // requested dialIn flow direction -static MOTOR_DIR_T dialInPumpDirectionSet = MOTOR_DIR_FORWARD; // currently set dialIn flow direction +static DIAL_IN_PUMP_STATE_T dialInPumpState = DIAL_IN_PUMP_OFF_STATE; ///< Current state of dialIn flow controller state machine +static U32 dialInFlowDataPublicationTimerCounter; ///< Used to schedule dialIn flow data publication to CAN bus +static BOOL isDialInPumpOn = FALSE; ///< DialIn pump is currently running +static F32 dialInPumpPWMDutyCyclePct = 0.0; ///< Initial dialIn pump PWM duty cycle +static F32 dialInPumpPWMDutyCyclePctSet = 0.0; ///< Currently set dialIn pump PWM duty cycle +static MOTOR_DIR_T dialInPumpDirection = MOTOR_DIR_FORWARD; ///< Requested dialysate flow direction +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. -DATA_DECL( U32, DialInFlowDataPub, dialInFlowDataPublishInterval, DIAL_IN_FLOW_DATA_PUB_INTERVAL, DIAL_IN_FLOW_DATA_PUB_INTERVAL ); // interval (in ms) at which to publish dialIn flow data to CAN bus -DATA_DECL( S32, TargetDialInFlowRate, targetDialInFlowRate, 0, 0 ); // requested dialIn flow rate -DATA_DECL( F32, MeasuredDialInFlowRate, measuredDialInFlowRate, 0.0, 0.0 ); // measured dialIn flow rate -DATA_DECL( F32, MeasuredDialInPumpRotorSpeed, dialInPumpRotorSpeedRPM, 0.0, 0.0 );// measured dialIn pump rotor speed -DATA_DECL( F32, MeasuredDialInPumpSpeed, dialInPumpSpeedRPM, 0.0, 0.0 ); // measured dialIn pump motor speed -DATA_DECL( F32, MeasuredDialInPumpMCSpeed, adcDialInPumpMCSpeedRPM, 0.0, 0.0 ); // measured dialIn pump motor controller speed -DATA_DECL( F32, MeasuredDialInPumpMCCurrent, adcDialInPumpMCCurrentmA, 0.0, 0.0 );// measured dialIn pump motor controller current +/// 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 }; +static U32 dialysateFlowDataFreshStatusCounter = 0; ///< Counter use to trigger alarm if no fresh dialysate flow data is received +static S32 targetDialInFlowRate = 0; ///< Requested dialIn flow rate +static OVERRIDE_F32_T measuredDialInFlowRate = { 0.0, 0.0, 0.0, 0 }; ///< Measured dialysate inlet flow rate +static OVERRIDE_F32_T dialInPumpRotorSpeedRPM = { 0.0, 0.0, 0.0, 0 }; ///< Measured dialysate inlet pump rotor speed +static OVERRIDE_F32_T dialInPumpSpeedRPM = { 0.0, 0.0, 0.0, 0 }; ///< Measured dialysate inlet pump motor speed +static OVERRIDE_F32_T adcDialInPumpMCSpeedRPM = { 0.0, 0.0, 0.0, 0 }; ///< Measured dialysate inlet pump motor controller speed +static OVERRIDE_F32_T adcDialInPumpMCCurrentmA = { 0.0, 0.0, 0.0, 0 }; ///< Measured dialysate inlet pump motor controller current + +static U08 lastDialInPumpDirectionCount = 0; ///< Previous pump direction error count reported by FPGA. -static F32 dipFlowError = 0.0; // dialIn flow error -static F32 dipFlowErrorSum = 0.0; // dialIn flow error sum -static U32 dipControlTimerCounter = 0; // determines when to perform control on dialIn flow +static U32 dipControlTimerCounter = 0; ///< Determines when to perform control on dialIn flow -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; // # of samples in flow rolling average buffer -static U32 flowReadingsTmrCtr = 0; // determines when to add samples to filter +static U32 dipRotorRevStartTime = 0; ///< Dialysate inlet pump rotor rotation start time (in ms) +static BOOL dipStopAtHomePosition = FALSE; ///< Stop dialysate inlet pump at next home position +static U32 dipHomeStartTime = 0; ///< When did dialysate inlet pump home command begin? (in ms) -static U32 dipCurrErrorDurationCtr = 0; // used for tracking persistence of dip current errors +static U16 dipLastMotorHallSensorCounts[ DIP_SPEED_CALC_BUFFER_LEN ]; ///< Last hall sensor count for the dialysate inlet pump motor +static U32 dipMotorSpeedCalcIdx = 0; ///< Index into 1 second buffer of motor speed hall sensor counts +static U32 dipMotorSpeedCalcTimerCtr = 0; ///< Counter determines interval for calculating dialysate inlet pump motor speed from hall sensor count. -static DIAL_IN_FLOW_SELF_TEST_STATE_T dialInPumpSelfTestState = DIAL_IN_FLOW_SELF_TEST_STATE_START; // current dialIn pump self test state -static U32 dialInPumpSelfTestTimerCount = 0; // timer counter for dialIn pump self test +static U32 errorDialInFlowVsMotorSpeedPersistTimerCtr = 0; ///< Persistence timer counter for flow vs. motor speed error condition. +static U32 errorDialInMotorOffPersistTimerCtr = 0; ///< Persistence timer counter for motor off check error condition. +static U32 errorDialInMotorSpeedPersistTimerCtr = 0; ///< Persistence timer counter for motor speed error condition. +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 F64 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 F64 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 HD_PUMPS_CAL_RECORD_T dialInPumpCalRecord; ///< Dialysate inlet calibration record. + +static OVERRIDE_U32_T dialysateInPumpRotorCounter = { 0, 0, 0, 0 }; ///< Running counter for dialin pump rotor revolutions +static F32 filteredDialInFlowMeterReading = 0.0; ///< Storage for current filtered flow meter reading + // ********** private function prototypes ********** static DIAL_IN_PUMP_STATE_T handleDialInPumpOffState( void ); static DIAL_IN_PUMP_STATE_T handleDialInPumpRampingUpState( void ); static DIAL_IN_PUMP_STATE_T handleDialInPumpRampingDownState( void ); static DIAL_IN_PUMP_STATE_T handleDialInPumpControlToTargetState( void ); +static void setDialInPumpControlSignalPWM( F32 newPWM ); static void stopDialInPump( void ); static void releaseDialInPumpStop( void ); static void setDialInPumpDirection( MOTOR_DIR_T dir ); static void publishDialInFlowData( void ); -static void resetDialInFlowMovingAverage( void ); -static void filterDialInFlowReadings( F32 flow ); +static void resetDialInFlowMovingAverage( void ); +static void filterDialInFlowReadings( F64 flow ); +static void updateDialInPumpSpeedAndDirectionFromHallSensors( void ); +static void checkDialInPumpRotor( void ); static void checkDialInPumpDirection( void ); +static void checkDialInPumpSpeeds( void ); static void checkDialInPumpMCCurrent( void ); -static DATA_GET_PROTOTYPE( U32, getPublishDialInFlowDataInterval ); +static void checkDialInPumpFlowRate( void ); +static F32 calcDialInFlow( void ); +static F32 dialysateInPumpPWMFromTargetFlowRate( F32 QdTarget ); +static void resetDialInPumpRotorCount( void ); -/************************************************************************* - * @brief initDialInFlow +/*********************************************************************//** + * @brief * The initDialInFlow function initializes the DialInFlow module. - * @details - * Inputs : none - * Outputs : DialInFlow module initialized. - * @param none + * @details Inputs: none + * @details Outputs: DialInFlow module initialized. * @return none *************************************************************************/ void initDialInFlow( void ) -{ - stopDialInPump(); +{ + U32 i; + + dialInFlowDataPublicationTimerCounter = DATA_PUBLISH_COUNTER_START_COUNT; + + signalDialInPumpHardStop(); setDialInPumpDirection( MOTOR_DIR_FORWARD ); - // zero rolling flow average buffer - resetDialInFlowMovingAverage(); + // Zero rolling flow average buffer + resetDialInFlowMovingAverage(); + // Zero pump rotor count + resetDialInPumpRotorCount(); + + // Zero motor hall sensors counts buffer + dipMotorSpeedCalcIdx = 0; + for ( i = 0; i < DIP_SPEED_CALC_BUFFER_LEN; i++ ) + { + dipLastMotorHallSensorCounts[ i ] = getFPGADialInPumpHallSensorCount(); + } + + // Initialize dialysate inlet flow PI controller + initializePIController( PI_CONTROLLER_ID_DIALYSATE_FLOW, MIN_DIAL_IN_PUMP_PWM_DUTY_CYCLE, + DIP_P_COEFFICIENT, DIP_I_COEFFICIENT, + MIN_DIAL_IN_PUMP_PWM_DUTY_CYCLE, MAX_DIAL_IN_PUMP_PWM_DUTY_CYCLE ); + + // Initialize persistent alarm for flow sensor signal strength too low + initPersistentAlarm( ALARM_ID_HD_DIAL_IN_FLOW_OUT_OF_RANGE, 0, DIP_MAX_FLOW_RATE_OUT_OF_RANGE_PERSIST ); + initTimeWindowedCount( TIME_WINDOWED_COUNT_DIP_COMMUTATION_ERROR, DIP_COMMUTATION_ERROR_MAX_CNT, DIP_COMMUTATION_ERROR_TIME_WIN_MS ); } -/************************************************************************* - * @brief setDialInPumpTargetFlowRate +/*********************************************************************//** + * @brief * The setDialInPumpTargetFlowRate function sets a new target flow rate and * pump direction. - * @details - * Inputs : isDialInPumpOn, dialInPumpDirectionSet - * Outputs : targetDialInFlowRate, dialInPumpdirection, dialInPumpPWMDutyCyclePct - * @param flowRate : new target dialIn flow rate - * @param dir : new dialIn flow direction + * @details Inputs: isDialInPumpOn, dialInPumpDirectionSet + * @details Outputs: targetDialInFlowRate, dialInPumpdirection, dialInPumpPWMDutyCyclePct + * @param flowRate new target dialIn flow rate + * @param dir new dialIn flow direction + * @param mode new control mode * @return TRUE if new flow rate & dir are set, FALSE if not *************************************************************************/ -BOOL setDialInPumpTargetFlowRate( U32 flowRate, MOTOR_DIR_T dir ) +BOOL setDialInPumpTargetFlowRate( U32 flowRate, MOTOR_DIR_T dir, PUMP_CONTROL_MODE_T mode ) { BOOL result = FALSE; - // direction change while pump is running is not allowed + // Direction change while pump is running is not allowed if ( ( FALSE == isDialInPumpOn ) || ( 0 == flowRate ) || ( dir == dialInPumpDirectionSet ) ) { - // verify flow rate - if ( flowRate <= MAX_DIAL_IN_FLOW_RATE ) - { - resetDialInFlowMovingAverage(); - targetDialInFlowRate.data = ( dir == MOTOR_DIR_FORWARD ? (S32)flowRate : (S32)flowRate * -1 ); - dialInPumpDirection = dir; - // set PWM duty cycle target to an estimated initial target to ramp to based on target flow rate - then we'll control to flow when ramp completed - dialInPumpPWMDutyCyclePct = DIP_PWM_FROM_ML_PER_MIN((F32)flowRate); // ~ 8% per 100 mL/min with a 10% zero offset added in (e.g. 100 mL/min = 8+10 = 18%) - // reset flow control stats - dipFlowError = 0.0; - dipFlowErrorSum = 0.0; - - switch ( dialInPumpState ) + S32 dirFlowRate = ( dir == MOTOR_DIR_FORWARD ? (S32)flowRate : (S32)flowRate * -1 ); + + // Don't interrupt pump control unless rate or mode is changing + if ( ( dirFlowRate != targetDialInFlowRate ) || ( mode != dialInPumpControlMode ) ) + { + BOOL isFlowRateInRange = ( flowRate <= MAX_DIAL_IN_FLOW_RATE ? TRUE : FALSE ); + +#ifndef _RELEASE_ + if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_PUMPS_FLOW_LIMITS ) ) + { + isFlowRateInRange = TRUE; + } +#endif + + // Verify flow rate + if ( TRUE == isFlowRateInRange ) { - case DIAL_IN_PUMP_RAMPING_UP_STATE: // see if we need to reverse direction of ramp - if ( dialInPumpPWMDutyCyclePct < dialInPumpPWMDutyCyclePctSet ) - { - dialInPumpState = DIAL_IN_PUMP_RAMPING_DOWN_STATE; - } - break; - case DIAL_IN_PUMP_RAMPING_DOWN_STATE: // see if we need to reverse direction of ramp - if ( dialInPumpPWMDutyCyclePct > dialInPumpPWMDutyCyclePctSet ) - { - dialInPumpState = DIAL_IN_PUMP_RAMPING_UP_STATE; - } - break; - case DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE: // start ramp to new target in appropriate direction - if ( dialInPumpPWMDutyCyclePctSet > dialInPumpPWMDutyCyclePct ) - { - dialInPumpState = DIAL_IN_PUMP_RAMPING_DOWN_STATE; - } - else - { - dialInPumpState = DIAL_IN_PUMP_RAMPING_UP_STATE; - } - break; - default: - // ok - not all states need to be handled here - break; + resetDialInFlowMovingAverage(); + targetDialInFlowRate = dirFlowRate; + dialInPumpDirection = dir; + dialInPumpControlMode = mode; + // Set PWM duty cycle target to an estimated initial target to ramp to based on target flow rate - then we will control to flow when ramp completed + dialInPumpPWMDutyCyclePct = ( 0 == flowRate ? DIP_PWM_ZERO_OFFSET : dialysateInPumpPWMFromTargetFlowRate( (F32)flowRate ) ); + + switch ( dialInPumpState ) + { + case DIAL_IN_PUMP_RAMPING_UP_STATE: // See if we need to reverse direction of ramp + if ( dialInPumpPWMDutyCyclePct < dialInPumpPWMDutyCyclePctSet ) + { + dialInPumpState = DIAL_IN_PUMP_RAMPING_DOWN_STATE; + } + break; + + case DIAL_IN_PUMP_RAMPING_DOWN_STATE: // See if we need to reverse direction of ramp + if ( dialInPumpPWMDutyCyclePct > dialInPumpPWMDutyCyclePctSet ) + { + dialInPumpState = DIAL_IN_PUMP_RAMPING_UP_STATE; + } + break; + + case DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE: // Start ramp to new target in appropriate direction + if ( dialInPumpPWMDutyCyclePctSet > dialInPumpPWMDutyCyclePct ) + { + dialInPumpState = DIAL_IN_PUMP_RAMPING_DOWN_STATE; + } + else + { + dialInPumpState = DIAL_IN_PUMP_RAMPING_UP_STATE; + } + break; + + default: + // Ok - not all states need to be handled here + break; + } + result = TRUE; } - result = TRUE; + + else // Requested flow rate too high + { +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_PUMPS_FLOW_LIMITS ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_DIAL_IN_FLOW_SET_TOO_HIGH, flowRate ) + } + } + } - else // requested flow rate too high - { - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_SOFTWARE_FAULT, SW_FAULT_ID_DIAL_IN_FLOW_SET_TOO_HIGH, flowRate ) - } } return result; } -/************************************************************************* - * @brief signalDialInPumpHardStop +/*********************************************************************//** + * @brief * The signalDialInPumpHardStop function stops the dialIn pump immediately. - * @details - * Inputs : none - * Outputs : DialIn pump stopped, set point reset, state changed to off - * @param none + * @details Inputs: none + * @details Outputs: DialIn pump stopped, set point reset, state changed to off * @return none *************************************************************************/ void signalDialInPumpHardStop( void ) { - targetDialInFlowRate.data = 0; + targetDialInFlowRate = 0; stopDialInPump(); dialInPumpState = DIAL_IN_PUMP_OFF_STATE; - dialInPumpPWMDutyCyclePct = 0.0; - dipFlowError = 0.0; - dipFlowErrorSum = 0.0; + dialInPumpPWMDutyCyclePct = DIP_PWM_ZERO_OFFSET; dipControlTimerCounter = 0; + resetPIController( PI_CONTROLLER_ID_DIALYSATE_FLOW, MIN_DIAL_IN_PUMP_PWM_DUTY_CYCLE ); } -/************************************************************************* - * @brief execDialInFlowMonitor +/*********************************************************************//** + * @brief + * The signalDialInPumpRotorHallSensor function handles the dialysate inlet pump rotor + * hall sensor detection. Calculates rotor speed (in RPM). Stops pump if + * there is a pending request to home the pump. + * @details Inputs: dipRotorRevStartTime, dipStopAtHomePosition + * @details Outputs: dipRotorRevStartTime, dialInPumpRotorSpeedRPM, dialysateInPumpRotorCounter, dipStopAtHomePosition + * @return none + *************************************************************************/ +void signalDialInPumpRotorHallSensor( void ) +{ + U32 rotTime = getMSTimerCount(); + U32 deltaTime = calcTimeBetween( dipRotorRevStartTime, rotTime ); + + // Calculate rotor speed (in RPM) + dialInPumpRotorSpeedRPM.data = ( 1.0 / (F32)deltaTime ) * (F32)MS_PER_SECOND * (F32)SEC_PER_MIN; + dipRotorRevStartTime = rotTime; + dialysateInPumpRotorCounter.data++; + + // If we are supposed to stop pump at home position, stop pump now. + if ( TRUE == dipStopAtHomePosition ) + { + signalDialInPumpHardStop(); + dipStopAtHomePosition = FALSE; + } +} + +/*********************************************************************//** + * @brief + * The homeDialInPump function initiates a dialysate inlet pump home operation. + * @details Inputs: dialInPumpState + * @details Outputs: dipStopAtHomePosition, dipHomeStartTime, dialysate inlet pump started (slow) + * @return none + *************************************************************************/ +BOOL homeDialInPump( void ) +{ + BOOL result = FALSE; + + if ( DIAL_IN_PUMP_OFF_STATE == dialInPumpState ) + { + dipStopAtHomePosition = TRUE; + dipHomeStartTime = getMSTimerCount(); + result = setDialInPumpTargetFlowRate( DIP_HOME_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The isDialInPumpRunning function returns whether the dialysate inlet pump + * is currently running or not. + * @details Inputs: isDialInPumpOn + * @details Outputs: none + * @return isDialInPumpOn + *************************************************************************/ +BOOL isDialInPumpRunning( void ) +{ + return isDialInPumpOn; +} + +/*********************************************************************//** + * @brief + * The isDialInPumpRampComplete function returns whether the dialysate inlet pump has + * completed its ramp up and entered control state (closed or open loop). + * @details Inputs: dialInPumpState + * @details Outputs: none + * @return TRUE if pump is in control state, FALSE if not + *************************************************************************/ +BOOL isDialInPumpRampComplete( void ) +{ + BOOL result = ( DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE == dialInPumpState ? TRUE : FALSE ); + + return result; +} + +/*********************************************************************//** + * @brief * The execDialInFlowMonitor function executes the dialIn flow monitor. - * @details - * Inputs : none - * Outputs : measuredDialInFlowRate, adcDialInPumpMCSpeedRPM, adcDialInPumpMCCurrentmA - * @param none + * @details Inputs: none + * @details Outputs: measuredDialInFlowRate, adcDialInPumpMCSpeedRPM, adcDialInPumpMCCurrentmA * @return none *************************************************************************/ void execDialInFlowMonitor( void ) -{ +{ + 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(); - + F64 dipFlow; + + // Process new dialysate flow readings + if ( TRUE == getDialysateFlowDataFreshFlag() ) + { + dipFlow = getDGDialysateFlowRateLMin() * (F64)ML_PER_LITER; // convert rate to mL/min + filterDialInFlowReadings( dipFlow ); // process the fresh dialysate flow data + measuredDialInFlowRate.data = calcDialInFlow(); // calculate the measured flow rate using blended algorithm + dialysateFlowDataFreshStatusCounter = 0; + } + else + { // Alarm if not receiving new dialysate flow readings in timely manner + if ( TRUE == isDGCommunicating() ) + { + if ( ++dialysateFlowDataFreshStatusCounter > DIP_DIALYSATE_FLOW_DATA_ALARM_THRESHOLD ) + { + filterDialInFlowReadings( 0.0 ); + activateAlarmNoData( ALARM_ID_HD_DIALYSATE_FLOW_DATA_NOT_RECEIVE ); + } + } + else + { + dialysateFlowDataFreshStatusCounter = 0; + } + } + + // Get latest pump speed and current from motor controller 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; + + // Calculate dialysate inlet pump motor speed/direction from hall sensor count + updateDialInPumpSpeedAndDirectionFromHallSensors(); - filterDialInFlowReadings( dipFlow ); - - // don't start enforcing checks until out of init/POST mode - if ( getCurrentOperationMode() != MODE_INIT ) + // Do not start enforcing checks until out of init/POST mode + if ( opMode != MODE_INIT ) { + // Check pump direction checkDialInPumpDirection(); + // Check pump controller current checkDialInPumpMCCurrent(); - } - - // publish dialIn flow data on interval + // Check pump speeds and flow + checkDialInPumpSpeeds(); + checkDialInPumpFlowRate(); + // Check for home position, zero/low speed + checkDialInPumpRotor(); + } + else + { + lastDialInPumpDirectionCount = getFPGADialInPumpHallSensorStatus() & PUMP_DIR_ERROR_COUNT_MASK; + } + + // Publish dialIn flow data on interval publishDialInFlowData(); } -/************************************************************************* - * @brief execDialInFlowController +/*********************************************************************//** + * @brief * The execDialInFlowController function executes the dialIn flow controller. - * @details - * Inputs : dialInPumpState - * Outputs : dialInPumpState - * @param none + * @details Inputs: dialInPumpState + * @details Outputs: dialInPumpState * @return none *************************************************************************/ void execDialInFlowController( void ) @@ -317,204 +555,199 @@ break; default: - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_SOFTWARE_FAULT, SW_FAULT_ID_DIAL_IN_FLOW_INVALID_DIAL_IN_PUMP_STATE, dialInPumpState ) + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_DIAL_IN_FLOW_INVALID_DIAL_IN_PUMP_STATE, dialInPumpState ) break; } } -/************************************************************************* - * @brief handleDialInPumpOffState - * The handleDialInPumpOffState function handles the dialIn pump off state \n +/*********************************************************************//** + * @brief + * The handleDialInPumpOffState function handles the dialIn pump off state * of the dialIn pump controller state machine. - * @details - * Inputs : targetDialInFlowRate, dialInPumpDirection - * Outputs : dialInPumpPWMDutyCyclePctSet, dialInPumpDirectionSet, isDialInPumpOn - * @param none + * @details Inputs: targetDialInFlowRate, dialInPumpDirection + * @details Outputs: dialInPumpPWMDutyCyclePctSet, dialInPumpDirectionSet, isDialInPumpOn * @return next state *************************************************************************/ static DIAL_IN_PUMP_STATE_T handleDialInPumpOffState( void ) { DIAL_IN_PUMP_STATE_T result = DIAL_IN_PUMP_OFF_STATE; - // if we've been given a flow rate, setup ramp up and transition to ramp up state - if ( getTargetDialInFlowRate() != 0 ) + // If we have been given a flow rate, setup ramp up and transition to ramp up state + if ( targetDialInFlowRate != 0 ) { - // set initial PWM duty cycle - dialInPumpPWMDutyCyclePctSet = MAX_DIAL_IN_PUMP_PWM_STEP_CHANGE; - etpwmSetCmpA( etpwmREG1, (U32)FLOAT_TO_INT_WITH_ROUND( dialInPumpPWMDutyCyclePctSet * (F32)(etpwmREG1->TBPRD) ) ); - // allow dialIn pump to run in requested direction + // Set initial PWM duty cycle + dialInPumpPWMDutyCyclePctSet = DIP_PWM_ZERO_OFFSET + MAX_DIAL_IN_PUMP_PWM_STEP_UP_CHANGE; + setDialInPumpControlSignalPWM( dialInPumpPWMDutyCyclePctSet ); + // Allow dialIn pump to run in requested direction setDialInPumpDirection( dialInPumpDirection ); releaseDialInPumpStop(); - // start PWM for dialIn pump - etpwmStartTBCLK(); isDialInPumpOn = TRUE; result = DIAL_IN_PUMP_RAMPING_UP_STATE; } return result; } -/************************************************************************* - * @brief handleDialInPumpRampingUpState - * The handleDialInPumpRampingUpState function handles the ramp up state \n +/*********************************************************************//** + * @brief + * The handleDialInPumpRampingUpState function handles the ramp up state * of the dialIn pump controller state machine. - * @details - * Inputs : dialInPumpPWMDutyCyclePctSet - * Outputs : dialInPumpPWMDutyCyclePctSet - * @param none + * @details Inputs: dialInPumpPWMDutyCyclePctSet + * @details Outputs: dialInPumpPWMDutyCyclePctSet * @return next state *************************************************************************/ static DIAL_IN_PUMP_STATE_T handleDialInPumpRampingUpState( void ) { DIAL_IN_PUMP_STATE_T result = DIAL_IN_PUMP_RAMPING_UP_STATE; - // have we been asked to stop the dialIn pump? - if ( 0 == getTargetDialInFlowRate() ) + // Have we been asked to stop the dialIn pump? + if ( 0 == targetDialInFlowRate ) { - // start ramp down to stop - dialInPumpPWMDutyCyclePctSet -= MAX_DIAL_IN_PUMP_PWM_STEP_CHANGE; - etpwmSetCmpA( etpwmREG1, (U32)FLOAT_TO_INT_WITH_ROUND( dialInPumpPWMDutyCyclePctSet * (F32)(etpwmREG1->TBPRD) ) ); + // Start ramp down to stop + dialInPumpPWMDutyCyclePctSet -= MAX_DIAL_IN_PUMP_PWM_STEP_DN_CHANGE; + setDialInPumpControlSignalPWM( dialInPumpPWMDutyCyclePctSet ); result = DIAL_IN_PUMP_RAMPING_DOWN_STATE; } - // have we reached end of ramp up? + // Have we reached end of ramp up? else if ( dialInPumpPWMDutyCyclePctSet >= dialInPumpPWMDutyCyclePct ) { resetDialInFlowMovingAverage(); + dialInPumpPWMDutyCyclePctSet = dialInPumpPWMDutyCyclePct; + resetPIController( PI_CONTROLLER_ID_DIALYSATE_FLOW, dialInPumpPWMDutyCyclePctSet ); + dialInPumpControlModeSet = dialInPumpControlMode; + setDialInPumpControlSignalPWM( dialInPumpPWMDutyCyclePctSet ); + dipControlTimerCounter = 0; result = DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE; } - // continue ramp up + // Continue ramp up else { - dialInPumpPWMDutyCyclePctSet += MAX_DIAL_IN_PUMP_PWM_STEP_CHANGE; - etpwmSetCmpA( etpwmREG1, (U32)FLOAT_TO_INT_WITH_ROUND( dialInPumpPWMDutyCyclePctSet * (F32)(etpwmREG1->TBPRD) ) ); + dialInPumpPWMDutyCyclePctSet += MAX_DIAL_IN_PUMP_PWM_STEP_UP_CHANGE; + setDialInPumpControlSignalPWM( dialInPumpPWMDutyCyclePctSet ); } return result; } -/************************************************************************* - * @brief handleDialInPumpRampingDownState - * The handleDialInPumpRampingDownState function handles the ramp down state \n +/*********************************************************************//** + * @brief + * The handleDialInPumpRampingDownState function handles the ramp down state * of the dialIn pump controller state machine. - * @details - * Inputs : dialInPumpPWMDutyCyclePctSet - * Outputs : dialInPumpPWMDutyCyclePctSet - * @param none + * @details Inputs: dialInPumpPWMDutyCyclePctSet + * @details Outputs: dialInPumpPWMDutyCyclePctSet * @return next state *************************************************************************/ static DIAL_IN_PUMP_STATE_T handleDialInPumpRampingDownState( void ) { DIAL_IN_PUMP_STATE_T result = DIAL_IN_PUMP_RAMPING_DOWN_STATE; - // have we essentially reached zero speed - if ( dialInPumpPWMDutyCyclePctSet < (MAX_DIAL_IN_PUMP_PWM_STEP_CHANGE + DIP_PWM_ZERO_OFFSET) ) + // Have we essentially reached zero speed + if ( dialInPumpPWMDutyCyclePctSet < (MAX_DIAL_IN_PUMP_PWM_STEP_DN_CHANGE + DIP_PWM_ZERO_OFFSET) ) { - stopDialInPump(); + signalDialInPumpHardStop(); result = DIAL_IN_PUMP_OFF_STATE; } - // have we reached end of ramp down? + // Have we reached end of ramp down? else if ( dialInPumpPWMDutyCyclePctSet <= dialInPumpPWMDutyCyclePct ) { resetDialInFlowMovingAverage(); + dialInPumpPWMDutyCyclePctSet = dialInPumpPWMDutyCyclePct; + resetPIController( PI_CONTROLLER_ID_DIALYSATE_FLOW, dialInPumpPWMDutyCyclePctSet ); + dialInPumpControlModeSet = dialInPumpControlMode; + setDialInPumpControlSignalPWM( dialInPumpPWMDutyCyclePctSet ); + dipControlTimerCounter = 0; result = DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE; } - // continue ramp down + // Continue ramp down else { - dialInPumpPWMDutyCyclePctSet -= MAX_DIAL_IN_PUMP_PWM_STEP_CHANGE; - etpwmSetCmpA( etpwmREG1, (U32)FLOAT_TO_INT_WITH_ROUND( dialInPumpPWMDutyCyclePctSet * (F32)(etpwmREG1->TBPRD) ) ); + dialInPumpPWMDutyCyclePctSet -= MAX_DIAL_IN_PUMP_PWM_STEP_DN_CHANGE; + setDialInPumpControlSignalPWM( dialInPumpPWMDutyCyclePctSet ); } return result; } -/************************************************************************* - * @brief handleDialInPumpControlToTargetState - * The handleDialInPumpControlToTargetState function handles the "control to \n +/*********************************************************************//** + * @brief + * The handleDialInPumpControlToTargetState function handles the "control to * target" state of the dialIn pump controller state machine. - * @details - * Inputs : none - * Outputs : dialInPumpState - * @param none + * @details Inputs: none + * @details Outputs: dialInPumpState * @return next state *************************************************************************/ static DIAL_IN_PUMP_STATE_T handleDialInPumpControlToTargetState( void ) { DIAL_IN_PUMP_STATE_T result = DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE; - F32 tgtFlow = (F32)getTargetDialInFlowRate(); - F32 actFlow = getMeasuredDialInFlowRate(); - F32 pTerm, iTerm; - F32 newPWM; - // control at set interval + // Control at set interval if ( ++dipControlTimerCounter >= DIP_CONTROL_INTERVAL ) { - // compute P term - if ( MOTOR_DIR_FORWARD == dialInPumpDirectionSet ) + if ( dialInPumpControlModeSet == PUMP_CONTROL_MODE_CLOSED_LOOP ) { - dipFlowError = tgtFlow - actFlow; + F32 tgtFlow = (F32)targetDialInFlowRate; + F32 actFlow = getMeasuredDialInFlowRate(); + F32 newPWM; + + newPWM = runPIController( PI_CONTROLLER_ID_DIALYSATE_FLOW, tgtFlow, actFlow ); + dialInPumpPWMDutyCyclePctSet = newPWM; + setDialInPumpControlSignalPWM( newPWM ); } - else - { - dipFlowError = (tgtFlow * -1.0) - (actFlow * -1.0); - } - pTerm = dipFlowError * DIP_P_COEFFICIENT; - pTerm = RANGE( pTerm, DIP_MIN_PWM_DC_DELTA, DIP_MAX_PWM_DC_DELTA ); - // compute I term - dipFlowErrorSum += dipFlowError; - iTerm = RANGE( dipFlowErrorSum, DIP_MIN_ERROR_SUM, DIP_MAX_ERROR_SUM ); - iTerm *= DIP_I_COEFFICIENT; - // compute new PWM duty cycle % for dialIn pump motor - newPWM = dialInPumpPWMDutyCyclePctSet + pTerm + iTerm; - newPWM = RANGE( newPWM, MIN_DIAL_IN_PUMP_PWM_DUTY_CYCLE, MAX_DIAL_IN_PUMP_PWM_DUTY_CYCLE ); - dialInPumpPWMDutyCyclePctSet = newPWM; - etpwmSetCmpA( etpwmREG1, (U32)FLOAT_TO_INT_WITH_ROUND( dialInPumpPWMDutyCyclePctSet * (F32)(etpwmREG1->TBPRD) ) ); + signalDialOutControl(); dipControlTimerCounter = 0; } return result; } -/************************************************************************* - * @brief stopDialInPump +/*********************************************************************//** + * @brief + * The setDialInPumpControlSignalPWM function sets the PWM duty cycle for + * the dialysate inlet pump to a given %. + * @details Inputs: none + * @details Outputs: dialIn pump stop signal activated, PWM duty cycle zeroed + * @param newPWM new duty cycle % to apply to PWM + * @return none + *************************************************************************/ +static void setDialInPumpControlSignalPWM( F32 newPWM ) +{ + etpwmSetCmpA( etpwmREG2, (U32)( (S32)( ( newPWM * (F32)(etpwmREG2->TBPRD) ) + FLOAT_TO_INT_ROUNDUP_OFFSET ) ) ); +} + +/*********************************************************************//** + * @brief * The stopDialInPump function sets the dialIn pump stop signal. - * @details - * Inputs : none - * Outputs : dialIn pump stop signal activated, PWM duty cycle zeroed - * @param none + * @details Inputs: none + * @details Outputs: dialIn pump stop signal activated, PWM duty cycle zeroed * @return none *************************************************************************/ static void stopDialInPump( void ) { isDialInPumpOn = FALSE; - dialInPumpPWMDutyCyclePctSet = 0.0; - etpwmSetCmpA( etpwmREG1, 0 ); - etpwmStopTBCLK(); + dialInPumpPWMDutyCyclePctSet = DIP_PWM_ZERO_OFFSET; + etpwmSetCmpA( etpwmREG2, 0 ); SET_DIP_STOP(); } -/************************************************************************* - * @brief releaseDialInPumpStop +/*********************************************************************//** + * @brief * The releaseDialInPumpStop function clears the dialIn pump stop signal. - * @details - * Inputs : none - * Outputs : dialIn pump stop signal - * @param none + * @details Inputs: none + * @details Outputs: dialIn pump stop signal * @return none *************************************************************************/ static void releaseDialInPumpStop( void ) { CLR_DIP_STOP(); } -/************************************************************************* - * @brief setDialInPumpDirection - * The setDialInPumpDirection function sets the set dialIn pump direction to \n +/*********************************************************************//** + * @brief + * The setDialInPumpDirection function sets the set dialIn pump direction to * the given direction. - * @details - * Inputs : dialInPumpState - * Outputs : dialInPumpState - * @param dir : dialIn pump direction to set + * @details Inputs: dialInPumpState + * @details Outputs: dialInPumpState + * @param dir dialIn pump direction to set * @return none *************************************************************************/ static void setDialInPumpDirection( MOTOR_DIR_T dir ) @@ -532,330 +765,730 @@ break; default: - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_SOFTWARE_FAULT, SW_FAULT_ID_DIAL_IN_FLOW_INVALID_DIAL_IN_PUMP_DIRECTION, dir ) + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_DIAL_IN_FLOW_INVALID_DIAL_IN_PUMP_DIRECTION, dir ) break; } } + +/*********************************************************************//** + * @brief + * The calcDialInFlow function calculates an estimated dialysate flow rate from + * a blended algorithm based on flow meter data and dialin pump speed and tubing wear + * (measured from count of rotor revolutions since cartridge install). + * @details Inputs: DialInPumpRotorCounter, targetDialInFlowRate, dialInPumpRotorSpeedRPM + * @details Outputs: measuredDialInFlowRate (ml/min) + * @return none + *************************************************************************/ +static F32 calcDialInFlow( void ) +{ + + F32 estimatedFlow; + F32 QdTarget = getTargetDialInFlowRate(); + + if (QdTarget >= DIAL_IN_FLOW_QHIGHTRANSITION) + { + //At higher flow rates, use the flow meter value. Assume target flow rate has already checked for out of bounds on high end. + estimatedFlow = filteredDialInFlowMeterReading; + } + else + { //Use blended or calculated flow rate + F32 pumpRPM = getMeasuredDialInPumpRotorSpeed(); + U32 r = getDialInPumpRotorCount(); + U32 rotCnt = CAP( r, DIAL_IN_MAX_ROTOR_COUNT_FOR_WEAR ); + F32 wear = DIAL_IN_FLOW_WEAR_A_TERM * (F32)rotCnt + DIAL_IN_FLOW_WEAR_B_TERM; + F32 Pest = DIAL_IN_FLOW_PEST_A_TERM * ( QdTarget * QdTarget ) + DIAL_IN_FLOW_PEST_B_TERM * QdTarget + DIAL_IN_FLOW_PEST_C_TERM; + F32 alpha = wear * Pest + DIAL_IN_FLOW_A_ZERO; + F32 calculatedFlow = ( alpha * pumpRPM * ML_PER_LITER ) / DIP_REV_PER_LITER; + + if ( ( QdTarget < DIAL_IN_FLOW_QHIGHTRANSITION ) && ( QdTarget >= DIAL_IN_FLOW_QLOWTRANSITION ) ) + { // use blended flow rate calculation + estimatedFlow = ( ( QdTarget - DIAL_IN_FLOW_QLOWTRANSITION ) / ( DIAL_IN_FLOW_QHIGHTRANSITION - DIAL_IN_FLOW_QLOWTRANSITION ) ) * calculatedFlow + + ( ( DIAL_IN_FLOW_QHIGHTRANSITION - QdTarget ) / ( DIAL_IN_FLOW_QHIGHTRANSITION - DIAL_IN_FLOW_QLOWTRANSITION ) ) * filteredDialInFlowMeterReading; + } + else + { // use calculated flow rate. Assume target flow rate has already checked for out of bounds on low end. + estimatedFlow = calculatedFlow; + } + } + + return estimatedFlow; +} + + +/*********************************************************************//** + * @brief + * The dialysateInPumpPWMFromTargetFlowRate function calculates a motor PWM setting + * from the estimator based on target flow rate and tubing wear. + * @details Inputs: dialInPumpRotorCounter + * @details Outputs: none + * @param QdTarget target dialysate flow rate + * @return Motor PWM value for given target flow rate + *************************************************************************/ +static F32 dialysateInPumpPWMFromTargetFlowRate( F32 QdTarget ) +{ + F32 uncorrectedPWM = DIP_PWM_FROM_ML_PER_MIN( QdTarget ); + U32 r = getDialInPumpRotorCount(); + U32 rotCnt = CAP( r, DIAL_IN_MAX_ROTOR_COUNT_FOR_WEAR ); + F32 wear = DIAL_IN_FLOW_WEAR_A_TERM * (F32)rotCnt + DIAL_IN_FLOW_WEAR_B_TERM; + F32 Pest = DIAL_IN_FLOW_PEST_A_TERM * (QdTarget * QdTarget) + DIAL_IN_FLOW_PEST_B_TERM * QdTarget + DIAL_IN_FLOW_PEST_C_TERM; + F32 alpha = wear * Pest + DIAL_IN_FLOW_A_ZERO; + F32 correctedPWM = ( uncorrectedPWM / alpha ); + + return correctedPWM; +} + +/*********************************************************************//** + * @brief + * The resetDialInPumpRotorCount function resets the dialin pump rotor counter + * that is a proxy for cartridge wear. Call this function after a new cartridge + * has been installed. + * @details Inputs: none + * @details Outputs: dialInPumpRotorCounter is reset to 0 + * @return none + *************************************************************************/ +void resetDialInPumpRotorCount( void ) +{ +#ifndef _RELEASE_ + if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_ENABLE_WORN_OUT_CARTRIDGE ) ) + { + dialysateInPumpRotorCounter.data = DIAL_IN_MAX_ROTOR_COUNT_FOR_WEAR; + } + else +#endif + { + dialysateInPumpRotorCounter.data = 0; + } +} -/************************************************************************* - * @brief getPublishDialInFlowDataInterval - * The getPublishDialInFlowDataInterval function gets the dialIn flow data \n - * publication interval. - * @details - * Inputs : dialInFlowDataPublishInterval - * Outputs : none - * @param none - * @return the current dialIn flow data publication interval (in ms). - *************************************************************************/ -DATA_GET( U32, getPublishDialInFlowDataInterval, dialInFlowDataPublishInterval ) - -/************************************************************************* - * @brief getTargetDialInFlowRate - * The getTargetDialInFlowRate function gets the current target dialIn flow \n +/*********************************************************************//** + * @brief + * The getTargetDialInFlowRate function gets the current target dialIn flow * rate. - * @details - * Inputs : targetDialInFlowRate - * Outputs : none - * @param none + * @details Inputs: targetDialInFlowRate + * @details Outputs: none * @return the current target dialIn flow rate (in mL/min). *************************************************************************/ -DATA_GET( S32, getTargetDialInFlowRate, targetDialInFlowRate ) +S32 getTargetDialInFlowRate( void ) +{ + return targetDialInFlowRate; +} -/************************************************************************* - * @brief getMeasuredDialInFlowRate - * The getMeasuredDialInFlowRate function gets the measured dialIn flow \n +/*********************************************************************//** + * @brief + * The getMeasuredDialInFlowRate function gets the measured dialIn flow * rate. - * @details - * Inputs : measuredDialInFlowRate - * Outputs : none - * @param none + * @details Inputs: measuredDialInFlowRate + * @details Outputs: none * @return the current dialIn flow rate (in mL/min). *************************************************************************/ -DATA_GET( F32, getMeasuredDialInFlowRate, measuredDialInFlowRate ) - -/************************************************************************* - * @brief getMeasuredDialInPumpRotorSpeed - * The getMeasuredDialInPumpRotorSpeed function gets the measured dialIn flow \n +F32 getMeasuredDialInFlowRate( void ) +{ + F32 result = measuredDialInFlowRate.data; + + if ( OVERRIDE_KEY == measuredDialInFlowRate.override ) + { + result = measuredDialInFlowRate.ovData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getMeasuredDialInPumpRotorSpeed function gets the measured dialIn flow * rate. - * @details - * Inputs : dialInPumpRotorSpeedRPM - * Outputs : none - * @param none + * @details Inputs: dialInPumpRotorSpeedRPM + * @details Outputs: none * @return the current dialIn flow rate (in mL/min). *************************************************************************/ -DATA_GET( F32, getMeasuredDialInPumpRotorSpeed, dialInPumpRotorSpeedRPM ) +F32 getMeasuredDialInPumpRotorSpeed( void ) +{ + F32 result = dialInPumpRotorSpeedRPM.data; + + if ( OVERRIDE_KEY == dialInPumpRotorSpeedRPM.override ) + { + result = dialInPumpRotorSpeedRPM.ovData; + } + + return result; +} -/************************************************************************* - * @brief getMeasuredDialInPumpSpeed - * The getMeasuredDialInPumpSpeed function gets the measured dialIn flow \n +/*********************************************************************//** + * @brief + * The getMeasuredDialInPumpSpeed function gets the measured dialIn flow * rate. - * @details - * Inputs : dialInPumpSpeedRPM - * Outputs : none - * @param none + * @details Inputs: dialInPumpSpeedRPM + * @details Outputs: none * @return the current dialIn flow rate (in mL/min). *************************************************************************/ -DATA_GET( F32, getMeasuredDialInPumpSpeed, dialInPumpSpeedRPM ) +F32 getMeasuredDialInPumpSpeed( void ) +{ + F32 result = dialInPumpSpeedRPM.data; + + if ( OVERRIDE_KEY == dialInPumpSpeedRPM.override ) + { + result = dialInPumpSpeedRPM.ovData; + } + + return result; +} -/************************************************************************* - * @brief getMeasuredDialInPumpMCSpeed - * The getMeasuredDialInPumpMCSpeed function gets the measured dialIn pump \n +/*********************************************************************//** + * @brief + * The getMeasuredDialInPumpMCSpeed function gets the measured dialIn pump * speed. - * @details - * Inputs : adcDialInPumpMCSpeedRPM - * Outputs : none - * @param none + * @details Inputs: adcDialInPumpMCSpeedRPM + * @details Outputs: none * @return the current dialIn pump speed (in RPM). *************************************************************************/ -DATA_GET( F32, getMeasuredDialInPumpMCSpeed, adcDialInPumpMCSpeedRPM ) +F32 getMeasuredDialInPumpMCSpeed( void ) +{ + F32 result = adcDialInPumpMCSpeedRPM.data; + + if ( OVERRIDE_KEY == adcDialInPumpMCSpeedRPM.override ) + { + result = adcDialInPumpMCSpeedRPM.ovData; + } + + return result; +} -/************************************************************************* - * @brief getMeasuredDialInPumpMCCurrent - * The getMeasuredDialInPumpMCCurrent function gets the measured dialIn pump \n +/*********************************************************************//** + * @brief + * The getMeasuredDialInPumpMCCurrent function gets the measured dialIn pump * current. - * @details - * Inputs : adcDialInPumpMCCurrentmA - * Outputs : none - * @param none + * @details Inputs: adcDialInPumpMCCurrentmA + * @details Outputs: none * @return the current dialIn pump current (in mA). *************************************************************************/ -DATA_GET( F32, getMeasuredDialInPumpMCCurrent, adcDialInPumpMCCurrentmA ) +F32 getMeasuredDialInPumpMCCurrent( void ) +{ + F32 result = adcDialInPumpMCCurrentmA.data; + + if ( OVERRIDE_KEY == adcDialInPumpMCCurrentmA.override ) + { + result = adcDialInPumpMCCurrentmA.ovData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getDialInPumpPWMDutyCyclePct function gets the current dialIn pump + * PWM duty cycle percentage. + * @details Inputs: dialInPumpPWMDutyCyclePctSet + * @details Outputs: none + * @param init Flag indicates whether or not initial PWM duty cycle is wanted + * @return the current dialIn pump PWM duty cycle percentage (0..1). + *************************************************************************/ +F32 getDialInPumpPWMDutyCyclePct( BOOL init ) +{ + F32 result = dialInPumpPWMDutyCyclePctSet; + + if ( TRUE == init ) + { + result = dialInPumpPWMDutyCyclePct; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getDialInPumpRotorCount function returns the current count for the + * dialin pump rotor revolution counter. + * @details Inputs: dialInPumpRotorCounter + * @details Outputs: none + * @return dialInPumpRotorCounter + *************************************************************************/ +U32 getDialInPumpRotorCount( void ) +{ + U32 result = dialysateInPumpRotorCounter.data; + + if ( OVERRIDE_KEY == dialysateInPumpRotorCounter.override ) + { + result = dialysateInPumpRotorCounter.ovData; + } + + return result; +} -/************************************************************************* - * @brief publishDialInFlowData - * The publishDialInFlowData function publishes dialIn flow data at the set \n +/*********************************************************************//** + * @brief + * The getPumpRotorErrorPersistTime function calculates the Rotor error persist time. + * @details Inputs: none + * @details Outputs: none + * @param mtr_rpm, RPM of the motor. + * gear_ratio, Gear ratio of motor vs motor + * @return Persistent error test time in (ms) + *************************************************************************/ +U32 getPumpRotorErrorPersistTime( F32 mtr_rpm, F32 gear_ratio ) +{ + U32 err_persist_time = HEX_32_BIT_FULL_SCALE; // 49 days + + if ( mtr_rpm > 0 ) + { + /// Calculate persist time for rotor speed error condition. + err_persist_time = ( ( DIP_ROTOR_ERROR_PERSIST_ROTATION_MIN / ( mtr_rpm / gear_ratio / SEC_PER_MIN ) ) * MS_PER_SECOND ); + } + + return err_persist_time; +} + +/*********************************************************************//** + * @brief + * The publishDialInFlowData function publishes dialIn flow data at the set * interval. - * @details - * Inputs : target flow rate, measured flow rate, measured MC speed, \n + * @details Inputs: target flow rate, measured flow rate, measured MC speed, * measured MC current - * Outputs : DialIn flow data is published to CAN bus. - * @param none + * @details Outputs: DialIn flow data is published to CAN bus. * @return none *************************************************************************/ static void publishDialInFlowData( void ) { - // publish dialIn flow data on interval - if ( ++dialInFlowDataPublicationTimerCounter > getPublishDialInFlowDataInterval() ) + // Publish dialIn flow data on interval + if ( ++dialInFlowDataPublicationTimerCounter >= getU32OverrideValue( &dialInFlowDataPublishInterval ) ) { - S32 flowStPt = (S32)getTargetDialInFlowRate(); - F32 measFlow = getMeasuredDialInFlowRate(); - F32 measRotSpd = getMeasuredDialInPumpRotorSpeed(); - F32 measSpd = getMeasuredDialInPumpSpeed(); - F32 measMCSpd = getMeasuredDialInPumpMCSpeed(); - F32 measMCCurr = getMeasuredDialInPumpMCCurrent(); -#ifdef DEBUG_ENABLED - // TODO - temporary debug code - remove later - S32 pwm = (S32)( 100.0 * dialInPumpPWMDutyCyclePctSet ); - char debugFlowStr[ 256 ]; - - sprintf( debugFlowStr, "Target Flow:%5d, Meas. Flow:%5d, Speed:%5d RPM, Current:%5d mA, PWM:%5d \n", flowStPt, (S32)measFlow, (S32)measMCSpd, (S32)measMCCurr, pwm ); - sendDebugData( (U08*)debugFlowStr, strlen(debugFlowStr) ); -#endif - // TODO - broadcastDialInFlowData( flowStPt, measFlow, measRotSpd, measSpd, measMCSpd, measMCCurr ); + DIALIN_PUMP_STATUS_PAYLOAD_T payload; + + payload.setPoint = targetDialInFlowRate; + payload.measFlow = getMeasuredDialInFlowRate(); + payload.measRotorSpd = getMeasuredDialInPumpRotorSpeed(); + payload.measPumpSpd = getMeasuredDialInPumpSpeed(); + payload.measMCSpd = getMeasuredDialInPumpMCSpeed(); + payload.measMCCurr = getMeasuredDialInPumpMCCurrent(); + payload.pwmDC = dialInPumpPWMDutyCyclePctSet * FRACTION_TO_PERCENT_FACTOR; + payload.flowSigStrength = 0.0; + broadcastData( MSG_ID_DIALYSATE_FLOW_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&payload, sizeof( DIALIN_PUMP_STATUS_PAYLOAD_T ) ); dialInFlowDataPublicationTimerCounter = 0; } } -/************************************************************************* - * @brief resetDialInFlowMovingAverage - * The resetDialInFlowMovingAverage function re-sizes and re-initializes the \n +/*********************************************************************//** + * @brief + * The resetDialInFlowMovingAverage function resets the properties of the * dialIn flow moving average sample buffer. - * @details - * Inputs : none - * Outputs : flowReadingsTotal, flowReadingsIdx, flowReadingsCount all set to zero. - * @param initFlow : the new dialIn flow set pt. - * @param flowDir : the new set direction - * @return none + * @details Inputs: none + * @details Outputs: flowReadingsTotal, flowReadingsIdx, flowReadingsCount all set to zero. + * @return none *************************************************************************/ static void resetDialInFlowMovingAverage( void ) { - flowReadingsTotal = 0.0; flowReadingsIdx = 0; flowReadingsCount = 0; + flowReadingsTotal = 0.0; dipControlTimerCounter = 0; } -/************************************************************************* - * @brief filterDialInFlowReadings - * The filterDialInFlowReadings function adds a new flow sample to the filter \n - * if decimation rate for current set point calls for it. - * @details - * Inputs : none - * Outputs : flowReadings[], flowReadingsIdx, flowReadingsCount - * @param flow : newest dialIn flow sample +/*********************************************************************//** + * @brief + * The filterDialInFlowReadings function adds a new flow sample to the filter. + * @details Inputs: none + * @details Outputs: flowReadings[], flowReadingsIdx, flowReadingsCount, flowReadingsTotal * @return none *************************************************************************/ -static void filterDialInFlowReadings( F32 flow ) +void filterDialInFlowReadings( F64 flow ) { - BOOL addSampleToFilter = FALSE; - - if ( ( targetDialInFlowRate.data < MIN_DIAL_IN_FLOW_RATE ) || ( targetDialInFlowRate.data >= MAX_DIAL_IN_FLOW_RATE ) ) + if ( flowReadingsCount >= SIZE_OF_ROLLING_AVG ) { - addSampleToFilter = TRUE; + flowReadingsTotal -= flowReadings[ flowReadingsIdx ]; } - else + flowReadings[ flowReadingsIdx ] = flow; + flowReadingsTotal += flow; + flowReadingsIdx = INC_WRAP( flowReadingsIdx, 0, SIZE_OF_ROLLING_AVG - 1 ); + flowReadingsCount = INC_CAP( flowReadingsCount, SIZE_OF_ROLLING_AVG ); + filteredDialInFlowMeterReading = (F32)( flowReadingsTotal / (F64)flowReadingsCount ); +} + +/*********************************************************************//** + * @brief + * The updateDialInPumpSpeedAndDirectionFromHallSensors function calculates + * the dialysate inlet pump motor speed and direction from hall sensor counter on + * a 1 second interval. + * @details Inputs: dipLastMotorHallSensorCount, dipMotorSpeedCalcTimerCtr, current count from FPGA + * @details Outputs: dipMotorDirectionFromHallSensors, dialInPumpSpeedRPM + * @return none + *************************************************************************/ +static void updateDialInPumpSpeedAndDirectionFromHallSensors( void ) +{ + if ( ++dipMotorSpeedCalcTimerCtr >= DIP_SPEED_CALC_INTERVAL ) { - switch ( flowReadingsTmrCtr ) - { - case 0: - addSampleToFilter = TRUE; - break; + U16 dipMotorHallSensorCount = getFPGADialInPumpHallSensorCount(); + U32 nextIdx = INC_WRAP( dipMotorSpeedCalcIdx, 0, DIP_SPEED_CALC_BUFFER_LEN - 1 ); + U16 incDelta = ( dipMotorHallSensorCount >= dipLastMotorHallSensorCounts[ nextIdx ] ? \ + dipMotorHallSensorCount - dipLastMotorHallSensorCounts[ nextIdx ] : \ + ( HEX_64_K - dipLastMotorHallSensorCounts[ nextIdx ] ) + dipMotorHallSensorCount ); + U16 decDelta = ( 0 == incDelta ? 0xFFFF : HEX_64_K - incDelta ); + U16 delta; - case 1: - addSampleToFilter = FALSE; - break; - - case 2: - if ( targetDialInFlowRate.data >= 400 ) - { - addSampleToFilter = TRUE; - } - break; - - case 3: - if ( targetDialInFlowRate.data >= 200 ) - { - addSampleToFilter = TRUE; - } - break; - - case 4: - if ( targetDialInFlowRate.data >= 300 ) - { - addSampleToFilter = TRUE; - } - break; - - default: - // TODO - s/w fault? - break; + // Determine dialysate inlet pump speed/direction from delta hall sensor count since last interval + if ( incDelta < decDelta ) + { + delta = incDelta; + dialInPumpSpeedRPM.data = ( (F32)delta / (F32)DIP_HALL_EDGE_COUNTS_PER_REV ) * (F32)SEC_PER_MIN; } + else + { + delta = decDelta; + dialInPumpSpeedRPM.data = ( (F32)delta / (F32)DIP_HALL_EDGE_COUNTS_PER_REV ) * (F32)SEC_PER_MIN * -1.0; + } + + // Update last count for next time + dipLastMotorHallSensorCounts[ nextIdx ] = dipMotorHallSensorCount; + dipMotorSpeedCalcIdx = nextIdx; + dipMotorSpeedCalcTimerCtr = 0; } +} - if ( TRUE == addSampleToFilter ) +/*********************************************************************//** + * @brief + * The checkDialInPumpRotor function checks the rotor for the dialysate inlet + * pump. If homing, this function will stop when hall sensor detected. If pump + * is off or running very slowly, rotor speed will be set to zero. + * @details Inputs: dipStopAtHomePosition, dipHomeStartTime, dipRotorRevStartTime + * @details Outputs: pump may be stopped if homing, dialInPumpRotorSpeedRPM may be set to zero. + * @return none + *************************************************************************/ +static void checkDialInPumpRotor( void ) +{ + // If homing, check timeout + if ( ( TRUE == dipStopAtHomePosition ) && ( TRUE == didTimeout( dipHomeStartTime, DIP_HOME_TIMEOUT_MS ) ) ) { - if ( flowReadingsCount >= SIZE_OF_ROLLING_AVG ) - { - flowReadingsTotal -= flowReadings[ flowReadingsIdx ]; - } - flowReadings[ flowReadingsIdx ] = flow; - flowReadingsTotal += flow; - flowReadingsIdx = INC_WRAP( flowReadingsIdx, 0, SIZE_OF_ROLLING_AVG - 1 ); - flowReadingsCount = INC_CAP( flowReadingsCount, SIZE_OF_ROLLING_AVG ); - measuredDialInFlowRate.data = flowReadingsTotal / (F32)flowReadingsCount; + signalDialInPumpHardStop(); + dipStopAtHomePosition = FALSE; + // TODO - alarm??? } - flowReadingsTmrCtr = INC_WRAP( flowReadingsTmrCtr, 0, MAX_FLOW_FILTER_INTERVAL - 1 ); + // If pump is stopped or running very slowly, set rotor speed to zero + if ( TRUE == didTimeout( dipRotorRevStartTime, DIP_HOME_TIMEOUT_MS ) ) + { + dialInPumpRotorSpeedRPM.data = 0.0; + } } -/************************************************************************* - * @brief checkDialInPumpDirection - * The checkDialInPumpDirection function checks the set direction vs. \n +/*********************************************************************//** + * @brief + * The checkDialInPumpDirection function checks the set direction vs. * the direction implied by the sign of the measured MC speed. - * @details - * Inputs : - * Outputs : - * @param none + * @details Inputs: adcDialInPumpMCSpeedRPM, dialInPumpDirectionSet, dialInPumpState + * @details Outputs: none * @return none *************************************************************************/ static void checkDialInPumpDirection( void ) { - MOTOR_DIR_T dipMCDir; - if ( DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE == dialInPumpState ) { - // check set direction vs. direction from sign of motor controller speed - dipMCDir = ( getMeasuredDialInPumpMCSpeed() >= 0.0 ? MOTOR_DIR_FORWARD : MOTOR_DIR_REVERSE ); - if ( dialInPumpDirectionSet != dipMCDir ) + MOTOR_DIR_T dipMCDir, dipDir; + U08 dirErrorCnt = getFPGADialInPumpHallSensorStatus() & PUMP_DIR_ERROR_COUNT_MASK; + F32 measMCSpeed = getMeasuredDialInPumpMCSpeed(); + BOOL minDirSpeed = ( measMCSpeed >= DIP_MIN_DIR_CHECK_SPEED_RPM ? TRUE : FALSE ); + BOOL isHallSensorFailed = ( TRUE == minDirSpeed && lastDialInPumpDirectionCount != dirErrorCnt ? TRUE : FALSE ); + + // Check pump direction error count + if ( ( TRUE == isHallSensorFailed ) && ( TRUE == incTimeWindowedCount( TIME_WINDOWED_COUNT_DIP_COMMUTATION_ERROR ) ) ) + { +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_PUMP_DIRECTION_CHECKS ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_PUMP_DIRECTION_STATUS_ERROR, (U32)HD_PUMP_DIALYSATE_INLET_PUMP ) + } + } + lastDialInPumpDirectionCount = dirErrorCnt; + + dipMCDir = ( getMeasuredDialInPumpMCSpeed() >= 0.0 ? MOTOR_DIR_FORWARD : MOTOR_DIR_REVERSE ); + dipDir = ( getMeasuredDialInPumpSpeed() >= 0.0 ? MOTOR_DIR_FORWARD : MOTOR_DIR_REVERSE ); + + // Check set direction vs. direction from hall sensors + if ( ( dialInPumpDirectionSet != dipDir ) && ( TRUE == minDirSpeed ) ) { - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DIAL_IN_PUMP_MC_DIRECTION_CHECK, (U32)dialInPumpDirectionSet, (U32)dipMCDir ) + if ( ++errorDialInPumpDirectionPersistTimerCtr >= DIP_DIRECTION_ERROR_PERSIST ) + { +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_PUMP_DIRECTION_CHECKS ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DIAL_IN_PUMP_MC_DIRECTION_CHECK, (U32)dialInPumpDirectionSet, (U32)dipDir ) + } + } } + // Check set direction vs. direction from sign of motor controller speed + else if ( ( dialInPumpDirectionSet != dipMCDir ) && ( TRUE == minDirSpeed ) ) + { + if ( ++errorDialInPumpDirectionPersistTimerCtr >= DIP_DIRECTION_ERROR_PERSIST ) + { +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_PUMP_DIRECTION_CHECKS ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DIAL_IN_PUMP_MC_DIRECTION_CHECK, (U32)dialInPumpDirectionSet, (U32)dipMCDir ) + } + } + } + else + { + errorDialInPumpDirectionPersistTimerCtr = 0; + } + } + else + { + errorDialInPumpDirectionPersistTimerCtr = 0; } } -/************************************************************************* - * @brief checkDialInPumpMCCurrent - * The checkDialInPumpMCCurrent function checks the measured MC current vs. \n - * the set state of the dialIn pump (stopped or running). - * @details - * Inputs : - * Outputs : - * @param none +/*********************************************************************//** + * @brief + * The checkDialInPumpSpeeds function checks several aspects of the dialysate + * inlet pump speed. + * 1. while pump is commanded off, measured motor speed should be < limit. + * 2. while pump is controlling, measured motor speed should be within allowed range of commanded speed. + * 3. measured motor speed should be within allowed range of measured rotor speed. + * All 3 checks have a persistence time that must be met before an alarm is triggered. + * @details Inputs: targetDialInFlowRate, dialInPumpSpeedRPM, dialInPumpRotorSpeedRPM + * @details Outputs: alarm(s) may be triggered * @return none *************************************************************************/ -static void checkDialInPumpMCCurrent( void ) +static void checkDialInPumpSpeeds( void ) { - F32 dipCurr; + F32 measMotorSpeed = getMeasuredDialInPumpSpeed(); + F32 measMCMotorSpeed = fabs( getMeasuredDialInPumpMCSpeed() ); - // dialIn pump should be off - if ( DIAL_IN_PUMP_OFF_STATE == dialInPumpState ) + // Check for pump running while commanded off + if ( 0 == targetDialInFlowRate ) { - dipCurr = fabs( getMeasuredDialInPumpMCCurrent() ); - if ( dipCurr > DIP_MAX_CURR_WHEN_STOPPED_MA ) + if ( measMotorSpeed > DIP_MAX_MOTOR_SPEED_WHILE_OFF_RPM ) { - dipCurrErrorDurationCtr += TASK_PRIORITY_INTERVAL; - if ( dipCurrErrorDurationCtr > DIP_MAX_CURR_ERROR_DURATION_MS ) - { - SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DIAL_IN_PUMP_MC_CURRENT_CHECK, getMeasuredDialInPumpMCCurrent() ); + if ( ++errorDialInMotorOffPersistTimerCtr >= DIP_OFF_ERROR_PERSIST ) + { +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_PUMP_SPEED_CHECKS ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DIAL_IN_PUMP_OFF_CHECK, measMotorSpeed ); + activateSafetyShutdown(); + } } } else { - dipCurrErrorDurationCtr = 0; + errorDialInMotorOffPersistTimerCtr = 0; } } - // dialIn pump should be running else { - dipCurr = fabs( getMeasuredDialInPumpMCCurrent() ); - if ( ( dipCurr < DIP_MIN_CURR_WHEN_RUNNING_MA ) || ( dipCurr > DIP_MAX_CURR_WHEN_RUNNING_MA ) ) + errorDialInMotorOffPersistTimerCtr = 0; + } + + // Checks that only occur when pump is running (and beyond ramp). + if ( DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE == dialInPumpState ) + { + F32 cmdMotorSpeed = DIP_PWM_TO_MOTOR_SPEED_RPM( dialInPumpPWMDutyCyclePctSet ); + F32 deltaMotorSpeed = fabs( measMotorSpeed - cmdMotorSpeed ); + F32 deltaMCMotorSpeed = fabs( measMCMotorSpeed - cmdMotorSpeed ); + F32 measRotorSpeed = fabs( getMeasuredDialInPumpRotorSpeed() ); + F32 measMotorSpeedInRotorRPM = measMotorSpeed / DIP_GEAR_RATIO; + F32 deltaRotorSpeed = fabs( measRotorSpeed - measMotorSpeedInRotorRPM ); + + // Check measured motor speed vs. commanded motor speed while controlling to target + if ( ( deltaMotorSpeed > DIP_MAX_MOTOR_SPEED_ERROR_RPM ) || ( deltaMCMotorSpeed > DIP_MAX_MOTOR_SPEED_ERROR_RPM ) ) { - dipCurrErrorDurationCtr += TASK_PRIORITY_INTERVAL; - if ( dipCurrErrorDurationCtr > DIP_MAX_CURR_ERROR_DURATION_MS ) - { - SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DIAL_IN_PUMP_MC_CURRENT_CHECK, getMeasuredDialInPumpMCCurrent() ); + if ( ++errorDialInMotorSpeedPersistTimerCtr >= DIP_MOTOR_SPEED_ERROR_PERSIST ) + { +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_PUMP_SPEED_CHECKS ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DIAL_IN_PUMP_MOTOR_SPEED_CHECK, cmdMotorSpeed, measMotorSpeed ); + } } } else { - dipCurrErrorDurationCtr = 0; + errorDialInMotorSpeedPersistTimerCtr = 0; } + + // Check measured rotor speed vs. measured motor speed while controlling to target + if ( deltaRotorSpeed > DIP_MAX_ROTOR_VS_MOTOR_DIFF_RPM ) + { + if ( ++errorDialInRotorSpeedPersistTimerCtr >= ( getPumpRotorErrorPersistTime( measMotorSpeed, DIP_GEAR_RATIO ) / TASK_PRIORITY_INTERVAL ) ) + { +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_PUMP_SPEED_CHECKS ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DIAL_IN_PUMP_ROTOR_SPEED_CHECK, measRotorSpeed, measMotorSpeed ); + } + } + } + else + { + errorDialInRotorSpeedPersistTimerCtr = 0; + } + } + else + { + errorDialInMotorSpeedPersistTimerCtr = 0; + errorDialInRotorSpeedPersistTimerCtr = 0; + } } -/************************************************************************* - * @brief execDialInFlowTest - * The execDialInFlowTest function executes the state machine for the \n - * DialInFlow self test. - * @details - * Inputs : none - * Outputs : none - * @param none - * @return the current state of the DialInFlow self test. +/*********************************************************************//** + * @brief + * The checkDialInPumpFlowRate function checks the measured dialysate flow + * rate is in range. + * @details Inputs: measuredDialInFlowRate + * @details Outputs: alarm may be triggered + * @return none *************************************************************************/ -SELF_TEST_STATUS_T execDialInFlowTest( void ) +static void checkDialInPumpFlowRate( void ) { - SELF_TEST_STATUS_T result = SELF_TEST_STATUS_FAILED; + F32 flow = getMeasuredDialInFlowRate(); + + // Range check on measure DPi flow rate. + if ( ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_DIAL_IN_FLOW_OUT_OF_RANGE, ( flow > DIP_MAX_FLOW_RATE ) || ( flow < DIP_MIN_FLOW_RATE ) ) ) ) + { +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_PUMP_FLOW_CHECKS ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_HD_DIAL_IN_FLOW_OUT_OF_RANGE, flow ); + } + } + + // Check only performed while in treatment mode and while we are in control to target state + if ( ( MODE_TREA == getCurrentOperationMode() ) && ( DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE == dialInPumpState ) ) + { + F32 flow = (F32)targetDialInFlowRate; + F32 speed = getMeasuredDialInPumpSpeed(); + F32 impliedSpeed = ( flow * DIP_ML_PER_MIN_TO_PUMP_RPM_FACTOR * DIP_GEAR_RATIO ); + F32 delta = fabs( speed - impliedSpeed ); + + if ( delta > DIP_MAX_FLOW_VS_SPEED_DIFF_RPM ) + { + if ( ++errorDialInFlowVsMotorSpeedPersistTimerCtr >= DIP_FLOW_VS_SPEED_PERSIST ) + { +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_PUMP_FLOW_CHECKS ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DIAL_IN_PUMP_FLOW_VS_MOTOR_SPEED_CHECK, flow, speed ); + } + } + } + else + { + errorDialInFlowVsMotorSpeedPersistTimerCtr = 0; + } + } + else + { + errorDialInFlowVsMotorSpeedPersistTimerCtr = 0; + } +} - // TODO - implement self test(s) - +/*********************************************************************//** + * @brief + * The checkDialInPumpMCCurrent function checks the measured MC current vs. + * the set state of the dialIn pump (stopped or running). + * @details Inputs: dialInPumpState, dipCurrErrorDurationCtr, adcDialInPumpMCCurrentmA + * @details Outputs: none + * @return none + *************************************************************************/ +static void checkDialInPumpMCCurrent( void ) +{ + F32 dipCurr; + + if ( FALSE == isAlarmActive( ALARM_ID_HD_AC_POWER_LOST ) ) + { + // DialIn pump should be off + if ( DIAL_IN_PUMP_OFF_STATE == dialInPumpState ) + { + dipCurr = fabs( getMeasuredDialInPumpMCCurrent() ); + if ( dipCurr > DIP_MAX_CURR_WHEN_STOPPED_MA ) + { + dipCurrErrorDurationCtr += TASK_PRIORITY_INTERVAL; + if ( dipCurrErrorDurationCtr > DIP_MAX_CURR_ERROR_DURATION_MS ) + { +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_MOTOR_CURRNT_CHECKS ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DIAL_IN_PUMP_MC_CURRENT_CHECK, getMeasuredDialInPumpMCCurrent() ); + } + } + } + else + { + dipCurrErrorDurationCtr = 0; + } + } + // DialIn pump should be running + else + { + dipCurr = fabs( getMeasuredDialInPumpMCCurrent() ); + if ( dipCurr > DIP_MAX_CURR_WHEN_RUNNING_MA ) + { + dipCurrErrorDurationCtr += TASK_PRIORITY_INTERVAL; + if ( dipCurrErrorDurationCtr > DIP_MAX_CURR_ERROR_DURATION_MS ) + { + #ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_MOTOR_CURRNT_CHECKS ) != SW_CONFIG_ENABLE_VALUE ) + #endif + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DIAL_IN_PUMP_MC_CURRENT_CHECK, getMeasuredDialInPumpMCCurrent() ); + } + } + } + else + { + dipCurrErrorDurationCtr = 0; + } + } + } +} + +/*********************************************************************//** + * @brief + * The execDialInFlowTest function executes the state machine for the + * DialInFlow self-test. + * @details Inputs: none + * @details Outputs: dialInPumpCalRecord + * @return the current state of the DialInFlow self-test. + *************************************************************************/ +SELF_TEST_STATUS_T execDialInFlowTest( void ) +{ + SELF_TEST_STATUS_T result = SELF_TEST_STATUS_PASSED; + + BOOL calStatus = getNVRecord2Driver( GET_CAL_PUMPS, (U08*)&dialInPumpCalRecord, sizeof( HD_PUMPS_CAL_RECORD_T ), + NUM_OF_CAL_DATA_HD_PUMPS, ALARM_ID_NO_ALARM ); + + if ( TRUE == calStatus ) + { + result = SELF_TEST_STATUS_PASSED; + } + else + { + result = SELF_TEST_STATUS_FAILED; + } + return result; } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ + - -/************************************************************************* - * @brief testSetDialInFlowDataPublishIntervalOverride - * The testSetDialInFlowDataPublishIntervalOverride function overrides the \n +/*********************************************************************//** + * @brief + * The testSetDialInFlowDataPublishIntervalOverride function overrides the * dialIn flow data publish interval. - * @details - * Inputs : none - * Outputs : dialInFlowDataPublishInterval - * @param value : override dialIn flow data publish interval with (in ms) + * @details Inputs: none + * @details Outputs: dialInFlowDataPublishInterval + * @param value override dialIn flow data publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetDialInFlowDataPublishIntervalOverride( U32 value ) @@ -874,13 +1507,12 @@ return result; } -/************************************************************************* - * @brief testResetDialInFlowDataPublishIntervalOverride - * The testResetDialInFlowDataPublishIntervalOverride function resets the override \n +/*********************************************************************//** + * @brief + * The testResetDialInFlowDataPublishIntervalOverride function resets the override * of the dialIn flow data publish interval. - * @details - * Inputs : none - * Outputs : dialInFlowDataPublishInterval + * @details Inputs: none + * @details Outputs: dialInFlowDataPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetDialInFlowDataPublishIntervalOverride( void ) @@ -897,88 +1529,338 @@ return result; } -/************************************************************************* - * @brief testSetTargetDialInFlowRateOverride and testResetTargetDialInFlowRateOverride - * The testSetTargetDialInFlowRateOverride function overrides the target \n - * dialIn flow rate. \n - * The testResetTargetDialInFlowRateOverride function resets the override of the \n - * target dialIn flow rate. - * @details - * Inputs : none - * Outputs : targetDialInFlowRate - * @param value : override target dialIn flow rate (in mL/min) +/*********************************************************************//** + * @brief + * The testSetTargetDialInFlowRateOverride function overrides the target + * dialysate inlet flow rate.n + * @details Inputs: none + * @details Outputs: targetDialInFlowRate + * @param value override target dialysate inlet flow rate (in mL/min) + * @param ctrlMode override pump control mode to this mode (0 = closed loop, 1 = open loop) * @return TRUE if override successful, FALSE if not *************************************************************************/ -DATA_OVERRIDE_FUNC( S32, testSetTargetDialInFlowRateOverride, testResetTargetDialInFlowRateOverride, targetDialInFlowRate ) +BOOL testSetTargetDialInFlowRateOverride( S32 value, U32 ctrlMode ) +{ + BOOL result = FALSE; -/************************************************************************* - * @brief testSetMeasuredDialInFlowRateOverride and testResetMeasuredDialInFlowRateOverride - * The testResetMeasuredDialInFlowRateOverride function overrides the measured \n - * dialIn flow rate. \n - * The testResetOffButtonStateOverride function resets the override of the \n + if ( TRUE == isTestingActivated() ) + { + MOTOR_DIR_T dir; + + if ( value < 0 ) + { + dir = MOTOR_DIR_REVERSE; + } + else + { + dir = MOTOR_DIR_FORWARD; + } + if ( ctrlMode < NUM_OF_PUMP_CONTROL_MODES ) + { + if ( 0 == value ) + { + signalDialInPumpHardStop(); + } + else + { + result = setDialInPumpTargetFlowRate( abs(value), dir, (PUMP_CONTROL_MODE_T)ctrlMode ); + } + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetMeasuredDialInFlowRateOverride function overrides the measured + * dialIn flow rate. + * @details Inputs: none + * @details Outputs: measuredDialInFlowRate + * @param value override measured dialIn flow rate (in mL/min) + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetMeasuredDialInFlowRateOverride( F32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + measuredDialInFlowRate.ovData = value; + measuredDialInFlowRate.override = OVERRIDE_KEY; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetMeasuredDialInFlowRateOverride function resets the override of the * measured dialIn flow rate. - * @details - * Inputs : none - * Outputs : measuredDialInFlowRate - * @param value : override measured dialIn flow rate (in mL/min) + * @details Inputs: none + * @details Outputs: measuredDialInFlowRate + * @return TRUE if reset successful, FALSE if not + *************************************************************************/ +BOOL testResetMeasuredDialInFlowRateOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + measuredDialInFlowRate.override = OVERRIDE_RESET; + measuredDialInFlowRate.ovData = measuredDialInFlowRate.ovInitData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetMeasuredDialInPumpRotorSpeedOverride function overrides the measured + * dialIn pump rotor speed. + * @details Inputs: none + * @details Outputs: dialInPumpRotorSpeedRPM + * @param value override measured dialIn pump rotor speed (in RPM) * @return TRUE if override successful, FALSE if not *************************************************************************/ -DATA_OVERRIDE_FUNC( F32, testSetMeasuredDialInFlowRateOverride, testResetMeasuredDialInFlowRateOverride, measuredDialInFlowRate ) +BOOL testSetMeasuredDialInPumpRotorSpeedOverride( F32 value ) +{ + BOOL result = FALSE; -/************************************************************************* - * @brief testSetMeasuredDialInPumpRotorSpeedOverride and testResetMeasuredDialInPumpRotorSpeedOverride - * The testSetMeasuredDialInPumpRotorSpeedOverride function overrides the measured \n - * dialIn pump rotor speed. \n - * The testResetMeasuredDialInPumpRotorSpeedOverride function resets the override of the \n + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + dialInPumpRotorSpeedRPM.ovData = value; + dialInPumpRotorSpeedRPM.override = OVERRIDE_KEY; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetMeasuredDialInPumpRotorSpeedOverride function resets the override of the * measured dialIn pump rotor speed. - * @details - * Inputs : none - * Outputs : dialInPumpRotorSpeedRPM - * @param value : override measured dialIn pump rotor speed (in RPM) + * @details Inputs: none + * @details Outputs: dialInPumpRotorSpeedRPM + * @return TRUE if reset successful, FALSE if not + *************************************************************************/ +BOOL testResetMeasuredDialInPumpRotorSpeedOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + dialInPumpRotorSpeedRPM.override = OVERRIDE_RESET; + dialInPumpRotorSpeedRPM.ovData = dialInPumpRotorSpeedRPM.ovInitData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetMeasuredDialInPumpSpeedOverride function overrides the measured + * dialIn pump motor speed. + * @details Inputs: none + * @details Outputs: dialInPumpSpeedRPM + * @param value override measured dialIn pump motor speed (in RPM) * @return TRUE if override successful, FALSE if not *************************************************************************/ -DATA_OVERRIDE_FUNC( F32, testSetMeasuredDialInPumpRotorSpeedOverride, testResetMeasuredDialInPumpRotorSpeedOverride, dialInPumpRotorSpeedRPM ) +BOOL testSetMeasuredDialInPumpSpeedOverride( F32 value ) +{ + BOOL result = FALSE; -/************************************************************************* - * @brief testSetMeasuredDialInPumpSpeedOverride and testResetMeasuredDialInPumpSpeedOverride - * The testSetMeasuredDialInPumpSpeedOverride function overrides the measured \n - * dialIn pump motor speed. \n - * The testResetMeasuredDialInPumpSpeedOverride function resets the override of the \n + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + dialInPumpSpeedRPM.ovData = value; + dialInPumpSpeedRPM.override = OVERRIDE_KEY; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetMeasuredDialInPumpSpeedOverride function resets the override of the * measured dialIn pump motor speed. - * @details - * Inputs : none - * Outputs : dialInPumpSpeedRPM - * @param value : override measured dialIn pump motor speed (in RPM) + * @details Inputs: none + * @details Outputs: dialInPumpSpeedRPM + * @return TRUE if reset successful, FALSE if not + *************************************************************************/ +BOOL testResetMeasuredDialInPumpSpeedOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + dialInPumpSpeedRPM.override = OVERRIDE_RESET; + dialInPumpSpeedRPM.ovData = dialInPumpSpeedRPM.ovInitData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetMeasuredDialInPumpMCSpeedOverride function overrides the measured + * dialIn pump motor speed. + * @details Inputs: none + * @details Outputs: adcDialInPumpMCSpeedRPM + * @param value override measured dialIn pump speed (in RPM) * @return TRUE if override successful, FALSE if not *************************************************************************/ -DATA_OVERRIDE_FUNC( F32, testSetMeasuredDialInPumpSpeedOverride, testResetMeasuredDialInPumpSpeedOverride, dialInPumpSpeedRPM ) +BOOL testSetMeasuredDialInPumpMCSpeedOverride( F32 value ) +{ + BOOL result = FALSE; -/************************************************************************* - * @brief testSetMeasuredDialInPumpMCSpeedOverride and testResetMeasuredDialInPumpMCSpeedOverride - * The testSetMeasuredDialInPumpMCSpeedOverride function overrides the measured \n - * dialIn pump motor speed. \n - * The testResetMeasuredDialInPumpMCSpeedOverride function resets the override of the \n + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + adcDialInPumpMCSpeedRPM.ovData = value; + adcDialInPumpMCSpeedRPM.override = OVERRIDE_KEY; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetMeasuredDialInPumpMCSpeedOverride function resets the override of the * measured dialIn pump motor speed. - * @details - * Inputs : none - * Outputs : adcDialInPumpMCSpeedRPM - * @param value : override measured dialIn pump speed (in RPM) + * @details Inputs: none + * @details Outputs: adcDialInPumpMCSpeedRPM + * @return TRUE if reset successful, FALSE if not + *************************************************************************/ +BOOL testResetMeasuredDialInPumpMCSpeedOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + adcDialInPumpMCSpeedRPM.override = OVERRIDE_RESET; + adcDialInPumpMCSpeedRPM.ovData = adcDialInPumpMCSpeedRPM.ovInitData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetMeasuredDialInPumpMCCurrentOverride function overrides the measured + * dialIn pump motor current. + * @details Inputs: none + * @details Outputs: adcDialInPumpMCCurrentmA + * @param value override measured dialIn pump current (in mA) * @return TRUE if override successful, FALSE if not *************************************************************************/ -DATA_OVERRIDE_FUNC( F32, testSetMeasuredDialInPumpMCSpeedOverride, testResetMeasuredDialInPumpMCSpeedOverride, adcDialInPumpMCSpeedRPM ) +BOOL testSetMeasuredDialInPumpMCCurrentOverride( F32 value ) +{ + BOOL result = FALSE; -/************************************************************************* - * @brief testSetMeasuredDialInPumpMCCurrentOverride and testResetMeasuredDialInPumpMCCurrentOverride - * The testSetMeasuredDialInPumpMCCurrentOverride function overrides the measured \n - * dialIn pump motor current. \n - * The testResetMeasuredDialInPumpMCCurrentOverride function resets the override of the \n + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + adcDialInPumpMCCurrentmA.ovData = value; + adcDialInPumpMCCurrentmA.override = OVERRIDE_KEY; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetMeasuredDialInPumpMCCurrentOverride function resets the override of the * measured dialIn pump motor current. - * @details - * Inputs : none - * Outputs : adcDialInPumpMCCurrentmA - * @param value : override measured dialIn pump current (in mA) - * @return TRUE if override successful, FALSE if not + * @details Inputs: none + * @details Outputs: adcDialInPumpMCCurrentmA + * @return TRUE if reset successful, FALSE if not *************************************************************************/ -DATA_OVERRIDE_FUNC( F32, testSetMeasuredDialInPumpMCCurrentOverride, testResetMeasuredDialInPumpMCCurrentOverride, adcDialInPumpMCCurrentmA ) +BOOL testResetMeasuredDialInPumpMCCurrentOverride( void ) +{ + BOOL result = FALSE; + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + adcDialInPumpMCCurrentmA.override = OVERRIDE_RESET; + adcDialInPumpMCCurrentmA.ovData = adcDialInPumpMCCurrentmA.ovInitData; + } + return result; +} + +/*********************************************************************//** + * @brief + * The testSetDialInPumpTargetDutyCycle function sets the duty cycle of the + * dialysate inlet pump by calling setDialInPumpTargetFlowRate. + * @details Inputs: none + * @details Outputs: none + * @param value duty cycle of the dialysate inlet pump (as a percentage). + * @return TRUE if reset successful, FALSE if not + *************************************************************************/ +BOOL testSetDialInPumpTargetDutyCycle( F32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + setDialInPumpTargetFlowRate( (U32)DIP_ML_PER_MIN_FROM_PWM(value), MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetDialInPumpRotorCountOverride function overrides the dialin pump + * rotor counter value. + * @details Inputs: none + * @details Outputs: dialInPumpRotorCounter + * @param value override dialin pump rotor counter value + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetDialInPumpRotorCountOverride( U32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + dialysateInPumpRotorCounter.ovData = value; + dialysateInPumpRotorCounter.override = OVERRIDE_KEY; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetDialInPumpRotorCountOverride function resets the override + * of the dialin pump rotor counter. + * @details Inputs: none + * @details Outputs: dialysateInPumpRotorCounter + * @return TRUE if reset successful, FALSE if not + *************************************************************************/ +BOOL testResetDialysateInPumpRotorCountOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + dialysateInPumpRotorCounter.override = OVERRIDE_RESET; + dialysateInPumpRotorCounter.ovData = dialysateInPumpRotorCounter.ovInitData; + } + + return result; +} + +/**@}*/