找回密码
 立即注册

QQ登录

只需一步,快速开始

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

STC15F单片机的自动控制位移小车程序设计

[复制链接]
跳转到指定楼层
楼主
概述
此系统采用IAP15F2K60S2单片机作为主控芯片,控制直流电机的转动,带动小车移动,使用超声波传感器来测量小车位移,实现小车行进指定位移的功能。显示部分使用LCD1602液晶屏。
主要由单片机控制器、直流电机驱动电路、超声波传感器测距电路、显示电路、锁定装置等部分组成。
核心在于如何能够驱动电机稳定又准确转动,以及如何构建适当的反馈系统来确保小车位移的准确性。
反馈控制系统如图2.1所示;系统通过按键模块将目标位移值输入反馈控制链中,当前位移值与目标值作差;控制系统对差值控制,在某一阈值之前,系统保持高速前进,到达阈值之后,使得小车能够逐渐减速并且最终能够在目标值处停下。其中使用超声波测距作为负反馈,在密闭的空间内能够拥有很好地精度,也能确定小车当前的位移,是良好的反馈系统。

图2.1 反馈控制系统框图

图2.2 系统连接框图
以模型小车为载体,各个模块都需要安装在小车上。系统的连接如图2.2所示,按键模块对应位移控制系统的输入,CUP对应着系统的程序部分,超声波测距对应着反馈部分。驱动部分接收来自MCU的命令,通过ENA的PWM的占空比以调节小车的行进。直流电机控制着传动系统,驱动小车的位移。具体的连线图参考图2.3.
按键模块接收输入信息和控制命令,系统通过测距模块采集距离信息,经过反馈算法之后,将通过ENA、IN1和IN2的状态自动控制直流电机转动等,最终使得小车的行进距离与输入值相等。

图2.3 电路连线图
一、       机驱
1、  模块简介
该模块主控芯片为L298N,一种双H桥电机驱动芯片,其中每个H桥可以提供2A的电流,功率部分的供电电压范围是2.5-48v,逻辑部分5v供电,接受5vTTL电平。其实质类似于一个单刀双掷开关,不过能够提供较大的电流。
该模块共有13个接口,此次项目将会使用到的共有8个:两个使能接口IN1、IN2,12V供电接口,GND接口,5V电源接口,驱动接口C、D,PWM控制接口ENA。本系统中12V供电接口接入的是9V电池,作为整个系统的供电;5V接口作为电源输出为单片机及其它外设供电;C、D接口与直流电机相连,当IN1、IN2接口接入的数值是10时,电机正转,01电机反转,00及11电机不转。
表3.1  L298N控制
  
IN1
  
IN2
工作状态
0
0
停止
1
0
正转
0
1
反转
1
1
停止
2、功能实现
控制小车行进速度的方法就是,通过调节控制接口ENA的PWM波占空比,来控制电机转动与否以及时长,如此控制小车的匀速减速等工作状态。
       这里使用定时器来实现PWM波的输出:每当定时时间来临,计数单位加一,达到一百就置零;而当计数单位小于占空比时,PWM输出口置1,否则清零。代码参考附录L298N.c。

二、       超声波测距
1、模块简介
选用常见的HC-SR04超声波模块,该模块使用直流5.0V供电,可探测距离为2cm-450cm,精度能够达到0.2cm,各参数基本符合此次项目的设计。该模块共有四个接口,其中VCC、GND为电源接口,TRIG控制接口、Echo为数据接口。
使用步骤有三:
(1)    触发测距:IO口TRIG触发,至少给10us的高电平信号;
(2)    模块内部自动发送8个40KHz的方波,自动检测是否有信号返回;
(3)    有信号返回,Echo输出一个高电平,高电平持续的时间就是超声波从发射到接收的时间。
可以根据超声波传输的时间计算出测量的距离:
2
t即为高电平持续的时间,c为声速340m/s。
2、功能实现
程序里,利用外部中断来接受来自Echo的高电平,上升沿定时器1开始计数,下降沿定时器计数结束。将计数时间乘以音速,再除以2便可得到测距的结果。
值得注意的是:此超声波测距模块测得的距离并不是那么准确,导致前后数据的差值较大。鉴于此,在获得距离之前,先进行五次测距,取它们的平均值作为当前测得的距离。另外这部分的重要性是毋庸置疑的,在实验过程之初,没有进行均值滤波之前,小车在到达目标1cm左右的位置时,小车会来回的抖动。一方面是由于当时控制的频率太高,而另一方面正是由于测得的距离有着十足的误差。代码请参考附录wave.c。

