陆羽泡的茶

  • 352 主题
  • 364 帖子
  • 4683 积分
  • 身份:LV5 资深技术员
  • E币:1792

写给新手:如何写一个标准的PID算法

2018-7-19 14:52:58 显示全部楼层
timg.jpg
写在前面的话:
这是我收藏的一个典型的PID处理程序,包含了最常用的PID算法的基本架构,没有包含输入输出处理部分。适合新手了解PID结构,入门学习用。
注意:
使用不同的MCU的时候,需要进行相应的简化和改写。而且由于单片机的处理速度和ram资源的限制,一般不采用浮点数运算。而将所有参数全部用整数,运算到最后再除以一个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 Structure
  • unsigned 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; /*移位操作,将本次要写的位移到最低位*/
  •           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;
  • }
  • }

最新评论

楼层直达:
我要评论
0
3
广告
关闭 热点推荐上一条 /1 下一条
快速回复 返回列表