打印

用软件怎么检测电平边沿

[复制链接]
5559|21
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
chenyongand|  楼主 | 2010-8-7 15:34 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
三个光开可以组成8种状态,000,001,010,011。。。。111这八种状态分布在一个盘上,盘随着齿轮带动转,
我现在需要在状态切换的瞬间做些处理,怎么能检测到状态的改变。
每种状态改变时其实就是一个光开的输出电平由高变低,或者由低变高,跟按键的按下和松开道理差不多,但我编写的程序总是不能准确的及时的检测,或者说根本就是不能检测,现在急着完成任务,很着急,等这关过去了,我把按键知识好好弄懂
谁有空给指导下我现在的问题,非常感谢!

相关帖子

沙发
xuyiyi| | 2010-8-7 15:50 | 只看该作者
纯软件法:
使用定时中断,每次中断响应时将上一次读入的三个光电开关从Xn转存Xn-1, 且读入三个光电开关输入值更新原Xn中值, 根据Xn及Xn-1中的开关量状态,即可判断每一路光电开关每种状态的改变。

软硬件结合法:
使用输入捕捉功能,即每次输入端电平发生变化后触发中断,在中断程序中,再判定这一路光电开关状态的改变。

使用特权

评论回复
板凳
Apmvista| | 2010-8-7 15:59 | 只看该作者
按照楼主的说法,这有点像绝对增量式的光电编码器,那么这8种状态应该是按一定顺序排列的,那么如果知道了当前状态的话,那么上一状态和下一状态应该是已知的,这样就给你可以简化你程序中的判断条件。
不过我只做过相对增量式光电编码器的程序,并且是两位的,只有四种状态。我觉得楼主可以把程序贴出来大家看一下,可以搞清楚楼主的思路。

使用特权

评论回复
地板
chenyongand|  楼主 | 2010-8-7 15:59 | 只看该作者
多谢你的回复,我现在用的是纯软件的方法,跟你说的那种方法是一样的,那个盘转的很慢整个八种状态走一遍得10多秒,我用100ms定时中断,每次中断时候,把上次的状态转到另一个变量里面,再读新的状态,如果两个变量的数值不相等就说明状态改变,还需要考虑别的什么吗?

使用特权

评论回复
5
chenyongand|  楼主 | 2010-8-7 16:11 | 只看该作者
3# Apmvista
感谢3楼的回复
我那八种状态确如你说的是按顺序分布在盘上的
但我现在不要状态,只要状态切换的瞬间那边沿
我是个初学者,程序写的很不好,我贴出来,不要砸砖啊
100ms定时中断里面的:
      zt2=zt1;    //zt2是上次状态,zt1是本次状态
      zt1=0;
         if(PDR0_P07==1) zt1=zt1|00000100;
         else            zt1=zt1&11111011;
         if(PDR6_P60==1) zt1=zt1|00000010;
         else            zt1=zt1&11111101;
         if(PDR6_P61==1) zt1=zt1|00000001;
         else            zt1=zt1&11111110;


主程序里面的:


     if(zt2==zt1) zt=zt1;//两次状态相等
     else zt=0;
  
      if(zt==3)       {gk_old=gk;gk=1;} //三个光开组成的值zt=3(也就是00000011)
        else if(zt==7){gk_old=gk;gk=2;}//下面同样类似,并且将八种光开电平状态编号,gk为1,2,3。。。
        else if(zt==5){gk_old=gk;gk=3;}
        else if(zt==1){gk_old=gk;gk=4;}
        else if(zt==0){gk_old=gk;gk=5;}
        else if(zt==4){gk_old=gk;gk=6;}
        else if(zt==6){gk_old=gk;gk=7;}
        else if(zt==2){gk_old=gk;gk=8;}
          else {}
        
        
   if(gk!=gk_old) //如果光开状态改变了,说明有边沿出现
   {
       if(gk==1)
              {
              if(gk_old==8) gkbz=1;    // gkbz是八个边沿的编号
              if(gk_old==2) gkbz=2;
              }
       else if(gk==2)
              {
              if(gk_old==1) gkbz=2;
              if(gk_old==3) gkbz=3;
              }
       else if(gk==3)
              {
              if(gk_old==2) gkbz=3;
              if(gk_old==4) gkbz=4;
              }
       else if(gk==4)
              {
              if(gk_old==3) gkbz=4;
              if(gk_old==5) gkbz=5;
              }
       else if(gk==5)
              {
              if(gk_old==4) gkbz=5;
              if(gk_old==6) gkbz=6;
              }
       else if(gk==6)
              {
              if(gk_old==5) gkbz=6;
              if(gk_old==7) gkbz=7;
              }
       else if(gk==7)
              {
              if(gk_old==6) gkbz=7;
              if(gk_old==8) gkbz=8;
              }
       else if(gk==8)
              {
              if(gk_old==7) gkbz=8;
              if(gk_old==1) gkbz=1;
              }
       else gkbz=0;
   }
