找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 8681|回复: 11
打印 上一主题 下一主题
收起左侧

L298N+51单片机串口、按键控制直流电机,PID速度闭环控制程序,串口和数码管显示

  [复制链接]
跳转到指定楼层
楼主
这是楼主的课程作业,代码写的可能有点乱,见谅见谅。
可能后面串口部分代码注释不是很多,赶时间,没办法
移植注意了,我用的是12Mhz的晶振,1200波特率
数码管用了IO扩展芯片,所以看代码时注意

直流电机速度闭环控制实验
.实物图
1.1 补充材料清单
序号
材料名称
1
51单片机开发板
(带数码管和矩阵键盘)
2
12V直流减速电机
3
12V电池
4
L298N电机驱动模块
  • 系统电路连接示意图
  • 实验原理及配置
1)原理:直流减速电机尾部为霍尔测速传感器,每转一圈,产生两个脉冲,单片机外部中断计数,在用定时器得到准确的时间,可以计算出电机转速和位置。
2用读取脉冲的单片机资源配置如下

  • 速度和角度解算
每500ms读取一次速度,由于电机每一转产生两个脉冲,因此500ms的计数可以近似看作每一秒钟转过的圈数。
分正反转累加或累减,可以得到电机的位置。
  • 实验数据记录
用Keil C编写程序,串口调试助手控制和显示电机状态,如下图


