Arduino中断不会忽略下降沿

时间:2018-09-16 21:44:04

标签: arduino interrupt avr isr debounce

我无法完全取消附加到中断的按钮。目的是使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上轻松测试一下。

5 个答案:

答案 0 :(得分:1)

您可以使用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
}

答案 1 :(得分:1)

您随时可以在硬件中进行反跳

我几次遇到按钮和编码器弹跳的情况,并且在软件中进行反跳会变得凌乱,并导致无法读取的代码或逻辑错误。

最简单的方法是添加一个0.1 uF电容器,如图所示:

simple debouncing circuit

Arduino具有滞后输入,如果您将10K用作上拉电阻,则可以在1ms以下进行跳动。这是我最喜欢的方法。

如果您想变得更认真,可以在互联网上找到精彩 pdf文件,其中包含许多示例和说明:A Guide to Debouncing

答案 2 :(得分:0)

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

因此,释放按钮后,它会继续弹跳,从而导致输入引脚上的逻辑电平发生变化。如果此时,当为DigitalRead()锁定引脚状态时,该状态被读取为高电平,则将满足整个条件并执行state = HIGH;

我不是艺术家,但是我尽力画出这个时序图:

Time diagram of the button bounce

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

正如在其他答案中所述,硬件反跳也将有所帮助。您可以使用更高的电阻(实际上您不需要使用外部电阻:将按钮连接至GND并启用内部上拉电阻,约为35kOhm)。并在按钮旁边并联一个大约1nF的电容器。

答案 3 :(得分:0)

消除中断中的按钮/开关很麻烦。

使用限位开关时,我遇到了类似的情况。 敲到限位开关的那一刻,必须发生一些事情-所以要中断。

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

无论如何,我使用标志结束了在中断外部的反跳操作。 该代码对此进行了解释,但: 按下开关,ISR运行(执行所需的操作)并设置ISR标志。 ISR标志会阻止ISR实际执行任何操作,直到将其清除为止。 在主循环中,如果设置了ISR标志,则调用反跳功能。 反跳功能将一直等待,直到引脚/开关在所需的状态(高/低)稳定一段预定义的时间,然后清除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中,以确保中断有效。我没有阅读有关您嘈杂环境的评论

答案 4 :(得分:0)

我的最终解决方案,感谢@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;
  }
}
相关问题