吴坚鸿 发表于 2015-11-28 02:05:15

新发现一种既能及时响应又抗干扰处理的行程开关识别思路

本帖最后由 吴坚鸿 于 2015-11-28 13:47 编辑

      大家好,我是吴坚鸿。今天在做数控三轴运动卡的项目时,正在考虑ARM单片机和FPGA如何检测步进电机回原点的问题,灵感一来,想到了一种我很满意的行程开关识别思路。为了说明此思路的优越性,我会先列出前面两种常用的思路作对比。并且举一个最简单的工控项目来说明。
      此工控项目要求:上电后,一个普通电机控制一个滑块从左边往右边推,最右边有一个行程开关,滑块碰到行程开关后,电机停止,运动结束。
       转化成单片机编程思路 :用1个IO输出高电平时电机运动,输出低电平时电机停止。另外再用1个IO口作输入,检测行程开关的电平状态,如果发现是高电平说明电机还没碰上行程开关感应器,如果发现是低电平就说明碰上了感应器,此时就可以发出停止电机的命令。
      就是这么简单的过程,其实在识别开关感应器时暗藏玄机,现在一一解剖分析给大家:
      第一种思路:直接判断行程感应器的电平状态,一旦发现低电平,就认为电机已经碰到了行程开关,马上停止电机。
      优点:响应及时。
      缺点:太灵敏了,以至于抗干扰能力非常差,在工控环境里,当电机正在行进的过程中,如果受到电源的波动或者外来的毛刺信号干扰,行程开关的输入信号可能会读取到瞬间的低电平,导致单片机误判断,提前把电机停止了,电机还没碰到行程开关就草率停机。
      源码如下:
#include "REG52.H"


#define CONT_TIME_20MS   10   //大概10次中断相当于20毫秒

void motor_run();      //电机往右边移动
void motor_stop();   //电机停止
void motor_pause();    //电机暂停,虽然暂停里面的代码跟停止的代码一致,但是概念不一样

void T0_time();//定时中断函数


sbit motor_dr=P3^5; //控制电机的启动和停止的输出
sbit right_sr=P0^2; //右边的行程开关检测输入

unsigned char u8MotorFlag=0;//电机的实时运动状态映射在此变量里,此变量的0和1代表了电机的停止和运动
unsigned char u8MotorFlagBackup=0;//用来记录上一次的电机状态,用于判断电机的状态是否发生了改变。

unsigned char u8TimeFlag=0; //计时器的开关
unsigned intu16TimeCnt=0; //计时器的计时变量

unsigned char u8RunStep;//运动控制的步骤变量

void main()
{
   TMOD=0x01;//设置定时器0为工作方式1

   TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f   大概每2ms产生一次中断,我没有详细计算。
   TL0=0x2f;

   EA=1;   //开总中断
   ET0=1;    //允许定时中断
   TR0=1;    //启动定时中断

   u8RunStep=1;//一上电,电机就开始从第1步开始启动
   while(1)   
   {
       switch(u8RunStep)
       {
          case 1:   
               motor_run();   //电机往右边移动
               u8RunStep=2;//切换到下一个步骤
               break;

         case 2:    //等待滑块移动到最右边,直到触发了最右边的行程开关感应器。
               if(0==right_sr)//右边的行程感应器被触发
               {
                              motor_stop();   //电机停止
                  u8RunStep=3;       //马上切换到电机停止的步骤
               }
               break;

         case 3:    //电机处于停止的步骤,除非重新断电重启,否则就一直处于此步骤状态
                break;
       }
   }

}



void T0_time() interrupt 1   //定时中断,大概每2ms就中断一次
{
TF0=0;//清除中断标志
TR0=0; //关中断


TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f   大概每2ms产生一次中断,我没有详细计算。
TL0=0x2f;
TR0=1;//开中断
}



