找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 16354|回复: 32
收起左侧

PID算法控制电机转速源程序+Proteus仿真工程文件+电路图

  [复制链接]
ID:87486 发表于 2015-8-14 14:11 | 显示全部楼层 |阅读模式
一个关于PID算法的控制电机转动的Proteus仿真及程序,电路图在仿真工程文件里面打开即可
0.png

  1. #include<reg52.h>
  2. #include"lcd1602.h"

  3. sfr T2MOD = 0x0c9;
  4. #define uchar unsigned char
  5. #define uint unsigned int

  6. sbit Q0 = P2^4;
  7. sbit Q1 = P2^5;
  8. sbit Q2 = P2^6;
  9. sbit Q3 = P2^7;

  10. sbit PWM                 = P1^7;
  11. sbit UP                         = P1^0;
  12. sbit DOWM                 = P1^1;
  13. sbit GORB                = P2^3; //换相
  14. sbit ADDSPEED         = P1^2;
  15. sbit SUBSPEED        = P1^3;

  16. uint tuint = 65535;
  17. uint tpwm = 1;        //pwm周期为10000us tpwm变量表示pwm高电平时间,也相当于占空比 (仿真时,频率高时,电机反应慢。在实物上要加大频率)
  18. uchar t1_flag = 0;

  19. uint pulse = 0;
  20. uint t0_flag = 0;
  21. uchar t2_flag = 0;
  22. bit t2_over = 0;
  23. bit Just_Get = 1;


  24. #define         ZZ                 { Q0 = 0;Q1 = 0;Q2 = 1;Q3 = 1;}        //正转
  25. #define         FZ                 { Q0 = 1;Q1 = 1;Q2 = 0;Q3 = 0;}        //反转
  26. #define         STOP        { Q0 = 1;Q1 = 0;Q2 = 1;Q3 = 0;}        //停止
  27. //禁止出现 Q0 = 0;Q1 = 1;Q2 = 0;Q3 = 1; 不然会烧掉mos管

  28. //************************ PID *************************************
  29. float now = 0,bef = 0,bbef = 0;         //本次采样值,上次采样值,上上次采样值
  30. float err_now,err_bef,err_bbef;                //当前偏差,上次偏差,上上次偏差
  31. float error_add = 0;                                //所有偏差之和
  32. float set = 25;                                                //设定值

  33. float kp = 25;
  34. float ki = 25;
  35. float kd = 0;

  36. //*****************************************************************

  37. void delayms(uint ms)//延时?个 ms
  38. {
  39.     uchar a,b,c;
  40.         while(ms--)
  41.         {
  42.           for(c=1;c>0;c--)
  43.         for(b=142;b>0;b--)
  44.             for(a=2;a>0;a--);
  45.         }
  46. }

  47. void timer_init()
  48. {
  49.         EA = 1;
  50.         ET0 = 1;
  51.         ET1 = 1;
  52.         ET2 = 1;
  53.         
  54.         TMOD = 0x15; //定时器0 计数模式 定时器1模式1
  55.         T2MOD = 0x01;
  56.         
  57.         TH0 = TL0 = 255;
  58.         TH2 = 0x3C;
  59.         TL2 = 0xB0;                //50MS
  60.         
  61. }
  62. void timer1() interrupt 3
  63. {
  64.         if(t1_flag == 0)
  65.         {
  66.                 t1_flag = 1;
  67.                 PWM = 1;
  68.                 TH1 = (tuint - tpwm + 1)/256;
  69.                 TL1 = (tuint - tpwm + 1)%256;
  70.                
  71.         }
  72.         else
  73.         {
  74.                 t1_flag = 0;
  75.                 PWM = 0;
  76.                 TH1 = (tuint - 10000 + tpwm + 1)/256;
  77.                 TL1 = (tuint - 10000 + tpwm + 1)%256;
  78.         }
  79. }

  80. void timer0() interrupt 1
  81. {
  82.         TH0 = TL0 = 255;
  83.         t0_flag++;
  84. }
  85. void timer2() interrupt 5
  86. {
  87.         TF2 = 0;
  88.         TH2 = 0x3C;
  89.         TL2 = 0xB0;                //50MS
  90.         
  91.         t2_flag++;
  92.         
  93.         if(t2_flag == 2)
  94.         {
  95.                 TR0 = 0;
  96.                 TR2 = 0;
  97.                 t2_flag = 0;
  98.                 t2_over = 1;        //表示100ms时间到
  99.         }
  100. }
  101. void GetPulse()
  102. {
  103.         t0_flag = 0;
  104.         t2_flag = 0;
  105.         
  106.         TH0 = TL0 = 255;
  107.         TH2 = 0x3C;
  108.         TL2 = 0xB0;                //50MS
  109.         
  110.         TR0 = 1;
  111.         TR2 = 1;
  112. }

  113. int PID()        //增量式PID
  114. {
  115.         int change;

  116.         err_now = set - now;
  117.         err_bef = set - bef;
  118.         err_bbef = set - bbef;
  119.         
  120.         change = kp*(err_now - err_bef) + ki*err_now + kd*(err_now - 2*err_bef + err_bbef);
  121.         
  122. /*        
  123.         if(set >= now)
  124.         {        
  125.                 if(set - now > 1)
  126.                         change = kp*(err_now - err_bef) + ki*err_now + kd*(err_now - 2*err_bef + err_bbef);
  127.                 else
  128.                         change = 0.2*kp*(err_now - err_bef) + 0.5*ki*err_now + kd*(err_now - 2*err_bef + err_bbef);
  129.         }
  130.         else if(now > set)
  131.         {
  132.                 if(now - set > 1)
  133.                         change = kp*(err_now - err_bef) + ki*err_now + kd*(err_now - 2*err_bef + err_bbef);
  134.                 else
  135.                         change = 0.2*kp*(err_now - err_bef) + 0.5*ki*err_now + kd*(err_now - 2*err_bef + err_bbef);
  136.                         
  137.         }
  138. */
  139.         
  140.         //change = (kp + ki + kd)*(set - now) + (-kp - 2*kd)*(set - bef) + kd*(set - bbef);
  141.         //change = kp*(set - now) + ki*(set - bef) + kd*(set - bbef);
  142.         if(change > 0)
  143.         {
  144.                 printchar(1,10,'+');        
  145.                 printuint(1,11,4,change);
  146.                
  147.         }
  148.         else if(change < 0)
  149.         {        
  150.                 printchar(1,10,'-');
  151.                 printuint(1,11,4,-change);
  152.         }
  153.         else if(change == 0)
  154.         {        
  155.                 printchar(1,10,' ');
  156.                 printword(1,11," 0  ");

  157.         }
  158.         
  159.         return(change);
  160. }

  161. int PID2()                //位置式PID
  162. {
  163.         
  164.         int num = 0;
  165.         static num_bef = 0;
  166.         
  167.         err_now = set - now;
  168.         err_bef = set - bef;
  169.         
  170.         error_add = error_add + err_now;  //误差累加。一旦误差为0则error_add的值不变,PID输出值不变

  171.         num = kp*err_now + ki*error_add + kd*(err_now - err_bef);
  172.         
  173. /*        
  174.         if(set - now >= 0)
  175.         {        
  176.                 if(set - now > 1)
  177.                         num = kp*err_now + ki*error_add + kd*(err_now - err_bef);
  178.                 else
  179.                         num = 0.1*kp*err_now + ki*error_add + kd*(err_now - err_bef);
  180.         }
  181.         else
  182.         {
  183.                 if(now - set > 1)
  184.                         num = kp*err_now + ki*error_add + kd*(err_now - err_bef);
  185.                 else
  186.                         num = 0.1*kp*err_now + ki*error_add + kd*(err_now - err_bef);
  187.                         
  188.         }
  189.         */
  190.         
  191.         if(num > num_bef)
  192.         {
  193.                 printchar(1,10,'+');        
  194.                 printuint(1,11,4,num - num_bef);
  195.         }
  196.         else if(num < num_bef)
  197.         {
  198.                 printchar(1,10,'-');        
  199.                 printuint(1,11,4,num_bef - num);
  200.         }
  201.         else
  202.         {        
  203.                 printchar(1,10,' ');
  204.                 printuint(1,11,4,0);
  205.         }
  206.         
  207.         num_bef = num;
  208.         
  209.         return((uint)num);
  210. }

  211. void main()
  212. {        
  213.         
  214.         lcd_init();
  215.         timer_init();
  216.         TH1 = TL1 = 255;
  217.         
  218.         printword(0,0,"P:");                //比例系数
  219.         printword(0,5,"S:");                //设定值
  220.         printword(1,0,"TPWM:");                //当前占空比
  221.         printword(0,10,"PS:");                //当前电机反馈的每秒脉冲数
  222.         
  223.         while(1)
  224.         {
  225.                 if(GORB == 1)
  226.                 {        ZZ;                }
  227.                 else
  228.                 {        FZ;                }
  229.                
  230.                 if(ADDSPEED == 0)
  231.                         set++;
  232.                 if(SUBSPEED == 0)
  233.                         set--;
  234.                
  235.                 if(Just_Get == 1)
  236.                 {        
  237.                         Just_Get = 0;
  238.                         GetPulse();
  239.                 }
  240.                 else if(t2_over == 1)
  241.                 {        
  242.                         t2_over = 0;
  243.                         Just_Get = 1;
  244.                         pulse = t0_flag;
  245.                         bbef = bef;
  246.                         bef = now;
  247.                         now = t0_flag;
  248.                         
  249.                         if(set != 0)
  250.                         {
  251.                                 TR1 = 1;
  252.                         }
  253.                         else
  254.                         {
  255.                                 TR1 = 0;
  256.                                 PWM = 0;
  257.                         }
  258.                         
  259.                 //        tpwm = tpwm + PID();                //增量式PID
  260.                         tpwm = PID2();                                //位置式PID
  261.                                        
  262.                 }
  263.                
  264.                 if(UP == 0)
  265.                         kp = kp + 1;
  266.                 if(DOWM == 0)
  267.                         kp = kp - 1;
  268.                
  269.                 printuint(0,2,3,kp);
  270.                 printuint(0,7,3,set);
  271.                 printuint(1,5,4,tpwm);
  272.                 printuint(0,13,5,pulse);

  273.         }
  274.         
  275. }
  276.         
