Определение количества нажатий кнопок (одиночное нажатие, двойное нажатие и т. д.) двумя одновременными кнопками

нуб здесь

Итак, то, что я пытаюсь сделать, это вывести другое число в зависимости от количества нажатий кнопок, например, для 1 нажатия кнопки должно выводиться 1 и так далее. После этого мне действительно нужны выходы только от 1 до 4 нажатий. Я использую эти входы для игры. Прямо сейчас он работает и считает до 4, а затем сбрасывается до 0. Проблема, с которой я столкнулся, заключается в том, что он продолжается после 1 клика до 2 кликов; то, что я хочу, это подсчитать 1 клик, когда он щелкнул один раз, дважды, когда он щелкнул дважды и т. д. Я пытаюсь использовать код устранения дребезга, чтобы различать одиночное нажатие, двойное нажатие, тройное нажатие и т. д. @VE7JRO любезно предоставил отличное решение этой проблемы, хотя и для одной кнопки. Я пытаюсь настроить код для размещения двух отдельных кнопок (на разных контактах), но это выдает ошибку.

Это код:

#include <Bounce2.h>

// Соединяем обе кнопки последовательно одним соединением
// к GND, а другой к цифровому выводу.
const int buttonPin1 = 2;
const int buttonPin2 = 3;

class Button
{
  private:

    int m_buttonPin1;
    int m_buttonPin2;
    int m_counter;
    unsigned long m_buttonPressTimeout;
    unsigned long m_previousMillis;

  public:

    Button(int button):
      m_buttonPin1(buttonPin1),
      m_buttonPin2(buttonPin2),
      m_counter(0),
      m_buttonPressTimeout(1500), // Время ожидания нажатия кнопки 500 мс.
      m_previousMillis(0){}



    void Update()
    {
      int valA = digitalRead(button1); // чтение состояния кнопки
      int  valB = digitalRead(button2); // чтение состояния кнопки
       if (valA == LOW &&valB == LOW)
       { 
      if(m_counter > 0 && millis() - m_previousMillis >= m_buttonPressTimeout)
      {
        Serial.print("Count from Update() just before it's re-set to 0 = ");
        Serial.println(GetCounter());
        m_counter = 0;
      }
    }

    void IncrementCounter(){
      m_counter++;
      if(m_counter > 4){m_counter = 4;}
      if(m_counter == 1){
        m_previousMillis = millis();
      }
    }

    byte GetCounter(){
      return m_counter;
    }

};

Bounce button1Debouncer = Bounce();
Bounce button2Debouncer = Bounce();
Button MyButton(buttonPin1);
Button MyButton(buttonPin2);

void setup(){

  Serial.begin(9600);

  pinMode(buttonPin1, INPUT_PULLUP);
  button1Debouncer.attach(buttonPin);
  button1Debouncer.interval(5);
  pinMode(buttonPin2, INPUT_PULLUP);
  button2Debouncer.attach(buttonPin);
  button2Debouncer.interval(5);

}

void loop(){

  // Вызовите функцию обновления как можно быстрее.
  MyButton.Update();

  // Кнопка нажата.
  if(button1Debouncer.update() && button2Debouncer.update())
  {
    if(button1Debouncer.fell() && button2Debouncer.fell()){
      MyButton.IncrementCounter();
      Serial.print("Count from Button Debouncer = ");
      Serial.println(MyButton.GetCounter());
    }
  }

}
}

Ошибка: «'buttonPin1' не является типом», я не понимаю эту ошибку, поскольку она не выдавала эту ошибку при использовании одной кнопки.

, 👍0

Обсуждение

Вы пишете, что хотите его для игры. Так что, возможно, вы хотите не считать нажатия, а просто посмотреть, какая кнопка была нажата правильно? Не вызывайте "digitalRead" много раз, если хотите быть быстрым. Лучшим решением было бы прочитать 7 контактов одним вызовом с помощью PIND, поэтому вы читаете контакты с 0 по 7 (дополнительно). Или, когда я анализирую ваш код, вы просто хотите получать уведомления об изменениях, вы можете использовать прерывания., @Adriano

