PID参数整定,新手求教

2019-07-15 16:04发布

#include  "msp430x14x.h"
#include  "cry12864.h"
#include  "12864.c"

#define   Num_of_Results   32
/*typedef unsigned char uchar;
typedef unsigned int  uint;*/

/************************PID结构体和函数声明**********************/
struct PID
{
float SetSpeed;    //定义设定值
float ActualSpeed; //定义实际值
float err;         //定义偏差值
float err_next;    //定义上一个偏差值
float err_last;    //定义最上前的偏差值
float Kp,Ki,Kd;    //定义比例、积分、微分系数
}pid;

void PID_init();
void int_clk();
void int_pwm();
void init_TB(void);

float PID_realize(float speed);
uint tamp;


void int_ADC(void);

/*******************12864显示声明******************/
uint T=20;//初始化目标温度
const uchar hang1[] = {"温度测量与控制 "};
const uchar hang2[] = {"当前电压:"};
const uchar hang3[] = {"当前温度:"};
const uchar hang4[] = {"目标温度:"};
uint wendu;
unsigned long sum = 0;
void mubiaowendu(uint T);
static uint results[Num_of_Results];    //保存ADC转换结果的数组           
void Trans_val(uint Hex_Val);
uchar shuzi[] = {"0123456789.V℃:"};

/************按键声明************/
void P1_IODect();
void P10_Onclick();
void P11_Onclick();
void P12_Onclick();
void P13_Onclick();
void GPIO_init();

/***************************主函数*************************/
void main( void )
{

    /*下面六行程序关闭所有的IO口*/
    P1DIR = 0XFF;P1OUT = 0XFF;
    P2DIR = 0XFF;P2OUT = 0XFF;
    P3DIR = 0XFF;P3OUT = 0XFF;
    P4DIR = 0XFF;P4OUT = 0XFF;
    P5DIR = 0XFF;P5OUT = 0XFF;
    P6DIR = 0XFF;P6OUT = 0XFF;

    WDTCTL = WDTPW + WDTHOLD;    //关狗
    P6DIR |= BIT2;P6OUT |= BIT2; //关闭电平转换
    Ini_Lcd();                  //初始化液晶
    mubiaowendu(T);
    Disp_HZ(0x80,hang1,8);     //第一行显示“温度测量与控制”

    GPIO_init();//IO初始化
    int_clk();
    PID_init();
    int_pwm();
    int_ADC();
     init_TB();
    _EINT();//使能总中断

  while(1)
{
        pid.ActualSpeed = wendu/100.0;      //当前温度
        if(pid.ActualSpeed<=(T-10))        
           TBCCR1=0;               //如果温差小于10度就全速加热   
        else                    //温差大于10度启用PID控制
           TACCR1=800-(uint)tamp*10;        
}

}

/*******************************************
函数名称:ADC12ISR
功    能:ADC中断服务函数,在这里用多次平均的
          计算P6.0口的模拟电压数值
参    数:无      
返回值  :无
********************************************/
#pragma vector=ADC_VECTOR
__interrupt void ADC12ISR (void)
{
  static uint index = 0;

  results[index++] = ADC12MEM0;               // Move results
  if(index == Num_of_Results)
  {
        uchar i;


        index = 0;
        for(i = 0; i < Num_of_Results; i++)
        {
            sum += results[i];
        }
        sum >>= 5;                            //除以32

        Trans_val(sum);
  }
}