这就是我的程序中关于边沿检测才部分
写的很乱,见谅啊,我刚刚实际接触单片机两个月

使用特权

评论回复
6
zyp898989| | 2010-8-7 16:28 | 只看该作者
没用过你说的那个东西,但是用软件定时检查来扑捉跳变,是不是很难啊?好像很多是用边缘促发中断来处理的吧?

使用特权

评论回复
7
Apmvista| | 2010-8-7 16:31 | 只看该作者
我觉得我应该是发现楼主的问题了。
首先吧,你想的太过复杂,或者楼主没有好好的分析一下状态的变化,制定流程图,所以导致自己在敲代码的过程中“迷失”了自我……呵呵,我其实也是这样。
第二吧,如果有八种状态的话,那么边沿的变化按你的思路不应该是16种 吗?比如说这里:
if(gk==1)
              {
              if(gk_old==8) gkbz=1;    // gkbz是八个边沿的编号
              if(gk_old==2) gkbz=2//此边沿是从状态2变为状态1
              }
       else if(gk==2)
              {
              if(gk_old==1) gkbz=2;
               //此边沿是从状态1变为状态2,与上面的边沿应该是不同的吧?为何赋值都为2呢?
              if(gk_old==3) gkbz=3;
              }
不知道是不是这里的原因啊 。
至于程序的简化……我试试吧。

使用特权

评论回复
8
疯子8972| | 2010-8-7 16:41 | 只看该作者
转这么慢应该没问题吧    慢慢读呗
速度要快就加硬件电路    出中断呗

使用特权

评论回复
9
Apmvista| | 2010-8-7 16:47 | 只看该作者
第一,我认为你的状态赋值不太合理。你要把程序简化就必须把你的状态变化变得简单,你应该按照PDR6_P60、PDR0_P07、PDR6_P61的顺序按高低位排序而不是PDR0_P07、PDR6_P60 、PDR6_P61;而且无论如何应该吧000附为初始值。这样你就会发现,状态变化时很有规律的,都是从低位向高位主次变高或或者变低。
第二,你做成一个轮盘,无非只有两种旋转的状态,即为“反转”和“正转”。为何还要判断如此多的边沿呢?
呵呵,我也比较菜啊,这是我想到的两点。

使用特权

评论回复
10
chenyongand|  楼主 | 2010-8-7 17:01 | 只看该作者
7# Apmvista
你说的问题我来解释一下
状态从2变到1,或者从1变到2,我都把他当着同一个边沿,因为这两个边沿在行程上,无论正反转,都对应着同一个物理位置,在这个位置,我的处理结果都是一样的,所以给赋值相同的数字,不用区分上升沿和下降沿
我是轮盘计的是门的行程,轮盘最多只转一圈,八种状态不会重复出现多次,轮盘转一圈,门已经走了差不多4m了,门最大开4m.不能超过4m

使用特权

评论回复
11
chenyongand|  楼主 | 2010-8-7 17:21 | 只看该作者
9# Apmvista
我轮盘上的状态已经固定了,分别是:
011      
111
101
001
000
100
110
101
对应十进制3,7,5,1,0,4,6,2
我接线是PDR0_P07,PDR6_P60,PDR6_P61,按高到底,这个没多大关系,反正状态变化时也是只变一位的电平。
确如你说的,正反转,不需要那么多边沿,但一个轮盘一圈就有8个边沿啊,不管正传还是反转,都是那8个边沿,不用分上升沿和下降沿,实际上是八个边沿把一段路程做了8个标记,就好比公路上里程碑

使用特权

评论回复
12
Apmvista| | 2010-8-7 17:28 | 只看该作者
11# chenyongand 那我觉得你的思路应该没问题,可能是这部分有点问题

if(zt2==zt1) zt=zt1;//两次状态相等
     else zt=0;         
    //两次状态不等的话就赋值为了,那么你下面的判断就始终跳不出else if(zt==0)
    // 了……

使用特权

评论回复
13
chenyongand|  楼主 | 2010-8-7 17:37 | 只看该作者
12# Apmvista
要是不相等,说明状态已经改变了,等再读一次的时候,就又相等了,不会跳不出的,等再度一次的时候,gk值就和旧的不一样了,这样就能判断边沿了

