Index: firmware/App/Controllers/Fans.c =================================================================== diff -u -r45263215b372cd579e8e16bb8073c287c726c55d -r1f500f8e6159a3fbab85ea68389e918a6df66400 --- firmware/App/Controllers/Fans.c (.../Fans.c) (revision 45263215b372cd579e8e16bb8073c287c726c55d) +++ firmware/App/Controllers/Fans.c (.../Fans.c) (revision 1f500f8e6159a3fbab85ea68389e918a6df66400) @@ -12,67 +12,74 @@ // ********** private definitions ********** -#define FANS_MIN_PWM_PERCENT 0.1 ///< Fans min PWM. -#define FANS_MAX_PWM_PERCENT 0.95 ///< Fans max PWM. +#define FANS_MIN_DUTY_CYCLE 0.1 ///< Fans min PWM. +#define FANS_MAX_DUTY_CYCLE 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 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 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 zero RPM toggle period value. +#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. /// Fans self test states typedef enum fans_Self_Test { FANS_SELF_TEST_START = 0, ///< Fans self test start state + FANS_SELF_TEST_CHECK_RPM, ///< Fans self test check RPM FAN_SELF_TEST_COMPLETE, ///< Fans self test complete 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_WAIT_FOR_POST = 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 + F32 targetDutyCycle; ///< Fan's target duty cycle that was fed to the fans + F32 previousDutyCycle; ///< Fan's previous duty cycle from calculation + U32 rpm[ NUM_OF_FANS_NAMES ]; ///< Fan's current tachometers reading in RPM } 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 FANS_EXEC_STATES_T fansExecState = FANS_EXEC_STATE_WAIT_FOR_POST; ///< 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 +/// 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 ); /// 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 const F32 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 FANS_SELF_TEST_STATES_T handleSelfTestStart( void ); +static FANS_SELF_TEST_STATES_T handleSelfTestCheckRPM( void ); +static FANS_EXEC_STATES_T handleExecStateWaitForPOST( void ); +static FANS_EXEC_STATES_T handleExecStateRun( void ); + static void setInletFansPWM( F32 pwm ); static void setOutletFansPWM( F32 pwm ); static F32 getMaximumTemperature( void ); -static void getFansRPM( void ); +static void convertFansTogglePeriod2RPM( void ); static U32 getPublishFansDataInterval( void ); static void publishFansData( void ); @@ -88,7 +95,7 @@ void initFans( void ) { // Initialize the variables - fansExecState = FANS_EXEC_STATE_START; + fansExecState = FANS_EXEC_STATE_WAIT_FOR_POST; fansSelfTestReslt = SELF_TEST_STATUS_IN_PROGRESS; fansSelfTestState = FANS_SELF_TEST_START; fansControlCounter = 0; @@ -107,8 +114,13 @@ switch ( fansSelfTestState ) { case FANS_SELF_TEST_START: + fansSelfTestState = handleSelfTestStart(); break; + case FANS_SELF_TEST_CHECK_RPM: + fansSelfTestState = handleSelfTestCheckRPM(); + break; + case FAN_SELF_TEST_COMPLETE: // Done with POST. Do nothing break; @@ -134,12 +146,12 @@ { switch ( fansExecState ) { - case FANS_EXEC_STATE_START: - fansExecState = handleFansExecStateStart(); + case FANS_EXEC_STATE_WAIT_FOR_POST: + fansExecState = handleExecStateWaitForPOST(); break; case FANS_EXEC_STATE_RUN: - fansExecState = handleFansExecStateRun(); + fansExecState = handleExecStateRun(); break; default: @@ -178,23 +190,95 @@ /*********************************************************************//** * @brief + * The handleSelfTestStart function handles the start state of the fans' + * self test. + * @details Inputs: fansStatus + * @details Outputs: none + * @return next state of self test + *************************************************************************/ +static FANS_SELF_TEST_STATES_T handleSelfTestStart( void ) +{ + FANS_SELF_TEST_STATES_T state = FANS_SELF_TEST_START; + FANS_NAMES_T fan; + + // Get the raw toggle periods from FPGA and convert them to RPM. They all should be 0 upon staring + // the device + convertFansTogglePeriod2RPM(); + + // Loop through all the fans to check their RPM + for( fan = FAN_INLET_1; fan < NUM_OF_FANS_NAMES; fan++ ) + { + if ( fansStatus.rpm[ fan ] > 0 ) + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_FAN_RPM_OUT_OF_RANGE, fan, fansStatus.rpm[ fan ] ); + } + } + + // Set the fans to the target PWM for the next stage + setInletFansPWM( FANS_SELF_TEST_TARGET_PWM ); + setOutletFansPWM( FANS_SELF_TEST_TARGET_PWM ); + state = FANS_SELF_TEST_CHECK_RPM; + + return state; +} + +/*********************************************************************//** + * @brief + * The handleSelfTestCheckRPM function handles the check RPM state of the + * fans' self test. + * @details Inputs: fansStatus + * @details Outputs: none + * @return next state of self test + *************************************************************************/ +static FANS_SELF_TEST_STATES_T handleSelfTestCheckRPM( void ) +{ + FANS_SELF_TEST_STATES_T state = FANS_SELF_TEST_CHECK_RPM; + FANS_NAMES_T fan; + + // Wait for the fans to run for a certain period of time before checking their RPM + if ( ++fansControlCounter > FANS_SELF_TEST_WAIT_INTERVAL ) + { + convertFansTogglePeriod2RPM(); + + // Loop through all the fans to check their RPM. They should above the specified RPM + for( fan = FAN_INLET_1; fan < NUM_OF_FANS_NAMES; fan++ ) + { + if ( fansStatus.rpm[ fan ] > MIN_TARGET_RPM_IN_SELF_TEST ) + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_FAN_RPM_OUT_OF_RANGE, fan, fansStatus.rpm[ fan ] ); + } + } + + // Turn off the fans, done with self test + setInletFansPWM( 0 ); + setOutletFansPWM( 0 ); + state = FAN_SELF_TEST_COMPLETE; + } + + return state; +} + +/*********************************************************************//** + * @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 ) +static FANS_EXEC_STATES_T handleExecStateWaitForPOST( void ) { - FANS_EXEC_STATES_T state = FANS_EXEC_STATE_START; + FANS_EXEC_STATES_T state = FANS_EXEC_STATE_WAIT_FOR_POST; - // TODO do we need to wait for POST or something? + // Wait for the self test to finish before starting the fans + if ( fansSelfTestState == FAN_SELF_TEST_COMPLETE ) + { + // Start the fans with minimum PWM. The control will decide the next PWM automatically. + setInletFansPWM( FANS_MIN_DUTY_CYCLE ); + setOutletFansPWM( FANS_MIN_DUTY_CYCLE ); + state = FANS_EXEC_STATE_RUN; + } - // Start the fans with minimum PWM - setInletFansPWM( FANS_MIN_PWM_PERCENT ); - setOutletFansPWM( FANS_MIN_PWM_PERCENT ); - state = FANS_EXEC_STATE_RUN; - return state; } @@ -206,7 +290,7 @@ * @details Outputs: fansStatus, fansControlCounter * @return the next state of the exec state machine *************************************************************************/ -static FANS_EXEC_STATES_T handleFansExecStateRun( void ) +static FANS_EXEC_STATES_T handleExecStateRun( void ) { FANS_EXEC_STATES_T state = FANS_EXEC_STATE_RUN; @@ -216,36 +300,47 @@ // 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; + // Solve the linear equation to calculate the duty cycle from temperature + F32 dutyCycle = slope * ( temperature - MIN_ALLOWED_AMBIENT_TEMPERATURE ) + FANS_MIN_DUTY_CYCLE; - // If the fans calculated PWM is greater than the previous calculated PWM, we are ramping up + // Check whether the duty cycle is not outside of the range + // and cap it if needed + if ( dutyCycle < FANS_MIN_DUTY_CYCLE ) + { + dutyCycle = FANS_MIN_DUTY_CYCLE; + } + if ( dutyCycle > FANS_MAX_DUTY_CYCLE ) + { + dutyCycle = FANS_MAX_DUTY_CYCLE; + } + + // If the fans calculated duty cycle is greater than the previous calculated duty cycle, we are ramping up // otherwise, we are ramping down - if ( pwm >= fansStatus.calculatedPWM ) + if ( dutyCycle >= fansStatus.previousDutyCycle ) { - // 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 ) + // 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.previousDutyCycle ) >= FANS_MAX_ALLOWED_RAMP_UP_DELTA_DUTY_CYCLE ) { - fansStatus.targetPWM = FANS_MAX_ALLOWED_RAMP_UP_PWM_CHANGE; + fansStatus.targetDutyCycle += FANS_MAX_ALLOWED_RAMP_UP_DELTA_DUTY_CYCLE; } else { - fansStatus.targetPWM = pwm; + fansStatus.targetDutyCycle = dutyCycle; } } 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; + // If we are ramping down, set the target duty cycle to max allowed ramp down PWM + fansStatus.targetDutyCycle -= FANS_MAX_ALLOWED_RAMP_DOWN_DELTA_DUTY_CYCLE; } - // Update the struct - fansStatus.calculatedPWM = pwm; + // Update the structure + fansStatus.previousDutyCycle = dutyCycle; // Set the PWM to inlet and outlet fans - setInletFansPWM( fansStatus.targetPWM ); - setOutletFansPWM( fansStatus.targetPWM ); + setInletFansPWM( fansStatus.targetDutyCycle ); + setOutletFansPWM( fansStatus.targetDutyCycle ); // Reset the counter fansControlCounter = 0; @@ -292,9 +387,8 @@ { F32 temperature; THERMISTORS_TEMP_SENSORS_T thermistor; + F32 maxTemperature = 0.0; - F32 maxTemperature = 0; - // Loop through the sensors and thermistors for ( thermistor = THERMISTOR_ONBOARD_NTC; thermistor < NUM_OF_THERMISTORS; thermistor++ ) { @@ -320,38 +414,38 @@ * @details Outputs: fansStatus * @return none ************************************************************************/ -static void getFansRPM( void ) +static void convertFansTogglePeriod2RPM( void ) { FANS_NAMES_T fan; - U32 RPM; + U32 togglePeriod; // 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 = getFPGAInletFan1Pulse(); //TODO is this the right place? + togglePeriod = getFPGAInletFan1TogglePeriod(); break; case FAN_INLET_2: - //TODO fill up + togglePeriod = getFPGAInletFan2TogglePeriod(); break; case FAN_INLET_3: - //TODO fill up + togglePeriod = getFPGAInletFan3TogglePeriod(); break; case FAN_OUTLET_1: - RPM = getFPGAInletFan2Pulse(); //TODO is this the right place? + togglePeriod = getFPGAOutletFan1TogglePeriod(); break; case FAN_OUTLET_2: - //TODO fill up + togglePeriod = getFPGAOutletFan2TogglePeriod(); break; case FAN_OUTLET_3: - //TODO fill up + togglePeriod = getFPGAOutletFan3TogglePeriod(); break; default: @@ -362,13 +456,13 @@ // 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 ) + if ( togglePeriod == FANS_ZERO_RPM_TOGGLE_PERIOD_VALUE ) { fansStatus.rpm[ fan ] = 0; } else { - fansStatus.rpm[ fan ] = RPM * toggle2RPMCoefficient; + fansStatus.rpm[ fan ] = togglePeriod * toggle2RPMCoefficient; } } } @@ -406,8 +500,8 @@ { FANS_DATA_T fansData; - fansData.fansCalculatedPWM = fansStatus.calculatedPWM * 100; - fansData.fansTargetPWM = fansStatus.targetPWM * 100; + fansData.fansCalculatedPWM = fansStatus.previousDutyCycle * 100; + fansData.fansTargetPWM = fansStatus.targetDutyCycle * 100; fansData.fanInlet1RPM = fansStatus.rpm[ FAN_INLET_1 ]; fansData.fanInlet2RPM = fansStatus.rpm[ FAN_INLET_2 ]; fansData.fanInlet3RPM = fansStatus.rpm[ FAN_INLET_3 ];