使用延迟消除 Arduino ISR 中的限位开关 [英] Debouncing a limit switch in Arduino ISR with delays

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

问题描述

我有一个连接到 arduino Mega 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 中使用计数器确认了它.最后,我编写了以下代码,它似乎可以可靠地识别我的限位开关在任何给定时间点是接合还是断开.

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 文档页面,上面写着

But, here is my problem. I came upon this Arduino documentation page, and it says

"由于delay()需要中断才能工作,如果调用它就不会工作在 ISR 内."

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

但是,我确实在 ISR 中使用了 delay() 并且它似乎有效,这是怎么回事?我是否遇到过这样的情况:目前工作正常,但由于 delay() 函数可能会按照文档中的说明出现故障,因此很容易中断?

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 的回答看起来很正确:您不会有任何问题.无论如何,在我看来,您的代码在概念上是错误的,至少有两个原因.现在我将向您解释原因.

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.

有两种输入:您必须立即回答的输入和您必须回答但不是直接威胁的输入.例如,通常安全限位器属于第一组,因为一旦您碰到它,您就需要停止执行器.另一方面,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 读取它,因为您可能会阻止更重要的内容.而是在主循环中读取它,正确地消除它.例如你可以使用 Bounce 库,或者自己实现:

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;
    }

    ...
}

这将每 20 毫秒检查一次值是否发生变化.但是,只有在稳定超过 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.

另一方面,如果您的输入对您的设备构成严重威胁(例如终点站),您需要尽快回答.如果是这种情况,您需要等待 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.

当然,您不能对这样的输入进行去抖动,因为去抖动会造成延迟.但是,您可以将一个状态置于另一个状态之上.在接地端止挡的情况下,严重的威胁是输入状态为接地时.所以我建议你以这样的方式设置你的变量:

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 毫秒(在主循环中)然后进行设置.

执行此操作的代码类似于:

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).

请记住:ISR 必须尽可能短,因为当微控制器位于其中之一时,它就会对其他一切视而不见

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天全站免登陆