2.算法
pwm:0%(cpld的0即0x0), 50%(cpld的127即0x7f), 100%(cpld的255即0xff)
调节参数达到某一稳定值,用比例积分微分式控制(PID)算法,温度影响计算得出的新的风扇PWM转速 = 当前风扇最大转速 + 比例系数(P) * (所有线卡上芯片当前最大温度 – 芯片上次温度)
+ 积分系数(I)*(所有线卡上芯片当前最大温度 – 设定的目标温度)
+ 微分系数(D)*(所有线卡上芯片当前最大温度 – 芯片上次温度 – (芯片上次温度 – 芯片上上次温度 ))
。内存中需要记录上次和上上次温度值。
进风口sensor芯片即环境温度采用如下图的线性模式调速,如下图T(k)<T(k-1)<T(k-2)时判定为降温过程,使用降温曲线(蓝色)进行调速,其余状态(升温或温度稳定状态)以升温曲线(黑色)进行调速。降温调速曲线与升温曲线间有3℃以上gap,避免环温小幅变化时风扇转速变化。
线性算法曲线可以分为4种,持续/震荡上升,持续/震荡下降。判断震荡曲线时,当采样点温度与上一次温度差小于回滞温度时,此采样点应判定为脏数据丢弃,PWM取上次PWM保持转速稳定。
if (temp > line_node->last_temp && line_node->last_temp > line_node->last_temp_2) //keep raising
is_raising = true;
else if (temp < line_node->last_temp && line_node->last_temp < line_node->last_temp_2) //keep failing
is_raising = false;
else { // 第一次和第二次上升下降相反如上图1和2
if ((fabs(temp - line_node->last_temp) >= line_node->temp_hyst) && (temp > line_node->last_temp)) //snake raising //图1
is_raising = true;
else if ((fabs(temp - line_node->last_temp) >= line_node->temp_hyst) && (temp < line_node->last_temp)) //snake failing //图2
is_raising = false;
else { // 小于回滞温度
//dirty data, we use last temp and last curve to cal pwm
//to aovid fsc donot take affect when inlet remain unchanged
temp = line_node->last_temp;
is_raising = line_node->last_curve;
}
}
3.
if( (g_last_temp_status >= CPU_MIN_ALARM) && (rc == NO_ERROR) ) // 需求:上次温度不正常(没有温度丢失,因为丢失的话,赋为上次温度), 这次温度正常, 30s持续满转(30s内维持上次温度/转速不变rc = g_last_temp_status)后才自适应
// 温度丢失大于15秒直接满转,15秒内维持丢失前转速不变,g_temp_recover_time=15 :上次温度正常,这次温度丢失,15秒内维持丢失前温度不变
{ // 15 20
if ( g_temp_recover_cnt*g_fsc_period < g_temp_recover_time )
{
if( g_temp_recover_cnt < MAX_FAIL_COUNT )
g_temp_recover_cnt++; // 0 ,1 ,2 每次只进1次
rc = g_last_temp_status; // 第0次【上次不正常,这次正常】cnt=1 (rc!=0不正常满转) , 第1次15秒后【上次不正常,这次正常】cnt=2 (rc!=0不正常满转)
}
else
{
g_last_temp_status=rc; // 第2次30秒后【上次不正常,这次正常】cnt=3(rc=NO_ERROR正常自适应)
g_temp_recover_cnt=0;
}
}
else
{
g_last_temp_status=rc; // 第0次 【 (上次正常,这次正常) 或 (上次不正常,这次不正常)】
g_temp_recover_cnt=0;
}
#include <stdio.h>
#include <stdlib.h>
int g_board_cnt = 6;
short switch_fatal_cnt = 0;
static int get_switch_temp_single_board(int board_index)
{
switch_fatal_cnt |= (0x1 << board_index); // |:有1为1 :将switch_fatal_cnt的第board_index位设为1
printf("111,0x%x,%d\n", switch_fatal_cnt, switch_fatal_cnt);
// switch_fatal_cnt = ~(0x1 << board_index); // 得到一个只有第board_index位为0的二进制数
// switch_fatal_cnt &= ~(0x1 << board_index); // &:有0为0 :将switch_fatal_cnt的第board_index位设为0
// printf("222,0x%x,%d\n", switch_fatal_cnt, switch_fatal_cnt);
}
int main()
{
int i, rc , cnt = 0;
for (i = 0; i < g_board_cnt; i++) {
rc=get_switch_temp_single_board(i);
cnt=(switch_fatal_cnt & (0x1<<i)) >> i; //保留 switch_fatal_cnt 中第i位的值,其余位为0, 将保留的值移回到最低位
printf("333,%d\n", cnt);
}
}
$ gcc a.c -o a
$ ./a
111,0x1,1
333,1
111,0x3,3
333,1
111,0x7,7
333,1
111,0xf,15
333,1
111,0x1f,31
333,1
111,0x3f,63
333,1
2.fcbcpld.c:fan10是fan1的rear rotor speed 后转子速度, 前转子front
如下第一列也就是ratio比例(reg:0-ff),在get_speed_tolerance函数中用到。
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <i2c_dev_sysfs.h>
#define REG_PWM_INLET1 0x32 // ...
#define REG_PWM_INLET5 0x72
#define REG_PWM_OUTLET1 0x37 // ...
#define REG_PWM_OUTLET5 0x77
#define SPEED_TARGET_ARRAY_SIZE 11
#define FAN_LED_STATUS_DARK 0x41
#define FAN_LED_STATUS_GREEN 0x44
#define FAN_LED_STATUS_YELLOW 0x54
#define FAN_LED_STATUS_GREEN_LIGHT_FLASHING 0x46
#define FAN_LED_STATUS_YELLOW_LIGHT_FLASHING 0x56
typedef enum{ // 一串数字
DARK, // 0
GREEN,
YELLOW,
RED,
BLUE,
GREEN_LIGHT_FLASHING,
YELLOW_LIGHT_FLASHING,
RED_LIGHT_FLASHING,
BLUE_LIGHT_FLASHING
}fan_led_status_t;
typedef enum{
FAN1_MIN, // 0 , 模拟寄存器dev_attr->ida_reg,当标志位用
FAN1_MAX,
FAN1_TOLERANCE, //误差值
FAN1_TARGET, //实际值
FAN10_MIN,
FAN10_MAX,
FAN10_TOLERANCE,
FAN10_TARGET,
...
} threshold_reg_t;
typedef struct{ // 定义风扇 前后转子转速结构体
int alarm_min;
int alarm_max;
int tolerance;
int target;
}threshold_data_t;
typedef struct{ // 分类存储传进的值,在赋给新变量传出去
threshold_data_t fan1;
threshold_data_t fan10;
...
threshold_data_t fan5;
threshold_data_t fan50;
int motor_num;
int fan_num;
}fan_threshold_data_t;
fan_threshold_data_t fan_threshold_data;
//begain
typedef enum
{
RATIO, /* {Duty Cycle(%) */ // 0
INLET, /* IN FAN Typ. */
OUTLET /* OUT FAN Typ. */
} threshold_rotor_t;
const int speed_target[SPEED_TARGET_ARRAY_SIZE][3] = { //11行3列
/* {Duty Cycle(%), IN FAN Typ., OUT FAN Typ.} */
{0, 11818, 11247},
{10, 2329, 2148},
{20, 4889, 4496},
{30, 7127, 6886},
{40, 9420, 9106},
{50, 11802, 11239},
{60, 14257, 13556},
{70, 16740, 16045},
{80, 19191, 17742},
{90, 21535, 19279},
{100, 22628, 19293}};
int get_speed_target(int ratio, threshold_reg_t reg)
{
int i = 0;
threshold_rotor_t rotor_index = 0;
int value = 0;
switch (reg) {
case FAN1_TARGET: //case里没有break
...
case FAN5_TARGET:
case FAN1_TOLERANCE:
...
case FAN5_TOLERANCE:
rotor_index = INLET; //如果上面匹配到某一个,这里一定会走到,值为1
break;
case FAN10_TARGET:
...
case FAN50_TARGET:
case FAN10_TOLERANCE:
...
case FAN50_TOLERANCE:
rotor_index = OUTLET; //值为2
break;
default:
break;
}
for (i = 0; i < SPEED_TARGET_ARRAY_SIZE - 1; i++) { // i:0-9,行,一直循环直到匹配到下一行if
if (ratio <= speed_target[i + 1][RATIO]) { // 循环第一次即第1行(不是第0行)第0列即10 , 判断ratio在哪个区间如ratio=55在50-60间即确定i和i+1
if (speed_target[i + 1][rotor_index] != speed_target[i][rotor_index]) {
value = ((speed_target[i + 1][rotor_index] - speed_target[i][rotor_index]) // =(14257-11802)/(60-50)*(60-55)+11802 或 =(14257-11802)/(60-50)*(55-60)+14257
* (ratio - speed_target[i + 1][RATIO]))
/ (speed_target[i + 1][RATIO] - speed_target[i][RATIO])
+ speed_target[i + 1][rotor_index];
} else {
value = speed_target[i][rotor_index];
}
break; //跳出for,不是if,value只取一个值,所以这里break出for循环
}
}
return value; // value是根据传入的ratio在speed_target二维数组中计算出INLET或OUTLET值
}
int get_speed_tolerance(int ratio, threshold_reg_t reg)
{
int value = 0;
if ((ratio <= 5) || (ratio > 10))
{
value = get_speed_target(ratio, reg) / 10; // 相当于*10%
} else {
value = 500;
}
return value; // 转速误差值,不是实际RPM转速
}
/end
static ssize_t chip_target_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
i2c_dev_data_st *data = i2c_get_clientdata(client);
i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr; // 传进来dev_attr->ida_reg为FAN1_TARGET
int value = 0;
int ratio = 0;
unsigned char reg = 0;
switch(dev_attr->ida_reg)
{
case FAN1_TARGET:
reg = REG_PWM_INLET1;
break;
...
case FAN5_TARGET:
reg = REG_PWM_INLET5;
break;
case FAN10_TARGET:
reg = REG_PWM_OUTLET1;
break;
...
case FAN50_TARGET:
reg = REG_PWM_OUTLET5;
break;
default:
break;
}
ratio = i2c_smbus_read_byte_data(client, reg); // 0-ff , 0-255
ratio *= 100;
if (ratio % 255 != 0)
ratio = ratio / 255 + 1;
else
ratio = ratio / 255;
value = get_speed_target(ratio, dev_attr->ida_reg); // ratio(0%-100%) , reg区分inlet还是outlet
return scnprintf(buf, PAGE_SIZE, "%d\n", value);
}
static ssize_t chip_tolerance_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
i2c_dev_data_st *data = i2c_get_clientdata(client);
i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
int value = 0;
int ratio = 0;
unsigned char reg = 0;
switch(dev_attr->ida_reg)
{
case FAN1_TOLERANCE:
reg = REG_PWM_INLET1;
break;
...
case FAN5_TOLERANCE:
reg = REG_PWM_INLET5;
break;
case FAN10_TOLERANCE:
reg = REG_PWM_OUTLET1;
break;
...
case FAN50_TOLERANCE:
reg = REG_PWM_OUTLET5;
break;
default:
break;
}
ratio = i2c_smbus_read_byte_data(client, reg);
ratio *= 100;
if (ratio % 255 != 0)
ratio = ratio / 255 + 1;
else
ratio = ratio / 255;
value = get_speed_tolerance(ratio, dev_attr->ida_reg);
return scnprintf(buf, PAGE_SIZE, "%d\n", value);
}
static int chip_thrshold_write(threshold_reg_t reg,long value )
{
int result = -1;
switch(reg)
{
case FAN1_MIN:
fan_threshold_data.fan1.alarm_min = value;
break;
case FAN1_MAX:
fan_threshold_data.fan1.alarm_max = value;
break;
case FAN10_MIN:
fan_threshold_data.fan10.alarm_min = value;
break;
case FAN10_MAX:
fan_threshold_data.fan10.alarm_max = value;
break;
...
case FAN5_MIN:
fan_threshold_data.fan5.alarm_min = value;
break;
case FAN5_MAX:
fan_threshold_data.fan5.alarm_max = value;
break;
case FAN50_MIN:
fan_threshold_data.fan50.alarm_min = value;
break;
case FAN50_MAX:
fan_threshold_data.fan50.alarm_max = value;
break;
default:
break;
}
return 0;
}
static int chip_thrshold_read(threshold_reg_t reg )
{
int result = -1;
switch(reg)
{
case FAN1_MIN:
result = fan_threshold_data.fan1.alarm_min;
break;
case FAN1_MAX:
result = fan_threshold_data.fan1.alarm_max;
break;
case FAN10_MIN:
result = fan_threshold_data.fan10.alarm_min;
break;
case FAN10_MAX:
result = fan_threshold_data.fan10.alarm_max;
break;
...
case FAN5_MIN:
result = fan_threshold_data.fan5.alarm_min;
break;
case FAN5_MAX:
result = fan_threshold_data.fan5.alarm_max;
break;
case FAN50_MIN:
result = fan_threshold_data.fan50.alarm_min;
break;
case FAN50_MAX:
result = fan_threshold_data.fan50.alarm_max;
break;
default:
break;
}
return result;
}
static ssize_t chip_threshold_show(struct device *dev,struct device_attribute *attr,char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
i2c_dev_data_st *data = i2c_get_clientdata(client);
i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
int result = -1;
result = chip_thrshold_read(dev_attr->ida_reg);
return scnprintf(buf, PAGE_SIZE, "%d\n", result);
}
static ssize_t chip_threshold_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
i2c_dev_data_st *data = i2c_get_clientdata(client);
i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
int rc = 0;
long write_value=0;
if (buf == NULL) {
return -ENXIO;
}
rc = kstrtol(buf, 0, &write_value);
if (rc != 0) {
return count;
}
chip_thrshold_write(dev_attr->ida_reg,write_value);
return count;
}
static ssize_t fan_led_status_show(struct device *dev,struct device_attribute *attr,char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
i2c_dev_data_st *data = i2c_get_clientdata(client);
i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
fan_led_status_t fan_led_status;
int value = -1;
value = i2c_smbus_read_byte_data(client,(dev_attr->ida_reg));
switch (value)
{
case FAN_LED_STATUS_DARK:
fan_led_status = DARK;
break;
case FAN_LED_STATUS_GREEN:
fan_led_status = GREEN;
break;
case FAN_LED_STATUS_YELLOW:
fan_led_status = YELLOW;
break;
case FAN_LED_STATUS_GREEN_LIGHT_FLASHING:
fan_led_status = GREEN_LIGHT_FLASHING;
break;
case FAN_LED_STATUS_YELLOW_LIGHT_FLASHING:
fan_led_status = YELLOW_LIGHT_FLASHING;
break;
default:
return scnprintf(buf, PAGE_SIZE, "NA\n");
break;
}
return scnprintf(buf, PAGE_SIZE, "%d\n", fan_led_status);
}
static ssize_t fan_led_status_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
i2c_dev_data_st *data = i2c_get_clientdata(client);
i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
int rc = 0, write_value = 0;
if (buf == NULL) {
return -ENXIO;
}
rc = kstrtoint(buf, 10, &write_value);
if (rc != 0) {
return count;
}
switch(write_value)
{
case DARK:
rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg), FAN_LED_STATUS_DARK);
if (rc != 0) {
return count;
}
break;
case GREEN:
rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg), FAN_LED_STATUS_GREEN);
if (rc != 0) {
return count;
}
break;
case YELLOW:
rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg), FAN_LED_STATUS_YELLOW);
if (rc != 0) {
return count;
}
break;
case GREEN_LIGHT_FLASHING:
rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg), FAN_LED_STATUS_GREEN_LIGHT_FLASHING);
if (rc != 0) {
return count;
}
break;
case YELLOW_LIGHT_FLASHING:
rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg), FAN_LED_STATUS_YELLOW_LIGHT_FLASHING);
if (rc != 0) {
return count;
}
break;
default:
break;
}
return count;
}
static ssize_t fan_pwm_ctrl_show(struct device *dev,struct device_attribute *attr,char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
i2c_dev_data_st *data = i2c_get_clientdata(client);
i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
int value = -1;
value = i2c_smbus_read_byte_data(client,(dev_attr->ida_reg)); //0x7F //127
value *= 100;
if ( value%255 != 0 )
value = value/255+1;
else
value = value/255;
return scnprintf(buf, PAGE_SIZE, "%d\n", value);
}
static ssize_t fan_pwm_ctrl_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
i2c_dev_data_st *data = i2c_get_clientdata(client);
i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
int rc = 0, write_value = 0, right_value = 0;
if (buf == NULL) {
return -ENXIO;
}
rc = kstrtoint(buf, 10, &write_value);
if (rc != 0) {
return count;
}
if (((write_value-1)*255) %255 !=0 )
right_value = (write_value-1)*255/100;
else
right_value = write_value*255/100;
rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg), right_value);
if (rc != 0) {
return count;
}
return count;
}
static ssize_t fan_watchdog_count_show(struct device *dev,struct device_attribute *attr,char *buf)
{
unsigned int val = i2c_dev_read_word_littleendian(dev,attr);
if(val <= 0)
{
DEV_DEBUG("Read Fan_Watchdog error!\n");
}
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
}
static ssize_t fan_watchdog_count_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
i2c_dev_data_st *data = i2c_get_clientdata(client);
i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
int rc = 0, write_value = 0;
if (buf == NULL) {
return -ENXIO;
}
rc = kstrtoint(buf, 10, &write_value);
if (rc != 0) {
return count;
}
rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg), write_value&0xff);
if (rc != 0) {
return count;
}
rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg+1), (write_value>>8)&0xff);
if (rc != 0) {
return count;
}
return count;
}
static ssize_t fcbcpld_fan_rpm_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int val;
val = i2c_dev_read_byte(dev, attr);
if (val < 0) {
return val;
}
/* Multiply by 150 to get the RPM */
val *= 150;
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
}
//config fan rear index as real index*10
//only for fan speed in order to disapply in sensors command
static const i2c_dev_attr_st fcbcpld_attr_table[] = {
{
"fan1_input",
"fan 1 front speed",
fcbcpld_fan_rpm_show,
I2C_DEV_ATTR_STORE_DEFAULT,
0x30, 0, 8,
},
{
"fan10_input",
"fan 1 rear speed",
fcbcpld_fan_rpm_show,
I2C_DEV_ATTR_STORE_DEFAULT,
0x31, 0, 8,
},
{
"fan1_pwm_ctrl",
"0xff means max",
fan_pwm_ctrl_show,
fan_pwm_ctrl_store,
0x32, 0, 8,
},
{
"fan10_pwm_ctrl",
"0xff means max",
fan_pwm_ctrl_show,
fan_pwm_ctrl_store,
0x37, 0, 8,
},
{
"fan1_led_status",
NULL,
fan_led_status_show,
fan_led_status_store,
0x34, 0, 8,
},
{
"fan1_min",
NULL,
chip_threshold_show,
chip_threshold_store,
FAN1_MIN, 0, 8,
},
{
"fan1_max",
NULL,
chip_threshold_show,
chip_threshold_store,
FAN1_MAX, 0, 8,
},
{
"fan1_tolerance",
NULL,
chip_tolerance_show,
NULL,
FAN1_TOLERANCE, 0, 8,
},
{
"fan1_target",
NULL,
chip_target_show,
NULL,
FAN1_TARGET, 0, 8,
},
{
"fan10_min",
NULL,
chip_threshold_show,
chip_threshold_store,
FAN10_MIN, 0, 8,
},
};
static i2c_dev_data_st fcbcpld_data;
/* FCBCPLD id */
static const struct i2c_device_id fcbcpld_id[] = {
{ "fcbcpld", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, fcbcpld_id);
/* Return 0 if detection is successful, -ENODEV otherwise */
static int fcbcpld_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
/*
* We don't currently do any detection of the FCBCPLD
*/
strlcpy(info->type, "fcbcpld", I2C_NAME_SIZE);
return 0;
}
static int fcbcpld_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int n_attrs = sizeof(fcbcpld_attr_table) / sizeof(fcbcpld_attr_table[0]);
return i2c_dev_sysfs_data_init(client, &fcbcpld_data,
fcbcpld_attr_table, n_attrs);
}
static int fcbcpld_remove(struct i2c_client *client)
{
i2c_dev_sysfs_data_clean(client, &fcbcpld_data);
return 0;
}
static struct i2c_driver fcbcpld_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "fcbcpld",
},
.probe = fcbcpld_probe,
.remove = fcbcpld_remove,
.id_table = fcbcpld_id,
.detect = fcbcpld_detect,
};
static int __init fcbcpld_mod_init(void)
{
return i2c_add_driver(&fcbcpld_driver);
}
static void __exit fcbcpld_mod_exit(void)
{
i2c_del_driver(&fcbcpld_driver);
}
MODULE_AUTHOR("Siyu Li");
MODULE_DESCRIPTION("fcbcpld Driver");
MODULE_LICENSE("GPL");
module_init(fcbcpld_mod_init);
module_exit(fcbcpld_mod_exit);
1.
散热会提供一个fan table(openloop,不是pid有反馈的closeloop,),里面会详细描述风扇要怎么转
BMC会透过PWM讯号去设定风扇转速,风扇也能透过TACH讯号来回传目前的转速
PWM是透过平均电压来传递类比讯号,简单来说就是在一个cycle中,高电为占百分之多少,就表示他要传递的值是多少
例如在一个周期中,如果高电位占25%,低电位占75%,这样表示我们要传递的值是25%,风扇就会转25%
那如果今天我们都一直是high (高电位),这样风扇就会全转,因此在线路图的review过程中,都会注意PWM有没有pull high,避免在BMC更新过程或是死掉后,机器过热
那每个周期的时间是多少呢? PWM的传递频率会定义在风扇的spec中,每一颗风扇的接收频率有可能会不一样
TACH (Tachometer)
我们可以透过Tach来传递风扇的转速,有修过机械方面的课程就会知道,马达会有n个机械原点,传一圈的话会产生n个pulse,一圈会产生几个pulse也是定义在风扇的spec中
假如今天风扇转一圈会产生两个pulse,我们在一秒内收到1000个pulse,这样表示风扇一秒转了500圈,风扇转速是用rpm(一分钟转几圈)表示的,因此500*60 rpm就是我们要求的值
在传统控制(Classical Control )理论中,通常会用有没有feedback 来区分open loop control 和 closed loop control,前者通常就是我们常说的stepwise,后者就是工业控制中广泛使用的PID control
1.
该文档讲述OCP BMC中如何使用phosphor-pid-control模块实现风扇自动控速。该模块可以通过IPMI指令开启和关闭自动控速模式,也可以通过kill 进程来关闭自动控速逻辑,手动控速可以通过fan-speed-set.sh脚本控制,当进程退出或则BMC reboot 系统会自动调用fan-full-speed.sh设fan到指定转速。
# json源码
{
"version": "R02",
"sensors": [
{
"name": "fan0_f_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan0_f_speed",
"writePath": "/xyz/openbmc_project/control/fanpwm/fan0_pwm",
"min": 0,
"max": 255
},
{
"name": "fan0_r_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan0_r_speed",
"min": 0,
"max": 255
},
{
"name": "fan1_f_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan1_f_speed",
"writePath": "/xyz/openbmc_project/control/fanpwm/fan1_pwm",
"min": 0,
"max": 255
},
{
"name": "fan1_r_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan1_r_speed",
"min": 0,
"max": 255
},
{
"name": "fan2_f_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan2_f_speed",
"writePath": "/xyz/openbmc_project/control/fanpwm/fan2_pwm",
"min": 0,
"max": 255
},
{
"name": "fan2_r_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan2_r_speed",
"min": 0,
"max": 255
},
{
"name": "fan3_f_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan3_f_speed",
"writePath": "/xyz/openbmc_project/control/fanpwm/fan3_pwm",
"min": 0,
"max": 255
},
{
"name": "fan3_r_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan3_r_speed",
"min": 0,
"max": 255
},
{
"name": "fan4_f_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan4_f_speed",
"writePath": "/xyz/openbmc_project/control/fanpwm/fan4_pwm",
"min": 0,
"max": 255
},
{
"name": "fan4_r_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan4_r_speed",
"min": 0,
"max": 255
},
{
"name": "fan5_f_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan5_f_speed",
"writePath": "/xyz/openbmc_project/control/fanpwm/fan5_pwm",
"min": 0,
"max": 255
},
{
"name": "fan5_r_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan5_r_speed",
"min": 0,
"max": 255
},
{
"name": "fan6_f_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan6_f_speed",
"writePath": "/xyz/openbmc_project/control/fanpwm/fan6_pwm",
"min": 0,
"max": 255
},
{
"name": "fan6_r_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan6_r_speed",
"min": 0,
"max": 255
},
{
"name": "fan7_f_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan7_f_speed",
"writePath": "/xyz/openbmc_project/control/fanpwm/fan7_pwm",
"min": 0,
"max": 255
},
{
"name": "fan7_r_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan7_r_speed",
"min": 0,
"max": 255
},
{
"name": "fan8_f_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan8_f_speed",
"writePath": "/xyz/openbmc_project/control/fanpwm/fan8_pwm",
"min": 0,
"max": 255
},
{
"name": "fan8_r_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan8_r_speed",
"min": 0,
"max": 255
},
{
"name": "fan9_f_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan9_f_speed",
"writePath": "/xyz/openbmc_project/control/fanpwm/fan9_pwm",
"min": 0,
"max": 255
},
{
"name": "fan9_r_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan9_r_speed",
"min": 0,
"max": 255
},
{
"name": "fan10_f_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan10_f_speed",
"writePath": "/xyz/openbmc_project/control/fanpwm/fan10_pwm",
"min": 0,
"max": 255
},
{
"name": "fan10_r_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan10_r_speed",
"min": 0,
"max": 255
},
{
"name": "fan11_f_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan11_f_speed",
"writePath": "/xyz/openbmc_project/control/fanpwm/fan11_pwm",
"min": 0,
"max": 255
},
{
"name": "fan11_r_speed",
"type": "fan",
"readPath": "/xyz/openbmc_project/sensors/fan_tach/fan11_r_speed",
"min": 0,
"max": 255
},
{
"name": "Inlet_temp",
"type": "temp",
"readPath": "/xyz/openbmc_project/sensors/temperature/Inlet_temp",
"writePath": "",
"min": 0,
"max": 0,
"ignoreDbusMinMax": true,
"timeout": 0
},
{
"name": "MB_PCH_TEMP",
"type": "temp",
"readPath": "/xyz/openbmc_project/sensors/temperature/MB_PCH_TEMP",
"writePath": "",
"min": 0,
"max": 0,
"ignoreDbusMinMax": true,
"timeout": 0
},
{
"name": "DTS_CPU1",
"type": "temp",
"readPath": "/xyz/openbmc_project/sensors/temperature/DTS_CPU1",
"writePath": "",
"min": 0,
"max": 0,
"ignoreDbusMinMax": true,
"timeout": 0
},
{
"name": "DTS_CPU2",
"type": "temp",
"readPath": "/xyz/openbmc_project/sensors/temperature/DTS_CPU2",
"writePath": "",
"min": 0,
"max": 0,
"ignoreDbusMinMax": true,
"timeout": 0
}
],
"zones": [
{
"id": 1,
"minThermalOutput": 0.0,
"failsafePercent": 75.0,
"pids": [
{
"name": "fan_tachs",
"type": "fan",
"inputs": [
"fan0_f_speed",
"fan0_r_speed",
"fan1_f_speed",
"fan1_r_speed",
"fan2_f_speed",
"fan2_r_speed",
"fan3_f_speed",
"fan3_r_speed",
"fan4_f_speed",
"fan4_r_speed",
"fan5_f_speed",
"fan5_r_speed",
"fan6_f_speed",
"fan6_r_speed",
"fan7_f_speed",
"fan7_r_speed",
"fan8_f_speed",
"fan8_r_speed",
"fan9_f_speed",
"fan9_r_speed",
"fan10_f_speed",
"fan10_r_speed",
"fan11_f_speed",
"fan11_r_speed"
],
"setpoint": 0.0,
"pid": {
"samplePeriod": 1.0,
"proportionalCoeff": 0.0,
"integralCoeff": 0.0,
"feedFwdOffsetCoeff": 0.0,
"feedFwdGainCoeff": 0.0044,
"integralLimit_min": 0.0,
"integralLimit_max": 0.0,
"outLim_min": 0.0,
"outLim_max": 100.0,
"slewNeg": 0.0,
"slewPos": 0.0
}
},
{
"name": "Inlet",
"type": "stepwise",
"inputs": [
"Inlet_temp"
],
"setpoint": 10.0,
"pid": {
"samplePeriod": 1.0,
"positiveHysteresis": 1.0,
"negativeHysteresis": 3.0,
"isCeiling": false,
"reading": {
"0": 10.0,
"1": 15.0,
"2": 17.0,
"3": 18.0,
"4": 20.0,
"5": 21.0,
"6": 23.0,
"7": 25.0,
"8": 26.0,
"9": 28.0,
"10": 30.0,
"11": 31.0,
"12": 33.0,
"13": 35.0,
"14": 37.0,
"15": 40.0
},
"output": {
"0": 4560.0,
"1": 4560.0,
"2": 4560.0,
"3": 5700.0,
"4": 5700.0,
"5": 5700.0,
"6": 6840.0,
"7": 6840.0,
"8": 6840.0,
"9": 7980.0,
"10": 7980.0,
"11": 7980.0,
"12": 9120.0,
"13": 9120.0,
"14": 9120.0,
"15": 10260.0
}
}
},
{
"name": "MB_PCH_TEMP",
"type": "temp",
"inputs": [
"MB_PCH_TEMP"
],
"setpoint": 70.0,
"pid": {
"samplePeriod": 1.0,
"proportionalCoeff": -714.0,
"integralCoeff": -9.0,
"feedFwdOffsetCoeff": 0.0,
"feedFwdGainCoeff": 0.0,
"integralLimit_min": 0.0,
"integralLimit_max": 0.0,
"outLim_min": 4560.0,
"outLim_max": 22800.0,
"slewNeg": 0.0,
"slewPos": 0.0,
"positiveHysteresis": 0.0,
"negativeHysteresis": 0.0
}
},
{
"name": "DTS_CPU1",
"type": "temp",
"inputs": [
"DTS_CPU1"
],
"setpoint": 70.0,
"pid": {
"samplePeriod": 1.0,
"proportionalCoeff": -714.0,
"integralCoeff": -9.0,
"feedFwdOffsetCoeff": 0.0,
"feedFwdGainCoeff": 0.0,
"integralLimit_min": 0.0,
"integralLimit_max": 0.0,
"outLim_min": 4560.0,
"outLim_max": 22800.0,
"slewNeg": 0.0,
"slewPos": 0.0,
"positiveHysteresis": 0.0,
"negativeHysteresis": 0.0
}
},
{
"name": "DTS_CPU2",
"type": "temp",
"inputs": [
"DTS_CPU2"
],
"setpoint": 70.0,
"pid": {
"samplePeriod": 1.0,
"proportionalCoeff": -714.0,
"integralCoeff": -9.0,
"feedFwdOffsetCoeff": 0.0,
"feedFwdGainCoeff": 0.0,
"integralLimit_min": 0.0,
"integralLimit_max": 0.0,
"outLim_min": 4560.0,
"outLim_max": 22800.0,
"slewNeg": 0.0,
"slewPos": 0.0,
"positiveHysteresis": 0.0,
"negativeHysteresis": 0.0
}
}
]
}
]
}