防抖动与延误的Arduino ISR限位开关 [英] Debouncing a limit switch in Arduino ISR with delays

查看:2117
本文介绍了防抖动与延误的Arduino ISR限位开关的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我必须连接到一个Arduino兆2650运动控制限位开关。限位开关的两个常开触点连接到一个Arduino引脚与地,例如,当限位开关被啮合,Arduino的引脚被短路接地。

I have a limit switch attached to an arduino Mega 2650 for motion control. The limit switch's two Normally Open contacts are connected to an Arduino Pin and ground, such that when the Limit Switch is engaged, the Arduino Pin gets short circuited to ground.

正如预期的那样,我已经反弹与此设置问题。我证实使用计数器在我的ISR它。最后,我写了下面code,似乎可靠地识别我的限位开关是否接合或分离在任何特定的时间点。

As expected, I have bouncing issues with this setup. I confirmed it using counters in my ISRs. Finally, I wrote the following code that seems to reliably identify whether my limit switch is engaged or disengaged at any given point in time.

const int lsOuterLeftIn = 18; // lsOuterLeftIn is my Limit Switch
const int LED = 9;
volatile bool lsEngaged = false; // flag for limit switch engaged
void setup() {
    pinMode(lsOuterLeftIn, INPUT_PULLUP);
    pinMode(LED, OUTPUT);
    attachInterrupt(digitalPinToInterrupt(lsOuterLeftIn), ISR1, FALLING);
    attachInterrupt(digitalPinToInterrupt(lsOuterLeftIn), ISR2, RISING);
}
void  loop() {
    if (lsEngaged) digitalWrite(LED, HIGH);
    else digitalWrite(LED, LOW);
}
void ISR1(){
    delay(100);
    lsEngaged = (digitalRead(lsOuterLeftIn));
}
void ISR2(){
    delay(100);
    lsEngaged = (digitalRead(lsOuterLeftIn));
}

但是,这里是我的问题。我来到这后, Arduino的文档页面,和它说

由于延迟()要求中断工作,也不会工作,如果叫
  在ISR内部。

"Since delay() requires interrupts to work, it will not work if called inside an ISR. "

不过,我确实使用延迟()内部ISR和它似乎工作,这是怎么回事?我是否有东西在哪里做什么工作的情况,但也容易折断,因为延迟()功能可以在我的文档说故障呢?

But, I do make use of delay() inside ISRs and it seems to work, what is going on? Do I have a situation where things are working at the moment, but could break easily because the delay() function could malfunction on me as the documentation says?

推荐答案

TomKeddie的回答看起来正确:你不会有任何问题。反正你code是,在我看来,在概念错误的原因至少有两个。现在我来解释一下你为什么。

TomKeddie's answer looks right: you won't have any problems. Anyway your code is, in my opinion, conceptually wrong for at least two reasons. Now I'll explain you why.

有2种输入:那些您有权立刻回答和那些你必须回答,但不是直接的威胁。例如通常挡块属于第一组,因为一旦你打它,你需要停止执行器安全。 UI按钮,在另一侧,属于第二组中,因为你并不需要立即回答。

There are two kind of inputs: the ones to which you have to answer IMMEDIATELY and the ones you have to answer but are not immediate threats. For instance usually a security endstop falls in the first group, since as soon as you hit it you need to stop the actuator. UI buttons, on the other side, fall in the second group, since you don't need to answer it immediately.

请注意:在一个做得好的程序,你可以通常回答第二类毫秒内十分投入,因此用户不会看到延迟

NOTE: in a well-done program you typically can answer to the second kind of inputs within tenth of milliseconds, so a user will never see delays.

现在,如果你的输入降到第二组输入,你不得使用ISR读它,因为你可能会阻止更重要的东西。相反,在主循环读它,正确的去抖动它。例如,您可以使用跳出库,或自己实现它:

Now, if your input falls in the second group of inputs, you shall NOT use an ISR to read it, since you may block something more important. Instead read it in the main loop, properly debouncing it. For instance you can use the Bounce library, or implement it by yourself:

#define CHECK_EVERY_MS 20
#define MIN_STABLE_VALS 5

unsigned long previousMillis;
char stableVals;

...

void  loop() {
    if ((millis() - previousMillis) > CHECK_EVERY_MS)
    {
        previousMillis += CHECK_EVERY_MS;
        if (digitalRead(lsOuterLeftIn) != lsEngaged)
        {
            stableVals++;
            if (stableVals >= MIN_STABLE_VALS)
            {
                lsEngaged = !lsEngaged;
                stableVals = 0;
            }
        }
        else
            stableVals = 0;
    }

    ...
}

这将检查每20ms如果值改变。值,然而,只有当它是超过5个周期(即100毫秒)稳定更新

This will check every 20ms if the value changed. The value, however, is updated only if it is stable for more than 5 cycles (i.e. 100 ms).

这样你是不是与任务阻塞你的主程序。

This way you are not blocking your main program with that task.

如果在另一方面,你再输入presents为您的设备构成严重威胁(例如挡块),你需要,只要你能回答。如果您遇到这种情况,你都在等待100毫秒才回答,这是对需要您输入的速度。

If on the other hand, your input represents a severe threat for your device (e.g. an endstop), you NEED to answer as soon as you can. If this is your case, you are waiting for 100ms before answering, and this is against the need for quickness of your input.

当然你也可以不抖这样的输入,因为去抖动presents延迟。你可以,但是,特权一个状态比其他。在连接到接地终点挡块的情况下,严重的威胁是当输入状态为接地。因此,我建议您设置变量这样的方式:

Of course you CAN'T debounce such an input, since debouncing presents delays. You can, however, privilege one state over the other. In the connected-to-ground-endstop case, the severe threat is when the input state is ground. So I suggest you to set your variable in such a way that:


  1. 当引脚下山你立即将其设置为0

  2. 当引脚变为了您等待100毫秒(主循环),然后设置。

在code要做到这一点是一样的东西:

The code to do this is something like:

#define CHECK_EVERY_MS 20
#define MIN_STABLE_VALS 5

unsigned long previousMillis;
char stableVals;

attachInterrupt(digitalPinToInterrupt(lsOuterLeftIn), ISR1, FALLING);

...

void  loop() {
    if ((millis() - previousMillis) > CHECK_EVERY_MS)
    {
        previousMillis += CHECK_EVERY_MS;
        if ((digitalRead(lsOuterLeftIn) == HIGH) && (lsEngaged == LOW))
        {
            stableVals++;
            if (stableVals >= MIN_STABLE_VALS)
            {
                lsEngaged = HIGH;
                stableVals = 0;
            }
        }
        else
            stableVals = 0;
    }

    ...
}

void ISR1()
{
    lsEngaged = LOW;
}

正如你所看到的唯一中断是一个下降,也是最重要的,这是非常短的。

As you can see the ONLY interrupt is the falling one, and MOST IMPORTANT, it is very short.

如果您需要执行其他指令,如停止马达,你可以在ISR1功能(如果他们是很短)。

If you need to perform other instructions, such as halting the motor, you can in the ISR1 function (IF they are quite short).

请记住:中断服务程序必须是尽可能短,因为当微控制器在其中的一个就变成盲目的一切

Just remember: ISRs MUST be as short as possible, since when the microcontroller is in one of them it becomes blind to everything else

这篇关于防抖动与延误的Arduino ISR限位开关的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