void motor_run()      //电机往右边移动
{
    motor_dr=1;
    u8MotorFlag=1;//电机的实时运动状态映射在此变量里,此变量的0和1代表了电机的停止和运动
}
void motor_stop()   //电机停止
{
    motor_dr=0;
    u8MotorFlag=0;//电机的实时运动状态映射在此变量里,此变量的0和1代表了电机的停止和运动
}
void motor_pause()    //电机暂停,虽然暂停里面的代码跟停止的代码一致,但是概念不一样
{
    motor_dr=0;
    u8MotorFlag=0;//电机的实时运动状态映射在此变量里,此变量的0和1代表了电机的停止和运动
}




      第二种思路:在判断行程感应器的电平状态时,加入了软件的抗干扰处理,一旦发现低电平,一个计时器开始计时,在计时的期间,如果发现出现高电平就马上把计时器清零,如果一直是低电平,并且期间没有出现高电平,就认为是稳定的低电平,此时判定是碰到了行程开关。
      优点:增加了抗干扰处理,几乎能百分百保证电机碰到了行程开关才停机,几乎不会误判了。
      缺点:在软件抗干扰环节增加了一小段延时,而这一小段的延时,会导致电机碰到行程开关后没有马上停止,滑块继续往右边运动了一段时间才停止,时间长了容易把右边的限位机械结构压坏挤坏,因为有应力存在。
      源码如下:
#include "REG52.H"


#define CONT_TIME_20MS   10   //大概10次中断相当于20毫秒

void motor_run();      //电机往右边移动
void motor_stop();   //电机停止
void motor_pause();    //电机暂停,虽然暂停里面的代码跟停止的代码一致,但是概念不一样

void T0_time();//定时中断函数


sbit motor_dr=P3^5; //控制电机的启动和停止的输出
sbit right_sr=P0^2; //右边的行程开关检测输入

unsigned char u8MotorFlag=0;//电机的实时运动状态映射在此变量里,此变量的0和1代表了电机的停止和运动
unsigned char u8MotorFlagBackup=0;//用来记录上一次的电机状态,用于判断电机的状态是否发生了改变。

unsigned char u8TimeFlag=0; //计时器的开关
unsigned intu16TimeCnt=0; //计时器的计时变量

unsigned char u8RunStep;//运动控制的步骤变量

void main()
{
   TMOD=0x01;//设置定时器0为工作方式1

   TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f   大概每2ms产生一次中断,我没有详细计算。
   TL0=0x2f;

   EA=1;   //开总中断
   ET0=1;    //允许定时中断
   TR0=1;    //启动定时中断

   u8RunStep=1;//一上电,电机就开始从第1步开始启动
   while(1)   
   {
       switch(u8RunStep)
       {
          case 1:   
               motor_run();   //电机往右边移动

               u8TimeFlag=0; //计时器关
               u16TimeCnt=0; //计时器的计时变量清零

               u8RunStep=2;//切换到下一个步骤
               break;

         case 2:    //等待滑块移动到最右边,直到触发了最右边的行程开关感应器。
               if(0==right_sr)//右边的行程感应器被触发
               {
                  u8TimeFlag=1; //计时器开
                  if(u16TimeCnt>=CONT_TIME_20MS)//连续20ms内都是低电平,则认为是低电平
                  {
                     u8TimeFlag=0; //计时器关
                     u16TimeCnt=0; //计时器的计时变量及时清零

                                 motor_stop();   //电机停止
                     u8RunStep=3;       //马上切换到电机停止的步骤
                  }
               }
               else//如果是高电平
               {
                  u8TimeFlag=0; //计时器关
                  u16TimeCnt=0; //计时器的计时变量及时清零
               }
               break;

         case 3:    //电机处于停止的步骤,除非重新断电重启,否则就一直处于此步骤状态
                break;
       }
   }

}



void T0_time() interrupt 1   //定时中断,大概每2ms就中断一次
{
TF0=0;//清除中断标志
TR0=0; //关中断

if(1==u8TimeFlag)
{
   u16TimeCnt++;
}


TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f   大概每2ms产生一次中断,我没有详细计算。
TL0=0x2f;
TR0=1;//开中断
}



void motor_run()      //电机往右边移动
{
    motor_dr=1;
    u8MotorFlag=1;//电机的实时运动状态映射在此变量里,此变量的0和1代表了电机的停止和运动
}
void motor_stop()   //电机停止
{
    motor_dr=0;
    u8MotorFlag=0;//电机的实时运动状态映射在此变量里,此变量的0和1代表了电机的停止和运动
}
void motor_pause()    //电机暂停,虽然暂停里面的代码跟停止的代码一致,但是概念不一样
{
    motor_dr=0;
    u8MotorFlag=0;//电机的实时运动状态映射在此变量里,此变量的0和1代表了电机的停止和运动
}





      第三种思路:本思路是结合了前面两种的优点,在判断行程感应器的电平状态时,当发现是低电平时(哪怕是干扰时出现的瞬间低电平),电机马上暂停(暂停和停止的概念不一样,虽然电机都是没有转),当发现是高电平时,电机继续运行,什么时候才认为碰到行程开关?当低电平像第二种思路那样连续持续低电平的时间超过某个值时,才认为碰到了行程开关。巧妙的是,在此判断低电平的小延时期间,电机是处于暂停的状态(没有转),所以不会过冲挤压右边的行程限位机构。
      优点:既能及时响应,又增加了行程开关检测的抗干扰处理,又不会让电机过冲挤压右边的行程开关。
      缺点:暂时还没想出来。
      源码如下:
#include "REG52.H"


#define CONT_TIME_20MS   10   //大概10次中断相当于20毫秒

void motor_run();      //电机往右边移动
void motor_stop();   //电机停止
void motor_pause();    //电机暂停,虽然暂停里面的代码跟停止的代码一致,但是概念不一样

void T0_time();//定时中断函数


sbit motor_dr=P3^5; //控制电机的启动和停止的输出
sbit right_sr=P0^2; //右边的行程开关检测输入

unsigned char u8MotorFlag=0;//电机的实时运动状态映射在此变量里,此变量的0和1代表了电机的停止和运动

unsigned char u8RightSrFlagBackup=0;//用来记录上一次的行程开关电平状态,用来判断行程开关是否发生过电平变化

unsigned char u8TimeFlag=0; //计时器的开关
unsigned intu16TimeCnt=0; //计时器的计时变量

unsigned char u8RunStep;//运动控制的步骤变量

void main()
{
   TMOD=0x01;//设置定时器0为工作方式1

   TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f   大概每2ms产生一次中断,我没有详细计算。
   TL0=0x2f;

   EA=1;   //开总中断
   ET0=1;    //允许定时中断
   TR0=1;    //启动定时中断

   u8RunStep=1;//一上电,电机就开始从第1步开始启动
   while(1)   
   {
       switch(u8RunStep)
       {
          case 1:   
               motor_run();   //电机往右边移动
               u8RightSrFlagBackup=1;//用来判断行程开关是否发生过电平变化

               u8TimeFlag=0; //计时器关
               u16TimeCnt=0; //计时器的计时变量清零

               u8RunStep=2;//切换到下一个步骤
               break;

         case 2:    //等待滑块移动到最右边,直到触发了最右边的行程开关感应器。
               if(0==right_sr)//右边的行程感应器被触发
               {
                              if(u8RightSrFlagBackup!=0) //行程开关的电平发生过变化
                                        {
                                           u8RightSrFlagBackup=0; //及时更新,避免一直触发电机的命令操作
                                           motor_pause();    //电机及时暂停
                                        }

                  u8TimeFlag=1; //计时器开
                  if(u16TimeCnt>=CONT_TIME_20MS)//连续20ms内都是低电平,则认为是低电平
                  {
                     u8TimeFlag=0; //计时器关
                     u16TimeCnt=0; //计时器的计时变量及时清零

                                 motor_stop();   //电机停止
                     u8RunStep=3;       //马上切换到电机停止的步骤
                  }
               }
               else//如果是高电平
               {

                              if(u8RightSrFlagBackup!=1) //行程开关的电平发生过变化
                                        {
                                           u8RightSrFlagBackup=1; //及时更新,避免一直触发电机的命令操作
                     motor_run();      //电机继续往右边移动
                                        }
                  u8TimeFlag=0; //计时器关
                  u16TimeCnt=0; //计时器的计时变量及时清零
               }
               break;

         case 3:    //电机处于停止的步骤,除非重新断电重启,否则就一直处于此步骤状态
                break;
       }
   }

}



void T0_time() interrupt 1   //定时中断,大概每2ms就中断一次
{
TF0=0;//清除中断标志
TR0=0; //关中断

if(1==u8TimeFlag)
{
   u16TimeCnt++;
}


TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f   大概每2ms产生一次中断,我没有详细计算。
TL0=0x2f;
TR0=1;//开中断
}



void motor_run()      //电机往右边移动
{
    motor_dr=1;
    u8MotorFlag=1;//电机的实时运动状态映射在此变量里,此变量的0和1代表了电机的停止和运动
}
void motor_stop()   //电机停止
{
    motor_dr=0;
    u8MotorFlag=0;//电机的实时运动状态映射在此变量里,此变量的0和1代表了电机的停止和运动
}
void motor_pause()    //电机暂停,虽然暂停里面的代码跟停止的代码一致,但是概念不一样
{
    motor_dr=0;
    u8MotorFlag=0;//电机的实时运动状态映射在此变量里,此变量的0和1代表了电机的停止和运动
}




      最后再提醒一下:凡是有行程限位开关的地方,速度都不宜太快,要控制好速度,因为高速会由于比较大的惯性产生碰击现象。如果需要高速的场合,最好能在到达终点前多增加一个感应器,在到达终点前做一些减速操作。如果是步进电机,也可以利用实时坐标信息来识别快到行程开关终点时做一些减速处理。