三、       控制算法
为了实现速度的闭环控制,这里使用了业内常常使用到了PID算法来控制PWM波的占空比,通过对偏差的比例、积分、微分进行控制,使偏差趋于零的过程。
系统每隔1ms进行一次采样,之后进行一次PID算法。
PID控制一般有两种写法,一个是位置式,另一个是增量式。位置式需要进行累加的操作,而此系统的主控芯片为IAP15F2K61S2,其存储与计算能力一般,于是这里采用增量式。
增量式程序:
//PID控制
       //move 与 deta的关系,最终使得 move = objective
       if(caiyang_FLAG_)
       {
              deta_ID[2]=deta;//采样                                                             PWM_deta=Kp*(deta_ID[2]-deta_ID[1])+Ki*deta_ID[2]+Kd*(deta_ID[2]-2*deta_ID[1]+deta_ID[0]);
              PWM_ID[1]= PWM_ID[0]+PWM_deta;
              PWM=(uint)PWM_ID[1];
              //准备下一轮控制
              PWM_ID[0]=PWM_ID[1];
              deta_ID[0]=deta_ID[1];
              deta_ID[1]=deta_ID[2];
       }
       if(deta>0)
              Z_F= 1;
       else
              Z_F= 2;
       mada();
此外,为了解决系统到达位置之后的原地抖动问题,以及消除到达之后受到人为遮挡会引起小车强烈反应的情况。又引入了消抖算法:小车位于指定位置±0.5cm的距离1秒钟以上时,视为调节结束,小车停止运动。这里使用定时器来确定一秒的时间。
代码如下:
START是能直接控制小车是否运动的标志,STOP是记录小车能否保持±0.5cm的标志。
if(deta<= 0.5 && deta >= -0.5)                        
                           sanmiao = 1;
                    //一旦deta的绝对值小于0.5,电机暂停控制
                    if(sanmiao == 1)
                    {
                           if(deta > 0.5 ||deta < -0.5)
                           {//跳出暂停
                                  STOP = 0;
                                  sanmiao_flag =0;
                                  sanmiao = 0;
                           }
                           else
                           {     
                                  STOP = 1;
                           }
                    }     
定时器处理函数里面:
if(sanmiao== 1 )
      {
             if(sanmiao_flag++ == 10000)
             {
                    sanmiao_flag = 0;
                    sanmiao = 0;
                    if(STOP == 1)
                           START = 0;
             }            
      }
四、       其他功能模块14*4按键模块:
4*4矩阵功能如下:
1    2     3     A
4    5     6     B
7    8     9     C
*  0     #     D,
其中A按键控制读入初始距离,B按键一键开始,C按键停止运动,D按键切换前进后退,*按键重新输入,#按键确认输入,各数字按键进行数字读入。

2、 电源模块
由于小车需要移动,系统必须使用移动电源。而普通电池电量不够充足,而且质量太大,影响电机的运转。故系统使用一节9V可充电电池供电,利用L298N的5V电源接口给单片机及其外设供电,电机驱动模块使用9V供电。

3、 显示模块
显示部分使用LCD1602液晶显示屏,也叫1602字符型液晶,它是一种专门用来显示字母、数字、符号等的点阵型液晶模块。它由若干个5X7或者5X11等点阵字符位组成。
共有16个接口,其中VSS、VDD、VO、A、K是电源接口,VSS、K接单片机的GND,VDD、A接单片机的VCC,VO接上变阻器;RS、RW、EN作为使能接口;其余8个引脚作为数据接口,以便显示不同的ASCII或者自定义的字符。
实验中显示初始距离、设定位移、当前距离以及实时误差。

