写在前面的话:
这是我收藏的一个典型的pid处理程序,包含了zui常用的pid算法的基本架构,没有包含输入输出处理部分。适合新手了解pid结构,入门学习用。
注意:
使用不同的mcu的时候,需要进行相应的简化和改写。而且由于单片机的处理速度和ram资源的限制,一般不采用浮点数运算。而将所有参数全部用整数,运算到zui后再除以一个2的n次方数据(相当于移位),作类似定点数运算。这样可大大提高运算速度,根据控制精度的不同要求,当精度要求很高时,注意保留移位引起的“余数”,做好余数补偿就好了。
废话不多说,下面上程序
#include<reg51.h>#include<intrins.h>#include<math.h>#include<string.h>struct pid {  unsigned int setpoint; // 设定目标 desired value  unsigned int proportion; // 比例常数 proportional const  unsigned int integral; // 积分常数 integral const  unsigned int derivative; // 微分常数 derivative const  unsigned int lasterror; // error[-1]  unsigned int preverror; // error[-2]  unsigned int sumerror; // sums of errors  };struct pid spid; // pid control structureunsigned int rout; // pid response (output)unsigned int rin; // pid feedback (input)sbit data1=p1^0;sbit clk=p1^1;sbit plus=p2^0;sbit subs=p2^1;sbit stop=p2^2;sbit output=p3^4;sbit dq=p3^3;unsigned char flag,flag_1=0;unsigned char high_time,low_time,count=0;//占空比调节参数unsigned char set_temper=35;unsigned char temper;unsigned char i;unsigned char j=0;unsigned int s;  /***********************************************************  延时子程序,延时时间以12m晶振为准,延时时间为30us×time  ***********************************************************/void delay(unsigned char time)  {    unsigned char m,n;    for(n=0;n<time;n++)    for(m=0;m<2;m++){}  }  /***********************************************************  写一位数据子程序  ***********************************************************/void write_bit(unsigned char bitval){    ea=0;    dq=0; /*拉低dq以开始一个写时序*/  if(bitval==1)  {    _nop_();    dq=1; /*如要写1,则将总线置高*/  }   delay(5); /*延时90us供da18b20采样*/   dq=1; /*释放dq总线*/  _nop_();  _nop_();  ea=1;}  /***********************************************************  写一字节数据子程序  ***********************************************************/void write_byte(unsigned char val){    unsigned char i;    unsigned char temp;    ea=0;    tr0=0;  for(i=0;i<8;i++) /*写一字节数据,一次写一位*/  {    temp=val>>i; /*移位操作,将本次要写的位移到zui低位*/    temp=temp&1;    write_bit(temp); /*向总线写该位*/  }    delay(7); /*延时120us后*/  // tr0=1;    ea=1;}  /***********************************************************  读一位数据子程序  ***********************************************************/unsigned char read_bit(){  unsigned char i,value_bit;  ea=0;  dq=0; /*拉低dq,开始读时序*/  _nop_();  _nop_();  dq=1; /*释放总线*/  for(i=0;i<2;i++){}  value_bit=dq;  ea=1;  return(value_bit);}  /***********************************************************  读一字节数据子程序  ***********************************************************/unsigned char read_byte(){  unsigned char i,value=0;  ea=0;  for(i=0;i<8;i++)  {  if(read_bit()) /*读一字节数据,一个时序中读一次,并作移位处理*/  value|=0x01<<i;  delay(4); /*延时80us以完成此次都时序,之后再读下一数据*/  }  ea=1;  return(value);}  /***********************************************************  复位子程序  ***********************************************************/unsigned char reset(){  unsigned char presence;  ea=0;  dq=0; /*拉低dq总线开始复位*/  delay(30); /*保持低电平480us*/  dq=1; /*释放总线*/  delay(3);  presence=dq; /*获取应答信号*/  delay(28); /*延时以完成整个时序*/  ea=1;  return(presence); /*返回应答信号,有芯片应答返回0,无芯片则返回1*/}  /***********************************************************  获取温度子程序  ***********************************************************/void get_temper(){  unsigned char i,j;  do  {   i=reset(); /*复位*/  }while(i!=0); /*1为无反馈信号*/    i=0xcc; /*发送设备定位命令*/   write_byte(i);   i=0x44; /*发送开始转换命令*/   write_byte(i);   delay(180); /*延时*/  do  {   i=reset(); /*复位*/  }while(i!=0);   i=0xcc; /*设备定位*/   write_byte(i);   i=0xbe; /*读出缓冲区内容*/   write_byte(i);   j=read_byte();    i=read_byte();   i=(i<<4)&0x7f;   s=(unsigned int)(j&0x0f);    //得到小数部分   s=(s*100)/16;   j=j>>4;   temper=i|j; /*获取的温度放在temper中*/  }  /*====================================================================================================  initialize pid structure  =====================================================================================================*/void pidinit (struct pid *pp){  memset ( pp,0,sizeof(struct pid));   //全部初始化为0}  /*====================================================================================================  pid计算部分  =====================================================================================================*/unsigned int pidcalc( struct pid *pp, unsigned int nextpoint ){  unsigned int derror,error;  error = pp->setpoint - nextpoint;    // 偏差     pp->sumerror += error;       // 积分             derror = pp->lasterror - pp->preverror;  // 当前微分  pp->preverror = pp->lasterror;           pp->lasterror = error;               return (pp->proportion * error     // 比例项     + pp->integral * pp->sumerror    // 积分项  + pp->derivative * derror);      // 微分项}  /***********************************************************  温度比较处理子程序  ***********************************************************/void compare_temper(){  unsigned char i;  if(set_temper>temper)  //是否设置的温度大于实际温度  {   if(set_temper-temper>1)   //设置的温度比实际的温度是否是大于1度    {   high_time=100;       //如果是,则全速加热   low_time=0;    }   else             //如果是在1度范围内,则运行pid计算    {    for(i=0;i<10;i++)    {    get_temper();        //获取温度    rin = s; // read input    rout = pidcalc ( &spid,rin ); // perform pid interation    }    if (high_time<=100)    high_time=(unsigned char)(rout/800);    else    high_time=100;    low_time= (100-high_time);    }  }  else if(set_temper<=temper)  {   if(temper-set_temper>0)    {    high_time=0;    low_time=100;    }   else    {     for(i=0;i<10;i++)   {   get_temper();   rin = s; // read input     rout = pidcalc ( &spid,rin ); // perform pid interation   }     if (high_time<100)    high_time=(unsigned char)(rout/10000);     else    high_time=0;    low_time= (100-high_time);    }  }  // else  // {}}  /*****************************************************  t0中断服务子程序,用于控制电平的翻转 ,40us*100=4ms周期  ******************************************************/void serve_t0() interrupt 1 using 1{  if(++count<=(high_time))  output=1;  else if(count<=100)  {  output=0;  }  else  count=0;  th0=0x2f;  tl0=0xe0;}  /*****************************************************  串行口中断服务程序,用于上位机通讯  ******************************************************/void serve_sio() interrupt 4 using 2{  /* ea=0;  ri=0;  i=sbuf;  if(i==2)  {  while(ri==0){}  ri=0;  set_temper=sbuf;  sbuf=0x02;  while(ti==0){}  ti=0;  }  else if(i==3)  {  ti=0;  sbuf=temper;  while(ti==0){}  ti=0;  }  ea=1; */}void disp_1(unsigned char disp_num1[6]){  unsigned char n,a,m;  for(n=0;n<6;n++)  {  // k=disp_num1[n];   for(a=0;a<8;a++)   {    clk=0;    m=(disp_num1[n]&1);    disp_num1[n]=disp_num1[n]>>1;    if(m==1)   data1=1;    else   data1=0;   _nop_();   clk=1;   _nop_();   }  }}  /*****************************************************  显示子程序  功能:将占空比温度转化为单个字符,显示占空比和测得到的温度  ******************************************************/void display(){  unsigned char code number[]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6};  unsigned char disp_num[6];  unsigned int k,k1;  k=high_time;  k=k%1000;  k1=k/100;  if(k1==0)  disp_num[0]=0;  else  disp_num[0]=0x60;  k=k%100;  disp_num[1]=number[k/10];  disp_num[2]=number[k%10];  k=temper;  k=k%100;  disp_num[3]=number[k/10];  disp_num[4]=number[k%10]+1;  disp_num[5]=number[s/10];  disp_1(disp_num);}  /***********************************************************  主程序  ***********************************************************/void main(){  unsigned char z;  unsigned char a,b,flag_2=1,count1=0;  unsigned char phil[]={2,0xce,0x6e,0x60,0x1c,2};  tmod=0x21;  th0=0x2f;  tl0=0x40;  scon=0x50;  pcon=0x00;  th1=0xfd;  tl1=0xfd;  ps=1;  ea=1;  ex1=0;  et0=1;  es=1;  tr0=1;  tr1=1;  high_time=50;  low_time=50;  pidinit ( &spid );  // initialize structure  spid.proportion = 10; // set pid coefficients比例常数 proportional const  spid.integral = 8;  //积分常数 integral const  spid.derivative =6; //微分常数 derivative const  spid.setpoint = 100; // set pid setpoint 设定目标 desired value  while(1){  if(plus==0){  ea=0;  for(a=0;a<5;a++)  for(b=0;b<102;b++){}  if(plus==0){  set_temper++;  flag=0;}}  else if(subs==0){  for(a=0;a<5;a++)  for(b=0;a<102;b++){}  if(subs==0)  {   set_temper--;   flag=0;  }}  else if(stop==0)  {    for(a=0;a<5;a++)    for(b=0;b<102;b++){}    if(stop==0)  {   flag=0;   break;  }   ea=1;  }   get_temper();   b=temper;  if(flag_2==1)    a=b;  if((abs(a-b))>5)    temper=a;  else    temper=b;    a=temper;    flag_2=0;  if(++count1>30)  {    display();    count1=0;  }    compare_temper();  }   tr0=0;   z=1;  while(1)  {    ea=0;  if(stop==0)  {    for(a=0;a<5;a++)    for(b=0;b<102;b++){}    if(stop==0)    disp_1(phil);  // break;  }  ea=1;}}复制代码
   
 
   