复制代码

这个是液晶头文件:
  1. #include <intrins.h>
  2. #define uchar unsigned char
  3. #define uint unsigned int

  4. //************************************ lcd1602开始 ***********************************

  5. #define LCD_DATA P0                //P0口接LCD数据口
  6. sbit RS                 = P2^2;
  7. sbit RW                 = P2^1;
  8. sbit LCDEN                 = P2^0;
  9. sbit LCD_BUSY        = P0^7;


  10. //********* lcd1602延时函数 ***************
  11. void delayms_lcd(uint ms)//延时?个 ms
  12. {
  13.     uchar a,b,c;
  14.         while(ms--)
  15.         {
  16.           for(c=1;c>0;c--)
  17.         for(b=142;b>0;b--)
  18.             for(a=2;a>0;a--);
  19.         }
  20. }
  21. //**********字符串复制函数**********
  22. void string_copy(uchar *target,uchar *source)//字符串复制 target:目标 source:源
  23. {
  24.         uchar i = 0;

  25.         for(i = 0;source[i] != '\0';i++)//注意target的长度 无保护措施!
  26.         {
  27.                 target[i] = source[i];        
  28.         }
  29.         target[i] = '\0';
  30. }
  31. //**********字符串比较函数**********
  32. uchar string_cmp(uchar *target,uchar *source)//字符串比较 target:目标 source:源
  33. {
  34.         uchar revalue;
  35.         uchar i = 0;

  36.         for(i = 0;target[i] != '\0' && source[i] != '\0';i++)  //两个都不等于'\0'才执行 出现一个等于'\0'就跳出
  37.         {
  38.                 if(target[i] == source[i])
  39.                 {        
  40.                         revalue = 1;
  41.                 }
  42.                 else
  43.                 {
  44.                         revalue = 0;
  45.                         break;
  46.                 }               
  47.         }
  48.         if(revalue == 1)
  49.         {
  50.                 if(target[i] == '\0' && source[i] == '\0')
  51.                         revalue = 1;
  52.                 else
  53.                         revalue = 0;        
  54.         }
  55.         return(revalue);
  56. }
  57. //**************** LCD1602函数 ********************
  58. //LCD基本函数:
  59. void busy_check()  //忙碌检测
  60. {        
  61. /*        RW = 1;         //读
  62.         RS = 0;        //指令寄存器
  63.         LCD_DATA = 0xFF;//实验证明读数时要将I/O口要置1  

  64.         LCDEN = 0;
  65.         _nop_();
  66.         _nop_();
  67.         _nop_();
  68.         _nop_();
  69.         LCDEN = 1;// EN高电平读信息 负跳变执行指令
  70.         _nop_();
  71.         _nop_();
  72.         _nop_();
  73.         _nop_();
  74.         while(1)
  75.         {
  76.                 if(LCD_BUSY == 0)//P07 == 0跳出循环
  77.                         break;
  78.         }  */
  79.         delayms_lcd(2);//仿真时用延时法 下载到真实单片机上时,将这句注释掉,采用上面的语句。
  80. }
  81. void lcdwrcom(uchar command)//写指令
  82. {        
  83.         busy_check();
  84.         RW = 0;//写
  85.     RS = 0;//指令寄存器
  86.     LCD_DATA = command;

  87.         LCDEN = 1;//负跳变写入指令
  88.         _nop_();
  89.         _nop_();
  90.         _nop_();
  91.         _nop_();
  92.         LCDEN = 0;
  93. }
  94. void lcdwrdata(uchar lcd_data)//写数据        数字、字母、标点符号都是数据
  95. {
  96.         busy_check();
  97.         RW = 0;//写
  98.     RS = 1;//数据寄存器
  99.     LCD_DATA = lcd_data;

  100.         LCDEN = 1;//负跳变写入指令
  101.         _nop_();
  102.         _nop_();
  103.         _nop_();
  104.         _nop_();
  105.         LCDEN = 0;        
  106. }

  107. void lcd_init()
  108. {        
  109.         delayms_lcd(15);//必要 lcd1602上电到电压稳定需要时间   
  110.         RW = 0;//写
  111.         RS = 0;//指令寄存器
  112.     LCD_DATA = 0x38;// 0x38设置显示模式为:16X2 显示,5X7 点阵,8 位数据接口'
  113.         LCDEN = 1;
  114.         _nop_();
  115.         _nop_();
  116.         _nop_();
  117.         _nop_();
  118.         LCDEN = 0;
  119.         delayms_lcd(5);

  120.         lcdwrcom(0x0c);//打开显示 无光标 不闪烁
  121.         lcdwrcom(0x06);//指令3 光标右移 屏幕所有文字移动无效
  122.         lcdwrcom(0x01);// 清显示,光标复位到地址00H位置。
  123. }

  124. //LCD扩展函数:
  125. void address(uchar x,uchar y)  //定位下一步要写数的地址
  126. {        
  127.         uchar location;
  128.         if(x == 0)
  129.             location = 0x80|y;
  130.         else
  131.                 location = 0xC0|y;

  132.          lcdwrcom(location);               
  133. }
  134. void printchar(uchar x,uchar y,uchar letter)//显示字母、单个字符
  135. {
  136.         address(x,y);
  137.         lcdwrdata(letter);
  138. }

  139. void printword(uchar x,uchar y,uchar *word) //显示单词(字符数组)
  140. {
  141.         uchar i = 0;
  142.         for(i = 0;word[i] != '\0';i++)
  143.         {
  144.                 address(x,y + i);
  145.                 lcdwrdata(word[i]);        
  146.         }        
  147. }
  148. void printuint(uchar x,uchar y,uchar num_ws_max,uint num)//显示无符号整形 0~65535 x:行 y:列 num_ws_max 变量的最大位数
  149. {
  150.         uchar i = 0;
  151.         uchar str[5] = {0x20,0x20,0x20,0x20,0x20};

  152.         if(num >= 10000)
  153.         {
  154.                 str[0] = num/10000 + '0';
  155.                 str[1] = num%10000/1000 + '0';
  156.                 str[2] = num%1000/100 + '0';
  157.                 str[3] = num%100/10 + '0';
  158.                 str[4] = num%10 + '0';
  159.         //        str[5] = '\0';         //手动加字符串结束标志
  160.         }
  161.         else if(num >= 1000)
  162.         {
  163.                 str[0] = num/1000 + '0';
  164.                 str[1] = num%1000/100 + '0';
  165.                 str[2] = num%100/10 + '0';
  166.                 str[3] = num%10 + '0';
  167.                 str[4] ='\0';
  168.         }
  169.         else if(num >= 100)
  170.         {        
  171.                 str[0] = num/100 + '0';
  172.                 str[1] = num%100/10 + '0';
  173.                 str[2] = num%10 + '0';
  174.                 str[3] = '\0';
  175.         }
  176.         else if(num >=10)
  177.         {        
  178.                 str[0] = num/10 + '0';
  179.                 str[1] = num%10 + '0';
  180.                 str[2] = '\0';
  181.         }
  182.         else if(num >= 0)
  183.         {
  184.                 str[0] = num + '0';
  185.                 str[1] = '\0';
  186.         }
  187.          
  188.         for(i = 0;i <= 5;i++)        //uint类型 最大值65535 为5位数
  189.         {
  190.                 if(str[i] != '\0' && i < num_ws_max)
  191.                 {
  192.                         address(x,y + i);
  193.                         lcdwrdata(str[i]);
  194.                 }
  195.                 else if(str[i] == '\0' && i < num_ws_max)
  196.                 {
  197.                         address(x,y+i);
  198.                         lcdwrdata(' ');//空格                                                  // 实现功能:在此变量的位数范围内,把没数字的位存0x20(空格)
  199.                                                                                                                  //例如:最大有3位:999 当变为99时,存为9+'0' 9+'0' 0x20
  200.                 }                                       
  201.         }        
  202. }
  203. //******************* lcd1602结尾 *****************************