@ Adriano Мне действительно нужно количество нажатий. Я думаю, что я должен сделать это немного яснее: Справедливое замечание ... Я никогда не объяснял этого Итак, у меня есть игрок, которому нужно прыгать на разные платформы, чтобы собирать монеты, и на каждом уровне платформы и номера монет различаются, и поскольку я использую устройство ввода в качестве отвлечения внимания, поэтому на каждом уровне количество кликов, необходимых для перемещение персонажа, изменения Таким образом, на одном уровне вам нужно нажимать кнопки дважды, на следующем уровне трижды и т. д., @user19964

Я не понимаю проблемы, которую вы пытаетесь решить. Сколько у вас отдельных кнопок? Каждая кнопка на отдельном пине или у вас матрица кнопок? Может ли пользователь одновременно нажимать более одной кнопки, а вы хотите определить, сколько из них нажато одновременно? Давайте назовем это numberOfButtonsPressed. Тогда вы также ищете, чтобы пользователь нажимал одинарное, двойное, тройное нажатие и т. д., какое-то количество кнопок? Итак, у вас обоих есть количество нажатых кнопок и количество нажатий на них?, @Duncan C

Вам нужно будет обрабатывать «отстой» на входе, когда пользователь нажимает разные кнопки в несколько разное время. Если они пытаются нажать 4 кнопки, вы можете обнаружить, что сначала нажата одна, затем 2, затем 3, затем 4 кнопки. И из-за дребезга вы можете увидеть, как некоторые из этих кнопок быстро переключаются между ненажатыми и нажатыми в течение нескольких миллисекунд, прежде чем успокоиться., @Duncan C

Вы можете попробовать 20-50 мс. Если вы не обнаружите каких-либо изменений в состоянии кнопки в течение этого времени, вы можете считать состояние кнопки стабильным и снять показания., @Duncan C

@DuncanC У меня есть 2 кнопки, которые подключены к отдельным контактам, но пользователь должен нажать обе кнопки одновременно, чтобы зарегистрировать одно нажатие. Итак, проблема, с которой я столкнулся, заключается в том, как отличить один толчок от двух толчков и т. Д. Итак, вы предлагаете мне обрабатывать «отстой» в этом случае?, @user19964

Вам нужно устранить дребезг кнопок. Это можно сделать довольно легко в программном обеспечении. Выполните поиск в Google, но основная идея заключается в том, что когда вы обнаруживаете нажатие кнопки (или нажатие двух кнопок в вашем случае), вы записываете значение millis() и игнорируете изменения цифровых входов для некоторой константы. Период ожидания. (20-100 мс — хороший диапазон для работы, в зависимости от ситуации.), @Duncan C

@DuncanC На самом деле я думал, что смогу уйти, не устраняя это (поскольку до этого момента он хорошо работает без этого), но, похоже, мне нужно вникнуть в это. Спасибо, @user19964

На самом деле написать собственный код для устранения дребезга довольно просто. Вам просто нужно записать чтение millis(), когда первая кнопка меняет состояние, и игнорировать дальнейшие изменения состояния в течение небольшого интервала времени., @Duncan C

@DuncanC Сегодня я попробовал код устранения дребезга, но теперь последовательный монитор ничего не выводит. Не могли бы вы взглянуть на мой обновленный код?, @user19964


4 ответа


Лучший ответ:

0

Если есть возможность соединить 2 кнопки последовательно, этот скетч может дать желаемый результат.

#include <Bounce2.h>

// Соединяем обе кнопки последовательно одним соединением
// к GND, а другой к цифровому выводу.
const byte buttonPin = 2;

class Button{

  private:

    byte m_buttonPin;
    byte m_counter;
    unsigned long m_buttonPressTimeout;
    unsigned long m_previousMillis;

  public:

