Arduino Interrupt не будет игнорировать спад - PullRequest
0 голосов
/ 17 сентября 2018

У меня возникли проблемы с полным отключением кнопки, связанной с прерыванием.Цель состоит в том, чтобы оператор в 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 и кратковременную тактильную кнопку с понижающим резистором 1 кОм.

Я не уверен, что происходит на этом этапе.Я надеюсь, что это достаточно просто, чтобы любой мог легко проверить это на своем Arduino, если он чувствует себя таким склонным.

Ответы [ 5 ]

0 голосов
/ 23 сентября 2018

Мое окончательное решение, благодаря @ 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;
  }
}
0 голосов
/ 20 сентября 2018

Отключение кнопок / переключателей в прерываниях - проблема.

Я столкнулся с (вроде) похожей ситуацией с концевыми выключателями. В момент срабатывания концевого выключателя что-то должно произойти - так что прерывайте.

Однако прерывание сработает, когда будет также отпущен концевой выключатель, что было проблемой. Моё прерывание было настроено на обрушивающуюся грань.

Во всяком случае, я завел дебасинг за пределами прерывания, используя флаги. Код объясняет это, но: Переключатель нажал, ISR работает (делает то, что нужно) и устанавливает флаг ISR. Флаг ISR мешает ISR фактически делать что-либо, пока это не очищено. В основном цикле вызовите функцию debounce, если установлен флаг ISR. функция debounce будет ждать, пока вывод / переключатель не стабилизируется в требуемом состоянии (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, чтобы убедиться, что прерывание действительно. Я не читал комментарий о вашей шумной обстановке

0 голосов
/ 17 сентября 2018

Вы всегда можете выполнить отладку в аппаратном обеспечении

Я несколько раз сталкивался с отскоком с помощью кнопок и кодировщиков, и отладка в программном обеспечении может привести к путанице и привести к нечитаемым кодовым или логическим ошибкам.

Самое простое, что вы можете сделать, это добавить конденсатор 0,1 мкФ, как показано на рисунке:

simple debouncing circuit

Arduino имеет гистерезисные входы, и если вы используете10K в качестве подтяжки, то это работает для отказов, которые меньше 1 мс.Это мой любимый подход.

Если вы хотите стать более серьезным, в интернете существует замечательный pdf с множеством примеров и объяснений: Руководство по разоблачению

0 голосов
/ 19 сентября 2018

Полагаю, основная проблема, как было сказано в комментариях, заключается в том, что у вас нет фактического отката.

Итак, после отпускания кнопки она продолжает подпрыгивать, вызывая изменение логического уровня на входном выводе. Если в момент, когда состояние вывода зафиксировано для digitalRead (), состояние считывалось как высокое, тогда все условие будет выполнено и будет выполнено state = HIGH;.

Я не художник, но я приложил все усилия, чтобы нарисовать эту временную диаграмму:

Time diagram of the button bounce

Так что, чтобы избежать этого, вы можете использовать любой простой подход к дебагингу. Самый простой способ - снова прочитать состояние вывода после небольшого тайм-аута, превышающего максимальное ожидаемое время отскока. Например, если вы ждете, когда кнопка будет нажата и вы получите высокий логический уровень (или низкий уровень, если кнопка подключена к GND), просто подождите около 5 мс и снова прочитайте уровень. Обрабатывать нажатие кнопки только в том случае, если уровень все еще высокий (низкий).

И, как было сказано в других ответах, аппаратная отладка также поможет. Вы можете использовать более высокий резистор (на самом деле вам не нужно использовать внешний резистор: подключите кнопку к GND и включите внутреннее подтягивание, которое составляет около 35 кОм). И добавьте конденсатор около 1 нФ, параллельно кнопке.

0 голосов
/ 17 сентября 2018

Вы используете внутренний подтягивающий резистор Uno, но также и подтягивающий резистор на самой кнопке.Это не правильно, вы можете использовать только один из них.Внутри if вы хотите, чтобы вход был высоким, поэтому используйте резистор понижения напряжения и измените INPUT_PULLUP на INPUT.

(чтобы прояснить ситуацию: резистор подключен между входным контактом и землей, кнопка между входным контактоми + 5 В)

[править]

Я думаю, что когда вызывается ISR, состояние interruptPin может снова измениться.Из-за медлительности digitalRead по сравнению с (возможным) всплеском.

Я не уверен, что это именно то, что вам нужно, но приведенный ниже пример работает.(Я включил светодиод в целях тестирования).Одно: удерживайте кнопку нажатой, по крайней мере, время ожидания (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
}
...