Как определить, нажата ли более одной кнопки

Я проектирую мигающий велосипед поворота, используя две кнопки в качестве входных, по одной для каждой стороны, с соответствующим светом для каждой кнопки. Идея такова:

  1. Нажатие одной стороны заставляет мигать один и тот же бок,
  2. Повторное нажатие этой кнопки, чтобы отключить его;
  3. Нажатие другой стороны отключает эту сторону и поворачивает другую сторону;
  4. (теперь сложная часть!), если нажаты кнопки обеих (точнее, если одна кнопка нажата до того, как другая будет отпущена), затем - и только тогда - я начну мигать обе стороны (функция «предупреждающий сигнал»).

Как я вижу, проблема в том, что мне нужно подождать, пока я не выпущу одну кнопку (это будет один полный клик), чтобы включить одну сторону, потому что, если я нажимаю второй, пока первый остается нажатым, это было бы другим событием /жестом («both_click», из-за отсутствия лучшего имени).

Другим вариантом дизайна будет включение сразу одного индикатора при нажатии, и если я нажму секунду перед тем, как отпустить первый, тогда в также поверните другой свет (таким образом, включающий «предупреждение» ", режим мигания в обоих режимах). Хотя это и кажется возможным, в классе Button («Botao») есть вся логика debouncing, и я боюсь, что я нарисовал себя в углу с конкретными объектно-ориентированными проектами, которые я сделал.

EDIT: третьим вариантом дизайна будет добавление метода run на каждую кнопку и вместо запуска только протестированного "псевдо -event ", сохранить текущее состояние и событие в переменных, протестировать эти переменные в loop() (свойство button.wasPressed вместо метода button.wasPressed(), например).

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

Основной файл .ino:

#include "PiscaPisca.cpp"
#include "Botao.cpp"

PiscaPisca pisca;

Botao botao1;
Botao botao4;

void setup() {

  pisca.configure(LEFT, RIGHT, BUZZER);

  botao1.configure(BUTTON1);
  botao4.configure(BUTTON4);
}

void loop() {

  // HOW SHOULD I DETECT BOTH WERE PRESSED??
  // if (bothPressed()) { pisca.toggleWarning(); }    

  if (botao1.wasPressed()) { pisca.toggleLeft();  }
  if (botao4.wasPressed()) { pisca.toggleRight(); }

  pisca.run();
}

Botao.cpp (это класс кнопки с debounce)

#include <Arduino.h>

class Botao
{
    int _pino;

    const int DEBOUNCE_DELAY = 30;

    int buttonState;
    int lastState = HIGH;

    int lastDebounceTime;

    public : void configure(int pino)
    {
        _pino = pino;
        pinMode(pino, INPUT_PULLUP);
    }

    public : boolean wasPressed()
    {        
        return debounce(LOW);
    }

    public : boolean wasReleased()
    {
        return debounce(HIGH);
    }

    public : boolean debounce(int state)
    {        
        boolean gotEvent = false;

        int reading = digitalRead(_pino);

        if (reading != lastState) {
            lastDebounceTime = millis();
        }

        if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) {
            if (reading != buttonState) {
                buttonState = reading;

                if (buttonState == state) {
                    gotEvent = true;
                }
            }
        }

        lastState = reading;

        return gotEvent;
    }    
};

PiscaPisca.cpp (сама машина состояния)

#include "Arduino.h"
#include "PwmPin.cpp"
#include "Beeper.cpp"

typedef enum {
    NONE        = 0,
    RIGHT_LIGHT = 1,
    LEFT_LIGHT  = 2,
    BOTH        = 3
};

class PiscaPisca
{
    PwmPin _left;
    PwmPin _right;

    Beeper _beeper;

    long _timeReference = 0;

    const int PERIOD = 350;

    boolean
        _running = false,
        _lightState = false;

    int _sides_to_turn = NONE;  


    public : void configure(int leftPin, int rightPin, int buzzerPin)
    {
        _left.configure(leftPin);
        _right.configure(rightPin);
        _beeper.configure(buzzerPin);
    }

    public : void run() 
    {        
        evaluateBlink();
    }



    public : void toggleLeft()
    {
        checkRestart(LEFT_LIGHT);
    }

    public : void toggleRight()
    {
        checkRestart(RIGHT_LIGHT);
    }

