Arduino中断不会忽略下降沿

Arduino Interrupt won't ignore falling edge

我在完全消除连接到中断的按钮时遇到了一些问题。目标是当按钮为 pressed/released.

时,void loop() 运行 中的语句恰好出现一次

通常最终发生的是两件事之一

  1. ISR 标志在按下按钮时设置一次。释放按钮没有任何作用,如预期的那样。
  2. 按下按钮时设置一次 ISR 标志,松开按钮时再设置一次。

这是我的确切代码:

#define interruptPin 2

#define DBOUNCE 100

volatile byte state = LOW; //ISR flag, triggers code in main loop
volatile unsigned long difference;

void setup() {
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
  Serial.begin(115200);
}

void loop() {
  if(state){ //If we have a valid interrupt
    Serial.println(difference); //Print the time since the last ISR call
    state = LOW; //Reset the flag
  }
}

void ISR_0() {
  static unsigned long last_interrupt = 0;
  if(millis()-last_interrupt > DBOUNCE && digitalRead(interruptPin)){
    difference=millis()-last_interrupt;
    state = HIGH; 
  }
  last_interrupt = millis(); //note the last time the ISR was called
}

这似乎是一种消除中断的流行方式,但无论出于何种原因,它都不适合我。

我希望在按钮释放的第一个下降沿 digitalRead(interruptPin) 会读取 low,因此不会设置 state 标志。

由于ISR更新了last_interrupt时间,第一个下降沿之后的连续反弹似乎仍然被成功忽略。这让我相信去抖动不是问题,但 digitalRead(interruptPin) 是。

除一个状态外,去抖动似乎可以处理所有状态。释放按钮时,代码偶尔仍会将 state 标志设置为 HIGH

这是一些示例输出:

3643(开机等待约 3.6 秒后,我按下按钮,约 1 秒后松开)

在与上述相同的场景中,输出偶尔如下所示:

3643
1018

这显示我按下了按钮,但同时也释放了按钮。

我正在使用 UNO R3 和带有 1k 下拉电阻的瞬时触觉按钮。

我不确定此时出了什么问题。我希望这足够简单,任何人都可以轻松地在他们的 arduino 上测试这个,如果他们愿意的话。

你使用了Uno的内部上拉电阻,但也使用了按钮本身的下拉电阻。那是不正确的,您只能使用其中之一。 在 if 里面,你希望输入为高,所以使用下拉电阻并将 INPUT_PULLUP 更改为 INPUT.

(说清楚:电阻连接在输入引脚和地之间,按钮连接在输入引脚和+5V之间)

[编辑]

我想当ISR被调用时,interruptPin的状态可能会再次改变。 由于 digitalRead 的速度较慢,与(可能的)峰值相比。

我不确定这是否是您想要的,但下面的示例有效。 (出于测试目的,我启用了 LED)。 一件事:至少在去抖动时间(100 毫秒)内按住按钮,否则它将无法工作。 不理想,但如果你想在开关上立即响应,这就是你必须付出的代价。

#define interruptPin 2
#define DBOUNCE 100

volatile byte state = LOW; //ISR flag, triggers code in main loop
volatile unsigned long difference;
bool hasPrinted = false;

