133 lines
4.2 KiB
C
133 lines
4.2 KiB
C
#include "fan_controller.h"
|
||
|
||
#include "eeprom_config.h"
|
||
|
||
/*
|
||
* The control loop is based on a temperature corridor concept. While the
|
||
* temperature is inside the corridor, the PWM duty cycle will not be changed.
|
||
*
|
||
* If it is above the corridor’s maximum temperature, the duty cycle increases
|
||
* as defined by a PI controller, i.e. there is a dynamic/temporary part (P)
|
||
* that depends on how far outside the corridor the temperature is, and there
|
||
* is a persistent part (I) that is continuously increased while the
|
||
* temperature is outside the corridor.
|
||
*
|
||
* If the temperature is below the corridor’s minimum temperature, similar
|
||
* rules are applied to decrease the fan speed. However, the P part makes
|
||
* little sense here so its gain is usually set to 0.
|
||
*
|
||
* The fan starts in a stopped state and stays there until the temperature
|
||
* rises above the startup temperature. Once that happens, the PWM is set
|
||
* to a specific duty cycle (high enough to reliably start the fan) and
|
||
* regulation begins.
|
||
*/
|
||
|
||
/* Note:
|
||
* - all temperatures are in °C represented in fxp_t.
|
||
* - all duty cycles are in percent represented in fxp_t.
|
||
*/
|
||
|
||
// corridor definition
|
||
#define CORRIDOR_MIN_TEMPERATURE EEPROM_CONFIG_CORRIDOR_MIN_TEMPERATURE
|
||
#define CORRIDOR_MAX_TEMPERATURE EEPROM_CONFIG_CORRIDOR_MAX_TEMPERATURE
|
||
|
||
// The fan is started when temperature rises above this temperature.
|
||
#define FAN_START_TEMPERATURE EEPROM_CONFIG_FAN_START_TEMPERATURE
|
||
|
||
// The fan is stopped when the temperature falls below this point.
|
||
#define FAN_OFF_TEMPERATURE EEPROM_CONFIG_FAN_OFF_TEMPERATURE
|
||
|
||
// Emergency temperature level. If this is reached, the duty cycle is directly
|
||
// set to the maximum to skip a long ramp-up phase.
|
||
#define EMERGENCY_TEMPERATURE EEPROM_CONFIG_EMERGENCY_TEMPERATURE
|
||
|
||
// The fan PWM duty cycle cannot fall below this limit. It is held until
|
||
// temperature falls below the off-temperature.
|
||
#define DUTY_CYCLE_MIN EEPROM_CONFIG_DUTY_CYCLE_MIN
|
||
|
||
// The fan PWM duty cycle cannot go above this limit. Should be set to 100.
|
||
#define DUTY_CYCLE_MAX EEPROM_CONFIG_DUTY_CYCLE_MAX
|
||
|
||
// The fan is always started with this duty cycle. Do not set too low.
|
||
#define DUTY_CYCLE_START EEPROM_CONFIG_DUTY_CYCLE_START
|
||
|
||
// gain unit: percent duty cycle change per °C deviation per update cycle
|
||
#define GAIN_T_HIGH_P EEPROM_CONFIG_GAIN_T_HIGH_P
|
||
#define GAIN_T_HIGH_I EEPROM_CONFIG_GAIN_T_HIGH_I
|
||
#define GAIN_T_LOW_P EEPROM_CONFIG_GAIN_T_LOW_P
|
||
#define GAIN_T_LOW_I EEPROM_CONFIG_GAIN_T_LOW_I
|
||
|
||
static fxp_t m_duty;
|
||
|
||
void fan_controller_init(void)
|
||
{
|
||
m_duty = 0; // fan off by default
|
||
}
|
||
|
||
|
||
uint8_t fan_controller_update(fxp_t temperature)
|
||
{
|
||
fxp_t duty_out = m_duty;
|
||
|
||
// check emergency case. Directly starts the fan, if necessary.
|
||
if(temperature > EMERGENCY_TEMPERATURE) {
|
||
m_duty = duty_out = DUTY_CYCLE_MAX;
|
||
}
|
||
|
||
if(m_duty == 0) {
|
||
// we are currently in off state. Check whether we need to start.
|
||
if(temperature > FAN_START_TEMPERATURE) {
|
||
m_duty = duty_out = DUTY_CYCLE_START;
|
||
}
|
||
} else {
|
||
// fan is currently on
|
||
|
||
// corridor handling: too warm
|
||
if(temperature > CORRIDOR_MAX_TEMPERATURE) {
|
||
fxp_t delta_above = fxp_sub(temperature, CORRIDOR_MAX_TEMPERATURE);
|
||
|
||
fxp_t duty_p = fxp_mult(delta_above, GAIN_T_HIGH_P);
|
||
fxp_t duty_i = fxp_mult(delta_above, GAIN_T_HIGH_I);
|
||
|
||
m_duty = fxp_add(m_duty, duty_i);
|
||
|
||
if(m_duty > DUTY_CYCLE_MAX) {
|
||
m_duty = DUTY_CYCLE_MAX;
|
||
}
|
||
|
||
duty_out = fxp_add(m_duty, duty_p);
|
||
|
||
if(duty_out > DUTY_CYCLE_MAX) {
|
||
duty_out = DUTY_CYCLE_MAX;
|
||
}
|
||
}
|
||
|
||
// corridor handling: too cold
|
||
if(temperature < CORRIDOR_MIN_TEMPERATURE) {
|
||
fxp_t delta_below = fxp_sub(temperature, CORRIDOR_MIN_TEMPERATURE);
|
||
|
||
fxp_t duty_p = fxp_mult(delta_below, GAIN_T_LOW_P);
|
||
fxp_t duty_i = fxp_mult(delta_below, GAIN_T_LOW_I);
|
||
|
||
m_duty = fxp_add(m_duty, duty_i);
|
||
|
||
if(m_duty < DUTY_CYCLE_MIN) {
|
||
m_duty = DUTY_CYCLE_MIN;
|
||
}
|
||
|
||
duty_out = fxp_add(m_duty, duty_p);
|
||
|
||
if(duty_out < DUTY_CYCLE_MIN) {
|
||
duty_out = DUTY_CYCLE_MIN;
|
||
}
|
||
}
|
||
|
||
// fan stop handling
|
||
if(temperature < FAN_OFF_TEMPERATURE) {
|
||
m_duty = duty_out = 0;
|
||
}
|
||
}
|
||
|
||
return FXP_TO_INT(duty_out);
|
||
}
|