Index: firmware/App/Controllers/Fans.c =================================================================== diff -u -rdaf8d5b60c753becab80cbaf164aac0e49d533a2 -r2f2d0ccadd8a09037eb3d0dd144549b2c8c8129b --- firmware/App/Controllers/Fans.c (.../Fans.c) (revision daf8d5b60c753becab80cbaf164aac0e49d533a2) +++ firmware/App/Controllers/Fans.c (.../Fans.c) (revision 2f2d0ccadd8a09037eb3d0dd144549b2c8c8129b) @@ -1,48 +1,415 @@ -/* - * Fans.c - * - * Created on: Aug 6, 2020 - * Author: fw - */ #include "Fans.h" +#include "TaskGeneral.h" +#include "Thermistors.h" +#include "SystemCommMessages.h" +#include "FPGA.h" -//TODO Do we need exec fans? -//TODO Do we need a wrapper for pwm as input? -//TODO What to have in init fans? +/** + * @addtogroup Fans + * @{ + */ +// ********** private definitions ********** + +#define FANS_MIN_PWM_PERCENT 0.1 ///< Fans min PWM. +#define FANS_MAX_PWM_PERCENT 0.95 ///< Fans max PWM. +#define MIN_ALLOWED_AMBIENT_TEMPERATURE 20 ///< Min allowed ambient temperature. +#define MAX_ALLOWED_AMBINET_TEMPERATURE 70 ///< Max allowed ambient temperature. +#define FANS_MAX_ALLOWED_RAMP_UP_PWM_CHANGE 0.3 ///< Fans max allowed ramp up PWM change. +#define FANS_MAX_ALLOWED_RAMP_DOWN_PWM_CHANGE 0.005 ///< Fans min allowed ramp down PWM change. + +#define ONE_MINUTE_TO_MICRO_SECONDS 60000000 ///< One minute to micro seconds conversion. +#define TOGGLE_PERIOD_RESOLUTION 2.5 ///< FPGA fans toggle period resolution in micro seconds. +#define ROTATIONAL_TO_TOGGLE_PERIOD_CONVERSION_COEFF 4 ///< FPGA rotational to toggle period conversion coefficient. + +#define FANS_DATA_PUBLISH_INTERVAL (MS_PER_SECOND / TASK_GENERAL_INTERVAL) ///< Fans publish data time interval in counts. +#define FANS_CONTROL_INTERVAL (MS_PER_SECOND / TASK_GENERAL_INTERVAL) ///< Fans control time interval in counts. +#define FANS_ZERO_RPM_TOGGLE_PERIOD_VALUE 0xFFFF + +/// Fans self test states +typedef enum fans_Self_Test +{ + FANS_SELF_TEST_START = 0, ///< Fans self test start state + NUM_OF_SELF_TEST_STATES, ///< Number of fans self test +} FANS_SELF_TEST_STATES_T; + +/// Fans exec states +typedef enum fans_Exec_States +{ + FANS_EXEC_STATE_START = 0, ///< Fans exec state start + FANS_EXEC_STATE_RUN, ///< Fans exec state run + NUM_OF_FANS_EXEC_STATES, ///< Number of fans exec states +} FANS_EXEC_STATES_T; + +/// Fans status struct +typedef struct +{ + F32 targetPWM; ///< Fan's Target PWM + F32 calculatedPWM; ///< Fan's calculated PWM from the calculations + U32 rpm[ NUM_OF_FANS_NAMES ]; ///< Fan's current speed +} FAN_STATUS_T; + +static FAN_STATUS_T fansStatus; ///< Fans status +static SELF_TEST_STATUS_T fansSelfTestReslt = SELF_TEST_STATUS_IN_PROGRESS; ///< Fans self test result +static FANS_SELF_TEST_STATES_T fansSelfTestState = FANS_SELF_TEST_START; ///< Fans self test state +static FANS_EXEC_STATES_T fansExecState = FANS_EXEC_STATE_START; ///< Fans exec state +static U32 fansControlCounter = 0; ///< Fans control interval counter +static U32 fansPublishCounter = 0; ///< Fans data publish interval counter + +static const F32 slope = ( MAX_ALLOWED_AMBINET_TEMPERATURE - MIN_ALLOWED_AMBIENT_TEMPERATURE ) / + ( FANS_MAX_PWM_PERCENT - FANS_MIN_PWM_PERCENT ); ///< Temperature to PWM conversion slope + +/// FGPA Toggle to RPM conversion coefficient +static F32 const toggle2RPMCoefficient = ( ONE_MINUTE_TO_MICRO_SECONDS * ROTATIONAL_TO_TOGGLE_PERIOD_CONVERSION_COEFF ) / TOGGLE_PERIOD_RESOLUTION; + +static OVERRIDE_U32_T fansPublishInterval = { FANS_DATA_PUBLISH_INTERVAL, FANS_DATA_PUBLISH_INTERVAL, 0, 0 }; ///< Fans publish time interval override + +static FANS_EXEC_STATES_T handleFansExecStateStart( void ); +static FANS_EXEC_STATES_T handleFansExecStateRun( void ); + +static void setInletFansPWM( F32 pwm ); +static void setOutletFansPWM( F32 pwm ); +static F32 getMaximumTemperature( void ); +static void getFansRPM( void ); +static U32 getPublishFansDataInterval( void ); +static void publishFansData( void ); + +/*********************************************************************//** + * @brief + * The initFans function initializes the fans module. + * @details Inputs: fansExecState, fansSelfTestReslt, fansSelfTestState, + * fansStatus, fansControlCounter, fansPublishCounter + * @details Outputs: fansExecState, fansSelfTestReslt, fansSelfTestState, + * fansStatus, fansControlCounter, fansPublishCounter + * @return none + *************************************************************************/ void initFans( void ) { - //TODO fill up + // Initialize the variables + fansExecState = FANS_EXEC_STATE_START; + fansSelfTestReslt = SELF_TEST_STATUS_IN_PROGRESS; + fansSelfTestState = FANS_SELF_TEST_START; + fansControlCounter = 0; + fansPublishCounter = 0; } +/*********************************************************************//** + * @brief + * The execFansSelfTest function executes the fans self test. + * @details Inputs: FILL UP + * @details Outputs: FILL UP + * @return Status of self test + *************************************************************************/ SELF_TEST_STATUS_T execFansSelfTest( void ) { } +/*********************************************************************//** + * @brief + * The execFans function executes the execFans exec states. + * @details Inputs: fansExecState + * @details Outputs: fansExecState + * @return none + *************************************************************************/ void execFans( void ) { - // Possibly the PI controller for the board temperature + switch ( fansExecState ) + { + case FANS_EXEC_STATE_START: + fansExecState = handleFansExecStateStart(); + break; + + case FANS_EXEC_STATE_RUN: + fansExecState = handleFansExecStateRun(); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_FAN_INVALID_EXEC_STATE, fansExecState ); + fansExecState = FANS_EXEC_STATE_RUN; + break; + } + + publishFansData(); } -BOOL startFan1( F32 pwmPercent ) +/*********************************************************************//** + * @brief + * The getFanRPM function returns the RPM a the selected fan. + * @details Inputs: fansStatus + * @details Outputs: none + * @param selected fan to read its RPM + * @return RPM of the selected fan + *************************************************************************/ +F32 getFanRPM( FANS_NAMES_T fan ) { - F32 pwm = pwmPercent / 100; + F32 rpm = 0.0; + // Check if the called fan is in range + if ( fan < NUM_OF_FANS_NAMES ) + { + rpm = fansStatus.rpm[ fan ]; + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_FAN_SELECTED, fan ); + } + + return rpm; +} + +/*********************************************************************//** + * @brief + * The handleFansExecStateStart function handles the start state of the + * fans exec state machine. + * @details Inputs: none + * @details Outputs: none + * @return the next state of the exec state machine + *************************************************************************/ +static FANS_EXEC_STATES_T handleFansExecStateStart( void ) +{ + FANS_EXEC_STATES_T state = FANS_EXEC_STATE_START; + + // TODO do we need to wait for POST or something? + + // Start the fans with minimum PWM + setInletFansPWM( FANS_MIN_PWM_PERCENT ); + setOutletFansPWM( FANS_MIN_PWM_PERCENT ); + state = FANS_EXEC_STATE_RUN; + + return state; +} + +/*********************************************************************//** + * @brief + * The handleFansExecStateRun function handles the run state of the + * fans exec state machine. + * @details Inputs: fansStatus, fansControlCounter + * @details Outputs: fansStatus, fansControlCounter + * @return the next state of the exec state machine + *************************************************************************/ +static FANS_EXEC_STATES_T handleFansExecStateRun( void ) +{ + FANS_EXEC_STATES_T state = FANS_EXEC_STATE_RUN; + + // Check if it is time to check for the control + if( ++fansControlCounter > FANS_CONTROL_INTERVAL ) + { + // Get the maximum temperature among all the thermistors and sensors to run fan from the hottest + F32 temperature = getMaximumTemperature(); + + // Solve the linear equation to calculate the PWM from temperature + F32 const pwm = ( slope * temperature ) - ( slope * FANS_MIN_PWM_PERCENT ) + MIN_ALLOWED_AMBIENT_TEMPERATURE; + + // If the fans calculated PWM is greater than the previous calculated PWM, we are ramping up + // otherwise, we are ramping down + if ( pwm >= fansStatus.calculatedPWM ) + { + // If the new PWM is greater than maximum allowed ramp up PWM, set it to max + // otherwise, set it to the actual PWM + if ( pwm >= FANS_MAX_ALLOWED_RAMP_UP_PWM_CHANGE ) + { + fansStatus.targetPWM = FANS_MAX_ALLOWED_RAMP_UP_PWM_CHANGE; + } + else + { + fansStatus.targetPWM = pwm; + } + } + else + { + // If we are ramping down, set the target PWM to max allowed ramp down PWM + fansStatus.targetPWM = FANS_MAX_ALLOWED_RAMP_DOWN_PWM_CHANGE; + } + + // Update the struct + fansStatus.calculatedPWM = pwm; + + // Set the PWM to inlet and outlet fans + setInletFansPWM( fansStatus.targetPWM ); + setOutletFansPWM( fansStatus.targetPWM ); + + // Reset the counter + fansControlCounter = 0; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The setInletFansPWM function sets the inlet fans PWM. + * @details Inputs: none + * @details Outputs: none + * @param PWM that will be set + * @return none + *************************************************************************/ +static void setInletFansPWM( F32 pwm ) +{ etpwmSetCmpA( etpwmREG6, (U32)( (S32)( ( pwm * (F32)(etpwmREG6->TBPRD) ) + FLOAT_TO_INT_ROUNDUP_OFFSET ) ) ); } -BOOL startFan2( F32 pwmPercent ) +/*********************************************************************//** + * @brief + * The setOutletFansPWM function sets the outlet fans PWM. + * @details Inputs: none + * @details Outputs: none + * @param PWM that will be set + * @return none + ************************************************************************/ +static void setOutletFansPWM( F32 pwm ) { + etpwmSetCmpB( etpwmREG6, (U32)( (S32)( ( pwm * (F32)(etpwmREG6->TBPRD) ) + FLOAT_TO_INT_ROUNDUP_OFFSET ) ) ); +} +/*********************************************************************//** + * @brief + * The getMaximumTemperature function runs through the thermistors driver + * and finds the maximum temperature. + * @details Inputs: none + * @details Outputs: none + * @return maximum temperature of the thermistors and sensors + ************************************************************************/ +static F32 getMaximumTemperature( void ) +{ + F32 temperature; + THERMISTORS_TEMP_SENSORS_T thermistor; + + F32 maxTemperature = 0; + + // Loop through the sensors and thermistors + for ( thermistor = THERMISTOR_ONBOARD_NTC; thermistor < NUM_OF_THERMISTORS; thermistor++ ) + { + // Get the value + temperature = getThermistorTemperatureValue( thermistor ); + + // If the latest temperature read is greater than the max temperature, + // set the maximum temperature there + if ( temperature > maxTemperature ) + { + maxTemperature = temperature; + } + } + + return maxTemperature; } -void stopFan1( void ) +/*********************************************************************//** + * @brief + * The getFansRPM function runs through the list of the fans to get the + * FPGA pulse from them and converts them to RPM. + * @details Inputs: fansStatus, toggle2RPMCoefficient + * @details Outputs: fansStatus + * @return none + ************************************************************************/ +static void getFansRPM( void ) { + FANS_NAMES_T fan; + U32 RPM; + // Loop through the fans and get the pulse of each of them + for ( fan = FAN_INLET_1; fan < NUM_OF_FANS_NAMES; fan++ ) + { + switch ( fan ) + { + case FAN_INLET_1: + RPM = getFPGAFan1Pulse(); //TODO is this the right place? + break; + + case FAN_INLET_2: + //TODO fill up + break; + + case FAN_INLET_3: + //TODO fill up + break; + + case FAN_OUTLET_1: + RPM = getFPGAFan2Pulse(); //TODO is this the right place? + break; + + case FAN_OUTLET_2: + //TODO fill up + break; + + case FAN_OUTLET_3: + //TODO fill up + break; + + default: + // Invalid fan has been selected, raise an alarm + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_FAN_SELECTED, fan ); + break; + } + + // If the pulse is close to 0 or 0, FPGA will report 0xFFFF + // Otherwise, convert the pulse to RPM + if ( RPM == FANS_ZERO_RPM_TOGGLE_PERIOD_VALUE ) + { + fansStatus.rpm[ fan ] = 0; + } + else + { + fansStatus.rpm[ fan ] = RPM * toggle2RPMCoefficient; + } + } } -void stopFan2( void ) + +/*********************************************************************//** + * @brief + * The getPublishFansDataInterval function gets the fans data publish interval. + * @details Inputs: fansPublishInterval + * @details Outputs: none + * @return data publish time interval in counts + *************************************************************************/ +static U32 getPublishFansDataInterval( void ) { + U32 result = fansPublishInterval.data; + if ( OVERRIDE_KEY == fansPublishInterval.override ) + { + result = fansPublishInterval.ovData; + } + + return result; } + +/*********************************************************************//** + * @brief + * The publishFansData function publishes the fans data at the specified + * time interval. + * @details Inputs: dataPublishCounter + * @details Outputs: dataPublishCounter + * @return none + *************************************************************************/ +static void publishFansData( void ) +{ + if ( ++fansPublishCounter > getPublishFansDataInterval() ) + { + FANS_DATA_T fansData; + + fansData.fansCalculatedPWM = fansStatus.calculatedPWM * 100; + fansData.fansTargetPWM = fansStatus.targetPWM * 100; + //TODO fill up the speeds + + broadcastFansData( &fansData ); + + fansPublishCounter = 0; + } +} + +/************************************************************************* +* TEST SUPPORT FUNCTIONS +*************************************************************************/ + +BOOL testSetFanPublishIntervalOverride( U32 value ) +{ + +} +BOOL testResetFanPublishIntervalOverride( void ) +{ + +} + +