    void checkRestart(int lightSide)
    {
        _timeReference = 0;  

        // some clever bit-twiddling can never hurt too much:
        _sides_to_turn = lightSide & ~_sides_to_turn;  

        Serial.println(lightSide);
        Serial.println(_sides_to_turn);     

        if (_sides_to_turn > 0)
        {
            _running = true;
        }
        else
        {
            lightsOff();
            _running = false;
            _lightState = false;      
        }
    }



    void evaluateBlink() 
    {
        if (!_running)
        {
            return;
        }
        else
        {
            long currentMillis = millis();
            if (currentMillis - _timeReference > PERIOD) {
                _timeReference = currentMillis;
                _lightState = !_lightState;
                performBlink();
            }
        }
    }

    void performBlink()
    {
        if (_lightState)
        {
            _beeper.beepIn();
            lightsOn();
        }
        else
        {
            _beeper.beepOut();
            lightsOff();
        }        
    }

    void lightsOn()
    {
        if (isLightSet(LEFT_LIGHT))
        {
            _left.on();
        }
        if (isLightSet(RIGHT_LIGHT))
        {
            _right.on();
        }        
    }

    boolean isLightSet(int lightSide)
    {
        return (_sides_to_turn & lightSide) == lightSide;
    }

    void lightsOff()
    {
        _left.off();
        _right.off();
    }



};
2 голоса | спросил heltonbiker 16 J000000Thursday15 2015, 05:17:13

1 ответ


2

Интересный вопрос, и сложно сделать совершенным. :)

Я написал класс менеджера коммутатора некоторое время назад. Это просто файл .h, который вы можете поместить в папку с вашими библиотеками (или добавить в свой эскиз).

SwitchManager.h

 #include <Arduino.h>

class SwitchManager
  {
  enum { debounceTime = 10, noSwitch = -1 };
  typedef void (*handlerFunction) (const byte newState, 
                                   const unsigned long interval, 
                                   const byte whichSwitch);

  int pinNumber_;
  handlerFunction f_;
  byte oldSwitchState_;
  unsigned long switchStateChangeTime_;  // when the switch last changed state
  unsigned long lastLowTime_;
  unsigned long lastHighTime_;

  public:

     // constructor
     SwitchManager () 
       {
       pinNumber_ = noSwitch;
       f_ = NULL;
       oldSwitchState_  = HIGH;
       switchStateChangeTime_ = 0;
       lastLowTime_  = 0;
       lastHighTime_ = 0;
       }  // end of constructor

     void begin (const int pinNumber, handlerFunction f)
       {
       pinNumber_ = pinNumber;
       f_ = f;
       if (pinNumber_ != noSwitch)
         pinMode (pinNumber_, INPUT_PULLUP);
       }  // end of begin()

     void check ()
       {
       // we need a valid pin number and a valid function to call
       if (pinNumber_ == noSwitch || f_ == NULL)
         return;

        // see if switch is open or closed
        byte switchState = digitalRead (pinNumber_);

        // has it changed since last time?
        if (switchState != oldSwitchState_)
          {
          // debounce
          if (millis () - switchStateChangeTime_ >= debounceTime)
             {
             switchStateChangeTime_ = millis ();  // when we closed the switch 
             oldSwitchState_ =  switchState;  // remember for next time 
             if (switchState == LOW)
               {
               lastLowTime_ = switchStateChangeTime_;
               f_ (LOW, lastLowTime_ -  lastHighTime_, pinNumber_);
               }
             else
               {
               lastHighTime_ = switchStateChangeTime_;
               f_ (HIGH, lastHighTime_ - lastLowTime_, pinNumber_);
               }

             }  // end if debounce time up
          }  // end of state change
       }  // end of check()

     unsigned long getLastStateChangeTime () const { return switchStateChangeTime_; }
     unsigned long getLastStateLowTime ()    const { return lastLowTime_; }
     unsigned long getLastStateHighTime ()   const { return lastHighTime_; }

  };  // class SwitchManager

Подробнее здесь .

Поместите это в папку с именем SwitchManager, поместите это в свою папку эскиза -> libraries и перезапустите среду IDE.

Это в основном обрабатывает:

  • дребезга
  • обнаружение изменений состояния (например, теперь включено, отключено)

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

 #include <SwitchManager.h>

