0

我在完全消除连接到中断的按钮时遇到了一些麻烦。目标是在按下/释放按钮时使语句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 上进行测试。

4

5 回答 5

1

您总是可以在硬件中进行去抖动

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

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

简单的去抖电路

Arduino 有迟滞输入,如果你使用 10K 作为上拉电阻,那么这适用于 1ms 以下的反弹。这是我最喜欢的方法。

如果你想更认真一点,互联网上有一个很棒的pdf 文件,里面有很多例子和解释:去抖指南

于 2018-09-17T20:48:04.677 回答
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
}
于 2018-09-17T18:07:04.860 回答
0

在中断中去抖按钮/开关是一件麻烦事。

我在限位开关上遇到了(某种)类似的情况。碰到限位开关的那一刻,必须发生一些事情-因此中断。

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

无论如何,我最终使用标志在中断之外进行了去抖动。代码解释了它,但是: Switch 被点击,ISR 运行(做它需要的)并设置一个 ISR 标志。ISR 标志阻止 ISR 实际执行任何操作,直到它被清除。在主循环中,如果设置了 ISR 标志,则调用 debounce 函数。去抖功能将等到引脚/开关稳定在所需状态(高/低)一段预定义的时间,然后清除 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 中,以确保中断有效。我没有阅读关于您嘈杂环境的评论

于 2018-09-20T10:42:01.793 回答
0

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

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

我不是艺术家,但我尽力绘制了这个时序图:

按键弹跳时间图

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

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

于 2018-09-19T18:02:57.467 回答
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;
  }
}
于 2018-09-23T03:17:29.043 回答