/* control.c */ #include #include "common.h" #include "timer.h" #include "config.h" #include "therm.h" #include "beep.h" static temp_t last_temp; static volatile bool preheat; static bool control_running; static uint32_t control_start_time; static int16_t integral; static int16_t control_output; temp_t temperature_at_time(uint32_t time); int16_t __attribute__ ((noinline)) clip16(int16_t val, int16_t min, int16_t max) { if (val < min) val = min; if (val > max) val = max; return val; } uint32_t control_now(void) { uint32_t this_seconds; ATOMIC_BLOCK(ATOMIC_FORCEON) { this_seconds = seconds; } if (preheat) control_start_time = this_seconds; return this_seconds - control_start_time; } void control_start(void) { preheat = TRUE; last_temp = therm_temp(); integral = 0; control_running = TRUE; control_output = 0; } #define TIME_UNIT 1 #define INTEGRAL_MAX (CONTROL_MAX * 2) #define INTEGRAL_MIN (-(INTEGRAL_MAX)) #define CONTROL_MAX (10 * 128) #define CONTROL_MIN (0) /* P = 1.00 means 1 degree C leads to full-scale output * This is represented by config.p == 128 * The calculations done here are in units of 0.1 degrees */ #define ABS(x) ((x) < 0 ? -(x) : (x)) #define INT_MAX 32767 #define INT_MIN (-32768) #if 0 int16_t multiply_clip(int16_t a, int16_t b) { if ((b == 0) || (ABS(a) > INT_MAX / ABS(b))) return ((a < 0) ^ (b < 0)) ? INT_MIN : INT_MAX; return a * b; } #endif void control_poll(void) { temp_t temp = therm_temp(); temp_t profile_temp; int16_t error; int16_t temp_diff = temp - last_temp; int16_t maxgain; int16_t p, i, d; last_temp = temp; if (!control_running) return; profile_temp = temperature_at_time(control_now()); if (profile_temp == INVALID_TEMPERATURE) { control_running = FALSE; output0 = 0; beep_off(); return; } error = (int16_t)(profile_temp - temp); if (error <= 0) { beep_on(); preheat = FALSE; } else { beep_off(); } maxgain = (error == 0) ? INT_MAX : ((2*CONTROL_MAX) / error); maxgain = ABS(maxgain); p = clip16(config.p, -maxgain, maxgain); i = clip16(config.i, -maxgain, maxgain); d = clip16(config.d, -maxgain, maxgain); integral += i * error * TIME_UNIT; integral = clip16(integral, INTEGRAL_MIN, INTEGRAL_MAX); control_output += p * error + integral + d * temp_diff / TIME_UNIT; control_output = clip16(control_output, CONTROL_MIN, CONTROL_MAX); output0 = (TIMER_MAX/5) * control_output / (CONTROL_MAX/5); }