Index: firmware/App/Controllers/Heaters.c =================================================================== diff -u --- firmware/App/Controllers/Heaters.c (revision 0) +++ firmware/App/Controllers/Heaters.c (revision 834e2f1c57b828c546c56dea42a9757988e16314) @@ -0,0 +1,658 @@ +/************************************************************************** +* +* Copyright (c) 2024-2024 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 Heaters.c +* +* @author (last) Sean Nash +* @date (last) 20-Dec-2024 +* +* @author (original) Sean Nash +* @date (original) 20-Dec-2024 +* +***************************************************************************/ + +#include // Used for mathematical operations + +#include "FpgaRO.h" +#include "Heaters.h" +#include "Level.h" +#include "MessageSupport.h" +#include "Messaging.h" +//#include "NVDataMgmt.h" +#include "OperationModes.h" +#include "PersistentAlarm.h" +#include "PIControllers.h" +#include "SafetyShutdown.h" +#include "TaskGeneral.h" +#include "TaskPriority.h" +#include "TemperatureSensors.h" +#include "Timers.h" +#include "Utilities.h" + +/** + * @addtogroup Heaters + * @{ + */ + +// ********** private definitions ********** + +#define HEATERS_MAX_DUTY_CYCLE 1.00F ///< Heater max duty cycle (100%) or ON state +#define HEATERS_MIN_DUTY_CYCLE 0.00F ///< Heater minimum duty cycle (0.00%) or OFF state +#define HEATER_PWM_FULL_SCALE 450 ///< Heater full scale PWM value. +#define HEATER_PWM_OFFSET 25 ///< Heater PWM offset. +#define HEATER_ON_CTRL_DUTY_CYCLE 1.00F ///< Heater ON control duty cycle. +#define HEATERS_DISINFECT_DUTY_CYCLE 0.80F ///< Heaters disinfect cycle. +#define HEATERS_DISINFECT_TRANSFER_DUTY_CYCLE 0.60F ///< Heaters disinfect transfer duty cycle. +#define HEATERS_DISINFECT_TEMPERATURE_DRIFT_C 3.0F ///< Heaters disinfect temperature drift in C. +#define HEATERS_ZERO_DELTA_TEMP_C 0.0F ///< Heaters zero delta temperature in C. +#define HEATERS_DUTY_CYCLE_CONVERSION_FACTOR 100.0F ///< Heaters duty cycle 0: OFF, 100: 100% duty cycle. + +#define HEATER_P_COEFFICIENT 1.0F ///< P Term for primary heater control. +#define HEATER_I_COEFFICIENT 1.0F ///< I Term for primary heater control. + +#define HEATER_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Heater data publish interval. + +#define HEATER_TEMP_CONTROL_TOLERANCE 2.0F ///< Primary Heater temp tolerance for ON/Off control +#define HEATER_TARGET_TEMPERATURE_MIN 10.0F ///< Minimum allowed target temperature for the heaters. +#define HEATER_TARGET_TEMPERATURE_MAX 90.0F ///< Maximum allowed target temperature for the heaters. + +#define HEATER_ON_NO_FLUID_TIMEOUT_MS ( 10 * MS_PER_SECOND ) ///< Heater on with no flow time out in milliseconds. +#define HEATER_MAX_OPERATING_VOLTAGE_V 24.0F ///< Heater max operating voltage in volts. +#define HEATER_VOLTAGE_OUT_OF_RANGE_TIMEOUT_MS ( 2 * MS_PER_SECOND ) ///< Heater voltage out of range time out in milliseconds. +#define HEATER_MAX_VOLTAGE_OUT_OF_RANGE_TOL 0.2F ///< Heater max voltage out of range tolerance. +#define HEATER_CONTROL_INTERVAL_COUNT ( ( 1 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) ///< Heatereater control interval count. + +#define DATA_PUBLISH_COUNTER_START_COUNT 70 ///< Data publish counter start count. + +//static const F32 HEATER_VOLTAGE_TOLERANCE_V = HEATERS_MAX_OPERATING_VOLTAGE_V * HEATERS_MAX_VOLTAGE_OUT_OF_RANGE_TOL; ///< Heater voltage tolerance in volts. + +/// Heater exec states +typedef enum Heater_Exec_States +{ + HEATER_EXEC_STATE_OFF = 0, ///< Heater controller state off. + HEATER_EXEC_STATE_RAMP_TO_TARGET, ///< Heater controller state ramp to target. + HEATER_EXEC_STATE_CONTROL_TO_TARGET, ///< Heater controller state control to target. + HEATER_EXEC_STATE_CONTROL_TO_DISINFECT_TARGET, ///< Heater controller state control to disinfect (heat) target. + NUM_OF_HEATER_STATES, ///< Number of heater states. +} HEATER_STATE_T; + +/// Heaters data structure +typedef struct +{ + HEATER_STATE_T state; ///< Heater state. + BOOL startHeaterSignal; ///< Heater start indication flag. + BOOL heaterOnState; ///< Heater on/off status flag. + F32 targetFlowLPM; ///< Heater target flow in L/min to calculate the duty cycle. + F32 nomTargetFlowLPM; ///< Heater nominal target flow in L/min. + BOOL hasTargetTempChanged; ///< Heater target temperature change flag indicator. + U32 controlIntervalCounter; ///< Heater control interval counter. + BOOL isThisFirstControl; ///< Heater is this first control interval. + F32 prevDiaTargetFlowLPM; ///< Heater previous target dialysate flow in L/min. +} HEATER_STATUS_T; + +/// Payload record structure for heaters start request +typedef struct +{ + U32 heaterID; ///< Heater ID (0: Primary, 1: Trimmer) + U32 startStop; ///< Heater Start:1 and Stop: 0 + F32 temperature; ///< Temperature range ( 10.0 to 90.0 deg ) +} HEATER_START_CMD_PAYLOAD_T; + +// ********** private data ********** + +static HEATER_STATUS_T heatersStatus; ///< Heaters status. +static OVERRIDE_F32_T targetTempC; ///< Heater target temperature. +static OVERRIDE_F32_T control; ///< Heater control ( Primary : On/Off, Trimmer : Dutycycle). +static U32 dataPublicationTimerCounter; ///< Data publication timer counter. +static OVERRIDE_U32_T heaterDataPublishInterval; ///< Heater data publish time interval. + +// ********** private function prototypes ********** + +static HEATER_STATE_T handleHeaterStateOff( void ); +static HEATER_STATE_T handleHeaterStateRampToTarget( void ); +static HEATER_STATE_T handleHeaterStateControlToTarget( void ); +static HEATER_STATE_T handleHeaterStateControlToDisinfectTarget( void ); + +static void setHeaterControl( void ); +static F32 getHeaterControl( void ); + +static void publishHeatersData( void ); +//static void monitorHeatersVoltage( void ); + +/*********************************************************************//** + * @brief + * The initHeater initializes the heaters controller unit. + * @details \b Inputs: none + * @details \b Outputs: Heaters unit variables initialized. + * @return none + *************************************************************************/ +void initHeater( void ) +{ + dataPublicationTimerCounter = DATA_PUBLISH_COUNTER_START_COUNT; + + targetTempC.data = 0.0F; + targetTempC.ovData = 0.0F; + targetTempC.ovInitData = 0.0F; + targetTempC.override = OVERRIDE_RESET; + heaterDataPublishInterval.data = HEATER_DATA_PUBLISH_INTERVAL; + heaterDataPublishInterval.ovData = HEATER_DATA_PUBLISH_INTERVAL; + heaterDataPublishInterval.ovInitData = HEATER_DATA_PUBLISH_INTERVAL; + heaterDataPublishInterval.override = OVERRIDE_RESET; + control.data = HEATERS_MIN_DUTY_CYCLE; + control.ovData = HEATERS_MIN_DUTY_CYCLE; + control.ovInitData = HEATERS_MIN_DUTY_CYCLE; + control.override = OVERRIDE_RESET; + heatersStatus.state = HEATER_EXEC_STATE_OFF; + heatersStatus.startHeaterSignal = FALSE; + heatersStatus.heaterOnState = FALSE; + heatersStatus.targetFlowLPM = 0.0F; + heatersStatus.nomTargetFlowLPM = 0.0F; + heatersStatus.hasTargetTempChanged = FALSE; + heatersStatus.controlIntervalCounter = 0; + heatersStatus.isThisFirstControl = TRUE; + heatersStatus.prevDiaTargetFlowLPM = 0.0F; + setHeaterControl(); + +// // Initialize the primary controller PI controller +// initializePIController( PI_CONTROLLER_ID_PRIMARY_HEATER, HEATERS_MIN_DUTY_CYCLE, PRIMARY_HEATER_P_COEFFICIENT, PRIMARY_HEATER_I_COEFFICIENT, +// HEATERS_MIN_DUTY_CYCLE, HEATERS_MAX_DUTY_CYCLE ); +// +// // Initialize the trimmer heater PI controller +// 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 ); + + // Initialize the persistent alarms + //initPersistentAlarm( ALARM_ID_DD_PRIMARY_HEATER_VOLTAGE_OUT_OF_RANGE, 0, HEATERS_VOLTAGE_OUT_OF_RANGE_TIMEOUT_MS ); + //initPersistentAlarm( ALARM_ID_DD_TRIMMER_HEATER_VOLTAGE_OUT_OF_RANGE, 0, HEATERS_VOLTAGE_OUT_OF_RANGE_TIMEOUT_MS ); +// initPersistentAlarm( ALARM_ID_RO_FLUID_TOO_LOW_WHILE_HEATER_IS_ON, 0, HEATER_ON_NO_FLUID_TIMEOUT_MS ); +} + +/*********************************************************************//** + * @brief + * The setHeaterTargetTemperature function sets the target temperature of + * the heater. + * @details \b Inputs: none + * @details \b Outputs: heaterStatus + * @param targetTemperature: target temperature of that the heater has to + * heat the fluid + * @return TRUE if the temperature was set, otherwise FALSE + *************************************************************************/ +BOOL setHeaterTargetTemperature( F32 targetTemperature ) +{ + BOOL result = FALSE; + + // Assume the target temperature has not changed + heatersStatus.hasTargetTempChanged = FALSE; + + // Check if the requested temperature is within the allowed range + if ( ( targetTemperature >= HEATER_TARGET_TEMPERATURE_MIN ) && ( targetTemperature <= HEATER_TARGET_TEMPERATURE_MAX ) ) + { + targetTempC.data = targetTemperature; + heatersStatus.hasTargetTempChanged = TRUE; + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getHeaterTargetTemperature function returns the heater target + * temperature. + * @details \b Inputs: none + * @details \b Outputs: targetTempC + * @return the heater target temperature + *************************************************************************/ +F32 getHeaterTargetTemperature( void ) +{ + F32 targetTemp = getF32OverrideValue( &targetTempC ); + + return targetTemp; +} + +/*********************************************************************//** + * @brief + * The isHeaterOn function returns the heater status whether it is on or off. + * @details \b Inputs: heaterStatus + * @details \b Outputs: none + * @return heater on/off status + *************************************************************************/ +BOOL isHeaterOn( void ) +{ + return heatersStatus.heaterOnState; +} + +/*********************************************************************//** + * @brief + * The startHeater function starts the given heater by setting the flag. + * @details \b Inputs: heatersStatus + * @details \b Outputs: startHeaterSignal + * @return None + *************************************************************************/ +void startHeater( void ) +{ + if ( HEATER_EXEC_STATE_OFF == heatersStatus.state ) + { + heatersStatus.startHeaterSignal = TRUE; + } +} + +/*********************************************************************//** + * @brief + * The stopHeater function stops the heater. + * @details \b Inputs: none + * @details \b Outputs: heaterStatus, control + * @return none + *************************************************************************/ +void stopHeater( void ) +{ + heatersStatus.startHeaterSignal = FALSE; + heatersStatus.heaterOnState = FALSE; + control.data = HEATERS_MIN_DUTY_CYCLE; + heatersStatus.state = HEATER_EXEC_STATE_OFF; + + // update duty cycle + setHeaterControl(); +} + +/*********************************************************************//** + * @brief + * The execHeaterMonitor function monitors the status of the heater. + * The internal temperature sensors and the voltages of the heater are + * monitored. The floater level is continuously checked and if there is no + * fluid at defined level, the heaters are turned off. + * @details \b Inputs: heaterStatus + * @details \b Outputs: heaterStatus + * @return none + *************************************************************************/ +void execHeaterMonitor( void ) +{ + // Check if the heater is on and if it is, check the level sensor status +// if ( TRUE == heatersStatus[ heater ].heaterOnState ) +// { +// BOOL isLevelLow = ( ( getLevelStatus() != 0 )? FALSE : TRUE ); +// +// checkPersistentAlarm( ALARM_ID_RO_FLUID_TOO_LOW_WHILE_HEATER_IS_ON, isLevelLow, 0.0F, 0.0F ); +// } +// else +// { +// checkPersistentAlarm( ALARM_ID_RO_FLUID_TOO_LOW_WHILE_HEATER_IS_ON, FALSE, 0.0F, 0.0F ); +// } + + // Monitor heater voltage + //monitorHeatersVoltage(); + + // Check for data publication + publishHeatersData(); +} + +/*********************************************************************//** + * @brief + * The execHeatersControl function executes the heaters state machine. + * @details \b Inputs: heaterStatus + * @details \b Outputs: heaterStatus + * @details \b Alarms: ALARM_ID_RO_SOFTWARE_FAULT when invalid heater + * executive state found. + * @return none + *************************************************************************/ +void execHeatersControl( void ) +{ + switch( heatersStatus.state ) + { + case HEATER_EXEC_STATE_OFF: + heatersStatus.state = handleHeaterStateOff(); + break; + + case HEATER_EXEC_STATE_RAMP_TO_TARGET: + heatersStatus.state = handleHeaterStateRampToTarget(); + break; + + case HEATER_EXEC_STATE_CONTROL_TO_TARGET: + heatersStatus.state = handleHeaterStateControlToTarget(); + break; + + case HEATER_EXEC_STATE_CONTROL_TO_DISINFECT_TARGET: + heatersStatus.state = handleHeaterStateControlToDisinfectTarget(); + break; + + default: + // The heater is in an unknown state. Alarm, turn heater off and switch to off state + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_RO_SOFTWARE_FAULT, SW_FAULT_ID_HEATER_INVALID_EXEC_STATE, (U32)heatersStatus.state ); + stopHeater(); + break; + } + + // Check if the heater is requested to be off + if ( FALSE == heatersStatus.heaterOnState ) + { + // stop the heater + stopHeater(); + } +} + +/*********************************************************************//** + * @brief + * The handleHeaterStateOff function handles the heater off state and transitions + * to heater On condition when the heater start flag is set to true. + * @details \b Inputs: heaterStatus + * @details \b Outputs: heaterStatus + * @return next state of the heater control state machine + *************************************************************************/ +static HEATER_STATE_T handleHeaterStateOff( void ) +{ + HEATER_STATE_T state = HEATER_EXEC_STATE_OFF; + + if ( TRUE == heatersStatus.startHeaterSignal ) + { + heatersStatus.heaterOnState = TRUE; + heatersStatus.startHeaterSignal = FALSE; + + // proceed to ramp state + state = HEATER_EXEC_STATE_RAMP_TO_TARGET; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeaterStateRampToTarget function handles the heaters' control + * while they are ramping to target temperature. + * @details \b Inputs: heaterStatus + * @details \b Outputs: heaterStatus + * @return next state of the heater control state machine + *************************************************************************/ +static HEATER_STATE_T handleHeaterStateRampToTarget( void ) +{ + HEATER_STATE_T state = HEATER_EXEC_STATE_RAMP_TO_TARGET; + RO_OP_MODE_T opMode = getCurrentOperationMode(); + F32 targetTemperature = getHeaterTargetTemperature(); + +// if ( RO_MODE_HEAT != opMode ) + { + control.data = HEATER_ON_CTRL_DUTY_CYCLE; + state = HEATER_EXEC_STATE_CONTROL_TO_TARGET; + } +// else +// { +// // TODO : Calculate required duty cycle +// state = HEATER_EXEC_STATE_CONTROL_TO_DISINFECT_TARGET; +// } + + setHeaterControl(); + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeaterStateControlToTarget function handles the heaters' control + * to target while the heater is targeting to reach to temperature. + * @details \b Inputs: heaterStatus + * @details \b Outputs: heaterStatus + * @return next state of the state machine + *************************************************************************/ +static HEATER_STATE_T handleHeaterStateControlToTarget( void ) +{ + HEATER_STATE_T state = HEATER_EXEC_STATE_CONTROL_TO_TARGET; + F32 targetTemperature = getHeaterTargetTemperature(); + F32 measuredTemperature = 0.0F; + + if( ++heatersStatus.controlIntervalCounter > HEATER_CONTROL_INTERVAL_COUNT ) + { + measuredTemperature = getTemperatureValue( TEMP_SENSOR_TRO1 ); + if ( measuredTemperature >= targetTemperature ) + { + // Turn off heater + control.data = HEATERS_MIN_DUTY_CYCLE; + } + else + { + // Turn On heater + control.data = HEATER_ON_CTRL_DUTY_CYCLE; + } + + //control = runPIController( PI_CONTROLLER_ID_PRIMARY_HEATER, targetTemperature, measuredTemperature ); + + heatersStatus.hasTargetTempChanged = FALSE; + heatersStatus.controlIntervalCounter = 0; + + setHeaterControl(); + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeaterStateControlToDisinfectTarget function handles the + * heaters' control to target while the operation mode is heat or chemical + * disinfects. + * @details \b Inputs: heaterStatus + * @details \b Outputs: heaterStatus + * @param heater: The heater Id that its on state is handled + * @return next state of the state machine + *************************************************************************/ +static HEATER_STATE_T handleHeaterStateControlToDisinfectTarget( void ) +{ + HEATER_STATE_T state = HEATER_EXEC_STATE_CONTROL_TO_DISINFECT_TARGET; + + //TODO : update dutycycle for the heat disinfect state + + setHeaterControl(); + + return state; +} + +/*********************************************************************//** + * @brief + * The setHeaterControl function sets the duty cycle of a heater. + * @details \b Inputs: control + * @details \b Outputs: FPGA heater control + * @return none + *************************************************************************/ +static void setHeaterControl( void ) +{ + F32 ctrl = getHeaterControl(); + BOOL enable = FALSE; + U16 pwm = (U16)( ctrl * (F32)HEATER_PWM_FULL_SCALE + (F32)HEATER_PWM_OFFSET + 0.5F ); + + if ( ctrl > 0.0F ) + { + enable = TRUE; + } + setFPGAHeaterEnabled( enable ); + setFPGAHeaterPWM( pwm ); +} + +/*********************************************************************//** + * @brief + * The getHeaterControl function returns the heater's duty cycle. + * @details \b Inputs: heaterStatus + * @details \b Outputs: none + * @return PWM duty cycle for the heater + *************************************************************************/ +static F32 getHeaterControl( void ) +{ + F32 duty = getF32OverrideValue( &control ); + + return duty; +} + +/*********************************************************************//** + * @brief + * The monitorHeatersVoltage function monitors the heaters' voltages + * @details \b Inputs: Voltage range + * @details \b Outputs: none + * @details \b Alarms: ALARM_ID_DD_MAIN_PRIMARY_HEATER_VOLTAGE_OUT_OF_RANGE when + * primary heater voltage found out of range. + * @details \b Alarms: ALARM_ID_DD_TRIMMER_HEATER_VOLTAGE_OUT_OF_RANGE when + * trimmer heater voltage found out of range. + * @return none + *************************************************************************/ +//static void monitorHeatersVoltage( void ) +//{ +// F32 mainPriVoltage = getMonitoredLineLevel( MONITORED_LINE_24V_GND_MAIN_PRIM_HTR_V ); +// F32 trimmerVoltage = getMonitoredLineLevel( MONITORED_LINE_24V_GND_TRIM_HTR_V ); +// +// // Voltage to PWM is reverse. If PWM = 0 -> V = 24V +// F32 mainPriDC = getHeaterControl( DD_PRIMARY_HEATER ); +// F32 trimmerDC = getHeaterControl( DD_TRIMMER_HEATER ); +// +// // The expected voltage is the inverse of the duty cycle +// F32 mainPriExpectedVoltage = HEATERS_MAX_OPERATING_VOLTAGE_V * ( 1.0F - mainPriDC ); +// F32 trimmerExpectedVoltage = HEATERS_MAX_OPERATING_VOLTAGE_V * ( 1.0F - trimmerDC ); +// +// BOOL isMainPriOut = FALSE; +// BOOL isTrimmerOut = FALSE; +// +// // If the system is DVT, check the FPGA persistent alarm of the main primary heater's voltage ADC +// checkFPGAPersistentAlarms( FPGA_PERS_ERROR_MAIN_PRIMARY_HEATER_VOLTAGE_ADC, getFPGAHeaterGateADCReadCount() ); +// +// isMainPriOut = ( fabs( mainPriExpectedVoltage - mainPriVoltage ) > HEATERS_VOLTAGE_TOLERANCE_V ? TRUE : FALSE ); +// isTrimmerOut = ( fabs( trimmerExpectedVoltage - trimmerVoltage ) > HEATERS_VOLTAGE_TOLERANCE_V ? TRUE : FALSE ); +// +// if ( getCurrentOperationMode() != DD_MODE_INIT ) +// { +// checkPersistentAlarm( ALARM_ID_DD_MAIN_PRIMARY_HEATER_VOLTAGE_OUT_OF_RANGE, isMainPriOut, mainPriDC, HEATERS_VOLTAGE_TOLERANCE_V ); +// checkPersistentAlarm( ALARM_ID_DD_TRIMMER_HEATER_VOLTAGE_OUT_OF_RANGE, isTrimmerOut, trimmerDC, HEATERS_VOLTAGE_TOLERANCE_V ); +// } +//} + +/*********************************************************************//** + * @brief + * The publishHeatersData function publishes the heaters data info + * at the defined time interval. + * @details \b Inputs: dataPublicationTimerCounter + * @details \b Outputs: dataPublicationTimerCounter + * @details \b Message \b Sent: MSG_ID_DD_HEATERS_DATA to publish + * heaters data. + * @return none + *************************************************************************/ +static void publishHeatersData( void ) +{ + if ( ++dataPublicationTimerCounter >= getU32OverrideValue( &heaterDataPublishInterval ) ) + { + HEATERS_DATA_T data; + + data.heaterHroDC = getHeaterControl(); + data.heaterHroTargetTemp = getHeaterTargetTemperature(); + data.heaterHroState = heatersStatus.state; + data.heaterHroControlCounter = heatersStatus.controlIntervalCounter; + + dataPublicationTimerCounter = 0; + + broadcastData( MSG_ID_RO_HEATER_DATA, COMM_BUFFER_OUT_CAN_RO_BROADCAST, (U08*)&data, sizeof( HEATERS_DATA_T ) ); + } +} + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + + +/*********************************************************************//** + * @brief + * The testHeatersDataPublishIntervalOverride function overrides the + * heaters data publish interval. + * @details \b Inputs: heaterDataPublishInterval + * @details \b Outputs: heaterDataPublishInterval + * @param Override message from Dialin which includes the interval + * (in ms) to override the heaters data broadcast interval to. + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testHeatersDataPublishIntervalOverride( MESSAGE_T *message ) +{ + BOOL result = u32BroadcastIntervalOverride( message, &heaterDataPublishInterval, TASK_PRIORITY_INTERVAL ); + + return result; +} + +/*********************************************************************//** + * @brief + * The testHeaterDutyCycleOverride function overrides the heater's + * duty cycle. + * @details \b Inputs: heatersStatus + * @details \b Outputs: heatersStatus + * @param message Override message from Dialin which includes the duty cycle + * of the heater. + * @return TRUE if the override was successful otherwise FALSE + *************************************************************************/ +BOOL testHeaterDutyCycleOverride( MESSAGE_T *message ) +{ + BOOL result = f32Override( message, &control ); + + return result; +} + +/*********************************************************************//** + * @brief + * The testHeaterTargetTemperatureOverride function overrides the heater's + * target temperature. + * @details \b Inputs: targetTempC + * @details \b Outputs: targetTempC + * @param message Override message from Dialin which includes the target + * temperature of the heater. + * @return TRUE if the override was successful otherwise FALSE + *************************************************************************/ +BOOL testHeaterTargetTemperatureOverride( MESSAGE_T *message ) +{ + BOOL result = f32Override( message, &targetTempC ); + + return result; +} + +/*********************************************************************//** + * @brief + * The testHeaterStartStopOverride function starts/stops a given heater + * at mentioned temperature. + * @details \b Inputs: tester logged in + * @details \b Outputs: heatersStatus[] + * @param message set message from Dialin which includes the heater to set + * and the state to set the heater to. + * @return TRUE if set request is successful, FALSE if not + *************************************************************************/ +BOOL testHeaterStartStopOverride( MESSAGE_T *message ) +{ + BOOL result = FALSE; + +// // Verify tester has logged in with TD +// if ( TRUE == isTestingActivated() ) +// { +// // Verify payload length is valid +// if ( sizeof( HEATER_START_CMD_PAYLOAD_T ) == message->hdr.payloadLen ) +// { +// HEATER_START_CMD_PAYLOAD_T payload; +// +// memcpy( &payload, message->payload, sizeof(HEATER_START_CMD_PAYLOAD_T) ); +// +// if ( (DD_HEATERS_T)payload.heaterID < NUM_OF_DD_HEATERS ) +// { +// // Handle start command +// if ( ( TRUE == payload.startStop ) && +// ( ( payload.temperature >= HEATER_TARGET_TEMPERATURE_MIN ) && ( payload.temperature <= HEATER_TARGET_TEMPERATURE_MAX ) ) ) +// { +// setHeaterTargetTemperature( (DD_HEATERS_T)payload.heaterID, payload.temperature ); +// startHeater( (DD_HEATERS_T)payload.heaterID ); +// result = TRUE; +// } +// +// //Handle stop command +// if ( FALSE == payload.startStop ) +// { +// stopHeater( (DD_HEATERS_T)payload.heaterID ); +// result = TRUE; +// } +// } +// } +// } + + return result; +} + +/**@}*/ Index: firmware/App/Controllers/Heaters.h =================================================================== diff -u --- firmware/App/Controllers/Heaters.h (revision 0) +++ firmware/App/Controllers/Heaters.h (revision 834e2f1c57b828c546c56dea42a9757988e16314) @@ -0,0 +1,64 @@ +/************************************************************************** +* +* Copyright (c) 2024-2024 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 Heaters.h +* +* @author (last) Sean Nash +* @date (last) 20-Dec-2024 +* +* @author (original) Sean Nash +* @date (original) 20-Dec-2024 +* +***************************************************************************/ + +#ifndef _HEATERS_H_ +#define _HEATERS_H_ + +#include "ROCommon.h" + +/** + * @defgroup Heaters Heaters + * @brief Heaters driver modules. Controls the primary and trimmer heaters. + * The primary heaters are manufactured by Watlow with + * fluid operating temperature in between 5 degrees C and 95 degrees C. + * The trimmer heater is manufactured by Watlow with + * fluid operating temperature in between 5 degrees C and 95 degrees C. + * + * @addtogroup Heaters + * @{ + */ + +// ********** Public definitions ********** + +/// Heaters data structure. +typedef struct +{ + F32 heaterHroDC; ///< HRO duty cycle + F32 heaterHroTargetTemp; ///< HRO target temperature + U32 heaterHroState; ///< HRO state + U32 heaterHroControlCounter; ///< HRO control interval count +} HEATERS_DATA_T; + +// ********** Public function prototypes ********** + +void initHeater( void ); +BOOL setHeaterTargetTemperature( F32 targetTemperature ); +void startHeater( void ); +F32 getHeaterTargetTemperature( void ); +BOOL isHeaterOn( void ); +void stopHeater( void ); +void execHeaterControl( void ); +void execHeaterMonitor( void ); + +BOOL testHeatersDataPublishIntervalOverride( MESSAGE_T *message ); +BOOL testHeaterDutyCycleOverride( MESSAGE_T *message ); +BOOL testHeaterTargetTemperatureOverride( MESSAGE_T *message ); +BOOL testHeaterStartStopOverride( MESSAGE_T *message ); + +/**@}*/ + +#endif Index: firmware/App/Drivers/BoostPump.c =================================================================== diff -u -rce66580e076bffa157868ff7e422556f78a95cac -r834e2f1c57b828c546c56dea42a9757988e16314 --- firmware/App/Drivers/BoostPump.c (.../BoostPump.c) (revision ce66580e076bffa157868ff7e422556f78a95cac) +++ firmware/App/Drivers/BoostPump.c (.../BoostPump.c) (revision 834e2f1c57b828c546c56dea42a9757988e16314) @@ -74,7 +74,7 @@ } // set RO pump to stop - setROPumpPWMPct( 0 ); + setROPumpPWM( 0 ); // TODO set boost pump to stop } @@ -91,7 +91,7 @@ U32 tach; // get latest RO pump duty cycle read back from FPGA - boostPumpReadDutyCycle[ RO_PUMP ].data = getROPumpPWMPct(); + boostPumpReadDutyCycle[ RO_PUMP ].data = getROPumpPWM(); // get latest RO pump tachometer count from FPGA and convert to RPM tach = (U32)getROPumpTachCount(); @@ -139,7 +139,7 @@ boostPumpCmdDutyCycle[ pumpID ] = pwm; if ( RO_PUMP == pumpID ) { - setROPumpPWMPct( pwm ); + setROPumpPWM( pwm ); } else { Index: firmware/App/Services/AlarmMgmtSWFaults.h =================================================================== diff -u -r340b6bedc5aac2ad47364ade48a842f00b12b8fe -r834e2f1c57b828c546c56dea42a9757988e16314 --- firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision 340b6bedc5aac2ad47364ade48a842f00b12b8fe) +++ firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision 834e2f1c57b828c546c56dea42a9757988e16314) @@ -125,6 +125,7 @@ SW_FAULT_ID_CONDUCTIVITY_WR_INVALID_EXEC_STATE = 108, SW_FAULT_ID_INVALID_TEMPERATURE_SENSOR_SELECTED1 = 109, SW_FAULT_ID_INVALID_TEMPERATURE_SENSOR_SELECTED2 = 110, + SW_FAULT_ID_HEATER_INVALID_EXEC_STATE = 111, NUM_OF_SW_FAULT_IDS } SW_FAULT_ID_T; Index: firmware/App/Services/FpgaRO.c =================================================================== diff -u -r340b6bedc5aac2ad47364ade48a842f00b12b8fe -r834e2f1c57b828c546c56dea42a9757988e16314 --- firmware/App/Services/FpgaRO.c (.../FpgaRO.c) (revision 340b6bedc5aac2ad47364ade48a842f00b12b8fe) +++ firmware/App/Services/FpgaRO.c (.../FpgaRO.c) (revision 834e2f1c57b828c546c56dea42a9757988e16314) @@ -38,6 +38,9 @@ #define MAX_FPGA_COMM_FAILURES 3 ///< FPGA maximum comm failures per MAX_FPGA_COMM_FAILURES_WINDOW_MS #define MIN_POWER_ON_TIME_FOR_COMM_FAILS ( 1 * MS_PER_SECOND ) ///< Allow FPGA comm errors for first second after power-up +#define HEATER_ENABLED 0x01 ///< Heater enable bit. +#define HEATER_DISABLED 0x00 ///< Heater disabled. + #define FPGA_CONDUCTIVITY_RESET_BIT 0x01 ///< Conductivity Sensor reset bit mask. #define FPGA_CONDUCTIVITY_INIT_ENABLE_BIT 0x02 ///< Conductivity Sensor initialization enable bit mask. #define FPGA_CONDUCTIVITY_WR_ENABLE_BIT 0x04 ///< Conductivity Sensor write enable bit mask. @@ -403,27 +406,29 @@ /*********************************************************************//** * @brief - * The setROPumpPWMPct function sets the RO pump PWM duty cycle. + * The setROPumpPWM function sets the RO pump PWM duty cycle. * The higher the PWM duty cycle (0..500), the faster the pump will go. + * @note PWM values < 5% or > 95% will cause pump to stop so effective + * range is actually 25..475. * @details \b Inputs: none * @details \b Outputs: fpgaActuatorSetPoints.roPumpPWMDutyCyclePct * @param pwm PWM duty cycle magnitude * @return none *************************************************************************/ -void setROPumpPWMPct( U16 pwm ) +void setROPumpPWM( U16 pwm ) { fpgaActuatorSetPoints.roPumpPWMDutyCyclePct = pwm; } /*********************************************************************//** * @brief - * The getROPumpPWMPct function gets a read back from FPGA of RO pump PWM + * The getROPumpPWM function gets a read back from FPGA of RO pump PWM * duty cycle. * @details \b Inputs: fpgaSensorReadings.roPumpPWMReadback * @details \b Outputs: none * @return measured speed (RPM) of the RO pump *************************************************************************/ -U16 getROPumpPWMPct( void ) +U16 getROPumpPWM( void ) { return fpgaSensorReadings.roPumpPWMReadback; } @@ -443,6 +448,42 @@ /*********************************************************************//** * @brief + * The setFPGAHeaterEnabled function sets the heater enable on or off. + * @details \b Inputs: none + * @details \b Outputs: fpgaActuatorSetPoints.heaterControl + * @param enable Flag indicating whether to enable or disable the heater. + * @return none + *************************************************************************/ +void setFPGAHeaterEnabled( BOOL enable ) +{ + if ( TRUE == enable ) + { + fpgaActuatorSetPoints.heaterControl = HEATER_ENABLED; + } + else + { + fpgaActuatorSetPoints.heaterControl = HEATER_DISABLED; + } +} + +/*********************************************************************//** + * @brief + * The setFPGAHeaterPWM function sets the heater PWM duty cycle. + * The higher the PWM duty cycle (0..500), the faster the pump will go. + * @note PWM values < 5% or > 95% will cause heater to go off so effective + * range is actually 25..475. + * @details \b Inputs: none + * @details \b Outputs: fpgaActuatorSetPoints.heaterPWMDutyCyclePct + * @param pwm PWM duty cycle magnitude + * @return none + *************************************************************************/ +void setFPGAHeaterPWM( U16 pwm ) +{ + fpgaActuatorSetPoints.heaterPWMDutyCyclePct = pwm; +} + +/*********************************************************************//** + * @brief * The getFPGAPRiRawPressure function gets the PRi pressure reading. * @details \b Inputs: fpgaSensorReadings.pressurePri * @details \b Outputs: none Index: firmware/App/Services/FpgaRO.h =================================================================== diff -u -r340b6bedc5aac2ad47364ade48a842f00b12b8fe -r834e2f1c57b828c546c56dea42a9757988e16314 --- firmware/App/Services/FpgaRO.h (.../FpgaRO.h) (revision 340b6bedc5aac2ad47364ade48a842f00b12b8fe) +++ firmware/App/Services/FpgaRO.h (.../FpgaRO.h) (revision 834e2f1c57b828c546c56dea42a9757988e16314) @@ -45,10 +45,13 @@ U16 getFPGAValveStates( void ); void setROPumpEnable( BOOL enable ); -void setROPumpPWMPct( U16 pwm ); -U16 getROPumpPWMPct( void ); +void setROPumpPWM( U16 pwm ); +U16 getROPumpPWM( void ); U16 getROPumpTachCount( void ); +void setFPGAHeaterEnabled( BOOL enable ); +void setFPGAHeaterPWM( U16 pwm ); + S16 getFPGAPRiRawPressure( void ); S16 getFPGAPRiRawTemperature( void ); U08 getFPGAPRiReadCount( void );