Index: firmware/App/Controllers/DialInFlow.c =================================================================== diff -u -r9a9d04b84f4345fca87fb14d26f09d497b08aae8 -r94895e32fe18e78b98fe3bb7786838cf00afdbfa --- firmware/App/Controllers/DialInFlow.c (.../DialInFlow.c) (revision 9a9d04b84f4345fca87fb14d26f09d497b08aae8) +++ firmware/App/Controllers/DialInFlow.c (.../DialInFlow.c) (revision 94895e32fe18e78b98fe3bb7786838cf00afdbfa) @@ -22,7 +22,6 @@ #include "gio.h" #include "mibspi.h" -#include "Common.h" #include "FPGA.h" #include "InternalADC.h" #include "OperationModes.h" @@ -40,34 +39,32 @@ #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_CHANGE 0.005 // duty cycle TODO - fixed or parameterized or set in motor controller? +#define MAX_DIAL_IN_PUMP_PWM_STEP_CHANGE 0.01 // 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_CONTROL_INTERVAL ( 1000 / TASK_GENERAL_INTERVAL ) // interval (ms/task time) at which the dialIn pump is controlled #define DIP_P_COEFFICIENT 0.00005 // P term for dialIn pump control #define DIP_I_COEFFICIENT 0.00015 // I term for dialIn pump control -#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_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_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 DIP_SPEED_ADC_TO_RPM_FACTOR 1.280938 // conversion factor from ADC counts to RPM for dialIn pump motor +#define DIP_CURRENT_ADC_TO_MA_FACTOR 3.002 // conversion factor from ADC counts to mA for dialIn pump motor -#define DIP_REV_PER_LITER 124.0 // rotor revolutions per liter +#define DIP_REV_PER_LITER 150.24 // rotor revolutions per liter #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.00042 // ~24 BP motor RPM = 1% PWM duty cycle +#define DIP_MOTOR_RPM_TO_PWM_DC_FACTOR 0.00035 // ~28 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 DIAL_IN_PUMP_ADC_FULL_SCALE_V 3.0 // BP analog signals are 0-3V (while int. ADC ref V is 3.3V) -#define DIAL_IN_PUMP_ADC_MID_PT_BITS ( (F32)( INT_ADC_FULL_SCALE_BITS >> 1 ) * ( DIAL_IN_PUMP_ADC_FULL_SCALE_V / INT_ADC_REF_V ) ) -#define SIGN_FROM_12_BIT_VALUE(v) ( (S16)(v) - (S16)DIAL_IN_PUMP_ADC_MID_PT_BITS ) +#define DIAL_IN_PUMP_ADC_FULL_SCALE_V 3.0 // BP analog signals are 0-3V (while int. ADC ref V may be different) +#define DIAL_IN_PUMP_ADC_ZERO ( (F32)( INT_ADC_ZERO ) * ( DIAL_IN_PUMP_ADC_FULL_SCALE_V / INT_ADC_REF_V ) ) +#define SIGN_FROM_12_BIT_VALUE(v) ( (S16)(v) - (S16)DIAL_IN_PUMP_ADC_ZERO ) #define DIAL_IN_FLOW_SAMPLE_FREQ ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) #define SIZE_OF_ROLLING_AVG ( DIAL_IN_FLOW_SAMPLE_FREQ * 2 ) // measured dialIn flow is filtered w/ moving average @@ -95,19 +92,26 @@ #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 ) +#ifndef BREADBOARD_TARGET + #define SET_DIP_STOP() gioSetBit( gioPORTA, STOP_DI_PUMP_GIO_PORT_PIN, PIN_SIGNAL_LOW ) + #define CLR_DIP_STOP() gioSetBit( gioPORTA, STOP_DI_PUMP_GIO_PORT_PIN, PIN_SIGNAL_HIGH ) +#else + #define SET_DIP_STOP() gioSetBit( gioPORTA, STOP_DI_PUMP_GIO_PORT_PIN, PIN_SIGNAL_HIGH ) + #define CLR_DIP_STOP() gioSetBit( gioPORTA, STOP_DI_PUMP_GIO_PORT_PIN, PIN_SIGNAL_LOW ) +#endif // ********** 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 = 5; // 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 = 5; // 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 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 @@ -179,9 +183,10 @@ * 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; @@ -194,6 +199,7 @@ resetDialInFlowMovingAverage(); targetDialInFlowRate.data = ( dir == MOTOR_DIR_FORWARD ? (S32)flowRate : (S32)flowRate * -1 ); dialInPumpDirection = dir; + dialInPumpControlMode = mode; // 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%) @@ -275,7 +281,7 @@ filterDialInFlowReadings( dipFlow ); - // don't start enforcing checks until out of init/POST mode + // don't start enforcing checks until out of init/POST mode if ( getCurrentOperationMode() != MODE_INIT ) { checkDialInPumpDirection(); @@ -377,7 +383,14 @@ else if ( dialInPumpPWMDutyCyclePctSet >= dialInPumpPWMDutyCyclePct ) { resetDialInFlowMovingAverage(); - resetPIController( PI_CONTROLLER_ID_DIALYSATE_FLOW, MIN_DIAL_IN_PUMP_PWM_DUTY_CYCLE ); + resetPIController( PI_CONTROLLER_ID_DIALYSATE_FLOW, dialInPumpPWMDutyCyclePctSet ); + dialInPumpControlModeSet = dialInPumpControlMode; + // if open loop mode, set PWM to requested duty cycle where it will stay during control state + if ( dialInPumpControlModeSet == PUMP_CONTROL_MODE_OPEN_LOOP ) + { + dialInPumpPWMDutyCyclePctSet = dialInPumpPWMDutyCyclePct; + setDialInPumpControlSignalPWM( dialInPumpPWMDutyCyclePct ); + } result = DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE; } // continue ramp up @@ -414,7 +427,14 @@ else if ( dialInPumpPWMDutyCyclePctSet <= dialInPumpPWMDutyCyclePct ) { resetDialInFlowMovingAverage(); - resetPIController( PI_CONTROLLER_ID_DIALYSATE_FLOW, MIN_DIAL_IN_PUMP_PWM_DUTY_CYCLE ); + resetPIController( PI_CONTROLLER_ID_DIALYSATE_FLOW, dialInPumpPWMDutyCyclePctSet ); + dialInPumpControlModeSet = dialInPumpControlMode; + // if open loop mode, set PWM to requested duty cycle where it will stay during control state + if ( dialInPumpControlModeSet == PUMP_CONTROL_MODE_OPEN_LOOP ) + { + dialInPumpPWMDutyCyclePctSet = dialInPumpPWMDutyCyclePct; + setDialInPumpControlSignalPWM( dialInPumpPWMDutyCyclePct ); + } result = DIAL_IN_PUMP_CONTROL_TO_TARGET_STATE; } // continue ramp down @@ -440,16 +460,20 @@ 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 newPWM; // control at set interval if ( ++dipControlTimerCounter >= DIP_CONTROL_INTERVAL ) { - newPWM = runPIController( PI_CONTROLLER_ID_DIALYSATE_FLOW, tgtFlow, actFlow ); - dialInPumpPWMDutyCyclePctSet = newPWM; - setDialInPumpControlSignalPWM( newPWM ); + if ( dialInPumpControlModeSet == PUMP_CONTROL_MODE_CLOSED_LOOP ) + { + F32 tgtFlow = (F32)getTargetDialInFlowRate(); + F32 actFlow = getMeasuredDialInFlowRate(); + F32 newPWM; + + newPWM = runPIController( PI_CONTROLLER_ID_DIALYSATE_FLOW, tgtFlow, actFlow ); + dialInPumpPWMDutyCyclePctSet = newPWM; + setDialInPumpControlSignalPWM( newPWM ); + } dipControlTimerCounter = 0; } @@ -630,21 +654,25 @@ static void publishDialInFlowData( void ) { // publish dialIn flow data on interval - if ( ++dialInFlowDataPublicationTimerCounter > getPublishDialInFlowDataInterval() ) + if ( ++dialInFlowDataPublicationTimerCounter >= getPublishDialInFlowDataInterval() ) { S32 flowStPt = (S32)getTargetDialInFlowRate(); +#ifndef SHOW_RAW_FLOW_VALUES F32 measFlow = getMeasuredDialInFlowRate(); +#else + F32 measFlow = getFPGADialysateFlow(); +#endif F32 measRotSpd = getMeasuredDialInPumpRotorSpeed(); F32 measSpd = getMeasuredDialInPumpSpeed(); F32 measMCSpd = getMeasuredDialInPumpMCSpeed(); F32 measMCCurr = getMeasuredDialInPumpMCCurrent(); F32 pumpPWMPctDutyCycle = dialInPumpPWMDutyCyclePctSet * FRACTION_TO_PERCENT_FACTOR; #ifdef DEBUG_ENABLED - // TODO - temporary debug code - remove later - char debugFlowStr[ 256 ]; - - sprintf( debugFlowStr, "Dial. Set Pt:%5d, Meas. Flow:%5d, Speed:%5d RPM, Current:%5d mA, PWM:%5d \n", flowStPt, (S32)measFlow, (S32)measMCSpd, (S32)measMCCurr, (S32)pumpPWMPctDutyCycle ); - sendDebugData( (U08*)debugFlowStr, strlen(debugFlowStr) ); +// // TODO - temporary debug code - remove later +// char debugFlowStr[ 256 ]; +// +// sprintf( debugFlowStr, "Dial. Set Pt:%5d, Meas. Flow:%5d, Speed:%5d RPM, Current:%5d mA, PWM:%5d \n", flowStPt, (S32)measFlow, (S32)measMCSpd, (S32)measMCCurr, (S32)pumpPWMPctDutyCycle ); +// sendDebugData( (U08*)debugFlowStr, strlen(debugFlowStr) ); #endif broadcastDialInFlowData( flowStPt, measFlow, measRotSpd, measSpd, measMCSpd, measMCCurr, pumpPWMPctDutyCycle ); dialInFlowDataPublicationTimerCounter = 0; @@ -791,7 +819,9 @@ dipCurrErrorDurationCtr += TASK_PRIORITY_INTERVAL; if ( dipCurrErrorDurationCtr > DIP_MAX_CURR_ERROR_DURATION_MS ) { +#ifndef DISABLE_MOTOR_CURRENT_ERRORS SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DIAL_IN_PUMP_MC_CURRENT_CHECK, getMeasuredDialInPumpMCCurrent() ); +#endif } } else @@ -808,7 +838,9 @@ dipCurrErrorDurationCtr += TASK_PRIORITY_INTERVAL; if ( dipCurrErrorDurationCtr > DIP_MAX_CURR_ERROR_DURATION_MS ) { +#ifndef DISABLE_MOTOR_CURRENT_ERRORS SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DIAL_IN_PUMP_MC_CURRENT_CHECK, getMeasuredDialInPumpMCCurrent() ); +#endif } } else @@ -893,20 +925,67 @@ } /************************************************************************* - * @brief testSetTargetDialInFlowRateOverride and testResetTargetDialInFlowRateOverride + * @brief * The testSetTargetDialInFlowRateOverride function overrides the target \n - * dialIn flow rate. \n - * The testResetTargetDialInFlowRateOverride function resets the override of the \n - * target dialIn flow rate. + * dialysate inlet flow rate. \n * @details * Inputs : none * Outputs : targetDialInFlowRate - * @param value : override target dialIn flow rate (in mL/min) + * @param value : override target dialysate inlet flow rate (in mL/min) * @return TRUE if override successful, FALSE if not *************************************************************************/ -DATA_OVERRIDE_FUNC( S32, testSetTargetDialInFlowRateOverride, testResetTargetDialInFlowRateOverride, targetDialInFlowRate ) +BOOL testSetTargetDialInFlowRateOverride( S32 value ) +{ + BOOL result = FALSE; + if ( TRUE == isTestingActivated() ) + { + MOTOR_DIR_T dir; + + if ( value < 0 ) + { + dir = MOTOR_DIR_REVERSE; + } + else + { + dir = MOTOR_DIR_FORWARD; + } + targetDialInFlowRate.ovInitData = targetDialInFlowRate.data; // backup current target flow rate + targetDialInFlowRate.ovData = value; + targetDialInFlowRate.override = OVERRIDE_KEY; + result = setDialInPumpTargetFlowRate( ABS(value), dir, dialInPumpControlMode ); + } + + return result; +} + /************************************************************************* + * @brief + * The testResetTargetDialInFlowRateOverride function resets the override of the \n + * target dialysate inlet flow rate. + * @details + * Inputs : none + * Outputs : targetDialInFlowRate + * @param none + * @return TRUE if override reset successful, FALSE if not + *************************************************************************/ +BOOL testResetTargetDialInFlowRateOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + targetDialInFlowRate.data = targetDialInFlowRate.ovInitData; // restore pre-override target flow rate + targetDialInFlowRate.override = OVERRIDE_RESET; + targetDialInFlowRate.ovInitData = 0; + targetDialInFlowRate.ovData = 0; + result = setDialInPumpTargetFlowRate( targetDialInFlowRate.data, dialInPumpDirection, dialInPumpControlMode ); + } + + return result; +} + +/************************************************************************* * @brief testSetMeasuredDialInFlowRateOverride and testResetMeasuredDialInFlowRateOverride * The testResetMeasuredDialInFlowRateOverride function overrides the measured \n * dialIn flow rate. \n