单片机源程序如下:
  1. /************************************
  2. 在基础版上增加了串口控制功能
  3. 计数方式由计数器变为外部中断0,编码器输出口接P32
  4. 波特率为1200,晶振为12Mhz      移植需注意

  5. 串口控制命令
  6. #start#                    启动
  7. #stop#                    停止
  8. #foreward#             正转
  9. #back#                    反转
  10. #speed up-N#        占空比增加N%
  11. #speed down-N#   占空比减少N%
  12. #pwm-N#               占空比调到N%

  13. ***************************************/

  14. #include "reg52.h"               //此文件中定义了单片机的一些特殊功能寄存器
  15. #include "stdio.h"                                 //printf头文件
  16. #include "string.h"                                 //字符串处理  头文件

  17. #define T0_time 1000             //计时器T0时间   单位us      晶振12MHz
  18. #define Total_PWM 100                         //占空比可以100级调整
  19. #define GPIO_KEY P1              //矩阵键盘

  20. typedef unsigned char u8;
  21. typedef unsigned int  u16;

  22. sbit Int1=P2^0;
  23. sbit Int2=P2^1;

  24. sbit LSA=P2^2;
  25. sbit LSB=P2^3;
  26. sbit LSC=P2^4;

  27. int SetPoint=0;          //目标速度
  28. double Proportion=0.32;  //比例常数
  29. double Integral=0.15;     //积分常数
  30. double Derlvative=0.12;   //微分常数
  31. int LastError;                //Error[-1]
  32. int PrevError;      //Error[-2]
  33. int Pid_flag=0;

  34. long angle=0;                 //记录角度
  35. int  i=0,j=0,Data=0,flag=1;         //用于串口接受数据,不得用于其他地方,flag用于区分接收指令1还是数据0
  36. char ReceiveData,Command[15];   //command接收指令
  37. char code Command_Choose[][20]={"start","stop","foreward","back","speed up","speed down","pwm","pid"};
  38.         
  39. int PWM_TIME_H=0,PWM=0;                  //用于控制占空比,高电平比例,最大为100
  40. u16 count=0,int_time=0,speed=0;               //count计脉冲数,int_time多少时间读取一次脉冲数
  41. u8 key=0,gear=0,speed_show_time=0;                                                //key表示被按下的按键,gear=1正转,2反转        .0停止           speed_show_time用于显示数码管
  42. u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
  43.                                         0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
  44. u8 code smgduan_direction[3]={0x00,0x00,0x40};


  45. void delay(unsigned int z);                    //延时函数
  46. void speed_out();      //数码管速度显示
  47. void Init(void);                //初始化定时器T0,计数器T1
  48. void Motor();                //电机驱动
  49. void KeyDown(void);                                //检测按键
  50. int PID(int NextPoint);                         //增量式PID算法
  51. void ChooseFunction(void);                 //执行按键对应的功能



  52. void main()
  53. {        
  54.         Init();

  55.         while(1)
  56.         {
  57.                 if(gear)
  58.                 {
  59.                         if(gear==1)
  60.                               printf("PWM=%d%%, Speed=%dn/s",PWM_TIME_H,speed);
  61.                         if(gear==2)
  62.                               printf("PWM=%d%%, Speed=-%dn/s",PWM_TIME_H,speed);
  63.                         
  64.                         if(Pid_flag||key>=5&&key<=8)                 //如果处于pid模式
  65.                                 printf(", PID=%d",SetPoint);

  66.                                                                                  //角度读取
  67.                                 printf(", angle=180*%ld\n",angle);

  68.                 }
  69.                 else
  70.                       printf("PWM=%d%%, Waiting to start\n",PWM_TIME_H);
  71.                 KeyDown();                                  //按键        
  72.         }               
  73. }

  74. int PID(int NextPoint)
  75. {
  76.         int iError,PID;                                         //当前误差和反馈量
  77.         iError = SetPoint-NextPoint;         //计算当前误差
  78.         PID = Proportion*iError                         //E[K]
  79.                 + Integral*LastError                 //E[K-1]
  80.                 + Derlvative*PrevError;                 //E[K-2]
  81.                                                                         
  82.         PrevError = LastError;                        //存储误差
  83.         LastError = iError;
  84.         return PID;
  85. }


  86. void uart_receiver(void) interrupt 4 //串口中断
  87. {
  88.     if(RI)  // 判断是串口接收产生中断
  89.     {
  90.         RI = 0;       // 清接收中断标志
  91.         ReceiveData = SBUF;  // 接收到的数据写入缓冲BUF

  92.                 if(flag)          Command[i++]= ReceiveData;                        //保存到字符组
  93.                 if(flag==0 && ReceiveData != '#')        
  94.                         Data=Data*10 + (int)ReceiveData-48;        //计算出数值
  95.                  
  96.                 if(ReceiveData == '-')        
  97.                         Data=0,flag=0,Command[i-1]='\0';        //准备开始接收数据
  98.                
  99.                 if(ReceiveData == '#')                                                         
  100.                  {        Command[i-1]='\0',i=0,flag=1;           //接收数据完成,i清零,准备下次接收
  101.                         Pid_flag=0,key=0;                                            //每次先关闭pid调速
  102.                         for(j=0;j<10;j++)
  103.                         {
  104.                                 if(strcmp(Command,Command_Choose[j])==0)
  105.                                 {
  106.                                         switch(j)                                   //每个按键对应相应的功能
  107.                                         {
  108.                                         case(0):                                                        //开始               
  109.                                                 gear=1;break;
  110.                                         case(1):                                                        //停止   
  111.                                                 gear=0;break;
  112.                                         case(2):                                                         //正转
  113.                                                 gear=1;break;
  114.                                         case(3):                                                         //反转
  115.                                                 gear=2;break;
  116.                                         case(4):                                                          //加速
  117.                                                 PWM_TIME_H+=Data;
  118.                                                 if(PWM_TIME_H>Total_PWM)   PWM_TIME_H=Total_PWM;
  119.                                                 break;
  120.                                         case(5):                                                           //减速
  121.                                                 PWM_TIME_H-=Data;
  122.                                                 if(PWM_TIME_H<0)   PWM_TIME_H=0;
  123.                                                 break;
  124.                                         case(6):                                                           //pwm
  125.                                                 if(Data>100)   PWM_TIME_H=100;
  126.                                                 else                    PWM_TIME_H=Data;
  127.                                                 break;
  128.                                         case(7):                                                                   //PID目标速度
  129.                                                 if(Data>70)    SetPoint=70;
  130.                                                 else                    SetPoint=Data;
  131.                                                 Pid_flag=1;
  132.                                                 break;
  133.                                         }
  134.                                 }        
  135.                         }
  136.                 }
  137.         }
  138. }


  139. void Count_EX0(void) interrupt 0        //外部中断0的中断函数
  140. {
  141.                    count++;                                        //脉冲计数
  142. }


  143. void timer0(void) interrupt 1                //定时器T0的中断函数
  144. {
  145.   TH0=(65536-T0_time)/256;                //TH0=(2^16-t)/256          晶振12MHZ   t单位us           
  146.   TL0=(65536-T0_time)/256;                //TL0=(2^16-t)%256          晶振12MHZ   t单位us
  147.   
  148.   PWM++;
  149.   if(PWM==Total_PWM)  PWM=0;
  150.   
  151.   int_time++;
  152.   if(int_time==500)                            //每500次(500ms)T0中断,测速一次
  153.   {
  154.           int_time=0;                     //重新赋值0
  155.         speed=count;                                //读取速度

  156.         if(gear==1)
  157.         angle+=count;                                //读取角度正转+
  158.         if(gear==2)
  159.         angle-=count;                                //反转-

  160.         count=0;                                    //计数清零
  161.   /******PID***********/
  162.         if(key>=5&&key<=8||Pid_flag)
  163.                 if((speed-SetPoint>1) || (speed-SetPoint<-1))
  164.                     {
  165.                         PWM_TIME_H+=PID(speed);
  166.                           if(PWM_TIME_H>Total_PWM) PWM_TIME_H=Total_PWM;
  167.                  }
  168. /*************************/
  169.   }
  170.   Motor();
  171.   speed_out();

  172. }

  173. void ChooseFunction(void)
  174. {
  175.         Pid_flag=0;                                            //每次先关闭pid调速
  176.         switch(key)                                   //每个按键对应相应的功能
  177.         {
  178.         case(1):                                                        //选择正反转,1正转,2反转
  179.                 if(gear==1)             gear=2;
  180.                 else if(gear==2)        gear=1;
  181.                 else gear=0;
  182.                 break;
  183.         case(2):                                                        //加占空比   
  184.                 PWM_TIME_H+=10;
  185.                 if(PWM_TIME_H>Total_PWM)  PWM_TIME_H=Total_PWM;
  186.                 break;
  187.         case(3):                                                         //减占空比
  188.                 PWM_TIME_H-=10;
  189.                 if(PWM_TIME_H<0)  PWM_TIME_H=0;
  190.                 break;
  191.         case(4):                                                         //开关
  192.                 if(gear==1||gear==2)        gear=0;
  193.                 else                                        gear=1;
  194.                 break;
  195.         case(5):                                                          //PID目标速度40
  196.                 SetPoint=40;
  197.                 break;
  198.         case(6):                                                           //PID目标速度50
  199.                 SetPoint=50;
  200.                 break;
  201.         case(7):                                                           //PID目标速度60
  202.                 SetPoint=60;
  203.                 break;
  204.         case(8):                                                                   //PID目标速度70
  205.                 SetPoint=70;
  206.                 break;
  207.         }
  208. }


  209. void KeyDown(void)
  210. {
  211.         char a=0;
  212.         GPIO_KEY=0x0f;
  213.         if(GPIO_KEY!=0x0f)//读取按键是否按下
  214.         {
  215.                 delay(100);//延时1ms进行消抖
  216.                 if(GPIO_KEY!=0x0f)//再次检测键盘是否按下
  217.                 {        
  218.                         //测试列
  219.                         GPIO_KEY=0X0F;
  220.                         switch(GPIO_KEY)
  221.                         {
  222.                                 case(0X07):        key=1;break;
  223.                                 case(0X0b):        key=2;break;
  224.                                 case(0X0d): key=3;break;
  225.                                 case(0X0e):        key=4;break;
  226.                         }
  227.                         //测试行
  228.                         GPIO_KEY=0XF0;
  229.                         switch(GPIO_KEY)
  230.                         {
  231.                                 case(0X70):        key=key;break;
  232.                                 case(0Xb0):        key=key+4;break;
  233.                                 case(0Xd0): key=key+8;break;
  234.                                 case(0Xe0):        key=key+12;break;
  235.                         }
  236.                         if(key==16) key=0;
  237.                         ChooseFunction();
  238.                         while((a<50)&&(GPIO_KEY!=0xf0))         //检测按键松手检测
  239.                         {
  240.                                 delay(100);
  241.                                 a++;
  242.                         }
  243.                 }         
  244.         }

  245. }


  246. void speed_out()//数码管显示速度与档位
  247. {        
  248.         u8 speed_3=speed%1000/100;                //速度百位数字
  249.         u8 speed_2=speed%100/10;                //速度十位数字
  250.         u8 speed_1=speed%10;                         //速度个位数字

  251.         u8 PWM_TIME_H_2=PWM_TIME_H/10;                         //档位十位数字
  252.         u8 PWM_TIME_H_1=PWM_TIME_H%10;                         //档位个位数字
  253.         
  254.         speed_show_time++;
  255.         if(speed_show_time==7)
  256.                 speed_show_time=0;
  257.         P0=0x00;                          //消影
  258.         switch(speed_show_time)
  259.         {
  260.         case(0):
  261.                 LSA=0;LSB=0;LSC=0;
  262.                 P0=smgduan[speed_1];
  263.                 break;
  264.         case(1):                                                           
  265.                 LSA=1;LSB=0;LSC=0;
  266.                 P0=smgduan[speed_2];
  267.                 break;
  268.         case(2):
  269.                 LSA=0;LSB=1;LSC=0;
  270.                 P0=smgduan[speed_3];
  271.                 break;
  272.         case(3):
  273.                 LSA=0;LSB=0;LSC=1;
  274.                 P0=smgduan[PWM_TIME_H_1];
  275.                 break;
  276.         case(4):
  277.                 LSA=1;LSB=0;LSC=1;
  278.                 P0=smgduan[PWM_TIME_H_2];
  279.                 break;
  280.         case(5):
  281.                 LSA=0;LSB=1;LSC=1;
  282.                 P0=smgduan_direction[gear];
  283.                 break;
  284.         case(6):
  285.                 LSA=1;LSB=1;LSC=1;
  286.                 P0=smgduan[key];
  287.                 break;        
  288.         }
  289. }

  290. void Motor(void)           //电机驱动
  291. {         switch(gear)
  292.          {
  293.          case(0):                 //电机停止
  294.                 Int2=Int1=0;
  295.                 break;

  296.          case(1):                        //电机正转
  297. ……………………

  298. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码

所有资料51hei提供下载:
程序源代码.zip (2.29 MB, 下载次数: 205)



评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏8 分享淘帖 顶1 踩
回复

使用道具 举报

沙发
ID:454653 发表于 2018-12-26 19:42 | 只看该作者
感觉不错
回复

使用道具 举报

板凳
ID:462570 发表于 2019-7-5 11:50 | 只看该作者
前来学习一波。
回复

使用道具 举报

地板
ID:578419 发表于 2019-7-5 12:42 | 只看该作者
强如大佬,表示好多地方看不懂,好高深的样子
回复

使用道具 举报

5#
ID:462570 发表于 2019-7-6 11:14 | 只看该作者
我仔细看了一下代码,发现里面错误很多,比如高级版 timer0中断函数里TL0的赋值就是不对的。还有基础版里面的问题就更多了,感觉牛头不对马嘴,貌似是硬凑出来的代码。我不知道楼主代码是否实际运行过,抑或这是楼主修改后(刻意)再发出来的代码。 如果是这样,那真是在坑人了。   不过这段代码可以借鉴,还是感谢楼主的分享。
回复

使用道具 举报

6#
ID:421308 发表于 2019-7-22 18:57 | 只看该作者
好多都看不懂啊
回复

使用道具 举报

7#
ID:436394 发表于 2019-12-21 20:52 | 只看该作者
觉得很棒
回复

使用道具 举报

8#
ID:671899 发表于 2019-12-23 23:23 | 只看该作者
感谢分享,对我的课程设计很有帮助
回复

使用道具 举报

9#
ID:298008 发表于 2020-2-17 19:13 | 只看该作者
谢谢楼主分享!!!
回复

使用道具 举报

10#
ID:110278 发表于 2020-4-7 14:51 | 只看该作者
感谢分享
回复

使用道具 举报

11#
ID:803822 发表于 2022-4-20 20:56 | 只看该作者
请问有没有电路仿真图哇
回复

使用道具 举报

12#
ID:228452 发表于 2022-4-20 23:34 | 只看该作者
"DC Motor Speed ​​Closed-loop"
What about position closed loop ?
Thanks for code...
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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