typedef enum {
    NONE,
    LH_DOWN,
    RH_DOWN,
    LH_LIGHT_ON,
    RH_LIGHT_ON,
    BOTH
};

const unsigned long BLINK_INTERVAL = 500; // ms

// pin assignments
const byte LH_SWITCH_PIN = 2;
const byte RH_SWITCH_PIN = 3;
const byte LH_LIGHT = A4;
const byte RH_LIGHT = A5;

SwitchManager LHswitch; 
SwitchManager RHswitch; 

byte state = NONE;

void handleLHPress (const byte newState, const unsigned long interval, const byte whichPin)
  {
  // switch down?
  if (newState == LOW)
     {
     switch (state)
       {
       // if other switch down, switch to warning mode
       case RH_DOWN:
         state = BOTH;
         break;

       // if already on or warning signal, turn all off
       case LH_LIGHT_ON:
       case BOTH:
         state = NONE;
         break;

       // otherwise switch is now down, but not yet released
       default:
         state = LH_DOWN;
         break;
       }  // end of switch
     return;
     }  // end of LH switch down

  // switch must be up

  if (state == LH_DOWN)  // if down, switch to down-and-released mode
    state = LH_LIGHT_ON;  
  }  // end of handleLHPress

void handleRHPress (const byte newState, const unsigned long interval, const byte whichPin)
  {
  // switch down?
  if (newState == LOW)
     {
     switch (state)
       {
       // if other switch down, switch to warning mode
       case LH_DOWN:
         state = BOTH;
         break;

       // if already on or warning signal, turn all off
       case RH_LIGHT_ON:
       case BOTH:
         state = NONE;
         break;

       // otherwise switch is now down, but not yet released
       default:
         state = RH_DOWN;
         break;
       }  // end of switch
     return;
     }  // end of RH switch down

  // switch must be up

  if (state == RH_DOWN)  // if down, switch to down-and-released mode
    state = RH_LIGHT_ON;  
  }  // end of handleRHPress

void setup ()
  {
  LHswitch.begin (LH_SWITCH_PIN, handleLHPress);
  RHswitch.begin (RH_SWITCH_PIN, handleRHPress);
  pinMode (LH_LIGHT, OUTPUT);
  pinMode (RH_LIGHT, OUTPUT);
  }  // end of setup

unsigned long lastBlink;
bool onCycle;

void blinkLights ()
  {
  lastBlink = millis ();
  onCycle = !onCycle;

  // default to off
  digitalWrite (LH_LIGHT, LOW);
  digitalWrite (RH_LIGHT, LOW);

  // every second time, turn them all off
  if (!onCycle)
    return;

  // blink light
  switch (state)
    {
    case NONE:
      break;

    case LH_DOWN:
    case LH_LIGHT_ON:
      digitalWrite (LH_LIGHT, HIGH);
      break;

    case RH_DOWN:
    case RH_LIGHT_ON:
      digitalWrite (RH_LIGHT, HIGH);
      break;

    case BOTH:
      digitalWrite (LH_LIGHT, HIGH);
      digitalWrite (RH_LIGHT, HIGH);
      break;

    }  // end of switch on state

  }  // end of blinkLights


void loop ()
  {
  LHswitch.check ();  // check for presses
  RHswitch.check ();  // check for presses

  if (millis () - lastBlink >= BLINK_INTERVAL)
    blinkLights ();

  // other stuff
  }  // end of loop

Конечный автомат имеет несколько состояний:

  • NONE -> все выключены.
  • LH_DOWN -> Переключатель LH нажат, еще не выпущен.
  • RH_DOWN -> Переключатель RH нажат, еще не выпущен.
  • LH_LIGHT_ON -> LH нажата и выпущена
  • RH_LIGHT_ON -> RH нажата и выпущена
  • BOTH -> Режим предупреждающих огней (мигает оба огня)

Если мы получим LH вниз, пока RH еще нажата, или наоборот, мы переключаемся в режим предупреждения.

При отпускании кнопки мы переключаемся с «switch down» на «switch release».

Второе нажатие, когда один или оба индикатора включены, отменяет их.

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

ответил Nick Gammon 16 J000000Thursday15 2015, 08:56:14

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132