复制代码


PID算法控制电机转速.zip

119.15 KB, 下载次数: 391, 下载积分: 黑币 -5

评分

参与人数 2黑币 +10 收起 理由
acenerv + 5 共享资料的黑币奖励!
我诺你一世不弃 + 5 很给力!

查看全部评分

回复

使用道具 举报

ID:91715 发表于 2015-10-6 14:29 | 显示全部楼层
哎,新人一枚,没有黑币,朋友能否给分享一份,万分感谢。邮箱:yuhaitao@hit.edu.cn
回复

使用道具 举报

ID:91871 发表于 2015-10-8 12:22 来自手机 | 显示全部楼层
来一水,51黑有你更精彩~~
回复

使用道具 举报

ID:79544 发表于 2015-11-27 19:43 | 显示全部楼层
放出源码多好啊,新手都可以学习学习!
回复

使用道具 举报

ID:1 发表于 2015-11-27 20:23 | 显示全部楼层
腾飞的龙 发表于 2015-11-27 19:43
放出源码多好啊,新手都可以学习学习!

刚编辑了一下,直接把源码弄出来了。
回复

使用道具 举报

ID:79544 发表于 2015-11-29 10:37 | 显示全部楼层
admin 发表于 2015-11-27 20:23
刚编辑了一下,直接把源码弄出来了。

