|
一個關(guān)于PID算法的控制電機(jī)轉(zhuǎn)動的Proteus仿真及程序,電路圖在仿真工程文件里面打開即可
- #include<reg52.h>
- #include"lcd1602.h"
- sfr T2MOD = 0x0c9;
- #define uchar unsigned char
- #define uint unsigned int
- sbit Q0 = P2^4;
- sbit Q1 = P2^5;
- sbit Q2 = P2^6;
- sbit Q3 = P2^7;
- sbit PWM = P1^7;
- sbit UP = P1^0;
- sbit DOWM = P1^1;
- sbit GORB = P2^3; //換相
- sbit ADDSPEED = P1^2;
- sbit SUBSPEED = P1^3;
- uint tuint = 65535;
- uint tpwm = 1; //pwm周期為10000us tpwm變量表示pwm高電平時間,也相當(dāng)于占空比 (仿真時,頻率高時,電機(jī)反應(yīng)慢。在實(shí)物上要加大頻率)
- uchar t1_flag = 0;
- uint pulse = 0;
- uint t0_flag = 0;
- uchar t2_flag = 0;
- bit t2_over = 0;
- bit Just_Get = 1;
- #define ZZ { Q0 = 0;Q1 = 0;Q2 = 1;Q3 = 1;} //正轉(zhuǎn)
- #define FZ { Q0 = 1;Q1 = 1;Q2 = 0;Q3 = 0;} //反轉(zhuǎn)
- #define STOP { Q0 = 1;Q1 = 0;Q2 = 1;Q3 = 0;} //停止
- //禁止出現(xiàn) Q0 = 0;Q1 = 1;Q2 = 0;Q3 = 1; 不然會燒掉mos管
- //************************ PID *************************************
- float now = 0,bef = 0,bbef = 0; //本次采樣值,上次采樣值,上上次采樣值
- float err_now,err_bef,err_bbef; //當(dāng)前偏差,上次偏差,上上次偏差
- float error_add = 0; //所有偏差之和
- float set = 25; //設(shè)定值
- float kp = 25;
- float ki = 25;
- float kd = 0;
- //*****************************************************************
- void delayms(uint ms)//延時?個 ms
- {
- uchar a,b,c;
- while(ms--)
- {
- for(c=1;c>0;c--)
- for(b=142;b>0;b--)
- for(a=2;a>0;a--);
- }
- }
- void timer_init()
- {
- EA = 1;
- ET0 = 1;
- ET1 = 1;
- ET2 = 1;
-
- TMOD = 0x15; //定時器0 計(jì)數(shù)模式 定時器1模式1
- T2MOD = 0x01;
-
- TH0 = TL0 = 255;
- TH2 = 0x3C;
- TL2 = 0xB0; //50MS
-
- }
- void timer1() interrupt 3
- {
- if(t1_flag == 0)
- {
- t1_flag = 1;
- PWM = 1;
- TH1 = (tuint - tpwm + 1)/256;
- TL1 = (tuint - tpwm + 1)%256;
-
- }
- else
- {
- t1_flag = 0;
- PWM = 0;
- TH1 = (tuint - 10000 + tpwm + 1)/256;
- TL1 = (tuint - 10000 + tpwm + 1)%256;
- }
- }
- void timer0() interrupt 1
- {
- TH0 = TL0 = 255;
- t0_flag++;
- }
- void timer2() interrupt 5
- {
- TF2 = 0;
- TH2 = 0x3C;
- TL2 = 0xB0; //50MS
-
- t2_flag++;
-
- if(t2_flag == 2)
- {
- TR0 = 0;
- TR2 = 0;
- t2_flag = 0;
- t2_over = 1; //表示100ms時間到
- }
- }
- void GetPulse()
- {
- t0_flag = 0;
- t2_flag = 0;
-
- TH0 = TL0 = 255;
- TH2 = 0x3C;
- TL2 = 0xB0; //50MS
-
- TR0 = 1;
- TR2 = 1;
- }
- int PID() //增量式PID
- {
- int change;
- err_now = set - now;
- err_bef = set - bef;
- err_bbef = set - bbef;
-
- change = kp*(err_now - err_bef) + ki*err_now + kd*(err_now - 2*err_bef + err_bbef);
-
- /*
- if(set >= now)
- {
- if(set - now > 1)
- change = kp*(err_now - err_bef) + ki*err_now + kd*(err_now - 2*err_bef + err_bbef);
- else
- change = 0.2*kp*(err_now - err_bef) + 0.5*ki*err_now + kd*(err_now - 2*err_bef + err_bbef);
- }
- else if(now > set)
- {
- if(now - set > 1)
- change = kp*(err_now - err_bef) + ki*err_now + kd*(err_now - 2*err_bef + err_bbef);
- else
- change = 0.2*kp*(err_now - err_bef) + 0.5*ki*err_now + kd*(err_now - 2*err_bef + err_bbef);
-
- }
- */
-
- //change = (kp + ki + kd)*(set - now) + (-kp - 2*kd)*(set - bef) + kd*(set - bbef);
- //change = kp*(set - now) + ki*(set - bef) + kd*(set - bbef);
- if(change > 0)
- {
- printchar(1,10,'+');
- printuint(1,11,4,change);
-
- }
- else if(change < 0)
- {
- printchar(1,10,'-');
- printuint(1,11,4,-change);
- }
- else if(change == 0)
- {
- printchar(1,10,' ');
- printword(1,11," 0 ");
- }
-
- return(change);
- }
- int PID2() //位置式PID
- {
-
- int num = 0;
- static num_bef = 0;
-
- err_now = set - now;
- err_bef = set - bef;
-
- error_add = error_add + err_now; //誤差累加。一旦誤差為0則error_add的值不變,PID輸出值不變
- num = kp*err_now + ki*error_add + kd*(err_now - err_bef);
-
- /*
- if(set - now >= 0)
- {
- if(set - now > 1)
- num = kp*err_now + ki*error_add + kd*(err_now - err_bef);
- else
- num = 0.1*kp*err_now + ki*error_add + kd*(err_now - err_bef);
- }
- else
- {
- if(now - set > 1)
- num = kp*err_now + ki*error_add + kd*(err_now - err_bef);
- else
- num = 0.1*kp*err_now + ki*error_add + kd*(err_now - err_bef);
-
- }
- */
-
- if(num > num_bef)
- {
- printchar(1,10,'+');
- printuint(1,11,4,num - num_bef);
- }
- else if(num < num_bef)
- {
- printchar(1,10,'-');
- printuint(1,11,4,num_bef - num);
- }
- else
- {
- printchar(1,10,' ');
- printuint(1,11,4,0);
- }
-
- num_bef = num;
-
- return((uint)num);
- }
- void main()
- {
-
- lcd_init();
- timer_init();
- TH1 = TL1 = 255;
-
- printword(0,0,"P:"); //比例系數(shù)
- printword(0,5,"S:"); //設(shè)定值
- printword(1,0,"TPWM:"); //當(dāng)前占空比
- printword(0,10,"PS:"); //當(dāng)前電機(jī)反饋的每秒脈沖數(shù)
-
- while(1)
- {
- if(GORB == 1)
- { ZZ; }
- else
- { FZ; }
-
- if(ADDSPEED == 0)
- set++;
- if(SUBSPEED == 0)
- set--;
-
- if(Just_Get == 1)
- {
- Just_Get = 0;
- GetPulse();
- }
- else if(t2_over == 1)
- {
- t2_over = 0;
- Just_Get = 1;
- pulse = t0_flag;
- bbef = bef;
- bef = now;
- now = t0_flag;
-
- if(set != 0)
- {
- TR1 = 1;
- }
- else
- {
- TR1 = 0;
- PWM = 0;
- }
-
- // tpwm = tpwm + PID(); //增量式PID
- tpwm = PID2(); //位置式PID
-
- }
-
- if(UP == 0)
- kp = kp + 1;
- if(DOWM == 0)
- kp = kp - 1;
-
- printuint(0,2,3,kp);
- printuint(0,7,3,set);
- printuint(1,5,4,tpwm);
- printuint(0,13,5,pulse);
- }
-
- }
-
復(fù)制代碼
這個是液晶頭文件:
- #include <intrins.h>
- #define uchar unsigned char
- #define uint unsigned int
- //************************************ lcd1602開始 ***********************************
- #define LCD_DATA P0 //P0口接LCD數(shù)據(jù)口
- sbit RS = P2^2;
- sbit RW = P2^1;
- sbit LCDEN = P2^0;
- sbit LCD_BUSY = P0^7;
- //********* lcd1602延時函數(shù) ***************
- void delayms_lcd(uint ms)//延時?個 ms
- {
- uchar a,b,c;
- while(ms--)
- {
- for(c=1;c>0;c--)
- for(b=142;b>0;b--)
- for(a=2;a>0;a--);
- }
- }
- //**********字符串復(fù)制函數(shù)**********
- void string_copy(uchar *target,uchar *source)//字符串復(fù)制 target:目標(biāo) source:源
- {
- uchar i = 0;
- for(i = 0;source[i] != '\0';i++)//注意target的長度 無保護(hù)措施!
- {
- target[i] = source[i];
- }
- target[i] = '\0';
- }
- //**********字符串比較函數(shù)**********
- uchar string_cmp(uchar *target,uchar *source)//字符串比較 target:目標(biāo) source:源
- {
- uchar revalue;
- uchar i = 0;
- for(i = 0;target[i] != '\0' && source[i] != '\0';i++) //兩個都不等于'\0'才執(zhí)行 出現(xiàn)一個等于'\0'就跳出
- {
- if(target[i] == source[i])
- {
- revalue = 1;
- }
- else
- {
- revalue = 0;
- break;
- }
- }
- if(revalue == 1)
- {
- if(target[i] == '\0' && source[i] == '\0')
- revalue = 1;
- else
- revalue = 0;
- }
- return(revalue);
- }
- //**************** LCD1602函數(shù) ********************
- //LCD基本函數(shù):
- void busy_check() //忙碌檢測
- {
- /* RW = 1; //讀
- RS = 0; //指令寄存器
- LCD_DATA = 0xFF;//實(shí)驗(yàn)證明讀數(shù)時要將I/O口要置1
- LCDEN = 0;
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- LCDEN = 1;// EN高電平讀信息 負(fù)跳變執(zhí)行指令
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- while(1)
- {
- if(LCD_BUSY == 0)//P07 == 0跳出循環(huán)
- break;
- } */
- delayms_lcd(2);//仿真時用延時法 下載到真實(shí)單片機(jī)上時,將這句注釋掉,采用上面的語句。
- }
- void lcdwrcom(uchar command)//寫指令
- {
- busy_check();
- RW = 0;//寫
- RS = 0;//指令寄存器
- LCD_DATA = command;
- LCDEN = 1;//負(fù)跳變寫入指令
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- LCDEN = 0;
- }
- void lcdwrdata(uchar lcd_data)//寫數(shù)據(jù) 數(shù)字、字母、標(biāo)點(diǎn)符號都是數(shù)據(jù)
- {
- busy_check();
- RW = 0;//寫
- RS = 1;//數(shù)據(jù)寄存器
- LCD_DATA = lcd_data;
- LCDEN = 1;//負(fù)跳變寫入指令
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- LCDEN = 0;
- }
- void lcd_init()
- {
- delayms_lcd(15);//必要 lcd1602上電到電壓穩(wěn)定需要時間
- RW = 0;//寫
- RS = 0;//指令寄存器
- LCD_DATA = 0x38;// 0x38設(shè)置顯示模式為:16X2 顯示,5X7 點(diǎn)陣,8 位數(shù)據(jù)接口'
- LCDEN = 1;
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- LCDEN = 0;
- delayms_lcd(5);
- lcdwrcom(0x0c);//打開顯示 無光標(biāo) 不閃爍
- lcdwrcom(0x06);//指令3 光標(biāo)右移 屏幕所有文字移動無效
- lcdwrcom(0x01);// 清顯示,光標(biāo)復(fù)位到地址00H位置。
- }
- //LCD擴(kuò)展函數(shù):
- void address(uchar x,uchar y) //定位下一步要寫數(shù)的地址
- {
- uchar location;
- if(x == 0)
- location = 0x80|y;
- else
- location = 0xC0|y;
- lcdwrcom(location);
- }
- void printchar(uchar x,uchar y,uchar letter)//顯示字母、單個字符
- {
- address(x,y);
- lcdwrdata(letter);
- }
- void printword(uchar x,uchar y,uchar *word) //顯示單詞(字符數(shù)組)
- {
- uchar i = 0;
- for(i = 0;word[i] != '\0';i++)
- {
- address(x,y + i);
- lcdwrdata(word[i]);
- }
- }
- void printuint(uchar x,uchar y,uchar num_ws_max,uint num)//顯示無符號整形 0~65535 x:行 y:列 num_ws_max 變量的最大位數(shù)
- {
- uchar i = 0;
- uchar str[5] = {0x20,0x20,0x20,0x20,0x20};
- if(num >= 10000)
- {
- str[0] = num/10000 + '0';
- str[1] = num%10000/1000 + '0';
- str[2] = num%1000/100 + '0';
- str[3] = num%100/10 + '0';
- str[4] = num%10 + '0';
- // str[5] = '\0'; //手動加字符串結(jié)束標(biāo)志
- }
- else if(num >= 1000)
- {
- str[0] = num/1000 + '0';
- str[1] = num%1000/100 + '0';
- str[2] = num%100/10 + '0';
- str[3] = num%10 + '0';
- str[4] ='\0';
- }
- else if(num >= 100)
- {
- str[0] = num/100 + '0';
- str[1] = num%100/10 + '0';
- str[2] = num%10 + '0';
- str[3] = '\0';
- }
- else if(num >=10)
- {
- str[0] = num/10 + '0';
- str[1] = num%10 + '0';
- str[2] = '\0';
- }
- else if(num >= 0)
- {
- str[0] = num + '0';
- str[1] = '\0';
- }
-
- for(i = 0;i <= 5;i++) //uint類型 最大值65535 為5位數(shù)
- {
- if(str[i] != '\0' && i < num_ws_max)
- {
- address(x,y + i);
- lcdwrdata(str[i]);
- }
- else if(str[i] == '\0' && i < num_ws_max)
- {
- address(x,y+i);
- lcdwrdata(' ');//空格 // 實(shí)現(xiàn)功能:在此變量的位數(shù)范圍內(nèi),把沒數(shù)字的位存0x20(空格)
- //例如:最大有3位:999 當(dāng)變?yōu)?9時,存為9+'0' 9+'0' 0x20
- }
- }
- }
- //******************* lcd1602結(jié)尾 *****************************
復(fù)制代碼
|
評分
-
查看全部評分
|