/*******************************************
函数名称:Trans_val
功    能:将16进制ADC转换数据变换成三位10进制
          真实的模拟电压数据,并在液晶上显示
参    数:Hex_Val--16进制数据
          n--变换时的分母等于2的n次方      
返回值  :无
********************************************/
void Trans_val(uint Hex_Val)
{
    unsigned long caltmp;
    uint Curr_Volt;
    uint a=137;//温度系数
    uchar t1,i,j;
    uchar ptr[5];
    uchar tmp[6];


    caltmp = Hex_Val;
    caltmp = (caltmp << 5) + Hex_Val;           //caltmp = Hex_Val * 33
    caltmp = (caltmp << 3) + (caltmp << 1);     //caltmp = caltmp * 10
    Curr_Volt = caltmp >> 12;                   //Curr_Volt = caltmp / 2^n
    ptr[0] = Curr_Volt / 100;                   //Hex->Dec变换
    t1 = Curr_Volt - (ptr[0] * 100);
    ptr[2] = t1 / 10;
    ptr[3] = t1 - (ptr[2] * 10);
    ptr[1] = 10; //shuzi表中第10位对应符号"."
    ptr[4] = 11; //第11位表示符号"V"

    //在液晶上显示变换后的结果
   wendu = ((Curr_Volt)-100)*a;

   tmp[5]=12;//第12位表示符号"℃"
   tmp[4]=wendu%10;
   tmp[3]=wendu/10%10;
   tmp[2]=10;
   tmp[1]=wendu/100%10;
   tmp[0]=wendu/1000%10;


   Disp_HZ(0x90,hang2,5);
     for(i=0;i<5;i++)
     {                       //这部分显示第二行的“当前电压:”和数值
       Delay_Nms(10);
      Write_Data(shuzi[ptr[i]]);
     }

      Disp_HZ(0x88,hang3,5);
      for(j=0;j<6;j++)
     {                       //这部分显示第三行的“当前温度:”和数值
       Delay_Nms(10);
      Write_Data(shuzi[tmp[j]]);
     }

}


/**********在液晶上第四行显示“目标温度:”和数值*********/
void mubiaowendu(uint T)
{
   uchar k;
   uchar MBWD[6];
   MBWD[5]=12;//第12位表示符号"℃"
   MBWD[1]=T%10;
   MBWD[0]=T/10%10;
   MBWD[2]=10;
   MBWD[3]=T/100%10;
   MBWD[4]=T/1000%10;

   Disp_HZ(0x98,hang4,5);
    for(k=0;k<6;k++)
     {
       Delay_Nms(10);
      Write_Data(shuzi[MBWD[k]]);
     }
}

/********************按键部分处理函数*********************/
void GPIO_init()
{
//-----设定P1.6的输出初始值-----
//P1DIR |= BIT6;//设定P1.6为输出
//P1OUT &= ~BIT6;//设定P1.6初值
//-----配置P1.3中断参数-----
P1DIR &= ~(BIT0+BIT1+BIT2+BIT3); //设P1.0~P1.3为输入
P1IES |= (BIT0+BIT1+BIT2+BIT3); // P1.0~P1.3设为下降沿中断
P1IE |= (BIT0+BIT1+BIT2+BIT3) ; // 允许P1.0~P1.3中断
}

#pragma vector = PORT1_VECTOR
__interrupt void PORT1_ISR(void)
{
//-----启用Port1事件检测函数-----
P1_IODect();//事件检测通过,则会调用事件处理函数
P1IFG=0; //退出中断前必须手动清除IO口中断标志
}

void P1_IODect()
{
uint Push_Key=0;
//-----排除输出IO的干扰后,锁定唯一被触发的中断标志位-----
Push_Key = P1IFG&(~P1DIR);
//-----延时一段时间,避开机械抖动区域-----
__delay_cycles(10000);
//消抖延时
//----判断按键状态是否与延时前一致-----
if((P1IN&Push_Key)==0)
//如果该次按键确实有效
{
//----判断具体哪个IO被按下,调用该IO的事件处理函数-----
switch(Push_Key){

case BIT0: P10_Onclick();
break;

case BIT1: P11_Onclick();
break;

case BIT2: P12_Onclick();
break;

case BIT3: P13_Onclick();
break;

default:
break;
//任何情况下均加上default
    }
  }
}

void P10_Onclick()
{
T=40;
mubiaowendu(T);
}

void P11_Onclick()
{
T=60;
mubiaowendu(T);
}

