acdt/users/Src/provalctrl.c

976 lines
27 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 "provalctrl.h"
//定义比例阀相结构体
propotion_valve pv_one;
propotion_valve pv_two;
//PID参数缓存用于EEPROM读写
// float data_wr[3] = {0}; //[0,1,2] -> “Kp, Ti, Td”
// float data_rd[3] = {0};
//比例阀参数初始化
void prov_init(void) //不同比例阀的初始参数可能会不一样,因此未使用结构体指针传参
{
//ee_readfloats(PRO1_ADDR,data_rd,3); //读取存储在EEPROM里的PID参数
pv_one.status = PROV_HOLDING;
pv_one.tag = 1; //标签比例阀1
for(uint8_t i = 0;i < SLDW_PRES_OUT;i++)//前n个时刻的输出气压
{
pv_one.previous_pressure[i] = 0;
}
pv_one.current_pressure = 0; //当前气压Kpa
pv_one.current_percent = 0; //当前气压百分比( 0~900Kpa -> 0~100%
pv_one.target_pressure = 0; //目标气压Kpa
pv_one.target_percent = 0; //目标气压百分比( 0~900Kpa -> 0~100%
pv_one.target_current = 0; //目标值的理论电流
pv_one.current_input = 0; //当前输入电流
pv_one.input_min = 3; //输入电流下限
pv_one.input_max = 20; //输入电流上限
pv_one.ctrl_min = 4; //理论控制范围下限
pv_one.ctrl_max = 20; //理论控制范围上限
pv_one.bias = 0; //偏差 = 目标气压百分比 - 当前气压百分比
pv_one.bias_previous = 0; //前一个时刻的偏差
pv_one.bias_area = 0.5; //允许的误差范围±a(%)
// pv_one.Kp = (isnan(data_rd[0]))?(0):(data_rd[0]); //比例系数NAN时视作0
// pv_one.Ti = (data_rd[1] == 0)?(1000000):(data_rd[1]); //积分时间,防止分母为零
// pv_one.Ing = 0; //积分输出
// pv_one.Ing_max = 5000; //积分上限
// pv_one.Ing_min = 0; //积分下限
// pv_one.Ing_sum = 0; //偏差求和
// pv_one.Td = data_rd[2]; //微分时间
// pv_one.Ts = 0.05; //采样周期50ms
// pv_one.Div = 0; //微分输出
// pv_one.pidout = 0; //PID输出
// pv_one.pidout_max = 0; //PID输出上限
// pv_one.pidout_min = 0; //PID输出下限
// pv_one.slow_down_flg = 0; //减速标志
pv_one.pvout = ao_blf1_set; //DAC输出函数
pv_one.pvout(0); //输出0mA
/************************************************/
//ee_readfloats(PRO2_ADDR,data_rd,3);
pv_two.status = PROV_HOLDING;
pv_two.tag = 2; //标签比例阀2
for(uint8_t i = 0;i < SLDW_PRES_OUT;i++)//前n个时刻的输出气压
{
pv_two.previous_pressure[i] = 0;
}
pv_two.current_pressure = 0;
pv_two.current_percent = 0;
pv_two.target_pressure = 0;
pv_two.target_percent = 0;
pv_two.target_current = 0; //目标值的理论电流
pv_two.current_input = 0;
pv_two.input_min = 3;
pv_two.input_max = 20;
pv_two.ctrl_min = 4; //理论控制范围下限
pv_two.ctrl_max = 20; //理论控制范围上限
pv_two.bias = 0;
pv_two.bias_previous = 0; //前一个时刻的偏差
pv_two.bias_area = 0.5;
// pv_two.Kp = (isnan(data_rd[0]))?(0):(data_rd[0]);;
// pv_two.Ti = (data_rd[1] == 0)?(1000000):(data_rd[1]); //积分时间
// pv_two.Ing = 0;
// pv_two.Ing_max = 5000;
// pv_two.Ing_min = 0;
// pv_two.Ing_sum = 0;
// pv_two.Td = data_rd[2];
// pv_two.Ts = 0.05; //采样周期50ms
// pv_two.Div = 0;
// pv_two.pidout = 0;
// pv_two.pidout_max = 0;
// pv_two.pidout_min = 0;
// pv_two.slow_down_flg = 0;
pv_two.pvout = ao_blf2_set;
pv_two.pvout(0);
}
//绝对值计算
float abs_bias(float bias)
{
bias =( bias>=0 )?(bias):(-bias);
return bias;
}
//模拟量控制
float ao_dwq = 0; //AO输出电流值(定位器)
float ao_blf1 = 0; //AO输出电流值(比例阀)
float ao_blf2 = 0; //AO输出电流值(比例阀)
float ao_ee_save_flag = 0;
// float atm_pressure = 0; //用于存放大气绝压单位0.1Kpa
// adj_steps astep = ADJ_WAIT; //自整定步骤,继电反馈
// adj_section_steps astep_s = SECTION_WAIT; //自整定步骤,分段
//电流输出校准
float AO_table[SECTION_NUM + 1] = {0};
float ao_cal_interval = 0;
//将记录的实际输出值在数组定义处填入此处
float AO_0_table[SECTION_NUM + 1] = {0};
float AO_1_table[SECTION_NUM + 1] = {0};
float AO_2_table[SECTION_NUM + 1] = {0};
void analog_ctrl(void)
{
if(ao_ee_save_flag == 1)
{
ee_writefloats(AO_0_SAVE_ADDR, AO_0_table);
ee_writefloats(AO_1_SAVE_ADDR, AO_1_table);
ee_writefloats(AO_2_SAVE_ADDR, AO_2_table);
AO_init_flag[AO_0] = 0;
AO_init_flag[AO_1] = 0;
AO_init_flag[AO_2] = 0;
current_output_calibrate_init();
ao_ee_save_flag = 0;
}
if(ao_dwq != (float)(HoldReg[0]) / 1000) //保持寄存器值发生变化时dac输出
{
ao_dwq = (float)(HoldReg[0]) / 1000; // uA -> mA
ao_dwq = current_output_calibrate(AO_0, ao_dwq);
ao_dwq_set(ao_dwq);
}
if( ao_blf1!= (float)(HoldReg[1]) / 1000) //保持寄存器值发生变化时dac输出
{
ao_blf1 = (float)(HoldReg[1]) / 1000; // uA -> mA
ao_blf1 = current_output_calibrate(AO_1, ao_blf1);
ao_blf1_set(ao_blf1);
//prov_set(ao_blf1,&pv_one); //控制比例阀1
}
if(ao_blf2 != (float)(HoldReg[2]) / 1000) //保持寄存器值发生变化时dac输出
{
ao_blf2 = (float)(HoldReg[2]) / 1000; // uA -> mA
ao_blf2 = current_output_calibrate(AO_2, ao_blf2);
ao_blf2_set(ao_blf2);
//prov_set(ao_blf2,&pv_two); //控制比例阀2
}
#if ENABLE_SECTION_CAL
if( (CoilState[3]&(0x40)) == 0x40 ) //触发比例阀1自整定
{
if( (astep_s == SECTION_WAIT) && (pv_two.status != PROV_ADJUSTING) )
{
pv_one.status = PROV_ADJUSTING;
}
}
if( (CoilState[3]&(0x20)) == 0x20 ) //触发比例阀2自整定
{
if( (astep_s == SECTION_WAIT) && (pv_one.status != PROV_ADJUSTING) )
{
pv_two.status = PROV_ADJUSTING;
}
}
if(it_100ms_flag_pv == 1) //每隔100ms更新一次数据
{
it_100ms_flag_pv = 0;
InputReg[7] = ( (InputReg[7]<16000)&&(InputReg[7]>8000) )?(InputReg[7]):(12000); //应对没接大气压力的情况
atm_pressure = ( (InputReg[7] - 4000)/(float)16000.0 ) * 2000; //大气绝压更新,4~20mA->0~200Kpa
for(uint8_t i = 1;i< SLDW_PRES_OUT;i++) //滑动窗口,记录历史纯输出气压
{
pv_one.previous_pressure[SLDW_PRES_OUT - i] = pv_one.previous_pressure[SLDW_PRES_OUT - i - 1];
pv_two.previous_pressure[SLDW_PRES_OUT - i] = pv_two.previous_pressure[SLDW_PRES_OUT - i - 1];
}
pv_one.previous_pressure[0] = pv_one.current_pressure;
pv_two.previous_pressure[0] = pv_two.current_pressure;
//比例阀1数据更新当前气压、当前气压百分比、百分比偏差、当前输入电流单片机->比例阀)
pv_one.current_pressure = (InputReg[16] - atm_pressure)/(float)10; //Kpasensor1 A口绝压转表压
pv_one.current_percent = pv_one.current_pressure/900*100;
pv_one.bias = pv_one.target_percent - pv_one.current_percent;
//比例阀2数据更新当前气压、当前气压百分比、百分比偏差、当前输入电流单片机->比例阀)
pv_two.current_pressure = (InputReg[17] - atm_pressure)/(float)10; //Kpasensor1 B口绝压转表压
pv_two.current_percent = pv_two.current_pressure/900*100;
pv_two.bias = pv_two.target_percent - pv_two.current_percent;
}
if(it_50ms_flag_pv == 1) //每隔50ms校准一次
{
it_50ms_flag_pv = 0;
//每个比例阀对应两个电磁阀,一个是气源一个是锁止阀
if( (CoilState[0]&(0x03)) == 0x03 ) //2个电磁阀都开启的情况下才进行控制比例阀1,否则保持
{
if(pv_two.status != PROV_ADJUSTING) //对其中一个比例阀进行自整定时,不对另一个进行控制
{
prov_ctrl(&pv_one, &adj_pv1);
}
}
if( (CoilState[0]&(0x0C)) == 0x0C ) //2个电磁阀都开启的情况下才进行控制比例阀2,否则保持
{
if(pv_one.status != PROV_ADJUSTING) //对其中一个比例阀进行自整定时,不对另一个进行控制
{
prov_ctrl(&pv_two, &adj_pv2);
}
}
}
#endif
}
int8_t AO_init_flag[3] = {0};
void current_output_calibrate_init(void)
{
ao_cal_interval = ( (float)(AO_CAL_END - AO_CAL_START) ) / ( (float)SECTION_NUM );
for(uint8_t i = 0; i < SECTION_NUM + 1; i++)
{
AO_table[i] = AO_CAL_START + i * ao_cal_interval;
}
ee_readfloats(AO_0_SAVE_ADDR, AO_0_table);
ee_readfloats(AO_1_SAVE_ADDR, AO_1_table);
ee_readfloats(AO_2_SAVE_ADDR, AO_2_table);
if( abs_bias( AO_0_table[0] - AO_table[0] ) > 1)
{
AO_init_flag[AO_0] = -1;
}
if( abs_bias( AO_1_table[0] - AO_table[0] ) > 1)
{
AO_init_flag[AO_1] = -1;
}
if( abs_bias( AO_2_table[0] - AO_table[0] ) > 1)
{
AO_init_flag[AO_2] = -1;
}
}
float current_output_calibrate(uint8_t tag, float target)
{
float result = 0;
if(target > 25) target = 25; //限幅
if(target < 0) target = 0; //限幅
switch (tag)
{
case AO_0:
{
if(AO_init_flag[AO_0] == -1)
{
result = target;
return result;
}
for(uint8_t i = 0; i < SECTION_NUM; i++)
{
if( (AO_0_table[i] <= target) && (target <= AO_0_table[i + 1]) )
{
result = ( (target - AO_0_table[i]) / ( AO_0_table[i + 1] - AO_0_table[i] ) ) * \
ao_cal_interval + AO_table[i];
if(result > 25) result = 25; //限幅
if(result < 0) result = 0; //限幅
return result;
}
}
}
break;
case AO_1:
{
if(AO_init_flag[AO_1] == -1)
{
result = target;
return result;
}
for(uint8_t i = 0; i < SECTION_NUM; i++)
{
if( (AO_1_table[i] <= target) && (target <= AO_1_table[i + 1]) )
{
result = ( (target - AO_1_table[i]) / ( AO_1_table[i + 1] - AO_1_table[i] ) ) * \
ao_cal_interval + AO_table[i];
if(result > 25) result = 25; //限幅
if(result < 0) result = 0; //限幅
return result;
}
}
}
break;
case AO_2:
{
if(AO_init_flag[AO_2] == -1)
{
result = target;
return result;
}
for(uint8_t i = 0; i < SECTION_NUM; i++)
{
if( (AO_2_table[i] <= target) && (target <= AO_2_table[i + 1]) )
{
result = ( (target - AO_2_table[i]) / ( AO_2_table[i + 1] - AO_2_table[i] ) ) * \
ao_cal_interval + AO_table[i];
if(result > 25) result = 25; //限幅
if(result < 0) result = 0; //限幅
return result;
}
}
}
break;
default:
break;
}
return 0;
}
#if ENABLE_SECTION_CAL
//比例阀目标值设定,调节范围计算
void prov_set(float target_p, propotion_valve *pvx)
{
target_p = (target_p < pvx->input_max)?(target_p):(pvx->input_max); //dac输出限幅
target_p = (target_p > pvx->input_min)?(target_p):(pvx->input_min);
pvx->target_percent = (target_p - pvx->ctrl_min) / ( pvx->ctrl_max - pvx->ctrl_min )*100; //目标百分比,按照理论范围计算
pvx->target_pressure = pvx->target_percent/100*900; //Kpa, 比例阀 (4~20mA -> 0~0.9Mpa)
switch (pvx->tag)//分段校准,不同比例阀的数据表不一样
{
case 1:
{
target_p = (prov_section_calculate(pvx->target_pressure,&adj_pv1) > 0)?(prov_section_calculate(pvx->target_pressure,&adj_pv1)):(target_p);
target_p = (target_p < pvx->input_max)?(target_p):(pvx->input_max); //dac输出限幅
target_p = (target_p > pvx->input_min)?(target_p):(pvx->input_min);
}
break;
case 2:
{
target_p = (prov_section_calculate(pvx->target_pressure,&adj_pv2) > 0)?(prov_section_calculate(pvx->target_pressure,&adj_pv2)):(target_p);
target_p = (target_p < pvx->input_max)?(target_p):(pvx->input_max); //dac输出限幅
target_p = (target_p > pvx->input_min)?(target_p):(pvx->input_min);
}
break;
default:
break;
}
pvx->target_current = target_p; //记录当前理论模拟输出
pvx->gas_direction = (pvx->target_current >= pvx->current_input)?(GAS_IN):(GAS_OUT); //判断即将进行充气还是排气
pvx->current_input = pvx->target_current; //理论值作为当前输出值
pvx->pvout(pvx->current_input); //dac输出
}
//分段计算达到目标气压所需的电流
float prov_section_calculate(float target_pressure, prov_adjust *adj_pvx)
{
float target_current = 0;
if(adj_pvx->table_pressure[SECTION_NUM_ADJ - 1] == -1) return -1; //未经过整定
//寻找目标值所在区间,并根据区间占比计算所需输出
for(uint8_t i = 0;i < SECTION_NUM_ADJ - 1;i++)
{
if( (target_pressure >= adj_pvx->table_pressure[i]) && (target_pressure < adj_pvx->table_pressure[i+1]) )
{
target_current = adj_pvx->table_current[i]+(adj_pvx->table_current[i+1] - adj_pvx->table_current[i]) \
* (target_pressure - adj_pvx->table_pressure[i])/(adj_pvx->table_pressure[i+1] - adj_pvx->table_pressure[i]); //目标气压在所在区间内的百分比
}
if(target_pressure >= adj_pvx->table_pressure[SECTION_NUM_ADJ - 1]) target_current = adj_pvx->table_current[SECTION_NUM_ADJ - 1];
}
return target_current;
}
//比例阀控制
void prov_ctrl(propotion_valve *pvx, prov_adjust *adj_pvx)
{
switch(pvx->status)
{
case PROV_RUNNING: //介入控制
{
if(adj_pvx->adj_flag == 1) //进入此处说明整定被打断
{
//整定结束
prov_adj_init(adj_pvx);
astep = ADJ_WAIT;
astep_s = SECTION_WAIT;
}
// prov_calibrate_step(&pvx);
prov_calibrate_pid(pvx);
}
break;
case PROV_ADJUSTING: //自整定
{
// //继电反馈
// if(astep == ADJ_WAIT)
// {
// prov_adj_init(adj_pvx);
// adj_pvx->adj_flag = 1; //整定开始
// astep = ADJ_START;
// }
// prov_adj(pvx, adj_pvx);
//分段
if(astep_s == SECTION_WAIT)
{
prov_adj_section_init(adj_pvx);
adj_pvx->adj_flag = 1; //整定开始
astep_s = SECTION_START;
}
prov_adj_section(pvx, adj_pvx);
}
break;
case PROV_HOLDING: //保持原样,不介入控制
{
if(adj_pvx->adj_flag == 1) //进入此处说明整定被打断
{
//整定结束
prov_adj_init(adj_pvx);
astep = ADJ_WAIT;
astep_s = SECTION_WAIT;
}
}
break;
default:
{
return;
}
}
}
#endif
#if ENABLE_PID_CTRL
//初始化自整定相关的参数
prov_adjust adj_pv1;
prov_adjust adj_pv2;
//初始化继电反馈整定参数
void prov_adj_init(prov_adjust *adj_pvx)
{
adj_pvx->adj_flag = 0; //自整定标志0空闲1整定中
for( uint8_t i = 0;i < OSCILL_TIMES;i++) //继电整定,振荡幅值,振荡周期
{
adj_pvx->relay_a[i] = 0;
adj_pvx->relay_tc[i] = 0;
}
adj_pvx->relay_d = 0.15; //继电整定,回环幅值
adj_pvx->air_source = 0; //气源单位Kpa
adj_pvx->middle_current = 0; //中间气压电流
adj_pvx->oscil_times = 0; //振荡次数
}
int tick_previous = -1; //用于计时
int tick_current = -1;
uint8_t hys_flag = 0; //0:充气, 1:排气
//PID参数自整定继电反馈
void prov_adj(propotion_valve *pvx, prov_adjust *adj_pvx)
{
switch(astep)
{
case ADJ_START:
{
if(tick_previous == -1)
{
tick_previous = tick500ms;
prov_set(4,pvx);//排空气体
}
tick_current = tick500ms;
if( (tick_current - tick_previous) > 20) //等待20*500ms
{
//记录此时气源压力
adj_pvx->air_source = (InputReg[18] - atm_pressure) / (float)10;
tick_previous = -1;
tick_current = -1;
astep++;
}else if( (tick_current - tick_previous) < 0)
{
tick_previous = -1;
tick_current = -1;
}
}
break;
case ADJ_MOVE2MIDDLE:
{
if(tick_previous == -1)
{
tick_previous = tick500ms;
//输出气源50%压力,根据目标压力倒推所需电流值
adj_pvx->middle_current = (adj_pvx->air_source/2/900)*(pvx->input_max - pvx->input_min) + pvx->input_min;
adj_pvx->middle_current = (adj_pvx->middle_current < pvx->input_max)?(adj_pvx->middle_current):(pvx->input_max); //dac输出限幅
adj_pvx->middle_current = (adj_pvx->middle_current > pvx->input_min)?(adj_pvx->middle_current):(pvx->input_min);
prov_set(adj_pvx->middle_current,pvx);
}
tick_current = tick500ms;
if( (tick_current - tick_previous) > 10) //等待10*500ms
{
tick_previous = -1;
tick_current = -1;
astep++;
}else if( (tick_current - tick_previous) < 0)
{
tick_previous = -1;
tick_current = -1;
}
}
break;
case ADJ_OSCILLATE:
{
if(adj_pvx->oscil_times < OSCILL_TIMES) //振荡次数是否达到目标
{
if(tick_previous == -1) //计时开始,记录起始时间
{
tick_previous = tick500ms;
}else
{
//记录第 oc_times 个周期的峰值
adj_pvx->relay_a[adj_pvx->oscil_times] = (adj_pvx->relay_a[adj_pvx->oscil_times] < pvx->current_pressure)?(pvx->current_pressure):(adj_pvx->relay_a[adj_pvx->oscil_times]);
}
tick_current = tick500ms; //记录当前时间
if( (tick_current - tick_previous) > TICK_LIMIT ) //是否超时TICK_LIMIT*500ms
{
astep = ADJ_END; //强制打断
}else if( (tick_current - tick_previous) < 0)
{
tick_previous = -1;
tick_current = -1;
}
if( ( pvx->current_pressure < (adj_pvx->air_source/2*(1 + adj_pvx->relay_d)) ) && (hys_flag == 0) ) //充气振荡
{
pvx->current_input = adj_pvx->middle_current*(1 + adj_pvx->relay_d); //输出气源 50*(1+d)% 压力
pvx->current_input = (pvx->current_input < pvx->input_max)?(pvx->current_input):(pvx->input_max); //dac输出限幅
pvx->current_input = (pvx->current_input > pvx->input_min)?(pvx->current_input):(pvx->input_min);
prov_set(pvx->current_input,pvx);
}else if( pvx->current_pressure >= (adj_pvx->air_source/2*(1 + adj_pvx->relay_d)) && (hys_flag == 0)) //切换至排气
{
hys_flag = 1;
}
if( ( pvx->current_pressure > (adj_pvx->air_source/2*(1 - adj_pvx->relay_d)) ) && (hys_flag == 1) ) //排气振荡
{
pvx->current_input = adj_pvx->middle_current*(1 - adj_pvx->relay_d); //输出气源 50*(1-d)% 压力
pvx->current_input = (pvx->current_input < pvx->input_max)?(pvx->current_input):(pvx->input_max); //dac输出限幅
pvx->current_input = (pvx->current_input > pvx->input_min)?(pvx->current_input):(pvx->input_min);
prov_set(pvx->current_input,pvx);
}else if( pvx->current_pressure <= (adj_pvx->air_source/2*(1 - adj_pvx->relay_d)) && (hys_flag == 1) ) //切换至充气
{
hys_flag = 0;
adj_pvx->relay_tc[adj_pvx->oscil_times] = (tick_current - tick_previous) * 500; //振荡周期单位ms
tick_previous = -1; //重新开始计时
tick_current = -1;
adj_pvx->oscil_times++; //开始下一次振荡
}
}else //振荡次数达到目标
{
if(tick_previous == -1)
{
tick_previous = tick500ms;
}
tick_current = tick500ms;
if( (tick_current - tick_previous) > 6) //等待6*500ms后排气
{
prov_set(4,pvx);
tick_previous = -1;
tick_current = -1;
astep++;
}else if( (tick_current - tick_previous) < 0)
{
tick_previous = -1;
tick_current = -1;
}
}
}
break;
case ADJ_CALCULATE:
{
for(uint8_t i = 0;i < OSCILL_TIMES; i++) //对振荡周期和峰值作均值滤波
{
adj_pvx->relay_A += adj_pvx->relay_a[i];
adj_pvx->relay_Tc += adj_pvx->relay_tc[i];
}
adj_pvx->relay_A /= OSCILL_TIMES * ( adj_pvx->air_source/2 * (1 + adj_pvx->relay_d) );
adj_pvx->relay_Tc /= OSCILL_TIMES;
/* PI [0.45,0.8]*/
/* PID [0.6,0.5,0.12]*/
pvx->Kp = (float)0.6 * ( 4 * (float)0.1 ) / ( (float)3.1415 * adj_pvx->relay_A ) / 3;
pvx->Ti = (float)0.5 * adj_pvx->relay_Tc/1000 / 20;
pvx->Td = (float)0.12 * adj_pvx->relay_Tc/1000;
pvx->Ing_sum = 0;
switch(pvx->tag)
{
case 1:
{
data_wr[0] = pvx->Kp;
data_wr[1] = pvx->Ti;
data_wr[2] = pvx->Td;
ee_writefloats(PRO1_ADDR,data_wr,3);
}
break;
case 2:
{
data_wr[0] = pvx->Kp;
data_wr[1] = pvx->Ti;
data_wr[2] = pvx->Td;
ee_writefloats(PRO2_ADDR,data_wr,3);
}
break;
default:
{
}
break;
}
astep++;
}
break;
case ADJ_END:
{
if(tick_previous == -1)
{
tick_previous = tick500ms;
}
tick_current = tick500ms;
if( (tick_current - tick_previous) > 10) //等待10*500ms后恢复控制等待期间排气 + 计算结果)
{
switch(pvx->tag) //重新触发保持寄存器
{
case 1:
{
ao_blf1 = -1;
CoilState[3] &= 0xBF; //D6 = 0, [1011 1111]
}
break;
case 2:
{
ao_blf2 = -1;
CoilState[3] &= 0xDF; //D5 = 0, [1101 1111]
}
break;
default:
{
}
break;
}
tick_previous = -1;
tick_current = -1;
prov_adj_init(adj_pvx);
pvx->status = PROV_RUNNING; //恢复控制
astep++;
}else if( (tick_current - tick_previous) < 0)
{
tick_previous = -1;
tick_current = -1;
}
}
break;
case ADJ_WAIT:
{
}
break;
default:
{
return;
}
}
}
//pid控制
float beta_in = 0, beta_out = 0; //变速积分
void prov_calibrate_pid(propotion_valve *pvx)
{
if( (abs_bias(pvx->bias) > pvx->bias_area ) && (abs_bias(pvx->bias) < BIAS_MAX) ) //误差进入目标±BIAS_MAX%以内后再进行控制
{
//变速积分
beta_in = ( abs_bias(pvx->bias*pvx->bias) + BIAS_MAX ) / ( BIAS_MAX );
beta_out = ( abs_bias(pvx->bias) + BIAS_MAX/2 ) / ( BIAS_MAX );
//充放气过程分开处理
switch(pvx->gas_direction)
{
case GAS_IN: //充气
{
pvx->Ing_sum += pvx->bias * beta_in; //偏差求和
pvx->Ing_sum = (pvx->Ing_sum > pvx->Ing_max)?(pvx->Ing_max):(pvx->Ing_sum); //积分限幅
pvx->Ing_sum = (pvx->Ing_sum < pvx->Ing_min)?(pvx->Ing_min):(pvx->Ing_sum);
pvx->Ing = ( pvx->Ts / pvx->Ti ) * pvx->Ing_sum; //积分项
pvx->Div = ( pvx->Td / pvx->Ts ) * ( pvx->bias - pvx->bias_previous ); //微分项
pvx->pidout = pvx->Kp*( pvx->bias + pvx->Ing + pvx->Div ); //pid输出
pvx->current_input = (pvx->pidout/100) * (pvx->input_max - pvx->input_min) + pvx->input_min;
}
break;
case GAS_OUT: //排气
{
pvx->Ing_sum += pvx->bias * beta_out; //偏差求和
pvx->Ing_sum = (pvx->Ing_sum > pvx->Ing_max)?(pvx->Ing_max):(pvx->Ing_sum); //积分限幅
pvx->Ing_sum = (pvx->Ing_sum < pvx->Ing_min)?(pvx->Ing_min):(pvx->Ing_sum);
pvx->Ing = ( pvx->Ts / pvx->Ti ) * pvx->Ing_sum; //积分项
pvx->Div = ( pvx->Td / pvx->Ts ) * ( pvx->bias - pvx->bias_previous ); //微分项
pvx->pidout = pvx->Kp*( pvx->bias + pvx->Ing + pvx->Div ); //pid输出
pvx->current_input = (pvx->pidout/100) * (pvx->input_max - pvx->input_min) + pvx->input_min;
}
break;
default:
{
}
break;
}
pvx->current_input = (pvx->current_input < pvx->pidout_max)?(pvx->current_input):(pvx->pidout_max); //充气时限制输出上限
pvx->current_input = (pvx->current_input > pvx->pidout_min)?(pvx->current_input):(pvx->pidout_min); //排气时限制输出下限
pvx->bias_previous = pvx->bias; //更新前一个时刻的偏差
if( abs_bias(pvx->previous_pressure[0] - pvx->previous_pressure[SLDW_PRES_OUT - 1]) <= 10 ) //判断是否趋于稳定
{
pvx->slow_down_flg = 1;
}
pvx->current_input = (pvx->target_current < pvx->input_max*(float)(0.995))?(pvx->current_input):(pvx->input_max); //小信号切除
if( pvx->target_current <= pvx->input_min*(float)(1.005) )
{
pvx->current_input = pvx->input_min;
pvx->Ing_sum = 0;
}
pvx->pvout(pvx->current_input); //dac输出
}else if(abs_bias(pvx->bias) >= BIAS_MAX)
{
switch(pvx->gas_direction) //根据气体方向对输出范围做补偿
{
case GAS_IN:
{
pvx->Ing_sum = (pvx->target_percent - 100*out_makeup) / pvx->Kp / ( pvx->Ts / pvx->Ti ); //预估积分值,改善滞后
pvx->Ing_sum = (pvx->Ing_sum < pvx->Ing_min)?(pvx->Ing_min):(pvx->Ing_sum);
}
break;
case GAS_OUT:
{
pvx->Ing_sum = pvx->target_percent / pvx->Kp / ( pvx->Ts / pvx->Ti ); //预估积分值,改善滞后
}
break;
default:
{
}
break;
}
}
}
#endif
#if ENABLE_SECTION_CAL
//分段校准参数初始化
void prov_adj_section_init(prov_adjust *adj_pvx)
{
adj_pvx->adj_flag = 0;
adj_pvx->stable_area = 9;
adj_pvx->wait_tick = 2 * 30;
for(uint8_t i = 0; i < SECTION_NUM_ADJ; i++)
{
adj_pvx->table_current[i] = -1;
adj_pvx->table_pressure[i] = -1;
}
}
uint8_t temp_cnt = 0;
//分段校准
void prov_adj_section(propotion_valve *pvx, prov_adjust *adj_pvx)
{
switch (astep_s)
{
case SECTION_START:
{
if(tick_previous == -1)
{
tick_previous = tick500ms;
prov_set(pvx->input_min,pvx); //排空气体
}
tick_current = tick500ms;
if( (tick_current - tick_previous) > 20) //等待20*500ms
{
tick_previous = -1;
tick_current = -1;
astep_s++;
}else if( (tick_current - tick_previous) < 0)
{
tick_previous = -1;
tick_current = -1;
}
}
break;
case SECTION_RECORD_AIRSOURCE:
{
//记录此时气源压力
adj_pvx->air_source = (InputReg[18] - atm_pressure) / (float)10;
//计算分段电流,上限为气源倒推得到的电流值
adj_pvx->table_current[SECTION_NUM_ADJ-1] = adj_pvx->air_source/900 * (pvx->ctrl_max - pvx->ctrl_min) + pvx->ctrl_min;
for(uint8_t i = 0;i < SECTION_NUM_ADJ - 1;i++)
{
adj_pvx->table_current[i] = pvx->ctrl_min + i * (adj_pvx->table_current[SECTION_NUM_ADJ-1] - pvx->ctrl_min)/(SECTION_NUM_ADJ - 1);
}
astep_s++;
}
break;
case SECTION_MOVE:
{
if(tick_previous == -1)
{
tick_previous = tick500ms;
prov_set( adj_pvx->table_current[temp_cnt], pvx ); //设定分段电流
}
tick_current = tick500ms;
if( (tick_current - tick_previous) > adj_pvx->wait_tick) //等待tick*500ms
{
adj_pvx->table_pressure[temp_cnt] = pvx->current_pressure; //记录当前实际气压
tick_previous = -1; //准备下一次计时
tick_current = -1;
temp_cnt++; //下一个
if(temp_cnt >= SECTION_NUM_ADJ)
{
temp_cnt = 0;
astep_s++;
}
}else if( (tick_current - tick_previous) < 0)
{
tick_previous = -1;
tick_current = -1;
}
}
break;
case SECTION_END:
{
if(tick_previous == -1)
{
tick_previous = tick500ms;
}
tick_current = tick500ms;
if( (tick_current - tick_previous) > 10) //等待10*500ms后恢复控制等待期间排气 + 计算结果)
{
switch(pvx->tag) //重新触发保持寄存器
{
case 1:
{
ao_blf1 = -1;
CoilState[3] &= 0xBF; //D6 = 0, [1011 1111]
}
break;
case 2:
{
ao_blf2 = -1;
CoilState[3] &= 0xDF; //D5 = 0, [1101 1111]
}
break;
default:
{
}
break;
}
tick_previous = -1;
tick_current = -1;
adj_pvx->adj_flag = 0;
pvx->status = PROV_HOLDING; //恢复控制
astep_s++;
}else if( (tick_current - tick_previous) < 0)
{
tick_previous = -1;
tick_current = -1;
}
}
break;
case SECTION_WAIT:
{
}
break;
default:
break;
}
}
#endif