Index: firmware/App/Controllers/Heaters.c =================================================================== diff -u -r60ec2f1256b02ee0a6d4346877494ce1bda55ab2 -r03e051ef654a1bff100da02483645c4e9d0b7a30 --- firmware/App/Controllers/Heaters.c (.../Heaters.c) (revision 60ec2f1256b02ee0a6d4346877494ce1bda55ab2) +++ firmware/App/Controllers/Heaters.c (.../Heaters.c) (revision 03e051ef654a1bff100da02483645c4e9d0b7a30) @@ -70,26 +70,29 @@ #define TEMPERATURES_MOVING_AVG_SIZE 3U ///< Heaters ramp state temperatures moving average size. #define DELTA_TEMPERATURE_TIME_COSNTANT_C 8.6 ///< Delta temperature calculated from time constant. +#define MAXIMUM_ALLOWED_TARGET_TEMPERATURE_DEVIATION_C 0.25 ///< Maximum allowed temperature deviation from target temperature in C. +#define PRIMARY_HEATER_DUTY_CYCLE_PER_TEMPERATURE_C 0.03 ///< Primary heaters duty cycle per temperature in C. static const F32 WATER_SPECIFIC_HEAT_DIVIDED_BY_MINUTES = 4184 / SEC_PER_MIN; ///< Water specific heat in J/KgC / 60. static const F32 PRIMARY_HEATERS_MAXIMUM_POWER_WATTS = 475 + 237.5; ///< Primary heaters maximum power (main primary = 475W and small primary = 237.5W). +/// Heaters exec states typedef enum Heaters_Exec_States { HEATER_EXEC_STATE_OFF = 0, ///< Heater exec state off. - HEATER_EXEC_STATE_RAMP_TO_TARGET, ///< Heater exec state ramp to target. - HEATER_EXEC_STATE_CONTROL_TO_TARGET, ///< Heater exec state control to target. + HEATER_EXEC_STATE_PRIMARY_RAMP_TO_TARGET, ///< Heater exec state ramp to target. + HEATER_EXEC_STATE_PRIMARY_CONTROL_TO_TARGET, ///< Heater exec state control to target. HEATER_EXEC_STATE_CONTROL_TO_DISINFECT_TARGET, ///< Heater exec state control to disinfect (heat or chemical) target. + HEATER_EXEC_STATE_TRIMMER_RAMP_TO_TARGET, + HEATER_EXEC_STATE_TRIMMER_CONTROL_TO_TARGET, NUM_OF_HEATERS_STATE, ///< Number of heaters state. } HEATERS_STATE_T; /// Heaters data structure typedef struct { - F32 targetTemp; // TODO do we need this anymore? ///< Heater target temperature. - F32 originalTargetTemp; ///< Heater original target temperature set by user. + F32 targetTemp; ///< Heater target temperature. HEATERS_STATE_T state; ///< Heater state. - TEMPERATURE_SENSORS_T feedbackSensor; // TODO do we need this? ///< Heater feedback sensor for controlling. U32 controlTimerCounter; // TODO remove? Maybe use in heat disinfect ///< Heater control timer counter. BOOL startHeaterSignal; ///< Heater start indication flag. BOOL isHeaterOn; ///< Heater on/off status flag. @@ -101,7 +104,6 @@ F32 heaterEfficiency; ///< Heater efficiency during the run. BOOL hasTargetBeenReached; ///< Heater flag to indicate whether the target temperature has been reached. - PI_CONTROLLER_ID_T controllerID; ///< Heater PI controller ID TODO remove this? U32 tempOutOfRangeTimer; ///< Heater temperature out of range timer TODO remove once the mechanical thermal cutoff was implemented BOOL isHeaterTempOutOfRange; ///< Heater temperature out of range flag indicator TODO remove once the mechanical thermal cutoff was implemented } HEATER_STATUS_T; @@ -130,8 +132,6 @@ static void checkPrimaryHeaterTempSensors( void ); static void checkTrimmerHeaterTempSensors( void ); static void monitorHeatersVoltage( void ); -static void checkHeaterOnStatus( DG_HEATERS_T heater ); -static void getHeatersEfficiencyFromRTCRAM( void ); /*********************************************************************//** * @brief @@ -146,31 +146,22 @@ DG_HEATERS_T heater; voltageMonitorTimeCounter = 0; - operationMode = 0; + operationMode = 0; for ( heater = DG_PRIMARY_HEATER; heater < NUM_OF_DG_HEATERS; heater++ ) { heatersStatus[ heater ].controlTimerCounter = 0; - // The default feedback sensor of the primary heater is TPo but it changes to THd in heat disinfect - // The default feedback sensor of the trimmer heater is TDi all the time - heatersStatus[ heater ].feedbackSensor = ( DG_PRIMARY_HEATER == heater ? TEMPSENSORS_OUTLET_PRIMARY_HEATER : TEMPSENSORS_INLET_DIALYSATE ); heatersStatus[ heater ].startHeaterSignal = FALSE; heatersStatus[ heater ].tempOutOfRangeTimer = 0; heatersStatus[ heater ].isHeaterTempOutOfRange = FALSE; heatersStatus[ heater ].state = HEATER_EXEC_STATE_OFF; heatersStatus[ heater ].targetTemp = 0.0; - heatersStatus[ heater ].originalTargetTemp = 0.0; heatersStatus[ heater ].dutycycle = 0.0; heatersStatus[ heater ].targetROFlow = 0.0; - heatersStatus[ heater ].controllerID = ( DG_PRIMARY_HEATER == heater ? 4 : PI_CONTROLLER_ID_TRIMMER_HEATER ); // TODO remove or refactor? heatersStatus[ heater ].hasTargetTempChanged = FALSE; heatersStatus[ heater ].heaterEfficiency = 1.0; // Assuming 100% efficiency during initialization until it is updated heatersStatus[ heater ].hasTargetBeenReached = FALSE; } - - // Initialize the PI controller for the trimmer heater - initializePIController( PI_CONTROLLER_ID_TRIMMER_HEATER, HEATERS_MIN_DUTY_CYCLE, TRIMMER_HEATER_P_COEFFICIENT, TRIMMER_HEATER_I_COEFFICIENT, - HEATERS_MIN_DUTY_CYCLE, HEATERS_MAX_DUTY_CYCLE ); // TODO remove? } /*********************************************************************//** @@ -193,7 +184,7 @@ // Check if the requested temperature is within the allowed range if ( ( targetTemperature >= MINIMUM_TARGET_TEMPERATURE ) && ( targetTemperature <= MAXIMUM_TARGET_TEMPERATURE ) ) { - heatersStatus[ heater ].originalTargetTemp = targetTemperature; + heatersStatus[ heater ].targetTemp = targetTemperature; heatersStatus[ heater ].hasTargetTempChanged = TRUE; // TODO alarm if temperature if out of range or just reject? } @@ -279,11 +270,11 @@ heatersStatus[ heater ].state = handleHeaterStateOff( heater ); break; - case HEATER_EXEC_STATE_RAMP_TO_TARGET: + case HEATER_EXEC_STATE_PRIMARY_RAMP_TO_TARGET: heatersStatus[ heater ].state = handleHeaterStateRampToTarget( heater ); break; - case HEATER_EXEC_STATE_CONTROL_TO_TARGET: + case HEATER_EXEC_STATE_PRIMARY_CONTROL_TO_TARGET: heatersStatus[ heater ].state = handleHeaterStateControlToTarget( heater ); break; @@ -431,7 +422,7 @@ heatersStatus[ heater ].startHeaterSignal = FALSE; // Turn on the heater - state = HEATER_EXEC_STATE_RAMP_TO_TARGET; + state = HEATER_EXEC_STATE_PRIMARY_RAMP_TO_TARGET; } return state; @@ -448,29 +439,26 @@ *************************************************************************/ static HEATERS_STATE_T handleHeaterStateRampToTarget( DG_HEATERS_T heater ) { - HEATERS_STATE_T state = HEATER_EXEC_STATE_RAMP_TO_TARGET; + HEATERS_STATE_T state = HEATER_EXEC_STATE_PRIMARY_RAMP_TO_TARGET; F32 inletTemperature = getTemperatureValue( (U32)TEMPSENSORS_HEAT_DISINFECT ); F32 targetFlow = 0.0; F32 dutyCycle = 0.0; - F32 targetTemperature = heatersStatus[ heater ].originalTargetTemp; - F32 heaterEfficiency = heatersStatus[ heater ].heaterEfficiency; + F32 targetTemperature = heatersStatus[ heater ].targetTemp; if ( DG_MODE_FILL == getCurrentOperationMode() ) { // Get the previous fill's average flow rate targetFlow = getAvgFillFlowRate(); dutyCycle = calculatePrimaryHeaterDutyCycle( targetTemperature, inletTemperature, targetFlow ); - // Multiply the duty cycle to the heater efficiency - dutyCycle *= heaterEfficiency; - state = HEATER_EXEC_STATE_CONTROL_TO_TARGET; + state = HEATER_EXEC_STATE_PRIMARY_CONTROL_TO_TARGET; } else if ( ( DG_MODE_GENE == getCurrentOperationMode() ) || ( DG_MODE_DRAI == getCurrentOperationMode() ) ) { targetTemperature += DELTA_TEMPERATURE_TIME_COSNTANT_C; // Use target flow rate during Idle and drain targetFlow = getTargetROPumpFlowRate(); dutyCycle = calculatePrimaryHeaterDutyCycle( targetTemperature, inletTemperature, targetFlow ); - state = HEATER_EXEC_STATE_CONTROL_TO_TARGET; + state = HEATER_EXEC_STATE_PRIMARY_CONTROL_TO_TARGET; } else if ( ( DG_MODE_HEAT == getCurrentOperationMode() ) || ( DG_MODE_CHEM == getCurrentOperationMode() ) ) { @@ -497,13 +485,13 @@ *************************************************************************/ static HEATERS_STATE_T handleHeaterStateControlToTarget( DG_HEATERS_T heater ) { - HEATERS_STATE_T state = HEATER_EXEC_STATE_CONTROL_TO_TARGET; + HEATERS_STATE_T state = HEATER_EXEC_STATE_PRIMARY_CONTROL_TO_TARGET; // TODO do we need any control to maintain the temperature? if ( TRUE == haveHeaterControlConditionsChanged( heater ) ) { - state = HEATER_EXEC_STATE_RAMP_TO_TARGET; + state = HEATER_EXEC_STATE_PRIMARY_RAMP_TO_TARGET; } return state; @@ -528,19 +516,19 @@ // Check if the heaters control conditions have changed, if yes, switch back to ramp to target if ( TRUE == haveHeaterControlConditionsChanged( heater ) ) { - state = HEATER_EXEC_STATE_RAMP_TO_TARGET; + state = HEATER_EXEC_STATE_PRIMARY_RAMP_TO_TARGET; } // If the heat disifect sensor indicates that the temperature is below target temperature but the target temperature had been reached // before turn on the heaters with 100% power - if ( ( heatDisinfectSensorTemp <= heatersStatus[ heater ].originalTargetTemp ) && ( TRUE == heatersStatus[ heater ].hasTargetBeenReached ) ) + if ( ( heatDisinfectSensorTemp <= heatersStatus[ heater ].targetTemp ) && ( TRUE == heatersStatus[ heater ].hasTargetBeenReached ) ) { heatersStatus[ heater ].hasTargetBeenReached = FALSE; setHeaterDutyCycle( heater, HEATERS_MAX_DUTY_CYCLE ); } // If we have reached to target temperature, turn off the heaters - if ( heatDisinfectSensorTemp > heatersStatus[ heater ].originalTargetTemp ) + if ( heatDisinfectSensorTemp > heatersStatus[ heater ].targetTemp ) { // Set the flag to true for the next run heatersStatus[ heater ].hasTargetBeenReached = TRUE; @@ -587,9 +575,29 @@ *************************************************************************/ static F32 calculatePrimaryHeaterDutyCycle( F32 targetTemperature, F32 currentTemperature, F32 flow ) { + // Get the primary heater's efficiency and the last fill temperature from the ModeFill + F32 heaterEfficiency = heatersStatus[ DG_PRIMARY_HEATER ].heaterEfficiency; + F32 lastFillTemperature = getLastFillTemperature(); + + // If the last fill temperature > target temperature, it means the primary heater overshot the duty cycle + // so with its efficiency is toned down for the next fill cycle + // If the heater undershot the duty cycle, the efficiency increases the duty cycle for the next fill cycle + if ( lastFillTemperature - targetTemperature > MAXIMUM_ALLOWED_TARGET_TEMPERATURE_DEVIATION_C ) + { + heaterEfficiency -= ( lastFillTemperature - targetTemperature ) * PRIMARY_HEATER_DUTY_CYCLE_PER_TEMPERATURE_C; + } + else if ( lastFillTemperature - targetTemperature <= MAXIMUM_ALLOWED_TARGET_TEMPERATURE_DEVIATION_C ) + { + heaterEfficiency += ( lastFillTemperature - targetTemperature ) * PRIMARY_HEATER_DUTY_CYCLE_PER_TEMPERATURE_C; + } + + // Update the heaters efficiency + heatersStatus[ DG_PRIMARY_HEATER ].heaterEfficiency = heaterEfficiency; + // Duty cycle = ( 69.73 * flow rate * deltaT / primary heater maximum power ) ^ 1/2 + // Multiply the duty cycle to the heater efficiency F32 dutyCycle = sqrt( ( WATER_SPECIFIC_HEAT_DIVIDED_BY_MINUTES * - fabs( targetTemperature - currentTemperature ) * flow ) / PRIMARY_HEATERS_MAXIMUM_POWER_WATTS ); + fabs( targetTemperature - currentTemperature ) * flow ) / PRIMARY_HEATERS_MAXIMUM_POWER_WATTS ) * heaterEfficiency; // Check the boundaries of the calculated duty cycle dutyCycle = ( dutyCycle > HEATERS_MAX_DUTY_CYCLE ? HEATERS_MAX_DUTY_CYCLE : dutyCycle ); @@ -824,16 +832,7 @@ } } -static void checkHeaterOnStatus( DG_HEATERS_T heater ) -{ - if ( FALSE == heatersStatus[ heater ].isHeaterOn ) - { - setHeaterDutyCycle( heater, HEATERS_MIN_DUTY_CYCLE ); - //state = HEATER_EXEC_STATE_NOT_RUNNING; - } -} - /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/