|
跟大家分享一下我对PID的理解:在控温中比例控制占主导,积分和微分都是辅助性的,在参数整定时先去掉微分和积分的作用,单独整定比例参数使温度能停止波动后比例参数即确定(此时由于没有积分作用允许与目标温度存在温差),再打开积分并调试其参数使温度无限接近目标温,注意积分周期宜长不宜短,积分周期必须大于热惯性作用时间,如果积分周期太短温度会始终在目标温附近波动,积分时间长只会使恒温过程变长。对于负载经常更换的控温体应使用热惯性最大的负载做积分参数整定。微分参数整定需放到比例和积分参数确定之后来做,微分的原则是:微分周期越短控温效果越好但是要能减小传感器的采样干扰,在不引起温度波动的前提下尽量加大微分作用。以下是一款热风枪的控温程序:
//======================
//MCU:S3F94C4
//系统晶振:3200000Hz
//======================
#include
//========宏定义========
#define uchar unsigned char
#define uint unsigned int
uchar tmr=0,tmr_sec=0,adc_cnt=0,com_duty;
uchar tmr_delay,dig0_p2=0,dig1_p2=0,dig2_p2=0;
uchar dig0_p0=0,dig1_p0=0,dig2_p0=0;
uchar Periods_cnt,ip_cnt=0,delay_sleep;
signed char d_tab[10]={0,0,0,0,0,0,0,0,0,0};//i_pid,d_pid=0,
int d_sum=0,p_pid;
uint sen_val=0,vr_val=0xFFFF,last_sev;//
uint tar_adv,tar_temp,cur_temp,ip_sum=0;
uchar ip_tab[8]={0,0,0,0,0,0,0,0};
#define NOP asm("NOP")
#define DI asm("DI")
#define EI asm("EI")
#define wdt_feed (BTCON |= 0x02)
#define Power_on (P1 |= 0x01)
#define Power_off (P1 &= 0xfe)
#define Relay_on (P1 |= 0x02)
#define Relay_off (P1 &= 0xfd)
#define sw_power (P1&4|flag.d_slp)
//#define sw_sleep (P2 & 0x01)
#define ad1c 64
//========SMART OPTION========
__root const uchar SmartOption[4] @ 0x003C ={0x00,0x00,0xB0,0x03};
__code const uint DigCode[];
__code const uint temp_tab[];
union a
{
struct
{
uchar h;
uchar l;
}u8r;
uint u16r;
};
struct b
{
unsigned t_stb:1;
unsigned d_slp:1;
};
static struct b flag;
void InitMCU(void)
{
BTCON=0xA3;
CLKCON=0x18;//fcpu=fosc/1
P0=0x00;
P1=0x00;
P2=0x00;
P0CONH=0xAA;//P0 口控制寄存器 (高字节)
P0CONL=0x8F;//P0 口控制寄存器 (低字节)
P0PND=0x00;//P0 中断标志位寄存器
P1CON=0x0A;
P2CONH=0x4A;
P2CONL=0xA8;//0xAA
T0DATA=0x7d;//10ms*125/125=10ms
T0CON=0x4A;//FOSC/256=3200000/256=12500
PWMDATA=0x00;
PWMCON=0x00;
ADCON=0x01;//ADC0 fosc/16 start A/D Convert//0.25ms
BTCON=0x00;
}
void Display(void)
{
uchar i;
if (com_duty)com_duty--;
if (com_duty==0)
{
uchar dat_p0,dat_p2;
if (!(P2 & 0x40))//last dig0
{
dat_p0=dig1_p0 & 0xEF;
dat_p2=dig1_p2;
dat_p0|=0x40;dat_p2|=0x40;
}
else
{
if (!(P0 & 0x10))//last dig1
{
dat_p0=dig2_p0 & 0xBF;
dat_p2=dig2_p2;
dat_p0|=0x10;dat_p2|=0x40;
}
else//last dig2
{
dat_p0=dig0_p0;
dat_p2=dig0_p2 & 0xBF;
dat_p0|=0x50;
}
}
P2 |= 0x40;
P0 |= 0xf8;
P2 |= 0x7e;
P2 &= dat_p2|0xC1;
P0 &= dat_p0|0x07;
P2 &= dat_p2|0x81;
//GetDutyValue
dat_p0 &= 0xA8;
dat_p2 &= 0x3e;
for (i=3;i>0;i--)
{
if (dat_p0 & 0x80)com_duty++;
dat_p0 <<= 2;
}
dat_p2>>=1;
for (i=5;i>0;i--)
{
if (dat_p2 & 0x01)com_duty++;
dat_p2>>=1;
}
}
}
void DispValue(uint val1)
{
if (val1>600)
{
dig0_p0=0xF0;
dig0_p2=0x74;
dig1_p0=0;
dig1_p2=0x20;
dig2_p0=0xF0;
dig2_p2=0x66;
}
/*
else if (tmr_delay&1)//ray
{
dig0_p0=0x50;dig0_p2=0x40;
dig1_p0=0x50;dig1_p2=0x40;
dig2_p0=0x50;dig2_p2=0x40;
}
*/
else
{
union a buf;
uchar b0=0,b1=0,b2=0;
while (val1>=100){val1-=100;b0++;}//uchar i=val1/100;
buf.u16r = DigCode[b0];
dig0_p0=buf.u8r.h;
dig0_p2=buf.u8r.l;
while (val1>=10){val1-=10;b1++;}b2=val1;//i=val1%100/10;
buf.u16r = DigCode[b1];
dig1_p0=buf.u8r.h;
dig1_p2=buf.u8r.l;
//i=val1%10;
buf.u16r = DigCode[b2];
dig2_p0=buf.u8r.h;
dig2_p2=buf.u8r.l;
}
}
//===============================================================================================
void main(void)
{
uint adc_val_buf=0;
DI;
SP=0xc0;//add by sp
InitMCU();
EI;
tmr = 0;
Relay_on;
while(1)
{
//主程序
wdt_feed;
//=============================================
while ((ADCON & 0x08)==0);//A/D Convertion done!
Display();
adc_val_buf += ((uint)ADDATAH<<2) + (ADDATAL & 0x03);
adc_cnt++;
if (ADCON & 0x10)//ADC1
{
if (adc_cnt & 0xc0)//adc_cnt>63
{
ADCON = 0x01;
adc_cnt = 0;
sen_val = adc_val_buf;
//delay wait for A/D channel to be stable
cur_temp=(int)tar_temp+(int)(adc_val_buf>>6)-(int)(tar_adv>>6);
if (flag.t_stb==0 && tmr_delay==0)
{
if (cur_temp42)cur_temp++;
else if (cur_temp>cur_temp && (sen_val&63)<21)cur_temp--;
DispValue(cur_temp);
if (tar_temp==cur_temp)flag.t_stb=1;
}
else if (cur_temp>tar_temp+5 || cur_temp<tar_temp-5)
{
flag.t_stb=0;
}
//Calculate the P of PID
p_pid=(int)(tar_adv>>4)-(int)(sen_val>>4);
adc_val_buf=0;
}
else ADCON |= 0x01;//Start A/D Convert
}
else//ADC0
{
if (adc_cnt & 0xf0)//adc_cnt>15
{
uint buf16;
ADCON=0x11;//ADCON ^= 0x10;
adc_cnt = 0;
buf16 = adc_val_buf>>4;
//delay wait for A/D channel to be stable
if (vr_val>buf16+2 || vr_val+2<buf16)
{
vr_val=buf16;
buf16=( (buf16<<4)+(buf16<<3)+buf16+(buf16>>3)>>6 )+100;//( (vr_val>>1)+(vr_val>>2)+(vr_val>>4)>>1 )
if (buf16>500)buf16=500;
tar_temp=buf16;
DispValue(buf16);
buf16-=100;
if ( (buf16&31)<16 )tar_adv=temp_tab[buf16>>5]+((buf16&31)<<6);
else tar_adv=temp_tab[buf16+32>>5]-(32-(buf16&31)<<6);
tmr_delay=4;
flag.t_stb=0;
}
adc_val_buf=0;
}
else ADCON |= 0x01;//Start A/D Convert
}
//=================================================
if (sw_power!=0 && sen_val<0x21E5)
{
union a period;uchar p2_buf=0x20;
P0=02=0x20;
Relay_off;
while (sw_power)
{
wdt_feed;
if (++period.u16r==0)p2_buf ^= 0x20;
if (period.u8r.l<period.u8r.h)p2 =="" p2_buf;
else P2 = p2_buf^0x20;
}
flag.t_stb=0;
Relay_on;
}
}
}
//======================
#pragma vector=0x00
__interrupt void int_9454()//T0 10ms/cycle
{
int Heat_duty,s16buf,d_pid;//
signed char s8buf,i_pid;
//中断服务程序
T0CON = T0CON & 0xfe;
//BTCON = 0x2;
if (++tmr>4)//50ms
{
tmr=0;
tmr_sec++;
if (tmr_sec>9)//0.5s
{
tmr_sec=0;
if (tmr_delay>0)tmr_delay--;
if ((P2&1)==0)
{
if (flag.t_stb!=0 && ++delay_sleep>119)//60/0.5=120
{
flag.d_slp=1;
flag.t_stb=0;
}
}
else {delay_sleep=0;flag.d_slp=0;}
//Calculate the I of PID
s8buf = ip_sum>>3;
if (s8buf+p_pid>100)s8buf=100;
else if (s8buf+p_pid<0)s8buf=0;
else s8buf += p_pid;
ip_sum = ip_sum + s8buf - ip_tab[ip_cnt];
ip_tab[ip_cnt++]=s8buf;
ip_cnt &= 7;
}
//Calculate the D of PID
if (last_sev<sen_val){s16buf=sen_val-last_sev;s8buf=1;}
else {s16buf=last_sev-sen_val;s8buf=0;}
if (s16buf>127)s16buf=127;
if (s8buf)s8buf = -(signed char)s16buf;
else s8buf = (signed char)s16buf;
d_sum -= d_tab[tmr_sec];
d_sum += s8buf;
d_tab[tmr_sec] = s8buf;
last_sev=sen_val;
}
i_pid = ip_sum>>3;
//if (d_pid&0x80)flag.t_stb=0;
d_pid = d_sum>>5;
if (sw_power)Heat_duty=0;//
else Heat_duty=p_pid+i_pid+d_pid;//
if (Heat_duty>100)Heat_duty=100;
else if (Heat_duty<0)Heat_duty=0;
Heat_duty>>=2;
Periods_cnt++;
if (Periods_cnt>24)Periods_cnt=0;
if (Periods_cnt<heat_duty)power_on;
else Power_off;
}
__code const uint DigCode[16] @ 0x0002 =
{// P0 P2
0xF856, //0
0x5850, //1
0x7866, //2
0x7874, //3
0xD870, //4
0xF074, //5
0xF076, //6
0x7850, //7
0xF876, //8
0xF874, //9
0xF872, //A
0xD076, //B
0xF046, //C
0x5876, //D
0xF066, //E
0xF062, //F
// 0x5040, //NOP
};
__code const uint temp_tab[14] @ 0x0040=
{
0x1556, //100
0x1D60, //132
0x256B, //164
0x2DCD, //196
0x3609, //228
0x3DEB, //260
0x46BA, //292
0x4F65, //324
0x579A, //356
0x6093, //388
0x6973, //420
0x6FF0, //452
0x798C, //484
0x8328, //516
};
</heat_duty)power_on;
</sen_val){s16buf=sen_val-last_sev;s8buf=1;}
</buf16)
</tar_temp-5)
|
|