28KD8JA)_F%`)3QTK$)}2Y2.png (43.23 KB, 下載次數: 32)
下載附件
2020-4-26 10:06 上傳
#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
}
|