增量式 PID 控制算法 温度控制实例
1 #include<reg51.h> 2 #include<intrins.h> 3 #include<math.h> 4 #include<string.h> 5 struct PID { 6 unsigned int SetPoint; // 设定目标 Desired Value 7 unsigned int Proportion; // 比例常数 Proportional Const 8 unsigned int Integral; // 积分常数 Integral Const 9 unsigned int Derivative; // 微分常数 Derivative Const 10 unsigned int LastError; // Error[-1] 11 unsigned int PrevError; // Error[-2] 12 unsigned int SumError; // Sums of Errors 13 }; 14 struct PID spid; // PID Control Structure 15 unsigned int rout; // PID Response (Output) 16 unsigned int rin; // PID Feedback (Input) 17 sbit data1=P1^0; 18 sbit clk=P1^1; 19 sbit plus=P2^0; 20 sbit subs=P2^1; 21 sbit stop=P2^2; 22 sbit output=P3^4; 23 sbit DQ=P3^3; 24 unsigned char flag,flag_1=0; 25 unsigned char high_time,low_time,count=0;//占空比调节参数 26 unsigned char set_temper=35; 27 unsigned char temper; 28 unsigned char i; 29 unsigned char j=0; 30 unsigned int s; 31 /*********************************************************** 32 延时子程序,延时时间以12M晶振为准,延时时间为30us×time 33 ***********************************************************/ 34 void delay(unsigned char time) 35 { 36 unsigned char m,n; 37 for(n=0;n<time;n++) 38 for(m=0;m<2;m++){} 39 } 40 /*********************************************************** 41 写一位数据子程序 42 ***********************************************************/ 43 void write_bit(unsigned char bitval) 44 { 45 EA=0; 46 DQ=0; /*拉低DQ以开始一个写时序*/ 47 if(bitval==1) 48 { 49 _nop_(); 50 DQ=1; /*如要写1,则将总线置高*/ 51 } 52 delay(5); /*延时90us供DA18B20采样*/ 53 DQ=1; /*释放DQ总线*/ 54 _nop_(); 55 _nop_(); 56 EA=1; 57 } 58 /*********************************************************** 59 写一字节数据子程序 60 ***********************************************************/ 61 void write_byte(unsigned char val) 62 { 63 unsigned char i; 64 unsigned char temp; 65 EA=0; 66 TR0=0; 67 for(i=0;i<8;i++) /*写一字节数据,一次写一位*/ 68 { 69 temp=val>>i; /*移位操作,将本次要写的位移到最低位*/ 70 temp=temp&1; 71 write_bit(temp); /*向总线写该位*/ 72 } 73 delay(7); /*延时120us后*/ 74 // TR0=1; 75 EA=1; 76 } 77/*********************************************************** 78 读一位数据子程序 79 ***********************************************************/ 80 unsigned char read_bit() 81 { 82 unsigned char i,value_bit; 83 EA=0; 84 DQ=0; /*拉低DQ,开始读时序*/ 85 _nop_(); 86 _nop_(); 87 DQ=1; /*释放总线*/ 88 for(i=0;i<2;i++){} 89 value_bit=DQ; 90 EA=1; 91 return(value_bit); 92 } 93 /*********************************************************** 94 读一字节数据子程序 95 ***********************************************************/ 96 unsigned char read_byte() 97 { 98 unsigned char i,value=0; 99 EA=0; 100 for(i=0;i<8;i++) 101 { 102 if(read_bit()) /*读一字节数据,一个时序中读一次,并作移位处理*/ 103 value|=0x01<<i; 104 delay(4); /*延时80us以完成此次都时序,之后再读下一数据*/ 105 } 106 EA=1; 107 return(value); 108 } 109 /*********************************************************** 110 复位子程序 111 ***********************************************************/ 112 unsigned char reset() 113 { 114 unsigned char presence; 115 EA=0; 116 DQ=0; /*拉低DQ总线开始复位*/ 117 delay(30); /*保持低电平480us*/ 118 DQ=1; /*释放总线*/ 119 delay(3); 120 presence=DQ; /*获取应答信号*/ 121 delay(28); /*延时以完成整个时序*/ 122 EA=1; 123 return(presence); /*返回应答信号,有芯片应答返回0,无芯片则返回1*/ 124 } 125 /*********************************************************** 126 获取温度子程序 127 ***********************************************************/ 128 void get_temper() 129 { 130 unsigned char i,j; 131 do 132 { 133 i=reset(); /*复位*/ 134 } while(i!=0); /*1为无反馈信号*/ 135 i=0xcc; /*发送设备定位命令*/ 136 write_byte(i); 137 i=0x44; /*发送开始转换命令*/ 138 write_byte(i); 139 delay(180); /*延时*/ 140 do 141 { 142 i=reset(); /*复位*/ 143 } while(i!=0); 144 i=0xcc; /*设备定位*/ 145 write_byte(i); 146 i=0xbe; /*读出缓冲区内容*/ 147 write_byte(i); 148 j=read_byte(); 149 i=read_byte(); 150 i=(i<<4)&0x7f; 151 s=(unsigned int)(j&0x0f); //得到小数部分 152 s=(s*100)/16; 153 j=j>>4; 154 temper=i|j; /*获取的温度放在temper中*/ 155 } 156 /*==================================================================================================== 157 Initialize PID Structure 158 =====================================================================================================*/ 159 void PIDInit (struct PID *pp) 160 { 161 memset ( pp,0,sizeof(struct PID)); //全部初始化为0 162 } 163 /*==================================================================================================== 164 PID计算部分 165 =====================================================================================================*/ 166 unsigned int PIDCalc( struct PID *pp, unsigned int NextPoint ) 167 { 168 unsigned int dError,Error; 169 Error = pp->SetPoint - NextPoint; // 偏差 170 pp->SumError += Error; // 积分 171 dError = pp->LastError - pp->PrevError; // 当前微分 172 pp->PrevError = pp->LastError; 173 pp->LastError = Error; 174 return (pp->Proportion * Error // 比例项 175 + pp->Integral * pp->SumError // 积分项 176 + pp->Derivative * dError); // 微分项 177 } 178/*********************************************************** 179 温度比较处理子程序 180***********************************************************/ 181 void compare_temper() 182 { 183 unsigned char i; 184 if(set_temper>temper) //是否设置的温度大于实际温度 185 { 186 if(set_temper-temper>1) //设置的温度比实际的温度是否是大于1度 187 { 188 high_time=100; //如果是,则全速加热 189 low_time=0; 190 } 191 else //如果是在1度范围内,则运行PID计算 192 { 193 for(i=0;i<10;i++) 194 { 195 get_temper(); //获取温度 196 rin = s; // Read Input 197 rout = PIDCalc ( &spid,rin ); // Perform PID Interation 198 } 199 if (high_time<=100) 200 high_time=(unsigned char)(rout/800); 201 else 202 high_time=100; 203 low_time= (100-high_time); 204 } 205 } 206 else if(set_temper<=temper) 207 { 208 if(temper-set_temper>0) 209 { 210 high_time=0; 211 low_time=100; 212 } 213 else 214 { 215 for(i=0;i<10;i++) 216 { 217 get_temper(); 218 rin = s; // Read Input 219 rout = PIDCalc ( &spid,rin ); // Perform PID Interation 220 } 221 if (high_time<100) 222 high_time=(unsigned char)(rout/10000); 223 else 224 high_time=0; 225 low_time= (100-high_time); 226 } 227 } 228 // else 229 // {} 230 } 231 /***************************************************** 232 T0中断服务子程序,用于控制电平的翻转 ,40us*100=4ms周期 233 ******************************************************/ 234 void serve_T0() interrupt 1 using 1 235 { 236 if(++count<=(high_time)) 237 output=1; 238 else if(count<=100) 239 { 240 output=0; 241 } 242 else 243 count=0; 244 TH0=0x2f; 245 TL0=0xe0; 246 } 247 /***************************************************** 248 串行口中断服务程序,用于上位机通讯 249 ******************************************************/ 250 void serve_sio() interrupt 4 using 2 251 { 252 /* EA=0; 253 RI=0; 254 i=SBUF; 255 if(i==2) 256 { 257 while(RI==0){} 258 RI=0; 259 set_temper=SBUF; 260 SBUF=0x02; 261 while(TI==0){} 262 TI=0; 263 } 264 else if(i==3) 265 { 266 TI=0; 267 SBUF=temper; 268 while(TI==0){} 269 TI=0; 270 } 271 EA=1; */ 272 } 273 void disp_1(unsigned char disp_num1[6]) 274 { 275 unsigned char n,a,m; 276 for(n=0;n<6;n++) 277 { 278 // k=disp_num1[n]; 279 for(a=0;a<8;a++) 280 { 281 clk=0; 282 m=(disp_num1[n]&1); 283 disp_num1[n]=disp_num1[n]>>1; 284 if(m==1) 285 data1=1; 286 else 287 data1=0; 288 _nop_(); 289 clk=1; 290 _nop_(); 291 } 292 } 293 } 294/***************************************************** 295 显示子程序 296 功能:将占空比温度转化为单个字符,显示占空比和测得到的温度 297 ******************************************************/ 298 void display() 299 { 300 unsigned char code number[]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6}; 301 unsigned char disp_num[6]; 302 unsigned int k,k1; 303 k=high_time; 304 k=k%1000; 305 k1=k/100; 306 if(k1==0) 307 disp_num[0]=0; 308 else 309 disp_num[0]=0x60; 310 k=k%100; 311 disp_num[1]=number[k/10]; 312 disp_num[2]=number[k%10]; 313 k=temper; 314 k=k%100; 315 disp_num[3]=number[k/10]; 316 disp_num[4]=number[k%10]+1; 317 disp_num[5]=number[s/10]; 318 disp_1(disp_num); 319 } 320 /*********************************************************** 321 主程序 322 ***********************************************************/ 323 void main() 324 { 325 unsigned char z; 326 unsigned char a,b,flag_2=1,count1=0; 327 unsigned char phil[]={2,0xce,0x6e,0x60,0x1c,2}; 328 TMOD=0x21; 329 TH0=0x2f; 330 TL0=0x40; 331 SCON=0x50; 332 PCON=0x00; 333 TH1=0xfd; 334 TL1=0xfd; 335 PS=1; 336 EA=1; 337 EX1=0; 338 ET0=1; 339 ES=1; 340 TR0=1; 341 TR1=1; 342 high_time=50; 343 low_time=50; 344 PIDInit ( &spid ); // Initialize Structure 345 spid.Proportion = 10; // Set PID Coefficients 比例常数 Proportional Const 346 spid.Integral = 8; //积分常数 Integral Const 347 spid.Derivative =6; //微分常数 Derivative Const 348 spid.SetPoint = 100; // Set PID Setpoint 设定目标 Desired Value 349 while(1) 350 { 351 if(plus==0) 352 { 353 EA=0; 354 for(a=0;a<5;a++) 355 for(b=0;b<102;b++){} 356 if(plus==0) 357 { 358 set_temper++; 359 flag=0; 360 } 361 } 362 else if(subs==0) 363 { 364 for(a=0;a<5;a++) 365 for(b=0;a<102;b++){} 366 if(subs==0) 367 { 368 set_temper--; 369 flag=0; 370 } 371 } 372 else if(stop==0) 373 { 374 for(a=0;a<5;a++) 375 for(b=0;b<102;b++){} 376 if(stop==0) 377 { 378 flag=0; 379 break; 380 } 381 EA=1; 382 } 383 get_temper(); 384 b=temper; 385 if(flag_2==1) 386 a=b; 387 if((abs(a-b))>5) 388 temper=a; 389 else 390 temper=b; 391 a=temper; 392 flag_2=0; 393 if(++count1>30) 394 { 395 display(); 396 count1=0; 397 } 398 compare_temper(); 399 } 400 TR0=0; 401 z=1; 402 while(1) 403 { 404 EA=0; 405 if(stop==0) 406 { 407 for(a=0;a<5;a++) 408 for(b=0;b<102;b++){} 409 if(stop==0) 410 disp_1(phil); 411 // break; 412 } 413 EA=1; 414 } 415 }
这个程序spid.SetPoint = 100;
Set PID Setpoint 设定目标 Desired Value是什么意思,
上面的eet_temper=35;
难道这个spid.SetPoint = 100是指35-34=1度的温差扩大100倍?