TinyFanControl-Firmware/src/fan_controller.c

133 lines
4.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 corridors 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 corridors 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);
}