void setup() {
  pinMode(interruptPin, INPUT);
  pinMode (13, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
  Serial.begin(115200);
}

void loop() {
  digitalWrite(13, state);
  if (!hasPrinted) {
    Serial.println(difference);
    hasPrinted = true;
  }
}

void ISR_0() {
  static unsigned long last_interrupt = 0;
  if(millis()-last_interrupt > DBOUNCE){
    state = !state;
    hasPrinted = false;
    difference = millis()-last_interrupt;
  }
  last_interrupt = millis(); //note the last time the ISR was called
}

您始终可以在硬件中进行去抖动

我曾多次遇到按钮和编码器的弹跳,在软件中进行去抖动可能会变得混乱并导致代码不可读或逻辑错误。

您可以做的最简单的事情是添加一个 0.1 uF 电容器,如图所示:

Arduino 具有迟滞输入,如果您使用 10K 作为上拉,那么这适用于低于 1 毫秒的弹跳。这是我最喜欢的方法。

如果您想更认真一点,Internet 上有一个 很棒的 pdf,其中包含大量示例和解释:A Guide to Debouncing

正如评论中所说,我认为主要问题是因为您没有实际的去抖动。

所以,按键松开后,会继续弹跳,导致输入引脚上的逻辑电平发生变化。如果此时,当为 digitalRead() 锁存引脚状态时,状态被读取为高电平,则将满足整个条件并执行 state = HIGH;

我不是美工,但我尽力画了这个时序图:

因此,为了避免这种情况,您可以使用任何简单的方法来去抖动。最简单的方法是在超时后再次读取引脚状态,超时时间大于最大预期反弹时间。 例如,如果您正在等待按下按钮并获得高逻辑电平(如果按钮连接到 GND,则为低逻辑电平),只需等待大约 5 毫秒,然后再次读取电平。仅当电平仍然高(低)时才处理按钮按下。

正如其他答案中所说,硬件去抖也有帮助。您可以使用更高的电阻(实际上您不需要使用外部电阻:将按钮连接到 GND 并启用内部上拉,大约 35kOhm)。并加一个1nF左右的电容,与按键并联。

在中断中去抖 buttons/switches 很麻烦。

我遇到了(某种)类似的限位开关情况。 限位开关被击中的那一刻,必须发生一些事情 - 所以中断。

然而,当限位开关被释放时中断也会触发,这是一个问题。我将中断设置为在下降沿触发。

无论如何,我最终使用标志在中断之外进行了去抖动。 代码对此进行了解释,但是: 按下开关,ISR 运行(做它需要做的事情)并设置一个 ISR 标志。 ISR 标志阻止 ISR 实际上做任何事情,直到它被清除。 在主循环中,如果设置了 ISR 标志,则调用去抖功能。 去抖功能将等待 pin/switch 在预定时间内稳定在所需状态 (HIGH/LOW),然后清除 ISR 标志,允许 ISR 再次执行某些操作。

#define interruptPin 2
#define DEBOUNCE_TIME 100L

volatile bool ISR_ACTIVATED = false;
volatile bool display_int_time = false;
bool debounce_started = false;
unsigned long Isr_debounce_pin_timer;
volatile unsigned long difference;

 void setup() {

  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
  Serial.begin(112500);

}


void loop() {

  if (display_int_time) {  
    Serial.println(difference); //Print the time since the last ISR call
    // Right, done with interrupt stuff. clear the interrupt flag
    display_int_time = false;
  }
  // Call debounce ISR routine in main loop
  if (ISR_ACTIVATED)
    ISR_ACTIVATED = !debounce(interruptPin, HIGH, Isr_debounce_pin_timer);

}

bool debounce(int debounce_pin, bool state, unsigned long &state_latch_start) {
  // debounce_pin - what pin are we debouncing?
  // state - should the stable state be HIGH or LOW
  // state_latch_start - when we 'latched' on to a (possibly) stable state

  static bool current_state;

  // Read the required pin
  current_state = digitalRead(debounce_pin);

  // Is the pin at the required state, and we have not yet 'latched' it?
  if ((!debounce_started) && (current_state == state)) {
    // 'latch' this state (ie take note of when the pin went to the required level)
    state_latch_start = millis();
    debounce_started = true;
  }

  // Have we 'latched', but the pin has bounced
  if (debounce_started && (current_state != state))
    // unlatch
    debounce_started = false;

  // Have we latched, the pin is at the required level and enough time has passed (ie pin is stable)
  if (debounce_started && (current_state == state) && (((unsigned long)(millis() - state_latch_start)) >= DEBOUNCE_TIME)) {
    // cool. unlatch
    debounce_started = false;
    // report back that all is goood.
    return(true);
  }

  // Blast. Either the pin is at the wrong level, or is still bouncing. Tell the boss to try again later!
  return(false);
}

void ISR_0() {


  static unsigned long last_interrupt = 0;
  if((millis()-last_interrupt > DEBOUNCE_TIME) && digitalRead(interruptPin) && !ISR_ACTIVATED){
    difference=millis()-last_interrupt;
    //state = HIGH; 
    ISR_ACTIVATED = true;
    display_int_time = true;
  }
  last_interrupt = millis(); //note the last time the ISR was called
}

*** 请注意,编辑我的代码以将去抖时间合并到您的原始 ISR 中,以确保中断有效。我没有看到关于你嘈杂环境的评论

我的最终解决方案,感谢@darrob

#define interruptPin 2
#define DEBOUNCE_TIME 50L

//ISR Flags
volatile bool ISR_DEACTIVATED = false;
volatile bool display_int_time = false;

bool debounce_started = false;
unsigned long Isr_debounce_pin_timer;

int x = 0;


void setup() {
  pinMode(interruptPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
  Serial.begin(112500);
}


void loop() {

  //This runs every time we press the button
  if (display_int_time) {  
    Serial.print("X "); //Print the time since the last ISR call
    x++;
    (x%10==0)?Serial.println(x):Serial.println();

    display_int_time = false; //Done with interrupt stuff. Clear the interrupt flag
  }

  //Debounce for the ISR routine in main loop
  if (ISR_DEACTIVATED)
    //Wait until the pin settles LOW to reactivate the ISR
    ISR_DEACTIVATED = !debounce(interruptPin, LOW, Isr_debounce_pin_timer);

}


//Returns TRUE if pin is stable at desired state, FALSE if bouncing or other state
bool debounce(const int debounce_pin, const bool state, unsigned long &state_latch_start) {
  // debounce_pin - what pin are we debouncing?
  // state - should the stable state be HIGH or LOW
  // state_latch_start - when we 'latched' on to a (possibly) stable state

  //If you are calling this routine to debounce multiple pins in the same loop,
  // this needs to be defined outside of the function, and passed in as a separate
  // parameter for each debounce item (like unsigned long &state_latch_start)
  static bool current_state;

  // Read the required pin
  current_state = digitalRead(debounce_pin);

  // Is the pin at the required state, and we have not yet 'latched' it?
  if ((!debounce_started) && (current_state == state)) {
    // 'latch' this state (ie take note of when the pin went to the required level)
    state_latch_start = millis();
    debounce_started = true;
  }

  // Have we 'latched', but the pin has bounced
  if (debounce_started && (current_state != state))
    // unlatch
    debounce_started = false;

  // Have we latched, the pin is at the required level and enough time has passed (ie pin is stable)
  if (debounce_started && (current_state == state) && (((unsigned long)(millis() - state_latch_start)) >= DEBOUNCE_TIME)) {
    // cool. unlatch
    debounce_started = false;
    // report back that all is goood.
    return(true);
  }

  //Either the pin is at the wrong level, or is still bouncing. Try again later!
  return(false);
}


void ISR_0() {
  if(!ISR_DEACTIVATED){
    ISR_DEACTIVATED = true;
    display_int_time = true;
  }
}