#include <REGX51.H>#include<stdio.h>
#define uchar unsigned char
#define uint unsigned int
#define THC0 0xf8
#define TLC0 0x50 //2ms,0x30,含中断处理时间时,0x50
#include "LCD.H"
#include "delay.h"
unsigned char aa[]={'T','a','r','g','e','t',' ',' ',' ',' ',' ','r','/','m','i','n'}; //目标转速。Target r/min
unsigned char cc[]={'A','c','t','u','a','l',' ',' ',' ',' ',' ','r','/','m','i','n'}; //实测转速: Actual r/min
uchar flag;
uchar i=0;
sbit PWM_FC=P3^3;
sbit IN1=P3^0;
sbit IN2=P3^1;
int e ,e1 ,e2 ;//pid 偏差
float uk ,uk1 ,duk ;//pid输出值
float Kp=0.36,Ki=0.05,Kd=0.016;//pid控制系数 0.1 0.05 0.016
/*
PID的参数设置可以参照以下来进行:
参数整定找最佳,从小到大顺序查;
先是比例后积分,最后再把微分加;
曲线振荡很频繁,比例度盘要放大;
曲线漂浮绕大湾,比例度盘往小扳;
曲线偏离回复慢,积分时间往下降;
曲线波动周期长,积分时间再加长;
曲线振荡频率快,先把微分降下来;
动差大来波动慢。微分时间应加长;
理想曲线两个波,前高后低4比1 ;
一看二调多分析,调节质量不会低;
*/
int out=0;
uint SpeedSet=2000;
uint cnt=0;
uint Inpluse=0,num=0,zs;//脉冲计数
uint PWMTime=100;//脉冲宽度
void PIDControl();
void SystemInit();
void delay(uchar x);
void PWMOUT();
void SetSpeed();
/**************主函数************/
void main()
{
SystemInit();
init();
LCD_Write_String(0,0,aa);
zs=1;
while(1)
{
if(flag==1)
{
flag=0;
LCD_Write_String(0,0,aa);
}
if(zs==1)
{
zs=0;
cc[7]=num/1000+'0';
cc[8]=num/100%10+'0';
cc[9]=num/10%10+'0';
cc[10]=num%10+'0';
LCD_Write_String(0,1,cc);
}
}
}
void PIDControl() //pid偏差计算
{
e=SpeedSet-num;
duk=(Kp*(e-e1)+Ki*e+Kd*(e-2*e1+e2));
uk=uk1+duk;
out=(int)uk;
if(out>1000)
{
out=1000;
}
else if(out<0)
{
out=0;
}
uk1=uk;
e2=e1;
e1=e;
PWMTime=out;
}
void delay(uchar x)
{
uint i,j;
for(i=x;i>0;i--)
for(j=50;j>0;j--);
}
void PWMOUT()
{
if(cnt<PWMTime)
{
PWM_FC=1;
}
else
{
PWM_FC=0;
}
if(cnt>1000) cnt=0;
}
void SystemInit()
{
TMOD=0X21; //t1用来串口t2定时
TH0=THC0;
TL0=TLC0;
TH1=0xC0;
TL1=0XC0;
ET1=1;
ET0=1;
TR0=1;
TR1=1;
EX0=1; //中断0用来测量转速
IT0=1;
EA=1;
e =0;
e1=0;
e2=0;
IN1=1;
IN2=1;
}
uchar Key_Scan(void)
{
static uchar State_Cnt=0;//静态变量,用于改变状态过程
uchar Key_State = 0;//用于存储键值码
switch(State_Cnt)
{
case 0x00:P1 = 0x1f;//先往P3(1到4独立按键)口送0001 1111
if(P1 != 0x1f)//有无按键被按下
{
State_Cnt = 0x01;//改变状态
break;
}
case 0x01:
if(P1 != 0x1f)//经过定时器延时后,再次判断按键是否按下
{
State_Cnt = 0x02;//改变状态
Key_State = P1;//把键值保存下来
P1 = 0x1f;//恢复P3,以便下次按下重新保存键值
break;
}
else
{
State_Cnt = 0x00;//改变状态
break;
}
case 0x02:
if(P1 == 0x1f)//判断按键释放
{
State_Cnt = 0x00;//改变状态
break;
}
default:break;
}
return Key_State;//返回键值
}
void Display_Key(uchar key_value)
{
switch(key_value)
{
case 0x1e:
IN1=0;
IN2=1;break;//键值改变时,做出相应的动作(用户自定义动作)
case 0x1d:
IN1=1;
IN2=1;break;
case 0x1b:
IN1=~IN1;
IN2=~IN2;
break;
case 0x17:
SpeedSet+=10;
if(SpeedSet>3500)
{
SpeedSet=3500;
}
break;
case 0x0f:
SpeedSet-=10;
if(SpeedSet<0) SpeedSet=0;
break;
}
}
void int0() interrupt 0
{
Inpluse++;
}
void t0() interrupt 1
{
static unsigned int time=0,time1=0;
TH0=THC0;
TL0=TLC0;
time++; //转速测量周期
time1++;
if(time1>=5)
{
time1=0;
flag=1;
Display_Key(Key_Scan());//按键按下,键值码送入函数,以便做相应动作
aa[7]=SpeedSet/1000+'0';
aa[8]=SpeedSet/100%10+'0';
aa[9]=SpeedSet/10%10+'0';
aa[10]=SpeedSet%10+'0';
}
if(time>500)
{
zs=1;
time=0;
num=Inpluse; //计算式中是仿真时,码盘数60时的情况,如果码盘数n=10时,num=Inpluse*60/n=Inpluse*6;
Inpluse=0;
PIDControl();
}
PWMOUT();
}
void timer_1() interrupt 3
{
cnt++; //cnt越大占空比越高2.5Khz
}
|