    Button(byte buttonPin):
      m_buttonPin(buttonPin),
      m_counter(0),
      m_buttonPressTimeout(500), // Время ожидания нажатия кнопки 500 мс.
      m_previousMillis(0){}

    void Update(){
      if(m_counter > 0 && millis() - m_previousMillis >= m_buttonPressTimeout){
        Serial.print("Count from Update() just before it's re-set to 0 = ");
        Serial.println(GetCounter());
        m_counter = 0;
      }
    }

    void IncrementCounter(){
      m_counter++;
      if(m_counter > 4){m_counter = 4;}
      if(m_counter == 1){
        m_previousMillis = millis();
      }
    }

    byte GetCounter(){
      return m_counter;
    }

};

Bounce buttonDebouncer = Bounce();
Button MyButton(buttonPin);

void setup(){

  Serial.begin(9600);

  pinMode(buttonPin, INPUT_PULLUP);
  buttonDebouncer.attach(buttonPin);
  buttonDebouncer.interval(5);

}

void loop(){

  // Вызовите функцию обновления как можно быстрее.
  MyButton.Update();

  // Кнопка нажата.
  if(buttonDebouncer.update()){
    if(buttonDebouncer.fell()){
      MyButton.IncrementCounter();
      Serial.print("Count from Button Debouncer = ");
      Serial.println(MyButton.GetCounter());
    }
  }

}

На основе вашего последнего обновления вопроса:

Я пытаюсь настроить код для размещения двух отдельных кнопок (на разных контактах), но это выдает ошибку.

Я обновил скетч, чтобы использовать 2 кнопки на отдельных цифровых выводах.

#include <Bounce2.h>

// Соединяем каждую кнопку одним соединением
// к GND, а другой к цифровому выводу.
const byte buttonPin = 2;
const byte buttonPin2 = 3;

class Button{

  private:

    byte m_buttonPin;
    byte m_counter = 0;
    unsigned long m_buttonPressTimeout;
    unsigned long m_previousMillis;

  public:

    Button(byte buttonPin):
      m_buttonPin(buttonPin),
      m_counter(0),
      m_buttonPressTimeout(750), // Время ожидания нажатия кнопки в мс.
      m_previousMillis(0){}

    void Update(){
      if(m_counter > 0 && millis() - m_previousMillis >= m_buttonPressTimeout){
        Serial.print("Count from Update() just before it's reset to 0 = ");
        Serial.println(GetCounter());
        m_counter = 0;
      }
    }

    void IncrementCounter(){
      m_counter++;
      if(m_counter > 4){m_counter = 4;}
      if(m_counter == 1){
        m_previousMillis = millis();
      }
    }

    friend void IncrementCounter(Button&);

    void IncrementCounter(Button&){
      IncrementCounter();
    }

    byte GetCounter(){
      return m_counter;
    }

};

Bounce buttonOneDebouncer = Bounce();
Bounce buttonTwoDebouncer = Bounce();
Button ButtonOne(buttonPin);
Button ButtonTwo(buttonPin2);

void setup(){
  Serial.begin(9600);
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(buttonPin2, INPUT_PULLUP);
  buttonOneDebouncer.attach(buttonPin);
  buttonTwoDebouncer.attach(buttonPin2);
  buttonOneDebouncer.interval(25);
  buttonTwoDebouncer.interval(25);
}

void loop(){

  // Вызовите функцию обновления как можно быстрее.
  ButtonOne.Update();
  ButtonTwo.Update();

  // Нажата первая кнопка.
  if(buttonOneDebouncer.update()){
    if(buttonOneDebouncer.fell()){
      if(digitalRead(buttonPin2) == 0){
        ButtonOne.IncrementCounter();
      }
    }
  }

  // Вторая кнопка нажата.
  if(buttonTwoDebouncer.update()){
    if(buttonTwoDebouncer.fell()){
      if(digitalRead(buttonPin) == 0){
        ButtonOne.IncrementCounter(ButtonTwo);
      }
    }
  }

}
,

спасибо, я попробую это. Использует ли это внешнюю библиотеку? @VE7JRO, @user19964