单片机源程序如下:
  1. /*******
  2. 位移控制系统设计:小车
  3. 设计编写:汪佳锌
  4. 开始时间:2019/6/4
  5. 结束时间:2019/6/8
  6. 最终时间:2019/6/8
  7. 功    能:按键输入指定位移,小于4米;
  8.                                         小车行进到指定距离(位置);
  9. 使用模块:矩阵按键、超声波、电机驱动、显示模块;
  10.                                         KBD_key.c\wave.c\L298N.c\LCD1602.c
  11. 按键功能:按键A:位置初始化;
  12.                                         数字按键 1,2,3,4,5,6,7,8,9,0:输入数字,百十个CM
  13.                                         按键#:确认输入;
  14.                                         按键*:重新输入;
  15.                                         按键A:位置初始化;
  16.                                         按键B:一键开始;
  17.                                         按键C:手动停止;
  18.                                         按键D: '+'/'-';
  19.                                         1、2、3、A
  20.                                         4、5、6、B
  21.                                         7、8、9、C
  22.                                         *、0、#、D
  23. 显示区域:1:
  24.                                         2:
  25. *******/

  26. #include <STC15F2K60S2.h>
  27. #include <intrins.h>
  28. #include "timer0.h"
  29. #include "L298N.h"
  30. #include "wave.h"
  31. #include "LCD1602.h"
  32. #include "KBD_keys.h"

  33. #define MAX 400
  34. //#ifndef uchar
  35. //typedef unsigned char uchar;
  36. //#endif
  37. //#ifndef uchar
  38. //typedef unsigned int uint;
  39. //#endif

  40. enum keyState {state0,state1,state2};

  41. bit key_FLAG_=1,mada_FLAG_=0,caiyang_FLAG_,wave_FLAG_,dsp_FLAG_,//定时器FLAG
  42.                 QJ_HT=1,//前进、后退
  43.                 sanmiao=1,//一秒消抖
  44.                 STOP=0,                //一秒消抖就停止运动
  45.                 START=0;        //开始、停止

  46. uint key_flag=0,countB=0,caiyang_flag,wave_flag,dsp_flag,
  47.                  sanmiao_flag,
  48.                  T;                                                                                //超声波测距返回的时间

  49. uchar Z_F,                                                //马达的状态:0代表停,1正转,2反转;
  50.                         key_value=44,                //指按下了哪个按键;
  51.                         add=0,                                        //按键的指针,三位数 150、010、007
  52.                         PWM=0;                                        //0~100表示占空比
  53. uchar aim[]={0,0,0};        //按键的输入

  54. float distance_caiyang=0,
  55.                         deta=20,                                                                        //                                                                                        --------------------------                墙壁
  56.                         //deta = 当前位置-目标位置,也是程序的控制对象                           ----------------        d                         终点 目标位置distance_aim
  57.                         distance=0,                                //当前位置                                                                                                        o                        d                                          i
  58.                         distance_init=0,        //初始位置                                                                                                        b                        eta                                        s
  59.                         distance_aim=0,                //目标位置=初始位置-目标位移                                j                 ^--^                                tance                现在 当前位置
  60.                         objective=0;                        //目的位移CM                                                                                                 e                                |                                                                                       
  61. //                                                                                                                                                                                                                                        ctive                |
  62. //使用PID算法:P比例,I微分,D积分;                                                                                --------------------------                起点 初始位置distance_init
  63. float deta_ID[3];                        //用于微分和积分
  64. float PWM_deta,Kp,Ki,Kd,zhongjian;
  65. uchar PWM_ID[2];

  66. void display(void);
  67. void control();

  68. void main()
  69. {
  70.         uchar p=0;
  71.        
  72.         Kp = 0.06;
  73.         Ki = 0.25;
  74.         Kd = 0.01;
  75.         deta_ID[0]=deta_ID[1]=0;
  76.         PWM_ID[0]=0;
  77.        
  78.         Timer0_init();
  79.         LCD_init();       
  80.         while(1)
  81.         {
  82.                 //按键扫描,
  83.                 if(key_FLAG_)
  84.                 {       
  85.                         key_FLAG_ = 0;
  86.                         key_value = key_scan();
  87.                         if(key_value != 44)
  88.                         {
  89.                                 key_hand(key_value);
  90.                         }
  91.                 }
  92.                 //测距,六次取平均值
  93.                 if(wave_FLAG_)
  94.                 {
  95.                         wave_FLAG_ = 0;
  96.                         send_wave();               
  97.                         zhongjian += distance_caiyang;
  98.                         if(p++ == 5)
  99.                         {
  100.                                 distance=zhongjian/6;
  101.                                 zhongjian = 0;
  102.                                 p=0;
  103.                         }
  104.                 }
  105.                 //显示
  106.                 if(dsp_FLAG_)
  107.                 {
  108.                         dsp_FLAG_ = 0;
  109.                         display();
  110.                 }
  111.                 //电机控制
  112.                 control();
  113. //                PWM = 50;
  114. //                Z_F = 1;
  115.                 mada();
  116.         }
  117. }
  118. void control()
  119. {
  120.         distance_aim = distance_init - objective;
  121.         deta = distance-distance_aim;
  122.         //deta = distance-distance_init+objective;
  123.         if(START == 1 && objective <= MAX && objective >= -MAX)
  124.                 {
  125.                         //控制部分
  126.                         if(deta <= 0.5 && deta >= -0.5)                               
  127.                                 sanmiao = 1;
  128.                         //一旦deta的绝对值小于0.5,电机暂停控制
  129.                         if(sanmiao == 1)
  130.                         {
  131.                                 if(deta > 0.5 || deta < -0.5)
  132.                                 {//跳出暂停
  133.                                         STOP = 0;
  134.                                         sanmiao_flag = 0;
  135.                                         sanmiao = 0;
  136.                                 }
  137.                                 else
  138.                                 {       
  139.                                         STOP = 1;
  140.                                 }
  141.                         }       
  142.                         else//
  143.                         {
  144.                                 //分段控制
  145.                                 if(deta >= 100)
  146.                                 {                PWM = 70;Z_F = 1;}
  147.                                 else if(deta >= 50)
  148.                                 {                PWM = 50;Z_F = 1;}
  149.                                 else if(deta >= 10)
  150.                                 {                PWM = 30;Z_F = 1;}
  151.                                 else if(deta > 5)
  152.                                 {                PWM = 10;Z_F = 1;}
  153.                                 else if(deta > 0.5)
  154.                                 {                PWM = 5;Z_F = 1;}
  155.                                 else if(deta >= -0.5 && deta <= 0.5)
  156.                                 {                //START = 0;
  157.                                 PWM = 0;Z_F = 0;}
  158.                                 else if(deta >= -5)
  159.                                 {                PWM = 5 ;Z_F = 2;}
  160.                                 else if(deta >= -10 && deta < -5)
  161.                                 {                PWM = 10;Z_F = 2;}
  162.                                 else if(deta >= -50)
  163.                                 {                PWM = 30;Z_F = 2;}
  164.                                 else if(deta >= -100)
  165.                                 {                PWM = 50;Z_F = 2;}
  166.                                 else
  167.                                 {                PWM = 70;Z_F = 2;}       
  168.                                 //PID控制
  169.                                 //move 与 deta的关系,最终使得 move = objective
  170. //                                if(caiyang_FLAG_)
  171. //                                {
  172. //                                        deta_ID[2]=deta;//采样
  173. //                                       
  174. //                                        PWM_deta=Kp*(deta_ID[2]-deta_ID[1])+Ki*deta_ID[2]+Kd*(deta_ID[2]-2*deta_ID[1]+deta_ID[0]);
  175. //                                       
  176. //                                        PWM_ID[1] = PWM_ID[0]+PWM_deta;
  177. //                                        PWM=(uint)PWM_ID[1];
  178. //                                        //准备下一轮控制
  179. //                                        PWM_ID[0]=PWM_ID[1];
  180. //                                        deta_ID[0]=deta_ID[1];
  181. //                                        deta_ID[1]=deta_ID[2];
  182. //                                }
  183. //                                if(deta>0)
  184. //                                        Z_F = 1;
  185. //                                else
  186. //                                        Z_F = 2;
  187.                                
  188.                         }
  189.                 }
  190.         else
  191.         {               
  192.                 Z_F = 0;
  193.                 ENB = 0;
  194.                 ENA = 0;               
  195.         }
  196.         //一秒无变化,停止运动
  197.                
  198. }

  199. void key_hand(uchar key_value)
  200. {
  201.         switch(key_value)
  202.         {
  203.                 case 1:
  204.                 case 2:
  205.                 case 3:aim[add]=key_value;add++;break;
  206.                 case 4:distance_init = distance;break;       
  207.                
  208.                 case 5:
  209.                 case 6:
  210.                 case 7:aim[add]=key_value-1;add++;break;
  211.                 case 8:START = 1;               
  212.                                          sanmiao = 0;                                        //开始、停止
  213.                                          objective = aim[0]*100+aim[1]*10+aim[2];        //确认输入
  214.                                          if(QJ_HT == 0)//后退
  215.                                          {       
  216.                                          objective = -objective;}
  217.                                          distance_init = distance;                                                                //位置初始化
  218.                                          break;//
  219.                
  220.                 case 9:
  221.                 case 10:
  222.                 case 11:aim[add]=key_value-2;add++;break;
  223.                 case 12:START = 0;
  224.                                                 break;
  225.                
  226.                 case 13:aim[0]=aim[1]=aim[2]=0;add=0;QJ_HT = 1;break;//重新输入
  227.                 case 14:aim[add]=0;add++;break;
  228.                 case 15:objective = aim[0]*100+aim[1]*10+aim[2];//确认输入
  229.                                                 if(QJ_HT == 0)//后退
  230.                                                 {        objective = -objective;}
  231.                                                 break;                                               
  232.                 case 16:QJ_HT = ~QJ_HT;break;                                        //输入前进或后退
  233.                 default:break;
  234.         }
  235.         if(add == 3)
  236.                 add = 0;
  237. }
  238. void display()
  239. {
  240.         LCD_write_com(0x80);
  241.         if(START == 1)
  242.                 LCD_write_data('T');
  243.         else
  244.                 LCD_write_data('P');
  245.         LCD_write_data(':');
  246.         display_32(distance_init);
  247.         LCD_write_data(' ');
  248.         if(QJ_HT == 1)
  249.                 LCD_write_data('+');
  250.         else
  251.                 LCD_write_data('-');
  252.         LCD_write_data('0'+aim[0]);
  253.         LCD_write_data('0'+aim[1]);
  254.         LCD_write_data('0'+aim[2]);
  255.         LCD_write_data('C');
  256.         LCD_write_data('M');
  257.         Delayms(1);
  258.        
  259.         LCD_write_com(0x80+0x40);
  260.         if(objective >MAX || objective < -MAX)
  261.         {
  262.                 LCD_write_string(2,1,"INPUT ERROR!    ");
  263.         }
  264.         else
  265.         {
  266.                 display_32(distance);
  267.                 LCD_write_data(' ');
  268.                 display_32(deta);
  269.         }
  270.         Delayms(1);
  271. }

  272. void Timer0_handle(void) interrupt 1
  273. {
  274.         if(sanmiao == 1 )
  275.         {
  276.                 if(sanmiao_flag++ == 10000)
  277.                 {
  278.                         sanmiao_flag = 0;
  279.                         sanmiao = 0;
  280.                         if(STOP == 1)
  281.                                 START = 0;
  282.                         else
  283.                                 START = 1;
  284.                 }               
  285.         }
  286.         if(dsp_flag++ == 30)                                //LCD显示3ms
  287.         {
  288.                 dsp_FLAG_=1;
  289.                 dsp_flag =0;
  290. ……………………

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

所有资料51hei提供下载:
工程程序.rar (65.19 KB, 下载次数: 26)



评分

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

查看全部评分

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

使用道具 举报

沙发
ID:444661 发表于 2019-10-15 21:07 | 只看该作者
谢谢,,,,,需要的
回复

使用道具 举报

板凳
ID:702992 发表于 2020-3-6 10:44 | 只看该作者

RE: STC15F单片机的自动控制位移小车程序设计

大神你好,我做的是红外线测距,然后控制小车位移,该怎么做
回复

使用道具 举报

地板
ID:637409 发表于 2020-6-10 17:02 | 只看该作者
老哥怎么图片显示不了啊
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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