使用特权

评论回复
14
chenyongand|  楼主 | 2010-8-7 17:39 | 只看该作者
我觉得可能跟抖动有关吧,我读状态的时候没有进行抖动处理啊,怎么更合理的添加抖动延时处理,如果用for循环,挺浪费的,或者三个公用一个for也行,尽量减少浪费

使用特权

评论回复
15
Apmvista| | 2010-8-7 17:44 | 只看该作者
13# chenyongand 呵呵,可能我没说明白……
比如,在进入中断后,zt2被赋值以前的状态zt1的值2,
检测到当前状态的值zt1=7,
那么进入主程序后zt=0
那么接下来的判断就到了 else if(zt==0){gk_old=gk;gk=5;}
此时我们可以知道gk_old应该是状态为2时gk的赋值,即为8(上次的赋值)
再接下来进入 if(gk!=gk_old) //如果光开状态改变了,说明有边沿出现
{
……
   else if(gk==5)
              {
              if(gk_old==4) gkbz=5;
              if(gk_old==6) gkbz=6;
              }
……
}
结果两个if都不符合判断,出错。
不知道这样说你明白了不?呵呵,我表达能力有点欠缺哈~~~

使用特权

评论回复
16
chenyongand|  楼主 | 2010-8-7 18:09 | 只看该作者
15# Apmvista
恩,我明白你说的意思,感谢你的耐心和细心
你说的那情况我刚开始也想到了,我的本意是如果两个if都不符合的话,就不改变gkbz的值,因为在下面的程序中我根据gkbz的值处理完行程数据后,就让gkbz=0了,
不知道如果两个if都不符合的话,程序会怎么处理gkbz,如果不改变gkbz的值,那就符合我的本意
另外即使出现了边沿,我这不能立即判断出来,好像得等两个100ms中断才能判断出来,第一个中断检测状态改变,第二个再测确认状态,这时候才能确定是哪个边沿出现,但这两个100ms的时间对行程的影响不大

使用特权

评论回复
17
weihualong| | 2010-8-7 20:45 | 只看该作者
在中断里面改变,在主程序中检测不安全.
建议这样:
定义全局变量:zt_Last保存上一次的变量.
1.中断函数出一个100ms的标志.
  FClk_100ms=1;
2.获取当前状态函数:
  uchar GetNowState()  //0--7
  {    uchar     zt1=0;
         if(PDR0_P07==1) zt1+=1;
         if(PDR6_P60==1) zt1+=2;      
         if(PDR6_P61==1) zt1+=4;
        return(zt1);
}

2.检测函数(放到主循环中执行):
CheckChange()
{  uchar zt;
    if(!FClk_100ms) return;
   FClk_100ms=0;
   zt=GetNowState();
   if(zt==zt_Last) return;
  zt_Last=zt;
  switch(zt)
   { case 0:...brak;
      ....
      case 7:...break;
   }
}

使用特权

评论回复
18
c8220| | 2010-8-7 21:43 | 只看该作者
to 楼主    你的程序的主要问题在这两句程序
if(zt2==zt1) zt=zt1;//两次状态相等  这里值的范围为0-7
     else zt=0;      //这里不能使用zt=0,可赋值为除0-7外的其他值
这样修改后应该可以正常

是否及时看楼主要求了?

使用特权

评论回复
19
流行音乐| | 2010-8-8 11:48 | 只看该作者
在主程序中访问zt1的时候要关中断,此外还要考虑楼上的建议。

使用特权

评论回复
20
chenyongand|  楼主 | 2010-8-9 13:11 | 只看该作者
17# weihualong
谢谢你的回复,你写的程序很正规,思路我看明白了,今天我试了试,是检测到边沿了,但好像同一个边沿,每次检测时候,并不是在相同的时间相同的地方检测出来的,因为我设置行程的时候,检测到个边沿,就把电机所走的脉冲数保存,设置完了,以后运行的时候,若再走到那个位置就把脉冲数读出来给当前的脉冲变量,这样总的下来,行程才不会偏移
  我还有个光开是数齿轮数,也就是计脉冲数,我没有用上那三个光开时,行程下来,基本准确,用上那三个光开的边沿后,感觉行程的两端限位有点偏移,每次运行位置基本都不固定,这可能跟读三光开的状态有关吧,是不是有干扰或者抖动什么的,该怎么处理

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

9

主题

514

帖子

8

粉丝