楼主好人,衷心感谢!其实发帖就是为了大家都可以进一步学习交流,很多初进论坛的新人没有黑币又想学习,就下载不了会很失望,这样放出源码会聚论坛的人气!
回复

使用道具 举报

ID:100387 发表于 2015-12-26 15:54 | 显示全部楼层
  仿真 软件 我是 8的   都打不开
回复

使用道具 举报

ID:116948 发表于 2016-4-27 09:46 | 显示全部楼层
正在找这样的资料,下载看看,感谢楼主
回复

使用道具 举报

ID:118433 发表于 2016-5-4 19:56 | 显示全部楼层
楼主一个呗
回复

使用道具 举报

ID:115002 发表于 2016-5-4 21:30 | 显示全部楼层
楼主,大爱无疆!!!
回复

使用道具 举报

ID:119368 发表于 2016-5-8 21:36 | 显示全部楼层
太好了。现在适合的很
回复

使用道具 举报

ID:20382 发表于 2016-5-13 09:39 | 显示全部楼层
正学习中,资料正好,感谢楼主
回复

使用道具 举报

ID:121727 发表于 2016-5-18 15:32 | 显示全部楼层
缺一枚黑币不能下载,挺急的,有没有亲给发一份啊454999972@qq.com 好人一生平安
回复

