Опять про кнопки. Обработка нажатия

funakoshi
Offline
Зарегистрирован: 13.03.2015

Ардуино увидел впервые  4 дня назад. Об уровне познаний в программировании судите сами :))

Вводная часть:

Pin3 - вход, получаем сигнал с датчика скорости авто. Pin7 - вход, кнопка. Pin9 - выход, реле. Необходимо запомнить 3 значения скорости: speedUp (скорость растет до заданного значения), speedDown (скорость падает до заданного значения), speedLimit (не равно speedUp, скорость растет до заданного значения).

Алгоритм работы кнопки следующий:
1. Одно короткое нажатие ( до 0,5 сек) - вкл и выкл реле;

2. 2 коротких нажатия (по 0,5 сек) в течение 1 сек - сменить режим speedUp/speedDown/speedLimit;

3. 1 длинное нажатие (более 0,5 сек) - присвоить текущее значение скорости выбранному режиму.

Основную часть накопипастил, несколько строк сам написал - на данный момент имею  вот такой код:

/*
created Tomasina, forum.amperka.ru
created Frud, drive2.ru
modified funakoshi
8 April 2015
*/

#define BUTTON_PIN 7
#define BUZZER_PIN 4
#define RELAY_PIN 9

int bounceTime = 10;          // задержка для подавления дребезга
int holdTime = 500;          // время, в течение которого нажатие можно считать удержанием кнопки
int doubleTime = 1000; // время, в течение которого нажатия можно считать двойным
int frequency = 3500; //частота звука "пищалки"
int speedMode = 0; //просто чтобы не ругался компилятор

boolean lastReading = false;  // флаг предыдущего состояния кнопки
boolean buttonSingle = false; // флаг состояния "краткое нажатие"
boolean buttonDouble = false; // флаг состояния "двойное нажатие"
boolean buttonHold = false;  // флаг состояния "долгое нажатие"
boolean dispState = false; //флаг "включенности" дисплея парктроника

long onTime = 0;              // переменная обработки временного интервала
long lastSwitchTime = 0;      // переменная времени предыдущего переключения состояния

volatile unsigned long micros_sp = 0;
volatile byte sz = 0;                  //счетчик обнуления
volatile unsigned int sp = 0;          //скорость
volatile unsigned int speedUp = 0;    //набор скорости
volatile unsigned int speedDown = 0;  //снижение скорости
volatile unsigned int speedLimit = 0; //предел превышения скорости
volatile boolean st = false;

void setup()
{
  pinMode(BUTTON_PIN, INPUT); // INPUT_PULLUP подключит кнопку через внутренний резистор
  attachInterrupt(1, speedometr, RISING);
}

void loop()
{
  if (sz != 0) {
    sz--;
  } else {
    sp = 0;
  };

  // ================ обработчик состояния кнопки ====================
  boolean reading = digitalRead(BUTTON_PIN);  /* читаем состояние пина кнопки. Если подключен через внутренний резистор (см. pinMode(BUTTON_PIN, INPUT_PULLUP)),
                                                 перед digitalRead(BUTTON_PIN) нужен !. */
  // проверка первичного нажатия
  if (reading && !lastReading)
  {
    onTime = millis();
  }
  // проверка удержания
  if (reading && lastReading)
  {
    if ((millis() - onTime) > holdTime)
    {
      buttonHold = true;
    }
  }
  // проверка отпускания кнопки
  if (!reading && lastReading)
  {
    if (((millis() - onTime) > bounceTime) && !buttonHold)
    {
      if ((millis() - lastSwitchTime) >= doubleTime)
      {
        lastSwitchTime = millis();
        buttonSingle = true;
      }
      else
      {
        lastSwitchTime = millis();
        buttonDouble = true;
        buttonSingle = false;
        isButtonDouble();
        buttonDouble = false;  // сброс состояния после выполнения команды
      }
    }
    if (buttonHold)
    {
      buttonDouble = false;
      isButtonHold(speedMode);
      buttonHold = false;  // сброс состояния после выполнения команды
    }
  }
  lastReading = reading;
  if (buttonSingle && (millis() - lastSwitchTime) > doubleTime)
  {
    buttonDouble = false;
    isButtonSingle();
    buttonSingle = false;  // сброс состояния после выполнения команды
  }
  // ================ конец обработчика состояния кнопки ==================
} // конец loop


void isButtonSingle() // действия после одиночного нажатия кнопки
{ //При однократном "коротком" нажатии кнопки реле замкнётся на 0,5 сек.
  digitalWrite(RELAY_PIN, HIGH);
  delay(500);
  digitalWrite(RELAY_PIN, LOW);
}