Да, библиотека Bounce2. Он доступен в менеджере библиотек IDE., @VE7JRO

О, хорошо, я попробую это, спасибо @VE7JRO, @user19964

Спасибо; это работает довольно хорошо ... теперь просто нужно выяснить, как подключить две отдельные кнопки к двум отдельным контактам @ VE7JRO, @user19964

@user19964 user19964 - легко добавить еще одну кнопку. Определите другую переменную, такую как buttonPin2, затем сделайте что-то вроде этого Bounce buttonDebouncer2 = Bounce(); и Button MyButton2(buttonPin2);. Немного копирования/вставки должно заставить 2 кнопки работать быстро. Самым сложным будет изменить логику в loop() для работы с двумя кнопками., @VE7JRO

для цикла() не могли бы вы просто добавить что-то вроде: if(button1Debouncer.update() && button2Debouncer.update()) { if(button1Debouncer.fell() && button2Debouncer.fell())}} ? @VE7JRO, @user19964

@ user19964 - Я думаю, вы могли бы сделать что-то подобное. Возможно, когда одна из кнопок нажата, функция "it's" в loop() может также выполнить digitalRead для "другой кнопки". Эта часть информации может быть полезна, чтобы определить, действительно ли обе кнопки нажаты одновременно, в этот самый момент времени., @VE7JRO