使用道具 举报

ID:10075 发表于 2016-7-11 10:19 | 显示全部楼层
哎,新人一枚,没有黑币,朋友能否给分享一份,万分感谢
回复

使用道具 举报

ID:138549 发表于 2016-9-6 16:48 | 显示全部楼层
不错 谢谢楼主
回复

使用道具 举报

ID:75390 发表于 2016-10-12 15:08 | 显示全部楼层
好东西啊,想要可以吗
回复

使用道具 举报

ID:61135 发表于 2016-10-27 15:50 | 显示全部楼层
好资料,学习了
回复

使用道具 举报

ID:21820 发表于 2016-11-26 20:47 | 显示全部楼层
非常感谢楼主的无私奉献!!!!
回复

使用道具 举报

ID:101533 发表于 2017-3-25 21:17 | 显示全部楼层
学习学习。谢谢!
回复

使用道具 举报

ID:101533 发表于 2017-3-25 21:18 | 显示全部楼层
学习学习。非常感谢!
回复

使用道具 举报

ID:186087 发表于 2017-4-4 18:50 | 显示全部楼层
很好,很给力!!!!!!!!!!!!!
回复

使用道具 举报

ID:185687 发表于 2017-4-11 21:15 | 显示全部楼层
谢谢这么有用的教程
回复

使用道具 举报

ID:223448 发表于 2017-7-30 21:31 | 显示全部楼层
很好啊,謝謝!
回复

使用道具 举报

ID:130345 发表于 2017-8-5 14:09 | 显示全部楼层
谢谢哈,正好拿来学学算法
回复

使用道具 举报

ID:140253 发表于 2018-3-12 17:18 | 显示全部楼层
谢谢,非常好的资料
回复

使用道具 举报

ID:294375 发表于 2018-4-29 17:46 | 显示全部楼层
可以告诉下这个仿真图怎么设置pid参数么?
回复

使用道具 举报

ID:457154 发表于 2019-1-2 18:35 | 显示全部楼层
非常给力!
回复

使用道具 举报

ID:726402 发表于 2020-4-10 17:06 | 显示全部楼层
哪位大哥能不能告诉我怎么下载
回复

使用道具 举报

ID:781688 发表于 2020-6-17 09:34 | 显示全部楼层
很给力
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表