void P12_Onclick()
{
T++;
mubiaowendu(T);
}

void P13_Onclick()
{
T--;
mubiaowendu(T);
}

/********************初始化ADC****************************/
void int_ADC(void)
{
  /*P5DIR|=BIT5;P5OUT&=~BIT5;               //关闭数码管显示
  P6DIR|=BIT6;P6OUT&=~BIT6;                 //半闭数码管显示
  P6DIR|=BIT5;P6OUT&=~BIT5;*/                 //半闭数码管显示  
  P6SEL |= 0x01;                            // 使能ADC通道P6.0
  ADC12CTL0 = ADC12ON+SHT0_8+MSC;           // 打开ADC,设置采样时间
  ADC12CTL1 = SHP+CONSEQ_2;                 // 使用采样定时器
  ADC12IE = 0x01;                           // 使能ADC中断
  ADC12CTL0 |= ENC;                         // 使能转换
  ADC12CTL0 |= ADC12SC;                     // 开始转换
}

/*************************初始化时钟*********************************/
void int_clk()
{
    uchar i;
    BCSCTL1&=~XT2OFF;  //打开XT振荡器
    BCSCTL2|=SELM1+SELS;//MCLK 8M and SMCLK 1M  
    do
        {
        IFG1 &= ~OFIFG;                 //清除振荡错误标志
        for(i = 0; i < 100; i++)
          _NOP();                        //延时等待
        }
    while ((IFG1 & OFIFG) != 0);        //如果标志为1继续循环等待
    IFG1&=~OFIFG;
}

/*************************初始化PWM*****************************/
void int_pwm()
{
  P2DIR |= 0x02;                           
  P2SEL |= 0x02;                           
  TACCR0 = 800;                             // PWM Period/2
  TACCTL1 = OUTMOD_7;                       // CCR1 toggle/set
  TACCR1 = 400;                              // CCR1 PWM duty cycle
  TACTL = TASSEL_2 + MC0;                  // ACLK, up-down mode
}

/************************Time_B初始化*************************/
void init_TB(void)
{
  TBCCTL0 = CCIE;                           // TBCCR0 interrupt enabled
  TBCCR0 = 20000;
  TBCTL = TBSSEL_2 + MC_1;                  // SMCLK, upmode
}

/************************Time_B中断*************************/
#pragma vector=TIMERB0_VECTOR
__interrupt void Timer_B (void)
{      
          tamp = (uint)PID_realize(T);
}

/*****************PID变量初始化函数**************************/
void PID_init()
{
  pid.SetSpeed=0.0;
  pid.ActualSpeed=0.0;
  pid.err=0.0;
  pid.err_last=0.0;
  pid.err_next=0.0;
  pid.Kp=1.0;
  pid.Ki=0.0;
  pid.Kd=0.0;
}

/*************************PID运算函数 ***************************/
float PID_realize(float speed)
{
  float incrementSpeed;
  pid.SetSpeed=speed;
  pid.err=pid.SetSpeed-pid.ActualSpeed;
  incrementSpeed=pid.Kp*(pid.err)+pid.Ki*pid.err+pid.Kd*(pid.err-2*pid.err_next+pid.err_last);
  pid.ActualSpeed+=incrementSpeed;
  pid.err_last=pid.err_next;
  pid.err_next=pid.err;
  return incrementSpeed;
}

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
2条回答
番茄味的猫
1楼-- · 2019-07-15 19:00
 精彩回答 2  元偷偷看……
michael_llh
2楼-- · 2019-07-16 00:13
PID参数整定有很多的参考文档,你可以在百度上面查到。但是也都是一些经验心得,具体还需要自己实际调才明白,一般是先整定P,到达系统平衡点的时候减小到原来的60%到80%,然后加上D直到系统稳定,如果此时系统稳定值和实际值由偏差,可以加入一点I,但是这个I的量一定要很小,因为是累加,所以不能太大,必要时还要加入清零

一周热门 更多>