Давайте [продолжим это обсуждение в чате](https://chat.stackexchange.com/rooms/101256/discussion-between-ve7jro-and-user19964)., @VE7JRO

@ VE7RO Я увидел это только сейчас и сразу же протестировал. Поскольку я этого не видел, я все еще несколько дней боролся с этим вопросом :(. БОЛЬШОЕ СПАСИБО!! Только что протестировал, и он работает хорошо; я действительно ценю всю помощь, которую вы мне оказали с этим вопрос :). Удачного дня!, @user19964


1

Вероятно, у вас возникли проблемы, связанные с кнопкой связаться с отказом. Здесь процессор настолько быстр, что «видит» кнопку, выполняющую несколько контактов, когда пользователь хочет, чтобы программа увидела только 1 контакт. Рассмотрите возможность использования этой библиотеки подавления дребезга кнопок вместо чтения состояния кнопок непосредственно в скетче.

Код приведенной выше библиотеки для защиты от дребезга Arduino находится здесь, на github.com. Как правило, у людей, использующих github.com, будет папка «example» вместе с папками исходного кода и документации. папка с примерами библиотеки debounce для Arduino находится здесь. Существует около полудюжины примеров скетчей Arduino, включая этот пример с двумя кнопками, который может использоваться в качестве отправной точки для проекта в вопросе.

Может возникнуть логическая проблема в коде при интерпретации количества нажатий кнопок в "a", "b", "c" & "д". Используется оператор по модулю "%". И в первом тесте проверяется количество нажатий кнопки %1. Если результат равен нулю, тест верен. Поскольку любое число % 1 не имеет остатка, этот тест всегда будет равен нулю и, следовательно, всегда верен. Поскольку все остальные тесты вложены в операторы else, ни один из других тестов выполняться не будет. Вместо этого рассмотрите возможность использования оператора switch/case, где код переключает число нажатий кнопок и операторами case являются "case 1:", case 2:", "case 3:" & "default:". Хорошей практикой кодирования всегда является использование case "default:" в операторе switch. Здесь мы используем «по умолчанию:» вместо «случай 4:». После использования значения, представляющего количество нажатий кнопки, всегда не забывайте очищать это значение перед следующей итерацией. Помните, что любые «глобальные» значения (значения, определенные вне функции ) сохранят свои значения между вызовами этой функции. Любое "локальное" значение (значения, определенные внутри функции) не сохранит свои значения, и его необходимо инициализировать при каждом использовании.

,

есть ли какие-нибудь примеры, показывающие, как реализовать эту библиотеку... я импортировал ее в свой проект, но не знаю, как ее реализовать @st2000, @user19964

Я добавлю, где найти примеры в моем ответе. Кроме того, я ценю, что вы отредактировали свой вопрос, чтобы сделать его лучше. Я добавил текст к вашему вопросу, чтобы было ясно, что было добавлено больше информации и что эта информация показывает., @st2000

да, я вижу, что вы отредактировали мой вопрос: спасибо... теперь он читается намного лучше. Да, эти примеры были бы очень признательны @st2000, @user19964

@ st2000просто хотел упомянуть, что счетчик сейчас работает, но он работает не совсем так, как мне бы хотелось., @user19964

Вы используете оператор по модулю "%". Я не думаю, что это то, что вы хотите. Например, вы можете разделить любое целое число на 1 и не получить остатка. Таким образом, 1 % 1 равно 0, 2 % 1 равно 0, что угодно, % 1 равно 0. Этот оператор всегда истинен, и ничто ниже него не будет выполняться. Вы всегда будете печатать «а». Вы хотите, чтобы я снова изменил свой ответ?, @st2000

ах да, это имеет смысл ... так много смысла. Когда я использовал if(buttonPushCounter ==1) и т. д., это не сработало. Да, я был бы признателен за это @st2000, @user19964

Большое спасибо за ваш обширный ответ. Я просмотрел там решения и добавил их в свое собственное решение. Как требуется. Счетчик теперь работает, выдает как надо и сбрасывает когда надо тоже: все как задумано. Я соответственно отредактировал свой вопрос @st2000, @user19964


2

Вам СЛЕДУЕТ обратить внимание на следующие вопросы:

1. Проблема с плавающим вводом:

Симптом: значение, считанное с входного контакта, не соответствует состоянию нажатия кнопки.

Причина: входной контакт НЕ использует подтягивающий или подтягивающий резистор.

Решение. Используйте подтягивающий или подтягивающий резистор. См. раздел Кнопка Arduino (с вытягиванием вверх/вниз)

2. Феномен болтовни

Это следует учитывать только в некоторых приложениях, которым необходимо определять точное количество нажатий.

Симптом: кнопка нажата один раз, но код Arduino определяет это несколько раз.

Причина. Из-за механических и физических проблем состояние кнопки (или переключателя) быстро переключается между НИЗКИМ и ВЫСОКИМ несколько раз

Решение. Отказаться. См. раздел отказ кнопки Arduino

.
,

Спасибо за советы. У меня есть подтягивающий резистор на обеих моих кнопках, и я реализовал код устранения дребезга в этом коде. Я получаю почти то же самое, что и раньше @Rozona Zoro, @user19964


2

В вашем обновленном коде много проблем. Чтобы устранить дребезг кнопок, попробуйте следующее:

//Настройте debounceTime по желанию. Более короткое время сделает кнопку быстрее
//ответить, но сделать устранение дребезга менее эффективным. 50 это 1/20 секунды.
#define debounceTime 50

unsigned long nextButtonCheckTime = 0;
bool buttonAState = false;
bool buttonBState = false;


void loop() {
  //Проверять кнопки, только если прошло время устранения дребезга
  if (millis() > nextButtonCheckTime) 
    bool newButtonAState = digitalRead(buttonAPin) == LOW;
    bool newButtonBState = digitalRead(buttonBPin) == LOW;
    //Если одна или обе кнопки изменили состояние
    if  (buttonAState != newButtonAState || buttonBState != newButtonBState)) {
      nextButtonCheckTime = millis() + debounceTime;
      buttonAState = newButtonAState;
      buttonBState = newButtonBState;
      //Обработка изменения состояния кнопки
    }
  }
}

Это псевдокод. Он почти наверняка содержит синтаксические ошибки. Вы должны использовать его в качестве руководства и переписать в соответствии с вашими потребностями. не говорите: «Я скопировал ваш код в свой скетч, и он не скомпилируется».

,

Спасибо; это действительно помогает в качестве ориентира. Я буду использовать его соответственно @DuncanC, @user19964