|
一個關于PID算法的控制電機轉動的Proteus仿真及程序,電路圖在仿真工程文件里面打開即可
0.png (47.14 KB, 下載次數: 239)
下載附件
2015-11-27 20:06 上傳
- #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高電平時間,也相當于占空比 (仿真時,頻率高時,電機反應慢。在實物上要加大頻率)
- 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;} //正轉
- #define FZ { Q0 = 1;Q1 = 1;Q2 = 0;Q3 = 0;} //反轉
- #define STOP { Q0 = 1;Q1 = 0;Q2 = 1;Q3 = 0;} //停止
- //禁止出現 Q0 = 0;Q1 = 1;Q2 = 0;Q3 = 1; 不然會燒掉mos管
- //************************ PID *************************************
- float now = 0,bef = 0,bbef = 0; //本次采樣值,上次采樣值,上上次采樣值
- float err_now,err_bef,err_bbef; //當前偏差,上次偏差,上上次偏差
- float error_add = 0; //所有偏差之和
- float set = 25; //設定值
- 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 計數模式 定時器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:"); //比例系數
- printword(0,5,"S:"); //設定值
- printword(1,0,"TPWM:"); //當前占空比
- printword(0,10,"PS:"); //當前電機反饋的每秒脈沖數
-
- 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);
- }
-
- }
-
復制代碼
這個是液晶頭文件:
- #include <intrins.h>
- #define uchar unsigned char
- #define uint unsigned int
- //************************************ lcd1602開始 ***********************************
- #define LCD_DATA P0 //P0口接LCD數據口
- sbit RS = P2^2;
- sbit RW = P2^1;
- sbit LCDEN = P2^0;
- sbit LCD_BUSY = P0^7;
- //********* lcd1602延時函數 ***************
- 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--);
- }
- }
- //**********字符串復制函數**********
- void string_copy(uchar *target,uchar *source)//字符串復制 target:目標 source:源
- {
- uchar i = 0;
- for(i = 0;source[i] != '\0';i++)//注意target的長度 無保護措施!
- {
- target[i] = source[i];
- }
- target[i] = '\0';
- }
- //**********字符串比較函數**********
- uchar string_cmp(uchar *target,uchar *source)//字符串比較 target:目標 source:源
- {
- uchar revalue;
- uchar i = 0;
- for(i = 0;target[i] != '\0' && source[i] != '\0';i++) //兩個都不等于'\0'才執行 出現一個等于'\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函數 ********************
- //LCD基本函數:
- void busy_check() //忙碌檢測
- {
- /* RW = 1; //讀
- RS = 0; //指令寄存器
- LCD_DATA = 0xFF;//實驗證明讀數時要將I/O口要置1
- LCDEN = 0;
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- LCDEN = 1;// EN高電平讀信息 負跳變執行指令
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- while(1)
- {
- if(LCD_BUSY == 0)//P07 == 0跳出循環
- break;
- } */
- delayms_lcd(2);//仿真時用延時法 下載到真實單片機上時,將這句注釋掉,采用上面的語句。
- }
- void lcdwrcom(uchar command)//寫指令
- {
- busy_check();
- RW = 0;//寫
- RS = 0;//指令寄存器
- LCD_DATA = command;
- LCDEN = 1;//負跳變寫入指令
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- LCDEN = 0;
- }
- void lcdwrdata(uchar lcd_data)//寫數據 數字、字母、標點符號都是數據
- {
- busy_check();
- RW = 0;//寫
- RS = 1;//數據寄存器
- LCD_DATA = lcd_data;
- LCDEN = 1;//負跳變寫入指令
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- LCDEN = 0;
- }
- void lcd_init()
- {
- delayms_lcd(15);//必要 lcd1602上電到電壓穩定需要時間
- RW = 0;//寫
- RS = 0;//指令寄存器
- LCD_DATA = 0x38;// 0x38設置顯示模式為:16X2 顯示,5X7 點陣,8 位數據接口'
- LCDEN = 1;
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- LCDEN = 0;
- delayms_lcd(5);
- lcdwrcom(0x0c);//打開顯示 無光標 不閃爍
- lcdwrcom(0x06);//指令3 光標右移 屏幕所有文字移動無效
- lcdwrcom(0x01);// 清顯示,光標復位到地址00H位置。
- }
- //LCD擴展函數:
- void address(uchar x,uchar y) //定位下一步要寫數的地址
- {
- 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) //顯示單詞(字符數組)
- {
- 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 變量的最大位數
- {
- 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'; //手動加字符串結束標志
- }
- 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位數
- {
- 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(' ');//空格 // 實現功能:在此變量的位數范圍內,把沒數字的位存0x20(空格)
- //例如:最大有3位:999 當變為99時,存為9+'0' 9+'0' 0x20
- }
- }
- }
- //******************* lcd1602結尾 *****************************
復制代碼
|
-
-
PID算法控制電機轉速.zip
2015-8-14 14:09 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
119.15 KB, 下載次數: 394, 下載積分: 黑幣 -5
評分
-
查看全部評分
|