#include <REGX51.H>
#include<intrins.h>
#include<stdio.h>
#define uchar unsigned char
#define uint unsigned int
#define THC0 0xf8
#define TLC0 0x50 //2ms,0x30,含中斷處理時間時,0x50
#define RS_CLR RS=0
#define RS_SET RS=1
#define RW_CLR RW=0
#define RW_SET RW=1
#define EN_CLR EN=0
#define EN_SET EN=1
sbit RS = P2^0; //定義端口
sbit RW = P2^1;
sbit EN = P2^2;
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 i=0;
sbit k1=P3^3;
sbit k2=P3^4;
sbit k3=P3^5;
sbit k4=P3^6;
sbit k5=P3^7;
sbit PWM_FC=P1^0;
sbit IN1=P1^1;
sbit IN2=P1^2;
int out=0;
uint SpeedSet=3000;
uint cnt=0;
uint Inpluse=0,num=0,zs;//脈沖計數
uint PWMTime=100;//脈沖寬度
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 ;
一看二調多分析,調節質量不會低;
*/
void PIDControl();
void SystemInit();
void delay(uchar x);
void PWMOUT();
void SetSpeed();
void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s);
void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data);
void init();
void write_com(unsigned char com);
void write_data(unsigned char date);
/*------------------------------------------------
uS延時函數,含有輸入參數 unsigned char t,無返回值
unsigned char 是定義無符號字符變量,其值的范圍是
0~255 這里使用晶振12M,精確延時請使用匯編,大致延時
長度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t);
/*------------------------------------------------
mS延時函數,含有輸入參數 unsigned char t,無返回值
unsigned char 是定義無符號字符變量,其值的范圍是
0~255 這里使用晶振12M,精確延時請使用匯編
------------------------------------------------*/
void DelayMs(unsigned char t);
void DelayUs2x(unsigned char t)
{
while(--t);
}
void DelayMs(unsigned char t)
{
while(t--) //大致延時1mS
{
DelayUs2x(245);
}
}
void write_com(unsigned char com) //寫命令
{
RS_CLR;
RW_CLR;
P0=com;
DelayMs(3);
EN_SET;
DelayMs(3);
EN_CLR;
}
void write_data(unsigned char date) //寫一個字符
{
RS_SET;
RW_CLR;
P0=date;
DelayMs(3);
EN_SET;
DelayMs(3);
EN_CLR;
}
void init() //初始化
{
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
}
/*------------------------------------------------
寫入字符串函數
------------------------------------------------*/
void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)
{
if (y == 0)
{
write_com(0x80 + x);
}
else
{
write_com(0xC0 + x);
}
while (*s)
{
write_data( *s);
s ++;
}
}
/**************主函數************/
void main()
{
SystemInit();
init();
LCD_Write_String(0,0,aa);
zs=1;
while(1)
{
SetSpeed();
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;
}
void SetSpeed()
{
if(k1==0)
{
delay(100);
if(k1==0)
{
IN1=0;
IN2=1;
}
}
if(k2==0)
{
delay(100);
if(k2==0)
{
IN1=1;
IN2=1;
}
}
if(k3==0)
{
delay(100);
if(k3==0)
{
IN1=~IN1;
IN2=~IN2;
}
while(k3==0);
}
if(k4==0)
{
delay(100);
if(k4==0)
{
SpeedSet+=10;
if(SpeedSet>3500)
{
SpeedSet=3500;
}
}
}
if(k5==0)
{
delay(100);
if(k5==0)
{
SpeedSet-=10;
if(SpeedSet<0) SpeedSet=0;
}
}
aa[7]=SpeedSet/1000+'0';
aa[8]=SpeedSet/100%10+'0';
aa[9]=SpeedSet/10%10+'0';
aa[10]=SpeedSet%10+'0';
LCD_Write_String(0,0,aa);
}
void int0() interrupt 0
{
Inpluse++;
}
void t0() interrupt 1
{
static unsigned int time=0;
TH0=THC0;
TL0=TLC0;
time++; //轉速測量周期
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
}
|