162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * pid.c PID controller for testing cooling devices 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Intel Corporation. All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author Name Jacob Pan <jacob.jun.pan@linux.intel.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <unistd.h> 1162306a36Sopenharmony_ci#include <stdio.h> 1262306a36Sopenharmony_ci#include <stdlib.h> 1362306a36Sopenharmony_ci#include <string.h> 1462306a36Sopenharmony_ci#include <stdint.h> 1562306a36Sopenharmony_ci#include <sys/types.h> 1662306a36Sopenharmony_ci#include <dirent.h> 1762306a36Sopenharmony_ci#include <libintl.h> 1862306a36Sopenharmony_ci#include <ctype.h> 1962306a36Sopenharmony_ci#include <assert.h> 2062306a36Sopenharmony_ci#include <time.h> 2162306a36Sopenharmony_ci#include <limits.h> 2262306a36Sopenharmony_ci#include <math.h> 2362306a36Sopenharmony_ci#include <sys/stat.h> 2462306a36Sopenharmony_ci#include <syslog.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "tmon.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/************************************************************************** 2962306a36Sopenharmony_ci * PID (Proportional-Integral-Derivative) controller is commonly used in 3062306a36Sopenharmony_ci * linear control system, consider the process. 3162306a36Sopenharmony_ci * G(s) = U(s)/E(s) 3262306a36Sopenharmony_ci * kp = proportional gain 3362306a36Sopenharmony_ci * ki = integral gain 3462306a36Sopenharmony_ci * kd = derivative gain 3562306a36Sopenharmony_ci * Ts 3662306a36Sopenharmony_ci * We use type C Alan Bradley equation which takes set point off the 3762306a36Sopenharmony_ci * output dependency in P and D term. 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * y[k] = y[k-1] - kp*(x[k] - x[k-1]) + Ki*Ts*e[k] - Kd*(x[k] 4062306a36Sopenharmony_ci * - 2*x[k-1]+x[k-2])/Ts 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci ***********************************************************************/ 4462306a36Sopenharmony_cistruct pid_params p_param; 4562306a36Sopenharmony_ci/* cached data from previous loop */ 4662306a36Sopenharmony_cistatic double xk_1, xk_2; /* input temperature x[k-#] */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * TODO: make PID parameters tuned automatically, 5062306a36Sopenharmony_ci * 1. use CPU burn to produce open loop unit step response 5162306a36Sopenharmony_ci * 2. calculate PID based on Ziegler-Nichols rule 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * add a flag for tuning PID 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ciint init_thermal_controller(void) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* init pid params */ 5962306a36Sopenharmony_ci p_param.ts = ticktime; 6062306a36Sopenharmony_ci /* TODO: get it from TUI tuning tab */ 6162306a36Sopenharmony_ci p_param.kp = .36; 6262306a36Sopenharmony_ci p_param.ki = 5.0; 6362306a36Sopenharmony_ci p_param.kd = 0.19; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci p_param.t_target = target_temp_user; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_civoid controller_reset(void) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci /* TODO: relax control data when not over thermal limit */ 7362306a36Sopenharmony_ci syslog(LOG_DEBUG, "TC inactive, relax p-state\n"); 7462306a36Sopenharmony_ci p_param.y_k = 0.0; 7562306a36Sopenharmony_ci xk_1 = 0.0; 7662306a36Sopenharmony_ci xk_2 = 0.0; 7762306a36Sopenharmony_ci set_ctrl_state(0); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* To be called at time interval Ts. Type C PID controller. 8162306a36Sopenharmony_ci * y[k] = y[k-1] - kp*(x[k] - x[k-1]) + Ki*Ts*e[k] - Kd*(x[k] 8262306a36Sopenharmony_ci * - 2*x[k-1]+x[k-2])/Ts 8362306a36Sopenharmony_ci * TODO: add low pass filter for D term 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci#define GUARD_BAND (2) 8662306a36Sopenharmony_civoid controller_handler(const double xk, double *yk) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci double ek; 8962306a36Sopenharmony_ci double p_term, i_term, d_term; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci ek = p_param.t_target - xk; /* error */ 9262306a36Sopenharmony_ci if (ek >= 3.0) { 9362306a36Sopenharmony_ci syslog(LOG_DEBUG, "PID: %3.1f Below set point %3.1f, stop\n", 9462306a36Sopenharmony_ci xk, p_param.t_target); 9562306a36Sopenharmony_ci controller_reset(); 9662306a36Sopenharmony_ci *yk = 0.0; 9762306a36Sopenharmony_ci return; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci /* compute intermediate PID terms */ 10062306a36Sopenharmony_ci p_term = -p_param.kp * (xk - xk_1); 10162306a36Sopenharmony_ci i_term = p_param.kp * p_param.ki * p_param.ts * ek; 10262306a36Sopenharmony_ci d_term = -p_param.kp * p_param.kd * (xk - 2 * xk_1 + xk_2) / p_param.ts; 10362306a36Sopenharmony_ci /* compute output */ 10462306a36Sopenharmony_ci *yk += p_term + i_term + d_term; 10562306a36Sopenharmony_ci /* update sample data */ 10662306a36Sopenharmony_ci xk_1 = xk; 10762306a36Sopenharmony_ci xk_2 = xk_1; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* clamp output adjustment range */ 11062306a36Sopenharmony_ci if (*yk < -LIMIT_HIGH) 11162306a36Sopenharmony_ci *yk = -LIMIT_HIGH; 11262306a36Sopenharmony_ci else if (*yk > -LIMIT_LOW) 11362306a36Sopenharmony_ci *yk = -LIMIT_LOW; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci p_param.y_k = *yk; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci set_ctrl_state(lround(fabs(p_param.y_k))); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci} 120