ttoto 发表于 2015-11-28 04:08:05

我想这些就是所谓的经验吧。

lgg88 发表于 2015-11-28 08:35:20

一般判断输入信号 都会软件滤波 ,不是只是检测IO电平。楼主第3中方式不错,估计大部分人只会用第2种。第3种方式有经验的人会用到。

tt98 发表于 2015-11-28 08:51:42

收下学习,力顶鸿哥!

rainbow 发表于 2015-11-28 08:55:00

用第二种,在限位点前加上一个“减速点”,这样很更安全,有些仪器上面就是这么干的。

adongliu 发表于 2015-11-28 08:59:10

一般是快速压到减速开关,减速直到抬起,继续运转到偏移设置才认为是参考点。

yngufeng 发表于 2015-11-28 09:05:51

软件上参考按键消抖足以,工控的可靠性和抗干扰能力主要还得靠硬件。

tenx 发表于 2015-11-28 09:06:15

第3种同第1种如果都作为一个时刻检测运行的任务来看,不都是一样的,压上停止,抬起运行

mcu_lover 发表于 2015-11-28 09:08:58

硬件输入滤波,用光电开关。
触碰减速,离开停止。

lingdianhao 发表于 2015-11-28 09:42:09

光电可以提前监测是否快到极限,有减速的时间,最后冲击力不是很大,限位开关只适合在低速,环境恶劣的地方,比如油雾环境!

biansf2001 发表于 2015-11-28 09:46:40

文字就够了,不需要上程序吧。反正我是跳过程序看文字的

chengsong 发表于 2015-11-28 10:07:08

用第二种比较多

ibichao 发表于 2015-11-28 10:20:27

楼主可搜一下消抖滤波。

R8C 发表于 2015-11-28 10:34:36

硬件直接通断,软件判断,响应比楼主更快

Flyback 发表于 2015-11-28 10:40:28

工业使用的时候,我们这种情况主要靠硬件

限位检测硬件一般都是有低通滤波器,后面的信号还会经过低速光耦

lgc150 发表于 2015-11-28 10:57:38

哈哈哈,又见到鸿哥了。思路不错,楼上加光电的方法也很好

easier 发表于 2015-11-28 11:17:23

一般應該會用第二種!
压坏行程制是硬件設計有問題!

吴坚鸿 发表于 2015-11-28 14:08:49

感谢各位网友的回复,你们的经验对我很有启发和帮助。

error_dan 发表于 2015-11-28 15:14:37

机械式接触开关,一般会有一个屈服行程,专门用来给LZ的第二种控制方式提供过行程后的运动空间,不过这样自然会导致位置误差,也就是典型的回差误差.
所以现在一般都不用机械式开关了,光电开关或者接近开关(电容式的,磁感式的)可以提供无机械触点的开关控制,要好的多~

另外,标准的电机位置控制,不是两个点就搞定的LZ可以找些伺服电机的手册看看,推荐的方式需要4个点甚至5个点.4个点的是原点,原点附近,终点,终点附近,5个点的是正负极限和极限附近,再加一个原点可以提供物理负行程.
伺服控制器里面,把附近点接进去以后,检测到这个点以后会开始减速,防止过冲.

简单的来回跑是非常简单,但是一旦电机负载大了,或者对速度有要求,也就不简单了.

llww30402048 发表于 2018-7-24 23:27:04

Flyback 发表于 2015-11-28 10:40
工业使用的时候,我们这种情况主要靠硬件

限位检测硬件一般都是有低通滤波器,后面的信号还会经过低速光耦 ...

你好 你有这方面的资料吗?谢谢啊

kcfoo1 发表于 2018-8-18 14:27:55

一般来说行程开关后面还有一个硬件断电安全开关的,或者行程开关是带硬件断电的两路开关

yanyanyan168 发表于 2018-8-18 22:27:04

多谢分享,学习了
页: [1]
查看完整版本: 新发现一种既能及时响应又抗干扰处理的行程开关识别思路