Index: firmware/App/Controllers/Fans.c =================================================================== diff -u -r01da5a9677431082a2ce6cdd69c26f9e778537b4 -ra89d6b091874136d75a9bfbdbbc1ff00f42467b3 --- firmware/App/Controllers/Fans.c (.../Fans.c) (revision 01da5a9677431082a2ce6cdd69c26f9e778537b4) +++ firmware/App/Controllers/Fans.c (.../Fans.c) (revision a89d6b091874136d75a9bfbdbbc1ff00f42467b3) @@ -1,9 +1,27 @@ +/************************************************************************** +* +* Copyright (c) 2020-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 Fans.c +* +* @author (last) Sean Nash +* @date (last) 15-Jul-2022 +* +* @author (original) Dara Navaei +* @date (original) 25-Nov-2020 +* +***************************************************************************/ #include "etpwm.h" +#include "math.h" #include "Fans.h" #include "FPGA.h" #include "MessageSupport.h" +#include "NVDataMgmt.h" #include "PersistentAlarm.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" @@ -18,28 +36,28 @@ // ********** private definitions ********** -#define FANS_MIN_DUTY_CYCLE 0.1 ///< Fans min PWM. -#define FANS_MAX_DUTY_CYCLE 0.95 ///< Fans max PWM. +#define FANS_MIN_DUTY_CYCLE 0.1F ///< Fans min PWM. +#define FANS_MAX_DUTY_CYCLE 0.95F ///< 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_DELTA_DUTY_CYCLE 0.3 ///< Fans max allowed ramp up PWM change. -#define FANS_MAX_ALLOWED_RAMP_DOWN_DELTA_DUTY_CYCLE 0.005 ///< Fans min allowed ramp down PWM change. +#define FANS_MAX_ALLOWED_RAMP_UP_DELTA_DUTY_CYCLE 0.3F ///< Fans max allowed ramp up PWM change. +#define FANS_MAX_ALLOWED_RAMP_DOWN_DELTA_DUTY_CYCLE 0.005F ///< Fans min allowed ramp down PWM change. -#define TOGGLE_PERIOD_RESOLUTION_SECONDS 0.0000025 ///< FPGA fans toggle period resolution in micro seconds. +#define TOGGLE_PERIOD_RESOLUTION_SECONDS 0.0000025F ///< FPGA fans toggle period resolution in micro seconds. #define ROTATIONAL_TO_TOGGLE_PERIOD_CONVERSION 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 zero RPM toggle period value. #define MIN_TARGET_RPM_IN_SELF_TEST 1000 ///< Fans min target RPM that they should be during POST. #define FANS_SELF_TEST_WAIT_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Fans self test wait time for the fans to get to RPM. -#define FANS_SELF_TEST_TARGET_PWM 0.5 ///< Fans self test target PWM for testing the fans are running. +#define FANS_SELF_TEST_TARGET_PWM 0.5F ///< Fans self test target PWM for testing the fans are running. #define FANS_MAX_ALLOWED_RPM_OUT_OF_RANGE_INTERVAL ( 3 * MS_PER_SECOND ) ///< Fans max allowed RPM out of range time interval. #define FANS_MAX_ALLOWED_RPM 5500 ///< Fans max allowed RPM value. #define FANS_MIN_ALLOWED_RPM 150 ///< Fans max allowed RPM value. -#define FANS_MONITOR_INTERVAL_COUNT ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Fans monitor time interval in counts. -#define FANS_MIN_RPM_OUT_OF_RANGE_TOL 0.25 ///< Fans min RPM out of range tolerance. -#define FANS_MAX_RPM_OUT_OF_RANGE_TOL 0.5 ///< Fans max RPM out of range tolerance. +#define FANS_MIN_RPM_OUT_OF_RANGE_TOL 0.25F ///< Fans min RPM out of range tolerance. +#define FANS_MAX_RPM_OUT_OF_RANGE_TOL 0.5F ///< Fans max RPM out of range tolerance. +#define DATA_PUBLISH_COUNTER_START_COUNT 12 ///< Data publish counter start count. /// Fans exec states typedef enum fans_Exec_States @@ -52,19 +70,22 @@ /// Fans status struct typedef struct { - F32 dutyCycle; ///< Fans duty cycle that was fed to the fans. + OVERRIDE_F32_T dutyCycle; ///< Fans duty cycle that was fed to the fans. F32 targetRPM; ///< Fans target RPM. OVERRIDE_F32_T rpm[ NUM_OF_FANS_NAMES ]; ///< Fan's current tachometers reading in RPM. } FAN_STATUS_T; +// ********** private data ********** + static FAN_STATUS_T fansStatus; ///< Fans status. static FANS_EXEC_STATES_T fansExecState = FANS_EXEC_STATE_WAIT_FOR_POST_STATE; ///< Fans exec state. static U32 fansControlCounter = 0; ///< Fans control interval counter. -static U32 fansPublishCounter = 0; ///< Fans data publish interval counter. -static U32 fansMonitorCounter = 0; ///< Fans monitor interval counter. +static U32 fansPublishCounter; ///< Fans data publish interval counter. static BOOL isPOSTComplete = FALSE; ///< Flag that indicates whether POST is complete or not. static BOOL hasAlarmBeenRaised = FALSE; ///< Flag that indicates whether the RPM out of range alarm been raise. -static U32 rpmAlarmStartTimer = 0; ///< RPM out of range alarm start timer when the alarm is raised. +static U32 rpmAlarmStartTimeOffset; ///< RPM out of range alarm start time offset. +static U32 rpmAlarmStartTime = 0; ///< RPM alarm start time. +static DG_FANS_CAL_RECORD_T fansCalReocrd; ///< Fans calibration record. /// Temperature to duty cycle conversion slope (duty cycle not in percent) static const F32 SLOPE = ( FANS_MAX_DUTY_CYCLE - FANS_MIN_DUTY_CYCLE ) / ( MAX_ALLOWED_AMBINET_TEMPERATURE - MIN_ALLOWED_AMBIENT_TEMPERATURE ); @@ -74,12 +95,14 @@ static OVERRIDE_U32_T fansPublishInterval = { FANS_DATA_PUBLISH_INTERVAL, FANS_DATA_PUBLISH_INTERVAL, 0, 0 }; ///< Fans publish time interval override. +// ********** private function prototypes ********** + static FANS_EXEC_STATES_T handleExecStateWaitForPOST( void ); static FANS_EXEC_STATES_T handleExecStateRun( void ); static void setInletFansDutyCycle( F32 pwm ); static void setOutletFansDutyCycle( F32 pwm ); -static F32 getMaximumTemperature( void ); +static F32 getMaximumTemperature( void ); static void convertTogglePeriod2RPM( void ); static void monitorFans( void ); static void publishFansData( void ); @@ -89,28 +112,31 @@ * The initFans function initializes the fans module. * @details Inputs: none * @details Outputs: fansExecState, fansControlCounter, fansPublishCounter, - * fansMonitorCounter, isPOSTComplete, hasAlarmBeenRaised, rpmAlarmStartTimer, - * fansStatus + * isPOSTComplete, hasAlarmBeenRaised, rpmAlarmStartTime, + * fansStatus, rpmAlarmStartTimeOffset * @return none *************************************************************************/ void initFans( void ) { FAN_NAMES_T fan; // Initialize the variables - fansExecState = FANS_EXEC_STATE_WAIT_FOR_POST_STATE; - fansControlCounter = 0; - fansPublishCounter = 0; - fansMonitorCounter = 0; - isPOSTComplete = FALSE; - hasAlarmBeenRaised = FALSE; - rpmAlarmStartTimer = 0; + fansExecState = FANS_EXEC_STATE_WAIT_FOR_POST_STATE; + fansControlCounter = 0; + fansPublishCounter = DATA_PUBLISH_COUNTER_START_COUNT; + isPOSTComplete = FALSE; + hasAlarmBeenRaised = FALSE; + rpmAlarmStartTime = 0; + rpmAlarmStartTimeOffset = 0; + fansStatus.dutyCycle.data = 0.0; + fansStatus.dutyCycle.ovData = 0.0; + fansStatus.dutyCycle.ovInitData = 0.0; + fansStatus.dutyCycle.override = OVERRIDE_RESET; + fansStatus.targetRPM = 0.0; // Initialize the fans for ( fan = FAN_INLET_1; fan < NUM_OF_FANS_NAMES; fan++ ) { - fansStatus.dutyCycle = 0.0; - fansStatus.targetRPM = 0.0; fansStatus.rpm[ fan ].data = 0.0; fansStatus.rpm[ fan ].ovData = 0.0; fansStatus.rpm[ fan ].ovInitData = 0.0; @@ -125,20 +151,28 @@ * @brief * The execFansSelfTest function executes the fans self test. * @details Inputs: none - * @details Outputs: isPOSTComplete + * @details Outputs: isPOSTComplete, fansCalReocrd * @return Status of self test *************************************************************************/ SELF_TEST_STATUS_T execFansSelfTest( void ) { - SELF_TEST_STATUS_T status = SELF_TEST_STATUS_IN_PROGRESS; + SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; - // TODO implement the calibration processing function. - // It returns a pass for now - + BOOL calStatus = getNVRecord2Driver( GET_CAL_FANS_RECORD, (U08*)&fansCalReocrd, sizeof( DG_FANS_CAL_RECORD_T ), + NUM_OF_CAL_DATA_FANS, ALARM_ID_NO_ALARM ); + // Signal POST is completed isPOSTComplete = TRUE; - status = SELF_TEST_STATUS_PASSED; - return status; + if ( TRUE == calStatus ) + { + result = SELF_TEST_STATUS_PASSED; + } + else + { + result = SELF_TEST_STATUS_FAILED; + } + + return result; } /*********************************************************************//** @@ -247,8 +281,9 @@ { FANS_EXEC_STATES_T state = FANS_EXEC_STATE_RUN_STATE; - // Check if it is time to check for the control - if ( ++fansControlCounter > FANS_CONTROL_INTERVAL ) + // Check if it is time to check for the control and the duty cycle override is in the reset mode so the duty cycle + // is not overridden + if ( ( ++fansControlCounter > FANS_CONTROL_INTERVAL ) && ( OVERRIDE_RESET == fansStatus.dutyCycle.override ) ) { // Get the maximum temperature among all the thermistors and temperature sensors to run fan from the hottest temperature F32 temperature = getMaximumTemperature(); @@ -268,44 +303,50 @@ // If the fans calculated duty cycle is greater than the previous calculated duty cycle, we are ramping up // otherwise, we are ramping down - if ( dutyCycle >= fansStatus.dutyCycle ) + if ( dutyCycle >= fansStatus.dutyCycle.data ) { // If the delta duty cycle from the previous duty cycle is greater than the max allowed ramp up duty cycle, // otherwise, only add the delta duty cycle - if ( ( dutyCycle - fansStatus.dutyCycle ) >= FANS_MAX_ALLOWED_RAMP_UP_DELTA_DUTY_CYCLE ) + if ( ( dutyCycle - fansStatus.dutyCycle.data ) >= FANS_MAX_ALLOWED_RAMP_UP_DELTA_DUTY_CYCLE ) { - fansStatus.dutyCycle += FANS_MAX_ALLOWED_RAMP_UP_DELTA_DUTY_CYCLE; + fansStatus.dutyCycle.data += FANS_MAX_ALLOWED_RAMP_UP_DELTA_DUTY_CYCLE; } else { - fansStatus.dutyCycle = dutyCycle; + fansStatus.dutyCycle.data = dutyCycle; } } else { // If the delta duty cycle from the previous duty cycle is greater than the max allowed ramp down duty cycle, // otherwise, only add the delta duty cycle - if ( ( fansStatus.dutyCycle - dutyCycle ) >= FANS_MAX_ALLOWED_RAMP_DOWN_DELTA_DUTY_CYCLE ) + if ( ( fansStatus.dutyCycle.data - dutyCycle ) >= FANS_MAX_ALLOWED_RAMP_DOWN_DELTA_DUTY_CYCLE ) { // If we are ramping down, set the target duty cycle to max allowed ramp down duty cycle - fansStatus.dutyCycle -= FANS_MAX_ALLOWED_RAMP_DOWN_DELTA_DUTY_CYCLE; + fansStatus.dutyCycle.data -= FANS_MAX_ALLOWED_RAMP_DOWN_DELTA_DUTY_CYCLE; } else { - fansStatus.dutyCycle = dutyCycle; + fansStatus.dutyCycle.data = dutyCycle; } } // Calculate the target RPM for the the duty cycle - fansStatus.targetRPM = fansStatus.dutyCycle * FANS_MAX_ALLOWED_RPM; + fansStatus.targetRPM = fansStatus.dutyCycle.data * FANS_MAX_ALLOWED_RPM; // Set the PWM to inlet and outlet fans - setInletFansDutyCycle( fansStatus.dutyCycle ); - setOutletFansDutyCycle( fansStatus.dutyCycle ); + setInletFansDutyCycle( fansStatus.dutyCycle.data ); + setOutletFansDutyCycle( fansStatus.dutyCycle.data ); // Reset the counter fansControlCounter = 0; } + else if ( OVERRIDE_KEY == fansStatus.dutyCycle.override ) + { + // Set the PWM to inlet and outlet fans + setInletFansDutyCycle( fansStatus.dutyCycle.ovData ); + setOutletFansDutyCycle( fansStatus.dutyCycle.ovData ); + } return state; } @@ -353,7 +394,7 @@ // Loop through the sensors and thermistors for ( thermistor = THERMISTOR_ONBOARD_NTC; thermistor < NUM_OF_THERMISTORS; thermistor++ ) { - temperature = getThermistorTemperatureValue( thermistor ); + temperature = getThermistorTemperatureValue( thermistor ); maxTemperature = ( temperature > maxTemperature ? temperature : maxTemperature ); } @@ -416,52 +457,62 @@ /*********************************************************************//** * @brief * The monitorFans function monitors the fans for RPM. - * @details Inputs: fansMonitorCounter, rpmAlarmStartTimer, hasAlarmBeenRaised - * @details Outputs: fansMonitorCounter, rpmAlarmStartTimer, hasAlarmBeenRaised + * @details Inputs: rpmAlarmStartTimer, hasAlarmBeenRaised + * @details Outputs: rpmAlarmStartTimer, hasAlarmBeenRaised * @return none *************************************************************************/ static void monitorFans( void ) { FAN_NAMES_T fan; + F32 rpm = 0.0; + BOOL isAlarmTriggered = FALSE; + BOOL isFanRPMOutOfRange = FALSE; - if ( ++fansMonitorCounter >= FANS_MONITOR_INTERVAL_COUNT ) + // The RPM is expected to be 5500 @ 100% duty cycle + // The nominal RPM = duty cycle * 5500 / 1.0 + // The RPM tolerance is -25% to +50% of the nominal RPM + F32 dutyCycle = ( OVERRIDE_RESET == fansStatus.dutyCycle.override ? fansStatus.dutyCycle.data : fansStatus.dutyCycle.ovData ); + F32 fansNominalRPM = dutyCycle * FANS_MAX_ALLOWED_RPM; + F32 fansMinAllowedRPM = fansNominalRPM - ( fansNominalRPM * FANS_MIN_RPM_OUT_OF_RANGE_TOL ); + F32 fansMaxAllowedRPM = fansNominalRPM + ( fansNominalRPM * FANS_MAX_RPM_OUT_OF_RANGE_TOL ); + + for ( fan = FAN_INLET_1; fan < NUM_OF_FANS_NAMES; fan++ ) { - // If the fans alarm has been raise already, do not raise again - if ( FALSE == hasAlarmBeenRaised ) - { - BOOL isFanRPMOutOfRange; + rpm = getMeasuredFanRPM( fan ); + isFanRPMOutOfRange |= ( ( rpm < fansMinAllowedRPM ) || ( ( rpm > fansMaxAllowedRPM ) ) ? TRUE : FALSE ); + } - // The RPM is expected to be 5500 @ 100% duty cycle - // The nominal RPM = duty cycle * 5500 / 1.0 - // The RPM tolerance is -25% to +50% of the nominal RPM - F32 fansNominalRPM = fansStatus.dutyCycle * FANS_MAX_ALLOWED_RPM; - F32 fansMinAllowedRPM = fansNominalRPM - ( fansNominalRPM * FANS_MIN_RPM_OUT_OF_RANGE_TOL ); - F32 fansMaxAllowedRPM = fansNominalRPM + ( fansNominalRPM * FANS_MAX_RPM_OUT_OF_RANGE_TOL ); + // If the fans alarm has been raised already, do not raise again + if ( FALSE == hasAlarmBeenRaised ) + { + isAlarmTriggered = isPersistentAlarmTriggered( ALARM_ID_DG_FAN_RPM_OUT_OF_RANGE, isFanRPMOutOfRange ); - for ( fan = FAN_INLET_1; fan < NUM_OF_FANS_NAMES; fan++ ) - { - isFanRPMOutOfRange = ( getMeasuredFanRPM( fan ) < fansMinAllowedRPM ) || ( getMeasuredFanRPM( fan ) > fansMaxAllowedRPM ); - checkPersistentAlarm( ALARM_ID_DG_FAN_RPM_OUT_OF_RANGE, isFanRPMOutOfRange, getMeasuredFanRPM( fan ), FANS_MAX_ALLOWED_RPM ); + if ( TRUE == isAlarmTriggered ) + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DG_FAN_RPM_OUT_OF_RANGE, rpm ) + // Set the alarm flag to TRUE + hasAlarmBeenRaised = TRUE; - // If the RPM out of range alarm has been raised, do not raise it again, until its alarm silence time has been elapsed - hasAlarmBeenRaised = ( TRUE == isAlarmActive( ALARM_ID_DG_FAN_RPM_OUT_OF_RANGE ) ? TRUE : FALSE ); - - // If the alarm has been raised and the alarm start timer is 0, start the timer - if ( ( TRUE == hasAlarmBeenRaised ) && ( 0 == rpmAlarmStartTimer ) ) - { - rpmAlarmStartTimer = getMSTimerCount(); - } + // If the alarm has been raised but the start time of the alarm has not been set, set the alarm start timer + if ( 0 == rpmAlarmStartTime ) + { + rpmAlarmStartTime = getMSTimerCount(); } } - // If the alarm has been raised the silence the alarm time has elapsed, get ready to raise the alarm in case the RPM was out of range - // Once the alarm is raised, it is not raised again for 24 hours - else if ( ( TRUE == hasAlarmBeenRaised ) && ( TRUE == didTimeout( rpmAlarmStartTimer, SECONDS_IN_A_DAY ) ) ) + } + // If the alarm has been raised and the alarm has been silent for at least a day, set the flag to FALSE + // This way, if the fans RPM are out of range the alarm will be raised again. This alarm is supposed to be raised + // and remain silent for a defined period of time. + else + { + S32 elapsedTime = rpmAlarmStartTime - rpmAlarmStartTimeOffset; + + if ( calcTimeSince( elapsedTime ) >= SECONDS_IN_A_DAY * MS_PER_SECOND ) { - hasAlarmBeenRaised = FALSE; - rpmAlarmStartTimer = 0; + hasAlarmBeenRaised = FALSE; + rpmAlarmStartTime = 0; + rpmAlarmStartTimeOffset = 0; } - - fansMonitorCounter = 0; } } @@ -479,14 +530,15 @@ { FANS_DATA_T fansData; - fansData.dutyCycle = fansStatus.dutyCycle * FRACTION_TO_PERCENT_FACTOR; - fansData.targetFansRPM = fansStatus.targetRPM; - fansData.fanInlet1RPM = getMeasuredFanRPM( FAN_INLET_1 ); - fansData.fanInlet2RPM = getMeasuredFanRPM( FAN_INLET_2 ); - fansData.fanInlet3RPM = getMeasuredFanRPM( FAN_INLET_3 ); - fansData.fanOutlet1RPM = getMeasuredFanRPM( FAN_OUTLET_1 ); - fansData.fanOutlet2RPM = getMeasuredFanRPM( FAN_OUTLET_2 ); - fansData.fanOutlet3RPM = getMeasuredFanRPM( FAN_OUTLET_3 ); + fansData.dutyCycle = ( OVERRIDE_RESET == fansStatus.dutyCycle.override ? fansStatus.dutyCycle.data : fansStatus.dutyCycle.ovData ) * FRACTION_TO_PERCENT_FACTOR; + fansData.targetFansRPM = fansStatus.targetRPM; + fansData.fanInlet1RPM = getMeasuredFanRPM( FAN_INLET_1 ); + fansData.fanInlet2RPM = getMeasuredFanRPM( FAN_INLET_2 ); + fansData.fanInlet3RPM = getMeasuredFanRPM( FAN_INLET_3 ); + fansData.fanOutlet1RPM = getMeasuredFanRPM( FAN_OUTLET_1 ); + fansData.fanOutlet2RPM = getMeasuredFanRPM( FAN_OUTLET_2 ); + fansData.fanOutlet3RPM = getMeasuredFanRPM( FAN_OUTLET_3 ); + fansData.rpmAlarmTimeOffset = calcTimeSince( rpmAlarmStartTime - rpmAlarmStartTimeOffset ); broadcastData( MSG_ID_DG_FANS_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&fansData, sizeof( FANS_DATA_T ) ); @@ -598,4 +650,74 @@ return result; } +/*********************************************************************//** + * @brief + * The testSetFanRPMAlarmStartTimestamp function set the fan RPM alarm start + * time offset. + * @details Inputs: none + * @details Outputs: rpmAlarmStartTimeOffset + * @param milliSeconds the milliseconds the time to be offset + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetFanRPMAlarmStartTimestamp( U32 milliSeconds ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + rpmAlarmStartTimeOffset = milliSeconds; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetFansDutyCycleOverride function overrides fans duty cycle + * @details Inputs: none + * @details Outputs: fansStatus + * @param value the duty cycle value to be overridden + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetFansDutyCycleOverride( F32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + if( ( value >= FANS_MIN_DUTY_CYCLE ) && ( value <= FANS_MAX_DUTY_CYCLE ) ) + { + fansStatus.dutyCycle.ovData = value; + fansStatus.dutyCycle.override = OVERRIDE_KEY; + + result = TRUE; + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetFansDutyCycleOverride function resets the fans duty cycle + * override + * @details Inputs: none + * @details Outputs: fansStatus + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testResetFansDutyCycleOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + fansStatus.dutyCycle.override = OVERRIDE_RESET; + fansStatus.dutyCycle.ovData = 0.0; + + result = TRUE; + } + + return result; +} + /**@}*/