18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * pid.c PID controller for testing cooling devices 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Intel Corporation. All rights reserved. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author Name Jacob Pan <jacob.jun.pan@linux.intel.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <unistd.h> 118c2ecf20Sopenharmony_ci#include <stdio.h> 128c2ecf20Sopenharmony_ci#include <stdlib.h> 138c2ecf20Sopenharmony_ci#include <string.h> 148c2ecf20Sopenharmony_ci#include <stdint.h> 158c2ecf20Sopenharmony_ci#include <sys/types.h> 168c2ecf20Sopenharmony_ci#include <dirent.h> 178c2ecf20Sopenharmony_ci#include <libintl.h> 188c2ecf20Sopenharmony_ci#include <ctype.h> 198c2ecf20Sopenharmony_ci#include <assert.h> 208c2ecf20Sopenharmony_ci#include <time.h> 218c2ecf20Sopenharmony_ci#include <limits.h> 228c2ecf20Sopenharmony_ci#include <math.h> 238c2ecf20Sopenharmony_ci#include <sys/stat.h> 248c2ecf20Sopenharmony_ci#include <syslog.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "tmon.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/************************************************************************** 298c2ecf20Sopenharmony_ci * PID (Proportional-Integral-Derivative) controller is commonly used in 308c2ecf20Sopenharmony_ci * linear control system, consider the the process. 318c2ecf20Sopenharmony_ci * G(s) = U(s)/E(s) 328c2ecf20Sopenharmony_ci * kp = proportional gain 338c2ecf20Sopenharmony_ci * ki = integral gain 348c2ecf20Sopenharmony_ci * kd = derivative gain 358c2ecf20Sopenharmony_ci * Ts 368c2ecf20Sopenharmony_ci * We use type C Alan Bradley equation which takes set point off the 378c2ecf20Sopenharmony_ci * output dependency in P and D term. 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * y[k] = y[k-1] - kp*(x[k] - x[k-1]) + Ki*Ts*e[k] - Kd*(x[k] 408c2ecf20Sopenharmony_ci * - 2*x[k-1]+x[k-2])/Ts 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci ***********************************************************************/ 448c2ecf20Sopenharmony_cistruct pid_params p_param; 458c2ecf20Sopenharmony_ci/* cached data from previous loop */ 468c2ecf20Sopenharmony_cistatic double xk_1, xk_2; /* input temperature x[k-#] */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * TODO: make PID parameters tuned automatically, 508c2ecf20Sopenharmony_ci * 1. use CPU burn to produce open loop unit step response 518c2ecf20Sopenharmony_ci * 2. calculate PID based on Ziegler-Nichols rule 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * add a flag for tuning PID 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ciint init_thermal_controller(void) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci int ret = 0; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* init pid params */ 608c2ecf20Sopenharmony_ci p_param.ts = ticktime; 618c2ecf20Sopenharmony_ci /* TODO: get it from TUI tuning tab */ 628c2ecf20Sopenharmony_ci p_param.kp = .36; 638c2ecf20Sopenharmony_ci p_param.ki = 5.0; 648c2ecf20Sopenharmony_ci p_param.kd = 0.19; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci p_param.t_target = target_temp_user; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return ret; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_civoid controller_reset(void) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci /* TODO: relax control data when not over thermal limit */ 748c2ecf20Sopenharmony_ci syslog(LOG_DEBUG, "TC inactive, relax p-state\n"); 758c2ecf20Sopenharmony_ci p_param.y_k = 0.0; 768c2ecf20Sopenharmony_ci xk_1 = 0.0; 778c2ecf20Sopenharmony_ci xk_2 = 0.0; 788c2ecf20Sopenharmony_ci set_ctrl_state(0); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* To be called at time interval Ts. Type C PID controller. 828c2ecf20Sopenharmony_ci * y[k] = y[k-1] - kp*(x[k] - x[k-1]) + Ki*Ts*e[k] - Kd*(x[k] 838c2ecf20Sopenharmony_ci * - 2*x[k-1]+x[k-2])/Ts 848c2ecf20Sopenharmony_ci * TODO: add low pass filter for D term 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci#define GUARD_BAND (2) 878c2ecf20Sopenharmony_civoid controller_handler(const double xk, double *yk) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci double ek; 908c2ecf20Sopenharmony_ci double p_term, i_term, d_term; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci ek = p_param.t_target - xk; /* error */ 938c2ecf20Sopenharmony_ci if (ek >= 3.0) { 948c2ecf20Sopenharmony_ci syslog(LOG_DEBUG, "PID: %3.1f Below set point %3.1f, stop\n", 958c2ecf20Sopenharmony_ci xk, p_param.t_target); 968c2ecf20Sopenharmony_ci controller_reset(); 978c2ecf20Sopenharmony_ci *yk = 0.0; 988c2ecf20Sopenharmony_ci return; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci /* compute intermediate PID terms */ 1018c2ecf20Sopenharmony_ci p_term = -p_param.kp * (xk - xk_1); 1028c2ecf20Sopenharmony_ci i_term = p_param.kp * p_param.ki * p_param.ts * ek; 1038c2ecf20Sopenharmony_ci d_term = -p_param.kp * p_param.kd * (xk - 2 * xk_1 + xk_2) / p_param.ts; 1048c2ecf20Sopenharmony_ci /* compute output */ 1058c2ecf20Sopenharmony_ci *yk += p_term + i_term + d_term; 1068c2ecf20Sopenharmony_ci /* update sample data */ 1078c2ecf20Sopenharmony_ci xk_1 = xk; 1088c2ecf20Sopenharmony_ci xk_2 = xk_1; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* clamp output adjustment range */ 1118c2ecf20Sopenharmony_ci if (*yk < -LIMIT_HIGH) 1128c2ecf20Sopenharmony_ci *yk = -LIMIT_HIGH; 1138c2ecf20Sopenharmony_ci else if (*yk > -LIMIT_LOW) 1148c2ecf20Sopenharmony_ci *yk = -LIMIT_LOW; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci p_param.y_k = *yk; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci set_ctrl_state(lround(fabs(p_param.y_k))); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci} 121