int isButtonDouble() // действия после двойного нажатия кнопки
{ //Выбрать активный режим (всего 3 режима: speedUp, speedDown, speedLimit)
  //Просигнализировать о выбранном режиме n-ым количеством бипов (n зависит от активного режима)
  int regim = 1;
  regim++;
  if (regim > 3) {
    regim = 1;
  }
  if (regim == 1) //speedUp
  {
    tone(BUZZER_PIN, frequency, 50);
    speedMode = 1;
    return speedMode;
  }
  if (regim == 2) //speedDown
  {
    tone(BUZZER_PIN, frequency, 50);
    speedMode = 2;
    return speedMode;
  }
  if (regim == 3) //speedLimit
  {
    tone(BUZZER_PIN, frequency, 50);
    speedMode = 3;
    return speedMode;
  }
}


int isButtonHold(int x) // действия после удержания кнопки
{ //Запомнить значание активному режиму. Об запоминании сигнализировать звуком
  switch (x) {
    case 1: //speedUp
      speedUp = sp;
      tone(BUZZER_PIN, frequency, 150);
      return speedUp;
      break;
    case 2: //speedDown
      speedDown = sp;
      tone(BUZZER_PIN, frequency, 150);
      return speedDown;
      break;
    case 3: //speedLimit
      speedLimit = sp;
      tone(BUZZER_PIN, frequency, 150);
      return speedLimit;
      break;
  }
}

void speedometr() { //измеряем частоту на входе спидометра по прерыванию
  if (!st) {
    micros_sp = micros();
  }
  else {
    sp = (1600000 / (micros() - micros_sp));
  }
  st = !st;
  sz = 30;
}

Помогите пожалуйста с наполнением функций isButtonHold и isButtonDouble. Особенно с последней.

 

vvadim
Offline
Зарегистрирован: 23.05.2012

Посмотрите готовые библиотеки для работы с кнопками  ClickButton   OneButton   Button  X-Dron

X-Dron
Offline
Зарегистрирован: 24.01.2015

С кнопками там более-менее нормально, вызова функций int isButtonHold(int x), void isButtonSingle(), int isButtonDouble() работают более менее корректно, проверял. (хотя я бы написал по другомую, не нравится мне, что Hold вызывается по отпусканию клнопки, а не по тому, когда интервал истек. Соответственно, с использованием своей библиотеки). Эта часть кода написана Tomasina.
Топикстартера интересует именно наполнение функций.
regim в int isButtonDouble() должен быть либо глобальной переменной либо static (лучше глобальной).
Зацикленная смена режима без if() -
regim = (regim++)%3;
Дальше тоже лучше без if(), на switch (regim) case.

 

funakoshi
Offline
Зарегистрирован: 13.03.2015

Спасибо за подсказку. Тут собственно моего кода - наполнение функций isButtonHold, isButtonSingle, isButtonDouble. Остальное написано знатоками :)

Так правильно?

int regim = 1;
int isButtonDouble() // действия после двойного нажатия кнопки
{ //Выбрать активный режим (всего 3 режима: speedUp, speedDown, speedLimit)
  //Просигнализировать о выбранном режиме n-ым количеством бипов (n зависит от активного режима)
  regim = (regim++) % 3; //зацикленная смена режима без использования if
  switch (regim) {
    case 1: // speedUp
      tone(BUZZER_PIN, frequency, 50);
      speedMode = 1;
      return speedMode;
      break;
    case 2: //speedDown
      tone(BUZZER_PIN, frequency, 50);
      speedMode = 2;
      return speedMode;
      break;
    case 3: //speedLimit
      tone(BUZZER_PIN, frequency, 50);
      speedMode = 3;
      return speedMode;
      break;
  } 
}

И можно поподробнее про (regim++)%3? Справку прочитал и не понял почему

x = 4%5     // x имеет значение 4

 

X-Dron
Offline
Зарегистрирован: 24.01.2015

А зачем возвращать функции значение глобальной переменной speedMode?

funakoshi
Offline
Зарегистрирован: 13.03.2015

Ну, чтобы использовать speedMode как входящий параметр для isButtonHold. Или это лишнее для ГЛОБАЛЬНОЙ переменной?

X-Dron
Offline
Зарегистрирован: 24.01.2015

лишнее.
Я немного наврал с (regim++)%3; ++ надо ставить до переменной, тогда работает. А с учетом, что режимы у Вас начинаются с 1 и до 3, а воздействовать надо на глобальную переменную, то вся функция isButtonDouble() вырождается в

void isButtonDouble() // действия после двойного нажатия кнопки
{ //Выбрать активный режим (всего 3 режима: speedUp, speedDown, speedLimit)
  //Просигнализировать о выбранном режиме n-ым количеством бипов (n зависит от активного режима)
  speedMode = (++speedMode) % 4;
  if (speedMode == 0) speedMode=1;
  for (int i= 0; i<speedMode; i++){
    tone(BUZZER_PIN, frequency, 50);
    delay(200);
  }
} 

delay мне, конечно, не нравится, он стопорит на время всю программу ради "аудиозации", но чтобы от него избавиться нужно менять подход к вызову функций. 

funakoshi
Offline
Зарегистрирован: 13.03.2015

Ничего себе оптимизация кода :)) Спасибо.