/*PID的參數設置可以參照以下來進行: 參數整定找最佳,從小到大順序查; 先是比例后積分,最后再把微分加; 曲線振盪很頻繁,比例度盤要放大; 曲線漂浮繞大灣,比例度盤往小扳; 曲線偏離回復慢,積分時間往下降; 曲線波動周期長,積分時間再加長; 曲線振盪頻率快,先把微分降下來; 動差大來波動慢。微分時間應加長; 理想曲線兩個波,前高后低4比1 ; 一看二調多分析,調節質量不會低; */ #include <reg51.h> #include<stdio.h> #define uchar unsigned char #define uint unsigned int sbit qidong=P1^3;//啟動鍵 sbit tingzhi=P1^4;//停止鍵 sbit fangxiang=P1^5;//轉向鍵 sbit AddSpeed=P1^6;//加速鍵 sbit SubSpeed=P1^7;//減速鍵 sbit RS = P2^5;//LCD1602數據命令選擇端口 sbit RW = P2^6;//LCD1602讀寫選擇端口 sbit EN = P2^7;//LCD1602使能端口 sbit IN2=P3^3;//L298輸入端2 sbit IN1=P3^4;//L298輸入端1 sbit PWM_FC=P3^5;//L298使能端口 uchar aa[]={'T','a','r','g','e','t',' ',' ',' ',' ',' ','r','/','s','e','c'};//目標轉速。Target r/sec uchar cc[]={'A','c','t','u','a','l',' ',' ',' ',' ',' ','r','/','s','e','c'};//實測轉速: Actual r/sec uchar displayflag;//顯示標志位變量 uint SetSpeed=3000;//聲明設定速度變量 uint ActualSpeed=0;//聲明實際速度變量 int e ,e1 ,e2 ;//聲明當前偏差值變量、之后偏差值變量、再后偏差值變量 int out=0;//PID調節后輸出偏差值變量 uint cnt=0;//定時器1中斷次數變量 uint Inpluse=0;//聲明脈沖計數變量、 uint PWMTime=100;//聲明脈沖寬度時間變量 float uk ,uk1 ,duk ;//聲明目前總偏差值變量、之后偏差值總變量、偏差值總變量 float Kp=0.36,Ki=0.05,Kd=0.016;//pid控制系數p=0.1,i=0.05,d=0.016。 void delay(uchar x) { uint i,j; for(i=x;i>0;i--) for(j=50;j>0;j--); } void DelayUs2x(unsigned char t) { while(--t); } void DelayMs(unsigned char t) { while(t--) { DelayUs2x(245); DelayUs2x(245); } } void write_com(uchar com)//寫命令 { RS=0; RW=0; P0=com; DelayMs(5); EN=1; DelayMs(5); EN=0; } void write_data(uchar date)//寫一個字符 { RS=1; RW=0; P0=date; DelayMs(5); EN=1; DelayMs(5); EN=0; } void init()//初始化 { write_com(0x38); write_com(0x0c); write_com(0x06); write_com(0x01); } void LCD_Write_String(uchar x,uchar y,uchar *s)//寫字符串 { if (y == 0) { write_com(0x80 + x); } else { write_com(0xC0 + x); } while (*s) { write_data( *s); s++; } } void PIDControl()//pid偏差計算 { e=SetSpeed-ActualSpeed;//計算當前偏差值變量 duk=(Kp*(e-e1)+Ki*e+Kd*(e-2*e1+e2));//PID連續系統離散化增量型PID算法,算出總偏差值變量。 uk=duk+uk1;//計算偏差值總變量加上之后偏差值總變量之和賦給目前總偏差值變量 out=(int)uk;//強制類型轉化為整數型的目前總偏差值變量賦給PID調節后輸出偏差值變量 if(out>1000)//判斷PID調節后輸出偏差值變量是否大於100 { out=1000;//PID調節后輸出偏差值變量為100 } else if(out<0)//判斷PID調節后輸出偏差值變量是否小於0 { out=0;//PID調節后輸出偏差值變量為0 } uk1=uk;//目前總偏差值變量賦給之后偏差值總變量 e2=e1;//之前偏差值變量賦給之后偏差值變量 e1=e;//當前偏差值變量賦給之前偏差值變量 PWMTime=out;//PID調節后輸出偏差值變量賦給脈沖寬度時間變量 } void PWMOUT() { if(cnt<PWMTime)//判斷定時器1中斷次數變量是否小於脈沖寬度時間變量 { PWM_FC=1;//脈沖寬度輸入端口輸出高電平 } else { PWM_FC=0;//脈沖寬度輸入端口輸出低電平 } if(cnt>1000)//判斷定時器1中斷次數變量是否大於100 cnt=0;//定時器1中斷次數變量歸0 } void SystemInit()//定時器0定時器1外部中斷0初始化函數 { TMOD=0X21;//定時器0方式1,定時器1方式2。 TH0=0xf8;//初裝定時器0高八位寄存器定時數值 TL0=0x50 ;//初裝定時器0低八位寄存器定時數值,即2毫秒。 TH1=0xC0;//初裝定時器1高八位寄存器定時數值 TL1=0XC0;//初裝定時器1低八位寄存器定時數值,即16毫秒。 EA=1;//開總中斷 EX0=1;//開外部中斷0 IT0=1;//外部中斷0下降沿觸發 ET0=1;//開定時器0中斷允許 ET1=1;//開定時器1中斷允許 TR0=1;//開定時器0中斷 TR1=1;//開定時器1中斷 e =0;//偏差值變量為0 e1=0;//之后偏差值變量為0 e2=0;//再后偏差值變量為0 IN1=1; IN2=1; } void SpeedSet()//設定速度函數 { if(qidong==0) { delay(5); if(qidong==0) { IN1=0; IN2=1; while(qidong==1); } } if(tingzhi==0) { delay(5); if(tingzhi==0) { IN1=1; IN2=1; EN=1; while(tingzhi==1); } } if(fangxiang==0) { delay(5); if(fangxiang==0) { IN1=~IN1; IN2=~IN2; while(fangxiang==1); } } if(AddSpeed==0)//判斷加速鍵是否按下 { delay(5);//延時 if(AddSpeed==0)//再次判斷加速鍵是否按下 { SetSpeed+=100;//設定速度變量每次加100 if(SetSpeed>3500)//判斷設定速度變量是否大於3500 { SetSpeed=3500;//設定速度變量歸為3500 } } } if(SubSpeed==0)//判斷減速鍵是否按下 { delay(5);//延時 if(SubSpeed==0)//再次判斷減速鍵是否按下 { SetSpeed-=100;//設定速度變量每次減100 if(SetSpeed<0)//判斷設定速度變量是否小於0 SetSpeed=0;//設定速度變量歸0 } } aa[7]=SetSpeed/1000+'0'; aa[8]=SetSpeed/100%10+'0'; aa[9]=SetSpeed/10%10+'0'; aa[10]=SetSpeed%10+'0'; LCD_Write_String(0,0,aa); } /**************主函數************/ void main() { SystemInit(); init(); LCD_Write_String(0,0,aa); displayflag=1; while(1) { SpeedSet(); if(displayflag==1) { displayflag=0; cc[7]=ActualSpeed/1000+'0'; cc[8]=ActualSpeed/100%10+'0'; cc[9]=ActualSpeed/10%10+'0'; cc[10]=ActualSpeed%10+'0'; LCD_Write_String(0,1,cc); } } } void int0() interrupt 0//外部中斷0函數 { Inpluse++;//脈沖計數變量加加 } void Timer0() interrupt 1//定時器0中斷服務函數 { static uint time=0;//轉速測量周期變量 TH0=0xf8;//重裝定時器0高八位寄存器計數值 TL0=0x50 ;//重裝定時器0低八位寄存器計數值,即2毫秒。 time++;//轉速測量周期變量加加 if(time>500)//判斷轉速測量周期變量是否大於500,等於500就是500x2毫秒=1000毫秒,也就是1s。 { time=0;//轉速測量周期變量歸0 displayflag=1;//顯示標志位變量置1 ActualSpeed=Inpluse;//脈沖計數變量表示實際速度變量 Inpluse=0;//脈沖計數變量歸0 PIDControl();//PID控制函數 } PWMOUT(); } void Timer1() interrupt 3//定時器1中斷服務函數 { cnt++;//定時器1中斷次數變量 }